ボリンジャーバンド(Bollinger Bands)は、移動平均の周りに「標準偏差で広がるバンド」を描いて値動きの相対位置を見るテクニカル指標です。本記事では、計算式・pandas 実装・可視化、そして「逆張り」「順張り」どちらの解釈で使うかの整理を行います。
目次
- バンドの定義
- サンプルデータの準備
- ボリンジャーバンドを実装する
- バンドを描画する
- 逆張りと順張りの 2 つの解釈
- バンドウォークを観察する
- バンド幅(squeeze)の検出
バンドの定義
期間 と倍率 を決め、次の 3 本を引きます。
慣例的に (N, k) = (20, 2) が広く使われます。標準偏差は 直近 N 日の終値 から計算するのが基本形です。
サンプルデータの準備
import numpy as npimport pandas as pd
rng = np.random.default_rng(seed=21)n = 240returns = rng.normal(loc=0.0005, scale=0.012, size=n)close = pd.Series( 1800 * np.exp(np.cumsum(returns)), index=pd.date_range("2025-09-15", periods=n, freq="B"), name="C",)ボリンジャーバンドを実装する
def bollinger_bands( close: pd.Series, window: int = 20, k: float = 2.0,) -> pd.DataFrame: """中央線・上下バンド・%B・バンド幅を返す。""" mid = close.rolling(window).mean() sd = close.rolling(window).std(ddof=0) # 母標準偏差(分母 N) upper = mid + k * sd lower = mid - k * sd pct_b = (close - lower) / (upper - lower).replace(0, np.nan) band_width = (upper - lower) / mid
return pd.DataFrame({ "mid": mid, "upper": upper, "lower": lower, "pct_b": pct_b, "band_width": band_width, })
bb = bollinger_bands(close, window=20, k=2.0)print(bb.tail())%B(パーセント B)はバンド内での価格の相対位置を 0 〜 1 で表す指標です。band_width はバンドの広さを中央線で正規化したもので、ボラティリティの代理指標として使えます。
std(ddof=0) か std(ddof=1) かは慣行が分かれます。TA-Lib は ddof=0(N で割る)、pandas のデフォルトは ddof=1(N-1 で割る) です。本記事は TA-Lib との一致を優先して ddof=0 を使います。
バンドを描画する
中央線・上下バンドを描き、バンド内を半透明で塗ります。
import matplotlib.pyplot as plt
df = close.to_frame().join(bb)
fig, ax = plt.subplots(figsize=(11, 5))ax.plot(df.index, df["C"], color="black", linewidth=0.8, label="C")ax.plot(df.index, df["mid"], color="tab:blue", linewidth=1.0, label="SMA(20)")ax.plot(df.index, df["upper"], color="tab:orange", linewidth=0.8, linestyle="--", label="+2σ")ax.plot(df.index, df["lower"], color="tab:orange", linewidth=0.8, linestyle="--", label="-2σ")ax.fill_between(df.index, df["lower"], df["upper"], color="tab:orange", alpha=0.08)
ax.set_title("Bollinger Bands (20, 2)")ax.set_xlabel("Date")ax.set_ylabel("Price")ax.legend()ax.grid(alpha=0.3)plt.tight_layout()plt.savefig("bollinger.png", dpi=120)plt.close(fig)
塗りつぶしを alpha=0.08 程度に薄くしておくと、ローソク足や別の線を重ねても視認性が保てます。
逆張りと順張りの 2 つの解釈
ボリンジャーバンドは、逆張り と 順張り のどちらにも引用される、ややこしい指標です。
| 解釈 | 想定 | シグナル例 |
|---|---|---|
| 逆張り | バンドは「平均回帰」する範囲 | 上限タッチ → 売り、下限タッチ → 買い |
| 順張り | バンド外への突き抜けは「強いトレンド」 | 上限突破 → 買い継続、下限突破 → 売り継続 |
考案者の Bollinger 自身は、「バンドの幅を見る指標」 として位置づけており、バンドタッチ単独で売買を決めるのは推奨していない と明言しています。
実務的には次のような使い方が見られます。
- バンドウォーク: 強いトレンドではバンド外側に張り付くことが続く → 単純な逆張りはほぼ機能しない
- スクイーズ: バンド幅が極端に狭くなった後の拡大は、トレンド発生の前触れとされる
- % B の組み合わせ: 0.5 を超えたら強気、下回ったら弱気の中立目安に使う
バンドウォークを観察する
「上限を超えた日」が連続するかを集計します。
df["above_upper"] = df["C"] > df["upper"]df["walk_len"] = ( df["above_upper"] .groupby((~df["above_upper"]).cumsum()) .cumsum())print(df["walk_len"].max(), "days continuous walk")連続日数が長い区間が見つかれば、その期間は 強いトレンドが出ていた と読みます。逆張り戦略を組むなら、こうした区間を避ける判定が必要になります。
バンド幅(squeeze)の検出
バンド幅が直近の最低水準にあるとき、スクイーズ(squeeze)と呼びます。
df["bw_min_60"] = df["band_width"].rolling(60).min()df["squeeze"] = df["band_width"] <= df["bw_min_60"]print(df.loc[df["squeeze"], "band_width"].tail())スクイーズの後で発生したブレイクアウトは、ノイズによるダマシも含まれる点を承知のうえで観察対象にします。
注意点
- 正規分布の前提は成立しないことが多い: 「2σ 内に 95%」というのはあくまで正規分布の話。株価リターンは裾が厚く、超える頻度は理論より高い
- 窓 N の選び方: 20 は慣例。短くすると敏感、長くすると鈍くなる
- 絶対水準では比較できない: 銘柄ごとに価格・ボラティリティが違うため、
band_widthを銘柄横断で比較するときは平均で正規化するなどの工夫が必要 - 株式分割・配当: 調整済み終値を使わないと、分割の日にバンドが暴れる
生成AI へのプロンプト例
ロング形式 DataFrame に対し、Code ごとにバンドを計算する関数を依頼します。
入力 DataFrame:- columns: Date (datetime64), Code (str), C (float)
Code ごとにボリンジャーバンド(N=20, k=2.0) を計算し、- bb_mid, bb_upper, bb_lower, pct_b, band_widthの 5 列を追加した DataFrame を返す関数 add_bollinger(df) を書いてください。
要件:- pandas 2.2 系- Code ごとに日付昇順に整列してから計算- 標準偏差は ddof=0(母標準偏差)- 各 Code の冒頭 19 日は NaN を許容- 末尾に動作確認のサンプルコードを付けるまとめ
- ボリンジャーバンドは「移動平均 ± k×標準偏差」で作る帯
- 慣例パラメータは (20, 2)。
std(ddof=0)で TA-Lib と整合する - バンドタッチを単独でシグナルにせず、% B やバンド幅と合わせて使う
- 逆張りと順張りどちらの解釈もある。強トレンドではバンドウォーク が起きる
- 実装は
rolling(N).mean()とrolling(N).std(ddof=0)の組み合わせで完結する