"""实例数据看板:本户活跃监控 / 持仓只读聚合.""" from __future__ import annotations from datetime import datetime, timezone from typing import Any, Callable, Optional def _row_dict(row: Any) -> dict[str, Any]: if row is None: return {} if isinstance(row, dict): return dict(row) try: return dict(row) except Exception: return {} def _safe_float(v: Any) -> Optional[float]: try: if v is None or v == "": return None return float(v) except (TypeError, ValueError): return None def _dir_label(direction: Any) -> str: d = str(direction or "").strip().lower() if d == "short": return "做空" if d == "long": return "做多" return str(direction or "-") def _format_order_item(od: dict[str, Any]) -> dict[str, Any]: try: from lib.strategy.strategy_trade_labels import apply_order_monitor_source_labels od = apply_order_monitor_source_labels(od) except Exception: pass try: from lib.trade.entry_model_lib import enrich_entry_model_display enrich_entry_model_display(od) except Exception: pass sym = od.get("exchange_symbol") or od.get("symbol") or "-" direction = str(od.get("direction") or "long").lower() mt = od.get("monitor_type_display") or od.get("monitor_type") or "" kst = od.get("key_signal_type") or "" title = f"{sym} {_dir_label(direction)}" bits = [x for x in (mt, kst) if x] subtitle = " · ".join(bits) if bits else "" entry = _safe_float(od.get("trigger_price")) sl = _safe_float(od.get("stop_loss")) tp = _safe_float(od.get("take_profit")) return { "id": od.get("id"), "kind": "order", "tab": "trade", "title": title, "subtitle": subtitle, "symbol": sym, "price_symbol": od.get("symbol") or sym, "direction": direction, "direction_label": _dir_label(direction), "entry": entry, "mark_price": None, "contracts": _safe_float(od.get("order_amount")), "tp_profit": None, "float_pnl": None, "stop_loss": sl, "take_profit": tp, "status": od.get("status") or "active", } OPTIONS_SOURCE_LABELS = { "option": "纯期权", "perp_options": "永期对冲", "options_options": "期期对冲", } HEDGE_ACTIVE_STATUSES = frozenset({"opening", "active", "partial"}) def _resolve_options_source(conn, inst_id: str) -> tuple[str, str]: """根据进行中对冲计划腿判定来源;默认纯期权.""" if not inst_id or not _table_exists(conn, "hedge_plans") or not _table_exists(conn, "hedge_plan_legs"): return "option", OPTIONS_SOURCE_LABELS["option"] try: row = conn.execute( """ SELECT p.plan_type FROM hedge_plans p JOIN hedge_plan_legs l ON l.plan_id = p.id WHERE p.status IN ('opening', 'active', 'partial') AND l.status = 'open' AND l.inst_id = ? ORDER BY p.id DESC LIMIT 1 """, (inst_id,), ).fetchone() except Exception: return "option", OPTIONS_SOURCE_LABELS["option"] if not row: return "option", OPTIONS_SOURCE_LABELS["option"] pt = str((_row_dict(row).get("plan_type") if isinstance(row, dict) else row[0]) or "").strip() if pt in OPTIONS_SOURCE_LABELS: return pt, OPTIONS_SOURCE_LABELS[pt] return "option", OPTIONS_SOURCE_LABELS["option"] def _format_options_target(p: dict[str, Any]) -> str: hedge = p.get("hedge_plan_target") if isinstance(p.get("hedge_plan_target"), dict) else None opt_type = str(p.get("opt_type") or p.get("optType") or "").upper() if hedge: ot = str(hedge.get("opt_type") or opt_type).upper() side = "Put ≤" if ot == "P" else "Call ≥" tgt = _safe_float(hedge.get("target_index")) pid = hedge.get("plan_id") if tgt is not None: return f"对冲#{pid} {side} {tgt:g}" if pid is not None else f"{side} {tgt:g}" tgt = _safe_float(p.get("target_index")) if tgt is not None and tgt > 0: side = "Put ≤" if opt_type == "P" else "Call ≥" return f"{side} {tgt:g}" return "—" def _format_options_item(p: dict[str, Any], *, conn=None) -> dict[str, Any]: inst = str(p.get("inst_id") or p.get("instId") or "-").strip() or "-" opt_type = str(p.get("opt_type") or p.get("optType") or "").upper() label = "Call" if opt_type == "C" else "Put" if opt_type == "P" else (opt_type or "OPT") # 看板期权列固定用净盈亏(买一回收−权利金);残档买一则空. pnl = None try: from lib.options.options_positions_lib import net_pnl_from_display_row pnl = net_pnl_from_display_row(p) except Exception: pnl = None pos = _safe_float(p.get("pos")) exp_ms = p.get("exp_time_ms") if exp_ms is None: exp_ms = p.get("exp_time") try: exp_ms = int(float(exp_ms)) if exp_ms not in (None, "") else None except (TypeError, ValueError): exp_ms = None source_key, source_label = ( _resolve_options_source(conn, inst) if conn is not None else ("option", OPTIONS_SOURCE_LABELS["option"]) ) return { "id": inst, "kind": "options", "tab": "options", "title": f"{inst} {label}", "subtitle": f"张数 {pos if pos is not None else '-'}", "inst_id": inst, "opt_type": opt_type, "opt_type_label": label, "source": source_key, "source_label": source_label, "pos": pos, "exp_time_ms": exp_ms, "target_monitor": _format_options_target(p), "pnl": round(pnl, 4) if pnl is not None else None, } def _format_hedge_item(plan: dict[str, Any]) -> dict[str, Any]: pid = plan.get("id") underlying = plan.get("underlying") or "-" plan_type = plan.get("plan_type") or "" status = str(plan.get("status") or "") summary = plan.get("contracts_summary") or "" plan_type_label = OPTIONS_SOURCE_LABELS.get(plan_type, plan_type) active = status in HEDGE_ACTIVE_STATUSES status_label = "进行中" if active else (status or "—") return { "id": pid, "kind": "hedge_plan", "tab": "hedge_plan", "title": f"对冲 #{pid} {underlying}", "subtitle": " · ".join(x for x in (plan_type_label, status_label, summary) if x), "underlying": underlying, "plan_type": plan_type, "plan_type_label": plan_type_label, "status": status, "status_label": status_label, "status_active": active, "contracts_summary": summary, } def _format_key_item(kd: dict[str, Any]) -> dict[str, Any]: sym = kd.get("exchange_symbol") or kd.get("symbol") or "-" direction = str(kd.get("direction") or "long").lower() signal = kd.get("signal_type") or kd.get("key_signal_type") or kd.get("monitor_type") or "" upper = _safe_float(kd.get("upper")) lower = _safe_float(kd.get("lower")) subtitle_parts = [] if signal: subtitle_parts.append(str(signal)) if upper is not None or lower is not None: subtitle_parts.append( f"上{upper if upper is not None else '-'} / 下{lower if lower is not None else '-'}" ) return { "id": kd.get("id"), "kind": "key", "tab": "key_monitor", "title": f"{sym} {_dir_label(direction)}", "subtitle": " · ".join(subtitle_parts), "symbol": sym, "direction": direction, "direction_label": _dir_label(direction), "upper": upper, "lower": lower, "status": kd.get("status") or "active", } def _format_trend_item(td: dict[str, Any]) -> dict[str, Any]: sym = td.get("exchange_symbol") or td.get("symbol") or "-" direction = str(td.get("direction") or "long").lower() status = td.get("status") or "active" entry = _safe_float(td.get("entry_price") or td.get("trigger_price")) return { "id": td.get("id"), "kind": "trend", "tab": "strategy", "title": f"趋势回调 {sym} {_dir_label(direction)}", "subtitle": f"状态 {status}", "symbol": sym, "direction": direction, "direction_label": _dir_label(direction), "entry": entry, "status": status, } def _format_roll_item(rd: dict[str, Any]) -> dict[str, Any]: sym = rd.get("exchange_symbol") or rd.get("symbol") or "-" direction = str(rd.get("direction") or "long").lower() status = rd.get("status") or "active" return { "id": rd.get("id"), "kind": "roll", "tab": "strategy", "title": f"顺势加仓 {sym} {_dir_label(direction)}", "subtitle": f"状态 {status}", "symbol": sym, "direction": direction, "direction_label": _dir_label(direction), "status": status, } def _table_exists(conn, name: str) -> bool: try: row = conn.execute( "SELECT 1 FROM sqlite_master WHERE type='table' AND name=? LIMIT 1", (name,), ).fetchone() return bool(row) except Exception: return False def collect_orders(conn) -> list[dict[str, Any]]: if not _table_exists(conn, "order_monitors"): return [] rows = conn.execute( "SELECT * FROM order_monitors WHERE status='active' ORDER BY id DESC" ).fetchall() return [_format_order_item(_row_dict(r)) for r in rows] def collect_keys(conn) -> list[dict[str, Any]]: if not _table_exists(conn, "key_monitors"): return [] rows = conn.execute("SELECT * FROM key_monitors ORDER BY id DESC").fetchall() return [_format_key_item(_row_dict(r)) for r in rows] def collect_trends(conn) -> list[dict[str, Any]]: if not _table_exists(conn, "trend_pullback_plans"): return [] try: rows = conn.execute( "SELECT * FROM trend_pullback_plans WHERE status='active' ORDER BY id DESC" ).fetchall() except Exception: return [] return [_format_trend_item(_row_dict(r)) for r in rows] def collect_rolls(conn) -> list[dict[str, Any]]: if not _table_exists(conn, "roll_groups") or not _table_exists(conn, "order_monitors"): return [] try: rows = conn.execute( """SELECT g.* FROM roll_groups g INNER JOIN order_monitors m ON m.id = g.order_monitor_id AND m.status='active' WHERE g.status='active' ORDER BY g.id DESC""" ).fetchall() except Exception: return [] return [_format_roll_item(_row_dict(r)) for r in rows] def collect_hedge_plans(conn) -> list[dict[str, Any]]: if not _table_exists(conn, "hedge_plans"): return [] try: from lib.hedge_plan.hedge_plan_db import attach_legs_to_plans, list_plans rows: list[dict[str, Any]] = [] for status in ("opening", "active", "partial"): rows.extend(list_plans(conn, status=status, limit=80)) rows.sort(key=lambda row: int(row.get("id") or 0), reverse=True) plans = attach_legs_to_plans(conn, rows) return [_format_hedge_item(p) for p in plans] except Exception: return [] def collect_options_items( fetch_options_positions: Optional[Callable[[], list[dict[str, Any]]]] = None, *, conn=None, ) -> list[dict[str, Any]]: if not callable(fetch_options_positions): return [] try: raw = fetch_options_positions() or [] except Exception: return [] out: list[dict[str, Any]] = [] for p in raw: if not isinstance(p, dict): continue out.append(_format_options_item(p, conn=conn)) return out def enrich_order_items_with_marks( items: list[dict[str, Any]], *, get_price: Optional[Callable[[str], Any]] = None, ) -> list[dict[str, Any]]: """后台聚合时补标记价(不打全量 fetch_positions;浮盈仍由实盘页口径负责).""" if not items or not callable(get_price): return items out: list[dict[str, Any]] = [] for it in items: row = dict(it) sym = str(row.get("price_symbol") or row.get("symbol") or "").strip() if not sym: out.append(row) continue try: px = get_price(sym) except Exception: px = None mark = _safe_float(px) if mark is None and ":" in sym: try: px = get_price(sym.split(":", 1)[0]) except Exception: px = None mark = _safe_float(px) if mark is not None: row["mark_price"] = mark out.append(row) return out def build_instance_dashboard_payload( conn, *, fetch_options_positions: Optional[Callable[[], list[dict[str, Any]]]] = None, hedge_enabled: bool = False, ) -> dict[str, Any]: orders = collect_orders(conn) keys = collect_keys(conn) trends = collect_trends(conn) rolls = collect_rolls(conn) strategy_items = trends + rolls options_items = collect_options_items(fetch_options_positions, conn=conn) hedge_items = collect_hedge_plans(conn) if hedge_enabled else [] now = datetime.now(timezone.utc).astimezone().strftime("%Y-%m-%d %H:%M:%S") return { "ok": True, "updated_at": now, "orders": {"title": "实盘下单", "count": len(orders), "items": orders, "tab": "trade"}, "keys": {"title": "关键位监控", "count": len(keys), "items": keys, "tab": "key_monitor"}, "strategy": { "title": "策略交易", "count": len(strategy_items), "items": strategy_items, "trends": trends, "rolls": rolls, "tab": "strategy", }, "options": { "title": "期权持仓", "count": len(options_items), "items": options_items, "visible": len(options_items) > 0, "tab": "options", }, "hedge_plan": { "title": "对冲计划", "count": len(hedge_items), "items": hedge_items, "visible": len(hedge_items) > 0, "tab": "hedge_plan", }, }