4、回测框架搭建:事件驱动回测引擎设计、仓位管理模块、手续费与滑点模型、绩效评估指标体系
好,咱们直接进入正题。回测框架这东西,说白了就是你的策略实验室。没有它,你所有的想法都只是纸上谈兵。我个人习惯把回测框架拆成四个核心模块来搞:引擎、仓位、成本、评估。一个一个来。
4.1 事件驱动回测引擎设计
为什么用事件驱动?而不是简单的向量化回测?
向量化回测快,但太理想化了。它假设你可以在任意时刻以任意价格成交。现实吗?不现实。事件驱动回测模拟的是真实交易流程:行情来了 -> 处理信号 -> 下单 -> 成交确认 -> 更新持仓。每一步都有时间戳,有顺序。
我设计引擎的核心是一个事件队列。所有东西都是事件:Bar数据、成交回报、订单状态变更。引擎主循环就是不断从队列里取事件,然后分发给对应的处理器。
class EventEngine:
def __init__(self):
self.queue = deque()
self.handlers = {}
def register_handler(self, event_type, handler):
self.handlers[event_type] = handler
def run(self):
while self.queue:
event = self.queue.popleft()
handler = self.handlers.get(event.type)
if handler:
handler(event)
嗯,这里要注意。事件队列的优先级很重要。比如成交回报的优先级应该高于新的行情数据,否则会出现用旧持仓计算新信号的逻辑错误。我在项目中遇到过这个问题,当时回测结果异常漂亮,实盘却一塌糊涂。查了两天才发现是事件顺序搞反了。
4.2 仓位管理模块
仓位管理,说白了就是每次开多少仓的问题。别小看这个,它直接决定了你的风险暴露。
我一般把仓位管理分成两类:
- 固定比例:每次开仓用总资金的固定百分比。比如永远只用10%的资金开仓。
- 动态调整:根据当前波动率或账户净值动态调整。比如波动率大了就减仓,净值回撤了就降杠杆。
我个人更倾向于动态调整。为什么?因为市场环境在变,固定比例太死板了。
举个例子,你设定每次开仓10%。当账户从100万亏到50万时,你还在开10%?那风险敞口其实变大了。动态调整会根据当前净值重新计算,更合理。
class PositionManager:
def __init__(self, initial_capital, risk_per_trade=0.02):
self.capital = initial_capital
self.risk_per_trade = risk_per_trade
def calculate_position_size(self, entry_price, stop_loss_price):
risk_amount = self.capital * self.risk_per_trade
risk_per_share = entry_price - stop_loss_price
if risk_per_share <= 0:
return 0
return int(risk_amount / risk_per_share)
上面这个例子用的是风险百分比法。每次亏损不超过总资金的2%。你想想看,连续亏10次,也就亏了20%,还有翻身的机会。如果每次满仓干,亏两次就没了。
4.3 手续费与滑点模型
这个模块最容易被忽略,但恰恰是区分回测和实盘的关键。
手续费:包括佣金、印花税、过户费等。不同市场、不同券商都不一样。我建议直接按实际费率写死,别偷懒用估算值。
滑点:这个更复杂。滑点就是你下单的价格和实际成交价格之间的差异。流动性好的股票滑点小,小盘股滑点大。
我曾经犯过一个错误:回测时设了固定滑点1个tick,结果实盘时遇到极端行情,滑点直接到了5个tick。那一个月白干。
所以我现在用动态滑点模型:
| 市场状态 | 滑点设置 |
|---|---|
| 正常行情 | 1个tick |
| 高波动行情(波动率>95%分位) | 3个tick |
| 开盘/收盘前5分钟 | 2个tick |
class SlippageModel:
def __init__(self, base_slippage=0.0001):
self.base_slippage = base_slippage
def get_slippage(self, volatility, volume):
# 波动率越高,滑点越大
# 成交量越小,滑点越大
slippage = self.base_slippage * (1 + volatility * 10)
slippage *= (1 + 1 / (volume + 1))
return slippage
4.4 绩效评估指标体系
回测跑完了,怎么评价策略好不好?光看收益率太片面了。我一般看这几个指标:
- 年化收益率:把总收益换算成年化。注意复利和单利的区别。
- 最大回撤:从最高点到最低点的最大跌幅。这个指标比收益率更重要。回撤超过30%的策略,一般人拿不住。
- 夏普比率:超额收益除以波动率。说白了就是每承担一单位风险能获得多少超额回报。大于1算及格,大于2算优秀。
- 胜率与盈亏比:胜率高不一定赚钱,盈亏比高也不一定。两者要结合看。我见过胜率只有30%但盈亏比5:1的策略,长期下来收益惊人。
- 卡玛比率:年化收益除以最大回撤。这个指标比夏普更直观,直接告诉你每亏一块钱能赚多少钱。
最后,绩效评估一定要做样本外测试。把数据分成训练集和测试集,训练集上优化参数,测试集上验证效果。如果测试集上的表现远差于训练集,说明策略过拟合了。嗯,这个坑我踩过不止一次。
好了,回测框架的四个核心模块就这些。记住,框架是工具,不是目的。别在框架上花太多时间,把精力放在策略本身。但框架也不能太简陋,否则回测结果会误导你。