The code
An Expert Advisor that closes its own positions (by magic number) on Friday evening and warns when the chart's symbol stops ticking. It can close real positions, so try it on a demo account first.
Save it as MQL5/Experts/ontimer-event.mq5 and compile it in MetaEditor (F7): 0 errors, 0 warnings.
#include <Trade/Trade.mqh>
input int InpSeconds = 5; // Timer period, seconds
input int InpStaleAfter = 120; // Warn when no tick for this many seconds
input bool InpFridayClose = true; // Close this EA's positions before the weekend
input int InpCloseHour = 21; // Friday closing time, server hour
input int InpCloseMinute = 30; // Friday closing time, minute (tries for 30 minutes from then)
input ulong InpMagic = 20261112; // Magic number of the positions to close
CTrade trade;
int OnInit()
{
// One timer per program: calling EventSetTimer() again replaces it
if(!EventSetTimer(InpSeconds))
{
PrintFormat("EventSetTimer failed: error %d", GetLastError());
return INIT_FAILED;
}
trade.SetExpertMagicNumber(InpMagic);
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
EventKillTimer();
}
void OnTick()
{
// Tick-driven logic stays here. OnTimer() runs even when no tick arrives.
}
void CloseOwnPositions()
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0 || PositionGetString(POSITION_SYMBOL) != _Symbol || PositionGetInteger(POSITION_MAGIC) != (long)InpMagic)
continue;
if(!trade.PositionClose(ticket) || trade.ResultRetcode() != TRADE_RETCODE_DONE)
PrintFormat("Weekend close of #%I64u failed: retcode %u (%s)", ticket, trade.ResultRetcode(), trade.ResultRetcodeDescription());
}
}
void OnTimer()
{
// TimeCurrent() is the time of the last tick and stands still when ticks stop.
// TimeTradeServer() keeps running: it's the server's clock as the terminal estimates it.
datetime now = TimeTradeServer();
MqlDateTime t;
TimeToStruct(now, t);
int minutes = t.hour * 60 + t.min;
int closeAt = InpCloseHour * 60 + InpCloseMinute;
// A 30-minute window: retries on every timer event, but stops before the market closes for the weekend
if(InpFridayClose && t.day_of_week == 5 && minutes >= closeAt && minutes < closeAt + 30)
CloseOwnPositions();
static bool warned = false;
MqlTick tick;
if(SymbolInfoTick(_Symbol, tick) && tick.time > 0)
{
long silent = (long)now - (long)tick.time;
bool weekday = t.day_of_week >= 1 && t.day_of_week <= 5;
if(weekday && silent > InpStaleAfter && !warned)
{
PrintFormat("No %s tick for %d seconds: connection lost, or the market is closed?", _Symbol, (int)silent);
warned = true;
}
else if(silent <= InpStaleAfter)
warned = false;
}
}How it works
EventSetTimer(InpSeconds). Starts a timer that callsOnTimer()every few seconds. It returnsfalseif the timer couldn't be created; the EA then refuses to start rather than run without it.EventKillTimer()inOnDeinit(). Stops the timer when the EA is removed or re-initialized, as MetaQuotes' reference recommends when the timer was set inOnInit().TimeTradeServer(). The terminal's estimate of the server's current time. It advances on its own, which is what a schedule needs;TimeCurrent()would freeze at the last tick.- The Friday window. The EA tries to close during a 30-minute window from the set time, retrying on every timer event. After the window it stops, so a market that closed for the weekend doesn't produce a refused order every five seconds until Monday.
- The stale-feed check.
SymbolInfoTick()gives the time of the last tick. On a weekday, a gap longer than the limit usually means a lost connection or a closed market; the warning prints once and resets when ticks return. OnTick()stays. Tick-driven logic and timed logic live side by side: the terminal calls each handler when its event arrives, one at a time.
Pitfalls
TimeCurrent()in a timer. It's the time of the last tick, so a schedule based on it stalls exactly when it matters: in a quiet market or after the close.- Long work in
OnTimer(). An EA handles one event at a time, and timer events don't pile up: while one is waiting or being processed, new ones are dropped. A slow web request in the timer also delaysOnTick(). - One timer per program. Calling
EventSetTimer()again replaces the first timer. For two schedules, use one fast timer and count. - Trading when the market is closed. The timer keeps firing on weekends and at night; orders sent then fail with 10018 market closed. Limit timed actions to the hours the symbol trades.
- Millisecond precision.
EventSetMillisecondTimer(10)doesn't make the terminal precise to 10 ms: the system's timer resolution is coarser, and events arrive late when the terminal is busy.
Variations
- Several symbols.
OnTick()only fires for the chart's symbol. A timer can pollSymbolInfoTick()for a list of symbols every second, the usual base of a multi-symbol EA. - A refreshed display. The on-chart dashboard updates its panel from a one-second timer instead of on every tick.
- An exact time once a day. With a one-second timer, compare
TimeTradeServer()with the target time and remember the date you last ran, so the action happens once per day. - Close everything, not just this EA. Replace
CloseOwnPositions()with the loop from close all positions.
Questions
Does OnTimer() work in the Strategy Tester?
Yes. The tester generates timer events from its simulated clock, so timed logic can be tested. Keep in mind that the simulated time moves in jumps between the ticks it models.
Can an MQL5 program have more than one timer?
No, one per program: a second EventSetTimer() call replaces the first. Run several schedules from one timer by counting calls or comparing times.
Why isn't my OnTimer() function called?
Usually because EventSetTimer() was never called or returned false, or because another handler, often a long OnTick(), keeps the EA busy. A typo in the function name also compiles: it must be exactly void OnTimer().
This program is a minimal example written to show one technique, not a trading system, and none of it is a recommendation to trade. Trading involves substantial risk of loss and is not suitable for every investor. Edgoro is a software tool, not a broker or financial adviser: nothing it produces is investment advice, and past or backtested performance does not guarantee future results.