【MQL4】デバッグに必須!Print・Alert・Comment・GetLastError・ResetLastErrorの使い方を徹底解説

【中級編】MQLプログラムの読み方・書き方

MQL4でEA(自動売買)やインジケーターを開発するとき、「思った通りに動かない」「どこでエラーが出ているかわからない」という場面は必ず訪れます。そんなときに頼りになるのがデバッグ用の関数たちです。

この記事では、MQL4開発で必須となる以下の5つ(+α)の関数について、基本的な使い方から実践的な注意点まで徹底的に解説します。

  • Print() — ログへの出力
  • Comment() — チャート上への表示
  • Alert() — ポップアップ通知
  • GetLastError() — エラーコードの取得
  • ResetLastError() — エラーコードのリセット

Print関数 — エキスパートログへの出力

基本的な使い方

Print関数は、MetaTraderの「エキスパート」タブ(ログ)にメッセージを出力する、最も基本的なデバッグ関数です。開発中に変数の値を確認したり、処理の流れを追跡したりするのに欠かせません。

void OnTick()
{
    double currentPrice = Ask;
    int spread = MarketInfo(Symbol(), MODE_SPREAD);

    Print("現在のAsk価格: ", currentPrice);
    Print("スプレッド: ", spread, " ポイント");
    Print("通貨ペア: ", Symbol(), " 時間足: ", Period());
}

Print関数はカンマ区切りで複数の値を渡すことができ、文字列・整数・小数点数などさまざまなデータ型を自動的に文字列に変換して出力してくれます。

データ型ごとの表示仕様

Print関数で各データ型を出力するときの仕様を覚えておきましょう。

void OnInit()
{
    // 整数型:そのまま数値で表示
    int intValue = 12345;
    Print("int型: ", intValue);           // → int型: 12345

    // double型:小数点以下の桁数は自動調整
    double dblValue = 1.23456789;
    Print("double型: ", dblValue);        // → double型: 1.23456789

    // bool型:trueまたはfalseで表示
    bool boolValue = true;
    Print("bool型: ", boolValue);         // → bool型: true

    // datetime型:日時形式で表示
    datetime dtValue = TimeCurrent();
    Print("datetime型: ", dtValue);       // → datetime型: 2024.01.15 12:30:00
}

出力先について

Print関数の出力先は2か所あります。

  • エキスパートタブ:MetaTrader画面下部の「エキスパート」タブにリアルタイム表示
  • ログファイル:MQL4/Logs フォルダ内にテキストファイルとしても記録される

ログファイルに残るため、後から問題を調査するときにも役立ちます。ただし、OnTick内で大量にPrintを呼ぶとパフォーマンスに影響する場合があるので、本番稼働時には不要なPrintは削除またはコメントアウトしましょう。

Comment関数 — チャート画面への表示

基本的な使い方

Comment関数は、チャートの左上にテキストを表示する関数です。リアルタイムで変数の値を視覚的に確認したいときに便利です。

void OnTick()
{
    double bid = Bid;
    double ask = Ask;
    int spread = (int)((ask - bid) / Point);

    Comment("Bid: ", bid, "\n",
            "Ask: ", ask, "\n",
            "Spread: ", spread, " points\n",
            "Time: ", TimeToStr(TimeCurrent(), TIME_SECONDS));
}

改行するには「\n」を使います。これにより、複数行にわたる情報をきれいに表示できます。

Comment関数の重要な注意点

Comment関数には大きな特徴(欠点)があります。最後に呼び出したComment関数の内容で上書きされるという点です。

void OnTick()
{
    Comment("1行目のメッセージ");
    Comment("2行目のメッセージ");  // ← これだけが表示される!
}

上記の場合、チャートには「2行目のメッセージ」しか表示されません。複数の情報を表示したい場合は、1回のComment呼び出しにすべての情報をまとめる必要があります。

また、Comment関数の表示をクリア(消去)したい場合は、空文字を渡します。

Comment("");  // チャート左上の表示をクリア

Alert関数 — ポップアップ通知

基本的な使い方

Alert関数は、ポップアップダイアログを表示して、音とともにユーザーに通知する関数です。重要なイベント(エントリーシグナル発生、エラー検知など)の通知に使います。

void OnTick()
{
    static datetime lastAlertTime = 0;
    datetime currentTime = TimeCurrent();

    // RSIが30以下になったらアラート(同じ足で1回だけ)
    double rsi = iRSI(Symbol(), 0, 14, PRICE_CLOSE, 0);

    if(rsi < 30.0 && lastAlertTime != iTime(Symbol(), 0, 0))
    {
        Alert(Symbol(), " RSIが30以下です! RSI=", NormalizeDouble(rsi, 2));
        lastAlertTime = iTime(Symbol(), 0, 0);
    }
}

