第 4 章 · 02 Bollinger Bands:W 底形态识别


文档摘要

第 4 章 · 02 Bollinger Bands:W 底形态识别 本节摘要:本节精读 Bollinger Bands 形态识别——一种纯算术的双底(W 底)抓取策略。布林带本身很简单,就是 20 日 SMA ± 2 倍移动标准差;但形态识别是另一回事:可视化对人类很容易,对机器却很难。本节的看点不在公式,而在 那段 4 层嵌套循环——它用 75 日窗口回溯找 W 形的 5 个节点 l-k-j-m-i(左上 / 左下 / 中 / 右下 / 右上),用 alpha=0.0001 的容差判定"贴近",用 beta=0.0001 的带宽阈值判定"收缩清仓"。作者甩出一句宣言:"为什么用机器学习,当算术更快更简单?" 这是本节的哲学底色。

第 4 章 · 02 Bollinger Bands:W 底形态识别

本节摘要:本节精读 Bollinger Bands 形态识别——一种纯算术的双底(W 底)抓取策略。布林带本身很简单,就是 20 日 SMA ± 2 倍移动标准差;但形态识别是另一回事:可视化对人类很容易,对机器却很难。本节的看点不在公式,而在 signal_generation 那段 4 层嵌套循环——它用 75 日窗口回溯找 W 形的 5 个节点 l-k-j-m-i(左上 / 左下 / 中 / 右下 / 右上),用 alpha=0.0001 的容差判定"贴近",用 beta=0.0001 的带宽阈值判定"收缩清仓"。作者甩出一句宣言:**"为什么用机器学习,当算术更快更简单?"** 这是本节的哲学底色。

涉及源码:原项目 Bollinger Bands Pattern Recognition backtest.py(约 237 行),数据 gbpusd.csv(histdata.com 1 分钟 bid/ask 平均值)。

⚠️ 注意:本节策略的核心难点是 5 个节点的查找顺序——迭代点是"右上节点 i",却要从 i 往回找 j、k、l、m。务必先在纸上画一个 W 形并标好 5 个节点的位置,再看代码,否则 4 层 for 循环会把人绕晕。

学习目标

阅读完本节,你应当能够:

  1. 说清布林带的三条线(中带 / 上带 / 下带)怎么算,以及 ±2σ 的统计学意义。
  2. 列举布林带的三大用途:波动率交易(带宽收缩扩张)、动量交易(walking the band)、形态识别(W 底 / M 顶 / 头肩)。
  3. 还原 W 底 5 个节点 l-k-j-m-i 的查找顺序与各自的条件。
  4. 解释 alpha=0.0001 与 beta=0.0001 两个容差/阈值的作用差异。
  5. 讲清楚"带宽收缩即清仓"的动量逻辑。

金融/数学原理

布林带:三线公式

布林带是一个非常朴素的指标——就是移动平均加上移动标准差。bollinger_bands 函数(L39-47)三行就能讲完:

data['std'] = data['price'].rolling(window=20, min_periods=20).std() data['mid band'] = data['price'].rolling(window=20, min_periods=20).mean() data['upper band'] = data['mid band'] + 2 * data['std'] data['lower band'] = data['mid band'] - 2 * data['std']

中带是 20 日简单移动平均(SMA),上下带各加减 2 倍移动标准差。2σ 在正态分布下覆盖约 95% 的样本,所以价格"正常"情况下应该落在带内,触带或破带就是异常信号。窗口 20 是惯例(约一个月交易日),min_periods=20 保证前 19 天是 NaN 而非瞎算。

布林带的三大用途

作者在注释里点出布林带的三种用法:

  1. 波动率交易:看带宽(bandwidth,即上下带之差)的收缩与扩张。价格盘整时带宽收窄,大行情来临时带宽急剧扩张。这种"收缩后必扩张"的规律是波动率交易的基础。
  2. 动量交易(walking the band):价格贴着上带连续走(或贴着下带),叫"走带"。作者个人觉得"走带"策略进场太晚——"after confirmation of several breakthroughs, maybe its good for stop and reverse",适合做止损反手,不适合顺势入场。本策略不用这个。
  3. 形态识别:在带上识别经典图表形态——W 底(双底)、M 顶(双顶)、头肩顶、头肩底、Elliott 浪。本节只做 W 底,因为"M 顶就是 W 底的反转"。

为什么用算术而非机器学习

