## 现象 Binance 现货持仓不带成本价,所以 QPK `portfolio_diagnostics` 永远算不出 `unrealized_pnl_pct`,风险闸门里的组合止损(未实现亏损 < -20% 时 `rejected:stop_loss`)在 Binance 上**实际从未生效**。 这是**既有问题**,不是 QPK `155d74a` 升级(#363)引入的:旧 pin `8e8ec51` 下,所有持仓都缺成本价时 `has_cost_basis=False → None`,结果一样。 ## 代码链路(main@44a0cd6) 1. `strategy_runtime.py` 组装快照时只给 `Position(symbol, quantity, market_value)`,没有 `average_cost`([L237](https://github.com/QuantStrategyLab/BinancePlatform/blob/44a0cd6/strategy_runtime.py#L237))。 2. CryptoStrategies `@7fcca8e` `entrypoints/_common.py::apply_risk_gate` → `extract_portfolio_risk_diagnostics(snapshot)` → `enrich_decision_risk_diagnostics(unrealized_pnl_pct=...)`。 3. QPK `@155d74a` `risk/portfolio_diagnostics.py`:缺 `average_cost` 的持仓记为未知;覆盖率 < 1 时 `unrealized_pnl_pct` 不写入(只写 `unrealized_pnl_coverage`)。 4. QPK `risk/gate.py`:`unrealized_pnl_pct` 缺失时跳过止损判断(`_STOP_LOSS_THRESHOLD = -0.20`)。 结果:有持仓时 `unrealized_pnl_coverage` 为 0,止损检查永远跳过;空仓时 PnL 为 0,也不会触发。连续亏损熔断(`consecutive_losses`,来自 `stamp_consecutive_losses_on_snapshot`)走的是另一条路径,不受影响。CryptoStrategies 自己的趋势止损也是另一套逻辑,本 issue 没有核验它。 ## 可选方案(只是提案,未实施) | # | 方案 | 优点 | 风险 / 代价 | | --- | --- | --- | --- | | A | **先让问题可见**:执行报告和通知里标出 `stop_loss_check=inactive (cost_basis_unavailable)`,带上 `unrealized_pnl_coverage` | 改动小,不改交易行为 | 只解决"看得见",保护还是没有 | | B | **平台侧成本账本**:在已有状态存储里按自己的成交回报维护每个币的平均成本;启用那一刻按当时的市值定基线 | 数据来源可控,不依赖历史 API | 止损的参照点是启用时,不是真实买入成本;充提币、手动交易需要对账,且要定义"未知时怎么处理" | | C | **从 Binance 成交历史重建成本**(`GET /api/v3/myTrades` 只读接口,按币做 FIFO 或平均成本) | 最接近真实成本 | 充值、划转、空投、BNB 手续费、历史窗口都会让成本不完整;需要限流和缓存;数据不全时要明确判为未知 | | D | **Binance 改用权益回撤闸门**:用 live equity 历史的峰值回撤代替未实现盈亏止损(比如通过 `metadata.unrealized_pnl_pct` 覆盖,或单独加一个 drawdown 检查) | 不需要成本价,语义清楚 | 风险语义变了(回撤 ≠ 未实现亏损),阈值要人工拍板;属于风控规则改动 | **建议**:先做 A(只读、低风险);同时由人工在 B 和 D 之间选一个作为真正的保护,C 只作为离线校验参考。B 和 D 都会改变实盘风控行为,需要单独批准,并先做不下单预演。 ## 不在本 issue 范围 不改 QPK 闸门阈值,不改 CryptoStrategies,不改实盘开关或密钥。 Author: Pigbibi(rebuild)
Activity