跳转到内容

策略复用与回测

研究单元格中仍然是可自由编辑的普通 Rust 代码。这些辅助工具只是让你更方便地复用现有策略代码和已保存结果,不会取代自定义分析,也不会引入另一套交易引擎。

在 Strategy code & backtests 下:

  • Load research tools 将策略的公共库和组件加载为 project::,同时加载项目的常规依赖及回测、研究辅助工具。它不会连接数据源,也不会运行策略。
  • Add discovery cell 列出已完成的项目回测和图参数 ID。
  • 选择基准和候选结果,然后点击 Add comparison cell。
  • Refresh saved runs 在实验后更新选择器,不重置变量。
  • Add signal chart & heatmap 为所选运行创建可编辑示例。
  • Add isolated backtest cell 使用笔记本中已加载的历史快照。

Add… cell 按钮只插入代码,不执行。Load research tools 会编译并初始化运行时,但绝不运行已保存的笔记本单元格。如果正在运行的会话已导入同一项目修订版,它会复用该会话,不重新编译、不重置变量,也不将输出标记为过时。新增或变更的导入会重置会话变量,并将先前输出标记为过时。

加载工具时,会恢复已加载的 K 线及其原始日期和时间配置。修改会话中的日期输入,不会改变现有快照所属的时间区间;如需使用其他区间,必须重新加载数据。

首次导入会解析项目依赖,编译 AQE、Polars 和导出的项目模块,然后链接笔记本辅助工具并恢复已加载快照。会话面板显示准备/编译阶段和耗时;Stop/reset 可取消操作。编译后的依赖按笔记本缓存,在重置和应用重启后仍保留。笔记本冷构建独立于桌面应用或策略可执行文件的构建,仍可能需要数分钟。

笔记本单元格和导入的依赖使用以快速构建为目标的开发配置:优化级别 0、256 个代码生成单元、增量编译、无调试符号、不启用链接时优化。调试断言、溢出检查和 panic 展开仍然启用。这套配置优先缩短编译时间,而不是提高执行速度,因此笔记本耗时不能作为发布模式策略的基准测试结果。这些覆盖设置只适用于笔记本编译,不影响实时策略构建、普通回测或隔离实验子进程。

更新 AQS 后,重启笔记本会话以采用新的编译器设置。配置变化时,Cargo 可能重建依赖一次,随后加载会复用该缓存。无需删除编译缓存。

重复研究时,保留同一笔记本及其编译缓存。使用 Refresh saved runs 更新回测选择器,无需编译。编辑本项目源代码或清单后,再次点击 Load research tools。编辑 AQE 或其他外部路径依赖后,先使用 Stop/reset,再选择 Load research tools,以强制重新检查导入。重置清除变量,但不清除依赖缓存;之后重新运行自定义配置单元格。

例如,导出 openingrange 模块的 Three Amigo 项目可以使用其实际 Alpha 实现:

use project::openingrange::Openingrange;
let alpha: Openingrange = Openingrange::new(
45, "07:00".to_string(), 14, 2.0, 1.5, false,
);
let study: AqsBacktest = aqs_run_alpha(alpha, &research_snapshot).await?;
let btc = study.bars("BTCUSD", "5m")?;
let signals = study.signals("BTCUSD")?;
aqs_chart(&btc).candles().signals(&signals)?.show()?;

请使用组件实际的模块名称和构造函数。aqs_run_alpha 按照 Alpha 生命周期,使用按时间筛选的历史数据执行 AQE 历史回放。这是仅针对信号的研究,不包含入场提交处理器,也不是策略收益回测。如果 Alpha 模型依赖自定义生命周期配置、额外周期、共享变量或执行状态,请在完整策略中测试。

与导入代码交互时,请像上例一样显式标注变量类型。即使普通 Cargo 构建能够推断类型,Rust 笔记本的增量类型分析仍可能要求标注。如果单元格提示无法确定变量类型,请添加标注,例如 let btc: DataFrame = ...;。如果诊断信息还要求重置,请重置会话并重新运行初始化代码。

