From de74ffe5b9403f0d32df1d8a46c466db5dff501b Mon Sep 17 00:00:00 2001 From: dekun Date: Thu, 25 Jun 2026 15:41:35 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=8C=81=E4=BB=93=E5=8D=A1=E7=89=87?= =?UTF-8?q?=E5=B8=83=E5=B1=80=E4=BC=98=E5=8C=96=E3=80=81=E9=97=AD=E7=9B=98?= =?UTF-8?q?=E7=A6=81=E7=94=A8=E5=B9=B3=E4=BB=93=E3=80=81=E5=9B=BA=E5=AE=9A?= =?UTF-8?q?=E9=87=91=E9=A2=9Dstep=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- install_trading.py | 2 + static/css/trade.css | 4 +- static/js/trade.js | 84 ++++++++++++++++++++++++----------------- templates/settings.html | 2 +- 4 files changed, 56 insertions(+), 36 deletions(-) diff --git a/install_trading.py b/install_trading.py index 0a48714..2e350d5 100644 --- a/install_trading.py +++ b/install_trading.py @@ -644,6 +644,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se "est_fee_open": fee_info["open_fee"], "est_fee_close": fee_info["close_fee"], "est_fee_close_type": fee_info["close_type"], + "fee_source": fee_info.get("fee_source") or "local", "est_pnl_net": est_net, "sl_order_active": order_st.get("sl_monitoring"), "tp_order_active": order_st.get("tp_monitoring"), @@ -654,6 +655,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se "price_precision": tick.get("price_precision"), "tick_size": tick.get("tick_size"), "can_close": True, + "close_allowed": is_trading_session(), "pending_orders": pending_for_row, "trailing_be": bool(mon.get("trailing_be")) if mon else False, "trailing_r_locked": int(mon.get("trailing_r_locked") or 0) if mon else 0, diff --git a/static/css/trade.css b/static/css/trade.css index da822c7..753d395 100644 --- a/static/css/trade.css +++ b/static/css/trade.css @@ -57,7 +57,9 @@ .pos-pending-item.tp{border-left:3px solid var(--profit)} .pos-pending-item.ctp{border-left:3px solid var(--accent)} .pos-close-btn{padding:.4rem .85rem;font-size:.78rem;border-radius:8px;border:1px solid var(--loss);background:var(--loss-bg);color:var(--loss);cursor:pointer;white-space:nowrap;width:auto;flex-shrink:0;min-height:36px} -.pos-close-btn:disabled{opacity:.55;cursor:wait} +.pos-close-btn:disabled,.pos-close-btn.is-session-off{opacity:.45;cursor:not-allowed;border-color:var(--text-muted);background:var(--card-inner);color:var(--text-muted)} +.pos-card-meta-line{font-size:.78rem;line-height:1.65;color:var(--text-muted);margin-bottom:.55rem} +.pos-card-meta-line strong{color:var(--text)} .pos-card-actions{display:flex;gap:.35rem;flex-shrink:0;align-items:center} .pos-order-btn{padding:.4rem .85rem;font-size:.78rem;border-radius:8px;border:1px solid var(--accent);background:rgba(56,189,248,.1);color:var(--accent);cursor:pointer;white-space:nowrap;width:auto;flex-shrink:0;min-height:36px} .pos-order-btn:disabled,.pos-order-btn.pos-order-done{opacity:.55;cursor:default;border-color:var(--table-border);background:var(--card-inner);color:var(--text-muted)} diff --git a/static/js/trade.js b/static/js/trade.js index 48ac8cb..315c178 100644 --- a/static/js/trade.js +++ b/static/js/trade.js @@ -623,12 +623,36 @@ }); } + function slTpStatusHtml(row) { + var parts = []; + if (row.sl_order_active || row.sl_monitoring) { + parts.push('止损监控中'); + } else if (row.stop_loss != null) { + parts.push('止损已设'); + } + if (row.tp_order_active || row.tp_monitoring) { + parts.push('止盈监控中'); + } else if (row.take_profit != null) { + parts.push('止盈已设'); + } + if (!parts.length) return '未设置'; + return parts.join(' · '); + } + + function trailingStatusHtml(row) { + if (row.trailing_be) { + return '已开启' + + (row.trailing_r_locked ? '(锁' + row.trailing_r_locked + 'R)' : '') + ''; + } + return '未开启'; + } + function buildPosCard(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 netClass = row.est_pnl_net > 0 ? 'pnl-pos' : (row.est_pnl_net < 0 ? 'pnl-neg' : ''); var dirBadge = row.direction_label || (row.direction === 'long' ? '做多' : '做空'); var openT = (row.open_time || '').replace('T', ' ').slice(0, 16); + var closeAllowed = row.close_allowed !== false && isTradingSession; var slTpBtn = (!row.stop_loss && !row.take_profit && row.can_close) ? '' : ''; + '' : ''; var actionBtns = (entrustBtn || orderBtn || closeBtn) ? '
' + entrustBtn + orderBtn + closeBtn + '
' : ''; - var riskMeta = ''; - if (row.rr_ratio != null) { - riskMeta += ' · 盈亏比 ' + row.rr_ratio + ':1'; - } - if (row.risk_amount != null) { - riskMeta += ' · 亏损额度 ' + fmtNum(row.risk_amount) + ' 元'; - } + var metaLine = + '来源 ' + (row.source_label || 'CTP') + '' + + (row.rr_ratio != null ? ' · 盈亏比 ' + row.rr_ratio + ':1' : '') + + ' · 止损 ' + (row.stop_loss != null ? fmtNum(row.stop_loss) : '--') + '' + + ' · 止盈 ' + (row.take_profit != null ? fmtNum(row.take_profit) : '--') + '' + + ' · ' + slTpStatusHtml(row) + + ' · 移动保本 ' + trailingStatusHtml(row) + + (slTpBtn ? ' · ' + slTpBtn : '') + + (row.sync_pending ? ' · 同步柜台中…' : ''); + var feeLabel = row.fee_source === 'ctp' ? '手续费(柜台)' : '手续费'; + var marginLabel = row.margin_source === 'ctp' ? '占用保证金(柜台)' : '占用保证金'; return ( '
' + '
' + row.symbol + ' ' + dirBadge + '
' + '
' + (row.symbol_code || '') + '
' + actionBtns + '
' + - '
来源 ' + (row.source_label || 'CTP') + '' + riskMeta + - (row.sync_pending ? ' · 同步柜台中…' : '') + - ' · 浮盈' + - (slTpBtn ? ' · ' + slTpBtn : '') + - (row.sl_order_active ? ' · 止损监控中' : '') + - (row.tp_order_active ? ' · 止盈监控中' : '') + '
' + + '
' + metaLine + '
' + '
' + - '
' + - (row.trailing_be ? - '已开启' + (row.trailing_r_locked ? '(锁' + row.trailing_r_locked + 'R)' : '') + '' : - '未开启') + - '
' + - '
' + fmtNum(row.entry_price) + '
' + + '
' + row.lots + ' 手 @ ' + fmtNum(row.entry_price) + '
' + '
' + (row.current_price != null ? fmtNum(row.current_price) : '--') + '
' + - '
' + (row.stop_loss != null ? fmtNum(row.stop_loss) : '--') + '
' + - '
' + (row.take_profit != null ? fmtNum(row.take_profit) : '--') + '
' + - '
' + (row.margin != null ? fmtNum(row.margin) + ' 元' : '--') + '
' + + '
' + (row.margin != null ? fmtNum(row.margin) + ' 元' : '--') + '
' + '
' + (row.position_pct != null ? fmtNum(row.position_pct) + '%' : '--') + '
' + '
' + pnlText + '
' + - '
' + (row.est_fee != null ? fmtNum(row.est_fee) + ' 元' : '--') + '
' + - '
' + - (row.est_pnl_net != null ? fmtNum(row.est_pnl_net) + ' 元' : '--') + '
' + + '
' + (row.est_fee != null ? fmtNum(row.est_fee) + ' 元' : '--') + '
' + + '
' + (openT || '--') + '
' + + '
' + (row.holding_duration || '--') + '
' + '
' + buildPendingHtml(row.pending_orders) + - '
' + '' ); } @@ -811,6 +823,10 @@ } function closePosition(payload, btn) { + if (!isTradingSession) { + alert('不在交易时间段,无法平仓'); + return; + } function doClose(price) { if (!price || price <= 0) { alert('无法获取现价'); return; } if (!confirm('确认平仓 ' + payload.lots + ' 手?')) return; diff --git a/templates/settings.html b/templates/settings.html index eb4c7e0..6d8a0bb 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -65,7 +65,7 @@
- +