从一次回测翻车说起
不知道你有没有过这种体验:回测曲线在周五到周一交接的地方,总会出现一段不符合策略逻辑的急涨急跌。我刚入门时碰到过很多次,一度怀疑是自己指标计算写错了,逐行debug了两天。后来我偶然检查了一下原始tick的分时分布,才发现周六和周日的时间里,拉取到的tick数量是零。
这其实不是接口的问题,而是XAUUSD本身就不是24小时连续交易的品种。周五纽约收盘后,全球黄金市场进入休息,所以大部分实时行情API在这段时间内都不会推送任何数据。对于刚接触黄金量化的同学,很容易忽视这个特性。
缺失数据如何影响回测准确性
回测引擎是按时间轴一步步前进的,周六和周日没有新价格进来,引擎就会默认价格保持不变,一直停在周五的收盘价。等到周一早上第一笔成交跳出来,价格可能已经大幅偏离,你的策略模型在图表上看到的是一条垂直的价格线。
举个例子:假设你使用双均线交叉系统,周五收盘时短期均线还在长期均线之下,本周一开盘价格直接高开10美元,短期均线可能瞬间上穿长期均线,产生一个虚假的买入信号。但实际上这只是周末积累的跳空,并不是日内趋势改变。模型误把“跳空”当成了“连续趋势”,这就是回测结果失真的根本。
几种填补方法的实战对比
我当时试了各种办法,从最简单的到稍复杂的,效果差别很大:
| 处理方式 | 回测一致性 | 实现难度 | 推荐度 |
|---|---|---|---|
| 直接忽略周末 | 差 | 简单 | 不推荐 |
| 用周五收盘价填充 | 中等 | 简单 | 勉强可用 |
| 用周一开盘价替代 | 中等 | 简单 | 一般 |
| 标记跳空区间 | 较好 | 中等 | 推荐 |
| 引入第三方参考数据 | 好 | 较高 | 按需选择 |
忽略周末显然不行。用周五价格填充虽然数据完整了,但背离了实盘中持仓过周末的资金风险。我个人最推荐标记跳空法——把周五收盘到周一开盘的价差单独存成一个“事件”,回测时不补数据,而是事后分析策略在跳空下的表现。
手把手:用Python隔离周末数据
我自己的项目中,会在收到实时行情tick后立即加一层时间过滤。接入ALLTICK API的实时API后,每笔数据都附带时间戳,我可以很方便地判断这一天是不是周末,然后分流处理。
import websocket
import json
from datetime import datetime
def on_message(ws, message):
data = json.loads(message)
timestamp = datetime.fromtimestamp(data['time'])
# Weekend ticks are logged only, never fed to strategy
if timestamp.weekday() >= 5:
print(f"Weekend data {data['price']} stored as gap reference")
# Save to gap buffer for backtesting
store_gap_data(data)
else:
# Normal weekday ticks trigger strategy logic
process_tick(data['symbol'], data['price'])
ws = websocket.WebSocketApp(
"wss://apis.alltick.co/websocket-api/stock-websocket-interface-api/transaction-quote-subscription",
on_message=on_message
)
ws.run_forever()
这样一来,实时交易中策略永远不会收到周末的干扰数据;回测时我把本地缓存的跳空事件重新注入,模拟出周一开盘瞬间的价格断层。
一个稳健的回测习惯
现在我做黄金回测,已经形成一套固定流程:
- 回测之前,先用交易时段表把时间轴切成一段段,确保不会把周末算入连续K线。
- 每周跳空数据单独维护一张表,用来评估策略对跳空的容忍度。
- 实盘和回测共享同一套数据清洗逻辑,绝不“为了让曲线好看”去填补数据。
黄金行情API在周末静默,其实是市场的真实状态。与其费力气虚构连续数据,不如让回测像实盘一样去面对跳空——这样你得到的权益曲线才真正有参考意义。
共同学习,写下你的评论
评论加载中...
作者其他优质文章
