Files
qihuo/static/js/trade.js
T
dekun 6e423eebfb 接入 SimNow 模拟盘与期货下单、策略及品种推荐功能。
新增 vnpy CTP 桥接、以损定仓/固定张数、趋势回调与滚仓策略、按资金推荐品种及交易风控;模拟盘走 SimNow,实盘预留期货公司配置。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-24 10:04:37 +08:00

128 lines
6.2 KiB
JavaScript

(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 =
'<p><strong>' + (data.name || sym) + '</strong> ' + (data.footer_text || '') + '</p>' +
'<p>价格精度 <strong>' + m.price_precision + '</strong> 位 · ' +
'最小变动 <strong>' + m.tick_size + '</strong> · ' +
'每跳 <strong>' + m.tick_value_per_lot + '</strong> 元/手 · ' +
'当前 <strong>' + lots + '</strong> 手每跳合计 <strong class="text-accent">' + m.tick_value_total + '</strong> 元</p>' +
(m.margin_total ? '<p class="text-muted">预估保证金约 ' + m.margin_total + ' 元</p>' : '');
}
}).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();
})();