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

函式、include 與標準函式庫

別再重複自己。認識 CTrade——那個幫你省下五十行樣板程式碼的類別。

中級閱讀時間約 9 分鐘初學 → 進階第 08 / 17 堂
在 MetaEditor 實際跑一次
可重複使用的 helper,加上你的第一次 CTrade 呼叫

存成 MQL5/Scripts/Lesson8Functions.mq5。它示範成交量正規化與 point 轉價格——第三部分與第四部分每一個 EA 都會重複使用的兩個 helper。

//+------------------------------------------------------------------+
//| Lesson 8 - Reusable helpers and the standard library             |
//+------------------------------------------------------------------+
#property copyright "Strategist Academy"
#property version   "1.00"

#include <Trade/Trade.mqh>

//+------------------------------------------------------------------+
//| Round a volume to what the broker actually accepts               |
//+------------------------------------------------------------------+
double NormalizeVolumeFor(const string sym, double vol)
{
   double vMin  = SymbolInfoDouble(sym, SYMBOL_VOLUME_MIN);
   double vMax  = SymbolInfoDouble(sym, SYMBOL_VOLUME_MAX);
   double vStep = SymbolInfoDouble(sym, SYMBOL_VOLUME_STEP);

   vol = MathMax(vMin, MathMin(vMax, vol));

   if(vStep > 0)
      vol = vMin + MathFloor((vol - vMin) / vStep + 0.0000001) * vStep;

   return NormalizeDouble(vol, 2);
}

//+------------------------------------------------------------------+
//| Convert a distance in points into an absolute price              |
//+------------------------------------------------------------------+
double PointsToPrice(const double points, const double fromPrice, const bool below)
{
   double offset = points * _Point;
   return below ? NormalizeDouble(fromPrice - offset, _Digits)
                : NormalizeDouble(fromPrice + offset, _Digits);
}

//+------------------------------------------------------------------+
//| Is there already a position opened by this magic number?         |
//+------------------------------------------------------------------+
bool HasPosition(const string sym, const ulong magic)
{
   if(!PositionSelect(sym))
      return false;
   return ((ulong)PositionGetInteger(POSITION_MAGIC) == magic);
}

void OnStart()
{
   string sym = _Symbol;

   Print("Volume normalisation on ", sym);
   Print("  0.137  -> ", DoubleToString(NormalizeVolumeFor(sym, 0.137), 2));
   Print("  1.005  -> ", DoubleToString(NormalizeVolumeFor(sym, 1.005), 2));
   Print("  999.0  -> ", DoubleToString(NormalizeVolumeFor(sym, 999.0), 2), "  (clamped to max)");

   double bid = SymbolInfoDouble(sym, SYMBOL_BID);
   double ask = SymbolInfoDouble(sym, SYMBOL_ASK);

   Print("Stop 200 points below bid: ", DoubleToString(PointsToPrice(200, bid, true),  _Digits));
   Print("Target 400 points above ask: ", DoubleToString(PointsToPrice(400, ask, false), _Digits));

   Print("Has position with magic 12345? ", HasPosition(sym, 12345) ? "yes" : "no");

   // CTrade is ready to use - Part 3 will actually send orders with it.
   CTrade trade;
   trade.SetExpertMagicNumber(12345);
   trade.SetDeviationInPoints(10);
   trade.SetTypeFillingBySymbol(sym);
   Print("CTrade configured. Filling mode selected by symbol: ",
         EnumToString((ENUM_ORDER_TYPE_FILLING)SymbolInfoInteger(sym, SYMBOL_FILLING_MODE)));
}
如何執行
  1. 1. 從 MT5 開啟 MetaEditor(按 F4), 選擇這支程式該放的資料夾——Experts、Indicators 或 Scripts。
  2. 2. 開新檔案、把這段程式貼上去,然後按 F7 編譯。Errors 分頁顯示的錯誤 都修掉。
  3. 3. 回到 MT5,把它拖到圖表上——如果是 EA,就打開 Strategy Tester。

為什麼函式在這裡重要

成交量正規化、point 轉價格、部位查詢,這三件事出現在你會寫的每一個 EA 裡。寫一次、寫對,然後重複使用。當你在其中一個裡找到 bug,你只需要修一個地方。

傳值 vs. 傳參考

MQL5 預設會複製參數。加上 & 就是傳參考,讓函式可以寫回呼叫者的變數——這就是你一次回傳多個值的方式,也是 CopyBuffer 對你的陣列所做的事。把唯讀的參考參數標成 const &,讓編譯器保護你免於誤寫。

#include 與標準函式庫

#include <Trade/Trade.mqh> 會帶進 CTrade,一個替你處理 OrderSend 那些繁瑣部分的包裝類別:成交模式選擇、滑價、retcode 紀錄、商品檢查。底層它仍然是在組一個 MqlTradeRequest——你在第三部分會手動做一次,好理解它在做什麼——但正式要上線的程式碼,用 CTrade。

三個能擋掉大多數拒絕單的 CTrade 設定

  • SetExpertMagicNumber(n)——替每一張單貼標,讓你的 EA 只管理自己的部位。
  • SetDeviationInPoints(n)——滑價容許度。太緊你會一直被重新報價;太鬆你會收到爛成交。
  • SetTypeFillingBySymbol(sym)——去問商品支援哪一種成交模式,而不是自己猜 FOK、IOC 還是 RETURN。

成交量正規化不是選項

券商只接受固定級距的成交量,而且有上下限。級距是 0.01 時你送 0.137 手,這張單就會被拒絕。永遠向下取整到級距,並夾在上下限之間——這堂課的那個 helper 就是在做這件事。

你剛完成了什麼

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