Alert関数の注意点

Alert関数には以下の注意点があります。

  • バックテストでは動作しない:ストラテジーテスターでのバックテスト時にはAlertは表示されません。バックテスト中のデバッグにはPrint関数を使いましょう。
  • 日本語の一部が文字化けする場合がある:環境によっては日本語表示が正しくされないことがあります。重要な情報は英数字で出力するのが安全です。
  • 連続呼び出しに注意:OnTick内で条件なしにAlertを呼ぶと、ティックごとにポップアップが出て操作不能になります。必ず条件制御を入れましょう。

GetLastError関数 — エラーコードの取得

基本的な使い方

GetLastError関数は、直前の操作で発生したエラーのコード(番号)を取得する関数です。注文送信やファイル操作などの後にエラーチェックを行う際に使います。

void OnTick()
{
    int ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, 0, 0, "Test", 0, 0, clrBlue);

    if(ticket < 0)
    {
        int errorCode = GetLastError();
        Print("注文エラー! エラーコード: ", errorCode);
    }
    else
    {
        Print("注文成功! チケット番号: ", ticket);
    }
}

よく見るエラーコード一覧

以下は、MQL4開発でよく遭遇するエラーコードの一覧です。

エラーコード 定数名 意味
0 ERR_NO_ERROR エラーなし
1 ERR_NO_RESULT エラーはないが結果が不明
2 ERR_COMMON_ERROR 一般的なエラー
6 ERR_NO_CONNECTION サーバーとの接続がない
130 ERR_INVALID_STOPS ストップが無効(SL/TPが近すぎる等)
131 ERR_INVALID_TRADE_VOLUME 取引量が無効
134 ERR_NOT_ENOUGH_MONEY 資金不足
138 ERR_REQUOTE リクオート
146 ERR_TRADE_CONTEXT_BUSY トレードコンテキストがビジー
4109 ERR_TRADE_NOT_ALLOWED 自動売買が許可されていない

【超重要】GetLastErrorは2度呼ぶとリセットされる!

GetLastError関数には非常に重要な仕様があります。一度呼び出すと、内部のエラーコードが0にリセットされてしまうのです。

// ★ ダメな例
int ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, 0, 0, "", 0, 0, clrBlue);
if(ticket < 0)
{
    Print("エラーコード: ", GetLastError());   // ← 1回目:正しいエラーコードが取得できる
    Print("エラーコード: ", GetLastError());   // ← 2回目:0が返る!(リセット済み)
}
// ★ 正しい例:変数で受けてから使う
int ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, 0, 0, "", 0, 0, clrBlue);
if(ticket < 0)
{
    int err = GetLastError();  // まず変数に格納!
    Print("エラーコード: ", err);
    Alert("注文失敗 エラー: ", err);  // 何度でも使える
}

これはMQL4開発でよくあるミスです。GetLastError()は必ず一度だけ呼び出して変数に保存してから使いましょう。

ResetLastError関数 — エラーコードのリセット

ResetLastError関数は、定義済み変数_LastErrorに格納されているエラーコードを0(エラーなし)にリセットするための関数です。

void OnTick()
{
    // エラーコードを事前にリセット
    ResetLastError();

    int ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, 0, 0, "Test", 0, 0, clrBlue);

    if(ticket < 0)
    {
        int err = GetLastError();
        Print("注文エラー: ", err);
    }
}

なぜリセットが必要なのか?

GetLastError()は「最後に発生したエラー」を返します。つまり、以前の処理で発生したエラーコードが残っている可能性があるのです。特定の処理のエラーだけを正確に検知したい場合は、処理の直前にResetLastError()を呼ぶのが定石です。

// 定石パターン:リセット → 処理 → エラーチェック
ResetLastError();           // ① リセット
bool result = OrderClose(ticket, lots, price, 3, clrRed);  // ② 処理実行
int err = GetLastError();   // ③ エラー取得(変数に格納)
if(err != 0)
{
    Print("決済エラー: ", err);
}

ErrorDescription関数 — エラーコードを文章に変換

エラーコードの番号だけでは、何が起きたのかすぐにはわかりません。そこで便利なのがErrorDescription関数です。この関数を使うと、エラーコードを人間が読める英語の文章に変換できます。

