先说结论:用 akshare 拉 A 股数据、按布林通道做均值回归,这套组合在个人量化里属于“性价比”很高的起手式——数据免费、逻辑透明、代码量小,踩着下轨买、碰到上轨卖,震荡行情里能跑出肉眼可见的效果。这篇文章不是给你一篇糊弄人的“策略源码”,而是把从装库、取数据、算布林带、写信号到回测踩坑的完整链路都摊开讲一遍,绝大多数坑都是我自己实盘验证过的,照着抄能少走很多弯路。
如果你刚入门量化、又不想在数据源上花太多钱,或者想验证某个技术指标到底有没有用,这篇非常适合你。我会尽量用大白话把布林通道的数学原理、akshare 的调用细节、信号后处理的常见坑讲透,读完你至少能自己搭出一套跑得通的本地策略雏形。
1. 项目整体设计与思路拆解
1.1 为什么数据源选 akshare
做任何量化策略,第一步永远不是写策略,而是搞定数据。A 股这块尤其麻烦:Tushare 老版本免费但积分门槛越来越高,聚宽、米筐这类平台虽然数据全,但策略只能挂在他们生态里,本地想随便跑个脚本数据还得申请导出。akshare 的逻辑完全不同——它本质上是一个“爬虫聚合库”,直接把东方财富、新浪财经、上交所、深交所等公开页面上的数据整理成规整的 pandas DataFrame,不需要注册、不需要 token,pip 装完就能用。
我说句实在话,akshare 的数据质量控制比不上专业金融终端,盘中数据偶尔会有字段类型不一致、停牌股票数据缺失这类小问题。但你要知道它的定位:个人做策略研究、写学习项目、拉历史数据做回测,这个颗粒度完全够用。真到了需要盘口 tick 级数据或者毫秒级行情的阶段,那不是 akshare 的赛道,你也不该用免费库去硬扛。
除了免费和无需认证这两个杀手锏,akshare 另一个优势是覆盖面够宽。它不只给日线周线,连期权数据、期货主力连续、可转债、基金净值、北向资金流向这些都有接口。这意味着你后期想在策略里加一个宏观维度或者换到期货市场,数据层不用重构,只需要换几个接口函数。对我来说,这比单点指标好用很多。
1.2 为什么策略选布林通道而不是别的
市面上技术指标加起来上百个,MACD、KDJ、RSI、CCI,各有各的信徒。但布林通道(Bollinger Bands)有一个先天的优势:它的数学定义极度清晰,完全由统计量构成,没有钝化、没有阈值经验值,随便一个人用同样的参数都能算出同样的曲线。这一点对“验证策略逻辑”来说至关重要——你能清晰地知道,赚的钱到底是来自统计规律,还是来自指标里暗藏的偏差。
布林通道的核心假设是价格服从某种均值回归过程。翻译成人话:价格短时间内冲得太猛,大概率要往均值方向回拉。所以你看到价格突破上轨,不是追涨信号,反而是一个“涨过头了、准备做空/减仓”的信号;价格跌破下轨,不是崩盘信号,而是“跌过头了、可以考虑抄底”的信号。这套逻辑在震荡市非常舒服,价格来回撞轨,策略来回吃肉。
当然,它的死穴也非常明确:单边趋势行情里,价格可以贴着上轨连续跑十几天,你每次“碰到上轨就卖”都是提前下车。所以整套策略设计里我会专门加“趋势过滤器”来回避这个问题。别怕指标有短板,最怕的是你不知道短板在哪就开始上实盘。
1.3 策略的目标场景和适用人群
这套“akshare 获取日线数据 + 布林通道择时”的组合,最适合三类人:
第一类是 Python 有点基础、但没正经做过量化的同学。这个项目代码量控制在两三百行以内,核心逻辑封装成一个函数,数据源免费,调试成本低,作为第一个完整量化项目非常合适。
第二类是手头有股票、但完全靠盘感操作的朋友。把布林通道写到脚本里,你可以每天收盘后自动跑一遍,让程序告诉你哪些票打到了下轨、哪些进入了减仓区间。它不替你决策,但它能帮你把“情绪化交易”这个最大的坑过滤掉。
第三类是准备深入量化、想先搭一套本地研究框架的人。akshare 负责数据层,pandas 负责清洗,backtrader 或者自写回测函数负责验证。这个流程跟上实盘、上仿真、上多因子线完全一致,后面加模型、加风控只是在这套骨架上填肉。
我自己用这套策略跑沪深 300 成分股,在 2019–2023 年这段区间,年化收益率不一定亮眼,但回撤控制得不错,而且全程逻辑是黑盒之外的,每一笔信号都能回溯到那一天的数据和均线位置。这就够了——策略的“可解释性”在实盘里比收益率更重要。
2. 环境准备与 akshare 数据链路搭建
2.1 akshare 安装与国内加速方案
先说安装。akshare 已经发布到 PyPI,安装命令就一条pip install akshare。但国内网络环境下,直接用默认源有时候会慢到怀疑人生,我建议你走清华镜像:
pip install akshare -i https://pypi.tuna.tsinghua.edu.cn/simple装完先别急着跑策略,用下面这行确认版本和依赖是否正常。akshare 迭代速度非常快,基本每个月都有更新,接口偶尔会调整参数名,所以“固定版本号”对长期项目很重要:
python -c "import akshare as ak; print(ak.__version__)"最好把版本号记下来,比如我当前用的是 1.14.x。后面如果重装环境,直接用pip install akshare==1.14.x锁版本,避免上游接口变动影响你的策略结果。这一条经验是我被坑过好多次才养成的习惯——你有一次的时候策略跑了三个月,某天突然报错“No module named xxx”或者“参数 xxx 不存在”,多半不是代码的问题,而是依赖库升级了。
依赖方面,akshare 会同时装上 pandas、requests、beautifulsoup4 这些基础库。如果你已经装了 anaconda,基本开箱即用,不需要额外配置。需要提醒的是,Python 版本最好在 3.8 到 3.12 之间,太老的 3.6 会碰到类型注解不兼容的问题,太新的 3.13 有些底层依赖还没跟上。我用的 3.10 至今没出过兼容性毛病。
2.2 跑通第一条数据:验证数据链路
装完库之后,第一件事不是写策略,而是验证数据能不能正常拉到。akshare 的接口很多,但最常用的是下面这几个:
ak.stock_zh_a_hist():A 股日线行情,支持复权ak.stock_zh_a_spot_em():A 股实时快照,全部股票最新价ak.stock_individual_info_em():个股基本信息ak.stock_zh_index_daily():指数日线
为了跑布林策略,我们需要的是个股的日线 OHLCV 数据,也就是开盘价、最高价、最低价、收盘价、成交量、成交额。用stock_zh_a_hist拿这些数据非常方便:
import akshare as ak import pandas as pd df = ak.stock_zh_a_hist( symbol="600519", # 贵州茅台 period="daily", # 日线 start_date="20200101", end_date="20231231", adjust="qfq" # 前复权 ) print(df.head()) print(df.tail())输出的 DataFrame 会包含日期、开盘、收盘、最高、最低、成交量、成交额、振幅、涨跌幅、涨跌额、换手率这些列。不过要注意,akshare 返回的列名是中文,这在编程的时候稍有点别扭,但优点是肉眼可读、调试方便。
实际写策略前,我建议你加一个清洗步骤:把“日期”列统一转成 pandas 的 datetime 类型并设为索引,把 OHLC 列转成 float,然后按日期排序。这一步看起来很基础,但数据预处理做不干净,后面画图和计算信号都会出各种诡异问题。
df = df.rename(columns={ "日期": "date", "开盘": "open", "收盘": "close", "最高": "high", "最低": "low", "成交量": "volume" }) df["date"] = pd.to_datetime(df["date"]) df = df.set_index("date").sort_index() for col in ["open", "close", "high", "low", "volume"]: df[col] = pd.to_numeric(df[col], errors="coerce") df = df.dropna(subset=["close"])2.3 数据链路里的“隐形坑”
我实际操作中发现,akshare 的数据链路有几个容易踩的坑值得专门写一下。
第一个是复权问题。默认情况下stock_zh_a_hist是可以指定复权方式的,""表示不复权,"qfq"是前复权,"hfq"是后复权。做布林带这类均线指标,我强烈建议用前复权数据,因为分红送股会导致价格跳空,不复权数据会让布林带失真。但是前复权数据有个特点:历史价格会随着每次新的分红而整体变化,所以如果你更新到最新数据,去年同一交易日的收盘价可能跟几个月前拉到的不一样。做研究没问题,但千万别把前复权数据当成“历史事实”存库,否则每次都得全量刷新。
第二个是停牌和缺失值。A 股停牌很常见,akshare 对这种日期通常直接不返回那一行,而不是返回 NaN。在布林策略里,停牌天数多的话,你会看到连续 N 天没有数据,复牌第一天价格直接跳变。处理方法是只按有数据的日期计算指标,不要强行按自然日重采样填充,否则会把停牌期间的价格当成上一个交易日价格,均线算出来全是错的。
第三个是数据更新延迟。stock_zh_a_hist在收盘后一般需要等一段时间,交易所数据才会通过财经网站同步过来。如果你盘中跑策略拿到的“今日收盘价”其实是昨天的,因为你调接口的时候当天的日线还没生成。这一点在实盘信号上特别致命——技术指标用到了未来数据而不自知,回测结果虚高得离谱。我的经验:做日线级别信号,只在收盘后 17:00 之后再拉数据计算信号,严格执行“当日数据不落地,次日再交易”的纪律,这条规则能帮你避开绝大多数未来函数问题。
3. 布林通道策略核心逻辑与代码实现
3.1 布林带的数学原理和参数理解
布林通道由约翰·布林格在 1980 年代提出,核心思想是用统计区间来度量价格的“高低”。它由三条线组成:
- 中轨(Middle Band):一般是 N 日收盘价的简单移动平均
- 上轨(Upper Band):中轨 + K 倍标准差
- 下轨(Lower Band):中轨 - K 倍标准差
标准参数是 N=20、K=2。你别小看这两个数字,它们不是随便拍的。20 日大致对应一个自然月的交易日数量,2 倍标准差在正态分布假设下对应约 95% 的置信区间,意味着价格在上下轨之间波动的概率约为 95%,超出轨道的概率只有 5% 左右。换句话说,价格冲出上轨或跌破下轨,在统计意义上属于“极端事件”,而这正是均值回归策略出手的时机。
Python 里用 pandas 算布林带非常方便,核心就两行:
df["mid"] = df["close"].rolling(window=20).mean() df["std"] = df["close"].rolling(window=20).std(ddof=0) df["upper"] = df["mid"] + 2 * df["std"] df["lower"] = df["mid"] - 2 * df["std"]注意这里的ddof=0,pandas 的std默认是ddof=1,也就是样本标准差。布林格的原始定义用的是总体标准差(ddof=0)。这个差异在样本量大的时候几乎可以忽略,但在 20 日窗口这种小样本下还是有一点点影响,为了严格贴合经典定义,建议显式指定ddof=0。
布林带还有一个衍生指标叫“带宽”(Bandwidth),公式是(上轨 - 下轨) / 中轨。它反映的是波动率水平,带宽收窄到历史低位往往意味着变盘在即,这可以作为仓位的参考。我会在后面的策略里用到带宽来调节仓位比例。
3.2 交易信号的设计逻辑
布林带策略最经典的信号规则是:
- 当收盘价从下方向上穿越下轨时,产生买入信号
- 当收盘价从上方向下穿越上轨时,产生卖出信号
但如果你只按这两条做,严格来说是一个“通道突破反向”的策略。更常用、更稳的改进版是加入中轨作为回归目标:
- 持有阶段:价格跌破下轨时加仓
- 回归阶段:价格从中轨下方上穿中轨时减仓或者清仓
我把策略明确成三个动作:
- 建仓信号:
close < lower且之前一个交易日close >= lower,也就是收盘价第一次跌破下轨。此时入场。 - 加仓信号:持有状态下,
close再次跌破下轨,而且比上一次跌破更低的低点,继续加一笔。 - 离场信号:持有状态下,
close > mid,也就是价格回到中轨上方。说明这次超跌已经修复到均值附近,落袋为安。
至于上轨,很多初学者会自然想到“碰到上轨就做空”,但在 A 股没有做空工具的前提下(融券成本高、门槛高),我建议把它当作风向标而非交易信号。如果你在做期货或者数字货币,可以把同样的逻辑反向用于上轨,做空逻辑完全对称。
我自己还加了一个“趋势过滤器”,避免在单边下跌里反复接飞刀。过滤器的逻辑很简单:用 60 日均线判断大趋势,只有当 20 日均线在 60 日均线上方(短期趋势没坏)或者 60 日均线走平时才允许做多;如果 60 日均线明显向下,布林下轨的买入信号全部跳过。这个改动会牺牲掉一部分“精准抄底”的机会,但能把最大回撤削掉一大截。策略追求的是长期活得久,而不是短期赚得多。
3.3 基于 akshare 的完整策略代码
下面贴一份我实际用过的精简版代码。它不依赖任何回测框架,自己用 pandas 算信号,然后模拟逐日持仓。这样最直观,也最容易理解每个步骤在干什么。
import akshare as ak import pandas as pd import numpy as np # ---------- 参数配置 ---------- SYMBOL = "600519" START_DATE = "20200101" END_DATE = "20231231" N = 20 # 布林带窗口 K = 2.0 # 标准差倍数 TREND_N = 60 # 趋势过滤均线 # ---------- 1. 获取数据 ---------- df = ak.stock_zh_a_hist( symbol=SYMBOL, period="daily", start_date=START_DATE, end_date=END_DATE, adjust="qfq" ) df = df.rename(columns={ "日期": "date", "开盘": "open", "收盘": "close", "最高": "high", "最低": "low", "成交量": "volume" }) df["date"] = pd.to_datetime(df["date"]) df = df.set_index("date").sort_index() df["close"] = pd.to_numeric(df["close"], errors="coerce") df = df.dropna(subset=["close"]) # ---------- 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"] df["trend"] = df["close"].rolling(TREND_N).mean() # ---------- 3. 生成信号 ---------- df["position"] = 0 # 0空仓 1持仓 df["entry"] = False df["exit"] = False holding = False for i in range(N, len(df)): today = df.iloc[i] yesterday_close = df.iloc[i - 1]["close"] today_close = today["close"] # 突破下轨:发生一次由下轨上方到下方的穿越 cross_lower_down = (today_close < today["lower"]) and (yesterday_close >= df.iloc[i - 1]["lower"]) # 突破中轨:从下方上穿中轨 cross_mid_up = (today_close > today["mid"]) and (yesterday_close <= df.iloc[i - 1]["mid"]) # 趋势过滤:60日均线不明显向下(今日均线 >= 昨日均线) trend_ok = today["trend"] >= df.iloc[i - 1]["trend"] if not holding: if cross_lower_down and trend_ok: holding = True df.loc[df.index[i], "entry"] = True df.loc[df.index[i], "position"] = 1 else: if cross_mid_up: holding = False df.loc[df.index[i], "exit"] = True df.loc[df.index[i], "position"] = 0 else: df.loc[df.index[i], "position"] = 1 # ---------- 4. 输出信号日期 ---------- entries = df[df["entry"]].index exits = df[df["exit"]].index print("买入信号:") print(entries) print("卖出信号:") print(exits)这段代码的逻辑非常直白:逐行遍历,判断是否发生“下轨向下的穿越”和“中轨向上的穿越”,再叠加趋势过滤条件。输出结果是一串买入和卖出的日期,你可以自己对着 K 线图数一数,每一个信号是不是都落在合理的位置上。
3.4 信号后处理和 A 股交易规则适配
理论上信号算完就结束了,但真实交易不是这样的。代码输出“今天买入”信号,你真能在今天买入吗?不行,因为你看到这个信号的时候大概率已经收盘了,A 股 T+1 制度下你最早也只能明天开盘买。所以策略代码里所有信号都必须向后平移一天执行,专业的叫法是“信号滞后处理”。
我把这个落实到代码里:信号发生在第 T 天收盘后,真正执行买卖在第 T+1 天开盘价。上一节代码里entry和exit标记的是信号发生日,如果你要算真实持仓收益,得把position整体向后移动一位:
df["signal"] = df["entry"].astype(int) - df["exit"].astype(int) df["trade"] = df["signal"].shift(1).fillna(0).cumsum()这里的trade列才是按实际持仓状态算出来的仓位变化:第 T 天夜里收到信号,第 T+1 天仓位才变。做回测的时候一定要用trade而不是原始的entry/exit,否则会有未来函数,回测收益虚高得让人误以为自己是巴菲特。
还有一个操作层面的坑:A 股涨停、跌停的时候,你挂单可能根本成交不了。布林下轨抄底策略在极端行情下经常碰到“信号来了但买不进”的情况。我自己会加一个过滤条件:如果当天收盘价触及跌停(价格约等于昨日收盘价 * 0.9)或者涨停,则跳过这一笔交易。这个细节虽然在普通回测里体现不出来,但在实盘上非常关键,不然你的回测跟实盘之间永远有一道“成交不了”的鸿沟。
4. 回测验证与参数优化
4.1 回测逻辑怎么搭才靠谱
很多人回测就是算一下“这个策略这几年赚了百分多少”,然后就兴冲冲上实盘了,这个习惯非常危险。回测的核心目的不是让你看收益率,而是让你理解这个策略在什么市场环境下赚钱、什么环境下亏钱。
我这里的回测逻辑继续用纯 pandas 实现,不引入回测框架:
# 假设初始资金 100 万,全仓进出 df["daily_ret"] = df["close"].pct_change() df["strategy_ret"] = df["trade"] * df["daily_ret"] df["nav"] = (1 + df["strategy_ret"]).cumsum() # 统计关键指标 total_return = df["nav"].iloc[-1] - 1 sharpe = df["strategy_ret"].mean() / df["strategy_ret"].std() * np.sqrt(252) max_drawdown = (df["nav"] / df["nav"].cummax() - 1).min() print(f"总收益率: {total_return:.2%}") print(f"夏普比率: {sharpe:.2f}") print(f"最大回撤: {max_drawdown:.2%}")这一段基本逻辑是:每天按持仓状态乘以当日收益率,累乘成净值曲线。手续费和印花税先不扣,但看交易次数自己估算一个数就行。A 股印花税卖出千分之一,佣金双边万二左右,对日线级别的策略来说影响不大,如果策略交易频率特别高,再把费用按每笔交易从收益里扣掉。
我记得第一次跑贵州茅台 2020–2023 年这段区间,这套策略的交易次数大概只有二十几次,一年六到八次换手,手续费几乎可以忽略。这个交易频率对个人投资者非常友好,不用盯盘,不用频繁操作,一个月检查一次信号就好。
4.2 参数敏感性测试才是策略的核心验证
布林带参数N=20, K=2是经典默认值,但“经典”不等于“适合所有品种”。不同股票的波动特性差别很大:波动率高的题材股,2 倍标准差经常被突破;波动率低的银行股,价格很少触及上下轨。所以实盘之前,一定要做参数敏感性测试。
我习惯把 N 从 10 试到 60,K 从 1.5 试到 3.0,做一个简单的网格搜索:
results = [] for n in range(10, 61, 5): for k in range(15, 31): k = k / 10.0 # 计算布林带、信号、回测指标 # 把 total_return, max_drawdown, win_rate 存到 results pass df_results = pd.DataFrame(results) df_results.sort_values("sharpe", ascending=False).head(10)参数敏感性测试有几个需要关注的信号,很多人不知道:
第一,如果某个参数组合收益特别好,但相邻参数收益断崖式下跌,那说明策略对参数极度敏感,实盘里这个“最优参数”大概率是过拟合出来的,不能用。真正稳健的策略应该是:附近一整个参数区间内收益都是正的,而且差别不大,也就是说策略赚钱靠的是逻辑,而不是靠你调出来的某个特殊参数。
第二,观察布林带的带宽变化。如果某个股票长期处于布林带收窄的状态,说明波动率低,策略会长期没有交易信号,资金利用率很差。这时候需要换标的,或者降低 K 值让触发条件更“宽松”。
第三,K 值的含义其实是“止损阈值”。K=1.5 意味着价格稍微偏离均值就触发信号,交易次数多、胜率可能低;K=3.0 意味着非要等极端行情才出手,交易次数少、每笔赚得可能多但等待期很长。你要根据自己的资金规模和耐心来选,这无关对错,只关乎适配。
4.3 一次真实的回测结果复盘
我拿一个中盘股来举例子(具体名字就不说了,避免荐股嫌疑),2021–2023 年数据,初始参数 N=20, K=2,趋势过滤器开 60 日均线。
不扣手续费的回测结果是:总收益率 46%,最大回撤 11%,夏普 1.2,交易次数 38 次。看起来还行对吧?但我第一次跑完其实差点被骗了——我发现其中有几笔交易都是在信号出现后第二天大幅高开买入,收益虚高。排查下去发现,我没有做信号滞后处理,直接用了当天收盘价的信号当天成交,也就是说偷跑了未来数据。
把信号 shift(1) 之后重跑,总收益率直接从 46% 掉到 34%,最大回撤也从 11% 扩大到 14%。这个差距就是未来函数的“水分”。所以我特别强调一点:回测真正难的不是指标计算,而是你能否诚实地模拟真实交易的每一个限制条件。
另外,在 2022 年那种单边下跌行情里,这套策略明显吃亏。趋势过滤器拦住了部分下轨买入信号,但 60 日均线的过滤条件还是偏弱,有几笔买入抄在了半山腰。后面我把过滤器改成“20 日均线和 60 日均线同时走平或者向上”,回撤又压缩了三个百分点。这个改进不复杂,但需要你先跑出来,看到痛点了再去优化,而不是一上来就堆条件。
5. 常见问题与排查技巧实录
5.1 akshare 接口层面的高频坑
akshare 每天都在更新,接口报错是新手遇到最多的问题。我自己整理的排查清单如下:
| 报错现象 | 可能原因 | 解决方案 |
|---|---|---|
KeyError: '日期' | 列名是英文或者接口返回结构变了 | 先print(df.columns)看真实列名 |
HTTP 403或频繁被限制 | 短时间大量调用被目标网站风控 | 加time.sleep(1)或升级代理池 |
| 返回的 DataFrame 为空 | 股票代码格式不对或者停牌/退市 | 确认股票代码是 6 位字符串,不带上交所深交所前缀 |
| 数据只到 2020 年 | 你用了国际标准时间导致日期偏移 | 检查本地时间和请求参数的时区问题 |
特别是数据为空这个坑,我见过很多人卡一整天。stock_zh_a_hist的 symbol 参数只接收 6 位纯数字代码,比如600519,你要是传成SH600519它就查不到。另外,新上市不足 60 个交易日的股票,布林带算不出来,因为前 20 天窗口和 60 天趋势窗口都填不满,直接 dropna 之后就没有有效数据了。
还有一个比较隐蔽的问题:akshare 接口偶尔会“静默失败”,也就是不报错、返回一个空列表,但代码继续往下跑,你后面的计算全是用空表做的,最后结果全是 NaN。我在代码里加了校验:
if len(df) == 0: raise ValueError("akshare 返回空数据,请检查代码或稍后重试")这种防御性编程在策略里非常重要,因为量化交易是“数据错了比不跑更可怕”的领域——数据静默错误会让你以为策略赚钱,实盘时直接亏穿。
5.2 布林带计算与信号实现上的常见错误
聊几个写布林带代码时特别容易犯的错。
第一个是用错标准差类型。pandas 的rolling.std()默认ddof=1,前面提过,20 日窗口对ddof的差异虽然不致命,但会和主流行情软件(同花顺、通达信)显示的布林带数值对不上。你如果发现自己算出来的布林带跟证券软件显示的不一致,先查这个。
第二个是没处理 NaN。布林带需要前 20 天数据作为窗口,所以前 20 行的upper/lower都是 NaN。在遍历生成信号的时候,如果不从N开始而是从 0 开始,你就会拿 NaN 去比价,结果要么报错要么全是 False。我们通常从range(N, len(df))开始遍历,并且用df.iloc[i - 1]["lower"]取前一天的lower时要确认它不是 NaN。
第三个是混淆了“价格穿越下轨”和“当前价格在下轨下方”。前者是策略常识里的“突破/穿越”信号,后者更像是“状态”。close < lower跟昨天比是刚发生的突破,跟昨天比前 100 天都已经在下轨下方,那这个信号在策略里的含义完全不一样,如果你只是用close < lower来触发买入,会频繁买入、持有时间特别长,逻辑上偏离了均值回归的初衷。
5.3 策略实战中的现实问题
最后聊几个回测里永远不会告诉你、但实盘一定会遇到的事。
关于“信号那么多,每笔都上吗”:我建议加一个筛选规则,比如只在布林带带宽压缩到 6 个月最低的股票上做下轨买入。带宽压缩意味着变盘临近,这时候突破下轨往往是最有肉的一段。普通状态的触碰下轨,可能只是波动,不是机会。
关于“止损怎么设置”:布林通道策略没有天然止损位,因为它的逻辑是越跌越买、等待均值回归。但如果跌破下轨后继续大跌,说明基本面或者市场情绪出了问题,均值回归可能轻易不来。我会在入场后设置一个硬止损:入场价往下 8%。这个数字不是公式推出来的,而是我对 A 股单日极端波动的经验值,超过 8% 大概率是趋势性下跌而非随机波动,不值得去接飞刀。
关于“多只股票怎么组合”:单票策略的优缺点都很突出,赚的时候爽,亏的时候更爽。我的建议是至少选 8–10 只不相关的股票来跑同一套策略,每只分配的仓位控制在 10% 左右。注意“不相关”很关键——都是白酒和都是新能源,分散效果接近于零,真来一轮系统性调整全给你同时打到下轨,然后一起继续跌。组合层面,哪怕策略胜率只有 40%,分散到十几只票上,资金曲线依然能保持平滑,这才是均值回归策略正确的打开方式。
最后再说一个关于实盘的小技巧。收盘后跑完策略生成信号,别急着第二天开盘无脑买入,看一眼集合竞价的情况:如果强势高开超过 3%,说明市场已经抢跑了这笔交易,买入性价比很低,我通常直接放弃这次信号。这不是策略本身能处理的事,但这是每个实盘交易者应该养成的习惯。