导入的指标可以直接操作 data.bars(symbol)?。导入的信号处理器需要策略上下文和适当的交易信号状态。将其注册到自定义 AQE Strategy 中,然后运行该策略:

// my_strategy implements AQE's existing Strategy interface.
let run = aqs_run_strategy(
my_strategy, &research_snapshot, 100_000.0, 10,
).await?;
aqs_table(&run.trades()?, 20)?;

这种自定义回放使用 PaperBroker 的默认费用,以及显式传入的资金和杠杆参数,不会继承节点图的生命周期注册或费用设置。如需保留这些设置,请使用下文基于已保存节点图的实验。你也可以直接在 Rust 单元格中定义函数、指标、模型和策略。

aqs_table(&backtests.list()?, 100)?;
let run = backtests.load("backtests/your-run-id")?;
run.metrics()

列表包含关联项目 backtests/ 目录中的已完成运行,以及 research/experiments/ 中已完成的实验。请使用列表返回的 ID;直接传入绝对路径或尝试访问父目录都会被拒绝。指标文件和 backtest.db 必须同时存在。

let equity = run.equity()?;
let trades = run.trades()?; // Closed round trips
let insights = run.insights()?; // Full saved insight records
let btc = run.bars("BTCUSD", "5m")?;
let fills = run.table("trade_log")?;
let monthly = run.table("monthly_returns")?;
aqs_table(&insights, 20)?;
aqs_plot(&equity, "equity")?;

bars 按完全匹配的品种和周期,读取已保存的非特征数据流,并添加一个以 Int64 表示 UTC 毫秒数的 timestamp 列。equity 会添加同样的时间戳列,以及 return_pct(相对首次观测净值的变化)和 drawdown_pct(相对截至当前已观测净值峰值的回撤)。这些收益数据没有经过现金流调整。

table(name) 保留存储的列名和数据。可用表包括 K 线、账户历史、交易信号、交易日志、完整开平仓记录、月度收益、时间绩效、回撤序列、策略/市场状态绩效、因子敞口、相关性、持仓集中度、滑点、交易最大不利变动、交易形态绩效、滚动夏普比率、参数扫描和滚动前推结果。旧运行可能缺少某些表,此时会报错,而不是编造数据。

完整读取每次请求上限为 250,000 行和 64 MiB。超过限制会报错,不会静默截断结果。大型运行需显式分页:

let (page, has_more) = run.table_page("insights", 10_000, 0)?;
// Next page: run.table_page("insights", 10_000, 10_000)?
let baseline = backtests.load("backtests/baseline-id")?;
let candidate = backtests.load("backtests/candidate-id")?;
aqs_compare_backtests(&[&baseline, &candidate])?;
aqs_returns_heatmap(&[&baseline, &candidate])?;
let differences = aqs_compare_insights(&baseline, &candidate)?;
aqs_table(&differences, 30)?;
AQS 研究笔记本,展示用于比较回测和交易信号的 Rust 代码、账户汇总,以及两次已保存回测的净值变化和回撤图表。AQS 研究笔记本,展示用于比较回测和交易信号的 Rust 代码、账户汇总,以及两次已保存回测的净值变化和回撤图表。

通过账户汇总、净值变化曲线和回撤曲线,比较已保存的回测结果。查看全尺寸比较截图查看全尺寸比较截图。

aqs_compare_backtests 接受 2–6 次运行,输出汇总、净值变化叠加图和回撤叠加图。统计会使用全部已加载的账户观测数据,曲线则仅在显示时采样。各次运行保留实际观测到的 UTC 时间,不会前向填充缺失数据,也不会虚构一个共同观测点来重新设定基准。

在将差异归因于模型更改前,检查测试区间、初始资金、现金流、源代码修订版、数据集、预热、资产规格和费用是否匹配。辅助工具可以显示不同期间,但不会声称它们属于受控实验。仍可通过 metrics() 获取已记录指标。

