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

計時器、事件與警示

OnTimer、OnTrade、OnChartEvent,以及把一個通知推到你的手機上。

進階閱讀時間約 10 分鐘初學 → 進階第 16 / 17 堂
在 MetaEditor 實際跑一次
計時器、交易事件、圖表按鍵與推播警示

存成 MQL5/Experts/Lesson16Events.mq5。它用計時器在圖表上放一個即時儀表板、記錄交易事件,並在你按下 B 時送出一個推播通知。

//+------------------------------------------------------------------+
//| Lesson 16 - Timers, trade events, chart events, notifications    |
//+------------------------------------------------------------------+
#property copyright "Strategist Academy"
#property version   "1.00"
#property strict

input int TimerSeconds = 60;

int OnInit()
{
   EventSetTimer(TimerSeconds);
   Print("Timer set to ", TimerSeconds, "s. Press 'B' on the chart to send a test alert.");
   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
   EventKillTimer();          // always - a stray timer is a nasty bug
   Comment("");               // clear the on-chart dashboard
}

//+------------------------------------------------------------------+
//| Time-based work: runs whether or not the market is moving        |
//+------------------------------------------------------------------+
void OnTimer()
{
   double bid    = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double equity = AccountInfoDouble(ACCOUNT_EQUITY);
   double free   = AccountInfoDouble(ACCOUNT_MARGIN_FREE);

   Comment("Lesson 16 dashboard",
           "\nSymbol:  ", _Symbol,
           "\nBid:     ", DoubleToString(bid, _Digits),
           "\nEquity:  ", DoubleToString(equity, 2),
           "\nFree:    ", DoubleToString(free, 2),
           "\nPositions: ", PositionsTotal(),
           "\nUpdated: ", TimeToString(TimeCurrent(), TIME_SECONDS));
}

//+------------------------------------------------------------------+
//| Fires when anything trade-related happens on the account         |
//+------------------------------------------------------------------+
void OnTrade()
{
   Print("Trade event. Positions total: ", PositionsTotal(),
         "   orders: ", OrdersTotal());

   // React, but do not place new orders from in here - set a flag instead.
   if(PositionSelect(_Symbol))
   {
      double sl = PositionGetDouble(POSITION_SL);
      double tp = PositionGetDouble(POSITION_TP);
      Print("Position on ", _Symbol,
            "  type=", (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY ? "BUY" : "SELL"),
            "  volume=", DoubleToString(PositionGetDouble(POSITION_VOLUME), 2),
            "  sl=", DoubleToString(sl, _Digits),
            "  tp=", DoubleToString(tp, _Digits));
   }
}

//+------------------------------------------------------------------+
//| Keyboard and object interaction                                  |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long   &lparam,
                  const double &dparam,
                  const string &sparam)
{
   if(id == CHARTEVENT_KEYDOWN)
   {
      if(lparam == 66)   // 'B'
      {
         double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
         string msg = _Symbol + " manual check at " + DoubleToString(bid, _Digits);

         Alert(msg);
         Print("Alert raised: ", msg);

         // Requires push notifications enabled in terminal settings.
         if(!SendNotification(msg))
            Print("SendNotification failed - enable push notifications in terminal options.");
      }
   }
}
如何執行
  1. 1. 從 MT5 開啟 MetaEditor(按 F4), 選擇這支程式該放的資料夾——Experts、Indicators 或 Scripts。
  2. 2. 開新檔案、把這段程式貼上去,然後按 F7 編譯。Errors 分頁顯示的錯誤 都修掉。
  3. 3. 回到 MT5,把它拖到圖表上——如果是 EA,就打開 Strategy Tester。

超越 OnTick

tick 只在市場有動靜時才來。而一個 EA 需要的東西,有很多是時間導向或事件導向的,不是價格導向的。MQL5 為每一種都提供了處理函式。

OnTimer

在 OnInit 裡呼叫 EventSetTimer(seconds),OnTimer() 不管市場有沒有活動,都會按那個間隔觸發。用它做時段檢查、每日風險重置、週期性健康報告,以及任何即使在死寂市場上也必須做的雜務。永遠在 OnDeinit 裡 EventKillTimer()——一個活得比它的 EA 還久的計時器,是一個日後非常難找的 bug。

OnTrade 與 OnTradeTransaction

這些在帳戶上發生事情時觸發:一張單被送出、一筆成交被執行、一個部位被關閉。用 OnTrade 來反應——更新你的狀態、寫紀錄、或送出通知。不要直接在裡面下新單;設一個旗標,在下一個 tick 時才行動,否則你會有重入迴圈的風險。

OnChartEvent

在滑鼠、鍵盤與物件事件時觸發。它是你做互動式 EA 的方式:按一個鍵關掉全部、點一個按鈕切換交易開關。id 參數告訴你發生了什麼;lparam 則帶著 CHARTEVENT_KEYDOWN 的鍵碼。

把警示送到你手上

  • Alert()——終端機裡的彈出視窗。大聲、本地,而且很容易錯過。
  • SendNotification()——推播到 MT5 手機 App。需要在終端機設定裡開啟通知,並設定你的 MetaQuotes ID。這是真正會送到你手上的那一個。
  • SendMail()——電子郵件,如果有設定 SMTP 的話。
  • PlaySound()——當你坐在桌前的時候用。

用 Comment 做一個即時儀表板

Comment() 把文字寫到圖表上。它是最便宜的狀態顯示——淨值、未平倉部位、目前的訊號——而且在測試器裡不花任何代價。在結束時用 Comment("") 清掉,不要留下過期的文字。

你剛完成了什麼

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