回测前须确保数据质量与时间对齐,优先用adj_close、清洗缺失值、校准时区、验证索引单调性;用vectorbt向量化实现双均线策略,严格禁用未来信息、加入交易成本、多维过滤趋势信号,并规范保存参数与结果。

回测前必须确认数据质量与时间对齐
直接用 yfinance 或 akshare 拉下来的日线数据,常存在停牌、复权不一致、时区错位等问题,导致回测信号在错误时间点触发。比如用 yf.download('AAPL', '2020-01-01', '2023-01-01') 默认返回 UTC 时间戳,但国内策略通常按北京时间交易日判断,若不做 .tz_localize(None) 或 .tz_convert('Asia/Shanghai') 处理,涨跌停判定和买卖信号会偏移一天。
- 优先用
adj_close(前复权收盘价)而非close,避免分红送股造成价格跳变 - 检查缺失值:
df['close'].isna().sum(),尤其注意节假日前后是否出现连续 NaN;用df = df.dropna(subset=['open','high','low','close','volume'])清洗 - 确保索引为
DatetimeIndex且单调递增:df.index.is_monotonic_increasing,否则backtrader或vectorbt会静默出错
用 vectorbt 快速实现双均线策略回测
vectorbt 比 backtrader 更适合快速验证逻辑,它把整个回测向量化,不用写 next() 方法,但代价是内存占用高、不支持盘中模拟。关键不是“能不能跑”,而是“信号生成是否符合真实交易约束”——比如禁止未来函数、考虑滑点和手续费。
- 计算信号:用
vbt.MA.run(df['close'], window=5).ma_cross(vbt.MA.run(df['close'], window=20))得到布尔型 entry/exit 信号 - 加交易成本:在
vbt.Portfolio.from_signals()中传入fees=0.001(千一印花税+佣金),否则盈利虚高 - 禁用未来信息:确保所有指标用
minp参数(如vbt.MA.run(..., minp=5)),避免前几行因窗口不足返回 NaN 后被填充导致信号泄露
趋势强度不能只看 MACD 或均线斜率
单纯用 ta.macd() 的柱状图正负判断多空,容易在震荡市频繁失效。真正有用的趋势特征是“价格在关键周期内持续位于某条均线上方 + 波动率收缩”,需组合多个维度。
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
- 用
ta.adx()输出的+DI/-DI差值 > 10 且ADX> 20,才认为趋势成立 - 配合波动率过滤:计算
df['close'].rolling(20).std() / df['close'].rolling(20).mean(),值低于 0.015 时说明趋势启动初期波动小,适合建仓 - 避免用
np.gradient()直接算斜率——它对噪声敏感;改用线性回归拟合最近 10 日收盘价,取斜率符号和 R² > 0.6 作为辅助条件
本地保存回测结果时别忽略策略参数快照
回测跑完导出 portfolio.stats() 到 CSV,如果没同时存下当时用的参数(比如 MA 周期、手续费率、起止日期),过两周再看结果根本没法复现或对比。这不是格式问题,是归因失效。
- 用
pickle存整个Portfolio对象太重,推荐用json.dump({'params': {'fast_ma': 5, 'slow_ma': 20, 'fee': 0.001}, 'stats': stats_dict}, f) - 文件名带哈希:
f"backtest_{hashlib.md5(str(params).encode()).hexdigest()[:8]}.json",避免手动重命名冲突 - 别把
df.to_csv('raw_data.csv')和回测结果混在一个目录——加子文件夹data/和results/,不然下次清理时误删原始行情
回测最耗时间的环节从来不是代码运行,而是验证信号是否真能落地执行。比如双均线金叉后,实际成交价大概率在次日跳空缺口里,这个 gap 不建模,再漂亮的夏普比率也没意义。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










