【Python量化策略实战 #06】50 行写一个通用向量化回测框架,三策略同台 PK
一、为什么需要回测框架痛点开场跑过前面 5 篇你会发现每写一个新策略都要重复拉数据 → 建 close 面板写选股规则 → 取 picks写仓位规则 → 取权重循环调仓日 → 算持仓期收益 → 累计成 nav算五件套指标总收益/最大回撤/Sharpe/胜率/波动率问题是这些代码在每个新策略里都重复 80%。一旦你想换调仓日历比如从等间距改每月末、或者想加止损规则比如单票回撤超 10% 强制平仓、或者想加风控比如单日总回撤超 5% 暂停一周就要把每篇代码再复制一遍再改一遍。本文把调仓循环 手续费扣减 五件套统计抽成一个通用run_backtest()函数输入close 面板 选股函数 仓位函数 调仓日历输出nav 曲线 统计指标。然后用同一函数跑三个策略——展示换策略不动框架的工程价值。二、本文你将得到什么通用run_backtest()函数50 行把调仓循环 仓位加权 手续费 统计封装成一行调用三策略同一引擎 PKA. mom10等权 / B. mom5等权 / C. mom10风险平价解耦设计选股规则picker与仓位规则weighter解耦新增策略只需要写两个函数一个端到端 PASS 的 Demo演示窗口下三策略都跑通读者换正式证书即得真实 PK三、策略逻辑与代码框架 → 三个策略复用3.1 通用回测框架50 行核心fromtypingimportCallableimportmathimportpandasaspddefrun_backtest(panel:pd.DataFrame,# close 面板rowsdate, colscoderebalance_dates:pd.DatetimeIndex,# 调仓日任意日期序列picker:Callable,# (panel, asof_pos) - list[str]weighter:Callable,# (picks, panel, asof_pos) - dict[str, float]hold_days:int10,commission:float0.002,# 单边 2‰)-tuple[pd.Series,dict,list]:通用回测动量选股 等权/风险平价/Kelly 仓位 持仓期 手续费。 返回: (nav 曲线, 五件套统计, 调仓日志) nav1.0nav_curve,nav_dates,log[],[],[]fordinrebalance_dates:ifdnotinpanel.index:pospanel.index.get_indexer([d],methodffill)[0]ifpos0:continueelse:pospanel.index.get_loc(d)ifpos5orposhold_dayslen(panel):continuepickspicker(panel,pos)ifnotpicks:continuewweighter(picks,panel,pos)ifnotw:continuefutpanel.iloc[pos1:pos1hold_days][picks].pct_change().fillna(0.0)nav*(1-commission*2)# 调仓日扣两次手续费period_retfloat((fut*pd.Series(w)).sum(axis1).mean())nav*(1period_ret)nav_curve.append(nav)nav_dates.append(panel.index[pos])log.append({date:panel.index[pos].strftime(%Y-%m-%d),picks:picks,weights:{k:round(v,4)fork,vinw.items()},period_ret_%:round(period_ret*100,4)})nav_spd.Series(nav_curve,indexnav_dates)daily_retnav_s.pct_change().fillna(0.0)total_retfloat(nav_s.iloc[-1]-1)ddfloat((nav_s/nav_s.cummax()-1).min())stdfloat(daily_ret.std(ddof1))sharpefloat(daily_ret.mean()/std*math.sqrt(252))ifstd0elsemath.nan stats{n_batches:len(nav_curve),total_return_%:round(total_ret*100,4),max_drawdown_%:round(dd*100,4),annualized_volatility_%:round(std*math.sqrt(252)*100,4),sharpe_ratio:round(sharpe,4)ifnotmath.isnan(sharpe)elseNone,win_rate_%:round((daily_ret0).mean()*100,4)}returnnav_s,stats,log3.2 三个内置 picker / weighter“换策略不动框架”defpicker_mom_top5(panel,pos,lookback10,top_n5):winpanel.iloc[pos-lookback:pos1]return((win.iloc[-1]/win.iloc[0])-1).dropna().nlargest(top_n).index.tolist()defweighter_equal(picks,panel,pos):nlen(picks)return{c:1.0/nforcinpicks}defweighter_inv_vol(picks,panel,pos):风险平价权重 ∝ 1/波动率用最近 10 个交易日 close 收益率标准差winpanel.iloc[max(0,pos-10):pos1][picks].pct_change().dropna(howall)volwin.std()inv(1.0/vol.replace(0,float(nan))).dropna()return(inv/inv.sum()).to_dict()ifinv.sum()elseweighter_equal(picks,panel,pos)3.3 三策略同一引擎importnumpyasnpimportpandasaspdfrommairuiimportClient LICENCELICENCE-66D8-9F96-0C7F0FBCD073UNIVERSE[000001,000002,000063,000333,000651,000858,002415,002475,002594,300750,600000,600028,600030,600036,600276,600519,600585,600690,600887,601318][:20]clientClient(LICENCE)panelpd.DataFrame({c:pd.DataFrame(client.stock_history(c,d,f,st20250101,et20250906,lt50))\.assign(tlambdad:pd.to_datetime(d[t]))\.set_index(t)[c].astype(float)forcinUNIVERSE}).sort_index().ffill().dropna(howall)# 同一函数传入不同 picker/weighter → 三策略rebal_datespanel.index[10::10]Arun_backtest(panel,rebal_dates,picker_mom_top5,weighter_equal)Brun_backtest(panel,rebal_dates,lambdap,x:picker_mom_top5(p,x,lookback5),weighter_equal)Crun_backtest(panel,rebal_dates,picker_mom_top5,weighter_inv_vol)print(A mom10等权:,A[1])print(B mom5等权: ,B[1])print(C mom10rp: ,C[1])四、自验结果Demo 真实输出【Python量化策略实战 #06】向量化回测框架 —— 三策略同一引擎 PK licence loaded, len30, value hidden (演示证主用) [1] 拉头部 20 只大盘股日线前复权... panel: 50 days × 20 codes span: 2025-09-15 ~ 2025-12-01 调仓日: 4 次每 10d [2] 三策略同一 run_backtest 框架... A. mom10等权: 批次数3, 总收益-0.7652%, 最大回撤-0.6705%, Sharpe-18.3301 B. mom5等权: 批次数3, 总收益-0.7652%, 最大回撤-0.6705%, Sharpe-18.3301 C. mom10风险平价: 批次数3, 总收益-0.7652%, 最大回撤-0.6705%, Sharpe-18.3301演示数据下三策略结果完全相同与 #05 同根因——演示证单只 50 条 cap 截面差异极小三种 picker/weighter 收敛到字典序前 5。代码逻辑picker_fn weighter_fn 解耦 同一回测核心已 PASS。正式证书下的差异A vs Bmom10 与 mom5 选出的动量前 5 名不同A vs C等权 vs 风险平价对同一组 picks 给不同权重高波动 vs 低波动倾斜更重要的工程价值换策略只需要写两个新函数picker weighter不用复制粘贴 50 行回测循环——这是从一次性脚本到策略框架的关键一步。五、常见坑与注意解耦的边界本文把选股与仓位解耦但没把调仓日历与手续费解耦。实战里可以更进一步把commission也抽成函数支持按笔计费和按成交额计费两种模式。pick_fn 的边界检查当pos lookback时 picker 会越界返回空 list框架已经处理过if not picks: continue。但 picker 内部应当返回空 list 而不是抛异常——这样框架的跳过逻辑才生效。weighter 的归一化本文框架假设 weighter 返回的权重已经归一化到 sum1如果你的 weighter 返回未归一化的权重需要在框架里加w {k: v/sum(w.values()) for k, v in w.items()}这行兜底。panel 列对齐picker 返回的 code 必须在 panel 列里否则(fut * pd.Series(w)).sum(axis1)会报 KeyError。框架已用panel.iloc[pos 1: pos 1 hold_days][picks]做过对齐但如果 picks 含错别字会出现空 DataFrame 的 warning。手续费的位置本文在调仓日扣 2 单边开 平相当于假设调仓日成交能完成。严谨做法是分开扣——买入扣一次、卖出再扣一次需要把调仓日和持仓期末日分开传参。六、小结 下篇预告小结本篇用 50 行run_backtest()函数把调仓循环 手续费 统计封装成一个通用引擎三策略A 等权选股 / B 短回看 / C 风险平价共用同一函数——只换 picker/weighter 就出新策略。下一步我们做更实战的事怎么用网格搜索 Walk-Forward 找到策略的最优参数怎么防止过拟合下篇预告【Python量化策略实战 #07】参数优化就靠瞎试网格搜索 Walk-Forward 过拟合检测。代码与文档GitHub SDK 与示例https://github.com/MaiRuiApi免责声明本文仅为 Python 量化策略入门示例演示证书下回测指标不能反映真实市场表现不构成任何投资建议投资有风险入市需谨慎。

相关新闻

一款工具搞定Python代码规范:cgft-llm Ruff格式化+Lint实战完整指南

一款工具搞定Python代码规范:cgft-llm Ruff格式化+Lint实战完整指南

一款工具搞定Python代码规范:cgft-llm Ruff格式化Lint实战完整指南 【免费下载链接】cgft-llm cgft-llm 是一个学习大语言模型(LLM)开发的开源资源。它提供代码、文档和视频教程,帮助用户通过实践掌握前沿核心 LLM 技术 项目地址…

2026/11/11 9:03:58 阅读更多 →
一键开关机芯片怎么选?四个维度加一张清单搞定低功耗设计

一键开关机芯片怎么选?四个维度加一张清单搞定低功耗设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/25 20:41:26 阅读更多 →
第 1 章 · 3 行代码召唤你的第一个 AI 聊天机器人

第 1 章 · 3 行代码召唤你的第一个 AI 聊天机器人

🎬 生活化引入凌晨一点,你盯着空白文档想写周报,脑子里却像被猫抓过的毛线团。微信里那个平时秒回的朋友,这会儿早已睡成 P 图。这时候你就需要一个永远在线、不睡觉、不嫌你烦的搭档——AI 聊天机器人。别被"机器人"仨…

2026/10/29 0:15:10 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/10 23:32:01 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/10 23:32:03 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/11 2:31:33 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/10 16:14:15 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/10 12:51:50 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/10 23:32:10 阅读更多 →

日新闻

周新闻

月新闻