4、时间戳设计:时间戳精度、格式与时区处理
时间戳这东西,看着简单,坑却不少。我见过太多系统因为时间戳没设计好,导致数据对不上、回测结果偏差、甚至实盘交易出问题。今天咱们就把这块彻底聊透。
4.1 时间戳精度:毫秒、微秒、纳秒怎么选?
先说精度。Tick级数据,说白了就是每一笔成交或每一次报价的瞬间快照。精度不够,数据就失真了。
毫秒(ms):大部分交易所的行情数据,比如国内期货、股票,通常就是毫秒级。我个人习惯,如果对接的是CTP这类接口,毫秒就够了。
微秒(μs):高频交易场景,比如股指期货、外盘期货,很多交易所已经提供微秒级时间戳。我记得有一次做美股Level2数据接入,纳斯达克的订单流时间戳就是微秒的。这时候你用毫秒去存,同一毫秒内可能挤进来几十笔订单,顺序都乱了。
纳秒(ns):极高频场景,比如FPGA硬件加速、自营做市商。说实话,软件层面很难达到纳秒级精度,更多是硬件层面的需求。我建议普通量化系统做到微秒就够了,别盲目追求纳秒。
| 精度 | 典型场景 | 存储建议 |
|---|---|---|
| 毫秒 | 国内股票、期货 | int64 存储毫秒数 |
| 微秒 | 外盘、高频策略 | int64 存储微秒数 |
| 纳秒 | FPGA、硬件加速 | int64 存储纳秒数 |
4.2 时间戳格式:Unix时间戳 vs ISO 8601
格式问题,说白了就是「存什么」和「怎么传」。
Unix时间戳:从1970年1月1日0点开始的整数或浮点数。优点是计算快、存储小、跨语言无歧义。我建议内部存储和计算都用Unix时间戳,尤其是微秒或纳秒级的整数。
ISO 8601:比如 2024-03-15T14:30:00.123456Z。可读性好,适合日志、API传输、人眼查看。但解析慢、存储大。我一般在对外接口、日志记录时用ISO 8601,内部处理还是转回Unix时间戳。
// 示例:Unix微秒转ISO 8601
// 输入:1710484200123456 (微秒)
// 输出:2024-03-15T14:30:00.123456Z
func UnixMicroToISO(micro int64) string {
sec := micro / 1_000_000
nsec := (micro % 1_000_000) * 1000
t := time.Unix(sec, nsec).UTC()
return t.Format("2006-01-02T15:04:05.000000Z")
}
4.3 时区处理与夏令时问题
这是最容易被忽视的坑。我曾经因为时区问题,导致回测数据差了整整一小时,查了两天才找到原因。
时区处理:我建议所有内部存储和计算统一使用UTC时间。为什么?因为UTC没有夏令时,不会出现时间跳变。展示给用户时再转成当地时区。你想想看,如果数据库里混着东八区和UTC,数据对账时得多痛苦。
夏令时问题:美国、欧洲很多市场有夏令时。每年3月和11月,时间会突然跳一小时。如果你用本地时间存储,那3月某一天只有23小时,11月某一天有25小时。回测时数据序列就断了。
具体做法:
- 数据入库前,统一转成UTC时间戳
- 对外API接口,可以同时返回UTC和本地时间
- 前端展示时,根据用户时区做转换
- 夏令时切换前后,做好数据校验
4.4 知识体系结构图
下面这张图,把时间戳设计的核心逻辑串起来了。你可以看到,从数据源到存储到展示,每一步都有对应的处理策略。
4.5 实战建议总结
嗯,最后总结几条实在的:
- 存储用Unix微秒整数,计算快、无歧义
- 日志和API用ISO 8601,方便人看
- 所有系统统一UTC,别碰本地时间
- 夏令时切换前后,加个数据校验逻辑
- 精度选微秒,大部分场景够用了
时间戳设计,说白了就是「存得准、算得快、看得懂」。做到这三点,你的Tick级数据接口就稳了。
公众号:蓝海数据掘金营,微信deep3321