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開発のスピードも自然と上がっていきますよ!

