1. MQL5の iBands とは何か
iBands は、MQL5でボリンジャーバンド(Bollinger Bands)を取得するための標準関数です。
MetaTrader 5(MT5)では、テクニカル指標の値をEA(自動売買プログラム)やスクリプトから取得する場合、インジケータハンドル(indicator handle:指標の計算結果へアクセスする識別番号)を作成し、そのハンドルを使ってデータを読み取る仕組みになっています。
iBands は、この ボリンジャーバンドの計算処理を内部で行うインジケータを生成し、そのハンドルを返す関数です。
EA開発では、このハンドルを利用して CopyBuffer 関数でバンドの値を取得し、売買判断ロジックに利用します。
一般的な処理の流れは次のとおりです。
iBandsでボリンジャーバンドのインジケータハンドルを作成CopyBufferでバンド値を取得- 取得した値を売買ロジックに使用
この構造は、MQL4のインジケータ取得方法と大きく異なる点です。
MQL4ではインジケータ関数を呼ぶだけで値を取得できましたが、MQL5では次のような2段階の仕組みになっています。
- ハンドル生成
- バッファから値取得
この違いを理解していないと、初心者は次のような問題に直面します。
よくある失敗
iBandsを呼んだだけで値が取得できると思ってしまうCopyBufferを使わずにバンド値を取得しようとするOnTick()内で毎回iBandsを作成してしまい、EAが重くなる
特に3つ目のミスは、EAのパフォーマンス低下の原因になります。
インジケータハンドルは通常 OnInit() で一度だけ作成するのが基本です。
1.1 ボリンジャーバンド(Bollinger Bands)の基本
ボリンジャーバンドは、価格の変動幅(ボラティリティ)を可視化するテクニカル指標です。
ジョン・ボリンジャーによって考案され、トレードでは非常に広く使われています。
ボリンジャーバンドは次の3つのラインで構成されます。
| ライン | 内容 |
|---|---|
| 中央線 | 移動平均(通常はSMA) |
| 上バンド | 移動平均 + 標準偏差 |
| 下バンド | 移動平均 – 標準偏差 |
ここでの 標準偏差とは、価格が平均値からどれくらい離れているかを示す統計値です。
つまり、ボリンジャーバンドは次のような特徴を持ちます。
- 相場のボラティリティが高い → バンドが広がる
- 相場が静かな → バンドが狭くなる
この特性を利用して、トレードでは次のような判断が行われます。
代表的な利用方法
- 上バンド付近 → 買われ過ぎ
- 下バンド付近 → 売られ過ぎ
- バンド拡張 → トレンド発生
- バンド収縮 → レンジ相場
ただし、これらは絶対的な売買シグナルではありません。
市場環境や時間足によって挙動は大きく変わるため、EA開発では他の指標と組み合わせて使用することが一般的です。
1.2 EA開発での主な用途
MQL5で iBands を使う目的は、ボリンジャーバンドを利用した売買ロジックの実装です。
EAでは主に次のような用途で使われます。
逆張り戦略
価格がバンドの外側に到達した場合、行き過ぎと判断して反転を狙う方法です。
例
- 上バンド到達 → 売り
- 下バンド到達 → 買い
この戦略はレンジ相場で有効な場合が多いですが、強いトレンドでは機能しないことがあります。
ブレイクアウト戦略
バンドの外側へ価格が突破した場合、トレンドの開始と判断する方法です。
例
- 上バンド突破 → 買い
- 下バンド突破 → 売り
この手法はトレンドフォロー型EAでよく使われます。
ボラティリティ判定
バンド幅を計算することで、相場の状態を判断できます。
例
- バンド幅が狭い → 相場が静か
- バンド幅が拡大 → トレンド開始の可能性
この情報は、次のような用途に使われます。
- トレード開始タイミング
- ロットサイズ調整
- トレード停止フィルター
注意点(初心者がよく誤解する点)
ボリンジャーバンドは方向を示す指標ではありません。
あくまで「価格の変動幅」を示す指標です。
そのため、次のような誤解がよくあります。
よくある誤解
- 上バンド到達 = 必ず下がる
- 下バンド到達 = 必ず上がる
実際には、強いトレンドではバンド沿いに価格が動き続けることもあります。
EAロジックでは、RSIやATRなどの他指標と組み合わせることが多いです。
2. iBands の構文(シンタックス)とパラメータ
iBands は、ボリンジャーバンドインジケータのハンドル(識別番号)を作成する関数です。
この関数を実行すると、MetaTrader内部でボリンジャーバンドの計算インジケータが生成され、そのハンドル(整数値)が返されます。
このハンドルは、その後 CopyBuffer を使ってバンド値を取得するために使用します。
MQL5での基本構文は次の通りです。
int iBands(
string symbol,
ENUM_TIMEFRAMES period,
int bands_period,
int bands_shift,
double deviation,
ENUM_APPLIED_PRICE applied_price
);
戻り値
- 成功:インジケータハンドル(0以上の整数)
- 失敗:
INVALID_HANDLE
そのため、EAでは通常次のようにエラーチェックを行います。
int bandsHandle = iBands(_Symbol, PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE);
if(bandsHandle == INVALID_HANDLE)
{
Print("iBandsの作成に失敗しました");
}
このエラーチェックを行わないと、後続の CopyBuffer で原因不明のエラーが発生することがあります。
2.1 各パラメータの意味
iBands には6つのパラメータがあります。
それぞれの意味を理解しておくことは、EA開発では非常に重要です。
| パラメータ | 内容 |
|---|---|
| symbol | 通貨ペア(例:EURUSD) |
| period | 時間足 |
| bands_period | 移動平均期間 |
| bands_shift | バンドのシフト |
| deviation | 標準偏差 |
| applied_price | 使用する価格 |
以下で個別に解説します。
symbol(通貨ペア)
インジケータを計算する通貨ペアを指定します。
例
_Symbol
これは「現在のチャートの通貨ペア」を意味します。
例
iBands("EURUSD", PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE);
複数通貨EAでは、異なる通貨ペアを指定することも可能です。
注意点
- 通貨名を間違えると INVALID_HANDLE になります
- ブローカーによっては EURUSD.m のような接尾辞が付く場合があります
period(時間足)
ボリンジャーバンドを計算する時間足です。
主な指定方法
PERIOD_CURRENT
PERIOD_M1
PERIOD_M5
PERIOD_M15
PERIOD_H1
PERIOD_D1
例
PERIOD_H1
これは1時間足のボリンジャーバンドを意味します。
よくあるミス
- EAの時間足とインジケータ時間足を混同する
- マルチタイムフレーム処理を理解せず使う
初心者はまず
PERIOD_CURRENT
を使うと安全です。
bands_period(移動平均期間)
中央線の移動平均期間です。
例
20
これは一般的なボリンジャーバンド設定です。
代表的な設定
| 期間 | 用途 |
|---|---|
| 20 | 一般的 |
| 14 | 短期トレード |
| 50 | 中期トレンド |
期間が小さいほど、反応が速いがノイズが増えます。
bands_shift(バンドシフト)
インジケータの表示位置を右へずらす値です。
通常は
0
を使用します。
この値は主にチャート表示の都合のためのものであり、EAロジックではほとんど使用されません。
初心者は 0固定で問題ありません。
deviation(標準偏差)
バンド幅を決める標準偏差倍率です。
代表例
2.0
これは最も一般的な設定です。
例
| deviation | 特徴 |
|---|---|
| 1.0 | 狭いバンド |
| 2.0 | 標準 |
| 3.0 | 非常に広い |
値が大きいほど、バンドは広くなります。
applied_price(適用価格)
インジケータ計算に使う価格の種類です。
主な値
PRICE_CLOSE
PRICE_OPEN
PRICE_HIGH
PRICE_LOW
PRICE_MEDIAN
PRICE_TYPICAL
PRICE_WEIGHTED
初心者は通常
PRICE_CLOSE
を使用します。
これは終値ベースのボリンジャーバンドです。
2.2 iBands を使う基本コード
EAでは通常、OnInit() でハンドルを作成します。
例
int bandsHandle;
int OnInit()
{
bandsHandle = iBands(
_Symbol,
PERIOD_CURRENT,
20,
0,
2.0,
PRICE_CLOSE
);
if(bandsHandle == INVALID_HANDLE)
{
Print("iBands作成エラー");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
この方法により、EA起動時に1回だけインジケータを生成できます。
よくある失敗
初心者がよくやるミスをまとめます。
OnTick()で毎回iBandsを作る
// 悪い例
void OnTick()
{
int handle = iBands(...);
}
これは毎ティックでインジケータ生成するため、EAが重くなります。
エラーチェックをしない
int handle = iBands(...);
これだけでは、失敗しても気づけません。
CopyBuffer前にハンドル確認をしない
ハンドルが無効だと
array out of range
などのエラー原因になります。
3. CopyBuffer を使ってボリンジャーバンドの値を取得する方法
iBands はボリンジャーバンドの計算インジケータを生成する関数ですが、
この関数だけではバンドの値を取得することはできません。
実際に値を取得するためには、CopyBuffer 関数を使用してインジケータバッファからデータをコピーする必要があります。
MQL5では、インジケータは内部的に複数のバッファ(データ配列)を持っています。
ボリンジャーバンドの場合、次の3つのラインがそれぞれ別のバッファに格納されています。
| バッファ番号 | 内容 |
|---|---|
| 0 | 上バンド(Upper Band) |
| 1 | 中央線(Middle Band / Moving Average) |
| 2 | 下バンド(Lower Band) |
このバッファ番号を指定して CopyBuffer を呼び出すことで、
EAは任意のバンド値を取得できます。
3.1 CopyBuffer の基本構文
CopyBuffer の基本構文は次の通りです。
int CopyBuffer(
int indicator_handle,
int buffer_num,
int start_pos,
int count,
double buffer[]
);
各パラメータの意味は次の通りです。
| パラメータ | 内容 |
|---|---|
| indicator_handle | iBandsで取得したハンドル |
| buffer_num | バッファ番号 |
| start_pos | 取得開始位置 |
| count | 取得するデータ数 |
| buffer | データ格納配列 |
戻り値
- コピーした要素数
- 失敗すると -1
そのため、実際のEAでは戻り値チェックを行うことが重要です。
3.2 上バンドを取得するコード例
まず、上バンド(Upper Band)を取得する例を見てみます。
double upperBand[];
if(CopyBuffer(bandsHandle, 0, 0, 1, upperBand) < 0)
{
Print("CopyBufferエラー");
}
このコードの意味は次の通りです。
| パラメータ | 意味 |
|---|---|
| bandsHandle | iBandsハンドル |
| 0 | 上バンド |
| 0 | 最新バー |
| 1 | 1本取得 |
結果は次のように取得できます。
double upper = upperBand[0];
3.3 下バンドと中央線を取得する方法
同じ方法で、中央線と下バンドも取得できます。
中央線(移動平均)
double middleBand[];
CopyBuffer(bandsHandle, 1, 0, 1, middleBand);
下バンド
double lowerBand[];
CopyBuffer(bandsHandle, 2, 0, 1, lowerBand);
3.4 実践的なコード例
EAでは通常、3つのバンドを同時に取得します。
double upper[1];
double middle[1];
double lower[1];
if(CopyBuffer(bandsHandle,0,0,1,upper) < 0) return;
if(CopyBuffer(bandsHandle,1,0,1,middle) < 0) return;
if(CopyBuffer(bandsHandle,2,0,1,lower) < 0) return;
double upperBand = upper[0];
double middleBand = middle[0];
double lowerBand = lower[0];
これにより、EA内でボリンジャーバンドの3ラインを使用できます。
3.5 よくある失敗と注意点
CopyBuffer は初心者が最もつまずきやすい部分です。
特に次のミスが非常に多く見られます。
配列サイズを確保していない
次のコードは危険です。
double upper[];
CopyBuffer(handle,0,0,1,upper);
配列サイズが確保されていない場合、エラーになる可能性があります。
安全な方法
double upper[1];
または
ArrayResize(upper,1);
バッファ番号を間違える
初心者が混乱しやすいポイントです。
| バッファ | 内容 |
|---|---|
| 0 | 上バンド |
| 1 | 中央線 |
| 2 | 下バンド |
この順番を間違えると、ロジックが完全に崩れます。
データがまだ準備されていない
EA起動直後などでは、インジケータ計算がまだ完了していない場合があります。
その場合、CopyBuffer は -1 を返します。
安全な処理
if(CopyBuffer(...) <= 0)
return;
最新バーと確定バーを混同する
start_pos = 0 は 現在進行中のバーです。
そのため、EAロジックでは次のように使う場合があります。
| start_pos | 意味 |
|---|---|
| 0 | 最新バー |
| 1 | 確定バー |
トレードロジックでは 確定バー(1) を使うケースも多いです。
4. EAで iBands を使う実装例
iBands は、単にボリンジャーバンドを表示するための関数ではありません。
EAでは、取得したバンド値と現在価格を比較し、売買条件を機械的に判定するための材料として使います。
ただし、ボリンジャーバンドはそれ単体で万能な売買シグナルではありません。
実務では、次のように役割を分けて使うことが多いです。
- 逆張り判定
- ブレイクアウト判定
- 相場の静かさ・荒さの判定
- エントリー禁止フィルター
このセクションでは、初心者が理解しやすいように、まずはもっとも基本的な2パターンを扱います。
- バンド到達で反転を狙う逆張り型
- バンド突破で流れに乗るブレイクアウト型
4.1 逆張りロジックの基本例
逆張り型では、価格がバンド外側まで到達したときに、行き過ぎからの戻りを狙います。
典型例は次の通りです。
- 現在価格が上バンド以上 → 売りを検討
- 現在価格が下バンド以下 → 買いを検討
コード例は次のようになります。
double upper[1];
double lower[1];
if(CopyBuffer(bandsHandle, 0, 0, 1, upper) <= 0) return;
if(CopyBuffer(bandsHandle, 2, 0, 1, lower) <= 0) return;
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(bid >= upper[0])
{
Print("売りシグナル候補:価格が上バンドに到達");
}
if(ask <= lower[0])
{
Print("買いシグナル候補:価格が下バンドに到達");
}
このコードでは、まだ注文は出していません。
まずは条件判定だけをログ出力する形にしておくと、ロジック検証がしやすくなります。
逆張り型の注意点
逆張りロジックは見た目にはわかりやすいですが、実運用では次の失敗が非常に多いです。
- 強いトレンド相場で逆張りしてしまう
- バンドに触れただけで即エントリーしてしまう
- 確定前のバーで判定してダマシを拾う
特に重要なのは、バンド到達 = すぐ反転、ではないという点です。
トレンドが強いときは、価格がバンド沿いに進み続けることがあります。
そのため、実際には次のような補助条件を追加することが多いです。
- RSIで買われ過ぎ・売られ過ぎを確認
- 確定バーでのみ判定
- 上位足のトレンド方向と逆行しないよう制限
4.2 ブレイクアウトロジックの基本例
ブレイクアウト型では、価格がバンド外へ出たことを勢いの発生とみなし、その方向へ追随します。
典型例は次の通りです。
- 終値が上バンドを上抜け → 買い候補
- 終値が下バンドを下抜け → 売り候補
この場合、現在進行中のバーではなく、確定したバーを使う方が安全です。
そのため、CopyBuffer では start_pos = 1 を使うことが多くなります。
double upper[1];
double lower[1];
double closePrice[1];
if(CopyBuffer(bandsHandle, 0, 1, 1, upper) <= 0) return;
if(CopyBuffer(bandsHandle, 2, 1, 1, lower) <= 0) return;
if(CopyClose(_Symbol, PERIOD_CURRENT, 1, 1, closePrice) <= 0) return;
if(closePrice[0] > upper[0])
{
Print("買いブレイク候補:確定足終値が上バンドを上抜け");
}
if(closePrice[0] < lower[0])
{
Print("売りブレイク候補:確定足終値が下バンドを下抜け");
}
この方法の利点は、未確定バーの一時的な飛び出しを除外しやすいことです。
ブレイクアウト型の注意点
ブレイクアウトはトレンド相場では有効なことがありますが、レンジ相場では失敗しやすくなります。
よくある失敗は次の通りです。
- 一瞬の飛び出しを本物のブレイクと誤認する
- バンド幅が極端に狭い場面でノイズに反応する
- 指標発表時の急変動に巻き込まれる
そのため、実務では次のような条件を追加することがあります。
- バンド幅が一定以上あるときだけ許可
- ATRでボラティリティ確認
- スプレッド拡大時は売買しない
4.3 バンド幅を使ったボラティリティ判定
ボリンジャーバンドは、上バンドと下バンドの差を取ることで、相場の変動の大きさを数値化できます。
計算式は単純です。
double bandWidth = upper[0] - lower[0];
この bandWidth を使うことで、EAに次のような判断をさせられます。
- バンド幅が狭い → 相場が停滞気味
- バンド幅が広い → 相場が活発
- 一定以下ならエントリーしない
- 一定以上ならブレイク戦略を有効化する
コード例は次の通りです。
double upper[1];
double lower[1];
if(CopyBuffer(bandsHandle, 0, 1, 1, upper) <= 0) return;
if(CopyBuffer(bandsHandle, 2, 1, 1, lower) <= 0) return;
double bandWidth = upper[0] - lower[0];
if(bandWidth < 0.0010)
{
Print("バンド幅が小さいため見送り");
}
else
{
Print("バンド幅が十分あるため売買候補");
}
ただし、この閾値は通貨ペアや桁数によって異なります。
たとえば EURUSD と USDJPY では価格スケールが異なるため、同じ数値基準は使えません。
バンド幅判定での注意点
- 固定値の閾値を全通貨ペアで使い回さない
- 5桁・3桁ブローカー差を無視しない
- テスターでは良くても実運用でズレることがある
初心者はまず、ログに bandWidth を出して観察し、
対象銘柄ごとの自然な範囲を把握してから条件化するのが安全です。
4.4 実装時によくある失敗
iBands をEAに組み込むとき、初心者が特につまずきやすい失敗をまとめます。
毎ティックで全条件を即発注につなげる
シグナル条件が成立した瞬間に毎回注文を出すと、同じ条件で連続発注してしまうことがあります。
対策例
- 新しいバーでのみ判定する
- ポジション保有中は再発注しない
- 前回シグナル状態を変数で保持する
bid と ask を混同する
買い注文は通常 Ask、売り判断は Bid を意識する必要があります。
ここを曖昧にすると、条件判定と実際の約定価格がズレることがあります。
確定足と未確定足を混ぜる
start_pos = 0 は未確定バー、start_pos = 1 は直近確定バーです。
ロジックの途中でこの使い分けがブレると、バックテスト結果と実運用結果が乖離しやすくなります。
CopyBuffer の失敗時処理を省略する
値が取れなかったのに配列参照を続けると、
ロジック誤作動や別エラーの原因になります。
必ず次のように確認します。
if(CopyBuffer(bandsHandle, 0, 1, 1, upper) <= 0)
return;
5. iBands 使用時の注意点と実務で起きやすいエラー
iBands はMQL5の標準関数であり、基本的な使い方自体は難しくありません。
しかし、実際にEAへ組み込む段階では、ハンドル管理・データ取得タイミング・価格の扱い方でつまずくことが非常に多いです。
特に初心者は、文法エラーよりも「動いているように見えるのに結果がおかしい」という状態に陥りやすいです。
このセクションでは、iBands を実務で使うときに注意すべき点を整理します。
5.1 INVALID_HANDLE になる原因
iBands の戻り値は、成功すればインジケータハンドル、失敗すれば INVALID_HANDLE です。
このチェックを省略すると、後続の CopyBuffer が失敗し、原因の切り分けが難しくなります。
基本の確認コードは次の通りです。
int bandsHandle = iBands(_Symbol, PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE);
if(bandsHandle == INVALID_HANDLE)
{
Print("iBandsの作成に失敗しました");
return INIT_FAILED;
}
INVALID_HANDLE になる主な原因は次の通りです。
- シンボル指定が誤っている
- 時間足指定が不適切
- 端末の状態やデータ読込が不十分
- 初期化タイミングの問題
よくある失敗
たとえば、複数通貨を扱うEAで "EURUSD" を直接書いた場合、
ブローカーによっては "EURUSDm" や "EURUSD." のようなシンボル名になっており、意図した通貨ペアが存在しないことがあります。
そのため、初心者はまず
_Symbol
を使う方が安全です。
また、ハンドル作成を OnTick() 内で行うのも典型的な失敗です。
毎ティックごとに新しいインジケータを作る形になり、端末負荷が増え、挙動も不安定になります。
5.2 CopyBuffer が失敗する原因
iBands が正常でも、CopyBuffer が必ず成功するわけではありません。
MQL5では、インジケータ計算がまだ準備できていない段階で CopyBuffer を呼ぶと失敗することがあります。
たとえば次のコードは、戻り値チェックがないため危険です。
double upper[1];
CopyBuffer(bandsHandle, 0, 0, 1, upper);
double value = upper[0];
この場合、コピー失敗時でもそのまま upper[0] を使ってしまい、
ロジック異常や誤判定の原因になります。
安全な書き方は次の通りです。
double upper[1];
if(CopyBuffer(bandsHandle, 0, 0, 1, upper) <= 0)
{
Print("上バンドの取得に失敗");
return;
}
CopyBuffer 失敗の主な原因
- ハンドルが無効
- バッファ番号が違う
- 必要なバー数がまだ計算されていない
- シンボルや時間足のデータ不足
- 起動直後で履歴が十分に読み込まれていない
5.3 バッファ番号の取り違え
iBands は3本のラインを扱いますが、
初心者が最も混乱しやすいのがバッファ番号の順番です。
本記事で扱っている前提は次の通りです。
| バッファ番号 | 内容 |
|---|---|
| 0 | 上バンド |
| 1 | 中央線 |
| 2 | 下バンド |
ここを逆に覚えると、ロジックはコンパイル上問題なく動いても、売買判定が完全に誤る可能性があります。
たとえば、本来は下バンド判定のつもりで buffer_num = 1 を指定すると、
実際には中央線を比較してしまい、エントリー条件が別物になります。
ありがちな状況
- 上バンドと中央線を取り違える
- コードコメントと実際のバッファ番号が一致していない
- 他記事や過去コードを流用して順番が混在する
この種のミスは、見た目では気付きにくいのが厄介です。
デバッグ時は、取得した3本の値をログへ出力し、上下関係が正しいか確認すると安全です。
Print("Upper=", upper[0], " Middle=", middle[0], " Lower=", lower[0]);
5.4 未確定バーと確定バーの混同
CopyBuffer で start_pos = 0 を指定すると、現在進行中のバーを取得します。
一方、start_pos = 1 は直近で確定したバーです。
この違いは、EAロジックでは非常に重要です。
start_pos = 0 の特徴
- リアルタイム性が高い
- 値が刻々と変わる
- シグナルの先出しができる
- ダマシも増えやすい
start_pos = 1 の特徴
- バー確定後の値を使える
- ロジックが安定しやすい
- バックテストと実運用の差が出にくい
初心者がよくやる失敗は、
エントリー判定は未確定バー、検証説明は確定バーのつもりで話してしまうことです。
これにより、ロジックの再現性が落ちます。
たとえば次のように明確に使い分けます。
// 未確定バーを見る
CopyBuffer(bandsHandle, 0, 0, 1, upper);
// 確定バーを見る
CopyBuffer(bandsHandle, 0, 1, 1, upper);
実運用を安定させたい場合は、まず確定バー基準から始めるのが無難です。
5.5 OnTick() で毎回重い処理をする問題
iBands 自体は便利ですが、使い方を誤るとEAが重くなります。
特に問題になりやすいのは次の2つです。
OnTick()内で毎回iBandsを作る- 毎ティックで多数の
CopyBufferを実行する
悪い例は次のようなコードです。
void OnTick()
{
int handle = iBands(_Symbol, PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE);
double upper[1];
CopyBuffer(handle, 0, 0, 1, upper);
}
この書き方では、ティックが来るたびにインジケータを新規生成することになり、
無駄な負荷が発生します。
基本方針は次の通りです。
- ハンドル作成は
OnInit()で一度だけ - 値取得は必要な場面だけ
- 可能なら新バー時だけ計算する
たとえば、新バー判定を入れて無駄な計算を減らす方法があります。
datetime lastBarTime = 0;
void OnTick()
{
datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
if(currentBarTime == lastBarTime)
return;
lastBarTime = currentBarTime;
// 新バー時だけ iBands の値を取得
}
この形にすると、ティックごとの過剰計算を避けやすくなります。
5.6 価格の桁数・通貨ペア差を無視する問題
ボリンジャーバンドの値そのものは価格です。
そのため、通貨ペアごとの価格スケール差を無視すると、条件設定を誤りやすくなります。
たとえば、EURUSD と USDJPY では価格帯が大きく異なります。
- EURUSD は 1.x 台
- USDJPY は 100円台以上
この違いがあるため、たとえばバンド幅の閾値を固定値で決めると、
片方では有効でも、もう片方では全く機能しないことがあります。
よくある失敗
bandWidth < 0.0010を全銘柄に使う- 5桁通貨と3桁通貨を同じ感覚で扱う
PointやDigitsを考慮しない
より安全なのは、価格差をポイント単位で扱う方法です。
double bandWidthPoints = (upper[0] - lower[0]) / _Point;
この形にすると、銘柄ごとの差を多少吸収しやすくなります。
ただし、それでも最適値は銘柄や時間足で異なるため、バックテストとログ観察で調整が必要です。
5.7 よくある失敗の整理
最後に、iBands 使用時の典型的な失敗をまとめます。
iBandsを呼んだだけで値が取れると思うINVALID_HANDLEを確認しないCopyBufferの戻り値を無視する- バッファ番号を取り違える
- 未確定バーと確定バーを混同する
OnTick()で毎回ハンドルを作成する- 価格スケール差を考えずに固定条件を使う
iBands は難しい関数ではありませんが、
MQL5特有の「ハンドル型インジケータ」の考え方に慣れていないと、実装ミスが起きやすい関数でもあります。
そのため、初心者はまず次の順序で確認すると安全です。
- ハンドル作成成功を確認
CopyBuffer成功を確認- 取得値をログ出力して検証
- その後に売買ロジックへ組み込む
6. よくある失敗とつまずきポイント
iBands は構文自体は短く、見た目も単純です。
しかし、実際にEAへ組み込むと、「コンパイルは通るのに意図通りに動かない」という問題が起きやすい関数です。
原因の多くは、MQL5特有の仕組みである
- ハンドル生成
- CopyBufferで値取得
- バー確定タイミングの管理
この3点を曖昧にしたまま実装してしまうことにあります。
ここでは、初心者が特につまずきやすい失敗を、実務上の重要度が高い順に整理します。
6.1 MQL4と同じ感覚で書いてしまう
もっとも多い失敗の1つが、MQL4とMQL5のインジケータ取得方法を混同することです。
MQL4では、インジケータ関数を呼ぶと、その場で値を直接取得できる場面が多くありました。
そのため、MQL5でも同じ感覚で iBands() を使うと、誤解が生じやすくなります。
初心者がやりがちな誤解は次のようなものです。
iBands()が上バンド値そのものを返すと思うiBands()の戻り値を価格として比較してしまうCopyBuffer()を不要だと思ってしまう
しかし、MQL5の iBands() が返すのは価格ではなくハンドルです。
つまり、次のようなコードは意味が違います。
int bandsHandle = iBands(_Symbol, PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE);
この bandsHandle は、たとえば「1.08452」のような価格ではありません。
あくまで、内部インジケータへアクセスするための識別番号です。
よくある誤実装
if(Bid > iBands(_Symbol, PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE))
{
// これは誤り
}
このような書き方は、意図した比較にはなりません。
正しい流れは次の通りです。
iBands()でハンドルを作るCopyBuffer()でバンド値を取得する- 取得した配列要素を価格と比較する
この基本を崩すと、その後の実装がすべて不安定になります。
6.2 バッファ番号の理解が曖昧なまま使う
iBands は3本のラインを持つため、どのバッファがどのラインかを正しく理解していないと、ロジック全体が崩れます。
本記事の前提では、次の対応で扱っています。
| バッファ番号 | 内容 |
|---|---|
| 0 | 上バンド |
| 1 | 中央線 |
| 2 | 下バンド |
ここで起きやすい失敗は次の通りです。
- 上バンドと中央線を混同する
- 中央線と下バンドを混同する
- コードコメントと実際の番号が一致していない
このミスの厄介な点は、コードが正常に動いて見えることです。
コンパイルエラーにもならず、CopyBuffer() も成功するため、見落としやすくなります。
実務での確認方法
取得後にログへ出して、数値の大小関係を確認します。
Print("Upper=", upper[0], " Middle=", middle[0], " Lower=", lower[0]);
通常は、同一バーで
- 上バンド > 中央線
- 中央線 > 下バンド
となるはずです。
この関係が崩れているなら、バッファ指定やデータ取得方法を見直すべきです。
6.3 配列の扱い方を曖昧にしてしまう
CopyBuffer() は配列へデータをコピーする関数です。
そのため、配列の準備や参照方法を曖昧にしたまま使うと、誤動作やエラーの原因になります。
代表的な失敗は次の通りです。
- 配列サイズを意識せずに使う
- 何本取得したのか分からないまま参照する
[0]が常に「最新確定足」だと思い込む
たとえば次のコードです。
double upper[];
CopyBuffer(bandsHandle, 0, 0, 1, upper);
Print(upper[0]);
これは環境によっては動くこともありますが、
常に安全とは言えません。初心者はまず、サイズを明示した方が安全です。
double upper[1];
if(CopyBuffer(bandsHandle, 0, 0, 1, upper) <= 0)
return;
Print(upper[0]);
また、複数本取得する場合は、どのインデックスがどのバーかを明確に意識する必要があります。
double upper[3];
CopyBuffer(bandsHandle, 0, 0, 3, upper);
この場合、通常は upper[0] が取得開始位置の最初、upper[1] がその次、という形で格納されます。
ただし、時系列方向の扱いは配列設定や取得方法の前提で混乱しやすいため、初心者はまず1本ずつ明示的に取る方が事故を減らせます。
6.4 start_pos の意味を誤解する
CopyBuffer() の start_pos は、どのバー位置から取得するかを示します。
この意味を曖昧にすると、シグナル判定が不安定になります。
代表的な意味は次の通りです。
| start_pos | 意味 |
|---|---|
| 0 | 現在進行中のバー |
| 1 | 直近確定バー |
| 2 | その1本前の確定バー |
ここで初心者がやりがちな失敗は、バックテストでは確定足で説明しながら、実装では 0 を使ってしまうことです。
たとえば、
- 記事では「終値が上バンドを超えたら買い」
- 実装では
start_pos = 0
としてしまうと、実際には終値確定前の一時的な値動きも拾ってしまいます。
失敗しにくい考え方
- シグナルの安定性重視 →
start_pos = 1 - 先行性重視 →
start_pos = 0
初心者は、まず 1 を使う方が安全です。
未確定バーはリアルタイムでは魅力的に見えますが、ダマシや条件ブレが増えやすくなります。
6.5 OnTick() のたびに同じことを繰り返す
EAでは OnTick() が非常に頻繁に呼ばれます。
そのため、iBands を使う処理を無計画に OnTick() へ書くと、無駄な計算や重複判定が起きやすくなります。
代表的な悪い例は次の通りです。
- 毎ティックで
iBands()を作る - 毎ティックで複数回
CopyBuffer()を呼ぶ - 同じバー内で何度もシグナル判定する
これにより、次の問題が起きます。
- 端末が重くなる
- ログが大量に出る
- 同一条件で複数回エントリーしやすくなる
- バックテスト結果と実運用感覚がズレやすい
改善の基本方針
- ハンドル作成は
OnInit()で一度だけ - 値取得は必要最小限にする
- シグナル判定は新バー時のみ行う
新バー判定の基本例は次の通りです。
datetime lastBarTime = 0;
void OnTick()
{
datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
if(currentBarTime == lastBarTime)
return;
lastBarTime = currentBarTime;
// ここで iBands の取得と判定を行う
}
この形にしておくと、無駄な再判定を減らしやすくなります。
6.6 バンド到達を過信してしまう
ボリンジャーバンドを使う初心者に多いのが、価格が上バンドや下バンドに触れたら、それだけで強い売買根拠になると考えてしまうことです。
しかし実際には、ボリンジャーバンドは
- トレンド方向そのものを示す指標ではない
- 反転保証をする指標でもない
- ブレイク継続保証をする指標でもない
という性質があります。
そのため、次のような断定的なロジックは危険です。
- 上バンド到達 → 必ず売り
- 下バンド到達 → 必ず買い
- バンド突破 → 必ず順張り
実際には、相場環境に応じて意味が変わります。
例
- レンジ相場では逆張りが機能しやすい
- 強トレンド相場では逆張りが危険
- バンド収縮直後の拡大ではブレイクが機能しやすい
- 指標発表時はノイズが急増することがある
そのため、EAへ組み込むときは、少なくとも次のどれかを追加すると安定しやすくなります。
- RSI
- ATR
- 移動平均方向
- 上位足トレンド
- スプレッドフィルター
6.7 ログ確認を省略してしまう
初心者が意外とやりがちな失敗が、取得値を確認せずにそのまま売買条件へつなげることです。
たとえば、次の順序を飛ばしてしまうケースです。
- ハンドル作成確認
CopyBuffer()成功確認- 取得値ログ出力
- 条件式検証
- 発注処理追加
この途中確認を省略すると、
「なぜこの場所でエントリーしたのか」が分からなくなります。
実装初期に確認すべきログ例
Print(
"Upper=", upper[0],
" Middle=", middle[0],
" Lower=", lower[0],
" Bid=", SymbolInfoDouble(_Symbol, SYMBOL_BID),
" Ask=", SymbolInfoDouble(_Symbol, SYMBOL_ASK)
);
この確認をしておくと、
- 本当に上バンドを超えているのか
- 価格比較方向は正しいか
BidとAskの使い分けは妥当か
を切り分けやすくなります。
6.8 初心者向けの安全な進め方
iBands を使うとき、最初から複雑なEAへ入れると失敗しやすくなります。
初心者は次の順番で進めると安全です。
OnInit()でハンドル作成CopyBuffer()で1本だけ取得- ログへ出力して値を確認
- 上下バンドと価格を比較
- 条件が合ったらログだけ出す
- その後に注文処理を追加
この順番なら、どこで壊れたかを把握しやすいです。
逆に、最初から
iBandsCopyBuffer- エントリー条件
- ロット計算
OrderSend
まで一気に書くと、原因切り分けが難しくなります。
7. FAQ(よくある質問)
7.1 iBands は何を返す関数ですか?
iBands はボリンジャーバンドの価格そのものではなく、インジケータハンドル(識別番号)を返します。
実際のバンド値を取得するには、CopyBuffer を使ってインジケータバッファからデータをコピーする必要があります。
7.2 iBands のバッファ番号はどう対応していますか?
ボリンジャーバンドは3つのラインで構成されており、それぞれ次のバッファ番号に対応します。
| バッファ番号 | 内容 |
|---|---|
| 0 | 上バンド(Upper Band) |
| 1 | 中央線(Middle Band / 移動平均) |
| 2 | 下バンド(Lower Band) |
CopyBuffer の第2引数にこの番号を指定することで、目的のラインを取得できます。
7.3 iBands はどこで作成するのが適切ですか?
通常は OnInit() 内で一度だけ作成するのが推奨されます。
OnTick() 内で毎回 iBands を作成すると、ティックごとにインジケータを生成することになり、
EAの処理が重くなったり、挙動が不安定になる可能性があります。
7.4 CopyBuffer の start_pos は何を意味しますか?
start_pos は、どのバー位置からデータを取得するかを指定する値です。
| start_pos | 意味 |
|---|---|
| 0 | 現在進行中のバー |
| 1 | 直近確定バー |
| 2 | その1本前のバー |
リアルタイムの変動を含めたくない場合は、確定バー(1)を使用する方が安定します。
7.5 CopyBuffer が失敗するのはなぜですか?
主な原因は次の通りです。
- インジケータハンドルが無効
- バー履歴がまだ読み込まれていない
- バッファ番号の指定ミス
- インジケータ計算が完了していない
そのため、CopyBuffer の戻り値を必ず確認し、取得失敗時は処理を中断するようにするのが安全です。
7.6 ボリンジャーバンドに触れたら必ず反転しますか?
必ずしも反転するわけではありません。
ボリンジャーバンドは価格の変動幅(ボラティリティ)を示す指標であり、
トレンド方向そのものを保証する指標ではありません。
強いトレンドでは、価格がバンド沿いに動き続ける(バンドウォーク)こともあります。
そのため、EAでは次のような補助条件を組み合わせることが一般的です。
- RSI
- ATR
- 移動平均トレンド
- 上位足の方向
7.7 バンド幅はどのように計算しますか?
ボリンジャーバンドの幅は、上バンドと下バンドの差で計算できます。
double bandWidth = upper[0] - lower[0];
この値を利用すると、次のような判断が可能になります。
- バンド幅が狭い → 相場が停滞している可能性
- バンド幅が広い → 相場が活発な可能性
ただし、通貨ペアや価格桁数によって値のスケールが異なるため、
固定値の閾値は環境によって調整が必要です。
7.8 iBands と iCustom の違いは何ですか?
iBands は MetaTraderに標準搭載されているボリンジャーバンドインジケータを呼び出す関数です。
一方 iCustom は、自作または外部のカスタムインジケータを呼び出す関数です。
標準ボリンジャーバンドを使う場合は iBands の方がシンプルで扱いやすく、
カスタム計算ロジックを使う場合は iCustom を使用します。