一、时钟同步基础:为什么回测需要精确时钟?
做回测系统这么多年,我见过太多人栽在时钟问题上。
你可能会想:不就是个时间戳吗?系统时间拿过来用不就行了?
嗯,没那么简单。
回测的本质是什么?是模拟真实交易环境。真实交易中,每一笔订单的到达时间、成交时间、行情数据的产生时间,都精确到毫秒甚至微秒。如果回测时的时钟不准,你模拟出来的结果就是空中楼阁。
时钟不准会带来什么后果?
我举个例子。假设你在回测一个高频策略,策略逻辑是:
- 在 09:30:00.000 收到行情 tick
- 在 09:30:00.003 发出订单
- 在 09:30:00.008 收到成交回报
如果回测系统的时钟比真实时间慢了 5 毫秒,会发生什么?
你的订单发出时间变成了 09:30:00.008,成交回报变成了 09:30:00.013。整个时间线都偏移了。更可怕的是,如果行情数据的时间戳和订单时间戳来自不同的时钟源,那偏差可能更大。
说白了,时钟不准会让你的回测结果完全失真。
核心结论:回测的时钟精度,直接决定了策略评估的可信度。时钟偏差 1 毫秒,对于高频策略可能就是天堂和地狱的区别。
时钟同步的核心指标
我个人习惯关注三个指标:
| 指标 | 说明 | 回测要求 |
|---|---|---|
| 精度 | 时钟与真实时间的偏差 | 至少毫秒级,高频策略需微秒级 |
| 稳定性 | 时钟偏差是否随时间波动 | 偏差应保持恒定,不能忽快忽慢 |
| 单调性 | 时间戳是否严格递增 | 必须保证,否则回测逻辑会乱 |
这里我要特别强调单调性。我在项目中遇到过一个问题:某次回测结果异常,查了半天发现是系统时钟被 NTP 服务往回调整了 200 毫秒。结果就是,后面的时间戳比前面的还小,回测引擎直接崩溃。
避坑指南:我曾经因为没检查时钟单调性,浪费了整整两天排查一个回测 bug。从那以后,我每次启动回测都会先跑一个时间戳单调性检查。
二、时钟源的选择:系统时钟 vs 网络时钟
现在我们来聊聊时钟源的选择。这是每个回测系统开发者都要做的决策。
系统时钟
系统时钟就是操作系统维护的时钟。在 Linux 上,你可以通过 clock_gettime() 获取。它的特点是:
- 速度快:获取时间戳的延迟极低,通常在纳秒级别
- 方便:不需要网络,本地就能用
- 但精度有限:普通 PC 的系统时钟精度在毫秒级,经过优化可以到微秒级
我个人的建议是:如果你的回测策略交易频率不高(比如分钟级、小时级),系统时钟完全够用。但如果你做高频回测,系统时钟的精度可能不够。
小技巧:在 Linux 上,使用 clock_gettime(CLOCK_MONOTONIC, ...) 而不是 gettimeofday()。前者是单调时钟,不会被 NTP 调整影响,能避免我前面说的那个坑。
网络时钟
网络时钟指的是通过网络协议(如 NTP、PTP)同步的时钟。它的特点是:
- 精度高:NTP 可以达到毫秒级,PTP 可以达到微秒甚至纳秒级
- 可追溯:时间源来自原子钟或 GPS,全球统一
- 但依赖网络:需要网络连接,且网络延迟会影响同步精度
你想想看,如果你的回测需要和真实市场数据对齐(比如回测时使用历史 tick 数据),网络时钟几乎是必须的。因为市场数据的时间戳通常来自交易所的时钟,而交易所的时钟是通过 GPS 或 PTP 同步的。
如何选择?
我一般这样决策:
- 低频策略(分钟级以上):系统时钟就够了。做好单调性检查,别让 NTP 往回调时间就行。
- 中频策略(秒级):建议使用 NTP 同步后的系统时钟。配置好本地 NTP 服务,定期同步。
- 高频策略(毫秒级及以下):必须使用 PTP 或硬件时间戳。我建议直接上 PTP 硬件时钟,精度有保障。
我的经验:做高频回测时,我习惯在服务器上装一块支持 PTP 的网卡,配合硬件时间戳。虽然成本高一点,但换来的是回测结果的可信度。这个投入值得。
代码示例:检查时钟源
下面是一个简单的 Python 示例,用来检查当前系统的时钟源和精度:
import time
import os
def check_clock_source():
"""检查当前系统的时钟源"""
# 检查是否支持 CLOCK_MONOTONIC
try:
start = time.clock_gettime(time.CLOCK_MONOTONIC)
time.sleep(0.001)
end = time.clock_gettime(time.CLOCK_MONOTONIC)
print(f"CLOCK_MONOTONIC 精度: {(end - start) * 1000:.3f} ms")
except:
print("不支持 CLOCK_MONOTONIC")
# 检查 NTP 同步状态
if os.path.exists('/var/run/ntpd.pid'):
print("NTP 服务正在运行")
else:
print("NTP 服务未运行,建议配置")
# 检查 PTP 硬件时钟
try:
with open('/sys/class/ptp/ptp0/clock_name', 'r') as f:
print(f"PTP 硬件时钟: {f.read().strip()}")
except:
print("未检测到 PTP 硬件时钟")
if __name__ == "__main__":
check_clock_source()
这段代码我经常在部署回测环境时先用一下,快速了解当前机器的时钟状况。
时钟同步架构图
下面这张图展示了我常用的回测时钟同步架构:
这张图展示了我常用的三层架构。时钟源层提供基准时间,同步层负责把时间同步到本地,回测引擎层管理时间戳的生成和校验。每一层都有明确的职责,出了问题也容易排查。
再次提醒:不管你选哪种时钟源,一定要在回测开始前做一次时间戳单调性测试。我曾经因为跳过这一步,结果回测跑了三天才发现数据有问题。那种感觉,嗯,不想再体验第二次。
好了,关于时钟同步基础就聊到这里。记住一句话:回测的时钟精度,决定了你策略评估的可信度。别在这个基础问题上省钱省力,否则后面吃亏的是你自己。
一句话总结:系统时钟够用但有限,网络时钟精准但有依赖。根据策略频率选,做好单调性检查,别让时钟坑了你。