<?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);
add_stylesheet('<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Noto+Sans+KR:wght@300;400;500;700&family=JetBrains+Mono:wght@400;700&display=swap">', 0);
error_reporting(E_ALL & ~E_NOTICE);
ini_set('display_errors', '0');
$new_board = sql_query("select * from $write_table order by wr_datetime desc limit 1");
$lat_board = sql_fetch_array($new_board);
?>
<style>
@import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css');
: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;
--realtime-blue: #5de2ff;
--golden-yellow: #ffdf00;
color-scheme: dark;
}
#LIST_WRAP { position: relative; min-height: 100vh; background: var(--bg-black); padding: 40px 0; font-family: 'Pretendard', sans-serif; color: #c9d1d9; overflow-x: hidden; }
#starCanvas { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 1; opacity: 0.2; }
#LIST_CONTENT { position: relative; z-index: 2; width: calc(100% - 100px); margin: 0 var(--side-margin); max-width: none; }
.list-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; }
.list-frame::before, .list-frame::after { content: ''; position: absolute; width: 30px; height: 30px; border: 2px solid var(--neon-cyan); z-index: 10; pointer-events: none; }
.list-frame::before { top: -1px; left: -1px; border-radius: 8px 0 0 0; border-right:0; border-bottom:0; }
.list-frame::after { bottom: -1px; right: -1px; border-radius: 0 0 8px 0; border-left:0; border-top:0; }
.Header-Area { display: flex; justify-content: space-between; align-items: center; padding: 25px 35px; border-bottom: 1px solid var(--border-color); background: linear-gradient(90deg, rgba(0, 212, 255, 0.05), transparent); }
.TopTitle p { font-family: 'Orbitron', sans-serif; font-size: 1.4rem; font-weight: 900; margin: 0; color: #fff; letter-spacing: 2px; }
.TopTitle span { display: block; margin-top: 5px; font-size: 0.75rem; color: var(--neon-cyan); letter-spacing: 1px; font-weight: 600; text-transform: uppercase; }
.btn-cyber { background: var(--neon-cyan); color: #000 !important; font-family: 'Orbitron', sans-serif; font-weight: 800; border: none; padding: 10px 25px; cursor: pointer; border-radius: 4px; transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); font-size: 0.8rem; letter-spacing: 1px; }
.btn-cyber:hover { background: var(--neon-pink) !important; color: #fff !important; box-shadow: 0 0 20px var(--neon-pink); transform: translateY(-3px) scale(1.05); z-index: 10; }
.Control-Bar { display: flex; justify-content: space-between; align-items: center; padding: 15px 35px; background: rgba(0, 0, 0, 0.15); border-bottom: 1px solid var(--border-color); }
.Filter-Group { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
button.sub-btn, select { background: var(--input-bg) !important; border: 1px solid var(--border-color) !important; color: var(--text-dim) !important; padding: 7px 12px !important; border-radius: 4px !important; font-size: 0.75rem; cursor: pointer; transition: 0.3s; font-family: 'JetBrains Mono'; outline: none; }
button.sub-btn:hover, select:hover { border-color: var(--neon-cyan) !important; color: #fff !important; box-shadow: 0 0 8px rgba(0, 212, 255, 0.6); background: rgba(0, 212, 255, 0.1) !important; transform: translateY(-2px); }
.List-Table { width: 100%; border-collapse: collapse; table-layout: fixed; }
.List-Table tr { border-bottom: 1px solid var(--border-color); transition: 0.2s; cursor: pointer; }
.List-Table tr:hover { background: rgba(255, 255, 255, 0.05); }
.List-Table th, .List-Table td { padding: 12px 10px; font-size: 0.8rem; vertical-align: middle; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.txt-right { text-align: right !important; }
.txt-center { text-align: center !important; }
.sortable { cursor: pointer; transition: color 0.2s; }
.sortable:hover { color: var(--neon-pink) !important; text-shadow: 0 0 5px var(--neon-pink); }
.price-int { color: var(--price-up); font-weight: 700; font-family: 'JetBrains Mono'; }
.price-decimal { color: #555; font-size: 0.85em; font-family: 'JetBrains Mono'; }
.rt-blue-main { color: var(--realtime-blue); font-weight: 700; font-family: 'JetBrains Mono'; }
.rt-blue-sub { color: #48a9c0; font-size: 0.85em; }
.st_badge { display: inline-block; padding: 4px 12px; border-radius: 3px; font-size: 0.65rem; font-weight: 700; text-transform: uppercase; border: 1px solid var(--border-color); font-family: 'Orbitron'; }
.st_blue { color: var(--neon-cyan); border-color: rgba(0, 212, 255, 0.3); background: rgba(0, 212, 255, 0.05); }
.st_pink { color: var(--neon-pink); border-color: rgba(255, 45, 85, 0.3); background: rgba(255, 45, 85, 0.05); }
.status-btn { display: inline-block; padding: 4px 12px; border-radius: 20px; font-size: 0.65rem; font-weight: 800; color: #000; transition: all 0.3s; }
.bg-on { background: var(--price-up); }
.bg-off { background: var(--text-dim); color: #fff; opacity: 0.5; cursor: not-allowed; }
.bg-off:hover { filter: grayscale(100%); opacity: 0.4; }
.mini-chart-bg { width: 100%; height: 6px; background: rgba(255,255,255,0.05); border-radius: 3px; overflow: hidden; position: relative; }
.mini-chart-bar { height: 100%; background: var(--neon-cyan); transition: width 0.3s ease; }
.target-dot { position: absolute; top: 0; right: 0; width: 2px; height: 100%; background: var(--golden-yellow); box-shadow: 0 0 5px var(--golden-yellow); }
/* 하단 요약 섹션 스타일 */
.Summary-Section { display: flex; flex-wrap: wrap; justify-content: space-around; align-items: center; padding: 25px 35px; background: rgba(0, 0, 0, 0.4); border-top: 1px solid var(--border-color); font-family: 'Orbitron'; gap: 20px; }
.Summary-Item { text-align: center; min-width: 120px; }
.Summary-Label { font-size: 0.65rem; color: var(--text-dim); margin-bottom: 7px; letter-spacing: 1px; font-weight: 700; }
.Summary-Value { font-size: 1.1rem; font-weight: 800; color: #fff; }
.Summary-Value span { font-family: 'JetBrains Mono'; }
.pos-ratio-wrap { display: flex; gap: 15px; font-size: 0.75rem; font-weight: 700; }
.ratio-long { color: var(--neon-cyan); }
.ratio-short { color: var(--neon-pink); }
.cyber-modal-overlay { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.85); z-index: 99999; align-items: center; justify-content: center; }
.cyber-modal { display: block; position: relative; width: 90%; max-width: 450px; background: var(--panel-bg); padding: 30px; border: 1px solid var(--neon-cyan); z-index: 100000; box-shadow: 0 20px 50px rgba(0,0,0,0.8); border-radius: 8px; }
.modal-header { font-family: 'Orbitron'; color: var(--neon-cyan); font-size: 1rem; margin-bottom: 20px; border-bottom: 1px solid var(--border-color); padding-bottom: 10px; }
</style>
<div id="LIST_WRAP">
<canvas id="starCanvas"></canvas>
<article id='LIST_CONTENT'>
<div class="list-frame">
<section class="Header-Area">
<div class="TopTitle"><p><?php echo strtoupper($bo_table); ?></p><span><i class="fa-solid fa-microchip"></i> <?php echo $board["bo_subject"]; ?></span></div>
<div class="Top-Action"><?php if ($write_href) { ?><button type="button" onclick="location.href='<?php echo $write_href; ?>'" class="btn-cyber">새 항목 등록</button><?php } ?></div>
</section>
<div class="Control-Bar">
<div class="Filter-Group">
<button type="button" class="sub-btn" onclick="toggleOverlay('NoticeOverlay', true)"><i class="fa-solid fa-bullhorn"></i> 공지사항</button>
<?php if ($is_category) { ?>
<select onchange="location.href=this.value;">
<option value="<?php echo get_pretty_url($bo_table); ?>">카테고리</option>
<?php $categories = explode('|', $board['bo_category_list']); foreach($categories as $ca) { if(!$ca) continue; echo "<option value='".get_pretty_url($bo_table, '', 'sca='.urlencode($ca))."' ".($sca==$ca?'selected':'').">$ca</option>"; } ?>
</select>
<?php } ?>
<select onchange="location.href=this.value;">
<option value="<?php echo get_pretty_url($bo_table); ?>">타입</option>
<?php $opts1 = explode('|', $board['bo_1']); foreach($opts1 as $opt) { if(!$opt) continue; echo "<option value='".get_pretty_url($bo_table, '', 'sfl=x2_ca2&stx='.urlencode($opt))."' ".($stx==$opt?'selected':'').">$opt</option>"; } ?>
</select>
<select onchange="location.href=this.value;">
<option value="">포지션</option>
<option value="<?php echo get_pretty_url($bo_table, '', 'sfl=x2_ca4&stx=롱'); ?>" <?php echo ($stx=='롱'?'selected':''); ?>>롱</option>
<option value="<?php echo get_pretty_url($bo_table, '', 'sfl=x2_ca4&stx=숏'); ?>" <?php echo ($stx=='숏'?'selected':''); ?>>숏</option>
</select>
<select onchange="location.href=this.value;">
<option value="<?php echo get_pretty_url($bo_table); ?>">배율</option>
<?php
$margin_list = array('1', '2', '5', '10', '20', '25', '50', '100');
foreach($margin_list as $mg) {
echo "<option value='".get_pretty_url($bo_table, '', 'sfl=x2_margin&stx='.$mg)."' ".($stx==$mg && $sfl=='x2_margin'?'selected':'').">x{$mg}</option>";
}
?>
</select>
<select onchange="location.href=this.value;">
<option value="">상태</option>
<option value="<?php echo get_pretty_url($bo_table, '', 'sfl=x2_ca3&stx=1'); ?>" <?php echo ($stx=='1'?'selected':''); ?>>가동</option>
<option value="<?php echo get_pretty_url($bo_table, '', 'sfl=x2_ca3&stx=0'); ?>" <?php echo ($stx=='0'?'selected':''); ?>>중지</option>
</select>
</div>
<div class="Filter-Group">
<button type="button" class="sub-btn" onclick="toggleOverlay('SearchOverlay', true)"><i class="fa-solid fa-search"></i> 검색</button>
<?php if ($is_admin) { ?>
<button type="button" class="sub-btn" onclick="location.href='<?php echo G5_ADMIN_URL; ?>/board_form.php?w=u&bo_table=<?php echo $bo_table; ?>';"><i class="fa-solid fa-gear"></i> 관리자</button>
<?php } ?>
</div>
</div>
<table class="List-Table">
<thead>
<tr style="background: rgba(255,255,255,0.02); color: var(--neon-cyan); font-family: 'Orbitron'; font-size: 0.7rem;">
<th style="width:50px;" class="txt-center">번호</th>
<th style="width:130px;" class="sortable" onclick="doSort('x2_coin')">코인</th>
<th style="width:160px;" class="txt-right sortable" onclick="doSort('trade_price')">현재가</th>
<th style="width:140px;" class="txt-right sortable" onclick="doSort('wr_subject')">감시가</th>
<th style="width:90px;" class="txt-right sortable" onclick="doSort('x2_qty')">수량</th>
<th style="width:110px;" class="txt-right sortable" onclick="doSort('diff')">변동액</th>
<th style="width:110px;" class="txt-right">수익금</th>
<th style="width:90px;" class="txt-right sortable" onclick="doSort('percent')">수익률</th>
<th style="width:90px;" class="txt-right sortable" onclick="doSort('target_rate')">목표율</th>
<th style="width:80px;" class="txt-center">도달여부</th>
<th style="width:80px;" class="txt-right">도달률</th>
<th style="width:110px;" class="txt-center">진행상황</th>
<th style="width:80px;" class="txt-center">분류</th>
<th style="width:110px;" class="txt-center">타입</th>
<th style="width:80px;" class="txt-center">포지션</th>
<th style="width:90px;" class="txt-center">배율</th>
<th style="width:110px;" class="txt-center">계정</th>
<th style="width:100px;" class="txt-center">상태</th>
</tr>
</thead>
<tbody id="coin_list_body">
<?php
for ($i=0; $i<count($list); $i++) {
$isActive = ($list[$i]['x2_ca3'] == '1');
$raw_watch = (float)$list[$i]['wr_subject'];
$raw_qty = (float)$list[$i]['x2_qty'];
if($raw_qty <= 0) $raw_qty = 1;
$targetType = $list[$i]['x2_target_type'];
$raw_goal = (float)$list[$i]['x2_target'];
$raw_rate = (float)$list[$i]['x2_rate'];
$unit = $list[$i]['x2_ver'] ? $list[$i]['x2_ver'] : 'USDT';
$raw_margin_str = $list[$i]['x2_margin'];
$margin = (float)preg_replace("/[^0-9.]/", "", $raw_margin_str);
if($margin < 1) $margin = 1;
$p_watch = number_format($raw_watch, 8, '.', ',');
$t_ex = explode('.', $p_watch);
$disp_watch = "<span class='price-int'>".$t_ex[0]."</span>.<span class='price-decimal'>".$t_ex[1]."</span>";
$status_text = ($list[$i]['x2_ca3'] == '1') ? '가동' : '중지';
$status_class = ($list[$i]['x2_ca3'] == '1') ? 'bg-on' : 'bg-off';
$disp_target_rate = ($targetType == 'rate') ? (int)$raw_rate.'%' : '-';
?>
<tr onclick="location.href='<?php echo $list[$i]['href']; ?>'"
data-coin-name="<?php echo $list[$i]['x2_coin']; ?>"
data-watch-val="<?php echo $raw_watch; ?>"
data-qty="<?php echo $raw_qty; ?>"
data-goal-val="<?php echo $raw_goal; ?>"
data-target-type="<?php echo $targetType; ?>"
data-rate-val="<?php echo $raw_rate; ?>"
data-pos="<?php echo $list[$i]['x2_ca4']; ?>"
data-unit="<?php echo $unit; ?>"
data-margin="<?php echo $margin; ?>"
data-status="<?php echo $list[$i]['x2_ca3']; ?>">
<td class="txt-center" style="font-family:'Orbitron'; opacity:0.5;"><?php echo $list[$i]['num']; ?></td>
<td style="text-align:left;"><span style="color:var(--neon-cyan); font-weight:700; padding-left:20px;"><?php echo $list[$i]['x2_coin']; ?></span> </td>
<td class="txt-right realtime-price-cell" data-coin="<?php echo $list[$i]['x2_coin']; ?>"><?php echo $isActive ? "<span class='rt-blue-main'>LOADING</span>" : '-'; ?></td>
<td class="txt-right"><?php echo $isActive ? $disp_watch : '-'; ?></td>
<td class="txt-right"><?php echo $isActive ? number_format($raw_qty, 4) : '-'; ?></td>
<td class="txt-right profit-diff-cell" style="font-weight:bold;"><?php echo $isActive ? '-' : '-'; ?></td>
<td class="txt-right profit-amount-cell" style="font-weight:bold;"><?php echo $isActive ? '-' : '-'; ?></td>
<td class="txt-right profit-percent-cell" style="font-weight:bold;"><?php echo $isActive ? '-' : '-'; ?></td>
<td class="txt-right" style="font-family:'JetBrains Mono'; color:var(--golden-yellow); font-weight:bold;"><?php echo $isActive ? $disp_target_rate : '-'; ?></td>
<td class="txt-center target-reach-cell" style="font-weight:900;"><?php echo $isActive ? '-' : '-'; ?></td>
<td class="txt-right target-progress-cell" style="font-family:'JetBrains Mono'; color:var(--golden-yellow);"><?php echo $isActive ? '-' : '-'; ?></td>
<td class="txt-center">
<?php if($isActive) { ?>
<div class="mini-chart-bg"><div class="mini-chart-bar" style="width:0%;"></div><div class="target-dot"></div></div>
<?php } else { echo "-"; } ?>
</td>
<td class="txt-center"><span style="font-size:0.75rem;"><?php echo $isActive ? $list[$i]['ca_name'] : '-'; ?></span></td>
<td class="txt-center"><?php echo $isActive ? "<span class='st_badge st_blue'>".$list[$i]['x2_ca2']."</span>" : '-'; ?></td>
<td class="txt-center"><?php echo $isActive ? "<span class='st_badge ".($list[$i]['x2_ca4']=='롱'?'st_blue':'st_pink')."'>".$list[$i]['x2_ca4']."</span>" : '-'; ?></td>
<td class="txt-center"><?php if($isActive) { echo "<span class='st_badge st_pink'>x{$margin}</span>"; } else { echo '-'; } ?></td>
<td class="txt-center"><?php if($isActive) { echo "<span class='st_badge st_blue' style='font-size:0.6rem;'>".$list[$i]['x2_account']."</span>"; } else { echo '-'; } ?></td>
<td class="txt-center"><span class="status-btn <?php echo $status_class; ?>"><?php echo $status_text; ?></span></td>
</tr>
<?php } ?>
</tbody>
</table>
<section class="Summary-Section">
<div class="Summary-Item">
<div class="Summary-Label">총 투자금액</div>
<div class="Summary-Value"><span id="total_invest_val">0.00</span> <small style="font-size:0.6rem; color:var(--text-dim);">USDT</small></div>
</div>
<div class="Summary-Item">
<div class="Summary-Label">총 수익금액</div>
<div class="Summary-Value"><span id="total_profit_val">0.00</span> <small style="font-size:0.6rem; color:var(--text-dim);">USDT</small></div>
</div>
<div class="Summary-Item">
<div class="Summary-Label">평균 수익률</div>
<div class="Summary-Value"><span id="total_roe_val">0.00%</span></div>
</div>
<div class="Summary-Item">
<div class="Summary-Label">롱/숏 비율</div>
<div class="pos-ratio-wrap">
<div class="ratio-long">롱: <span id="long_count">0</span> (<span id="long_ratio">0</span>%)</div>
<div class="ratio-short">숏: <span id="short_count">0</span> (<span id="short_ratio">0</span>%)</div>
</div>
</div>
<div class="Summary-Item">
<div class="Summary-Label">평균 레버리지</div>
<div class="Summary-Value" style="color:var(--neon-pink);">x<span id="avg_leverage">0.0</span></div>
</div>
</section>
</div>
<div class="Pagination"><?php echo $write_pages; ?></div>
</article>
</div>
<div id="SearchOverlay" class="cyber-modal-overlay">
<div class="cyber-modal">
<form name="fsearch" method="get" action="<?php echo G5_BBS_URL; ?>/board.php">
<input type="hidden" name="bo_table" value="<?php echo $bo_table; ?>">
<input type="hidden" name="sca" value="<?php echo $sca; ?>">
<input type="hidden" name="sfl" value="wr_subject||wr_content">
<input type="text" name="stx" value="<?php echo stripslashes($stx); ?>" required placeholder="검색어 입력..." style="width:100%; background:var(--input-bg); border:1px solid var(--border-color); color:#fff; font-size:1rem; outline:none; padding:12px; border-radius:4px;">
<div style="margin-top:25px; display:flex; gap:10px;">
<button type="submit" class="btn-cyber" style="flex:1;">실행</button>
<button type="button" onclick="toggleOverlay('SearchOverlay', false)" class="sub-btn" style="flex:1;">닫기</button>
</div>
</form>
</div>
</div>
<div id="NoticeOverlay" class="cyber-modal-overlay">
<div class="cyber-modal">
<div class="modal-header">SYSTEM_NOTICE</div>
<div style="font-size:0.85rem; line-height:1.6; color:#c9d1d9; max-height:300px; overflow-y:auto; margin-bottom:25px;">
<?php echo nl2br(stripslashes($board['notice'])); ?>
<?php if(!$board['notice']) echo "등록된 공지사항이 없습니다."; ?>
</div>
<div style="text-align:right; display:flex; gap:10px; justify-content: flex-end;">
<button type="button" onclick="open_notice_window('<?php echo $board_skin_url; ?>');" class="btn-cyber">입력하기</button>
<button type="button" onclick="toggleOverlay('NoticeOverlay', false)" class="sub-btn">닫기</button>
</div>
</div>
</div>
<script>
const ROE_FIX = 1.31;
function toggleOverlay(id, show) { if (show) { $('#' + id).css('display', 'flex').hide().fadeIn(200); } else { $('#' + id).fadeOut(200); } }
function formatNumber(num) {
if (num === undefined || num === null || isNaN(num)) return '-';
const abs = Math.abs(num);
if (abs === 0) return '0.00';
if (abs < 0.0001) return num.toFixed(8);
if (abs < 1) return num.toFixed(6);
if (abs < 10) return num.toFixed(4);
return num.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
function fetchMultiBybitPrice() {
const rows = document.querySelectorAll('#coin_list_body tr');
let totalInvest = 0;
let totalProfit = 0;
let activeCount = 0;
let longCount = 0;
let shortCount = 0;
let totalLeverage = 0;
const tasks = [];
rows.forEach(row => {
if (row.dataset.status !== '1') return;
const sym = (row.dataset.coinName || '').trim().toUpperCase();
if (!sym) return;
let category = 'linear';
if (sym.endsWith('USD') && !sym.endsWith('USDT')) {
category = 'inverse';
}
const randomStr = Math.random().toString(36).substring(7);
const url = `https://api.bybit.com/v5/market/tickers?category=${category}&symbol=${encodeURIComponent(sym)}&_t=${new Date().getTime()}&_r=${randomStr}`;
tasks.push(
fetch(url, { cache: 'no-store' })
.then(res => res.json())
.then(json => {
const list = json?.result?.list || [];
if (!list.length) return null;
return {
symbol: list[0].symbol.toUpperCase(),
last: parseFloat(list[0].lastPrice || 0)
};
}).catch(() => null)
);
});
if (!tasks.length) return;
Promise.all(tasks).then(results => {
const map = {};
results.forEach(r => { if (r) map[r.symbol] = r; });
rows.forEach(row => {
if (row.dataset.status !== '1') return;
const sym = row.dataset.coinName.trim().toUpperCase();
if (map[sym] === undefined) return;
const lastPrice = map[sym].last;
const watch = parseFloat(row.dataset.watchVal || '0');
const qty = parseFloat(row.dataset.qty || '1');
const pos = row.dataset.pos || '롱';
const margin = parseFloat(row.dataset.margin || '1');
const isInverse = sym.endsWith('USD') && !sym.endsWith('USDT');
const pCell = row.querySelector('.realtime-price-cell');
const diffCell = row.querySelector('.profit-diff-cell');
const amountCell = row.querySelector('.profit-amount-cell');
const percentCell = row.querySelector('.profit-percent-cell');
const reachCell = row.querySelector('.target-reach-cell');
const progCell = row.querySelector('.target-progress-cell');
const bar = row.querySelector('.mini-chart-bar');
const pStr = lastPrice.toFixed(8);
const pParts = pStr.split('.');
pCell.innerHTML = `<span class='rt-blue-main'>${parseFloat(pParts[0]).toLocaleString()}</span>.<span class='rt-blue-sub'>${pParts[1] || '00000000'}</span>`;
let diff = 0;
let pnl = 0;
let roe = 0;
if (watch > 0 && lastPrice > 0) {
totalInvest += (watch * qty);
totalLeverage += margin;
if (pos === '숏') {
shortCount++;
diff = watch - lastPrice;
if (isInverse) {
roe = ((watch / lastPrice) - 1) * 100 * margin * ROE_FIX;
pnl = qty * (1 - (lastPrice / watch));
} else {
roe = ((watch - lastPrice) / watch) * 100 * margin * ROE_FIX;
pnl = (watch - lastPrice) * qty;
}
} else {
longCount++;
diff = lastPrice - watch;
if (isInverse) {
roe = (1 - (watch / lastPrice)) * 100 * margin * ROE_FIX;
pnl = qty * ((lastPrice / watch) - 1);
} else {
roe = ((lastPrice - watch) / watch) * 100 * margin * ROE_FIX;
pnl = (lastPrice - watch) * qty;
}
}
totalProfit += pnl;
activeCount++;
}
const color = roe >= 0 ? 'var(--price-up)' : 'var(--price-down)';
diffCell.innerHTML = `<span style="color:${color}">${diff >= 0 ? '+' : ''}${formatNumber(diff)}</span>`;
amountCell.innerHTML = `<span style="color:${color}">${pnl >= 0 ? '+' : ''}${formatNumber(pnl)}</span>`;
percentCell.innerHTML = `<span style="color:${color}">${roe >= 0 ? '+' : ''}${roe.toFixed(2)}%</span>`;
let goalPrice = 0;
if (row.dataset.targetType === 'rate') {
const rVal = parseFloat(row.dataset.rateVal) || 0;
goalPrice = (pos === '숏') ? watch * (1 - (rVal / margin / 100)) : watch * (1 + (rVal / margin / 100));
} else {
goalPrice = parseFloat(row.dataset.goalVal) || 0;
}
const reached = (pos === '숏') ? (lastPrice <= goalPrice) : (lastPrice >= goalPrice);
reachCell.innerHTML = `<span class="status-btn ${reached ? 'bg-on' : 'bg-off'}" style="${!reached ? 'background:#333;' : ''}">${reached ? '달성' : '진행'}</span>`;
if (watch > 0 && goalPrice > 0 && bar) {
let progress = 0;
const totalMove = Math.abs(goalPrice - watch);
const currentMove = (pos === '숏') ? (watch - lastPrice) : (lastPrice - watch);
if (totalMove > 0) progress = (currentMove / totalMove) * 100;
const barWidth = Math.max(0, Math.min(100, progress));
bar.style.width = barWidth + '%';
progCell.innerText = progress.toFixed(1) + '%';
}
});
document.getElementById('total_invest_val').innerText = totalInvest.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
const profColor = totalProfit >= 0 ? 'var(--price-up)' : 'var(--price-down)';
document.getElementById('total_profit_val').innerText = (totalProfit >= 0 ? '+' : '') + totalProfit.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
document.getElementById('total_profit_val').style.color = profColor;
const totalRoe = totalInvest > 0 ? (totalProfit / totalInvest) * 100 : 0;
document.getElementById('total_roe_val').innerText = (totalRoe >= 0 ? '+' : '') + totalRoe.toFixed(2) + '%';
document.getElementById('total_roe_val').style.color = profColor;
document.getElementById('long_count').innerText = longCount;
document.getElementById('short_count').innerText = shortCount;
const lRatio = activeCount > 0 ? (longCount / activeCount * 100).toFixed(0) : 0;
const sRatio = activeCount > 0 ? (shortCount / activeCount * 100).toFixed(0) : 0;
document.getElementById('long_ratio').innerText = lRatio;
document.getElementById('short_ratio').innerText = sRatio;
const avgLev = activeCount > 0 ? (totalLeverage / activeCount).toFixed(1) : "0.0";
document.getElementById('avg_leverage').innerText = avgLev;
});
}
function doSort(key) {
const tbody = document.getElementById('coin_list_body');
const rows = Array.from(tbody.querySelectorAll('tr'));
const isAsc = tbody.dataset.sortOrder !== 'asc';
rows.sort((a, b) => {
let valA, valB;
if (key === 'x2_coin') { valA = a.dataset.coinName; valB = b.dataset.coinName; }
else if (key === 'wr_subject') { valA = parseFloat(a.dataset.watchVal); valB = parseFloat(b.dataset.watchVal); }
else if (key === 'x2_qty') { valA = parseFloat(a.dataset.qty); valB = parseFloat(b.dataset.qty); }
else if (key === 'target_rate') { valA = parseFloat(a.dataset.rateVal); valB = parseFloat(b.dataset.rateVal); }
else if (key === 'trade_price') {
const aPrice = a.querySelector('.rt-blue-main');
const bPrice = b.querySelector('.rt-blue-main');
valA = aPrice ? parseFloat(aPrice.innerText.replace(/,/g, '')) : -1;
valB = bPrice ? parseFloat(bPrice.innerText.replace(/,/g, '')) : -1;
}
else if (key === 'diff') {
const aDiff = a.querySelector('.profit-diff-cell span');
const bDiff = b.querySelector('.profit-diff-cell span');
valA = aDiff ? parseFloat(aDiff.innerText.replace(/[+,]/g, '')) : -999999;
valB = bDiff ? parseFloat(bDiff.innerText.replace(/[+,]/g, '')) : -999999;
}
else if (key === 'percent') {
const aPct = a.querySelector('.profit-percent-cell span');
const bPct = b.querySelector('.profit-percent-cell span');
valA = aPct ? parseFloat(aPct.innerText.replace(/[+%,]/g, '')) : -999999;
valB = bPct ? parseFloat(bPct.innerText.replace(/[+%,]/g, '')) : -999999;
}
if (valA < valB) return isAsc ? -1 : 1;
if (valA > valB) return isAsc ? 1 : -1;
return 0;
});
tbody.dataset.sortOrder = isAsc ? 'asc' : 'desc';
rows.forEach(row => tbody.appendChild(row));
}
function open_notice_window(skinUrl) { window.open(skinUrl + '/notice.php', 'notice_win', 'width=800,height=600,left='+(screen.width/2-400)+',top='+(screen.height/2-300)+',scrollbars=yes'); }
$(function() {
fetchMultiBybitPrice(); setInterval(fetchMultiBybitPrice, 2000);
const canvas = document.getElementById('starCanvas'); const ctx = canvas.getContext('2d'); let stars = [];
function initStars() { 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.2, speed: Math.random() * 0.15 }); }
function drawStars() { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = "rgba(201, 209, 217, 0.3)"; stars.forEach(s => { ctx.beginPath(); ctx.arc(s.x, s.y, s.size, 0, Math.PI*2); ctx.fill(); s.y += s.speed; if(s.y > canvas.height) s.y = 0; }); requestAnimationFrame(drawStars); }
initStars(); drawStars(); window.addEventListener('resize', initStars);
$('.cyber-modal-overlay').on('click', function(e) { if ($(e.target).hasClass('cyber-modal-overlay')) toggleOverlay(e.target.id, false); });
});
</script>