OLDBOY/skin/board/outline_bybit/view.skin.php
<?php
if (!defined('_GNUBOARD_')) exit;

add_stylesheet('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">', 0);
include_once(G5_LIB_PATH.'/thumbnail.lib.php');

// 가동 여부 확인
$is_active = ($view['x2_ca3'] == '1');
// 목표 타입 확인
$is_rate_type = ($view['x2_target_type'] == 'rate');

// 1. 목표가 미리 계산
$calc_target_price = 0;
if ($is_rate_type && $is_active) {
    $watch_p = (float)str_replace(',', '', $view['wr_subject']);
    $rate_val = (float)$view['x2_rate'] / 100;
    $raw_margin = preg_replace("/[^0-9.]/", "", $view['x2_margin']);
    $calc_margin = $raw_margin ? (float)$raw_margin : 1;

    if ($view['x2_ca4'] == '롱') {
        $calc_target_price = $watch_p * (1 + ($rate_val / $calc_margin));
    } else {
        $calc_target_price = $watch_p * (1 - ($rate_val / $calc_margin));
    }
}
?>

<style>
    @import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css');
    @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=JetBrains+Mono:wght@400;700&family=Noto+Sans+KR:wght@400;700;900&display=swap');

    :root {
        --neon-cyan: #00d4ff; --neon-pink: #ff2d55; --bg-black: #0d1117;
        --panel-bg: #161b22; --input-bg: #0d1117; --border-color: #30363d;
        --text-dim: #8b949e; --side-margin: 50px;
        --price-up: #4ade80; --price-down: #ff2d55;
        --electric-blue: #5de2ff;
        --golden-yellow: #ffdf00;
        --bright-green: #39ff14;
        --dark-red: #8b0000;
        --inverse-purple: #8a2be2; 
        color-scheme: dark;
    }

    #CYBER_LOADER { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: var(--bg-black); z-index: 10000; display: flex; flex-direction: column; align-items: center; justify-content: center; transition: opacity 0.5s ease; }
    .load-text { font-family: 'Orbitron'; color: var(--neon-cyan); letter-spacing: 3px; margin-bottom: 20px; font-size: 0.9rem; }
    .load-bar-wrap { width: 200px; height: 1px; background: rgba(255,255,255,0.1); position: relative; }
    .load-bar { width: 0%; height: 100%; background: var(--neon-cyan); animation: loadingProgress 1.5s forwards; }
    @keyframes loadingProgress { to { width: 100%; } }

    #VIEW_WRAP { position: relative; min-height: 100vh; background: var(--bg-black); padding: 40px 0; font-family: 'Pretendard', sans-serif; color: #c9d1d9; overflow-x: hidden; }
    #space-canvas { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 1; opacity: 0.2; }
    .view-container { position: relative; z-index: 2; width: calc(100% - 100px); margin: 0 var(--side-margin); max-width: none !important; animation: fadeIn 0.6s ease-out; }
    @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }

    #v_frame { background: var(--panel-bg); border: 1px solid var(--border-color); border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,0.5); overflow: hidden; position: relative; }
    #v_frame::before, #v_frame::after { content: ''; position: absolute; width: 30px; height: 30px; border: 2px solid var(--neon-cyan); z-index: 10; pointer-events: none; }
    #v_frame::before { top: -1px; left: -1px; border-right: 0; border-bottom: 0; border-radius: 8px 0 0 0; }
    #v_frame::after { bottom: -1px; right: -1px; border-left: 0; border-top: 0; border-radius: 0 0 8px 0; }

    .view-header { padding: 25px 35px; border-bottom: 1px solid var(--border-color); background: linear-gradient(90deg, rgba(0, 212, 255, 0.05), transparent); }
    .view-header h2 { margin:0; font-family: 'Orbitron'; font-size: 1.1rem; color: var(--neon-cyan); font-weight: 700; letter-spacing: 1px; }
    .info-meta { margin-top: 10px; font-family: 'JetBrains Mono'; font-size: 0.75rem; color: var(--text-dim); }

    .view-info-bar { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; padding: 20px 35px; background: rgba(0, 0, 0, 0.2); border-bottom: 1px solid var(--border-color); }
    .info-tag { border: 1px solid var(--border-color); padding: 5px 15px; border-radius: 4px; font-size: 0.8rem; font-family: 'Noto Sans KR'; font-weight: 600; color: #fff; background: rgba(255,255,255,0.03); }
    .info-tag.cyan { color: var(--neon-cyan); border-color: rgba(0, 212, 255, 0.3); }
    .info-tag.coin-id { color: var(--neon-pink); border-color: var(--neon-pink); font-family: 'Orbitron'; letter-spacing: 1px; }
    
    .status-badge { padding: 6px 20px; border-radius: 4px; font-size: 0.9rem; font-weight: 800; font-family: 'Noto Sans KR', sans-serif; letter-spacing: 1px; transition: 0.3s; color: #fff  }
    .stat-on { background:#1fca00; box-shadow: 0 0 15px rgba(57, 255, 20, 0.4); }
    .stat-off { background: var(--dark-red); box-shadow: 0 0 15px rgba(139, 0, 0, 0.4); }

    .dashboard-panel { display: flex; align-items: stretch; border-bottom: 1px solid var(--border-color); background: rgba(0,0,0,0.1); }
    .dash-item { flex: 1; padding: 45px 10px; border-right: 1px solid var(--border-color); text-align: center; display: flex; flex-direction: column; justify-content: center; align-items: center; transition: 0.3s; position: relative; }
    .dash-item:last-child { border-right: none; }
    .dash-item:hover { background: rgba(255,255,255,0.03); }
    
    .dash-label { font-family: 'Noto Sans KR'; font-size: 1rem; color: #ffffff; margin-bottom: 18px; font-weight: 700; letter-spacing: 0.5px; position: relative; padding-bottom: 8px; }
    .dash-label::after { content: ''; position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); width: 20px; height: 2px; background: rgba(255,255,255,0.2); }
    
    .dash-value { font-family: 'JetBrains Mono'; font-size: 2.2rem; font-weight: 800; color: #fff; letter-spacing: -1px; line-height: 1; }
    .dash-sub { font-family: 'JetBrains Mono'; font-size: 1.0rem; color: var(--text-dim); margin-top: 5px; font-weight: 500; }
    
    .dash-watch .dash-value { color: var(--bright-green); text-shadow: 0 0 15px rgba(57, 255, 20, 0.4); }
    .dash-target .dash-value { color: var(--golden-yellow); text-shadow: 0 0 20px rgba(255, 223, 0, 0.4); }
    .dash-realtime .dash-value { color: var(--electric-blue); font-size: 2.5rem; text-shadow: 0 0 25px rgba(93, 226, 255, 0.5); }
    
    #profit_indicator, #pnl_indicator { transition: all 0.5s ease; padding: 5px 15px; border-radius: 8px; }
    .p-up { color: var(--price-up) !important; text-shadow: 0 0 15px rgba(74, 222, 128, 0.4); }
    .p-down { color: var(--price-down) !important; text-shadow: 0 0 15px rgba(255, 45, 85, 0.4); }
    
    .inverse-mode { background: rgba(138, 43, 226, 0.15); border: 1px solid rgba(138, 43, 226, 0.3); box-shadow: 0 0 15px rgba(138, 43, 226, 0.2); }

    .status-reached { background: var(--bright-green) !important; color: #fff !important; box-shadow: 0 0 25px rgba(57, 255, 20, 0.5); border: none; padding: 8px 15px; border-radius: 20px; font-weight: 900; }
    .status-waiting { color: #fff; padding: 8px 15px; border-radius: 20px; font-weight: 900; background: #333; }
    
    /* 도달 차트 (Progress Bar) */
    .view-chart-bg { width: 100%; height: 8px; background: rgba(255,255,255,0.1); border-radius: 4px; overflow: hidden; margin-top: 10px; position: relative; }
    .view-chart-bar { height: 100%; background: var(--neon-cyan); width: 0%; transition: width 0.5s ease; }

    .view-tags { padding: 20px 35px; border-top: 1px solid var(--border-color); display: flex; flex-wrap: wrap; gap: 10px; }
    .tag-link { color: var(--neon-pink); font-size: 0.75rem; text-decoration: none; opacity: 0.8; }
    
    .view-footer { padding: 30px 35px; border-top: 1px solid var(--border-color); display: flex; justify-content: space-between; align-items: center; background: rgba(0, 0, 0, 0.1); }
    .btn-action { border: 1px solid var(--border-color); background: transparent; color: var(--text-dim); padding: 8px 18px; border-radius: 4px; font-family: 'Orbitron'; font-weight: 600; cursor: pointer; transition: 0.2s; font-size: 0.7rem; text-decoration: none; text-transform: uppercase; }
    .btn-action.highlight { background: var(--neon-cyan); color: #000; border: none; }

    .view-nav { display: flex; border-top: 1px solid var(--border-color); }
    .view-nav a { flex: 1; padding: 25px 35px; text-decoration: none; color: #fff; border-right: 1px solid var(--border-color); transition: 0.2s; }
    .nav-label { font-family: 'Orbitron'; font-size: 0.6rem; color: var(--neon-cyan); display: block; margin-bottom: 5px; }
    .nav-subject { font-size: 0.85rem; color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

    @media (max-width: 1200px) {
        .dash-value { font-size: 1.8rem; }
        .dash-realtime .dash-value { font-size: 2rem; }
    }

    @media (max-width: 1024px) { 
        :root { --side-margin: 20px; } 
        .view-container { width: calc(100% - 40px); margin: 0 20px; }
        .dashboard-panel { flex-direction: column; }
        .dash-item { border-right: none; border-bottom: 1px solid var(--border-color); padding: 35px 20px; }
        .dash-value { font-size: 2.8rem; }
    }
</style>

<div id="CYBER_LOADER"><div class="load-text">DECRYPTING_DATA</div><div class="load-bar-wrap"><div class="load-bar"></div></div></div>

<div id="VIEW_WRAP">
    <canvas id="space-canvas"></canvas>
    <article class="view-container">
        <div id="v_frame">
            <div class="view-header">
                <div style="display:flex; justify-content:space-between; align-items:center;">
                    <div>
                        <h2><i class="fa-solid fa-database"></i> <?php echo $board['bo_subject']; ?></h2>
                        <div class="info-meta">
                            <span style="color:var(--neon-cyan); margin-right:15px;">ID: <?php echo $view['wr_id']; ?></span>
                            <span><i class="fa-regular fa-clock"></i> <?php echo date("Y-m-d H:i", strtotime($view['wr_datetime'])); ?></span>
                            <span style="margin-left:15px;"><i class="fa-regular fa-eye"></i> <?php echo number_format($view['wr_hit']); ?></span>
                        </div>
                    </div>
                </div>
            </div>

            <div class="view-info-bar">
                <?php if($view['ca_name']) { ?><span class="info-tag"><?php echo $view['ca_name']; ?></span><?php } ?>
                <?php if($view['x2_ca2']) { ?><span class="info-tag"><?php echo $view['x2_ca2']; ?></span><?php } ?>
                <?php if($view['x2_ca4']) { ?><span class="info-tag cyan"><?php echo $view['x2_ca4']; ?></span><?php } ?>
                <?php if($view['x2_coin']) { ?><span class="info-tag coin-id"><i class="fa-solid fa-coins"></i> <?php echo $view['x2_coin']; ?></span><?php } ?>
                
                <?php 
                    $stat_text = $is_active ? '가동중' : '정지됨';
                    $stat_class = $is_active ? 'stat-on' : 'stat-off';
                ?>
                <span class="status-badge <?php echo $stat_class; ?>"><?php echo $stat_text; ?></span>
            </div>

            <div class="dashboard-panel">
                <div class="dash-item dash-watch">
                    <div class="dash-label">감시금액</div>
                    <div class="dash-value"><?php echo number_format($view['wr_subject'], 4); ?></div>
                </div>
                
                <div class="dash-item dash-target">
                    <?php if ($is_rate_type) { ?>
                        <div class="dash-label">목표수익률</div>
                        <div class="dash-value"><?php echo $is_active ? number_format((float)$view['x2_rate'], 2) . '%' : '-'; ?></div>
                        <div class="dash-sub">(<?php echo number_format($calc_target_price); ?>)</div>
                    <?php } else { ?>
                        <div class="dash-label">목표금액</div>
                        <div class="dash-value"><?php echo $is_active ? number_format((float)$view['x2_target']) : '-'; ?></div>
                    <?php } ?>
                </div>

                <div class="dash-item dash-realtime">
                    <div class="dash-label">실시간 현재금액</div>
                    <div class="dash-value" id="realtime_upbit_price"><?php echo $is_active ? 'LOADING...' : '-'; ?></div>
                </div>

                <div class="dash-item">
                    <div class="dash-label">변동액</div>
                    <div class="dash-value" id="diff_indicator" style="font-size:1.8rem;">-</div>
                </div>
            </div>

            <div class="dashboard-panel">
                <div class="dash-item">
                    <div class="dash-label">수량</div>
                    <div class="dash-value" style="font-size:1.8rem; color:#fff;"><?php echo $is_active ? number_format((float)$view['x2_qty'], 4) : '-'; ?></div>
                </div>

                <div class="dash-item">
                    <div class="dash-label">배율</div>
                    <div class="dash-value" style="font-size:1.8rem; color:var(--neon-pink);">x<?php echo $is_active ? $view['x2_margin'] : '-'; ?></div>
                </div>

                <div class="dash-item">
                    <div class="dash-label">계정</div>
                    <div class="dash-value" style="font-size:1.2rem; font-family:'Orbitron'; color:var(--electric-blue);"><?php echo $is_active ? $view['x2_account'] : '-'; ?></div>
                </div>
                
                <div class="dash-item">
                    <div class="dash-label">수익금</div>
                    <div class="dash-value" id="pnl_indicator">-</div>
                </div>
            </div>

            <div class="dashboard-panel">
                <div class="dash-item">
                    <div class="dash-label">수익률 (ROE)</div>
                    <div class="dash-value" id="profit_indicator">-</div>
                </div>

                <div class="dash-item">
                    <div class="dash-label">도달률</div>
                    <div class="dash-value" id="progress_percent" style="color:var(--golden-yellow);">-</div>
                </div>

                <div class="dash-item">
                    <div class="dash-label">도달 상태</div>
                    <div style="text-align:center;">
                        <span id="target_status_text" class="status-waiting"><?php echo $is_active ? '분석 중...' : '-'; ?></span>
                        <div class="view-chart-bg">
                            <div id="progress_chart_bar" class="view-chart-bar"></div>
                        </div>
                    </div>
                </div>
            </div>

            <?php if ($view['x2_tag']) { ?>
            <div class="view-tags">
                <span style="font-size:0.75rem; color:var(--text-dim);">태그 : </span><?php $tags = explode(',', $view['x2_tag']); foreach($tags as $t) { $t = trim($t); if($t) echo '<a href="'.G5_BBS_URL.'/board.php?bo_table='.$bo_table.'&amp;stx='.urlencode($t).'&amp;sfl=wr_subject||wr_content" class="tag-link">#'.$t.'</a>'; } ?>
            </div>
            <?php } ?>

            <div class="view-footer">
                <div style="display:flex; gap:8px;">
                    <a href="<?php echo $list_href; ?>" class="btn-action">감시 코인 목록</a>
                    <?php if ($write_href) { ?><a href="<?php echo $write_href; ?>" class="btn-action highlight">새 코인 등록</a><?php } ?>
                </div>
                <div style="display:flex; gap:8px;">
                    <?php if ($update_href) { ?><a href="<?php echo $update_href; ?>" class="btn-action highlight">코인 감시 수정</a><?php } ?>
                    <?php if ($delete_href) { ?><a href="<?php echo $delete_href; ?>" class="btn-action" onclick="return confirm('PURGE DATA FROM SYSTEM?');">삭제</a><?php } ?>
                </div>
            </div>

            <nav class="view-nav">
                <?php if ($prev_href) { ?><a href="<?php echo $prev_href; ?>"><span class="nav-label"><i class="fa-solid fa-angle-left"></i> PREV_SECTOR</span><div class="nav-subject"><?php echo $prev_wr_subject; ?></div></a><?php } ?>
                <?php if ($next_href) { ?><a href="<?php echo $next_href; ?>" style="text-align:right;"><span class="nav-label">NEXT_SECTOR <i class="fa-solid fa-angle-right"></i></span><div class="nav-subject"><?php echo $next_wr_subject; ?></div></a><?php } ?>
            </nav>
        </div>
    </article>
</div>

<script src="<?php echo G5_JS_URL; ?>/viewimageresize.js"></script>
<script>
// ★★★ [오빠가 원하는 보정값 설정] 여기서 숫자를 바꾸세요! ★★★
const ROE_FIX = 1.0; 

window.addEventListener('load', function() { setTimeout(() => { document.getElementById('CYBER_LOADER').style.opacity = '0'; setTimeout(() => document.getElementById('CYBER_LOADER').style.visibility = 'hidden', 500); }, 800); });

function fetchRealtimeData() {
    const isActive = "<?php echo $view['x2_ca3']; ?>" === "1";
    
    if (!isActive) {
        document.getElementById('realtime_upbit_price').innerText = "-";
        document.getElementById('profit_indicator').innerText = "-";
        document.getElementById('pnl_indicator').innerText = "-";
        document.getElementById('diff_indicator').innerText = "-";
        document.getElementById('progress_percent').innerText = "-";
        document.getElementById('target_status_text').innerText = "-";
        return;
    }

    const coinSymbol = "<?php echo $view['x2_coin']; ?>";
    const watchPrice = parseFloat("<?php echo str_replace(',', '', $view['wr_subject']); ?>");
    const targetType = "<?php echo $view['x2_target_type']; ?>";
    const position = "<?php echo $view['x2_ca4']; ?>";
    
    const rawMargin = "<?php echo $view['x2_margin']; ?>";
    const margin = parseFloat(rawMargin.replace(/[^0-9.]/g, '')) || 1;
    const qty = parseFloat("<?php echo $view['x2_qty']; ?>") || 1;

    let targetPrice = 0;
    if (targetType === 'rate') {
        const rate = parseFloat("<?php echo $view['x2_rate']; ?>") / 100;
        if (position === "롱") {
            targetPrice = watchPrice * (1 + (rate / margin));
        } else {
            targetPrice = watchPrice * (1 - (rate / margin));
        }
    } else {
        targetPrice = parseFloat("<?php echo str_replace(',', '', $view['x2_target']); ?>");
    }

    if (!coinSymbol) return;

    const symUpper = coinSymbol.toUpperCase().trim();
    let category = 'linear';
    if (symUpper.endsWith('USD') && !symUpper.endsWith('USDT')) {
        category = 'inverse';
    }

    const randomStr = Math.random().toString(36).substring(7);

    fetch(`https://api.bybit.com/v5/market/tickers?category=${category}&symbol=${encodeURIComponent(symUpper)}&_t=${new Date().getTime()}&_r=${randomStr}`)
        .then(response => response.json())
        .then(json => {
            const list = json && json.result && json.result.list ? json.result.list : [];
            if (list && list[0]) {
                const tradePrice = parseFloat(list[0].lastPrice || list[0].last_price || 0);
                if (!tradePrice) return;

                const formattedPrice = new Intl.NumberFormat('ko-KR').format(tradePrice);
                document.getElementById('realtime_upbit_price').innerText = formattedPrice;

                // [계산 로직]
                const isInverse = (category === 'inverse');
                let roe = 0;
                let pnl = 0;
                let diff = 0;

                const profitBox = document.getElementById('profit_indicator');
                const pnlBox = document.getElementById('pnl_indicator');
                
                if (isInverse) profitBox.classList.add('inverse-mode');
                else profitBox.classList.remove('inverse-mode');

                if (position === "숏") {
                    diff = watchPrice - tradePrice; 
                    if (isInverse) {
                        roe = ((watchPrice / tradePrice) - 1) * 100 * margin * ROE_FIX;
                        pnl = qty * (1 - (tradePrice / watchPrice));
                    } else {
                        roe = ((watchPrice - tradePrice) / watchPrice) * 100 * margin * ROE_FIX;
                        pnl = (watchPrice - tradePrice) * qty;
                    }
                } else {
                    diff = tradePrice - watchPrice; 
                    if (isInverse) {
                        roe = (1 - (watchPrice / tradePrice)) * 100 * margin * ROE_FIX;
                        pnl = qty * ((tradePrice / watchPrice) - 1);
                    } else {
                        roe = ((tradePrice - watchPrice) / watchPrice) * 100 * margin * ROE_FIX;
                        pnl = (tradePrice - watchPrice) * qty;
                    }
                }

                const pClass = roe > 0 ? "p-up" : (roe < 0 ? "p-down" : "");

                // 1. 변동액
                const diffHtml = `<span class="${pClass}">${diff >= 0 ? '+' : ''}${new Intl.NumberFormat('ko-KR').format(diff)}</span>`;
                document.getElementById('diff_indicator').innerHTML = diffHtml;

                // 2. 수익금
                const pnlHtml = `<span class="${pClass}">${pnl >= 0 ? '+' : ''}${new Intl.NumberFormat('ko-KR').format(pnl)}</span>`;
                pnlBox.innerHTML = pnlHtml;

                // 3. 수익률
                const roeHtml = `<span class="${pClass}">${roe.toFixed(2)}%</span>`;
                profitBox.innerHTML = roeHtml;

                // 4. 도달률 & 차트
                let reached = false;
                let progress = 0;
                const totalMove = Math.abs(targetPrice - watchPrice);
                const currentMove = (position === "숏") ? (watchPrice - tradePrice) : (tradePrice - watchPrice);
                
                // ★★★ [도달률 10으로 나누기 패치 적용] ★★★
                if (totalMove > 0) {
                    progress = ((currentMove / totalMove) * 100);
                }
                
                if (position === "롱") reached = tradePrice >= targetPrice;
                else reached = tradePrice <= targetPrice;

                const statusText = document.getElementById('target_status_text');
                const chartBar = document.getElementById('progress_chart_bar');
                const progressText = document.getElementById('progress_percent');

                // 도달률 숫자 표시
                progressText.innerText = progress.toFixed(1) + "%";

                if (reached) {
                    statusText.innerText = "목표 달성!";
                    statusText.className = "status-reached";
                    chartBar.style.width = "100%";
                } else {
                    statusText.innerText = "진행 중";
                    statusText.className = "status-waiting";
                    const barWidth = Math.max(0, Math.min(100, progress));
                    chartBar.style.width = barWidth + "%";
                }
            }
        });
}

$(function() {
    fetchRealtimeData(); 
    setInterval(fetchRealtimeData, 1000);
    $(".view-content img").viewimageresize();
    const canvas = document.getElementById('space-canvas'); const ctx = canvas.getContext('2d'); let stars = [];
    function initSpace() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; stars = []; for(let i=0; i<80; i++) stars.push({ x: Math.random() * canvas.width, y: Math.random() * canvas.height, size: Math.random() * 1.1, speed: Math.random() * 0.15 }); }
    function animateSpace() { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = "rgba(201, 209, 217, 0.3)"; stars.forEach(star => { ctx.beginPath(); ctx.arc(star.x, star.y, star.size, 0, Math.PI*2); ctx.fill(); star.y += star.speed; if(star.y > canvas.height) star.y = 0; }); requestAnimationFrame(animateSpace); }
    window.addEventListener('resize', initSpace); initSpace(); animateSpace();
});
</script>