资讯详情

资讯详情

machine-learning-for-trading 如何验证回测与实盘引擎的信号一致性

machine-learning-for-trading 如何验证回测与实盘引擎的信号一致性【免费下载链接】machine-learning-for-tradingCode for Machine Learning for Trading, 3rd edition — from data sourcing to live execution.项目地址: https://gitcode.com/GitHub_Trending/ma/machine-learning-for-trading策略回测盈利、实盘跑一段时间后收益却对不上最常见的原因不是模型失效而是回测代码和实盘代码是两份实现某处细节不一致而回测根本发现不了。machine-learning-for-tradingML4T第 25 章的做法是同一个Strategy类同时跑在ml4t.backtest.Engine和ml4t.live.LiveEngine上然后逐字段比对两条信号流水signal tape不一致就让测试直接失败。本文基于该仓库25_live_trading目录下的两个 notebook走一遍完整验证路径先在单阶段信号比对上跑通再升级到分阶段的流水线校验最后得到一个可以进 CI 的 pass/fail 门禁。验证为什么分两级仓库 25_live_trading/README.md 对这两个 notebook 的定位是01_unified_framework_demo.ipynb用同一个双均线交叉策略跑两个引擎逐字段比较信号验证同一份策略代码回测与实盘输出一致。08_pipeline_verification.ipynb把一致性拆成特征、预测、信号、订单四个阶段每阶段是一个 gate门禁而不是报告最终产出 CI 可消费的退出码。01 只比较策略最终发出的信号足够证明单类实现的想法08 指出实盘流水线可能在信号一致的情况下仍在特征、预测或订单规模上漂移所以必须分阶段检查。本文按 01 → 08 的顺序执行。准备环境两个 notebook 都不需要真实券商或 API key全部使用模拟 broker 和确定性合成数据这是它们能作为测试而不是演练的前提。安装环境按 docs/installation.md 任选一条路径Dockerdocker compose pull ml4t后docker compose run --rm ml4t …或本地uvuv sync后uv run …。本文命令以本地uv路径书写Docker 路径把uv run换成docker compose run --rm ml4t即可。安装后用仓库自带的验证脚本确认所有组件可导入这是跑 notebook 前的唯一硬性检查uv run python scripts/verify_installation.py每一行输出PASS/FAIL全部 PASS 即可开始。第 01 个 notebook 读取本地 ETF 数据SPY/QQQ/IWM 的日线需要先下载免费无需 keyuv run python data/etfs/market/download.py注意所有命令都从仓库根目录执行。数据加载器按工作目录解析data/在章节目录里运行会报数据缺失。第 08 个 notebook 使用合成数据不需要任何下载。第一步单策略双引擎信号比对notebook 01无显示环境CI、服务器执行MPLBACKENDAgg PLOTLY_RENDERERjson uv run python 25_live_trading/01_unified_framework_demo.py本地有桌面环境时去掉前缀直接uv run python 25_live_trading/01_unified_framework_demo.py即可。也可以用 Papermill 缩减参数跑测试模式uv run pytest tests/test_chapter_notebooks.py -v -k 25_live_trading这个 notebook 的对照设计决定了比较的有效性执行前值得了解四件被钉死的事同一份数据load_etfs()加载一次过滤出2023-01-01至2024-01-01的 SPY/QQQ/IWM 日线两个引擎都只读这份数据raw_data。同一个策略类DualMAStrategy10/30 日均线交叉只交易 SPY两个引擎各自 new 一个实例参数完全相同。回测引擎的配置BacktestConfig(execution_modeExecutionMode.SAME_BAR, commission_rate0.0005)initial_cash100_000。实盘引擎的替身SimulatedBroker当前收盘价立即成交HistoricalReplayFeed按顺序重放历史 bar缺 bar 直接抛错而不是跳过。notebook 特意不用SafeBroker包一层因为风控拒绝订单会把引擎一致性测试变成风控测试。注意SAME_BAR的用途回测与模拟 broker 都在当前收盘价成交把执行因素从比较中剔除。notebook 明确说明这不是对可成交价格的主张打印出的收益率不能当回测结果引用。01 的结果判读运行末尾输出最终结论仓库中已执行的 notebook 输出文档示例为Backtest signals: 9 Live signals: 9 Matching signals: 9/9 Backtest final value: $… Signal tapes: identical (9/9 fields match)判断逻辑在代码里不靠肉眼看图表数量断言前置assert len(backtest_signals) len(live_signals)——信号条数不一致直接抛错因为逐条比较只在条数相同时有意义逐字段比较pivot 后逐信号对比timestamp、symbol、side以及price、fast_ma、slow_ma后三者容差 0.01任一字段不符该信号记为 mismatch并打印前三个不一致的行结论matches len(backtest_signals)时打印identical否则divergent。两个容易误读的输出notebook 自己给了说明信号数 ≠ 交易数。信号是一次下单交易analyzer 口径是一笔完整来回。策略买卖交替奇数条信号意味着期末还留着一个未平仓位交易数比入场数少一。feed_terminated/runtime degraded警告是正确行为。实盘里 feed 断流是故障重放里它是文件读完LiveEngine对两种情况打同一条日志。这也是 notebook 的结论之一重放实盘路径是一致性测试不是上线预演。如果输出divergent比较结果里带engine标签的对照表就是审计面失败会落到具体的日期和均线值而不是一个总数。第二步分阶段流水线校验notebook 08MPLBACKENDAgg PLOTLY_RENDERERjson uv run python 25_live_trading/08_pipeline_verification.py08 的输入是确定性合成 tape30 根分钟 bar、SPY/QQQ 两个品种、SEED 12345。关键细节是 per-symbol 偏移量写死为SYMBOL_OFFSETS {SPY: 101, QQQ: 202}而不是用 Python 内置hash()——后者按进程随机化会导致不同机器读到的 tape 不同、任何不一致都无法归因。tape 被设计成前半段涨、后半段跌QQQ 镜像保证 BUY 和 SELL 两条下单路径都被实际走到而不是只比较 HOLD。两侧流水线的差别在于包裹方式回测侧参考路径VerifiableStrategy(lookback10, threshold0.01) 同步的BacktestBroker每根 bar 记录feature_log/prediction_log/signal_log/order_log四个阶段的日志实盘侧同一个VerifiableStrategyLiveBroker异步适配器外面套SafeBrokershadow_modeTruemax_position_value100_000.0max_order_value50_000.0和ThreadSafeBrokerWrapper用asyncio.to_thread桥接同步策略。9 个 gate 与 CI 退出码TEST_NAMES定义的门禁共 9 项分两组组检查项提交前Feature Count Parity、Feature Value Parity、Prediction Parity、Signal Parity提交后Order Count Parity、Order Fill Parity、Cash Path Parity、Position Path Parity、Order ID Uniqueness值比较只对命名过的浮点字段用1e-10容差空日志或条数不等的比较永远失败空对空不可能通过。仓库中已执行的 notebook 输出文档示例为[OK] PASS: Feature Count Parity … Gate tests passed: 9/9 Gate tests failed: 0/9 Expected differences (informational): 0 Skipped: 0 [OK] ALL GATED VERIFICATION TESTS PASSED Technical parity confirmed between backtest and live pipelines Exit code: 0退出码语义0 全部 gate 通过1 有 gate 失败或 live 侧根本没有执行。最后一项值得强调——当 Papermill 的 async 环境约束导致某条流水线无法执行时notebook 对全部测试输出SKIPPED记录并在结尾打印[FAIL] LIVE PIPELINE NOT EXECUTED、exit code 1。skip 不是 pass两条路径都实际跑过才能评估一致性。失败时定位到具体阶段08 的设计原则是失败要能说出坏在哪一层notebook 的 Key Takeaways 给出了四个阶段各自捕获的 bug 类型Feature parity数据顺序、浮点顺序问题Prediction parity模型版本或预处理漂移Signal parity阈值或持仓状态 bugOrder parity规模sizing与取整差异。EXPECTED_DIFFERENCE集合当前为空是留给确实预期不同的阶段单独声明的契约比如特征 warm-up 差异任何差异必须走这条显式声明的路径harness 不会因为方便而豁免不一致。转成回归测试notebook 末尾给出了直接可放进 pytest 的断言模式test_parity_gate全部 skip 时直接AssertionErrorlive 未执行门禁不完整否则只要求非 skip、非expected_difference的结果全部通过。把 notebook 提升为 CI harness 时这就是tests/live/test_parity.py该带的断言并且应在 broker 包装层、sizing 逻辑或预处理代码变更时重跑notebook 给出的 Next 建议。这条验证路径的边界以下限制来自 notebook 原文是判读结果时必须带着的01 只验证了单品种、单年日线、单策略、无延迟无拒单的重放。replay feed 给引擎的是完整 bar没有部分成交、拒单和断连所以两个引擎在这些情况存在时仍一致不被此测试覆盖——08、07_order_state_machine.ipynb、12_ib_basket_rebalance_demo.ipynb 分别补这些部分。一致不等于策略好两个引擎对一个烂策略同样一致parity is not quality。08 的门禁依赖确定性 tape。合成数据只隔离技术漂移不覆盖真实市场噪声下的行为。Papermill 环境下 async 流水线可能被跳过此时应看到 9 条 SKIPPED exit code 1而不是 9/9 通过交互式非 Papermill执行才会得到完整结果。下一步01 的实盘路径刻意去掉了 broker 侧风控10_safety_risk_demo.ipynb 演示把这些控制订单/持仓/日损上限、shadow mode、kill switch加回来后各失败模式的行为真实 feed 与执行的一致性用需要真实 TWS/Alpaca 会话的部署循环 notebook 验证如 02_etfs_deployment_loop.ipynb这与本文的模拟路径是两种不同的验证目的不要混在同一条操作链里。【免费下载链接】machine-learning-for-tradingCode for Machine Learning for Trading, 3rd edition — from data sourcing to live execution.项目地址: https://gitcode.com/GitHub_Trending/ma/machine-learning-for-trading创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →