"""price_snapshot 共用:订单行情价兜底,避免 get_price 失败时整单不入 order_prices。""" from __future__ import annotations from typing import Any, Callable, Mapping, Optional, Sequence from lib.hub.hub_position_metrics import parse_position_mark_price def resolve_order_snapshot_price( symbol: str, prices: Mapping[str, float], *, position_row: Optional[dict[str, Any]] = None, order_leverage=None, parse_position_metrics_fn: Callable[..., dict[str, Any] | None] | None = None, get_mark_price_fn: Callable[[str], float | None] | None = None, fallback_entry: float | None = None, ) -> float | None: """ 解析下单监控轮询用的现价/标记价,优先级: 1. 已批量拉取的 ticker last 2. get_symbol_mark_price(含 mark) 3. 交易所持仓 mark(parse_ccxt_position_metrics / parse_position_mark_price) 4. 计划成交价 trigger_price """ sym = (symbol or "").strip() if not sym: return None cached = prices.get(sym) if cached is not None: try: v = float(cached) if v > 0: return v except (TypeError, ValueError): pass if get_mark_price_fn is not None: try: mp = get_mark_price_fn(sym) if mp is not None and float(mp) > 0: return float(mp) except Exception: pass if position_row: mark = None if parse_position_metrics_fn is not None: try: metrics = parse_position_metrics_fn( position_row, order_leverage=order_leverage ) if isinstance(metrics, dict) and metrics.get("mark_price") is not None: mark = float(metrics["mark_price"]) except Exception: mark = None if mark is None or mark <= 0: try: mp = parse_position_mark_price(position_row) if mp is not None and mp > 0: mark = float(mp) except Exception: mark = None if mark is not None and mark > 0: return mark if fallback_entry is not None: try: entry = float(fallback_entry) if entry > 0: return entry except (TypeError, ValueError): pass return None def seed_prices_from_positions( prices: dict[str, float], order_rows: Sequence[Any], all_positions: Sequence[dict[str, Any]], *, resolve_ex_sym_fn: Callable[[Any], str], ) -> None: """用持仓标记价补全 prices 字典(symbol 与 order_monitors 行对齐)。""" if not all_positions or not order_rows: return try: from lib.hub.hub_symbol_lib import symbols_match except Exception: symbols_match = None for r in order_rows: try: sym = str(r["symbol"] or "").strip() except (KeyError, TypeError, IndexError): sym = "" if not sym or sym in prices: continue try: ex_sym = resolve_ex_sym_fn(r) except Exception: ex_sym = sym try: direction = str(r["direction"] or "long").lower() except (KeyError, TypeError, IndexError): direction = "long" for p in all_positions: if not isinstance(p, dict): continue ps = p.get("symbol") or "" if not ps: continue matched = ps == sym or ps == ex_sym if not matched and symbols_match is not None: matched = symbols_match(sym, ps) or symbols_match(ex_sym, ps) if not matched: continue side = (p.get("side") or "").lower() if side and side != direction: continue mp = parse_position_mark_price(p) if mp is not None and mp > 0: prices[sym] = float(mp) break