
简介这是一份面向个人投资者与量化初学者的完整策略源码包围绕数据采集、数据预处理、支持向量机建模、模型评估与仓位管理五个环节构建了一条可运行的量化研究流水线。压缩包内共有十八个文件包括十个功能脚本、七个数据库脚本和一个说明文档整体体积约五十六千字节目录按模块划分清晰打开即可对照阅读。已有三千五百六十六人学习下载适合希望通过实际代码理解机器学习选股、组合优化与回测评估的读者。实现层面数据采集脚本调用行情接口将日线数据写入本地数据库预处理脚本把基础行情整理成标准训练集建模脚本对每只股票训练支持向量机并输出预测评估脚本使用回测与推进式建模计算查准率、查全率和F1分值并将结果汇总到数据表仓位管理脚本基于马科维茨投资组合理论在给定股票池上完成权重配置。除了核心流程资源还包含每日资金更新、股票池过滤、任务调度等辅助脚本文档中说明了各模块的调用顺序SQL脚本预建的数据表让中间结果一目了然方便定位问题。读者可以从数据落库、特征生成、模型训练到最终组合决策逐段查看每一行代码通过修改参数、替换标的或调整模型快速验证新思路也可将其作为个人量化策略框架的起点继续扩展或集成其他数据源从零搭建并复现自己的研究环境。1. 量化策略源码到底解决什么问题先看懂手里拿的是什么量化策略源码这个词在从业者手里往往指向两种东西一种是别人封装好的完整策略包拿来改改参数就能跑另一种是自己反复迭代形成的策略骨架包含了信号计算、仓位管理、回测引擎、绩效归因这一整套链路。很多人拿到一套源码的第一反应是跑起来看收益曲线但真正决定这套源码值不值得投入的是它的架构能不能承载你的交易思想以及它在风险端做了多少防护。本文围绕「量化策略源码」的完整落地路径展开从源码的构成拆解、最小回测环境的搭建到四类常用策略的源码实现、参数寻优的统计陷阱再到回测与实盘的偏差修正。适合已经有基本 Python 基础、正在从单一策略脚本走向系统性开发的从业者也适合拿了一套别人给的源码却不知道怎么验证和改造的新手。量化策略源码不是“看一眼收益曲线就能判断好坏”的黑匣子。它是一套有结构、有状态、有边界的工程系统任何一个环节出了问题后面的结果都不可信。我见过太多人拿着年化 80% 的回测曲线兴奋地准备上实盘结果发现源码里写死了未来数据或者滑点设成了零。所以这篇文章会先帮你建立对源码结构的正确认知再逐步告诉你每一行代码背后的逻辑和坑。2. 量化策略源码的核心构成信号、仓位、执行、风控四个模块缺一不可2.1 不要把策略源码当成一个文件它是四个模块的协作一份合格的量化策略源码通常不是单个 Python 文件而是一组按职责拆分的模块。最常见的分层是数据层负责取数和预处理信号层负责产生买卖信号仓位层负责确定每次下单的规模执行层负责模拟成交和记录订单风控层负责拦截异常信号和极端仓位。模块典型职责对应 Python 文件数据层K线获取、复权处理、缺失值填充data_loader.py信号层指标计算、多空信号生成signal.py仓位层固定权重、波动率目标、凯利公式position.py执行层回测撮合、滑点模拟、手续费记录broker.py风控层最大回撤限制、单票持仓上限、黑名单risk.py拿到一套源码时先看这几个模块是否独立。如果所有逻辑都堆在一个文件里说明这套源码的演进性很差后续你加一个过滤条件、换一组参数都可能在某个隐蔽的函数里引发副作用。我一般会先花十分钟画出它的模块调用关系——用pydeps或直接看 import 结构——来判断这套源码是值得深耕还是需要重写。2.2 信号层是策略的灵魂锚定指标和过滤条件怎么选信号层决定了策略赚的是什么钱。常见做法是先用一个核心锚定指标产生基础信号再叠加过滤条件降低噪声。比如双均线策略核心信号是短期均线上穿长期均线过滤条件可以是成交量放大、价格站上布林带中轨等。下面是一段典型的多头信号生成逻辑import pandas as pd def generate_signal(df: pd.DataFrame, fast: int 5, slow: int 20) - pd.DataFrame: # 计算均线序列 df[fast_ma] df[close].rolling(fast).mean() df[slow_ma] df[close].rolling(slow).mean() # 上穿信号fast 从下方穿过 slow记为 1 df[ma_cross] (df[fast_ma] df[slow_ma]).astype(int) df[signal_raw] df[ma_cross].diff() # 使用成交量过滤只在成交量高于过去 20 日均量 1.2 倍时保留信号 df[volume_ma] df[volume].rolling(20).mean() df[volume_filter] df[volume] df[volume_ma] * 1.2 # 合并信号只有同时满足均线交叉和量能过滤才触发 df[signal] 0 df.loc[(df[signal_raw] 1) df[volume_filter], signal] 1 df.loc[(df[signal_raw] -1), signal] -1 return df这段代码里有两个参数是必须亲手调过的fast和slow的窗口长度以及volume_ma的倍数。前者决定了信号的敏感度后者决定了信号的确认成本。如果你拿到一套源码发现这些参数被硬编码在函数内部建议立刻改造成**kwargs或配置字典否则后面的参数寻优根本没法做。2.3 仓位层被忽略的次数最多但它是收益曲线的放大器很多新手拿到源码后只盯着买卖信号把仓位直接设为全进全出这是最容易踩的坑。真实的量化策略源码里仓位模块往往比信号模块更复杂因为它必须在盈利扩张和风险控制之间做权衡。import numpy as np def fixed_fraction_position(equity: float, signal: int, risk_per_trade: float 0.02, atr: float, entry_price: float) - float: # 固定分数仓位单笔亏损不超过总资金 risk_per_trade 的比例 if signal 0: return 0.0 risk_amount equity * risk_per_trade stop_loss_distance atr * 1.5 # 用 ATR 确定止损距离 position_size risk_amount / stop_loss_distance # 防止仓位过大限制单票市值占总权益的比例 max_position_value equity * 0.25 position_value position_size * entry_price if position_value max_position_value: position_size max_position_value / entry_price return round(position_size, 2)这段代码的精髓在于它把“亏多少”而不是“赚多少”作为仓位计算的起点。risk_per_trade和atr倍数是我最常调的两个参数。如果你拿到的源码里没有类似的风险预算控制那么它本质上只是一套信号展示脚本离可实盘的量化策略源码还差一个身位。2.4 执行层决定回测是否可信滑点、手续费、成交逻辑执行层是最容易被源码作者偷懒的地方也是导致实盘和回测偏差最大的地方。一个最小可用的执行层至少包含三件事手续费率、滑点模型、成交条件。下面是最简版本的实现框架COSTS { commission_rate: 0.0003, # 万三不含最低五元限制 slippage: 0.001, # 单边千一滑点 } def simulate_fill(price: float, direction: int, volume: float) - float: # direction: 1 买入-1 卖出 fill_price price * (1 direction * COSTS[slippage]) commission fill_price * volume * COSTS[commission_rate] # 实际成交成本 total_cost fill_price * volume commission return total_cost注意这里的slippage是单边千一很多源码为了曲线好看会把滑点设成零或者万零点五这在流动性好的品种上还能接受但在小市值股票或合约上就是灾难。我一般会在回测完成后用两倍滑点做一次压力测试如果收益曲线从陡峭变成震荡说明策略的真实竞争力存疑。提示拿到任何一套量化策略源码先检查执行层是否有滑点参数。如果完全没有建议直接放弃——因为连作者自己都没有验证过实盘环境下的表现。3. 从零搭建最小回测环境让源码在本地跑起来的关键步骤3.1 环境准备和依赖管理避免源码上来就报错绝大多数量化策略源码跑不起来不是因为策略逻辑复杂而是环境不一致。第一步永远是创建干净的虚拟环境然后安装依赖。源码通常依赖pandas、numpy、matplotlib这三个基础库回测框架层可能会依赖backtrader或vectorbt。我的建议是先用pip list查清楚当前环境再安装缺失的部分python -m venv quant_env source quant_env/bin/activate pip install pandas numpy matplotlib backtrader这里的pandas版本要特别注意。如果源码是在老版本上写的而新环境的 pandas 版跨度太大比如 1.x 到 2.xappend方法、iteritems等旧 API 都会报错。遇到这种情况最省事的办法是看源码里的requirements.txt是否锁定了版本没有的话就逐步把报错信息贴给搜索工具逐行适配。3.2 接入历史数据数据格式的统一是第一个拦路虎量化策略源码对 K 线的列名要求五花八门有的是open、high、low、close、volume有的是中文列名还有的是大小写混用。统一数据接口是跑通源码之前必须做的一步。def normalize_ohlcv(df: pd.DataFrame) - pd.DataFrame: # 统一转为小写列名并保证时间索引 df.columns [c.lower() for c in df.columns] rename_map {时间: time, 开盘: open, 最高: high, 最低: low, 收盘: close, 成交量: volume} df df.rename(columnsrename_map) df[time] pd.to_datetime(df[time]) df df.set_index(time).sort_index() # 只保留需要的列避免脏数据干扰 df df[[open, high, low, close, volume]].astype(float) return df这一步看起来简单但在遇到时间格式不一致、缺失交易时段的数据时非常容易翻车。我遇到过把时间列解析成带时区偏移的 datetime结果sort_index()之后顺序乱了。所以我会建议在sort_index()之后检查一下索引的单调性最好打印前几行确认。3.3 跑通第一轮回测从运行的输出里读出策略的健康度把数据和策略源码对接好之后第一轮回测不要急着看收益率先看三个指标成交次数是否过少或过多、单笔盈亏分布是否合理、回撤曲线是否连续。如果成交量不足说明信号条件过于苛刻如果单笔盈亏极端可能数据处理有未来函数。python backtest.py --start 2022-01-01 --end 2023-12-31回测脚本的输出通常包含净值曲线和交易明细。我会用tail -n 50查看最后的交易列表重点看有没有当天买入当天卖出的情况——如果是日线策略说明可能存在次日逻辑的错误如果是分钟级策略则另当别论。注意第一轮回测的目标不是验证策略能不能赚钱而是验证策略的代码逻辑是否自洽。先把异常输出清干净再谈收益优化。4. 四类经典策略源码的落地范式从趋势跟踪到统计套利4.1 趋势跟踪策略源码均线通道与 ADX 过滤的配合趋势跟踪是量化策略源码中最容易上手的类型但它需要明确的前提市场存在可捕捉的惯性。这类策略在震荡行情中会很惨所以需要在源码里加入趋势强度过滤。def trend_following_entry(df: pd.DataFrame, adx_period: int 14) - pd.Series: # 计算 ADX 值用于判断趋势强度 high df[high] low df[low] close df[close] up_move high.diff() down_move -low.diff() plus_dm np.where((up_move down_move) (up_move 0), up_move, 0.0) minus_dm np.where((down_move up_move) (down_move 0), down_move, 0.0) tr np.maximum(high - low, np.maximum(abs(high - close.shift(1)), abs(low - close.shift(1)))) atr pd.Series(tr).rolling(adx_period).mean() plus_di 100 * pd.Series(plus_dm).rolling(adx_period).mean() / atr minus_di 100 * pd.Series(minus_dm).rolling(adx_period).mean() / atr dx 100 * abs(plus_di - minus_di) / (plus_di minus_di) adx dx.rolling(adx_period).mean() # 趋势策略入场条件价格站上 20 日均线且 ADX 大于阈值 20 ma20 close.rolling(20).mean() entry (close ma20) (adx 20) return entryadx_period和阈值 20 是两个需要配合调的参数。如果 ADX 阈值太高信号会很少但胜率通常高一些如果阈值太低就会在震荡行情里连续止损。这套源码的行数不多但它体现了趋势策略的核心设计——用 ADX 过滤趋势环境再用均线确认入场点。4.2 均值回归策略源码布林带与 z-score 的边界设定均值回归策略的对立面它赚的是价格偏离后回归的钱。这类策略在市场处于震荡区间时表现好但在单边行情中会被持续打脸。源码的核心在于如何定义“偏离”。def mean_reversion_signal(df: pd.DataFrame, window: int 20, num_std: float 2.0) - pd.DataFrame: # 计算布林带中轨和上下轨 df[mid] df[close].rolling(window).mean() df[std] df[close].rolling(window).std() df[upper] df[mid] num_std * df[std] df[lower] df[mid] - num_std * df[std] # z-score 计算标准化价格偏离程度 df[zscore] (df[close] - df[mid]) / df[std] # 入场跌破下轨且 zscore 小于 -2止损zscore 反弹回到 0 df[entry] (df[close] df[lower]) (df[zscore] -num_std) df[exit] df[zscore] 0 return df这里的num_std是最核心的参数。2.0 意味着平均大约 20 个交易日才出现一次入场信号。如果你发现源码默认参数产生的交易次数太少不要急着调低num_std先看看品种的波动特征——波动越大的品种越需要更大的偏离阈值来避免过早入场。4.3 网格交易源码间距与仓位阶梯的平衡网格交易源码在币圈和 A 股 ETF 中都很常见。它的逻辑简单但坑很多。常见做法是设定一个基准价和价格间距价格下跌买入、上涨卖出。代码如下def grid_levels(base_price: float, grid_step: float, num_grids: int 10): # 以 base_price 为中心向下生成 num_grids 档买入价向上生成 num_grids 档卖出价 levels [] for i in range(1, num_grids 1): buy_price base_price * (1 - grid_step * i) sell_price base_price * (1 grid_step * i) levels.append({buy: round(buy_price, 4), sell: round(sell_price, 4)}) return levels网格参数里grid_step直接决定了资金的使用效率。间距过小会频繁触发交易手续费吃掉利润间距过大资金效率低且可能错过行情。我做过一次数据回测grid_step从 0.5% 改为 1.5% 之后年化收益没差太多但交易次数从 600 次降到 200 次手续费省了一大截。4.4 多因子选股源码因子计算逻辑与组合打分多因子选股在 A 股和美股量化中都适用。它的源码结构通常是先计算市值、估值、动量、波动率等因子再对全市场股票打分选出得分最高的前 N 只构成组合。def factor_calc(df: pd.DataFrame) - pd.DataFrame: # 计算技术因子动量、波动率、成交量变化 df[momentum_20] df[close] / df[close].shift(20) - 1 # 20日动量 df[volatility_20] df[close].pct_change().rolling(20).std() # 20日波动率 df[volume_ratio] df[volume] / df[volume].rolling(20).mean() # 量比 # 标准化z-score df[momentum_z] (df[momentum_20] - df[momentum_20].mean()) / df[momentum_20].std() df[volatility_z] (df[volatility_20] - df[volatility_20].mean()) / df[volatility_20].std() df[volume_z] (df[volume_ratio] - df[volume_ratio].mean()) / df[volume_ratio].std() # 综合打分动量正贡献、波动率负贡献、量比正贡献 df[total_score] df[momentum_z] - df[volatility_z] df[volume_z] return df这里有一个容易忽略的细节因子标准化必须在横截面上做也就是同一时间截面上的所有股票一起做 z-score而不是在单只股票的时间序列里做。很多新手源码在这块会出错导致聚合后的组合表现失真。提示拿到多因子源码后先确认因子计算使用的是横截面标准化还是时间序列标准化。两种方式在逻辑上都有道理但它们对应的收益解释完全不同。5. 参数寻优与过拟合量化策略源码中最容易翻车的实战环境5.1 参数寻优的两种方法网格搜索与随机搜索的适用边界一套量化策略源码跑通之后下一步就是调参。网格搜索在小参数空间里很好用比如双均线的 fast 参数取 5 到 15slow 参数取 20 到 60组合数量有限遍历一次在几秒内可以完成。随机搜索更适合参数多、维度高的情况比如同时优化止损比例、仓位倍率、出场阈值面对几百万种组合时随机采样反而效率更高。from sklearn.model_selection import ParameterGrid param_grid { fast: [5, 10, 15], slow: [20, 30, 50], stop_loss: [0.03, 0.05, 0.08] } for params in ParameterGrid(param_grid): result backtest(df, **params) print(params, result[sharpe], result[max_drawdown])跑完一轮后重点比较每组参数下的夏普比率和最大回撤而不是收益率的绝对大小。高收益率伴随高回撤的策略并不难设计难的是在收益和回撤之间找到可接受的平衡点。5.2 过拟合的信号参数高原与孤峰效应参数寻优最大的风险是找到孤峰。当你把收益最高的那组参数用在真实交易中结果往往从峰顶摔下来。判断一组参数是否可信可以看它的“参数高原”——即在这个参数周边一圈稍逊的参数它们的回测表现是否依然接近。如果只有唯一一组参数突出其他相邻参数全部表现很差说明这是过拟合的孤峰不是真实的策略优势。我保存了一次双均线寻优的热力图数据网格中 fast10、slow30 附近有一圈收益在 15% 到 20% 的暖色区域这种情况才说明策略在参数空间上是平滑的。反之如果整个热力图只有 fast12、slow28 这一点特别亮其余全是暗淡那这组参数就是典型的幸存者偏差。5.3 避免过拟合的三个习惯样本外验证、滚动前推、参数简约具体习惯有三条每一条都值得写进你的源码工程里。第一把数据分成样本内训练与样本外验证样本外数据在寻优期间绝对不能碰。第二使用滚动前推方法比如用前 3 年数据寻优后 1 年数据验证然后逐年滑动窗口重复验证。第三尽量保持参数数量少——同一个策略每增加一个自由参数过拟合的概率就显著上升这不是心理安慰而是真实存在的统计规律。注意如果一个源码跑出来的最优参数只有一组而且它的表现远超次优参数不用怀疑这几乎可以确定是过拟合。真实市场的可交易参数通常一片温和的连续区域而不是孤立尖峰。6. 从回测到实盘量化策略源码落地前的验证与部署清单6.1 模拟盘验证用最小的资金成本观察真实滑点与成交延迟回测再漂亮也只是历史模拟。上实盘之前至少要做两到四周的模拟盘跟踪。模拟盘的价值在于观察三个回测无法完整建模的真实因素滑点是否符合预期、成交延迟对信号时效性的影响、以及盘中瞬时价格跳动对触发价的影响。很多人发现模拟盘的收益曲线比回测差一截这是正常的滑点模型不可能完全准确。我的习惯是模拟盘阶段把日志记录得比回测细得多——每笔订单的发出时间、接受时间、成交时间、实际成交价和信号参考价之间的偏差都写入独立的日志文件。这样在真正上实盘后如果持仓表现异常可以回溯到执行链路的具体环节。6.2 实盘部署的最小结构信号服务器与订单执行的解耦实盘部署不建议把信号生成和交易执行放在同一个进程里。常见做法是信号服务器独立运行每隔固定时间计算出目标持仓写入一个状态文件或消息队列执行端读取信号后再连接券商接口下单。这种解耦方案的好处是即使行情源卡顿或者下单接口出错不会阻塞信号生成的主流程。# 信号生成侧定时计算目标持仓并写入状态文件 import json, time while True: df load_latest_klines() target_pos calculate_target_position(df) with open(target_position.json, w) as f: json.dump(target_pos, f) time.sleep(60) # 每分钟刷新一次# 执行侧读取状态文件对比当前持仓决定是否调仓 with open(target_position.json, r) as f: target json.load(f) current_pos get_current_positions() for symbol, target_qty in target.items(): if current_pos.get(symbol, 0) ! target_qty: place_order(symbol, qtytarget_qty - current_pos.get(symbol, 0))这种异步解耦的结构里状态文件就是两个进程之间的对接协议。注意写入和读取时要保证原子性——写文件时先写临时文件再原子重命名避免执行端读到写了一半的脏数据。6.3 上线后的持续监控用这个关键指标判断是否需要下线策略策略上线后的第一周是最关键的观察窗口。不要看日收益看实际滑点和滑点承受能力的关系。如果你设定的滑点模型是万分之二而实盘实际平均滑点到了万分之六那么说明策略的成交冲击成本被低估需要重新评估策略容量或更换执行方式。此外持续监控最大回撤与源码设置的风控阈值是否匹配——如果源码里设了 15% 的回撤熔断实盘净值一旦触发应当无条件执行下线而不是“再观察一下”。我经历过一次因为犹豫而没有执行熔断的教训当时策略在周内连续回撤已经逼近源码中设置的风控线但我因为相信“下周行情会回来”而没有及时下线结果回撤从 12% 扩到接近 20%。从那之后我把风控线的触发视为不可逆的自动化动作不给人为犹豫留出空间。6.4 策略版本管理给每一组参数、每一版代码留后悔药上线后策略不可能一成不变。随着市场风格切换、标的流动性变化参数可能需要迭代。建议从第一天就把每个可运行版本固定下来——记录源码 commit、参数配置、回测区间、以及上线时间。我用的是对每次修改打标签的做法git tag v1.2.0-ma5-sl20 -m MA5/SL20, 2024Q1 sample每个版本的配置、净值和交易日志都归到对应标签下。这样回看某个时间段策略表现的优劣时能够精确找到当时的代码和参数而不是靠记忆推测“当时好像用的是另一套止损”。做完这些验证和部署再回头看量化策略源码本身它就不再是神秘的黑匣子而是一套你自己完全把得住边界的工程系统。希望这篇文章能帮你少走弯路。本文还有配套的精品资源点击获取