From c8fef65de7b3cf171a4b4f5688477488728db833 Mon Sep 17 00:00:00 2001 From: dekun Date: Tue, 30 Jun 2026 09:14:13 +0800 Subject: [PATCH] Fix float PnL to match displayed entry price and contract multiplier. Co-authored-by: Cursor --- install_trading.py | 81 ++++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/install_trading.py b/install_trading.py index 4dc22ab..74e8aa4 100644 --- a/install_trading.py +++ b/install_trading.py @@ -990,14 +990,16 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se lots = int(p.get("lots") or 0) entry = float(p.get("avg_price") or 0) ctp_margin = float(p.get("margin") or 0) - float_pnl = p.get("pnl") - if float_pnl is not None: - float_pnl = round(float(float_pnl), 2) mark = None if ctp_status(mode).get("connected"): mark = ctp_get_tick_price(mode, sym) if mark is None or mark <= 0: mark = entry if entry else None + float_pnl = None + if mark and entry and lots > 0: + float_pnl = calc_position_metrics( + direction, entry, entry, entry, lots, mark, capital, sym, + ).get("float_pnl") est = calc_position_metrics( direction, entry, entry, entry, lots, mark or entry, capital, sym, ).get("margin") @@ -1085,16 +1087,12 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se margin = None position_pct = None mark = None - float_pnl = ctp.get("pnl") - if float_pnl is not None: - float_pnl = round(float(float_pnl), 2) + float_pnl = None if lots <= 0: return None if ctp: - if ctp.get("pnl") is not None: - float_pnl = round(float(ctp["pnl"]), 2) if not mon: ctp_lots = int(ctp.get("lots") or 0) if ctp_lots > 0: @@ -1124,7 +1122,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se if mark is None or mark <= 0: mark = entry if entry else None close_est = float(mark) if mark and mark > 0 else entry - if float_pnl is None and mark and entry: + if mark and entry and lots > 0: pos_tmp = calc_position_metrics( direction, entry, sl or entry, tp or entry, lots, mark, capital, sym, ) @@ -1676,35 +1674,42 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se if not positions: positions = _ctp_positions(mode, refresh_if_empty=False) quotes: list[dict] = [] - for p in positions: - lots = int(p.get("lots") or 0) - if lots <= 0: - continue - ths = _ctp_pos_to_ths_code(p) or (p.get("symbol") or "") - if not ths: - continue - entry = float(p.get("avg_price") or 0) - if entry <= 0: - continue - direction = (p.get("direction") or "long").strip().lower() - mark = ctp_get_tick_price(mode, ths) - if not mark or mark <= 0: - continue - mult = float(get_contract_spec(ths).get("mult") or 10) - if direction == "long": - float_pnl = round((mark - entry) * mult * lots, 2) - else: - float_pnl = round((entry - mark) * mult * lots, 2) - row_key = _canonical_position_key( - ths, direction, (p.get("exchange") or ""), - ) - quotes.append({ - "key": row_key, - "position_key": row_key, - "mark_price": mark, - "current_price": mark, - "float_pnl": float_pnl, - }) + conn = get_db() + try: + for p in positions: + lots = int(p.get("lots") or 0) + if lots <= 0: + continue + ths = _ctp_pos_to_ths_code(p) or (p.get("symbol") or "") + if not ths: + continue + direction = (p.get("direction") or "long").strip().lower() + entry = float(p.get("avg_price") or 0) + mon = _find_active_monitor(conn, ths, direction) + if mon and float(mon.get("entry_price") or 0) > 0: + entry = float(mon["entry_price"]) + if entry <= 0: + continue + mark = ctp_get_tick_price(mode, ths) + if not mark or mark <= 0: + continue + mult = float(get_contract_spec(ths).get("mult") or 10) + if direction == "long": + float_pnl = round((mark - entry) * mult * lots, 2) + else: + float_pnl = round((entry - mark) * mult * lots, 2) + row_key = _canonical_position_key( + ths, direction, (p.get("exchange") or ""), + ) + quotes.append({ + "key": row_key, + "position_key": row_key, + "mark_price": mark, + "current_price": mark, + "float_pnl": float_pnl, + }) + finally: + conn.close() return {"ok": True, "quotes": quotes} def _push_position_quotes_async() -> None: