コンテンツにスキップ

【検証#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構文

MQL
double iCustom(
   string       symbol,           // 通貨ペア
   int          timeframe,        // 時間足
   string       name,             // インジケータ名
   ...                            // インジケータのパラメータ群
   int          mode,             // バッファ番号
   int          shift             // シフト
);

問題:可変長引数

... の部分が可変長引数になっており、インジケータのパラメータを自由に渡せます。

これが悲劇の始まりです。


危険なコード例

インジケータ側(MyIndicator.mq4)

MQL
// 3つのパラメータを持つインジケータ
extern int    Period1 = 14;
extern int    Period2 = 26;
extern double Factor  = 1.5;

EA側の呼び出し(危険)

MQL
// ★危険★ 引数を一部省略
double value = iCustom(NULL, 0, "MyIndicator",
                       14,      // Period1のみ指定
                       // Period2とFactorを省略!
                       0, 0);   // mode, shift

何が起こるか

省略した Period2Factor にはメモリ上の不定値が使われます。
- マシンAでは偶然正しい値が入り動作 - マシンBではゴミデータが入り誤動作 - 再起動すると結果が変わる


なぜこれが最悪なのか

1. 再現性がない

開発者のPCでは動作するが、ユーザーのPCでは動作しない。

2. 発見が困難

コンパイルエラーも実行時エラーも出ない。

3. 間欠的に発生

「昨日は動いたのに今日は動かない」という現象。


MQL5での変更

MQL5では iCustom の仕様が変更されました。

MQL5構文

MQL
int iCustom(
   string           symbol,        // 通貨ペア
   ENUM_TIMEFRAMES  period,        // 時間足
   string           name,          // インジケータ名
   ...                             // パラメータ群(可変長)
);

MQL5ではハンドルを返す方式になり、値の取得には CopyBuffer を使います。

MQL
int handle = iCustom(_Symbol, PERIOD_CURRENT, "MyIndicator", 14, 26, 1.5);
double buffer[];
CopyBuffer(handle, 0, 0, 1, buffer);  // バッファ0のshift 0を取得

MQL5でも注意が必要

可変長引数自体は残っているため、引数の省略は依然として危険です。


MQL5での検証コード

MQL
//+------------------------------------------------------------------+
//|                                          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のナビゲーターから「スクリプト」を展開し、チャートにドラッグ&ドロップで実行

09_iCustom_Trap.mq5 をダウンロード


関連用語

この記事で登場した用語は用語集でも解説しています。