作者在文件开头甩出一句近乎挑衅的宣言:

"when we talk about pattern recognition these days, people always respond with machine learning. why machine learning when u can use arithmetic approach which is much faster and simpler?"

这是一个重要的方法论立场:形态识别不一定要上 CNN/LSTM,把"贴近下带""带宽收缩"这些人眼识别 W 底的判据翻译成 alpha 容差判断,就足以让机器跑出可解释、可调参、计算极快的策略。本节的 5 节点查找就是这套"算术形态学"的范例。

W 底的 5 个节点

W 形从左到右、从上到下有 5 个节点,作者命名为 l-k-j-m-i(注意不是字母顺序):

l i ← 第一顶(左上) 右上(突破上带) \ / k j m ← 第一底(左下) 中 第二底(右下) \ / \ / \_/ \___/
  • i(右上):迭代点,价格突破上带——这是 W 形完成的确认信号。
  • j(中):W 的中间峰,贴近中带。
  • k(第一底):左边的底,贴近下带。
  • m(第二底):右边的底,贴近下带,且价格低于 k(threshold)——这是"双底不破前低"的判据。
  • l(第一顶):仅供画图用,不参与信号判定(作者明说"l is not any of those four conditions, we just use it for pattern visualization")。

investopedia 建议:双底形态应用 3 个月视野,即 75 个交易日作为回溯窗口(period=75)。

算法与代码精读

signal_generation 的 4 层嵌套

Bollinger Bands Pattern Recognition backtest.py:67-169signal_generation 是全策略的灵魂。两个超参:

alpha = 0.0001 # 价格与布林带的贴近容差 beta = 0.0001 # 带宽收缩的清仓阈值

外层循环从 i = period(75)开始,每个 i 是一个潜在的"右上节点"。查找顺序是反直觉的——从 i 往回找,先找 j 再找 k 再找 l 最后找 m:

第 0 层 触发条件(L110-111):i 点价格必须突破上带,且当前无持仓:

if (df['price'][i] > df['upper band'][i]) and (df['cumsum'][i] == 0):

第 1 层 找中节点 j(L113-119):从 i 往回扫,找一个价格贴近中带、且中带贴近上带(在 i 处)的点:

for j in range(i, i-period, -1): if (np.abs(df['mid band'][j] - df['price'][j]) < alpha) and \ (np.abs(df['mid band'][j] - df['upper band'][i]) < alpha): moveon = True; break

两个条件:价格在中带附近(中节点特征),且当时的中带水平与 i 处的上带差不多(说明 W 的"高度"合理)。

第 2 层 找第一底 k(L121-129):从 j 继续往回,找一个贴近下带的点,记下它的价格作为 threshold:

for k in range(j, i-period, -1): if np.abs(df['lower band'][k] - df['price'][k]) < alpha: threshold = df['price'][k] moveon = True; break

第 3 层 找第一顶 l(仅供画图,L131-138):从 k 往回,找一个价格高于中带的点(画 W 的左上角):

for l in range(k, i-period, -1): if df['mid band'][l] < df['price'][l]: moveon = True; break

第 4 层 找第二底 m(L140-152):这次从 i 往 j 方向(向左小范围)扫,找一个贴近下带、低于下带上方、且**价格低于 threshold(k 的价格)**的点:

for m in range(i, j, -1): if (df['price'][m] - df['lower band'][m] < alpha) and \ (df['price'][m] > df['lower band'][m]) and \ (df['price'][m] < threshold): df.at[i, 'signals'] = 1 df.at[i, 'coordinates'] = '%s,%s,%s,%s,%s' % (l, k, j, m, i) df['cumsum'] = df['signals'].cumsum() moveon = True; break

5 个节点全部凑齐才算一个有效 W 底,在 i 处发出 signals=1(做多),并把 5 个节点的行号存进 coordinates 字符串列(画图用)。

收缩即清仓

最外层循环还有一个退出分支(L163-167):

if (df['cumsum'][i] != 0) and (df['std'][i] < beta) and (moveon == False): df.at[i, 'signals'] = -1 df['cumsum'] = df['signals'].cumsum()

带宽(移动标准差)小于 beta=0.0001 时清仓。逻辑:价格有动量时,标准差会急剧放大;动量消失时,价格回到盘整,标准差收敛。一旦 std 跌破 beta,说明"动量没了",持仓已无意义,立刻平仓。

