返回《給 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), 選擇這支程式該放的資料夾——ExpertsIndicators 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 堂課。跑完範例或讀完這段之後, 把它勾起來,然後進下一堂課。