均值回归 · 布林带
直觉
趋势策略相信「会涨的继续涨」,均值回归则相反:「价格偏离均值太远,迟早会拉回来」。布林带用「均线 ± k 倍标准差」画出一条正常波动带;当价格冲出带子,就视为过度反应,押注它回归均线。
布林带定义
其中 是 期标准差,通常 、。
交易规则
- 价格跌破下轨 → 超卖,做多(+1);
- 价格涨破上轨 → 超买,做空/离场(−1);
- 用
ffill维持仓位直到下一次触发。
「人话」解释:均值回归什么时候有效?
均值回归最适合区间震荡、无明显趋势的市场——价格在一个带子里来回弹。 一旦遇到强单边趋势(比如持续上涨),布林带会一路被打穿上轨,均值回归策略会反复「逆势抄顶」而巨亏。 所以趋势 vs 回归是两种对立信仰,择时/识别市场状态是关键。
可运行案例:布林带均值回归策略
import quant
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv('/data/spy_daily.csv', parse_dates=['date']).set_index('date')
close = df['adj_close']
N, k = 20, 2.0
mid = close.rolling(N).mean()
sd = close.rolling(N).std()
upper, lower = mid + k*sd, mid - k*sd
# 信号:跌破下轨做多、涨破上轨做空,ffill 维持
raw = pd.Series(np.nan, index=close.index)
raw[close < lower] = 1.0
raw[close > upper] = -1.0
signal = raw.ffill().fillna(0.0)
res = quant.vector_backtest(close, signal, cost_bps=2.0, freq=252)
print(quant.performance_summary(res['equity'], res['returns'], freq=252).round(3))
# 画最近一年的带子与触发点
fig, ax = plt.subplots(2, 1, figsize=(9, 5.2), sharex=True,
gridspec_kw={'height_ratios':[2,1]})
recent_start = close.index.max() - pd.Timedelta(days=365)
rc, ru, rl, rm = [x.loc[recent_start:] for x in (close, upper, lower, mid)]
ax[0].plot(rc, color='#2563eb', lw=1, label='收盘价')
ax[0].plot(rm, color='#94a3b8', lw=1, label='中轨'); ax[0].fill_between(ru.index, ru, rl, alpha=0.15, label='布林带')
ax[0].plot(rc[rc < rl], 'g^', ms=5, label='做多触发')
ax[0].plot(rc[rc > ru], 'rv', ms=5, label='做空触发')
ax[0].legend(fontsize=8); ax[0].set_title('布林带与触发点(近1年)')
ax[1].plot(res['equity'].loc[recent_start:], color='crimson'); ax[1].set_title('策略资金曲线(近1年)')
plt.tight_layout(); plt.show()
动手改一改:拖动参数即时看回测
拖动窗口 、倍数 与成本——更宽的带子(大 )触发少、换手低;过窄()则假信号泛滥、被成本拖垮。
# ParamSlider(SSR 预览)
参数: N, K, COST_BPS
import quant
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv('/data/spy_daily.csv', parse_dates=['date']).set_index('date')
close = df['adj_close']
mid = close.rolling(N).mean(); sd = close.rolling(N).std()
upper, lower = mid + K*sd, mid - K*sd
raw = pd.Series(np.nan, index=close.index)
raw[close < lower] = 1.0
raw[close > upper] = -1.0
signal = raw.ffill().fillna(0.0)
r = quant.vector_backtest(close, signal, cost_bps=COST_BPS, freq=252)
print(quant.performance_summary(r['equity'], r['returns'], freq=252).round(3))
plt.figure(figsize=(9, 3.6))
plt.plot(r['equity'], color='crimson')
plt.title(f'布林带 N={N}, k={K}, 成本 {COST_BPS} bps'); plt.ylabel('净值')
plt.tight_layout(); plt.show()
小结
- 布林带 = 均线 ± 倍标准差,刻画「正常波动带」;
- 突破上下轨 → 押注回归均值(超卖做多、超买做空);
- 均值回归怕强趋势——在单边行情里会持续逆势亏损。