注意 moveon == False 这个条件:作者注释说"just in case our signal generation time is contraction period, but we dont wanna clear positions right now"——如果这一轮 i 刚好触发了入场(moveon=True),就不清仓,避免同一天既入场又离场的矛盾。

moveon:流程控制开关

4 层查找共用一个布尔开关 moveon:每层进入前重置为 False,只有上一层的 break 把它点亮 True,下一层才会执行。任何一层没找到节点,moveon 保持 False,后续层全部跳过,这个 i 就不产生信号。这是用"开关链"实现的短路逻辑,比 4 层嵌套 if 紧凑得多。

图: 布林带W底

上图是 W 底形态的可视化:蓝色价格线、青绿色带、虚线中带。绿色三角是入场点(右上节点 i,突破上带),红色倒三角是离场点(带宽收缩)。红色粗折线把 5 个节点 l-k-j-m-i 连起来,画出 W 形。"Expansion"标注在入场处(带宽扩张确认动量),"Contraction"标注在离场处(带宽收缩确认动量消失)。

图: 布林带持仓

第二张图展示了持仓随时间的演化,可以直观看到入场与离场的节奏。

🎯 反直觉发现:本节没有 Pair Trading 那种戏剧性的"关系破裂"反转,但有一个更朴素的反直觉——形态识别可以完全不靠机器学习。当大家一窝蜂上 CNN/LSTM 识别 K 线形态时,作者用 4 层 for 循环 + alpha=0.0001 容差就把 W 底抓出来了,而且每一步都可解释、可调参、计算复杂度极低。这提醒我们:在"算术够用"的场合,机器学习往往是过度工程。"为什么用机器学习,当算术更快更简单?"——这句宣言值得每一个量化新人贴在显示器上。

关键技巧

  1. 5 节点查找顺序反直觉:迭代点 i(右上)是入口,却要往回找 j→k→l→m,且 l 只供画图不参与判定。务必画图辅助理解。
  2. moveon 开关链实现短路:4 层查找共用一个布尔,层层接力,任一层失败则后续全跳过,代码紧凑。
  3. alpha=0.0001 是"贴近"容差:np.abs(price - band) < alpha 判定价格是否"贴在带上",太小不触发,太大误触发。
  4. beta=0.0001 是带宽清仓阈值:std < beta 判定动量消失,与 alpha 语义完全不同,别混用。
  5. coordinates 字符串列存 5 节点:'%s,%s,%s,%s,%s' % (l,k,j,m,i),画图时 split 再映射回日期索引,画出 W 形折线。
  6. cumsum 既是持仓也是防重复:cumsum[i]==0 保证只在空仓时入场,避免重复开仓。
  7. moveon==False 守护收缩清仓:防止"入场与离场撞同一天"的矛盾,体现作者对边界条件的细致处理。
  8. W 底只做多,不做空:本策略只抓 W 底(看涨反转),M 顶是其镜像,作者建议自行扩展。

本节要点

  1. 布林带 = 20 日 SMA ± 2 倍移动标准差;2σ 覆盖正态分布约 95% 样本。
  2. 三大用途:波动率交易(带宽收缩扩张)、动量交易(走带)、形态识别(W 底 / M 顶 / 头肩)。本节只做 W 底。
  3. W 底 5 节点 l-k-j-m-i:右上 i(入口,突破上带)、中 j(贴中带)、左下 k(贴下带,记 threshold)、左上 l(仅供画图)、右下 m(贴下带且低于 threshold)。
  4. 查找顺序反直觉:从 i 往回找 j→k→l,再从 i 往 j 找 m,用 moveon 开关链短路。
  5. alpha=0.0001 判"贴近",beta=0.0001 判"收缩清仓"——两个超参语义不同。
  6. 方法论高潮:"为什么用机器学习,当算术更快更简单?"——算术形态学可解释、可调参、计算极快。

下一节,我们看另一个形态识别——RSI 头肩顶。同样是"找节点",但这次找的是 7 个节点,而且作者对 RSI 这个指标本身毫不客气:"我其实觉得 RSI 有点扯(bs)"。更有趣的是 RSI 发明人 Wilder 与其门徒 Cardwell 在"熊背离"上互相矛盾的公案。


作者与出处
原作者: 灏天文库
整理: 灏天文库整理
本站整理收录,版权归原作者/开源协议所有;欢迎通过原文链接访问源仓库。
发布者: 作者: 灏天文库 转发
评论区 (0)
U