MQL4のiCustom関数は、自作インジケーターの計算結果を、
そのまま数値として返してくれます。
ところがMQL4のコードをそのままMQL5に持ち込んでiCustomを呼ぶと、
返ってくるのは価格でも指標値でもない、意味不明な小さい整数です。
これはバグではなく、MQL5のiCustomが「値」ではなく「ハンドル」
しか返さない仕様に変わったためです。
この記事では、MQL5でiCustomからハンドルを受け取り、
CopyBufferで実際の値を取り出すまでの一連の流れを、
自作インジケーターと呼び出し側スクリプトの両方の完全なコードで解説します。
設計思想: なぜMQL5はハンドル方式に変えたのか
MQL4のiCustomは、呼び出すたびに指定したインジケーターの計算をその場で走らせ、
指定したバッファ・シフトの値を直接返していました。
この方式は書く側は楽ですが、
同じインジケーターを何度も呼び出すたびに、
毎回ゼロから再計算が走るという無駄がありました。
MQL5では、iMAやiRSIなど標準インジケーターの呼び出しが、
すべて「ハンドルを取得してからCopyBufferで値を読む」
という2段階方式に統一されており、iCustomも同じ設計に合わせられています。
ハンドルは一度だけ取得してOnInit()等で保持しておけば、
その後は同じ計算結果をCopyBufferで何度でも安く読み出せるという利点があります。
これはCopyBuffer関数の解説記事で扱った、
標準インジケーターの仕組みと全く同じ考え方です。
iCustom関数の書式(MQL5版)
int iCustom( string symbol, // 通貨ペア。NULLで現在のチャート ENUM_TIMEFRAMES period, // 時間足。PERIOD_CURRENTで現在の時間足 string name, // カスタムインジケーター名(拡張子なし) ... // カスタムインジケーターのinput引数(省略可) );
戻り値はintのハンドルで、取得に失敗すると`INVALID_HANDLE`が返ります。
第3引数のnameには、
MQL5\Indicatorsフォルダを基準にしたファイル名(拡張子なし)を渡します。
サブフォルダに入れている場合は、
`”MyFolder\\MyIndicator”`のようにパスごと指定します。
呼び出される側: 自作インジケーターSimpleSMA
まず、iCustomから呼び出す側の、
シンプルな単純移動平均を計算するだけの自作インジケーターを用意します。
このファイルを先にコンパイルし、
MQL5\Indicatorsフォルダに保存しておく必要があります。
//+------------------------------------------------------------------+
//| SimpleSMA.mq5 |
//| 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 indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrDodgerBlue
input int MaPeriod = 20;//移動平均の期間
double SmaBuffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, SmaBuffer, INDICATOR_DATA);
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, MaPeriod);
IndicatorSetString(INDICATOR_SHORTNAME, "SimpleSMA(" + IntegerToString(MaPeriod) + ")");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| 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[])
{
if(rates_total < MaPeriod)
return(0);
int start = (prev_calculated > 0) ? prev_calculated - 1 : MaPeriod - 1;
for(int i = start; i < rates_total; i++)
{
double sum = 0;
for(int j = 0; j < MaPeriod; j++)
sum += close[i - j];
SmaBuffer[i] = sum / MaPeriod;
}
return(rates_total);
}
//+------------------------------------------------------------------+
OnCalculate()の配列は、
MQL5では既定でインデックスが昇順(0が一番古い足)になっている点がMQL4と逆なので、
`close[i - j]`のようにiより過去を指すには添字を引き算します。
呼び出す側: iCustomとCopyBufferでSimpleSMAの値を読む
次に、上のSimpleSMAをiCustomで呼び出し、
CopyBufferで最新3本分の値を取得するスクリプトです。
//+------------------------------------------------------------------+
//| iCustomDemo.mq5 |
//| 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 script_show_inputs
input int MaPeriod = 20;//SimpleSMA側に渡す期間(SimpleSMAのinputと合わせる)
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
int handle = iCustom(_Symbol, _Period, "SimpleSMA", MaPeriod);
if(handle == INVALID_HANDLE)
{
Print("iCustomのハンドル取得に失敗しました。エラーコード=", GetLastError());
return;
}
double buf[];
ArraySetAsSeries(buf, true);
int copied = CopyBuffer(handle, 0, 0, 3, buf);
if(copied <= 0)
{
Print("CopyBufferに失敗しました。エラーコード=", GetLastError());
IndicatorRelease(handle);
return;
}
Print("SimpleSMA 現在値=", buf[0], " 1本前=", buf[1], " 2本前=", buf[2]);
IndicatorRelease(handle);
}
//+------------------------------------------------------------------+
CopyBuffer()に渡す配列は、
`ArraySetAsSeries(buf, true)`で、
「0番目=最新」の並びに変えているため、
SimpleSMA.mq5側の昇順とは向きが逆になります。
同じプログラムの中でも、
OnCalculate内の配列と、
CopyBufferで受け取る配列とで向きが違うという点は、
MQL5を書き始めた人が混乱しやすいポイントです。
MetaEditorで実際に動かす手順
- MetaEditorで新規カスタムインジケーターを作成し、名前を「SimpleSMA」にしてテンプレートの中身を上のコードに置き換え、F7でコンパイルする
- MT5のナビゲータの「インジケーター」欄にSimpleSMAが表示されることを確認する(チャートに適用しなくてよい)
- MetaEditorで新規スクリプトを作成し、名前を「iCustomDemo」にしてテンプレートの中身を上のコードに置き換え、F7でコンパイルする
- チャートへiCustomDemoをドラッグ&ドロップして実行し、「エキスパート」タブにSimpleSMAの直近3本の値がPrint出力されることを確認する
コピペして動かないときに確認すること
- SimpleSMA.mq5を先にコンパイルしてMQL5\Indicatorsフォルダに置いたか。iCustomは実行時にコンパイル済みの.ex5ファイルを名前で探すため、ソースコードが同じフォルダにあるだけでは見つからない
- iCustomに渡すinput引数の順序・個数が、SimpleSMA側のinput宣言順と一致しているか。ずれていても文法エラーにはならず、意図しない期間で計算されるだけなので気づきにくい
- handleが有効でもCopyBufferの戻り値がマイナスや0になる場合、履歴データがまだ読み込み中の可能性がある。数ティック待ってから再試行するか、待機用の再試行ループを入れる
- MQL4の`iCustom(NULL,0,"SimpleSMA",20,0,0)`のように末尾に「バッファ番号,シフト」を書く古い呼び出し方をMQL5にそのまま持ち込むと、余分な引数がinputに渡ってしまい正しく動かない
まとめ
MQL4のiCustomは自作インジケーターの値をその場で直接返しますが、
MQL5のiCustomはハンドルしか返さず、
値を読むにはCopyBufferをもう1段呼ぶ必要があります。
この2段階方式は書く手間が増える代わりに、
同じ計算結果を何度も安く読み出せる利点があります。
ハンドルを使い終えたらIndicatorRelease()で解放する習慣も、
CopyBuffer関数や、
IndicatorCreate/IndicatorRelease関数の記事と合わせて、
身につけておくと安心です。
コメント