ただし、この関数はMQL4に標準で組み込まれているわけではなく、stdlib.mqhというライブラリファイルをインクルードする必要があります。

#include <stdlib.mqh>  // ErrorDescription関数を使うために必要

void OnTick()
{
    ResetLastError();

    int ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, 0, 0, "Test", 0, 0, clrBlue);

    if(ticket < 0)
    {
        int err = GetLastError();
        Print("エラーコード: ", err, " 内容: ", ErrorDescription(err));
        // 例:エラーコード: 134 内容: not enough money
    }
}

エラーの原因がすぐにわかるため、デバッグの効率が格段に上がります。ぜひstdlib.mqhのインクルードを習慣にしましょう。

実践例 — 5つの関数を組み合わせたデバッグ付きEA

ここまで学んだ関数を組み合わせた、実践的なサンプルコードをご紹介します。

#include <stdlib.mqh>

input double LotSize = 0.1;
input int MagicNumber = 12345;

void OnTick()
{
    // 現在の状態をチャートに表示
    Comment("=== デバッグ情報 ===\n",
            "通貨ペア: ", Symbol(), "\n",
            "Bid: ", Bid, "  Ask: ", Ask, "\n",
            "スプレッド: ", MarketInfo(Symbol(), MODE_SPREAD), "\n",
            "保有ポジション: ", CountOrders(), "\n",
            "口座残高: ", AccountBalance(), "\n",
            "更新時刻: ", TimeToStr(TimeCurrent(), TIME_SECONDS));

    // 簡単な売買ロジック(例:移動平均のクロス)
    double maFast = iMA(Symbol(), 0, 10, 0, MODE_SMA, PRICE_CLOSE, 0);
    double maFastPrev = iMA(Symbol(), 0, 10, 0, MODE_SMA, PRICE_CLOSE, 1);
    double maSlow = iMA(Symbol(), 0, 25, 0, MODE_SMA, PRICE_CLOSE, 0);
    double maSlowPrev = iMA(Symbol(), 0, 25, 0, MODE_SMA, PRICE_CLOSE, 1);

    // ゴールデンクロスで買い
    if(maFastPrev <= maSlowPrev && maFast > maSlow && CountOrders() == 0)
    {
        Print("ゴールデンクロス検出! 買い注文を送信します。");
        Print("MA(10)=", maFast, " MA(25)=", maSlow);

        ResetLastError();  // エラーをリセット
        int ticket = OrderSend(Symbol(), OP_BUY, LotSize, Ask, 3, 0, 0,
                               "MA Cross Buy", MagicNumber, 0, clrBlue);

        int err = GetLastError();  // 変数に格納(1回だけ呼ぶ!)

        if(ticket >= 0)
        {
            Print("注文成功! チケット: ", ticket);
        }
        else
        {
            Print("注文失敗! エラーコード: ", err,
                  " 内容: ", ErrorDescription(err));
            Alert("注文失敗! Error: ", err, " ", ErrorDescription(err));
        }
    }
}

// 保有ポジション数をカウントする関数
int CountOrders()
{
    int count = 0;
    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
            if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
                count++;
        }
    }
    return count;
}

このサンプルでは、以下のようにデバッグ関数を使い分けています。

  • Comment:リアルタイムの状態監視(チャート上に常時表示)
  • Print:処理の流れやシグナル検出の記録(ログに残る)
  • Alert:注文失敗などの重要イベントの即時通知
  • ResetLastError → GetLastError:正確なエラーコード取得の定石パターン
  • ErrorDescription:エラー内容の可読化

まとめ — デバッグ関数の使い分け

最後に、各関数の特徴と使い分けを表で整理します。

関数名 出力先 主な用途 注意点
Print() エキスパートログ 変数の値確認、処理フロー追跡 大量出力でパフォーマンス低下
Comment() チャート左上 リアルタイムの状態監視 最後の呼び出しで上書きされる
Alert() ポップアップ 重要イベントの即時通知 バックテスト非対応・日本語文字化けの可能性
GetLastError() エラーコード取得 2度呼ぶとリセットされる(変数で受ける)
ResetLastError() エラーコードの初期化 処理前にリセットするのが定石

デバッグ関数を上手に使いこなすことは、MQL4開発の効率を大きく左右します。特に「GetLastErrorは変数で一度受けてから使う」「処理前にResetLastErrorでリセットする」の2つは、ぜひ今日から実践してください。

デバッグの精度が上がれば、EA開発のスピードも自然と上がっていきますよ!