feat(paper): 框架级持仓保护止损 Position Guard(ADR-0052)#88
Conversation
framework 级持仓保护止损:硬止损/止盈/移动止损三闸 + 峰值跟踪;出场打 tag 经 close_detector→exit_reason,直发 EXECUTION_ENGINE_ENDPOINT 绕过开仓闸。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
两套引擎在 update_mark 之后同一逻辑点调 guard.evaluate,保证回测与模拟盘 行为一致;阈值全 None 时退化为无 guard(向后兼容)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
config 加三项 protective_* 默认(硬止损 0.20,止盈/移动止损默认关);main 进程 runner 解析阈值传给子进程(picklable,子进程不读 Settings),live_runner 透传给 session 并对保护性 tag 跳过 enforce(回撤熔断锁期内仍能平仓,保留 notional 上限)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
report 加 protective_exits 计数把兜底触发可见化;test 扩单仓崩盘端到端兜底、 无 guard 对照、回测 vs live feed_bar 同口径(同组件/同阈值/同判定点)回归。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
BacktestResponse 加 protective_exits 字段、runner 响应组装映射 report→response; orchestration paper client 类型 + run_backtest tool 描述同步透出,让 agent 在回测 结果里看到"框架兜底本次触发 N 次",把回测对未来 live 的兜底可见化。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Deploying inalpha-web with
|
| Latest commit: |
7ab462d
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://ca58c8d9.inalpha-web.pages.dev |
| Branch Preview URL: | https://feat-position-guard.inalpha-web.pages.dev |
本轮 commit 截至 7ab462d
PR 意图引入框架级持仓保护止损(ADR-0052): 必修(critical / major)[major]
|
- 移动止损改用「自峰值价格回撤」口径(非成本收益率降幅),并仅在仓位进入盈利区后 生效——避免大盈利下过激触发 + 开仓即亏的假触发(CR medium 1) - config 注释纠正:关闭硬止损=不设环境变量(None),0 不合法(gt=0)(CR medium 2) - 末根 bar 触发的保护性出场单收尾按该根 close 兜底成交(决策价,非 look-ahead, 与 live 同口径),修「末根触发漏计/持仓显示未平」的回测/live 不一致(CR medium 3) - 补单测:trailing 价格回撤口径 / 从未盈利不触发 / 末根触发成交 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
flush_protective_at_close 按单 instrument 收尾、bind_strategy 单值——沿用引擎 「单 strategy 单 instrument per session」契约;多标的/多策略是引擎层整体未做项, 与引擎多模式化一并推进,非本闸单独修。CR 两条新 medium 仅在该未支持场景触发。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- PROTECTIVE_EXIT_TAGS 从 engine.position_guard 移到中立层 model.orders(Order.tag 约定同源),position_guard/exchange/report/live_runner 均改引中立层;删掉 exchange.flush_protective_at_close 里打补丁的局部 import → 双向环消除 - 多策略防呆:BacktestEngine.add_strategy 在 guard 启用下挂第二个策略即抛 RuntimeError;PositionGuard.bind_strategy 绑第二个不同 strategy_id 也抛——把 「单策略限制」从静默错误结果转成明确报错(未启 guard 的多策略回测不受影响) - 补单测:bind 拒第二策略 / add_strategy 拒第二策略 / 无 guard 仍可多策略 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
显式传 rules + 启用 guard 时,guard 的 SELL 当根仅入 pending、下一根才成交, 同 bar 策略 BUY 经 RiskEngine 时查不到平仓记录 → 基于 closed_trades 的锁可能 放过重入单。生产 runner(rules=None)不触达、live 顺序路由不受影响;严格联动走 live。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
之前只豁免行为型锁 enforce,漏了 check_order_notional:配了 max_order_notional 时, 价涨/累积建仓后保护性 SELL 的 notional 必然超单笔上限 → 被 RISK_REJECTED → 持仓 不动 → 每根 bar 重试又被拒 → 止损静默失效(take_profit/trailing 尤甚)。改为两道闸 对保护性 tag 全豁免:notional 上限是防胖手指超大开仓单,平实际持仓量不属此列。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
order.tag 是策略可控字段,仅靠它判保护性出场 → 策略写 Order(tag="stop_loss") 即可
绕过 enforce + notional 上限(熔断锁期内继续开仓)。改为不可仿冒双因子:tag ∈ 保护集
**且** client_order_id 以 GUARD_ORDER_PREFIX("guard-")开头(由框架生成)。新增
model.orders.is_protective_order 集中判定,live_runner / exchange.flush 共用。
附带(CR medium):
- restore_position docstring 注明 resume 不还原 guard _peak_mark → trailing 首触延迟
(硬止损/止盈不受影响,trailing 默认关)
- backtest 末根 snapshot 注明 Portfolio.snapshot 对同 ts 覆盖、不产生重复点(bot 漏看
去重逻辑,实为非问题)
- 单测:tag 仿冒(无 guard 前缀)→ is_protective_order False、不豁免
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
is_protective_order 之前只校验 tag + guard 前缀,策略若同时伪造二者 + side=BUY 仍可 借豁免下超大开仓单(跳过 notional + enforce)。guard 永不生成 BUY,故加 side==SELL 作 第三因子,三者缺一即非保护单。补 BUY 仿冒回归测试。 附带(CR medium): - live_runner 出单日志区分「策略信号」vs「框架 guard 保护性出场」,不再混算误导运维 - TS BacktestReport.protective_exits 改为非 optional(Python default=0 恒带该字段) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ajor) 之前 report 按 f.tag in PROTECTIVE_EXIT_TAGS 计数,但策略也能自打 tag="stop_loss" → 框架 guard 止损与策略自身止损混算,agent 误报"框架止损 N 次"。改为成交侧用三因子 is_protective_signature(side+tag+guard 前缀)算出 FillRecord.is_guard,report 按 is_guard 计数。新增 is_protective_signature 原语(供 Order 路径与成交侧共用),补策略自带 tag 不计入的回归测试。docstring 补三因子「非对抗性防伪、依赖 AST 审计+沙箱」安全前提。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
背景
回测只是历史,无法保证未来跑模拟盘的情况。把止盈止损「写进策略再回测」解决不了这个问题——为让回测曲线好看而调出来的止损位本身就是过拟合参数,未来一样不灵。
代码实测确认一个真实缺口:回测引擎与 live session 都是纯信号驱动,既有风控规则(MaxDrawdown / StoplossGuard / Cooldown)只「挡开仓」、从不强平已有持仓——单仓失控时没有兜底刀。
本 PR 加一道独立于策略 alpha、回测与 live 共用、行为一致的持仓级保护止损(灾难性兜底),专门封尾部风险。详见 ADR-0052(本地
docs/miro/,按约定不入 git)。改动
新组件
engine/position_guard.py+ 接入两套引擎 + 配置贯通 + 报表/工具透出,共 5 个 commit:close_detector→exit_reason,直发EXECUTION_ENGINE_ENDPOINT绕过开仓闸update_mark之后同一逻辑点调guard.evaluate,保证回测与模拟盘行为一致protective_*三项默认(硬止损 0.20,止盈/移动止损默认关);live runner 对保护性 tag 跳过enforce(回撤熔断锁期内仍能平仓)BacktestReport.protective_exits计数;回测 vs livefeed_bar同口径回归BacktestResponse字段 + runner 映射 + orchestration TS client/tool,agent 能看到「框架兜底本次触发 N 次」设计原则
验证
uv run ruff check .✅ /uv run pytest✅ 723 passed(含新增test_position_guard.py9 例 + e2e 一致性回归 3 例)tsc --noEmit✅注意
protective_exits已到 API 响应 + agent 工具描述;前端展示可作后续增强🤖 Generated with Claude Code