最終更新: 2026年6月
「既製のインジケーターでは自分の戦略を表現しきれない」と感じたとき、カスタムインジケーター開発が現実的な選択肢になる。
MQL4のカスタムインジケーターは、EAより習得の入口が低い。EAのように注文処理の複雑さはなく、「計算した値をチャートに描画する」という目的に絞られているからだ。本記事では、構造の理解から最初のインジケーター実装・チャートへの表示まで、一通りの手順を示す。
カスタムインジケーターとEAの違い
MQL4の開発対象は3種類ある。
| 種別 | 役割 | 難易度 | |---|---|---| | EA(Expert Advisor) | 自動売買の実行 | 高(注文処理が複雑) | | カスタムインジケーター | チャートへの描画 | 中(計算と描画が中心) | | スクリプト | 一回限りの処理 | 低 |
カスタムインジケーターはEAに比べて「やること」がシンプルだ。毎ティック(またはバーの確定時)に計算し、結果をバッファに書き込むと描画される。注文の送信や口座残高の操作は不要。
カスタムインジケーターの基本構造
MQL4のカスタムインジケーターには、必ず以下の要素が存在する。
プロパティ宣言
#property indicator_chart_window // チャートウィンドウ上に描画
// または
#property indicator_separate_window // 別ウィンドウ(サブウィンドウ)に描画
#property indicator_buffers 1 // バッファ数を宣言
#property indicator_color1 Blue // バッファ1の色
バッファ配列の宣言と登録
double Buffer1[]; // バッファとして使う配列を宣言
int OnInit() {
IndicatorBuffers(1); // 使用バッファ数の宣言(最大8)
SetIndexBuffer(0, Buffer1); // 配列をバッファ0番として登録
SetIndexStyle(0, DRAW_LINE); // 描画スタイルを線に設定
SetIndexLabel(0, "MyLine"); // レジェンドのラベル名
return(INIT_SUCCEEDED);
}
OnCalculate関数
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[]) {
// 計算ループ
for (int i = prev_calculated; i < rates_total; i++) {
// ここに計算ロジックを書く
Buffer1[i] = close[i]; // 例: 終値をそのままバッファに入れる
}
return(rates_total);
}
OnCalculate はティックごとに呼び出される。prev_calculated は「前回の呼び出しで計算済みのバー数」で、これを使うと未計算分だけを処理できる(全バーを毎回再計算する無駄を省ける)。
最初の実装:シンプルな移動平均線インジケーター
既存の iMA() 関数を使って独自の移動平均線を描画するインジケーターを作ってみる。「自作」と言っても、組み込み関数を使って計算を楽にしながら描画スタイルをカスタマイズするという実装だ。
// シンプルカスタム移動平均インジケーター(MQL4)
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1 DodgerBlue
#property indicator_width1 2
input int MAPeriod = 20; // 移動平均の期間
input int MAShift = 0; // シフト
double MABuffer[];
int OnInit() {
IndicatorBuffers(1);
SetIndexBuffer(0, MABuffer);
SetIndexStyle(0, DRAW_LINE);
SetIndexLabel(0, "Custom MA(" + (string)MAPeriod + ")");
SetIndexShift(0, MAShift);
return(INIT_SUCCEEDED);
}
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 start = MAPeriod - 1; // 移動平均の計算に必要な最低バー数
if (prev_calculated > start) start = prev_calculated;
for (int i = start; i < rates_total; i++) {
MABuffer[i] = iMA(NULL, 0, MAPeriod, 0, MODE_SMA, PRICE_CLOSE, rates_total - 1 - i);
}
return(rates_total);
}
このコードは「組み込みの iMA() 関数を内部で呼び出し、結果を自分のバッファに書き込む」という構造だ。見た目は既存の移動平均線と同じだが、線の色・太さ・ラベルを好きにカスタマイズできる。
独自計算を実装する:終値の平均を手計算する例
組み込み関数に頼らず、自分でアルゴリズムを書く例を示す。
// 手計算の単純移動平均インジケーター(OnCalculate版)
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1 OrangeRed
input int Period = 14;
double SMABuffer[];
int OnInit() {
IndicatorBuffers(1);
SetIndexBuffer(0, SMABuffer);
SetIndexStyle(0, DRAW_LINE);
SetIndexLabel(0, "HandSMA(" + (string)Period + ")");
return(INIT_SUCCEEDED);
}
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[]) {
// close[]は古い順(インデックス0が最古)のため注意
int start = (prev_calculated > Period) ? prev_calculated : Period;
for (int i = start; i < rates_total; i++) {
double sum = 0;
for (int j = 0; j < Period; j++) {
sum += close[i - j];
}
SMABuffer[i] = sum / Period;
}
return(rates_total);
}
重要な注意点が一つある。OnCalculate に渡される close[] 配列は、インデックス0が最古のバーで、最後のインデックスが最新バーという順序になっている(従来の Close[] グローバル配列とは逆順)。この点を誤解するとバッファの値がずれる。
コンパイルとチャートへの表示
手順1: MetaEditorでコンパイルする
MetaEditorの「F7キー(コンパイル)」を押して、エラーが出ないことを確認する。コンパイル成功後、MQL4/Indicators/ フォルダに .ex4 ファイルが生成される。
手順2: MT4でチャートに表示する
MT4の「ナビゲーター → インジケーター」を右クリックして「更新」を選ぶ。新しいインジケーターが一覧に現れたら、チャートにドラッグ&ドロップして表示される。
手順3: iCustom関数でEAから呼び出す
作ったカスタムインジケーターをEAから使いたい場合は iCustom() 関数を使う。
// EAからカスタムインジケーターの値を取得する
double ma_value = iCustom(_Symbol, 0, "MyCustomMA", // インジケーター名(.ex4なし)
20, // インジケーターのパラメーター(Period)
0, // バッファインデックス
1); // シフト(1=1本前のバー)
第3引数がインジケーター名、その後ろにインジケーターのパラメーターを順番に並べ、最後から2番目がバッファインデックス(何番目のバッファか)、最後がシフト数だ。
Claudeと会話しながらインジケータが作れるhedgrow-fxでは、こうしたカスタムインジケーターの設計・計算ロジックの実装をAIと対話しながら進められる。「このロジックをコードにしたい」という段階からの壁打ちに使えるツールだ。
よくある実装ミスと対処法
バッファの値が描画されない
IndicatorBuffers() の宣言数と、実際に SetIndexBuffer() で登録したバッファ数が一致していない場合に起きる。使う配列の数と IndicatorBuffers() の引数を必ず一致させる。
最初の数バーのみ描画される / 逆向きに描画される
OnCalculate の close[] 配列が「古い順」であることを忘れて、ループのインデックスを逆にしてしまうミス。インデックス0が最古であることを意識して実装する。
コンパイルは通るが値がおかしい
数値が期待値から大きくずれている場合、配列のインデックスが範囲外を参照している可能性がある。i - j が負にならないように start の設定を確認する。
よくある質問(FAQ)
Q: インジケーターのバッファは何個まで使えますか?
A: MQL4では最大8バッファまで使用できます。複数の線を描画したい場合は、バッファを複数宣言して SetIndexBuffer() で登録してください。
Q: カスタムインジケーターをEAから呼び出す際、インジケーター名に日本語は使えますか? A: 技術的には可能ですが、文字コードの問題でエラーになることがあります。インジケーター名はアルファベット・数字・アンダースコアのみ使用を推奨します。
Q: OnCalculate と OnTick は何が違いますか?
A: カスタムインジケーターでは OnCalculate を使います。OnTick はEAで使う関数です。インジケーターに OnTick を書いても動作しません(コンパイルエラーにもならないため気づきにくい)。
Q: チャートに表示した後にパラメーターを変えたいのですが。
A: チャート上のインジケーター名をダブルクリック(または右クリック→「プロパティ」)で設定画面が開き、input 変数の値を変更できます。
Q: 作ったインジケーターを他の人に配布できますか?
A: .ex4 ファイルを配布すればコードを見せずに使ってもらえます。ソースコードを共有したい場合は .mq4 ファイルを渡します。
免責事項: 本記事のコード例は学習目的のものです。実際の取引への適用はデモ口座で十分な検証を行ってから実施してください。FX取引には元本割れリスクがあります。
著者情報: 金融工学専攻・アルゴリズム取引研究者(EA・インジケーター開発歴8年)
