Implement CTP-authoritative trading UI with event-driven state.
Add in-memory order/position books fed by CTP events, split active orders above positions in the UI, tick-triggered local SL/TP, and 30-second full calibration. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+75
-13
@@ -80,6 +80,37 @@ def _find_ctp_position(positions: list[dict], sym: str, direction: str) -> Optio
|
||||
return None
|
||||
|
||||
|
||||
def _vt_order_in_active(vt_oid: str, active_orders: dict[str, dict]) -> bool:
|
||||
oid = (vt_oid or "").strip()
|
||||
if not oid:
|
||||
return False
|
||||
if oid in active_orders:
|
||||
return True
|
||||
tail = oid.rsplit("_", 1)[-1]
|
||||
for key in active_orders:
|
||||
if key == oid or key.endswith(tail) or oid.endswith(key):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _symbol_open_order_active(
|
||||
orders: list[dict],
|
||||
sym: str,
|
||||
direction: str,
|
||||
match_fn: Callable[[str, str], bool],
|
||||
) -> Optional[dict]:
|
||||
direction = (direction or "long").strip().lower()
|
||||
for o in orders or []:
|
||||
offset_u = (o.get("offset") or "").upper()
|
||||
if offset_u and "OPEN" not in offset_u:
|
||||
continue
|
||||
if (o.get("direction") or "long") != direction:
|
||||
continue
|
||||
if match_fn(o.get("symbol") or "", sym):
|
||||
return o
|
||||
return None
|
||||
|
||||
|
||||
def reconcile_pending_orders(
|
||||
conn,
|
||||
mode: str,
|
||||
@@ -103,13 +134,15 @@ def reconcile_pending_orders(
|
||||
else []
|
||||
)
|
||||
try:
|
||||
active_orders = {
|
||||
str(o.get("order_id") or ""): o
|
||||
for o in ctp_list_active_orders(mode)
|
||||
if o.get("order_id")
|
||||
}
|
||||
active_order_list = ctp_list_active_orders(mode)
|
||||
active_orders = {}
|
||||
for o in active_order_list:
|
||||
for key in (o.get("order_id"), o.get("vt_order_id")):
|
||||
if key:
|
||||
active_orders[str(key)] = o
|
||||
except Exception as exc:
|
||||
logger.debug("list active orders: %s", exc)
|
||||
active_order_list = []
|
||||
active_orders = {}
|
||||
|
||||
rows = conn.execute(
|
||||
@@ -137,7 +170,7 @@ def reconcile_pending_orders(
|
||||
stats["promoted"] += 1
|
||||
continue
|
||||
|
||||
if vt_oid and vt_oid in active_orders:
|
||||
if vt_oid and _vt_order_in_active(vt_oid, active_orders):
|
||||
if age >= limit_sec and is_trading_session():
|
||||
if ctp_cancel_order(mode, vt_oid):
|
||||
conn.execute(
|
||||
@@ -149,13 +182,42 @@ def reconcile_pending_orders(
|
||||
logger.warning("pending auto-cancel failed monitor=%s order=%s", mid, vt_oid)
|
||||
continue
|
||||
|
||||
# 委托已不在活跃列表且无持仓:拒单/撤单/过期
|
||||
if age >= 8:
|
||||
conn.execute(
|
||||
"UPDATE trade_order_monitors SET status='closed' WHERE id=?",
|
||||
(mid,),
|
||||
)
|
||||
stats["closed"] += 1
|
||||
live_open = _symbol_open_order_active(active_order_list, sym, direction, match)
|
||||
if live_open or (vt_oid and age < limit_sec):
|
||||
if live_open and age >= limit_sec and is_trading_session():
|
||||
cancel_oid = (
|
||||
vt_oid
|
||||
or live_open.get("vt_order_id")
|
||||
or live_open.get("order_id")
|
||||
or ""
|
||||
)
|
||||
if cancel_oid and ctp_cancel_order(mode, cancel_oid):
|
||||
conn.execute(
|
||||
"UPDATE trade_order_monitors SET status='closed' WHERE id=?",
|
||||
(mid,),
|
||||
)
|
||||
stats["cancelled"] += 1
|
||||
continue
|
||||
|
||||
if age >= limit_sec:
|
||||
if vt_oid and is_trading_session():
|
||||
if ctp_cancel_order(mode, vt_oid):
|
||||
conn.execute(
|
||||
"UPDATE trade_order_monitors SET status='closed' WHERE id=?",
|
||||
(mid,),
|
||||
)
|
||||
stats["cancelled"] += 1
|
||||
else:
|
||||
logger.info(
|
||||
"pending monitor=%s order=%s kept (cancel not confirmed)",
|
||||
mid, vt_oid,
|
||||
)
|
||||
elif not vt_oid:
|
||||
conn.execute(
|
||||
"UPDATE trade_order_monitors SET status='closed' WHERE id=?",
|
||||
(mid,),
|
||||
)
|
||||
stats["closed"] += 1
|
||||
|
||||
if any(stats.values()):
|
||||
conn.commit()
|
||||
|
||||
Reference in New Issue
Block a user