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

多週期與多商品

讀更高的時間週期來判斷環境、掃描其他商品,並避開重繪陷阱。

進階閱讀時間約 12 分鐘初學 → 進階第 15 / 17 堂
在 MetaEditor 實際跑一次
從較低週期讀取的日線環境濾網

存成 MQL5/Scripts/Lesson15MTF.mq5。它從任何一張圖表讀出 D1 的 EMA200 與 H1 的 EMA50 並印出環境——注意它用 shift 1 來避免重繪。

//+------------------------------------------------------------------+
//| Lesson 15 - Multi-timeframe regime filter                        |
//+------------------------------------------------------------------+
#property copyright "Strategist Academy"
#property version   "1.00"

void OnStart()
{
   string sym = _Symbol;

   int maD1 = iMA(sym, PERIOD_D1, 200, 0, MODE_EMA, PRICE_CLOSE);
   int maH1 = iMA(sym, PERIOD_H1, 50,  0, MODE_EMA, PRICE_CLOSE);
   int maH4 = iMA(sym, PERIOD_H4, 50,  0, MODE_EMA, PRICE_CLOSE);

   if(maD1 == INVALID_HANDLE || maH1 == INVALID_HANDLE || maH4 == INVALID_HANDLE)
   {
      Print("Handle creation failed. Error: ", GetLastError());
      return;
   }

   double d1[], h1[], h4[];
   ArraySetAsSeries(d1, true);
   ArraySetAsSeries(h1, true);
   ArraySetAsSeries(h4, true);

   // Ask for 3 bars so index 1 (last CLOSED bar) is always available.
   bool okD1 = (CopyBuffer(maD1, 0, 0, 3, d1) >= 3);
   bool okH1 = (CopyBuffer(maH1, 0, 0, 3, h1) >= 3);
   bool okH4 = (CopyBuffer(maH4, 0, 0, 3, h4) >= 3);

   if(!okD1 || !okH1 || !okH4)
   {
      Print("Not enough higher-timeframe data yet. Error: ", GetLastError());
      IndicatorRelease(maD1); IndicatorRelease(maH1); IndicatorRelease(maH4);
      return;
   }

   double price = SymbolInfoDouble(sym, SYMBOL_BID);

   Print("=== Multi-timeframe view of ", sym, " ===");
   Print("Current chart timeframe: ", EnumToString((ENUM_TIMEFRAMES)_Period));
   Print("Bid:       ", DoubleToString(price, _Digits));
   Print("D1 EMA200: ", DoubleToString(d1[1], _Digits));
   Print("H4 EMA50:  ", DoubleToString(h4[1], _Digits));
   Print("H1 EMA50:  ", DoubleToString(h1[1], _Digits));
   Print("");
   Print("Daily regime:  ", (price > d1[1]) ? "ABOVE the daily trend - longs only"
                                            : "BELOW the daily trend - shorts only");
   Print("H4 alignment:  ", (price > h4[1]) ? "above" : "below");
   Print("H1 alignment:  ", (price > h1[1]) ? "above" : "below");
   Print("");
   Print("Note: all values use shift 1 (last CLOSED bar). Shift 0 is still");
   Print("forming and would make this repaint - the backtest would look better");
   Print("than the live result, every time.");

   // Raw bars from another timeframe, for reference.
   MqlRates dailyBars[];
   ArraySetAsSeries(dailyBars, true);
   if(CopyRates(sym, PERIOD_D1, 0, 5, dailyBars) == 5)
   {
      Print("");
      Print("Last 5 completed daily bars:");
      for(int i = 1; i < 5; i++)
         Print("  ", TimeToString(dailyBars[i].time, TIME_DATE),
               "  O=", DoubleToString(dailyBars[i].open,  _Digits),
               "  H=", DoubleToString(dailyBars[i].high,  _Digits),
               "  L=", DoubleToString(dailyBars[i].low,   _Digits),
               "  C=", DoubleToString(dailyBars[i].close, _Digits));
   }

   IndicatorRelease(maD1);
   IndicatorRelease(maH1);
   IndicatorRelease(maH4);
}
如何執行
  1. 1. 從 MT5 開啟 MetaEditor(按 F4), 選擇這支程式該放的資料夾——ExpertsIndicators Scripts
  2. 2. 開新檔案、把這段程式貼上去,然後按 F7 編譯。Errors 分頁顯示的錯誤 都修掉。
  3. 3. 回到 MT5,把它拖到圖表上——如果是 EA,就打開 Strategy Tester

就只是傳不同的商品或時間週期

每一個指標函式與複製函式都吃一個商品與一個時間週期。iMA(_Symbol, PERIOD_D1, 200, ...) 就在一張小時圖上給你一條日均線。沒有特別的 API,也沒有 request.security 的對應物——就只是不同的參數。

交易者為什麼這樣做

更高的時間週期給你環境(regime):我們大致上是往上還是往下?只在更高週期的方向上交易,你就能濾掉很大一部分的洗盤。這是多數初學者系統能加上的最高價值的一件事。

實務上的重繪陷阱

在 shift 0 讀更高週期,你就是在讀一根還沒收盤的 K 線。在一張小時圖上讀日線濾網,那個日線值會整天一直變。你的回測——事後算的——會顯示最終值,而上線時你看到的是持續演變中的那個。回測永遠比現實好看。用 shift 1。

要原始 K 線就用 CopyRates

當你需要開高低收而不是指標值時,用 CopyRates(symbol, timeframe, 0, count, rates[]) 讀進一個 MqlRates 陣列。它跨商品、跨週期的行為完全一致。

跨商品的同步

當你掃描數個商品時,它們的 K 線不會對齊——某一個可能最近都沒跳動。要嘛接受並處理缺失資料,要嘛明確地等你要的那根 K 線。不要因為你圖表上那個商品有新的 K 線,就假設每一個商品都有。

效能

每個 tick 讀十個商品很貴。把你的多商品工作閘門設在計時器或新 K 線上,並盡可能快取結果。策略測試器會感謝你。

你剛完成了什麼

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