ローソク足が、ボリンジャーバンドの外側に沿って、
連続で進んでいく「バンドウォーク」という現象があります。
これは、トレンドが続きやすい強いサインなのですが、
チャートを目で追い続けて気づくのは、意外と大変です。
この記事では、終値が何本連続でバンドの外側にあるかを判定し、
バンドウォークが確定した瞬間だけに矢印を表示するインジケーターを作ります。
・バンドウォークの定義と、スクイーズ(収縮)との関係
・iBands関数のmode引数(中央線・上部バンド・下部バンド)の使い方
・「状態」ではなく「連続N本」という変化を検知する設計
・実際に動くインジケーターのコード全体
バンドウォークとは何か
まず、矢印の元になる「バンドウォーク」という現象の定義を確認します。
ボリンジャーバンドの基本(期間とσ)
ボリンジャーバンドは、移動平均線(中央線)を基準に、
価格のばらつき(標準偏差)の分だけ上下に幅を取った線を表示します。
標準偏差を何倍にするかが「σ(シグマ)」で、
このインジケーターでは一般的な2.0を初期値にしています。
バンドウォークの定義(連続何本からか)
終値が、上部バンド(または下部バンド)の外側に、
3本以上連続で留まり続けている状態を、バンドウォークと呼びます。
・バンドの中に収まる値動きが約95%(σ2.0の場合)という前提がある
・その「約95%」の外に何本も連続で居座るのは、通常より強い力が働いている証拠
・本数の基準(3本)に厳密な公式定義はない
・本記事では入力パラメーターで、この本数を変更できるようにする
スクイーズ(収縮)との関係、既刊「バンドの傾き」記事との役割分担
バンドの幅が一度狭くなった後(スクイーズ)に、
バンドウォークが発生しやすい傾向があります。
このインジケーター単体ではスクイーズの判定はしていないため、
バンド幅を監視したい場合は、
既刊bollinger-band-histogram.mdと併用してください。
既刊記事は、上下バンドが同じ方向に「傾いているか」を見て、
上昇・下降・レンジをヒストグラムで表示する設計でした。
本記事は、傾きではなく「終値が連続何本、バンドの外側にあるか」という、
別の数え方でトレンド継続を判定します。
・既刊記事のヒストグラムが上昇方向に傾いている最中に
・本記事の矢印が出た場合、トレンド継続の根拠が2つ重なっていると読める
・どちらか一方だけでは、ダマシの可能性が残る
iBands関数の引数とmode(中央線・上部・下部)
判定の土台になる、iBands関数の引数を確認します。
iBandsのシグネチャ
double iBands( string symbol, int timeframe, int period, double deviation, int bands_shift, int applied_price, int mode, int shift );
iMACDやiOsMA等と同じ並びに加えて、
`deviation`(標準偏差の倍率)と、
`bands_shift`(インジケーター自体のシフト)が、
追加で入っている点が特徴です。
mode=0/1/2(中央線・上部バンド・下部バンド)の違い
`mode`に`MODE_MAIN`(0)を指定すると中央線、
`MODE_UPPER`(1)を指定すると上部バンド、
`MODE_LOWER`(2)を指定すると下部バンドの値が、それぞれ取得できます。
bands_shiftと通常のshiftを混同しない
・bands_shift(第5引数)は「インジケーター全体」を左右にずらす値
・通常は0のままでよい
・shift(第8引数)は「何本前の値を取得するか」を指定する値
・こちらをループで動かして使う
・2つの役割を混同すると、意図しない本数分だけ値がずれる
連続N本バンド外を判定する関数の作り方
バンドウォークを、そのままコードにすると何が起きるかを確認してから、
実際に判定関数を組み立てます。
単純な条件だけだと矢印が出っぱなしになる
Close[i] > upperBand
この条件だけだと、終値がバンドの外にある間、
ずっと条件を満たし続けてしまい、毎回矢印を描くと画面が埋まります。
「ちょうどN本目に到達した瞬間」だけを取り出す
//今の足を含めてN本連続でバンド外(確定) IsWalkingUp(i, N) == true //1本前を起点にしてもN本連続ではなかった(まだ確定していなかった) IsWalkingUp(i+1, N) == false
この2つを同時に満たす足だけに矢印を出すことで、
「ちょうどバンドウォークが確定した瞬間」だけを取り出せます。
形成中の0番目の足は、終値が変化し続けて確定していないため、
このコードは1本目(i=1)以降を基準に判定します。
IsWalkingUp/IsWalkingDown関数の中身
bool IsWalkingUp(int shift, int bars)
{
for(int j = 0; j < bars; j++)
{
double upper = iBands(NULL, 0, InpBandPeriod, InpBandDeviation, 0, PRICE_CLOSE, MODE_UPPER, shift + j);
if(Close[shift + j] <= upper)
return(false);
}
return(true);
}
bool IsWalkingDown(int shift, int bars)
{
for(int j = 0; j < bars; j++)
{
double lower = iBands(NULL, 0, InpBandPeriod, InpBandDeviation, 0, PRICE_CLOSE, MODE_LOWER, shift + j);
if(Close[shift + j] >= lower)
return(false);
}
return(true);
}
`shift`から`shift+bars-1`までの、
すべての終値がバンドの外側にあるかを、1本ずつ確認し、
1本でも条件を満たさなければ`false`を返します。
配列を使わずループだけで済ませている理由、初期値を3本にした理由
連続本数を専用の配列に保存しておく実装も可能ですが、
本記事では`InpWalkBars`の初期値が3〜5程度と小さいため、
毎回ループで数え直しても処理負荷は軽く、コードをシンプルに保てます。
WebSearchで確認した複数のFX専門メディアの解説でも、
「3本以上」をバンドウォークの目安とする説明が多かったため、
本記事もデフォルト値を3本にしています。
実際に動くバンドウォーク検知インジケーターのコード全体
ここまでの要素をまとめた、インジケーターの全体です。
//+------------------------------------------------------------------+
//| BollingerBandWalkIndicator.mq4 |
//| Copyright 2026, FX-EA System Project Creator |
//| https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, FX-EA System Project Creator"
#property link "https://creator.fx-ea-system-project.com/"
#property version "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1 DeepSkyBlue
#property indicator_color2 OrangeRed
input int InpBandPeriod = 20; //ボリンジャーバンドの期間
input double InpBandDeviation = 2.0; //標準偏差の倍率(σ)
input int InpWalkBars = 3; //バンドウォークと判定する連続本数
input int InpArrowGapPoints = 30; //高値・安値から矢印を離すポイント数
double UpWalkArrow[];
double DownWalkArrow[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, UpWalkArrow);
SetIndexStyle(0, DRAW_ARROW, STYLE_SOLID, 2);
SetIndexArrow(0, 233);
SetIndexEmptyValue(0, EMPTY_VALUE);
SetIndexBuffer(1, DownWalkArrow);
SetIndexStyle(1, DRAW_ARROW, STYLE_SOLID, 2);
SetIndexArrow(1, 234);
SetIndexEmptyValue(1, EMPTY_VALUE);
IndicatorShortName("Bollinger Band Walk Detector");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| 終値がshiftからbars本、連続でバンドの上側にあるか |
//+------------------------------------------------------------------+
bool IsWalkingUp(int shift, int bars)
{
for(int j = 0; j < bars; j++)
{
double upper = iBands(NULL, 0, InpBandPeriod, InpBandDeviation, 0, PRICE_CLOSE, MODE_UPPER, shift + j);
if(Close[shift + j] <= upper)
return(false);
}
return(true);
}
//+------------------------------------------------------------------+
//| 終値がshiftからbars本、連続でバンドの下側にあるか |
//+------------------------------------------------------------------+
bool IsWalkingDown(int shift, int bars)
{
for(int j = 0; j < bars; j++)
{
double lower = iBands(NULL, 0, InpBandPeriod, InpBandDeviation, 0, PRICE_CLOSE, MODE_LOWER, shift + j);
if(Close[shift + j] >= lower)
return(false);
}
return(true);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
int needed = InpBandPeriod + InpWalkBars + 2;
if(rates_total < needed)
return(0);
int limit = (prev_calculated == 0) ? rates_total - InpWalkBars - 2
: rates_total - prev_calculated + InpWalkBars + 1;
if(limit > rates_total - InpWalkBars - 2)
limit = rates_total - InpWalkBars - 2;
for(int i = limit; i >= 1; i--)
{
UpWalkArrow[i] = EMPTY_VALUE;
DownWalkArrow[i] = EMPTY_VALUE;
if(IsWalkingUp(i, InpWalkBars) && !IsWalkingUp(i + 1, InpWalkBars))
UpWalkArrow[i] = Low[i] - InpArrowGapPoints * Point;
else if(IsWalkingDown(i, InpWalkBars) && !IsWalkingDown(i + 1, InpWalkBars))
DownWalkArrow[i] = High[i] + InpArrowGapPoints * Point;
}
return(rates_total);
}
//+------------------------------------------------------------------+
`i=1`から始めているのは、1本前(`i+1`)との比較が必要なため、
0番目(形成中の足)を基準にしないようにするためです。
MetaEditorでのコンパイル・表示手順
実際にチャートへ適用するまでの手順です。
- MT4のMetaEditorで「ファイル」→「新規作成」→「カスタムインディケータ」を選ぶ
- 「BollingerBandWalkIndicator」という名前で作成し、テンプレートの中身をすべて削除して上のコードを貼り付ける
- F7キーでコンパイルし、「0 error(s), 0 warning(s)」になることを確認する
- チャートへ適用し、標準のボリンジャーバンドインジケーターも同時に表示する
- 終値が3本連続でバンドの外側に出た直後の足に、矢印が出ることを目視で確認する
よくあるエラーと注意点
実際に動かす前に、確認しておきたい点です。
矢印が全く表示されない
`needed`の本数に足りるヒストリーデータがあるか確認してください。
`InpWalkBars`を大きくするほど、必要な本数も増えます。
レンジ相場で矢印が頻発する
・`InpBandPeriod`を短くすると、バンド自体が価格に追従しやすくなる
・その結果、外側に出る頻度も増える
・矢印が頻発する場合はInpWalkBarsを増やす
・または既刊の効率比インジケーターと併用する
・レンジ判定を加えることでダマシを減らせる
パラメータを変えても矢印が変わらない
`InpBandPeriod`等を変更した後は、一度インジケーターを外して再度適用すると、
反映漏れを防げます。
バンドウォークの使いどころと限界
このインジケーターの、得意な場面と弱点を整理します。
スクイーズ後の初動を捉えやすい
バンド幅が狭くなった後に矢印が出た場合は、
トレンドの初動である可能性が、相対的に高くなります。
歩き始めた後の「降り際」はこのインジケーターだけでは分からない
このインジケーターは「歩き始めた瞬間」だけを検知する設計で、
「いつ歩き終わるか(トレンド終了)」は判定していません。
降り際は、終値がバンドの内側に戻った時点で、
目視、またはオシレーター系インジケーターとの併用で判断してください。
InpWalkBarsを増減させたときのトレードオフ
本数を増やすと、ダマシは減りますが、
エントリーのタイミングは、その分遅くなります。
よくある質問
バンドウォーク検知の実装で、よく検索されている疑問に回答します。
バンドウォークは何本からと判定すべきですか?
厳密な公式定義はありませんが、3本以上を目安にする解説が多く、
本記事も3本を初期値にしています。
通貨ペアや時間足によって最適な本数は変わるため、
バックテストで調整することをおすすめします。
既刊bollinger-band-histogram.mdとどちらを使うべきですか?
判定の切り口が異なるため、どちらか一方ではなく、
両方を表示して根拠が重なっているかを確認する使い方が向いています。
EAのエントリー条件にそのまま使えますか?
この記事のコードは表示専用のインジケーターです。
`IsWalkingUp`/`IsWalkingDown`関数を、
そのままEAの`OnTick()`内にコピーすれば、
エントリー条件として使えます。
ただし、損切り・ポジション重複防止は別途実装が必要です。
【MQL4】ボリンジャーバンドの「バンドウォーク」を矢印で検知するインジケーターの作り方(スクイーズ後のトレンド継続サインを見逃さない設計):まとめ
この記事では、終値が連続何本バンドの外側にあるかを判定し、
バンドウォークが確定した瞬間だけに矢印を表示するインジケーターの作り方を解説しました。
「バンドの外にあるかどうか」という状態ではなく、「ちょうどN本目に到達したか」
という変化を検知することが、矢印を出っぱなしにしないための設計のポイントです。
コメント