简介这份资源面向量化交易初学者与进阶开发者系统梳理了10种经典策略的核心逻辑与适用场景涵盖趋势跟踪、均值回归、统计套利、高频交易、机器学习、事件驱动、波动率、多因子选股、套利及另类数据策略帮助读者建立策略选型的整体认知框架。压缩包共5个文件约146KB包含Python策略源码、运行说明文本、网页展示文件及回测结果图其中源码以移动平均线交叉的趋势跟踪策略为例借助backtrader框架演示了策略实现与回测流程便于直接运行验证。目前已有343人学习下载。读者可从中获得各策略的指标方法、市场特点与风险提示并参考回测与过拟合、交易成本、风险控制等关键注意事项为后续实盘实践提供可复用的代码基础与思路参考。1. 10种经典量化策略源码从双均线到多因子一套能跑起来的骨架很多人第一次接触量化是从网上扒到一段“神一般的主图指标源码”开始的——打开一看几百行公式金叉买死叉卖回测曲线漂亮得不像话。但真拿去跑实盘滑点一加、手续费一扣收益直接腰斩。问题不在策略本身而在于那段代码只给了信号没给仓位管理、没给风控、没给数据对齐。所谓“10种经典量化策略源码”真正有价值的不是那10个信号公式而是它们背后共用的一套工程骨架数据怎么喂、信号怎么算、仓位怎么定、回撤怎么控。这篇文章面向的是想用Python把量化策略真正跑起来的从业者——不管你是做期货量化策略还是股票短线这10种策略的源码结构是相通的。我会把双均线、布林带突破、RSI反转、动量轮动、均值回归、海龟交易、网格、多因子选股、配对交易、趋势跟踪这10种经典策略拆成可复现的代码模块重点讲清楚每个策略的参数怎么设、坑在哪、什么行情下会翻车。2. 双均线与布林带两条最基础的源码骨架怎么搭2.1 双均线策略的源码结构与参数选择双均线是所有量化策略的“Hello World”但恰恰是它最容易让人产生“我行了”的错觉。核心逻辑简单到一句话短均线上穿长均线做多下穿做空或平仓。但源码里真正要处理的是三件事均线类型SMA还是EMA、周期参数、以及信号确认机制。我一般用EMA而不是SMA因为EMA对近期价格赋予更高权重信号滞后更小。周期上经典组合是5/20、10/30、20/60但这不是拍脑袋定的——你得看标的的波动周期。一个实用的方法是先做参数扫描看哪个组合在样本内夏普比率最高且参数平原最宽即邻近参数表现不急剧变差。import pandas as pd import numpy as np def dual_ma_strategy(df, short_window10, long_window30, ma_typeema): 双均线策略源码核心逻辑 df: 包含 close 列的 DataFrame索引为日期 short_window: 短均线周期 long_window: 长均线周期 ma_type: sma 或 ema data df.copy() if ma_type ema: data[ma_short] data[close].ewm(spanshort_window, adjustFalse).mean() data[ma_long] data[close].ewm(spanlong_window, adjustFalse).mean() else: data[ma_short] data[close].rolling(windowshort_window).mean() data[ma_long] data[close].rolling(windowlong_window).mean() # 信号短均线在长均线上方为1下方为0 data[signal] np.where(data[ma_short] data[ma_long], 1, 0) # 持仓变化signal从0变1为开多从1变0为平仓 data[position] data[signal].diff() return data这段代码里ewm(spanshort_window, adjustFalse)是EMA的标准写法adjustFalse保证递推计算与常见软件一致。signal.diff()捕捉的是信号变化点而不是持仓状态本身——这是很多人写错的地方直接用signal当仓位会导致在信号持续期间反复计算收益。参数上短周期低于5会产生大量噪音信号长周期超过120则信号稀疏到几乎没有交易机会。对于日线级别的期货量化策略10/30是一个比较稳的起点。2.2 布林带突破策略的源码实现与带宽过滤布林带的核心不是中轨而是带宽。很多新手写的布林带策略源码只用了上下轨突破结果在震荡市里被反复打脸。正确的做法是加一层带宽过滤当带宽上轨减下轨除以中轨低于某个阈值时说明市场波动收缩此时突破信号更可靠带宽过高时反而要警惕假突破。def bollinger_breakout(df, window20, num_std2.0, bandwidth_threshold0.05): 布林带突破策略源码 window: 均线窗口 num_std: 标准差倍数 bandwidth_threshold: 带宽过滤阈值低于此值才考虑突破 data df.copy() data[mid] data[close].rolling(windowwindow).mean() data[std] data[close].rolling(windowwindow).std() data[upper] data[mid] num_std * data[std] data[lower] data[mid] - num_std * data[std] # 带宽 (上轨 - 下轨) / 中轨 data[bandwidth] (data[upper] - data[lower]) / data[mid] # 突破信号收盘价突破上轨且带宽低于阈值 data[signal] 0 data.loc[(data[close] data[upper]) (data[bandwidth] bandwidth_threshold), signal] 1 data.loc[(data[close] data[lower]) (data[bandwidth] bandwidth_threshold), signal] -1 return data参数上window20是经典值num_std2.0对应约95%的置信区间。bandwidth_threshold需要根据标的的历史带宽分布来定——我一般取过去250个交易日带宽的30%分位数。这样做的逻辑是只在波动收缩后等待突破避免在已经大幅波动后追高。注意布林带策略在趋势启动初期表现最好但在趋势中段会频繁被上下轨“扫损”所以必须配合止损规则不能只靠信号反手。3. RSI反转与动量轮动震荡市和趋势市的两种活法3.1 RSI反转策略的源码细节与超卖阈值调整RSI反转策略的逻辑是RSI低于30超卖买入高于70超买卖出。但直接套用这个规则在A股或期货上会亏得很惨因为强趋势中RSI可以长期钝化在30以下或70以上。源码里必须加入趋势过滤——比如只在价格高于200日均线时才做多RSI超卖信号。def rsi_reversal(df, rsi_period14, oversold30, overbought70, trend_ma200): RSI反转策略源码带趋势过滤 data df.copy() delta data[close].diff() gain delta.where(delta 0, 0.0) loss -delta.where(delta 0, 0.0) avg_gain gain.ewm(alpha1/rsi_period, adjustFalse).mean() avg_loss loss.ewm(alpha1/rsi_period, adjustFalse).mean() rs avg_gain / avg_loss data[rsi] 100 - (100 / (1 rs)) data[trend_ma] data[close].rolling(windowtrend_ma).mean() data[signal] 0 # 只在上升趋势中做多超卖 data.loc[(data[rsi] oversold) (data[close] data[trend_ma]), signal] 1 # 只在下降趋势中做空超买 data.loc[(data[rsi] overbought) (data[close] data[trend_ma]), signal] -1 return data这里ewm(alpha1/rsi_period, adjustFalse)是Wilder原始RSI的递推算法和常见的SMA版RSI有细微差别但更贴近经典定义。超卖阈值不要死守30——对于波动大的品种可以调到20甚至15对于波动小的25可能更合适。判断方法是看RSI在过去一年的分布取5%分位数作为超卖线。趋势过滤的200日均线是分水岭价格在均线下方时RSI超卖信号大概率是下跌中继不是反转。3.2 动量轮动策略的源码框架与调仓周期动量轮动是“买最强的卖最弱的”在期货多品种或股票多标的之间轮动。源码核心是计算每个标的的动量得分比如过去20日收益率然后持有得分最高的N个。但这里有个大坑调仓周期太短会导致换手率爆炸手续费吃掉所有利润太长则跟不上风格切换。def momentum_rotation(price_dict, lookback20, top_n3, rebalance_days5): 动量轮动策略源码 price_dict: {标的代码: 收盘价Series} lookback: 动量计算回看天数 top_n: 持有标的数量 rebalance_days: 调仓间隔天数 # 合并所有标的收盘价 prices pd.DataFrame(price_dict) # 计算动量得分 momentum prices.pct_change(periodslookback) signals pd.DataFrame(0, indexprices.index, columnsprices.columns) for i in range(lookback, len(prices), rebalance_days): # 每 rebalance_days 调仓一次 scores momentum.iloc[i].dropna() if len(scores) top_n: continue top_assets scores.nlargest(top_n).index signals.iloc[i:irebalance_days, :] 0 signals.loc[signals.index[i:irebalance_days], top_assets] 1 return signalslookback20对应约一个月的动量rebalance_days5是周度调仓。注意signals.iloc[i:irebalance_days, :] 0这行先把区间清零再赋值避免前一次调仓的持仓残留。动量轮动在趋势明显的年份表现极好但在震荡市会反复被打脸——2021年春节后的核心资产回调就是典型翻车场景。所以源码里最好再加一层波动率过滤当所有标的的动量得分都低于某个阈值时空仓等待。4. 均值回归与配对交易统计套利的源码怎么落地4.1 均值回归策略的源码实现与布林带变体均值回归和布林带突破是反过来的价格偏离均线越远越可能回归。源码上可以用z-score来标准化偏离程度。def mean_reversion(df, window20, entry_z2.0, exit_z0.5): 均值回归策略源码 entry_z: 开仓z-score阈值 exit_z: 平仓z-score阈值 data df.copy() data[ma] data[close].rolling(windowwindow).mean() data[std] data[close].rolling(windowwindow).std() data[zscore] (data[close] - data[ma]) / data[std] data[signal] 0 # z-score低于 -entry_z 做多 data.loc[data[zscore] -entry_z, signal] 1 # z-score高于 entry_z 做空 data.loc[data[zscore] entry_z, signal] -1 # z-score回归到 ±exit_z 以内平仓 data.loc[abs(data[zscore]) exit_z, signal] 0 return dataentry_z2.0意味着价格偏离均值2个标准差exit_z0.5是回到0.5个标准差内平仓。这个策略在震荡品种上表现好比如某些农产品期货。但要注意z-score的均值和标准差是滚动计算的当趋势来临时均线本身会跟着价格移动导致z-score永远回不到0——这就是均值回归策略在单边行情中爆仓的原因。源码里必须加一个硬止损当亏损超过账户权益的2%时强制平仓。4.2 配对交易策略的协整检验与源码步骤配对交易是均值回归的进阶版找两个历史走势高度相关的标的当价差偏离均值时做多一个做空另一个。源码的第一步不是写交易逻辑而是做协整检验。import statsmodels.api as sm from statsmodels.tsa.stattools import coint def pair_trading(price_a, price_b, window60, entry_z2.0, exit_z0.5): 配对交易策略源码 price_a, price_b: 两个标的的收盘价Series # 第一步协整检验 score, pvalue, _ coint(price_a, price_b) if pvalue 0.05: print(f协整检验不通过pvalue{pvalue:.4f}不适合配对交易) return None # 第二步计算对冲比例用OLS回归 X sm.add_constant(price_b) model sm.OLS(price_a, X).fit() hedge_ratio model.params.iloc[1] # price_b的系数 # 第三步计算价差和z-score spread price_a - hedge_ratio * price_b spread_ma spread.rolling(windowwindow).mean() spread_std spread.rolling(windowwindow).std() zscore (spread - spread_ma) / spread_std # 第四步生成信号 signals pd.DataFrame(indexprice_a.index) signals[zscore] zscore signals[signal] 0 signals.loc[zscore -entry_z, signal] 1 # 价差过低做多价差 signals.loc[zscore entry_z, signal] -1 # 价差过高做空价差 signals.loc[abs(zscore) exit_z, signal] 0 return signals, hedge_ratio协整检验的p值必须小于0.05才能继续否则两个标的的价差没有稳定的均值。hedge_ratio是对冲比例决定了做多1手A的同时要做空多少手B。配对交易最大的坑是“协整关系破裂”——比如两个公司原本业务相似突然一个转型了价差就再也回不来了。所以源码里要定期重新做协整检验一般每60个交易日重跑一次。5. 海龟交易与网格策略源码里的仓位管理和边界条件5.1 海龟交易策略的ATR仓位计算源码海龟交易的核心不是入场信号唐奇安通道突破而是仓位管理。源码里必须包含ATR计算和单位仓位调整。def turtle_strategy(df, entry_period20, exit_period10, atr_period20, risk_pct0.01): 海龟交易策略源码 entry_period: 入场通道周期 exit_period: 出场通道周期 atr_period: ATR计算周期 risk_pct: 每笔交易风险占账户比例 data df.copy() data[high_low] data[high] - data[low] data[high_close] abs(data[high] - data[close].shift(1)) data[low_close] abs(data[low] - data[close].shift(1)) data[tr] data[[high_low, high_close, low_close]].max(axis1) data[atr] data[tr].rolling(windowatr_period).mean() data[entry_high] data[high].rolling(windowentry_period).max() data[entry_low] data[low].rolling(windowentry_period).min() data[exit_high] data[high].rolling(windowexit_period).max() data[exit_low] data[low].rolling(windowexit_period).min() # 单位仓位 (账户权益 * risk_pct) / ATR # 这里假设账户权益为100万 account_equity 1000000 data[unit_size] (account_equity * risk_pct) / data[atr] data[signal] 0 data.loc[data[close] data[entry_high].shift(1), signal] 1 data.loc[data[close] data[entry_low].shift(1), signal] -1 # 出场跌破10日低点平多涨破10日高点平空 data.loc[data[close] data[exit_low].shift(1), signal] 0 data.loc[data[close] data[exit_high].shift(1), signal] 0 return dataunit_size是海龟交易的精髓ATR越大仓位越小保证每笔交易的风险敞口恒定。risk_pct0.01意味着每笔交易最多亏账户的1%。这个策略在趋势流畅的品种上表现极好但在震荡市会连续小亏。源码里还要加一个“加仓”逻辑每盈利0.5个ATR加仓一次最多加3次。不加仓的海龟不是完整的海龟。5.2 网格策略的源码实现与震荡区间判定网格策略是在价格区间内等距挂买卖单适合震荡市。源码的关键是确定网格上下界和网格数量。def grid_strategy(df, window60, grid_num10): 网格策略源码 window: 用于确定震荡区间的回看天数 grid_num: 网格数量 data df.copy() data[upper] data[high].rolling(windowwindow).max() data[lower] data[low].rolling(windowwindow).min() data[grid_step] (data[upper] - data[lower]) / grid_num # 生成网格线 for i in range(1, grid_num): data[fgrid_{i}] data[lower] i * data[grid_step] # 信号价格每跌破一条网格线买入一份 data[signal] 0 for i in range(1, grid_num): data.loc[data[close] data[fgrid_{i}], signal] 1 # 价格每涨破一条网格线卖出一份 for i in range(1, grid_num): data.loc[data[close] data[fgrid_{i}], signal] - 1 return data网格策略最大的风险是单边行情——价格跌破下界后继续跌网格策略会满仓被套。所以源码里必须加一个“破网止损”当价格跌破下界超过一定幅度比如2%清仓并停止网格。window60是确定震荡区间的回看期太短会导致区间频繁变化太长则反应迟钝。6. 多因子选股与趋势跟踪从单策略到组合的源码整合6.1 多因子选股策略的源码框架与因子合成多因子选股是股票量化的主流源码核心是因子计算、因子标准化、因子合成、选股。这里给一个简化框架。def multi_factor_selection(factor_dict, top_n20): 多因子选股策略源码 factor_dict: {因子名: DataFrame(索引为日期列为股票代码)} top_n: 选股数量 # 第一步因子标准化z-score normalized {} for name, df in factor_dict.items(): normalized[name] (df - df.mean(axis1, keepdimsTrue)) / df.std(axis1, keepdimsTrue) # 第二步因子合成等权 composite sum(normalized.values()) / len(normalized) # 第三步选股 signals pd.DataFrame(0, indexcomposite.index, columnscomposite.columns) for date in composite.index: scores composite.loc[date].dropna() if len(scores) top_n: continue top_stocks scores.nlargest(top_n).index signals.loc[date, top_stocks] 1 return signals因子标准化用横截面z-score即每个交易日对所有股票做标准化。因子合成用等权实际中可以用IC加权或机器学习加权。注意多因子选股必须做行业中性化和市值中性化否则选出来的全是小市值股票风格暴露太大。6.2 趋势跟踪策略的源码实现与多周期过滤趋势跟踪是“截断亏损让利润奔跑”。源码上可以用双均线加ATR止损。def trend_following(df, fast20, slow60, atr_period20, atr_mult2.0): 趋势跟踪策略源码 data df.copy() data[ma_fast] data[close].rolling(windowfast).mean() data[ma_slow] data[close].rolling(windowslow).mean() data[tr] data[[high, low, close]].apply( lambda x: max(x[high]-x[low], abs(x[high]-x[close]), abs(x[low]-x[close])), axis1) data[atr] data[tr].rolling(windowatr_period).mean() data[signal] 0 data.loc[data[ma_fast] data[ma_slow], signal] 1 data.loc[data[ma_fast] data[ma_slow], signal] -1 # ATR止损入场价 ± atr_mult * ATR data[entry_price] data[close].where(data[signal].diff() ! 0) data[entry_price] data[entry_price].ffill() data[stop_long] data[entry_price] - atr_mult * data[atr] data[stop_short] data[entry_price] atr_mult * data[atr] # 触发止损则平仓 data.loc[(data[signal] 1) (data[close] data[stop_long]), signal] 0 data.loc[(data[signal] -1) (data[close] data[stop_short]), signal] 0 return dataatr_mult2.0是止损倍数越大越能扛波动但单笔亏损也越大。趋势跟踪在商品期货上表现最好因为期货趋势持续时间长。源码里还可以加一个“多周期过滤”只在周线趋势向上时日线才做多。7. 避坑与排查10种策略源码里最容易翻车的5个地方7.1 未来函数回测漂亮实盘拉胯的元凶现象回测年化收益50%实盘一跑就亏。原因源码里用了未来数据比如用当日收盘价计算信号却用当日开盘价成交。解决所有信号必须用shift(1)滞后一天确保用昨天收盘后知道的信息今天开盘才交易。7.2 过拟合参数扫描扫出来的“最优解”现象某个参数组合在历史上表现完美换一段数据就崩。原因参数被过度优化拟合了历史噪音。解决用样本外测试和滚动窗口验证参数平原比参数峰值更重要。7.3 手续费和滑点被忽略现象回测收益高实盘被手续费吃光。原因源码里没扣交易成本。解决股票双边千分之三期货按手数算滑点至少加一个最小变动价位。7.4 数据对齐错误现象多品种回测时某些品种的信号用了未来数据。原因不同品种的交易时间不同直接合并DataFrame会导致对齐错误。解决用reindex对齐到统一交易日历并检查每个品种的信号是否滞后。7.5 仓位管理缺失现象策略信号对但账户波动巨大。原因每次满仓进出没有根据波动率调整仓位。解决用ATR或波动率倒数分配仓位确保每笔交易风险恒定。8. 把10种策略源码串成一个可复用的回测框架写到这里10种策略的源码骨架都过了一遍。但真正让它们产生价值的不是单独跑某一个而是把它们塞进同一个回测框架里用同一套数据、同一套成本模型、同一套风控规则去对比。我自己的习惯是先写一个BaseStrategy类定义generate_signals和calculate_position两个抽象方法然后每个策略继承它。这样换策略只需要改信号逻辑不用动回测引擎。class BaseStrategy: def __init__(self, data, params): self.data data self.params params def generate_signals(self): raise NotImplementedError def calculate_position(self, signals): # 默认等权仓位子类可覆盖 return signals * (1.0 / signals.abs().sum(axis1).max()) def backtest(self): signals self.generate_signals() positions self.calculate_position(signals) returns self.data[close].pct_change().shift(-1) strategy_returns (positions * returns).sum(axis1) return strategy_returns这个框架里returns.shift(-1)是第二天收益positions是今天收盘后确定的仓位两者相乘就是策略收益。注意calculate_position里做了归一化确保总仓位不超过1。实际用的时候每个策略子类只需要实现generate_signals返回一个和data同索引的DataFrame列是标的值是目标仓位。最后一个技巧不要迷信任何一个策略。我自己的习惯是每周跑一次全部10个策略的回测看哪些策略近期在回撤、哪些在创新高然后动态调整权重。量化交易不是找到圣杯而是管理一组相关性低的策略让整体曲线平滑。希望帮到你。本文还有配套的精品资源点击获取