第21章:订单流与量化回测:回测框架搭建、订单流因子构建、策略绩效评估
量化回测这件事,说白了就是让历史数据告诉你——你的策略到底行不行。
我见过太多人,拿着订单流数据兴奋得不行,上来就写策略,结果回测一跑,亏得底裤都不剩。为什么?因为回测框架没搭好,因子构建有漏洞,绩效评估只看收益率。
这一章,我就带你一步步把这三件事做扎实。
21.1 回测框架搭建:从零开始,别踩坑
回测框架的核心就三个字:快、准、稳。
快——数据加载和计算要快,别让CPU空转。准——撮合逻辑要贴近真实市场,别搞出「理想化成交」。稳——代码要健壮,别跑一半崩了。
我个人习惯用Python搭框架,因为生态好,pandas、numpy、vectorbt这些库都能用。但要注意,订单流数据是逐笔级别的,量很大,处理不好会卡死。
核心组件清单:
- 数据引擎:加载订单流数据(逐笔成交、逐笔委托),支持多周期切片
- 撮合引擎:模拟限价单、市价单的成交逻辑,考虑滑点和手续费
- 策略引擎:接收信号,生成订单,管理仓位
- 绩效引擎:计算收益率、夏普比率、最大回撤等指标
我曾经在项目里用纯Python循环处理百万级订单流数据,结果跑一次要半小时。后来改用numpy向量化操作,压缩到3秒。嗯,这里要注意:能用向量化就别用循环。
# 一个极简的回测框架骨架
import pandas as pd
import numpy as np
class OrderFlowBacktester:
def __init__(self, data, initial_capital=100000):
self.data = data
self.capital = initial_capital
self.position = 0
self.trades = []
def run(self, strategy_func):
for i in range(len(self.data)):
signal = strategy_func(self.data.iloc[:i+1])
if signal == 1: # 买入
self._buy()
elif signal == -1: # 卖出
self._sell()
return self._calc_performance()
def _buy(self):
# 实际项目中要加滑点和手续费
cost = self.data['price'].iloc[-1] * 1.001
shares = self.capital // cost
self.position += shares
self.capital -= shares * cost
self.trades.append(('buy', cost, shares))
def _sell(self):
if self.position > 0:
revenue = self.position * self.data['price'].iloc[-1] * 0.999
self.capital += revenue
self.position = 0
self.trades.append(('sell', self.data['price'].iloc[-1], self.position))
def _calc_performance(self):
total_return = (self.capital - 100000) / 100000
return {'total_return': total_return, 'trades': len(self.trades)}
避坑指南:我曾经在回测里忘了处理「停牌日」的数据,结果策略在停牌期间还假装成交,收益率虚高20%。记住:回测数据必须包含完整的交易日历,停牌日要跳过。
21.2 订单流因子构建:从原始数据到有效信号
订单流数据本身是噪音,你得把它加工成因子。
因子构建的核心思路是:从逐笔数据中提取出「不平衡」和「异常」。市场之所以有趋势,就是因为买卖力量不平衡。订单流因子就是量化这种不平衡的工具。
我常用的订单流因子有这几类:
| 因子类别 | 具体因子 | 计算方式 | 逻辑解释 |
|---|---|---|---|
| 成交量不平衡 | Delta | 主动买量 - 主动卖量 | 正Delta表示买方强势 |
| 成交笔数不平衡 | Trade Count Imbalance | 买方笔数 - 卖方笔数 | 笔数多说明散户参与度高 |
| 大单流向 | Large Trade Ratio | 大单成交量 / 总成交量 | 大单占比高说明机构在行动 |
| 订单簿压力 | Order Book Imbalance | (买一量 - 卖一量) / (买一量 + 卖一量) | 正值表示买盘支撑强 |
你想想看,如果Delta连续5根K线都是正的,而且大单占比也在上升,这说明什么?说明机构在偷偷吸筹。这时候你跟着做多,胜率会高很多。
# 构建Delta因子
def calc_delta(tick_data, window=10):
"""
tick_data: 逐笔成交数据,包含'price', 'volume', 'side'(1=买, -1=卖)
"""
tick_data['delta'] = tick_data['volume'] * tick_data['side']
tick_data['cum_delta'] = tick_data['delta'].rolling(window).sum()
return tick_data
# 构建大单因子
def calc_large_trade_ratio(tick_data, threshold=100000):
"""
threshold: 大单阈值,比如10万元
"""
large_trades = tick_data[tick_data['volume'] * tick_data['price'] >= threshold]
total_volume = tick_data['volume'].sum()
large_volume = large_trades['volume'].sum()
return large_volume / total_volume if total_volume > 0 else 0
实战技巧:因子不是越多越好。我建议你先从Delta和Order Book Imbalance这两个最基础的因子开始,跑通回测流程后,再逐步加入其他因子。贪多嚼不烂。
21.3 策略绩效评估:别只看收益率
收益率是最容易骗人的指标。
我见过一个策略,年化收益率80%,但最大回撤60%。你敢用吗?不敢。因为回撤60%意味着你账户从100万跌到40万,心理上根本扛不住。
所以绩效评估要全面,我一般看这几个维度:
- 收益类:年化收益率、累计收益率、超额收益
- 风险类:最大回撤、波动率、VaR(在险价值)
- 风险调整收益类:夏普比率、卡玛比率、索提诺比率
- 交易类:胜率、盈亏比、交易次数、平均持仓周期
举个例子,夏普比率低于1的策略,我基本不会实盘。为什么?因为夏普比率衡量的是「每承担一单位风险能获得多少超额收益」,低于1说明风险收益比不划算。
# 计算核心绩效指标
def calc_performance_metrics(equity_curve, risk_free_rate=0.03):
returns = equity_curve.pct_change().dropna()
# 年化收益率
annual_return = (1 + returns.mean()) ** 252 - 1
# 年化波动率
annual_vol = returns.std() * np.sqrt(252)
# 夏普比率
sharpe = (annual_return - risk_free_rate) / annual_vol
# 最大回撤
cumulative = (1 + returns).cumprod()
peak = cumulative.expanding().max()
drawdown = (cumulative - peak) / peak
max_drawdown = drawdown.min()
# 卡玛比率
calmar = annual_return / abs(max_drawdown) if max_drawdown != 0 else np.inf
return {
'annual_return': annual_return,
'annual_vol': annual_vol,
'sharpe_ratio': sharpe,
'max_drawdown': max_drawdown,
'calmar_ratio': calmar
}
避坑指南:我曾经在评估策略时,用了「未来数据」——回测时用了当天的收盘价来生成当天的信号。结果策略表现好得离谱,实盘一跑就崩。记住:回测必须严格避免未来函数,信号生成只能用到当前时刻之前的数据。
21.4 知识体系总览
下面这张图,是我对本章核心逻辑的总结。你把它存下来,以后搭回测框架时对照着看。
嗯,这一章的内容就到这里。回测框架、因子构建、绩效评估,这三件事是订单流量化的基石。你先把框架搭稳,因子做精,评估做全,后面写策略才不会翻车。
一句话总结:回测是策略的照妖镜,订单流因子是信号的放大器,绩效评估是风控的守门员。三者缺一不可。