
数据质量决定回测成败
量化交易的核心在于用历史数据验证策略逻辑。股票与期货市场的数据来源多样,交易所Level-1和Level-2行情、第三方数据商、经纪商接口,每个渠道的数据完整性、准确性和时间戳精度都有差异。缺失的tick数据、错误的除权因子、未调整的期货主力合约切换,都会让回测结果偏离真实交易。
期货数据特别需要注意合约换月。不同月份的合约价格存在价差,直接拼接连续合约会产生跳空缺口。正确的做法是使用复权价格或价差调整方法,确保价格序列连续。股票数据要处理分红送股,前复权与后复权在回测中效果不同。前复权适合观察历史价格形态,后复权适合计算真实收益率。

数据清洗步骤不可缺少。剔除涨跌停无法成交的样本,标记停牌期间,修正异常跳动点。成交量与持仓量数据用于过滤流动性不足的品种。股票日线数据要检查是否包含ST股票,期货要确认夜盘交易时段是否纳入。
回测框架的常见陷阱
回测引擎的速度和精度需要平衡。向量化回测速度快,适合初步筛选因子,但难以模拟逐笔成交和订单排队。事件驱动回测更接近实盘,可以处理部分成交、撤单、滑点,但开发复杂度高。股票T+1制度下,当日买入无法卖出,回测必须遵守该规则。期货T+0且带杠杆,保证金计算和强平逻辑要精确模拟。
滑点假设不能忽略。流动性好的股票滑点小,小盘股或期货远月合约滑点大。固定滑点模型简单但失真,百分比滑点更合理。冲击成本模型考虑订单大小与市场深度的关系,适合大资金回测。手续费包含佣金、印花税、过户费,期货还有交易所手续费和投资者保障基金。
幸存者偏差常见于股票回测。只使用当前上市的股票池,会剔除退市股票,导致收益虚高。解决方法是用历史成分股数据,或者包含退市股票的完整数据库。期货主力合约切换规则要固定,避免未来函数。
未来函数是回测中最隐蔽的错误。使用当日收盘价计算信号,却用当日开盘价成交,这是典型未来函数。正确做法是信号在收盘后生成,下一交易日开盘或收盘成交。财务数据要按公告日期对齐,不能用报告期数据提前交易。
股票回测的数据验证要点
股票数据验证从基础行情开始。检查每日开盘价、最高价、最低价、收盘价的关系,最高价不低于最低价,开盘价在最高最低之间。成交量不为负,成交额与成交量、均价匹配。除权除息日的前后价格跳空要合理。
因子数据验证需要多维度。市盈率、市净率、净资产收益率等基本面因子,要确认计算口径一致。技术因子如移动平均线、相对强弱指标,要验证边界条件。动量因子在股票除权日附近容易失真。
回测股票策略时,交易成本要分市场。上海和深圳市场佣金不同,印花税单边收取。最小交易单位100股,回测中要取整。涨跌停板限制股票成交,回测中涨停无法买入,跌停无法卖出,需要特殊处理。
期货回测的特殊处理
期货数据验证从合约代码开始。每个合约有独立代码,主力合约切换规则要明确。使用持仓量最大或成交量最大的合约作为主力,切换频率可以是每日或每周。价格调整使用价差法或比例法,保持价格连续性。
保证金计算动态变化。交易所会根据市场波动调整保证金比例,回测中要使用历史保证金数据。逐日盯市制度下,每日结算盈亏,保证金不足会触发强平。回测引擎要模拟强平逻辑,否则可能产生无限亏损的假象。
期货回测要处理涨跌停板和扩板。不同品种涨跌停幅度不同,连续涨跌停后可能扩板。回测中要记录无法成交的情况,避免虚假成交。夜盘交易时段的数据要合并到下一交易日,或者单独处理。
交割月规则影响策略。个人客户不能进入交割月,回测中要提前平仓。法人客户可以交割,但需要处理仓单和增值税。期货回测的滑点通常比股票大,因为杠杆放大资金波动。
回测评估与实盘衔接
回测结果不能只看收益率。夏普比率、最大回撤、胜率、盈亏比、交易频率都要评估。股票策略关注年化收益和换手率,期货策略关注杠杆倍数和保证金占用。
样本内与样本外测试要分开。用一部分数据优化参数,另一部分数据验证。滚动窗口回测更接近实盘,每次用过去N天数据训练,预测未来M天。股票市场风格切换快,期货市场波动率高,滚动窗口能捕捉变化。
回测与实盘的差异来源包括滑点、延迟、流动性、心理因素。实盘交易中,信号生成到订单执行有时间差。股票集合竞价和连续竞价阶段成交概率不同,期货夜盘流动性差。回测中应该加入随机延迟模拟。
量化数据验证与回测不是一次性工作。市场结构变化、交易规则调整、数据源更新,都需要重新验证。股票注册制改革、期货新品种上市,都会影响策略表现。持续监控回测与实盘的偏差,及时修正模型,才能保持策略可靠性。
代码示例 期货回测中的保证金与强平模拟
import numpy as np
import pandas as pd
class FuturesBacktest:
def __init__(self, initial_capital, margin_ratio, fee_rate, slippage):
self.capital = initial_capital
self.margin_ratio = margin_ratio
self.fee_rate = fee_rate
self.slippage = slippage
self.position = 0
self.entry_price = 0
def update(self, price, signal):
# signal: 1 开多, -1 开空, 0 平仓
if self.position == 0 and signal != 0:
self.position = signal
self.entry_price = price * (1 + self.slippage * signal)
margin = self.entry_price * self.margin_ratio
if margin > self.capital:
self.position = 0
return
self.capital -= margin
self.capital -= self.entry_price * self.fee_rate
elif self.position != 0 and signal == 0:
pnl = (price - self.entry_price) * self.position
self.capital += pnl
self.capital -= price * self.fee_rate
self.position = 0
# 每日盯市与强平检查
if self.position != 0:
unrealized = (price - self.entry_price) * self.position
margin_required = price * self.margin_ratio
if self.capital + unrealized < margin_required:
# 强制平仓
self.capital += unrealized
self.capital -= price * self.fee_rate * 2
self.position = 0
该代码演示期货回测中保证金占用、手续费扣除和强平逻辑。股票回测需要调整T+1规则和印花税。实际回测中还要处理涨跌停无法成交、合约换月、夜盘数据合并等细节。