交易策略回测怎么做
交易策略回测必须先冻结规则、数据时点、样本与成本,再运行代码。本文提供一个只用 Python 标准库即可复算的合成数据示例,展示信号时序、单边成本与预期结果,并说明怎样检查前视、数据窥探、幸存者偏差与样本外污染。Bastix 没有统一回测引擎,不能把研究计算写成已上线产品能力。
交易策略回测的首要目标不是画收益曲线,而是证明规则、数据和代码在时间上自洽。最小样本都无法手算时,换成多年行情只会把错误藏得更深。本页先给一个合成数据例子,再说明怎样把同一协议迁移到获准的点时市场数据。
历史结果不能保证未来。回测只能回答“在固定数据版本、规则和假设下,代码产生了什么结果”,不能证明策略因果、可成交性或未来表现。
运行前冻结这张协议
| 项目 | 本例填写值 | 换真实数据时必须补充 |
|---|---|---|
| 研究目的 | 检查信号时序和成本记账 | 可证伪假设与决策用途 |
| 数据 | 10 个合成收盘价 | 来源、许可、时区、复权、披露时点、修订版本 |
| 规则 | 收盘价高于含当日的 3 日简单均值则下一段持有 | 完整阈值、标的池、再平衡和异常规则 |
| 成交 | 当日收盘形成信号,按下一段收盘收益记账 | 可用订单类型、延迟、成交量和价格假设 |
| 成本 | 仓位每变化 1,扣 0.1%;最后平仓再扣 0.1% | 佣金、税费、价差、滑点、冲击和容量 |
| 参数搜索 | 不搜索,只运行窗口 3 | 候选总数、选择规则和搜索预算 |
| 结果用途 | 仅验证实现 | 样本内/验证/样本外及停止条件 |
本例的成交设定是教学简化,不代表现实中能在收盘价成交。把它换成真实研究时,必须按市场和订单逻辑重写。
合成数据与可运行代码
保存下面代码为任意本地 Python 文件即可运行;只使用标准库。数据是人工构造的,不包含真实证券或收益主张。
prices = [100, 101, 102, 100, 99, 103, 104, 102, 105, 106]
one_way_cost = 0.001
gross_equity = 1.0
net_equity = 1.0
previous_position = 0
rows = []
for t in range(len(prices) - 1):
if t < 2:
position = 0
moving_average = None
else:
moving_average = sum(prices[t - 2:t + 1]) / 3
position = 1 if prices[t] > moving_average else 0
next_return = prices[t + 1] / prices[t] - 1
turnover = abs(position - previous_position)
cost = turnover * one_way_cost
gross_equity *= 1 + position * next_return
net_equity *= 1 + position * next_return - cost
rows.append({
"t": t,
"price": prices[t],
"moving_average": moving_average,
"position_for_next_period": position,
"next_return": next_return,
"cost": cost,
})
previous_position = position
# 样本结束时强制把剩余仓位降为 0,并计一次单边成本
net_equity *= 1 - abs(previous_position) * one_way_cost
print("positions:", [row["position_for_next_period"] for row in rows])
print("gross_return:", round(gross_equity - 1, 8))
print("net_return:", round(net_equity - 1, 8))预期输出为:
positions: [0, 0, 1, 0, 0, 1, 1, 0, 1]
gross_return: -0.0198798
net_return: -0.02574676这组负结果是有意保留的。代码审查不应因为结果难看就换窗口、删价格或隐藏成本。若本地输出不同,先检查 Python 版本之外的代码差异、切片边界、成本扣除和最后平仓。
用这个例子做四个失败测试
前视测试
信号在 t 只能读取 prices[t] 及之前的数据,收益从 t 到 t+1 发生。若移动均值或标准化过程访问 prices[t+1],就是未来数据进入信号。真实财务数据还要检查披露日,而不只是报告期。
成本测试
把 one_way_cost 分别设为 0、0.001 和 0.005,保存每组结果。成本不应只在最终收益上扣一次;它随仓位变化发生。换手高的规则可能在低成本假设下看似可行,稍微提高成本就失效。
参数搜索测试
如果继续测试 2 日、3 日、4 日直到找到最好结果,要把测试总数和全部结果一起记录,并预留独立样本外。Lo 与 MacKinlay 的NBER 论文说明数据窥探会严重扭曲检验;Bailey 等人的回测过拟合研究专门讨论大量搜索造成的样本外退化。
缺失和异常测试
把一个价格替换成 None,代码目前会失败。这是有价值的:它迫使研究者先定义缺失政策。真实数据中还要测试停牌、退市、涨跌停、复权事件、重复行和时区错位,不应默认前值填充。
换成真实数据前还差什么
点时数据与历史成分
使用当时可得的数据,而不是今天数据库中最终修订的值。财务字段以披露日控制可得性;指数和标的池保留历史成分及退市证券;公司行动与复权方法固定版本。
样本分离
样本内用于形成规则,验证集只做有限选择,样本外只在最终运行。反复查看样本外并调参后,它已经成为训练过程的一部分。应记录每次实验,而不是只保留最佳一次。
现实成交
说明信号何时可得、订单何时提交、用哪一档价格、未成交如何处理。加入佣金、税费、买卖价差、滑点、市场冲击、容量、最小申报单位、停牌和涨跌停。不同市场与产品的规则不能共用一个默认值。
基准和压力测试
与空仓、持有或更简单规则比较;改变成本、执行延迟、相邻参数与市场阶段。一个只在单一参数、单一时期成立的结果不能被概括成“有效策略”。
回测与程序化交易的监管边界
历史研究不等于真实下单。证监会程序化交易管理规定发布说明说明,程序化交易是通过计算机程序自动生成或者下达交易指令的行为。上交所实施细则进一步列出报告、交易软件版本、指令速率、风险监控等要求。
准备进入实盘前,应由相应券商、机构合规和专业人员确认适用规则;一段回测代码或研究页面不构成完成合规准备。
Bastix 没有统一回测引擎
Bastix 当前没有现成统一回测引擎。用户只能在受限代码工具中自行组织研究计算;自然语言策略结构化为 Beta,输出是研究与策略骨架,不是可直接执行算法。内置能力也没有券商连接和真实下单。1
因此本页 CTA 是“复制协议并独立复算”,不是“进入 Bastix 一键回测”。Bastix 可以帮助整理假设、数据清单和复核问题,但结果、代码和市场适用性仍由研究者负责。
下一步先原样运行合成例子,逐项完成前视、成本、参数搜索和缺失测试;全部结果与修改记录一致后,再把输入替换成授权的点时数据。需要生成规则草稿时,先使用AI 生成交易策略验收方法;想把回测放进更完整的量化研究流程,可参考量化交易研究指南。
事实脚注
Footnotes
-
Bastix《公开产品事实登记》0.1.0(取证日期 2026-08-14):“回测引擎”为 unsupported;“自然语言策略结构化”为 beta;“券商连接与真实下单”为 unsupported。相关市场数据链路也有覆盖、许可、延迟和修订边界。 ↩