返回《給 CFD 交易者的 MetaTrader 5 與 MQL5》
第一部分

資料型別、變數與嚴格型別

MQL5 不會像 Pine 那樣默默幫你轉型。你必須分清楚 int 與 double。

初學閱讀時間約 8 分鐘初學 → 進階第 03 / 17 堂
在 MetaEditor 實際跑一次
型別、轉型與浮點等值陷阱

存成 MQL5/Scripts/Lesson3Types.mq5 然後執行。注意最後兩行:天真地比較 double 會失敗,用容許誤差比較才會成功。

//+------------------------------------------------------------------+
//| Lesson 3 - Types, conversions, float comparison                  |
//+------------------------------------------------------------------+
#property copyright "Strategist Academy"
#property version   "1.00"

void OnStart()
{
   string   sym      = _Symbol;
   int      bars     = Bars(sym, _Period);
   double   point    = _Point;
   datetime now      = TimeCurrent();
   long     tradeMode= SymbolInfoInteger(sym, SYMBOL_TRADE_MODE);
   bool     tradable = (tradeMode != SYMBOL_TRADE_MODE_DISABLED &&
                        tradeMode != SYMBOL_TRADE_MODE_CLOSEONLY);

   Print("Symbol:        ", sym);
   Print("Bars on chart: ", bars);
   Print("Point:         ", DoubleToString(point, _Digits));
   Print("Server time:   ", TimeToString(now, TIME_DATE | TIME_SECONDS));
   Print("Trading open:  ", tradable ? "yes" : "no");

   // Build strings explicitly - no implicit double-to-string here.
   string label = StringFormat("%s | bars=%d | point=%.5f", sym, bars, point);
   Print(label);

   // Parsing back from string
   string volText = "0.10";
   double vol     = StringToDouble(volText);
   Print("Parsed volume: ", DoubleToString(vol, 2));

   // --- The float trap ------------------------------------------------
   double a = 0.1 + 0.2;
   Print("0.1 + 0.2 == 0.3 exactly? ", (a == 0.3) ? "true" : "false  <-- the trap");
   Print("Same check with tolerance: ",
         (MathAbs(a - 0.3) < 0.000001) ? "true" : "false");

   // In trading terms: compare prices using points, not raw equality.
   double bid  = SymbolInfoDouble(sym, SYMBOL_BID);
   double level= NormalizeDouble(bid, _Digits);
   Print("Bid equals its own normalised copy (within half a point)? ",
         (MathAbs(bid - level) <= _Point / 2.0) ? "yes" : "no");
}
如何執行
  1. 1. 從 MT5 開啟 MetaEditor(按 F4), 選擇這支程式該放的資料夾——Experts、Indicators 或 Scripts。
  2. 2. 開新檔案、把這段程式貼上去,然後按 F7 編譯。Errors 分頁顯示的錯誤 都修掉。
  3. 3. 回到 MT5,把它拖到圖表上——如果是 EA,就打開 Strategy Tester。

MQL5 是嚴格型別的

這是從 PineScript 過來最大的一個調整。在 Pine 裡,一個值是一個 series,runtime 多半會原諒你。在 MQL5 裡你宣告一個型別,編譯器就會要求你遵守它。型別不合是編譯錯誤,不是默默算錯——這在一開始很惱人,但長期來說真的比較安全。

你實際上會用到的型別

  • int——計數、位移、錯誤碼、magic number(用 long/ulong)。
  • double——所有價格、成交量與金額。
  • bool——由 &&、||、! 產生的旗標。
  • string——商品名稱、註解、訊息。
  • datetime——從 1970 年起的秒數。用 TimeToString 格式化。
  • color——clrDodgerBlue,或 C'255,0,0'。
  • ENUM_*——具名常數:ENUM_TIMEFRAMES、ENUM_ORDER_TYPE、ENUM_MA_METHOD,還有數十個其他。優先用這些,不要用魔術數字。

轉型是明確的

用 DoubleToString()、IntegerToString()、StringToDouble()、StringToTime() 與 StringFormat()。不要倚賴隱含轉型——也永遠不要把一個 double 串進字串然後期待它長得對。

永遠不要用等值比較兩個 double

浮點運算代表 0.1 + 0.2 並不精確等於 0.3。任何你在比較兩個價格的地方,都要用一個以 point 為單位的容許誤差。這在「價格有沒有碰到我的價位」這類檢查上咬得最兇——精確比較會永遠靜默地不觸發。

作用域與 static

在函式內宣告的變數,在函式回傳時就消失。宣告在檔案作用域的,則活到程式結束。把一個區域變數標成 static 可以在多次呼叫之間保留它的值——這是一個不必用全域變數就能數 tick 或記住上一根 K 線的便宜做法。

你剛完成了什麼

《給 CFD 交易者的 MetaTrader 5 與 MQL5》的第 03 / 17 堂課。跑完範例或讀完這段之後, 把它勾起來,然後進下一堂課。