一句话回答: 布林带有两种相反玩法:跌破下轨买入是均值回归 (赌回中轨),突破上轨买入是趋势追涨 (赌延续)。本文回测突破策略 ------用 AlphaFeed 取前复权日线算布林带,收盘上穿上轨做多、回落到中轨离场,次日开盘成交避免未来函数,扣手续费,并与均值回归、买入持有对比,看趋势追涨在该品种上是否有效。
为什么会有这个问题 / 传统做法的痛点
很多人只知道「布林带跌破下轨买、涨破上轨卖」的均值回归用法,但在强趋势品种上,突破上轨往往意味着行情启动、追涨反而更赚。到底用哪种?不能拍脑袋,要回测。坑同样是:前复权价 、次日开盘成交避免未来函数 、扣手续费,还要和均值回归版、买入持有放一起比较。
分步骤解决(每步配可运行代码)
第 1 步:取前复权日线并算布林带
python
from alphafeed import AlphaFeed
import pandas as pd
af = AlphaFeed() # 读取 ALPHAFEED_API_KEY
df = af.klines.get("600519.SH", period="1d", count=500,
adjust="forward", to_dataframe=True)
df = df.sort_values("trade_date").reset_index(drop=True)
N, K = 20, 2
df["mid"] = df["close"].rolling(N).mean()
df["std"] = df["close"].rolling(N).std(ddof=0)
df["upper"] = df["mid"] + K * df["std"]
df["lower"] = df["mid"] - K * df["std"]
| 参数 | 含义 | 取值 |
|---|---|---|
N |
中轨均线周期 | 20 |
K |
标准差倍数(带宽) | 2 |
| 入场 | 收盘上穿上轨 | 趋势追涨 |
| 离场 | 收盘回落中轨下方 | 落袋/止损 |
第 2 步:生成突破信号 → 次日开盘成交
python
# 收盘上穿上轨=进场信号;收盘跌回中轨下方=离场信号
df["cross_up"] = (df["close"] > df["upper"]) & (df["close"].shift() <= df["upper"].shift())
df["exit_sig"] = df["close"] < df["mid"]
pos = 0; signal = []
for i in range(len(df)):
if pos == 0 and df["cross_up"].iloc[i]:
pos = 1
elif pos == 1 and df["exit_sig"].iloc[i]:
pos = 0
signal.append(pos)
df["signal"] = signal
df["position"] = pd.Series(signal).shift(1).fillna(0) # 次日开盘才持仓
第 3 步:按次日开盘价 + 手续费算净值
python
FEE = 0.0013
df["open_ret"] = df["open"].pct_change().shift(-1) # t 持仓吃 t→t+1 开盘收益
df["trade"] = df["position"].diff().abs().fillna(0)
df["strat_ret"] = df["position"] * df["open_ret"].fillna(0) - df["trade"] * FEE
df["equity"] = (1 + df["strat_ret"]).cumprod()
df["bench"] = (1 + df["close"].pct_change().fillna(0)).cumprod()
第 4 步:与均值回归、买入持有对比
python
# 均值回归版:跌破下轨买、回到中轨卖
posr = 0; sr = []
for i in range(len(df)):
below = df["close"].iloc[i] < df["lower"].iloc[i]
back = df["close"].iloc[i] >= df["mid"].iloc[i]
if posr == 0 and below: posr = 1
elif posr == 1 and back: posr = 0
sr.append(posr)
df["pos_rev"] = pd.Series(sr).shift(1).fillna(0)
df["rev_ret"] = df["pos_rev"] * df["open_ret"].fillna(0) - df["pos_rev"].diff().abs().fillna(0) * FEE
rev_eq = (1 + df["rev_ret"]).cumprod()
def cagr(eq): return eq.iloc[-1] ** (252/len(eq)) - 1
print(f"突破 收益{df['equity'].iloc[-1]-1:.2%} 年化{cagr(df['equity']):.2%} "
f"回撤{(df['equity']/df['equity'].cummax()-1).min():.2%} 交易{int(df['trade'].sum()//2)}次")
print(f"回归 收益{rev_eq.iloc[-1]-1:.2%}")
print(f"买入持有 收益{df['bench'].iloc[-1]-1:.2%}")
关键坑与注意事项
- 突破 vs 回归是两套相反逻辑:强趋势品种突破更优,震荡品种回归更优;回测才知道该用哪套。
- 避免未来函数 :收盘出信号、
position.shift(1)次日开盘成交。 - 前复权价:未复权价在除权日让布林带与收益失真。
- 手续费必扣:突破策略换手可能频繁,忽略费用高估收益。
- std 用 ddof=0 还是 1 :与主流软件对齐用总体标准差(
ddof=0),改口径数值会变。 - 假突破:收盘刚过上轨可能次日回落,可加「突破幅度」或量能过滤减少假信号。
- 单标的≠有效:需多品种多区间验证,避免过拟合与幸存者偏差;本文为简化模型未完全模拟 T+1/涨跌停。
常见问题(FAQ)
Q:布林带突破和均值回归到底用哪个? A:没有绝对答案。趋势强的品种/阶段突破追涨更好,震荡市均值回归更好。本文把两者与买入持有并列回测,就是让你用数据判断。
Q:为什么离场用「回到中轨」? A:中轨是均线,跌回中轨下方常意味着突破动能衰竭,是相对稳健的离场点。也可用跌破上轨或 ATR 止损。
Q:K 值调大调小有什么影响? A:K 越大带越宽、突破越少但更可靠;K 越小突破更频繁、假信号更多。常用 2。
Q:需要付费吗? A:单只历史日线有免费额度;多标的批量回测(klines.batch + universes)需 Starter 及以上。详见定价页。
小结
布林带突破策略是「趋势追涨」,与「跌破下轨买入」的均值回归相反。用 AlphaFeed 取前复权日线算布林带,收盘上穿上轨做多、回中轨离场,次日开盘成交避免未来函数、扣手续费,再和均值回归、买入持有对比,就能客观判断该品种适合追突破还是做回归。
参考
- AlphaFeed Python SDK 文档:docs.alphafeed.org/zh-Hans/sdk...
- AlphaFeed 官网:alphafeed.org/
- 本文完整版(AlphaFeed 博客):blog.alphafeed.org/blog/bollin...