fix: 中控持仓卡合并最新风险与保证金展示

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-02 22:19:07 +08:00
parent 687a34474d
commit 0b8f410fbe
2 changed files with 172 additions and 66 deletions
+112 -56
View File
@@ -1658,6 +1658,97 @@ def _exchange_tpsl_from_hub_order(hub_orders: list, symbol: str, side: str) -> d
return None
def _order_price_op_indexes(order_prices: list) -> tuple[dict, list]:
"""price_snapshot order_pricesid 可能为 int/str,需双键索引。"""
by_id: dict = {}
flat: list = []
for op in order_prices:
if not isinstance(op, dict):
continue
flat.append(op)
oid = op.get("id")
if oid is None:
continue
by_id[oid] = op
by_id[str(oid)] = op
try:
by_id[int(oid)] = op
except (TypeError, ValueError):
pass
return by_id, flat
def _match_order_price_op(
order_row: dict,
by_id: dict,
order_prices: list,
) -> dict | None:
if not isinstance(order_row, dict):
return None
oid = order_row.get("id")
if oid is not None:
for key in (oid, str(oid)):
op = by_id.get(key)
if isinstance(op, dict):
return op
try:
op = by_id.get(int(oid))
if isinstance(op, dict):
return op
except (TypeError, ValueError):
pass
sym = order_row.get("exchange_symbol") or order_row.get("symbol") or ""
direction = (order_row.get("direction") or "").lower()
for op in order_prices:
if not isinstance(op, dict):
continue
if not _symbols_match(sym, op.get("symbol") or ""):
continue
op_dir = (op.get("direction") or "").lower()
if direction and op_dir and direction != op_dir:
continue
return op
return None
_ORDER_PRICE_MERGE_KEYS = (
"stop_loss",
"take_profit",
"stop_loss_display",
"take_profit_display",
"display_rr_ratio",
"latest_risk_amount",
"contracts",
"exchange_initial_margin",
"plan_margin",
"time_close_enabled",
"time_close_hours",
"time_close_at_ms",
"time_close_label",
"time_close_countdown",
"time_close_remaining_sec",
)
def _apply_order_price_op_fields(target: dict, op: dict) -> None:
if not isinstance(target, dict) or not isinstance(op, dict):
return
if op.get("rr_ratio") is not None:
target["rr_ratio"] = op["rr_ratio"]
if "sl_breakeven_secured" in op:
target["sl_breakeven_secured"] = bool(op["sl_breakeven_secured"])
for key in _ORDER_PRICE_MERGE_KEYS:
if key not in op:
continue
val = op[key]
if key == "latest_risk_amount":
if val is not None and val != "":
target[key] = val
continue
if val not in (None, ""):
target[key] = val
def _find_exchange_tpsl_for_position(
symbol: str,
side: str,
@@ -1665,11 +1756,7 @@ def _find_exchange_tpsl_for_position(
hub_orders: list,
) -> dict | None:
side_l = (side or "").lower()
op_by_id = {
op.get("id"): op
for op in order_prices
if isinstance(op, dict) and op.get("id") is not None
}
by_id, flat = _order_price_op_indexes(order_prices)
for o in hub_orders:
if not isinstance(o, dict):
continue
@@ -1678,13 +1765,13 @@ def _find_exchange_tpsl_for_position(
continue
if (o.get("direction") or "").lower() != side_l:
continue
op = op_by_id.get(o.get("id"))
op = _match_order_price_op(o, by_id, flat)
if not isinstance(op, dict):
continue
et = op.get("exchange_tpsl")
if isinstance(et, dict) and (et.get("sl") or et.get("tp")):
return et
for op in order_prices:
for op in flat:
if not isinstance(op, dict):
continue
if not _symbols_match(symbol, op.get("symbol") or ""):
@@ -1700,47 +1787,21 @@ def _merge_flask_order_price_fields(hub_mon: dict | None, snap: dict | None) ->
if not isinstance(hub_mon, dict) or not isinstance(snap, dict):
return
order_prices = snap.get("order_prices") or []
op_by_id = {
op.get("id"): op
for op in order_prices
if isinstance(op, dict) and op.get("id") is not None
}
by_id, flat = _order_price_op_indexes(order_prices)
orders = hub_mon.get("orders") or []
if not isinstance(orders, list):
return
for o in orders:
if not isinstance(o, dict):
continue
op = op_by_id.get(o.get("id"))
op = _match_order_price_op(o, by_id, flat)
if not isinstance(op, dict):
continue
if op.get("rr_ratio") is not None:
o["rr_ratio"] = op["rr_ratio"]
if "sl_breakeven_secured" in op:
o["sl_breakeven_secured"] = bool(op["sl_breakeven_secured"])
for key in (
"stop_loss",
"take_profit",
"stop_loss_display",
"take_profit_display",
"display_rr_ratio",
"latest_risk_amount",
"contracts",
"exchange_initial_margin",
"plan_margin",
"time_close_enabled",
"time_close_hours",
"time_close_at_ms",
"time_close_label",
"time_close_countdown",
"time_close_remaining_sec",
):
if key in op and op[key] not in (None, ""):
o[key] = op[key]
_apply_order_price_op_fields(o, op)
def _merge_flask_position_breakeven(agent_row: dict, snap: dict | None, hub_mon: dict | None) -> None:
"""将 price_snapshot 的已保本状态同步到 agent 持仓,供中控首页表格展示"""
"""将 price_snapshot 的已保本、最新风险、保证金等同步到 agent 持仓。"""
ag = agent_row.get("agent")
if not isinstance(ag, dict) or not isinstance(snap, dict):
return
@@ -1748,14 +1809,10 @@ def _merge_flask_position_breakeven(agent_row: dict, snap: dict | None, hub_mon:
if not isinstance(positions, list) or not positions:
return
order_prices = snap.get("order_prices") or []
by_id, flat = _order_price_op_indexes(order_prices)
hub_orders = []
if isinstance(hub_mon, dict):
hub_orders = hub_mon.get("orders") or []
op_by_id = {
op.get("id"): op
for op in order_prices
if isinstance(op, dict) and op.get("id") is not None
}
for p in positions:
if not isinstance(p, dict):
continue
@@ -1770,18 +1827,22 @@ def _merge_flask_position_breakeven(agent_row: dict, snap: dict | None, hub_mon:
continue
if (o.get("direction") or "").lower() != side:
continue
matched = op_by_id.get(o.get("id"))
break
matched = _match_order_price_op(o, by_id, flat)
if isinstance(matched, dict):
break
if o.get("latest_risk_amount") is not None or o.get("exchange_initial_margin") is not None:
matched = o
break
if matched is None:
for op in order_prices:
for op in flat:
if not isinstance(op, dict):
continue
if not _symbols_match(sym, op.get("symbol") or ""):
continue
matched = op
break
if isinstance(matched, dict) and "sl_breakeven_secured" in matched:
p["sl_breakeven_secured"] = bool(matched["sl_breakeven_secured"])
if isinstance(matched, dict):
_apply_order_price_op_fields(p, matched)
def _agent_position_has_mark(p: dict) -> bool:
@@ -1809,10 +1870,10 @@ def _find_matched_order_price_op(
p: dict,
order_prices: list,
hub_orders: list,
op_by_id: dict,
) -> dict | None:
sym = p.get("symbol") or ""
side = (p.get("side") or "").lower()
by_id, flat = _order_price_op_indexes(order_prices)
for o in hub_orders:
if not isinstance(o, dict):
continue
@@ -1821,11 +1882,11 @@ def _find_matched_order_price_op(
continue
if (o.get("direction") or "").lower() != side:
continue
matched = op_by_id.get(o.get("id"))
matched = _match_order_price_op(o, by_id, flat)
if isinstance(matched, dict):
return matched
break
for op in order_prices:
for op in flat:
if not isinstance(op, dict):
continue
if not _symbols_match(sym, op.get("symbol") or ""):
@@ -1848,15 +1909,10 @@ def _merge_flask_position_mark_price(
hub_orders = []
if isinstance(hub_mon, dict):
hub_orders = hub_mon.get("orders") or []
op_by_id = {
op.get("id"): op
for op in order_prices
if isinstance(op, dict) and op.get("id") is not None
}
for p in positions:
if not isinstance(p, dict) or _agent_position_has_mark(p):
continue
matched = _find_matched_order_price_op(p, order_prices, hub_orders, op_by_id)
matched = _find_matched_order_price_op(p, order_prices, hub_orders)
if isinstance(matched, dict):
_apply_agent_mark_price(
p,