【検証#09】iCustomの引数省略は超危険!¶
この記事の3行まとめ
- 🚨 MQL4の
iCustomで引数を省略すると未定義動作を引き起こす - 💀 「動作するマシン」と「動作しないマシン」が出る最悪のバグパターン
- ✅ MQL5ではハンドル方式になり問題は軽減(でも注意は必要)
はじめに¶
この記事は、「とあるMetaTraderの備忘秘録」様のブログ記事を検証・紹介するシリーズの第9弾です。
今回は発見が困難な「再現性のないバグ」の原因として知られる iCustom の罠を解説します。
元ネタ
iCustom のアレの話。
https://fai-fx.hatenadiary.org/entry/20100413/1271093279
(2010年4月13日 公開)
時代背景に関する注意
この記事の元となるブログ記事は2010年頃に公開されたものです。
MQL5では iCustom がハンドルを返す仕様に変更され、値の取得は CopyBuffer を使用するため、問題の発生パターンは異なりますが、引数省略のリスクは依然として存在します。
iCustomとは¶
iCustom は、カスタムインジケータの値を取得する関数です。
MQL4構文¶
double iCustom(
string symbol, // 通貨ペア
int timeframe, // 時間足
string name, // インジケータ名
... // インジケータのパラメータ群
int mode, // バッファ番号
int shift // シフト
);
問題:可変長引数¶
... の部分が可変長引数になっており、インジケータのパラメータを自由に渡せます。
これが悲劇の始まりです。
危険なコード例¶
インジケータ側(MyIndicator.mq4)¶
// 3つのパラメータを持つインジケータ
extern int Period1 = 14;
extern int Period2 = 26;
extern double Factor = 1.5;
EA側の呼び出し(危険)¶
// ★危険★ 引数を一部省略
double value = iCustom(NULL, 0, "MyIndicator",
14, // Period1のみ指定
// Period2とFactorを省略!
0, 0); // mode, shift
何が起こるか
省略した Period2 と Factor にはメモリ上の不定値が使われます。
- マシンAでは偶然正しい値が入り動作 - マシンBではゴミデータが入り誤動作 - 再起動すると結果が変わる
なぜこれが最悪なのか¶
1. 再現性がない¶
開発者のPCでは動作するが、ユーザーのPCでは動作しない。
2. 発見が困難¶
コンパイルエラーも実行時エラーも出ない。
3. 間欠的に発生¶
「昨日は動いたのに今日は動かない」という現象。
MQL5での変更¶
MQL5では iCustom の仕様が変更されました。
MQL5構文¶
int iCustom(
string symbol, // 通貨ペア
ENUM_TIMEFRAMES period, // 時間足
string name, // インジケータ名
... // パラメータ群(可変長)
);
MQL5ではハンドルを返す方式になり、値の取得には CopyBuffer を使います。
int handle = iCustom(_Symbol, PERIOD_CURRENT, "MyIndicator", 14, 26, 1.5);
double buffer[];
CopyBuffer(handle, 0, 0, 1, buffer); // バッファ0のshift 0を取得
MQL5でも注意が必要¶
可変長引数自体は残っているため、引数の省略は依然として危険です。
MQL5での検証コード¶
//+------------------------------------------------------------------+
//| iCustom_Trap.mq5 |
//| 「とあるMetaTraderの備忘秘録」様の記事を検証 |
//| https://fai-fx.hatenadiary.org/entry/20100413/1271093279 |
//+------------------------------------------------------------------+
#property copyright "FXおもしろラボ"
#property link "https://fx-omoshiro-lab.com/"
#property version "1.00"
#property script_show_inputs
void OnStart()
{
Print("=== iCustom 引数問題の解説 ===");
Print("");
Print("このスクリプトは概念説明用です。");
Print("実際のインジケータは含まれていません。");
Print("");
// 正しい呼び出し例(RSI)
Print("--- 正しい呼び出し例 ---");
int rsiHandle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);
if(rsiHandle != INVALID_HANDLE)
{
double rsiBuffer[];
ArraySetAsSeries(rsiBuffer, true);
CopyBuffer(rsiHandle, 0, 0, 1, rsiBuffer);
PrintFormat("RSI(14) = %.2f", rsiBuffer[0]);
IndicatorRelease(rsiHandle);
}
Print("");
Print("--- iCustom使用時の注意点 ---");
Print("1. インジケータのパラメータ数を正確に把握する");
Print("2. すべてのパラメータを明示的に指定する");
Print("3. パラメータの型(int/double/string)を正しく指定する");
Print("4. デフォルト値であっても省略しない");
Print("");
Print("--- 危険なパターン例 ---");
Print("// インジケータが3つのパラメータを要求するのに2つしか渡さない");
Print("// int h = iCustom(_Symbol, PERIOD_H1, \"SomeIndicator\", 14, 26);");
Print("// ↑ 3番目のパラメータは不定値になる!");
Print("");
Print("--- 安全なパターン ---");
Print("// すべてのパラメータを明示的に指定");
Print("// int h = iCustom(_Symbol, PERIOD_H1, \"SomeIndicator\", 14, 26, 1.5);");
Print("");
Print("=== 解説完了 ===");
}
チェックリスト¶
iCustom使用時の確認事項¶
| # | 確認項目 |
|---|---|
| 1 | インジケータのソースコードでパラメータ数を確認したか? |
| 2 | すべてのパラメータを明示的に指定したか? |
| 3 | パラメータの型(int/double/string/bool)は正しいか? |
| 4 | デフォルト値でも省略せず記述したか? |
| 5 | 複数のマシンで動作確認したか? |
まとめ¶
| ポイント | 内容 |
|---|---|
| 問題 | iCustomの引数省略で未定義動作 |
| 症状 | マシンによって動作が異なる |
| 原因 | 可変長引数でメモリの不定値を参照 |
| 対策 | すべての引数を明示的に指定 |
| MQL5 | ハンドル方式に変更、だが注意は必要 |
オリジナル記事への謝辞
この記事は「とあるMetaTraderの備忘秘録」様の貴重な知見をもとに、
MQL5での検証と解説を加えたものです。
オリジナル記事に心より感謝いたします。
ソースコードのダウンロード¶
この記事で紹介したコードをダウンロードできます。
ファイルの種類:スクリプト
保存先: MQL5/Scripts/ フォルダ
使い方: MT5のナビゲーターから「スクリプト」を展開し、チャートにドラッグ&ドロップで実行
関連用語¶
この記事で登場した用語は用語集でも解説しています。