Track open orders as pending until CTP fill, with cancel and timeout.
Add configurable pending timeout in settings and clearer CTP password save feedback. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+63
-5
@@ -225,6 +225,7 @@
|
||||
}
|
||||
list.innerHTML = rows.map(buildPosCard).join('');
|
||||
bindPendingDismiss(list);
|
||||
bindCancelOpenButtons(list);
|
||||
bindSlTpButtons(list);
|
||||
bindPlaceOrderButtons(list);
|
||||
list.querySelectorAll('[data-close]').forEach(function (btn) {
|
||||
@@ -632,7 +633,10 @@
|
||||
showOrderMsg(data.error || '下单失败', false);
|
||||
return;
|
||||
}
|
||||
var msg = data.message || ('开仓成功 · ' + (data.lots || lots) + ' 手');
|
||||
var msg = data.message || (
|
||||
data.filled ? ('开仓成功 · ' + (data.lots || lots) + ' 手') :
|
||||
('委托已提交 · ' + (data.lots || lots) + ' 手挂单中')
|
||||
);
|
||||
showOrderMsg(msg, true);
|
||||
pollPositions();
|
||||
refreshQuote();
|
||||
@@ -666,14 +670,20 @@
|
||||
return '<div class="pos-pending-orders"><div class="pending-title">止盈止损监控</div>' + rows + '</div>';
|
||||
}
|
||||
|
||||
function dismissMonitor(monitorId, btn) {
|
||||
function dismissMonitor(monitorId, btn, opts) {
|
||||
opts = opts || {};
|
||||
if (!monitorId) return;
|
||||
if (!confirm('取消该本地止盈止损监控?(不影响柜台委托)')) return;
|
||||
var isPending = !!opts.pending;
|
||||
var confirmMsg = isPending
|
||||
? '撤销该开仓委托?(将向柜台发送撤单)'
|
||||
: '取消该本地止盈止损监控?(不影响柜台委托)';
|
||||
if (!confirm(confirmMsg)) return;
|
||||
if (btn) {
|
||||
btn.disabled = true;
|
||||
btn.textContent = '取消中…';
|
||||
}
|
||||
fetch('/api/trading/monitor/dismiss', {
|
||||
var url = isPending ? '/api/trading/monitor/cancel-open' : '/api/trading/monitor/dismiss';
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ monitor_id: monitorId })
|
||||
@@ -687,11 +697,20 @@
|
||||
alert(e.message || '取消失败');
|
||||
if (btn) {
|
||||
btn.disabled = false;
|
||||
btn.textContent = '取消';
|
||||
btn.textContent = isPending ? '撤单' : '取消';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function bindCancelOpenButtons(root) {
|
||||
if (!root) return;
|
||||
root.querySelectorAll('[data-cancel-open]').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
dismissMonitor(parseInt(btn.getAttribute('data-cancel-open'), 10), btn, { pending: true });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function bindPendingDismiss(root) {
|
||||
if (!root) return;
|
||||
root.querySelectorAll('[data-monitor-id]').forEach(function (btn) {
|
||||
@@ -725,7 +744,46 @@
|
||||
return '<span class="text-muted">未开启</span>';
|
||||
}
|
||||
|
||||
function buildPendingOrderCard(row) {
|
||||
var dirBadge = row.direction_label || (row.direction === 'long' ? '做多' : '做空');
|
||||
var openT = (row.open_time || '').replace('T', ' ').slice(0, 16);
|
||||
var orderPx = row.order_price != null ? row.order_price : row.entry_price;
|
||||
var remainMin = row.pending_timeout_min != null
|
||||
? row.pending_timeout_min
|
||||
: (row.auto_cancel_sec != null ? Math.max(1, Math.ceil(row.auto_cancel_sec / 60)) : 5);
|
||||
var cancelBtn = row.can_cancel_order ?
|
||||
'<button type="button" class="pos-close-btn" data-cancel-open="' + row.monitor_id + '">撤单</button>' : '';
|
||||
var metaLine =
|
||||
'状态 <strong class="text-accent">挂单中</strong>' +
|
||||
' · 委托价 <strong>' + fmtNum(orderPx) + '</strong>' +
|
||||
(row.rr_ratio != null ? ' · 盈亏比 <strong>' + row.rr_ratio + ':1</strong>' : '') +
|
||||
' · ' + slTpStatusHtml(row) +
|
||||
' · 移动保本 ' + trailingStatusHtml(row) +
|
||||
' · <span class="text-muted">约 ' + remainMin + ' 分钟内未成交自动撤单</span>';
|
||||
return (
|
||||
'<div class="pos-card is-pending">' +
|
||||
'<div class="pos-card-head"><div><div class="title">' + row.symbol +
|
||||
' <span class="badge dir">' + dirBadge + '</span>' +
|
||||
' <span class="badge pending">挂单中</span></div>' +
|
||||
'<div class="text-muted" style="font-size:.72rem">' + (row.symbol_code || '') + '</div></div>' +
|
||||
'<div class="pos-card-actions">' + cancelBtn + '</div></div>' +
|
||||
'<div class="pos-card-meta pos-card-meta-line">' + metaLine + '</div>' +
|
||||
'<div class="pos-metrics">' +
|
||||
'<div class="cell"><label>委托手数</label><div><strong>' + row.lots + ' 手</strong></div></div>' +
|
||||
'<div class="cell"><label>委托价</label><div>' + fmtNum(orderPx) + '</div></div>' +
|
||||
'<div class="cell"><label>当前价格</label><div>' + (row.current_price != null ? fmtNum(row.current_price) : '--') + '</div></div>' +
|
||||
'<div class="cell pnl-pending"><label>状态</label><div class="text-accent">等待成交</div></div>' +
|
||||
'<div class="cell"><label>提交时间</label><div>' + (openT || '--') + '</div></div>' +
|
||||
'</div>' +
|
||||
buildPendingHtml(row.pending_orders) +
|
||||
'</div>'
|
||||
);
|
||||
}
|
||||
|
||||
function buildPosCard(row) {
|
||||
if (row.order_state === 'pending') {
|
||||
return buildPendingOrderCard(row);
|
||||
}
|
||||
var pnlClass = row.float_pnl > 0 ? 'pnl-pos' : (row.float_pnl < 0 ? 'pnl-neg' : '');
|
||||
var pnlText = row.float_pnl != null ? ((row.float_pnl >= 0 ? '+' : '') + fmtNum(row.float_pnl) + ' 元') : '--';
|
||||
var dirBadge = row.direction_label || (row.direction === 'long' ? '做多' : '做空');
|
||||
|
||||
Reference in New Issue
Block a user