An MT5 terminal restart, a VPS reboot, a recompile, a timeframe switch or a changed input all reinitialize your Expert Advisor. Every global variable starts from zero again. If the EA was counting today's trades, tracking peak equity for a drawdown stop, or remembering that it already hit its daily loss limit, that memory is gone and the EA may happily start trading again.
This post covers three ways to keep state, and a simple rule for choosing between them.
Decide what actually needs to survive
Typical candidates:
- Number of entries opened today
- Peak equity for an intraday drawdown stop
- A "locked for today" flag after a loss limit is hit
- The last grid level or last entry price
Some of these can be rebuilt from the server. Some cannot. That difference drives the design.
Option 1: Rebuild from trade history
The trade server is the best source of truth. Positions and deals survive anything that happens on your machine. Counting today's entries is a history query, not a variable:
input long InpMagic = 240101;
datetime DayStart()
{
MqlDateTime t;
TimeToStruct(TimeCurrent(), t);
t.hour = 0; t.min = 0; t.sec = 0;
return StructToTime(t);
}
int EntriesToday()
{
int count = 0;
if(!HistorySelect(DayStart(), TimeCurrent()))
return 0;
int total = HistoryDealsTotal();
for(int i = 0; i < total; i++)
{
ulong ticket = HistoryDealGetTicket(i);
if(HistoryDealGetInteger(ticket, DEAL_MAGIC) != InpMagic) continue;
if(HistoryDealGetString(ticket, DEAL_SYMBOL) != _Symbol) continue;
if(HistoryDealGetInteger(ticket, DEAL_ENTRY) == DEAL_ENTRY_IN) count++;
}
return count;
}
Note that DayStart uses server time. If your prop firm resets the day in another time zone, shift it accordingly.
Option 2: Terminal global variables
Values that cannot be derived, like peak equity, need storage. Terminal global variables are the quickest option. They survive restarts, but they only hold doubles and are deleted after four weeks without access.
Key them by magic number and symbol so two copies of the EA never share state:
double gPeakEquity = 0.0;
bool gLockedToday = false;
string Key(const string name)
{
return "EA_" + IntegerToString(InpMagic) + "_" + _Symbol + "_" + name;
}
void SaveState()
{
GlobalVariableSet(Key("day"), (double)DayStart());
GlobalVariableSet(Key("peak"), gPeakEquity);
GlobalVariableSet(Key("locked"), gLockedToday ? 1.0 : 0.0);
GlobalVariablesFlush();
}
void LoadState()
{
bool sameDay = GlobalVariableCheck(Key("day")) &&
(datetime)GlobalVariableGet(Key("day")) == DayStart();
if(sameDay)
{
gPeakEquity = GlobalVariableGet(Key("peak"));
gLockedToday = GlobalVariableGet(Key("locked")) > 0.5;
}
else
{
gPeakEquity = AccountInfoDouble(ACCOUNT_EQUITY);
gLockedToday = false;
}
}
Call LoadState() in OnInit and SaveState() whenever a value changes. Do not rely on OnDeinit alone. A crash or power cut skips it entirely.
Option 3: A small file
For strings, arrays or anything structured, write a file in the MQL5/Files sandbox. Write to a temporary name first and then rename it with FileMove and the FILE_REWRITE flag, so a crash halfway through never leaves a corrupted state file behind.
The rule: when they disagree, trust the server
Rebuild everything you can from positions and history. Persist only what cannot be derived. On startup, if a stored value conflicts with what the server says, for example a stored open grid level with no matching position, believe the server and log the mismatch.
Test it like a restart
The Strategy Tester never restarts your EA mid run, so it will not catch these bugs. On a demo account, open a trade, remove the EA from the chart, attach it again and check the journal. Then do the same with a full terminal restart.
It is a ten minute test that prevents the classic "it opened five trades after the VPS rebooted" morning.
If you would rather have restart safe state built into your EA from the start, I build custom MT4 and MT5 Expert Advisors at Custom Trading Bots.
Top comments (1)
The HistorySelect failure path returns 0, which is also a valid 'no entries today' result. Missing history after a restart could therefore reopen the daily allowance. I would return success separately from the count and block new entries until history is available. How would your restart test handle that failure?