MQL5 錯誤處理與除錯技巧:GetLastError() 與日誌實戰
📌 本文重點
掌握 MQL5 錯誤處理的完整方法:GetLastError()、常見錯誤碼解析、ResetLastError(),以及如何用 Print() 建立有效的除錯日誌系統。
掌握 MQL5 錯誤處理的完整方法:GetLastError()、常見錯誤碼解析、ResetLastError(),以及如何用 Print() 建立有效的除錯日誌系統。
為什麼錯誤處理如此重要?
EA 在實盤運行時,下單失敗、網路中斷、伺服器拒絕等情況隨時都可能發生。如果沒有適當的錯誤處理,EA 可能靜默失敗——你以為有下單,其實什麼都沒發生。
GetLastError() 基本用法
bool SafeOrderSend(MqlTradeRequest &req, MqlTradeResult &res)
{
ResetLastError(); // 清除之前的錯誤碼
bool ok = OrderSend(req, res);
if (!ok)
{
int err = GetLastError();
string errMsg = ErrorToString(err);
Print("下單失敗 | 錯誤碼: ", err, " | 說明: ", errMsg);
Print(" 品種: ", req.symbol, " | 方向: ", EnumToString(req.type));
Print(" 價格: ", req.price, " | 手數: ", req.volume);
Print(" 回傳碼: ", res.retcode, " | 說明: ", res.comment);
}
return ok;
}
// 常見錯誤碼對照
string ErrorToString(int code)
{
switch(code)
{
case 0: return "無錯誤";
case 4756: return "下單被拒絕";
case 4753: return "無效的止損";
case 4752: return "無效的手數";
case 4051: return "無效的交易品種";
case 4752: return "交易量過小";
case 10006: return "下單被拒絕(retcode)";
case 10007: return "等待回應超時";
case 10010: return "只允許部分成交";
case 10013: return "無效請求";
case 10014: return "無效手數";
case 10015: return "無效價格";
case 10016: return "無效止損/止盈";
case 10018: return "市場已關閉";
case 10019: return "保證金不足";
case 10024: return "下單頻率過高";
case 10026: return "自動交易被伺服器禁止";
default: return "未知錯誤 " + IntegerToString(code);
}
}
完整的重試機制
bool OrderSendWithRetry(MqlTradeRequest &req, MqlTradeResult &res,
int maxRetries = 3, int delayMs = 1000)
{
for (int i = 0; i < maxRetries; i++)
{
ResetLastError();
bool ok = OrderSend(req, res);
if (ok && res.retcode == TRADE_RETCODE_DONE)
{
Print("下單成功!重試次數: ", i, " | 票號: ", res.order);
return true;
}
// 某些錯誤不需要重試
if (res.retcode == TRADE_RETCODE_INVALID_VOLUME ||
res.retcode == TRADE_RETCODE_INVALID_STOPS ||
res.retcode == TRADE_RETCODE_MARKET_CLOSED)
{
Print("不可恢復錯誤,停止重試: ", res.retcode);
return false;
}
Print("下單失敗(", i+1, "/", maxRetries, ")retcode=", res.retcode, ",等待重試...");
Sleep(delayMs);
// 重新整理報價
req.price = (req.type == ORDER_TYPE_BUY)
? SymbolInfoDouble(req.symbol, SYMBOL_ASK)
: SymbolInfoDouble(req.symbol, SYMBOL_BID);
}
Print("已達最大重試次數,下單失敗");
return false;
}
結構化日誌系統
// 日誌等級
enum ENUM_LOG_LEVEL { LOG_DEBUG=0, LOG_INFO=1, LOG_WARN=2, LOG_ERROR=3 };
input ENUM_LOG_LEVEL InpLogLevel = LOG_INFO; // 最低顯示等級
void Log(ENUM_LOG_LEVEL level, string msg)
{
if (level < InpLogLevel) return;
string prefix;
switch(level)
{
case LOG_DEBUG: prefix = "[DEBUG]"; break;
case LOG_INFO: prefix = "[INFO] "; break;
case LOG_WARN: prefix = "[WARN] "; break;
case LOG_ERROR: prefix = "[ERROR]"; break;
}
string timestamp = TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS);
Print(prefix, " ", timestamp, " | ", msg);
}
// 使用範例
void OnTick()
{
Log(LOG_DEBUG, "OnTick 觸發,當前價格: " + DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits));
if (!IsNewBar()) return;
Log(LOG_INFO, "新K線: " + TimeToString(iTime(_Symbol, _Period, 0)));
int signal = GetSignal();
if (signal != 0)
Log(LOG_INFO, "交易信號: " + (signal == 1 ? "BUY" : "SELL"));
}
常見除錯技巧
- 加入大量 Print() 語句:驗證每一步的數值是否符合預期
- 確認數組方向:
ArraySetAsSeries後,[0]是最新;忘記設定是常見 bug - 用 Comment() 在圖表上顯示即時數據:
Comment("MA Fast=", fast, "\nMA Slow=", slow); - 策略測試器 + 視覺模式:可以觀察每個 Tick 的 EA 行為,是最有效的除錯方式
本文由 James Lee 撰寫。內容僅供教育目的,不構成投資建議。