※当サイトはアフィリエイト広告(Amazonアソシエイツ等)を利用しています。
こんにちは。trade-engineer.com 運営者のHです。MQL4でロウソク足の陽線か陰線かを判定するコードを書くたびに、始値と終値を引いてif文を並べる作業を繰り返していませんか。今回はその冗長さを解消するために、自作関数 iBarType を MQL4 で実装し、ロウソク足の種別を一行で判定できるようにしていきます。
本記事では iBarType のコード全文・引数設計・組み込み手順・よくあるエラー対処・バックテストでの検証方法まで、再利用性を意識したエンジニア視点で示します。BO時代に数千パターンのバックテストを回した経験から、こうした基盤関数を最初に整えておくと後続のロジック開発が劇的に速くなるので、ぜひ最後まで読んでみてください。
- iBarType関数で陽線陰線同事の判定を一行に集約できる
- 戻り値を0/1/2の3値設計にすることで同事を中立に扱える
- iOpen・iCloseを使ったコード全文と組み込み手順を提供
- シフトや時間足の落とし穴と検証方法までエンジニア視点で整理
MQL4でiBarType関数を作る目的と仕様
最初のH2では、なぜ iBarType を関数として独立させる必要があるのかを明確にし、戻り値や使用シーンといった仕様面を固めます。仕様が曖昧なまま実装すると後続のロジックで矛盾が出るため、ここでの整理が後半の実装を素直にします。
陽線陰線判定を毎回書く非効率
MQL4でロウソク足の陽線か陰線かを判定する場合、もっとも素直な書き方は始値と終値を取得して引き算する方法です。具体的には iOpen と iClose で対象バーの価格を取り、差分が正なら陽線・負なら陰線として条件分岐します。しかしこのコードを必要な箇所のたびに書くと、同じロジックがインジケーターやEAの中に何度も登場することになります。
さらに複数の通貨ペアや時間足を同時に扱う場合、その都度引数を変えながら同じ計算を埋め込む必要があり、見通しが急速に悪化します。条件分岐の中身を変えた瞬間、別の場所の同等ロジックも変更し忘れるリスクが生じます。バックテストを繰り返してロジックを微調整するエンジニアにとって、この種の重複は致命的なバグの温床になります。
たとえば RSI の値が一定範囲にあるときに陽線だったらシグナルを出すロジックを書く場合、エントリー条件・エグジット条件・統計集計の3か所で同じ判定を書きがちです。コードの可読性は下がり、後から「同事の場合は条件を変えたい」という拡張が入った瞬間、3か所すべてを書き換える必要が出てきます。
trade-engineer.com では、自作インジケーターでこの問題に何度も直面してきました。BO時代のバックテストでも、判定ロジックが分散したことで「サンプル1500件のうち127件は意図と違う向きを採用していた」というミスがあり、結果の信頼性を大きく下げました。だからこそ、再利用される判定処理は早い段階で関数化しておくのが鉄則です。
iBarTypeで一行化するメリット
iBarType関数を導入する最大のメリットは、ロウソク足の判定が int bartype = iBarType("USDJPY", PERIOD_M5, i); という一行で完結することです。差分の計算もif文も外から見えなくなり、呼び出し側はバーの種別だけを意識すればよくなります。コードの行数が単純に減るだけでなく、ロジックが意図ベースで読めるようになる点が大きな違いです。
判定基準を変更したい場合、たとえば「実体の長さが pip 単位で一定以下なら同事として 0 扱いする」といった拡張を入れたいときでも、変更箇所は iBarType 関数の内部 1 か所で済みます。インジケーターや EA をいくつも持っている開発者にとって、変更コストの差は積み重なるほど効いてきます。テスト時にも「iBarType 関数だけ動作確認すればよい」と局所化できるのは非常に大きな利点です。
さらに名前付き関数として登場するため、コードレビューで他人が読んだときの意図伝達も明確になります。M1_Close - M1_Open > 0 という式が出てくるだけのコードと、iBarType(NULL, NULL, i) == 1 と書かれたコードでは、前者は読み手にも判定意図を再構築させますが、後者は瞬時に意図が伝わります。
判定値を整数で返す設計にしておくと、後続のロジックでも扱いやすくなります。たとえば switch 文で陽線・陰線・同事を場合分けする、あるいは過去 N 本の連続陽線をカウントする、といった処理が自然に書けます。同様の関数化アプローチは VLDMI インジケーターの自作手順 でも採用しており、共通モジュール化の効果を実感できる構成にしています。
戻り値の仕様と0/1/2の決め方
iBarType 関数の戻り値は、実装の核となる部分です。今回は「0=同事もしくは四値同時」「1=陽線」「2=陰線」という3値で設計します。bool にせず int で返す理由は、同事のケースを明確に分離するためです。bool の場合は陽線か陰線かの2択になり、同事は強制的にどちらかに寄せる必要があります。
バックテストの精度を上げる立場では、同事を中立として扱える設計は重要です。たとえば連勝・連敗カウントの集計時、同事をどちらかに混ぜると結果が歪みます。USDJPY 5分足のように同事の発生頻度が無視できない時間足では、3値設計が結果の解像度を上げます。
0/1/2 という割り当ては慣習的にいくつか選択肢があります。0 が中立を表す値として直感的に分かりやすい点、1 と 2 が陽線・陰線の対称性を表す点が今回の割り当ての理由です。マジックナンバーを避けたいなら、後から define や enum に置き換える設計も可能です。MQL4 には enum を定義する仕組みがあり、たとえば BAR_TYPE_NEUTRAL=0, BAR_TYPE_BULL=1, BAR_TYPE_BEAR=2 と書けば、呼び出し側のコードはさらに自己説明的になります。
重要なのは「同事の閾値」をどう定義するかです。本記事の実装では「終値と始値が完全に同値」のときだけ 0 を返しますが、現実のティック単位では完全一致は稀です。実用上は「実体の絶対値が 0.1pip 未満」のような閾値を引数で受け取れるようにしておくと、戦略によって柔軟に判定基準を切り替えられます。trade-engineer.com ではバックテスト時のサンプル件数を変える要因として、この閾値を 3 パターン用意して比較するのが定番です。
3値設計を採用すると、後で同事の頻度を集計に組み込みやすくなります。bool型で実装してしまうと、後から「同事だけ別扱いしたい」と思った瞬間に関数のシグネチャごと書き換える必要が出てくるため、最初から int で 3 値を返す設計に統一しておくのが安全です。
想定する使用シーンとMT4要件
iBarType 関数は、ロウソク足の方向だけを必要とする多くのシーンで活躍します。代表例はトレンド判定、N 本連続陽線の検出、エントリー直後のフィードバック計測、バックテストの結果集計です。
特にバックテストの計測ロジックでは、判定時刻のバーが陽線か陰線かによって勝敗を切り替えるパターンが多いため、iBarType のような関数が自然と中心に座ります。BO 検証で使用していた判定モジュールでも、判定時刻のバーを iBarType 相当のロジックで取得し、エントリー方向と突き合わせて勝敗を判定していました。FX 移行後も同じ思想で EA・インジケーターに組み込んでいます。バックテストでロウソク足ベースの集計を行う具体例は ロウソク足制御でバックテスト にもまとめてあるので、活用イメージが湧きにくい方は併読してください。
事前準備としては MT4 と MetaEditor が起動できる環境であれば十分です。MT4 はビルド 1090 以降であれば問題なく動作します。MQL4 で書いた本関数はそのまま MT4 用インジケーターや EA に組み込めます。MQL5 への移植も難しくはなく、関数シグネチャを MQL5 の規約に合わせれば再利用可能です。
処理速度の面でも軽量です。iOpen と iClose は MT4 の組み込み関数で、内部でバッファ参照を行うのみのため、コストはほぼ無視できます。1000 バー単位のループでも遅延は感じません。OnCalculate 内で全バーに対してループするタイプのインジケーターでも、計算負荷の懸念なく導入できます。
ただし注意点として、iOpen と iClose は時間足やシフトの指定を間違えると意図しないバーを参照する事故が起きます。とくに NULL を通貨ペアと時間足の両方に与えると、現在チャートのシンボル・時間足を参照する挙動になるため、明示的に "USDJPY" や PERIOD_M5 を渡す癖を付けておくと安全です。
関数化に必要な事前知識
iBarType を実装するうえで前提として把握しておきたい知識は、MQL4 の関数定義の基本構文、iOpen・iClose の引数仕様、シフト値(インデックス)の意味の 3 点です。
関数の構文は他の C 系言語と類似しており、戻り値の型・関数名・引数リストを並べた後、波括弧の中に処理を書きます。グローバルスコープに配置すれば、同一ファイル内のどこからでも呼び出せます。複数のインジケーター・EA で共通利用したい場合は、別途インクルードファイル(.mqh)に切り出すと再利用性がさらに高まります。MQL4 の言語仕様は MQL4 公式ドキュメント に一通り揃っており、関数定義の章を一度通読しておくと迷いません。
iOpen と iClose の引数は「通貨ペア(string)」「時間足(int / ENUM_TIMEFRAMES)」「シフト(int)」の 3 つです。シフトは「いま現在のバーを 0、1 本前を 1、N 本前を N」と数える MT4 の標準仕様です。バックテストやヒストリカル分析では、シフト指定がそのまま検証対象の参照点になります。
注意点として、MT4 はティック単位で動くため、現在足(シフト 0)は未確定値を返します。確定足ベースで判定したい場合はシフト 1 以上を渡すか、Volume[0]==1 のタイミングでのみ評価するなど、確定足の扱いを設計段階で決めておく必要があります。trade-engineer.com の実装ガイドラインでは、シグナル系のロジックは確定足ベース、リアルタイム監視系はティックベース、と用途に応じて分けています。
時間足は int 値(1, 5, 15, …)でも ENUM_TIMEFRAMES の定数(PERIOD_M1, PERIOD_M5, …)でも渡せます。可読性と将来の MQL5 移植を考えると ENUM_TIMEFRAMES を使うのがおすすめです。MQL5 では時間足の指定が ENUM_TIMEFRAMES に統一されているため、最初から定数で書いておくと移植コストが下がります。
iBarType関数の実装と動作確認
後半の H2 では、実際の MQL4 コードを示しながら関数を組み立て、インジケーターへの組み込みと動作確認まで一気通貫で行います。コードはコピペで動く形で提示するので、MetaEditor を開きながら読み進めるとそのまま動かせます。
iBarType関数のコード全文
それでは実際のコードを示します。以下を MQL4 のインジケーターまたは EA ファイルの末尾(OnInit や OnCalculate の外側)に追記すれば、iBarType として利用できます。
//+------------------------------------------------------------------+
//| iBarType : 指定バーが陽線/陰線/同事のいずれかを返す関数 |
//| symbol : 通貨ペア(NULL で現在チャート) |
//| timeframe : 時間足(0 で現在チャート、ENUM_TIMEFRAMES 推奨) |
//| shift : シフト値(0=現在足、1=確定済み直近足) |
//| 戻り値 : 0=同事 / 1=陽線 / 2=陰線 |
//+------------------------------------------------------------------+
int iBarType(string symbol, int timeframe, int shift)
{
double open_price = iOpen (symbol, timeframe, shift);
double close_price = iClose(symbol, timeframe, shift);
double body = close_price - open_price;
if(body > 0.0) return(1); // 陽線
if(body < 0.0) return(2); // 陰線
return(0); // 同事
}
この関数は 3 つの引数を取り、内部で iOpen・iClose を呼び出して終値と始値の差分を計算します。差分が正なら 1(陽線)、負なら 2(陰線)、それ以外(完全一致)なら 0(同事)を返します。コードは 20 行に満たないシンプルな構成ですが、機能としては必要十分です。型は int で返すことで、呼び出し側で switch 文や条件分岐に乗せやすくなります。MQL5 への移植も同じ構造のまま、関数名や定数の差を吸収するだけで完了します。
関数の置き場所はファイルの末尾、OnInit や OnCalculate と同じスコープに配置します。何度も呼び出しても MQL4 のインライン展開を期待できる程度の処理量なので、別途インクルードに分離せずインジケーター内に置いても問題ありません。複数の EA で共有したい場合は、include 用の .mqh ファイル(例: ibartype.mqh)として切り出し、#include で読み込むと再利用性が高くなります。trade-engineer.com の開発テンプレートでは、共通関数群を common/ ディレクトリに集約し、#include "common/ibartype.mqh" の一行で読み込めるようにしてあります。複数プロジェクトを並行運用する場合、この共通化はメンテナンス工数を大幅に減らします。
引数の設計と他関数との整合
iBarType 関数の引数設計は、汎用性と安全性のバランスがポイントです。第 1 引数の通貨ペア(symbol)は string で渡します。NULL や空文字を渡すと現在チャートのシンボルが採用される MT4 仕様にしたがいます。
第 2 引数の時間足(timeframe)は int で受け取ります。実体としては ENUM_TIMEFRAMES の定数(PERIOD_M1=1, PERIOD_M5=5, …)を渡す形が一般的です。可読性と将来の MQL5 移植を見据えるなら、関数のシグネチャ自体を ENUM_TIMEFRAMES timeframe に置き換えてもよいでしょう。MQL4 でも ENUM_TIMEFRAMES は使えるため、より厳格な型チェックが効きます。
第 3 引数のシフト(shift)はバーのインデックスです。0 が現在進行中の足、1 が直近の確定足、2 以上が過去の確定足です。リアルタイムロジックではシフト 0 を、過去検証や確定足判定ではシフト 1 以上を渡します。trade-engineer.com では「リアルタイム参照は明示的に 0、確定足参照は 1 を既定値」というルールにしており、ミスを減らすためのコーディング規約として運用しています。
引数のバリデーションは関数内では行っていません。理由は MQL4 の iOpen・iClose が内部でエラー処理を持ち、不正な引数の場合は 0 を返してくる仕様だからです。呼び出し側で「symbol が存在しない」「シフトが利用可能本数を超えている」といったケースを事前にチェックしておくのが安全です。本気で堅牢化するなら、symbol の存在確認を SymbolSelect で、シフトの妥当性を iBars で検証する処理を関数の入り口に入れます。
引数の順序は MT4 の他の i 系関数(iOpen, iClose, iHigh, iLow, iRSI など)と統一しました。順序を揃えておくと、IDE の補完で混乱せず、ロウソク足系のロジックを書くたびに違和感なくタイピングできます。
iOpen iCloseと判定ロジックの解説
iBarType 関数の内部処理を、ブロックごとに見ていきます。最初の 2 行は iOpen と iClose で対象バーの始値と終値を取得しています。MT4 の組み込み関数で、内部的にはバッファ参照のため非常に軽量です。
3 行目で body = close_price - open_price を計算します。これがロウソク足の実体(陽線なら正、陰線なら負、同事なら 0)です。body 変数は double 型で受け取っており、価格の最小単位(USDJPY なら 0.001 相当)まで保持できます。
4 〜 6 行目で条件分岐を行います。body > 0 で陽線(1 を返却)、body < 0 で陰線(2 を返却)、いずれにも該当しなければ同事として 0 を返却します。return の順序は陽線→陰線→同事ですが、頻度の高い順に並べることで、たとえ最適化が効かない場合でも分岐コストを抑えられます。
ここで注意したいのは「完全一致による同事」の頻度です。USDJPY のような 3 桁価格では、始値と終値が完全一致するケースは稀ですが、ティックの薄い時間帯やマイナー通貨ペアでは発生し得ます。バックテストで「同事の本数が 0 で集計される」と感じたら、価格精度のしきい値を緩めるか、MathAbs(body) < threshold の形で同事を許容する閾値を導入するのが定石です。
浮動小数点の比較に厳密な等号を使うのが気持ち悪いと感じる場合は、MathAbs(body) < _Point のように _Point を用いて 1 ポイント未満を同事と定義する書き換えも可能です。trade-engineer.com の検証で同事の頻度を再現したい場合、しきい値を _Point の 0.5 倍にすると、ヒストリカルデータの精度ともよく整合します。
インジケーターへの組み込み手順
iBarType 関数を実際にインジケーターへ組み込む手順を順番に示します。前提は MT4 と MetaEditor がインストール済みで、空のインジケーターを新規作成できる状態です。
1. MetaEditor を起動 → ファイル → 新規作成 → カスタムインジケーター 2. 名前を「iBarTypeDemo」とし、ウィザードを完了 3. デフォルトで生成された OnCalculate の下に iBarType 関数を追記 4. OnCalculate の中で iBarType を呼び出し、Comment で結果を表示 5. F7 でコンパイル → エラーがなければOK 6. MT4 を起動し、ナビゲーターから iBarTypeDemo をチャートにドラッグ&ドロップ 7. チャート左上の Comment 表示で値が更新されることを確認
具体的な呼び出し例は次の通りです。確定足ベース(シフト 1)で動作確認するのが、安定した値が取れて分かりやすいです。
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 type = iBarType(Symbol(), Period(), 1);
string label = (type==1) ? "陽線" : (type==2) ? "陰線" : "同事";
Comment("直近確定足: ", label, " (raw=", type, ")");
return(rates_total);
}
このサンプルでは現在チャートのシンボルと時間足を Symbol()・Period() で取得し、シフト 1 の確定足に対して iBarType を呼び出しています。Comment で陽線・陰線・同事のラベルと raw 値を表示するため、目視で動作確認できます。USDJPY 5 分足で起動すれば、5 分ごとに値が切り替わる様子が観察できます。
よくあるエラーと検証方法
iBarType の組み込みでつまずきやすいエラーは大きく分けて 4 つあります。第 1 は「シンボル名のスペル違い」です。”USDJPY” を “USDJPY.” や “USD.JPY” と書いてしまうと iOpen が 0 を返し、iBarType は常に 0(同事)を返す挙動になります。動作確認時にすべて 0 が返るならまずシンボル名を疑います。
第 2 は「シフト値が iBars を超えるケース」です。ヒストリカルデータの本数が少ない通貨ペアでは、シフト 1000 の参照などで 0 が返ります。iBars(symbol, timeframe) で利用可能本数を事前に確認し、それ以下のシフトしか渡さないように呼び出し側でガードします。
第 3 は「未確定足の参照」です。シフト 0 は確定していないため、ティックが進むたびに iBarType の戻り値が陽線→陰線→同事と変化します。これは仕様ですが、シグナル系ロジックで使うと判定が安定しません。シグナル目的の参照は必ずシフト 1 以上を使うのが安全です。
第 4 は「PERIOD_CURRENT の挙動」です。timeframe に NULL や 0 を渡すと、MT4 は PERIOD_CURRENT として現在チャートの時間足を採用します。複数の時間足をまたぐロジックを書く際に意図しない参照を起こす原因になるため、明示的に PERIOD_M5 などを指定する習慣をつけるとミスを減らせます。
浮動小数点の厳密な等号比較は、業者やヒストリカルデータの提供精度によって挙動が変わります。同事の検出率が想定と大きくずれる場合は、_Point を使った閾値比較に置き換え、サンプル件数の差分を必ず記録しておくことをおすすめします。
検証はバックテスト機能(ストラテジーテスター)でも可能です。EA に iBarType を組み込み、Print で各バーの判定結果を出力し、エクスポートした CSV で陽線・陰線・同事の本数比率を集計します。trade-engineer.com の検証では、USDJPY 5 分足 2024 年 1 年間で陽線 48.7%・陰線 48.3%・同事 3.0% という分布が出ており、この程度の比率に収まれば実装は正しく動いていると判断できます。
まとめ:iBarTypeの応用展開
iBarType 関数を導入することで、ロウソク足の方向判定が一行で記述でき、ロジックの可読性と保守性が大きく向上します。今回の実装では戻り値を 0(同事)・1(陽線)・2(陰線)の 3 値で設計し、引数も他の i 系関数と揃えました。コードは 20 行に満たないシンプルさながら、トレード判定の基盤として広範に応用が効きます。
応用展開としてまず考えられるのは、N 本連続陽線・連続陰線の検出関数 iBarStreak の実装です。iBarType を for ループで連続呼び出しし、同じ値が続く本数を返せば実装できます。さらに同事の扱いをスキップするか連続途切れと見なすかでロジックの厚みが変わります。
次に、実体の長さや上ヒゲ・下ヒゲの比率を返す iBarBody や iBarWick 系の関数も同じ思想で作れます。iHigh・iLow を組み合わせれば、ロウソク足の形状を数値化した「形状特徴量」を簡単に量産できます。シグナルとして矢印を表示するところまで含めた応用は アローの出し方 にまとめていますので、iBarType の戻り値と組み合わせて矢印を出すロジックを試したい方は併せて参照してください。
バックテスト面では、iBarType を使った判定モジュールを EA に組み込み、エントリー方向と判定結果を突き合わせて勝率と期待値を集計するパターンが定番です。trade-engineer.com で公開しているロジック検証もこの構造を採用しており、初回検証から再現性のある数値が取れます。
最後に、MQL5 への移植性を意識しておくと、将来 MT5 への切り替え時にも資産が活きます。iOpen・iClose の MQL5 版(CopyOpen / CopyClose)に置き換えれば、ほぼ同じインターフェースで再利用可能です。今後 MT5 対応を検討しているなら、最初から ENUM_TIMEFRAMES と関数化された判定処理を徹底しておくとスムーズです。iBarType を起点に、自分専用のロウソク足分析ライブラリを少しずつ育てていきましょう。


コメント