(function () { var keyTimer = null; var posTimer = null; function fmtDist(v) { if (v === null || v === undefined) return '--'; return Number(v).toFixed(2); } function fmtNum(v, digits) { if (v === null || v === undefined) return '--'; return Number(v).toFixed(digits === undefined ? 2 : digits); } function pollKeyPrices() { var list = document.getElementById('key-monitor-list'); if (!list || !list.querySelector('.key-item')) return; fetch('/api/key_prices') .then(function (r) { return r.json(); }) .then(function (rows) { rows.forEach(function (row) { var el = list.querySelector('.key-item[data-key-id="' + row.id + '"]'); if (!el) return; var priceEl = el.querySelector('.live-price'); var upEl = el.querySelector('.dist-up'); var downEl = el.querySelector('.dist-down'); if (priceEl) priceEl.textContent = row.price != null ? row.price : '--'; if (upEl) upEl.textContent = fmtDist(row.dist_upper); if (downEl) downEl.textContent = fmtDist(row.dist_lower); }); }) .catch(function () { /* ignore */ }); } function buildPosCard(row) { var pnlClass = ''; if (row.float_pnl > 0) pnlClass = 'pnl-pos'; if (row.float_pnl < 0) pnlClass = 'pnl-neg'; var pnlText = '--'; if (row.float_pnl != null) { var sign = row.float_pnl >= 0 ? '+' : ''; pnlText = sign + fmtNum(row.float_pnl) + '元'; if (row.float_pct != null) { pnlText += ' (' + sign + fmtNum(row.float_pct) + '%)'; } } var rr = row.rr_ratio != null ? row.rr_ratio + ':1' : '--'; var openT = (row.open_time || '').replace('T', ' ').slice(0, 16); return ( '
' + '
' + '
' + row.symbol + ' ' + row.direction + '
' + '
' + '
' + '
' + '
来源 手动输入 · 风险 ' + fmtNum(row.risk_pct) + '%≈' + fmtNum(row.risk_amount) + '元
' + '
' + '
' + fmtNum(row.entry_price) + '
' + '
' + fmtNum(row.stop_loss) + '
' + '
' + fmtNum(row.take_profit) + '
' + '
' + rr + '
' + '
' + (row.mark_price != null ? fmtNum(row.mark_price) : '--') + '
' + '
' + pnlText + '
' + '
' + '
' ); } function pollPositions() { var list = document.getElementById('position-live-list'); if (!list) return; fetch('/api/position_live') .then(function (r) { return r.json(); }) .then(function (rows) { if (!rows.length) { list.innerHTML = '
暂无持仓,左侧录入后显示
'; return; } list.innerHTML = rows.map(buildPosCard).join(''); }) .catch(function () { /* ignore */ }); } function startPolling() { if (keyTimer) clearInterval(keyTimer); if (posTimer) clearInterval(posTimer); pollKeyPrices(); pollPositions(); keyTimer = setInterval(pollKeyPrices, 1000); posTimer = setInterval(pollPositions, 1000); } document.addEventListener('DOMContentLoaded', startPolling); })();