首页 / 资讯中心 / 文章详情

量化策略源码骨架解析:从回测到实盘的完整链路

量化策略源码骨架解析:从回测到实盘的完整链路 ★ FEATURED ARTICLE
简介这份量化策略源码资源面向具备一定Python与机器学习基础的量化研究者、金融数据爱好者及策略开发初学者围绕个股日线数据的采集、预处理、建模、评估与仓位管理提供一套可本地复现的完整流程帮助读者理解从原始行情到组合配置的落地路径。资源包共18个文件以10个Python脚本和7个SQL建表/结果存储脚本为主另附1份README说明文档压缩包约56KB脚本与数据库脚本分工明确便于按模块拆解学习。目前已有3570人学习下载具备一定的参考热度。内容涵盖tushare日线行情入库、训练集整合、SVM个股建模与预测、基于回测和推进式建模的查准率、查全率与F1评估以及马科维茨投资组合的仓位管理读者可据此搭建数据管道、复现建模评估链路并借鉴结果表与股票池的存储设计形成可扩展的策略研究框架。1. 量化策略源码从一份能跑起来的策略骨架说起很多人第一次接触量化卡住的地方不是不会写 Python而是拿到一份策略源码后不知道从哪下手——回测跑出来的曲线漂亮得不像话一上模拟盘就原形毕露。这份量化策略源码要解决的正是这个断层它给出一套完整的策略骨架包含数据读取、指标计算、信号生成、仓位管理和回测统计五个环节每个环节都能单独替换。适合两类人一类是刚学完 pandas 想找个真实项目练手的新手照着改参数就能跑通全流程另一类是手里有想法但懒得从零搭框架的熟手直接把自己的信号逻辑塞进预留的接口里。下面按「这项目长什么样 → 怎么跑起来 → 核心代码怎么读 → 怎么改成自己的策略」的顺序拆。2. 项目结构与技术选型五个模块如何串成一条回测链路2.1 目录结构与各文件职责这份源码的组织方式很朴素没有引入复杂的框架核心就是一个主入口加若干功能模块。常见做法是下面这种扁平结构每个文件职责单一改哪块一目了然quant_strategy/ ├── main.py # 主入口串起数据、信号、回测 ├── config.py # 全局参数标的、时间区间、手续费、滑点 ├── data_loader.py # 数据读取与清洗 ├── indicators.py # 技术指标计算均线、ATR、RSI 等 ├── signals.py # 信号生成逻辑 ├── portfolio.py # 仓位管理与资金曲线 ├── backtest.py # 回测引擎与绩效统计 └── requirements.txt # 依赖清单config.py是整个项目的参数中枢标的代码、起止日期、初始资金、手续费率、滑点全部集中在这里改策略之前先改配置避免参数散落在各个文件里。data_loader.py负责把原始行情整理成统一格式的 DataFrame列名固定为date / open / high / low / close / volume后面所有模块都按这个约定读取。indicators.py和signals.py分开是有意为之——指标是纯数学计算信号是业务判断混在一起后期改逻辑会非常痛苦。portfolio.py管钱backtest.py管账两者分离让「策略亏在哪」和「成本吃掉多少」能分开看。2.2 技术栈与依赖选择技术栈就是 Python 数据分析那一套没有花哨的东西依赖用途说明pandas数据处理行情对齐、滚动计算、绩效统计全靠它numpy数值计算向量化运算避免逐行循环matplotlib结果可视化资金曲线、回撤图tushare / akshare行情数据源二选一接口封装在 data_loader 里选 pandas 而不是自己写循环是因为回测里大量操作是「按时间对齐 滚动窗口」用rolling()和shift()一行顶几十行 for 循环速度差一个数量级。数据源没有写死data_loader.py里留了一个fetch_data()函数换数据源只改这一个函数上层完全无感。这种「数据源可插拔」的设计在实盘对接时特别省事从本地 CSV 切到接口拉取不用动策略代码。2.3 一条完整的回测链路整个流程是单向的main.py先读config.py拿到参数调data_loader拉数据并清洗把干净数据喂给indicators算出指标列signals根据指标列生成 0/1 持仓信号portfolio按信号和手续费模拟逐日持仓与资金变化最后backtest统计年化收益、最大回撤、夏普比率并画图。这条链路的关键约束是信号必须用当日收盘数据生成、次日开盘执行否则就会出现「用未来数据交易」的经典错误回测曲线会好看到失真。源码里用signal.shift(1)做了这一天的延迟这个细节后面会专门讲。3. 跑起来从环境准备到第一张资金曲线3.1 环境准备与依赖安装先确认 Python 版本建议 3.8 以上pandas 新版本对类型推断更严格老代码容易在这里翻车。建虚拟环境是血泪经验量化项目依赖版本冲突很常见别装到全局环境里python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install -r requirements.txtrequirements.txt里通常锁了 pandas、numpy、matplotlib 和行情接口库。如果安装行情库时报编译错误多半是缺少系统级的编译工具按报错提示补装即可。装完后跑一句python -c import pandas; print(pandas.__version__)确认环境干净。3.2 配置参数与数据准备打开config.py先把最关键的几个参数改成自己的# config.py 核心参数 SYMBOL 000001 # 标的代码 START_DATE 2020-01-01 # 回测起始 END_DATE 2023-12-31 # 回测结束 INIT_CAPITAL 100000 # 初始资金 COMMISSION 0.0003 # 单边手续费率 SLIPPAGE 0.001 # 滑点比例 FAST_MA 5 # 短均线周期 SLOW_MA 20 # 长均线周期参数含义很直白但有两个容易忽略的点COMMISSION是单边费率买卖各收一次回测里要乘 2SLIPPAGE是成交价相对信号价的偏移设成 0 会让回测虚高一般小盘股设 0.001 到 0.002 比较接近真实。数据方面如果本地有 CSV把路径填进data_loader的DATA_PATH没有的话走接口拉取首次拉取会慢一些建议拉完存一份本地缓存反复回测时不用重复请求。3.3 执行回测与结果解读一切就绪后直接跑主入口python main.py终端会打印绩效摘要同时弹出一张图上半部分是资金曲线对比基准下半部分是回撤。重点看三个数年化收益率、最大回撤、夏普比率。年化高但回撤也大的策略实盘很难拿住夏普低于 1 的策略收益基本是靠承担波动换来的。源码里backtest.py的统计函数是这样算的def performance(equity: pd.Series, freq: int 252) - dict: ret equity.pct_change().dropna() annual (1 ret).prod() ** (freq / len(ret)) - 1 # 年化收益 drawdown (equity / equity.cummax() - 1).min() # 最大回撤 sharpe ret.mean() / ret.std() * (freq ** 0.5) # 夏普比率 return {annual: annual, mdd: drawdown, sharpe: sharpe}freq252是 A 股一年的交易日数换成其他市场要相应调整。pct_change()算日收益cummax()求历史最高净值用来算回撤ret.std()是日收益标准差乘sqrt(252)年化。这三个指标缺一不可只看年化收益是新手最容易踩的坑。4. 核心模块拆解信号、仓位与回测引擎怎么配合4.1 指标计算与信号生成indicators.py里的均线计算是整份源码最基础也最值得读的部分它决定了信号的质量def add_ma(df: pd.DataFrame, fast: int, slow: int) - pd.DataFrame: df df.copy() df[ma_fast] df[close].rolling(fast).mean() df[ma_slow] df[close].rolling(slow).mean() return dfrolling(fast).mean()取最近 fast 天的收盘均价前 fast-1 行是 NaN这是正常的信号生成时要跳过。signals.py根据两条均线的相对位置出信号def ma_cross(df: pd.DataFrame) - pd.Series: signal (df[ma_fast] df[ma_slow]).astype(int) return signal.shift(1).fillna(0) # 关键延迟一天避免未来函数(ma_fast ma_slow)得到布尔序列astype(int)转成 0/11 表示持有。shift(1)是整段代码的灵魂——今天收盘才知道均线位置最早只能明天开盘成交不延迟就是用未来数据回测会骗人。fillna(0)处理开头 NaN 区间默认空仓。改策略时把这段换成自己的判断逻辑即可只要输出仍是 0/1 序列后面所有模块都不用动。4.2 仓位管理与成本模拟portfolio.py负责把信号翻译成持仓和资金变化核心是逐日推进def run_portfolio(df: pd.DataFrame, signal: pd.Series, capital: float, commission: float, slippage: float): position, cash, equity 0, capital, [] for i in range(len(df)): price df[close].iloc[i] target signal.iloc[i] if target 1 and position 0: # 开仓 buy_price price * (1 slippage) position cash / (buy_price * (1 commission)) cash 0 elif target 0 and position 0: # 平仓 sell_price price * (1 - slippage) cash position * sell_price * (1 - commission) position 0 equity.append(cash position * price) return pd.Series(equity, indexdf.index)buy_price在收盘价上加了滑点sell_price减了滑点一买一卖各扣一次手续费这样算出来的资金曲线才接近实盘。position用股数表示cash在持仓时为 0全仓进出。这段代码是简化版真实场景还要处理最小交易单位、涨跌停无法成交等情况但作为骨架已经够用。想改成半仓把cash / (...)改成cash * 0.5 / (...)即可。4.3 回测引擎的组装与绩效输出backtest.py把前面几块拼起来并负责画图def backtest(cfg): df load_data(cfg.SYMBOL, cfg.START_DATE, cfg.END_DATE) df add_ma(df, cfg.FAST_MA, cfg.SLOW_MA) sig ma_cross(df) equity run_portfolio(df, sig, cfg.INIT_CAPITAL, cfg.COMMISSION, cfg.SLIPPAGE) stats performance(equity) plot_equity(equity, df[close]) return statsload_data内部做了列名统一和缺失值处理add_ma加指标ma_cross出信号run_portfolio算资金performance出统计plot_equity画图。整个函数不到十行但把五个模块串成了闭环。这种「主流程短、细节下沉」的写法值得学改策略时只需要替换中间某一环其余不动。5. 避坑与排查回测跑通之后最容易翻车的几处5.1 回测收益高得离谱现象年化收益 80% 以上资金曲线几乎不回撤。原因九成是未来函数信号没有shift(1)或者用了当日收盘价成交。解决检查signals.py里信号是否延迟一天检查portfolio.py成交价是否用了信号当天的价格。把shift(1)去掉再跑一次对比如果收益骤降说明之前确实偷看了未来。5.2 手续费吃掉大半利润现象策略逻辑没问题但扣完成本后收益转负。原因COMMISSION只算了一边或者高频调仓没考虑滑点。解决确认买卖各扣一次手续费滑点按标的流动性设小盘股别低于 0.001。把COMMISSION临时设成 0 跑一遍两次结果差值就是成本影响心里有数再决定要不要降低调仓频率。5.3 数据对齐错位导致信号乱跳现象信号频繁翻转持仓天数极短。原因行情数据有停牌日或缺失日rolling窗口跨了缺口均线计算失真。解决在data_loader里先做交易日对齐缺失日用前值填充或直接剔除确保rolling窗口内都是有效交易日。跑之前打印一下df.isna().sum()有缺失先处理再回测。5.4 换标的之后结果完全不可用现象同一套参数在 A 标的上有效换 B 标的就亏。原因参数是过拟合出来的均线周期只适合特定波动特征。解决别拿单一标的调参至少在三五个不同标的上跑一遍参数在多数标的上都站得住才算数。这也是为什么config.py把参数集中管理——换标的只改配置方便批量验证。6. 二次开发把骨架换成自己的策略并验证有效性拿到这份源码最有价值的用法不是直接跑它的均线策略而是把它当成一个「策略容器」。改造路径很清晰先在indicators.py里加自己的指标函数比如布林带、动量因子保持输入输出都是 DataFrame再在signals.py里写判断逻辑输出 0/1 序列记得shift(1)最后在config.py里加对应参数backtest.py里把新函数接进主流程。整个过程不动portfolio.py和performance()资金管理和绩效统计是通用的没必要重写。验证改造是否有效我一般会做三组对照。第一组是参数敏感性把核心参数上下浮动 20% 各跑一遍如果收益曲线剧烈波动说明策略靠的是运气不是逻辑。第二组是样本外测试用前两年数据调参后一年数据验证样本外收益不低于样本内的一半才算过关。第三组是成本压力测试把手续费和滑点翻倍再跑如果策略直接由盈转亏说明它对交易成本太敏感实盘很难存活。这三组跑下来还站得住的策略才值得往实盘方向推进。还有一个容易被忽略的改造点data_loader.py的数据源接口。回测用日线实盘往往要接分钟线或实时推送把fetch_data()抽象成统一返回格式上层策略代码一行不用改就能切换频率。我见过太多项目在回测转实盘时推倒重来就是因为数据层和策略层耦合太紧。这份源码把数据读取单独抽出来正是为了留这个后路。从那以后我每次改完策略都强制先跑一遍样本外和成本压力测试再决定要不要继续投入时间优化——回测好看不等于能赚钱这个习惯帮我省下了大量白折腾的功夫。希望这份拆解能帮你把源码跑起来也帮你少走几步弯路。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站