diff --git a/app.py b/app.py index db4f1a7..1bf39b1 100644 --- a/app.py +++ b/app.py @@ -286,6 +286,8 @@ def init_db(): "ALTER TABLE review_records ADD COLUMN sina_code TEXT", "ALTER TABLE trade_logs ADD COLUMN fee REAL", "ALTER TABLE trade_logs ADD COLUMN pnl_net REAL", + "ALTER TABLE trade_logs ADD COLUMN margin_pct REAL", + "ALTER TABLE trade_logs ADD COLUMN equity_after REAL", "ALTER TABLE review_records ADD COLUMN fee REAL", "ALTER TABLE review_records ADD COLUMN pnl_net REAL", ] @@ -1082,16 +1084,21 @@ def close_position(pid): pnl_net = round(pnl - fee, 2) result = classify_close_result(direction, close_price, sl, tp) minutes = holding_to_minutes(open_time, close_time) + margin_pct = metrics.get("position_pct") + from trade_log_lib import calc_equity_after + equity_after = calc_equity_after(capital, pnl_net) conn.execute( """INSERT INTO trade_logs (symbol, symbol_name, market_code, sina_code, monitor_type, direction, entry_price, stop_loss, take_profit, close_price, lots, margin, - holding_minutes, open_time, close_time, pnl, fee, pnl_net, result) - VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""", + margin_pct, holding_minutes, open_time, close_time, pnl, fee, pnl_net, + equity_after, result) + VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""", ( sym, row["symbol_name"], market, sina, "持仓监控", direction, entry, sl, tp, close_price, lots, metrics["margin"], - minutes, open_time, close_time, pnl, fee, pnl_net, result, + margin_pct, + minutes, open_time, close_time, pnl, fee, pnl_net, equity_after, result, ), ) conn.execute("DELETE FROM position_monitors WHERE id=?", (pid,)) @@ -1226,6 +1233,15 @@ def records(): trade_list = conn.execute( "SELECT * FROM trade_logs ORDER BY id DESC LIMIT 500" ).fetchall() + from trade_log_lib import enrich_trades_for_records + try: + initial_capital = float(get_setting("live_capital", "0") or 0) + except (TypeError, ValueError): + initial_capital = 0.0 + trades, equity_curve = enrich_trades_for_records( + [dict(r) for r in trade_list], + initial_capital=initial_capital, + ) conn.close() trade_prefill_keys = ( @@ -1238,7 +1254,8 @@ def records(): return render_template( "records.html", reviews=review_list, - trades=trade_list, + trades=trades, + equity_curve=equity_curve, auto_records=auto_list, preset=preset, start=start, diff --git a/sl_tp_guard.py b/sl_tp_guard.py index 5ece4ec..4f71dd7 100644 --- a/sl_tp_guard.py +++ b/sl_tp_guard.py @@ -11,6 +11,7 @@ from zoneinfo import ZoneInfo from contract_specs import calc_position_metrics from ctp_symbol import ths_to_vnpy_symbol from fee_specs import calc_round_trip_fee +from trade_log_lib import calc_equity_after from market_sessions import is_trading_session from symbols import ths_to_codes from vnpy_bridge import ( @@ -220,6 +221,8 @@ def write_trade_log( sym, entry, close_price, lots, open_time, close_time, trading_mode=trading_mode, ) pnl_net = round(pnl - fee, 2) + margin_pct = metrics.get("position_pct") + equity_after = calc_equity_after(capital, pnl_net) try: from app import holding_to_minutes @@ -231,8 +234,9 @@ def write_trade_log( """INSERT INTO trade_logs (symbol, symbol_name, market_code, sina_code, monitor_type, direction, entry_price, stop_loss, take_profit, close_price, lots, margin, - holding_minutes, open_time, close_time, pnl, fee, pnl_net, result) - VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""", + margin_pct, holding_minutes, open_time, close_time, pnl, fee, pnl_net, + equity_after, result) + VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""", ( sym, symbol_name, @@ -246,12 +250,14 @@ def write_trade_log( close_price, lots, metrics.get("margin"), + margin_pct, minutes, open_time, close_time, pnl, fee, pnl_net, + equity_after, result if result in TRADE_RESULTS else "手动平仓", ), ) diff --git a/static/js/equity_curve.js b/static/js/equity_curve.js new file mode 100644 index 0000000..09c4e77 --- /dev/null +++ b/static/js/equity_curve.js @@ -0,0 +1,61 @@ +(function () { + var el = document.getElementById('equity-curve-chart'); + var raw = window.__EQUITY_CURVE__; + if (!el || !raw || !raw.length || !window.LightweightCharts) return; + + function parseTime(s) { + if (!s) return null; + var t = String(s).trim().replace(' ', 'T'); + if (t.length === 16) t += ':00'; + var d = new Date(t); + if (isNaN(d.getTime())) return null; + return Math.floor(d.getTime() / 1000); + } + + var data = []; + var lastTs = 0; + raw.forEach(function (p) { + var ts = parseTime(p.time); + if (ts == null) return; + if (ts <= lastTs) ts = lastTs + 1; + lastTs = ts; + data.push({ time: ts, value: Number(p.value) }); + }); + if (!data.length) { + el.innerHTML = '
暂无资金曲线数据
'; + return; + } + + var c = { + bg: '#1a1d24', + text: '#9ca3af', + grid: '#2d3139', + line: '#6366f1', + }; + var chart = LightweightCharts.createChart(el, { + width: el.clientWidth || 800, + height: 220, + layout: { + background: { type: 'solid', color: c.bg }, + textColor: c.text, + fontSize: 11, + }, + grid: { + vertLines: { color: c.grid }, + horzLines: { color: c.grid }, + }, + rightPriceScale: { borderColor: c.grid }, + timeScale: { borderColor: c.grid, timeVisible: true, secondsVisible: false }, + }); + var series = chart.addLineSeries({ + color: c.line, + lineWidth: 2, + priceFormat: { type: 'price', precision: 2, minMove: 0.01 }, + }); + series.setData(data); + chart.timeScale().fitContent(); + + window.addEventListener('resize', function () { + chart.applyOptions({ width: el.clientWidth || 800 }); + }); +})(); diff --git a/templates/records.html b/templates/records.html index 9bbf536..011d1e8 100644 --- a/templates/records.html +++ b/templates/records.html @@ -1,6 +1,13 @@ {% extends "base.html" %} {% block title %}交易记录与复盘 - 国内期货监控系统{% endblock %} {% block content %} +