3、数据模型设计:时间序列数据模型、订单簿数据结构、交易对与资产模型、数据存储方案(Parquet/Feather)、数据清洗与预处理
做回测引擎,说白了就是跟数据打交道。你策略写得再花哨,数据模型设计得不好,回测结果就是垃圾。我见过太多人一上来就写策略,结果数据格式不对、时间戳对不齐、订单簿重建出来全是错的……嗯,今天咱们就把这块地基打牢。
3.1 时间序列数据模型
回测里最核心的数据,就是K线(OHLCV)。我个人习惯用时间戳作为索引,而不是自增ID。为什么?因为时间戳天然有序,而且能直接对齐外部数据源。
来看一个典型的结构:
// 用结构体表示一根K线
struct KLine {
int64 timestamp; // 毫秒级时间戳
float open; // 开盘价
float high; // 最高价
float low; // 最低价
float close; // 收盘价
double volume; // 成交量
int32 count; // 成交笔数(可选)
};
这里有个坑——时间戳的精度。我在项目中遇到过,有的交易所给的是秒级,有的是毫秒级,甚至微秒级。你如果不统一,回测时K线对不齐,策略信号会乱跳。
3.2 订单簿数据结构
订单簿(Order Book)是高频回测的硬骨头。它本质上是一个价格-数量的映射,但实现方式决定了你的回测速度。
我推荐用跳表(Skip List)或红黑树来维护订单簿。为什么不用数组?因为订单簿的插入和删除操作太频繁了,数组的O(n)复杂度扛不住。
一个精简的订单簿结构:
struct OrderBookLevel {
float price; // 价格
double quantity; // 数量
int32 orderCount; // 挂单数量
};
struct OrderBook {
vector<OrderBookLevel> bids; // 买单,按价格降序
vector<OrderBookLevel> asks; // 卖单,按价格升序
int64 timestamp; // 快照时间
};
3.3 交易对与资产模型
交易对(Trading Pair)是回测引擎里的基本交易单元。比如BTC/USDT,你得同时管理基础资产(BTC)和计价资产(USDT)。
我习惯这样设计:
struct TradingPair {
string baseAsset; // 基础资产,如 "BTC"
string quoteAsset; // 计价资产,如 "USDT"
float minQty; // 最小交易数量
float tickSize; // 最小价格变动
int32 precision; // 价格精度(小数位数)
};
资产模型更简单,就是一个资产-余额的映射:
map<string, double> wallet; // key: 资产名称, value: 可用余额
这里有个经验——永远用double存余额。float精度不够,尤其是做高频交易时,小数点后第6位的误差会累积成灾难。我曾经因为用float,回测跑了一周发现收益多了0.3%,查了两天才找到原因……
3.4 数据存储方案:Parquet vs Feather
回测数据动不动就是几十GB,选对存储格式能省一半时间。我主要用两种:
| 特性 | Parquet | Feather |
|---|---|---|
| 压缩率 | 高(可压缩到原始大小的20%) | 低(基本不压缩) |
| 读取速度 | 中等(需要解压) | 极快(内存映射) |
| 列式存储 | 是 | 是 |
| 适用场景 | 长期归档、跨平台交换 | 本地快速回测、中间结果 |
我个人习惯:原始数据用Parquet存,因为省空间;回测时转成Feather,因为读得快。你想想看,回测一次要读几百个文件,Feather能快3-5倍,省下来的时间够你多调几次参数了。
3.5 数据清洗与预处理
数据清洗是回测里最脏最累的活,但躲不掉。我总结了一套流程:
- 去重:同一时间戳的K线只保留一条。交易所偶尔会推送重复数据。
- 补缺失:如果某根K线缺失,用前一根的收盘价填充,或者直接删除(取决于策略敏感度)。
- 去异常值:价格超过3倍标准差?成交量突然为0?这些都得标记出来。
- 对齐时间戳:不同交易对的数据可能时间戳不一致,统一对齐到整点或整分钟。
我曾经接手过一个项目,回测年化收益50%,结果发现是数据里混入了几个价格极低的异常点,策略在那些点疯狂买入……嗯,从那以后我每次回测前都会先跑一遍数据质量检查。
3.6 知识体系总览
下面这张图概括了本章的核心内容,你可以把它当作数据模型设计的检查清单:
数据模型设计这块,说白了就是把现实世界的交易行为,用计算机能高效处理的方式表达出来。你设计得好,回测引擎跑得又快又准;设计得不好,后面全是坑。
我个人建议,刚开始做回测引擎时,先把时间序列和资产模型搞定,订单簿可以后面再优化。别想着一步到位,迭代开发才是正道。