Add stats trading calendar and fix CTP position avg/sync.
Calendar shows daily closed trade count and PnL with emotion-day highlighting; day click loads review-first trade list. Use exchange-only entry average and improve vnpy position sync after CTP reconnect.
This commit is contained in:
+68
-24
@@ -9,6 +9,7 @@ from __future__ import annotations
|
||||
import json
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
from datetime import datetime
|
||||
from typing import Any, Callable, Optional
|
||||
|
||||
@@ -120,7 +121,7 @@ from trading_context import (
|
||||
is_ctp_connected,
|
||||
trading_mode_label,
|
||||
)
|
||||
from ctp_entry_price import resolve_ctp_entry
|
||||
from ctp_entry_price import round_to_tick
|
||||
from ctp_symbol import ths_to_vnpy_symbol
|
||||
from ctp_trading_state import position_key, trading_state
|
||||
from vnpy_bridge import (
|
||||
@@ -549,14 +550,9 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
mode: str,
|
||||
fallback: float = 0.0,
|
||||
) -> float:
|
||||
"""滚仓/展示用均价:优先柜台成交加权与持仓价。"""
|
||||
"""滚仓/展示用均价:仅柜台持仓价。"""
|
||||
if not ctp_status(mode).get("connected"):
|
||||
return fallback
|
||||
trades: list = []
|
||||
try:
|
||||
trades = ctp_list_trades(mode)
|
||||
except Exception:
|
||||
pass
|
||||
for p in trading_state.get_positions() or _ctp_positions(
|
||||
mode, refresh_if_empty=False,
|
||||
):
|
||||
@@ -564,11 +560,9 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
continue
|
||||
if not _match_ctp_symbol(p.get("symbol") or "", sym):
|
||||
continue
|
||||
entry, _ = resolve_ctp_entry(
|
||||
sym, direction, p, trades, tick=ctp_get_tick_price(mode, sym),
|
||||
)
|
||||
if entry > 0:
|
||||
return float(entry)
|
||||
avg = float(p.get("avg_price") or 0)
|
||||
if avg > 0:
|
||||
return avg
|
||||
return fallback
|
||||
|
||||
def _resolve_ctp_entry_price(
|
||||
@@ -577,17 +571,13 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
direction: str,
|
||||
ctp: Optional[dict],
|
||||
) -> tuple[float, str]:
|
||||
del mode, direction
|
||||
if not ctp:
|
||||
return 0.0, "none"
|
||||
trades: list = []
|
||||
tick = None
|
||||
if ctp_status(mode).get("connected"):
|
||||
try:
|
||||
trades = ctp_list_trades(mode)
|
||||
except Exception:
|
||||
pass
|
||||
tick = ctp_get_tick_price(mode, sym)
|
||||
return resolve_ctp_entry(sym, direction, ctp, trades, tick=tick)
|
||||
avg = float(ctp.get("avg_price") or 0)
|
||||
if avg > 0:
|
||||
return round_to_tick(avg, sym), "ctp"
|
||||
return 0.0, "none"
|
||||
|
||||
def _open_commission_from_ctp_trades(
|
||||
mode: str, sym: str, direction: str,
|
||||
@@ -1246,6 +1236,8 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
ctp_margin = float(ctp.get("margin") or 0)
|
||||
if (margin is None or float(margin or 0) <= 0) and ctp_margin > 0:
|
||||
margin = ctp_margin
|
||||
if ctp_status(mode).get("connected"):
|
||||
source_label = "CTP 柜台"
|
||||
|
||||
codes = ths_to_codes(sym)
|
||||
tick = calc_order_tick_metrics(sym, lots, entry, trading_mode=mode)
|
||||
@@ -1677,10 +1669,17 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
monitor_by_pk = _monitors_by_position_key(conn)
|
||||
ctp_list: list[dict] = []
|
||||
if ctp_status(mode).get("connected"):
|
||||
ctp_list = trading_state.get_positions()
|
||||
ctp_list = _ctp_positions(mode, refresh_if_empty=False, refresh_margin=False)
|
||||
if not ctp_list:
|
||||
ctp_list = _ctp_positions(
|
||||
mode, refresh_if_empty=True, refresh_margin=not fast,
|
||||
ctp_list = trading_state.get_positions()
|
||||
if not ctp_list:
|
||||
try:
|
||||
with _ctp_td_lock:
|
||||
get_bridge().calibrate_trading_state()
|
||||
except Exception as exc:
|
||||
logger.debug("live calibrate: %s", exc)
|
||||
ctp_list = trading_state.get_positions() or _ctp_positions(
|
||||
mode, refresh_if_empty=False, refresh_margin=False,
|
||||
)
|
||||
|
||||
rows: list[dict] = []
|
||||
@@ -1740,6 +1739,51 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
|
||||
continue
|
||||
seen.add(rk)
|
||||
deduped.append(row)
|
||||
|
||||
if not deduped and ctp_status(mode).get("connected") and monitor_by_pk:
|
||||
margin_used = float(ctp_account_margin_used(mode) or 0)
|
||||
since_connect = 9999.0
|
||||
try:
|
||||
since_connect = time.time() - float(
|
||||
getattr(get_bridge(), "_last_connect_ok_ts", 0) or 0,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
if margin_used > 100 or since_connect < 300:
|
||||
for mon in monitor_by_pk.values():
|
||||
lots = int(mon.get("lots") or 0)
|
||||
if lots <= 0:
|
||||
continue
|
||||
sym = (mon.get("symbol") or "").strip()
|
||||
direction = (mon.get("direction") or "long").strip().lower()
|
||||
if fast:
|
||||
mon = _overlay_sl_tp_readonly(conn, mon, sym, direction) or mon
|
||||
else:
|
||||
mon = (
|
||||
_restore_monitor_sl_tp_if_missing(conn, mon, sym, direction)
|
||||
or mon
|
||||
)
|
||||
try:
|
||||
row = _compose_position_row(
|
||||
conn,
|
||||
mon=mon,
|
||||
ctp=None,
|
||||
mode=mode,
|
||||
capital=capital,
|
||||
now_iso=now_iso,
|
||||
fast=fast,
|
||||
)
|
||||
if not row:
|
||||
continue
|
||||
rk = row.get("key") or row.get("position_key") or ""
|
||||
if rk and rk in seen:
|
||||
continue
|
||||
if rk:
|
||||
seen.add(rk)
|
||||
deduped.append(row)
|
||||
except Exception as exc:
|
||||
logger.warning("compose monitor fallback row failed: %s", exc)
|
||||
|
||||
return deduped
|
||||
|
||||
def _build_trading_live_payload(conn, *, fast: bool = False) -> dict:
|
||||
|
||||
Reference in New Issue
Block a user