aqs_compare_insights 根据品种、确切创建时间、Alpha 模型、方向、周期和父子状态匹配生成的信号,而不是使用随机生成的交易信号 UUID。重复信号也会计数。它返回基准与候选运行的信号数量、匹配数量,以及各次运行独有的信号。它不会据此认定成交或盈亏相同;如需核实这些内容,请检查完整的交易信号表和交易表。

旧版 aqs_compare(&data, symbols, "close") 比较的是市场价格,而不是回测绩效。

let candles = baseline.bars("BTCUSD", "5m")?;
let generated = baseline.signals("BTCUSD")?;
aqs_chart(&candles).candles().signals(&generated)?.show()?;
let executed = baseline.fills("BTCUSD")?;
aqs_chart(&candles).candles().signals(&executed)?.show()?;

生成信号的标记使用 created_at,入场成交的标记使用 filled_at。信号已生成不代表订单已成交。自定义标记数据帧需要 timestamp(UTC 毫秒)、side(Buy/Sell,不区分大小写),以及可选的 label。请自行筛选为图表所用的单个品种,最多使用 500 个标记。

交互式图表会将标记放在事件之前最近的一根已显示 K 线上,并在文字中保留确切的事件时间。超出范围的标记不显示。降采样可能改变标记的显示位置,但不会改变存储的时间戳。AQI 文章快照中的已保存时间序列图使用相同的交互式渲染器,并保留带有事件时间说明的静态 SVG 备用图。

基于已保存节点图运行隔离实验

Section titled “基于已保存节点图运行隔离实验”

保存图并加载历史数据,然后检查实际节点 ID/输入名称:

project_parameters
let baseline = aqs_backtest()
.name("Baseline")
.snapshot(&research_snapshot)
.run()?;
let candidate = aqs_backtest()
.name("Candidate")
.snapshot(&research_snapshot)
.parameter("actual-node-id", "actual-input-name", 45)
.run()?;
aqs_compare_backtests(&[&baseline, &candidate])?;

运行器会为项目源代码和已保存的节点图创建快照,验证历史 Parquet 文件的指纹,生成 CSV 输入,并通过常规 AQE 代码生成流程使用 PaperBroker。节点图中的仓位计算、风险设置、处理器顺序、生命周期和费用配置都会保留。参数覆盖只对本次实验生效,不会改写原始 .aqmeta、源代码或清单文件,也不会包含编辑器中未保存的节点图修改。请先确定超参数默认值;运行器不会隐式启动组合扫描。

每个实验位于 research/experiments/<id>/,包含源代码/配置快照、provenance.json、execution.log 和普通 AQE 回测产物。构建复用 research/experiment-target/。Stop/reset 会终止笔记本进程树,包括正在运行的实验。失败运行保留诊断信息,不会列为已完成。

外部路径依赖仍引用现有源目录,不会复制进实验。比较运行时,请保持这些依赖稳定。项目锁文件用作起点,但 Cargo 可能解析研究构建所需的变更;复现结果时,请检查实验最终的 Cargo.lock。

**数据模型限制:**CSV 回放使用合成资产,不使用 MT5 合约规格。已加载的数据周期必须与节点图的执行周期完全一致;缺失的数据流不会被其他数据替代,也不会前向填充。仅加载一个周期,并不会提供额外的特征周期或测试区间之前的预热历史。如果模型需要这些数据流,请检查数据覆盖范围和日志。这些结果不能直接等同于使用经纪商数据源的回测或实时执行结果。隔离节点图运行器暂不支持带自定义构建脚本的项目。

选定的代码、比较图、标记图、热力图和表格预览,都可加入 AQI 文章的笔记本块中。阅读时不会执行任何内容,分享也不会自动上传项目源代码目录、原始数据库、数据集或凭据。保存或发表文章前,请检查代码、打印出的路径和数据预览。