(function () { var symInput = document.getElementById('trade-symbol'); var lotsInput = document.getElementById('trade-lots'); var priceInput = document.getElementById('trade-price'); var footer = document.getElementById('trade-footer'); var slInput = document.getElementById('trade-sl'); var tpInput = document.getElementById('trade-tp'); var debounceTimer; function selectedSymbol() { return (symInput && symInput.value || '').trim(); } function refreshQuote() { var sym = selectedSymbol(); var lots = lotsInput ? lotsInput.value : '1'; if (!sym) return; fetch('/api/trade/quote?symbol=' + encodeURIComponent(sym) + '&lots=' + encodeURIComponent(lots)) .then(function (r) { return r.json(); }) .then(function (data) { if (!data.ok) return; if (priceInput && !priceInput.dataset.manual && data.price) { priceInput.value = data.price; } var px = data.price != null ? data.price : '—'; ['px-long', 'px-short'].forEach(function (id) { var el = document.getElementById(id); if (el) el.textContent = px; }); var ml = document.getElementById('max-long'); var ms = document.getElementById('max-short'); if (ml) ml.textContent = '≤' + (data.max_open_long || '—'); if (ms) ms.textContent = '≤' + (data.max_open_short || '—'); document.getElementById('pos-long').textContent = '≤' + (data.pos_long || 0); document.getElementById('pos-short').textContent = '≤' + (data.pos_short || 0); if (footer && data.metrics) { var m = data.metrics; footer.innerHTML = '

' + (data.name || sym) + ' ' + (data.footer_text || '') + '

' + '

价格精度 ' + m.price_precision + ' 位 · ' + '最小变动 ' + m.tick_size + ' · ' + '每跳 ' + m.tick_value_per_lot + ' 元/手 · ' + '当前 ' + lots + ' 手每跳合计 ' + m.tick_value_total + '

' + (m.margin_total ? '

预估保证金约 ' + m.margin_total + ' 元

' : ''); } }).catch(function () {}); } function scheduleRefresh() { clearTimeout(debounceTimer); debounceTimer = setTimeout(refreshQuote, 400); } if (symInput) symInput.addEventListener('input', scheduleRefresh); if (lotsInput) lotsInput.addEventListener('input', scheduleRefresh); if (priceInput) { priceInput.addEventListener('input', function () { priceInput.dataset.manual = '1'; }); } function postOrder(offset, direction) { var sym = selectedSymbol(); if (!sym) { alert('请选择品种'); return; } var body = { symbol: sym, offset: offset, direction: direction, lots: parseInt(lotsInput.value, 10) || 1, price: parseFloat(priceInput.value) || 0, stop_loss: slInput ? parseFloat(slInput.value) : null, take_profit: tpInput ? parseFloat(tpInput.value) : null }; fetch('/api/trade/order', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }).then(function (r) { return r.json(); }).then(function (data) { if (!data.ok) { alert(data.error || '下单失败'); return; } alert('已提交 ' + (data.lots || '') + ' 手'); location.reload(); }); } var btnLong = document.getElementById('btn-open-long'); var btnShort = document.getElementById('btn-open-short'); var btnCloseL = document.getElementById('btn-close-long'); var btnCloseS = document.getElementById('btn-close-short'); if (btnLong) btnLong.addEventListener('click', function () { postOrder('open', 'long'); }); if (btnShort) btnShort.addEventListener('click', function () { postOrder('open', 'short'); }); if (btnCloseL) btnCloseL.addEventListener('click', function () { postOrder('close', 'long'); }); if (btnCloseS) btnCloseS.addEventListener('click', function () { postOrder('close', 'short'); }); var btnConnect = document.getElementById('btn-ctp-connect'); if (btnConnect) { btnConnect.addEventListener('click', function () { btnConnect.disabled = true; btnConnect.textContent = '连接中…'; fetch('/api/ctp/connect', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' }) .then(function (r) { return r.json(); }) .then(function (d) { if (!d.ok) { alert(d.error || '连接失败'); return; } location.reload(); }) .finally(function () { btnConnect.disabled = false; btnConnect.textContent = '连接 CTP'; }); }); } setInterval(function () { fetch('/api/account_snapshot').then(function (r) { return r.json(); }).then(function (d) { var cap = document.getElementById('cap-display'); if (cap && d.capital != null) cap.textContent = Number(d.capital).toFixed(2); var badge = document.getElementById('risk-badge'); if (badge && d.risk_status) badge.textContent = d.risk_status.status_label; var ctpBadge = document.getElementById('ctp-badge'); if (ctpBadge && d.ctp_status) { ctpBadge.textContent = d.ctp_status.connected ? 'CTP 已连接' : 'CTP 未连接'; ctpBadge.className = 'badge ' + (d.ctp_status.connected ? 'profit' : 'planned'); } }).catch(function () {}); }, 5000); scheduleRefresh(); })();