An Expert Advisor does exactly what its code says, at any hour, without hesitating. That's the appeal — and the danger. A missing stop-loss, a lot size that ignores the broker's volume step, or an entry that fires on every tick instead of once per bar can turn a reasonable idea into an expensive afternoon.
Here's the checklist we suggest running through before any EA touches a live account, whether you wrote it, bought it or generated it. The snippets use MQL5 and the standard CTrade class.
The checklist
- Every order is sent with a stop-loss
- Lot size is calculated from risk, not hard-coded
- Volumes respect the broker's minimum, maximum and step
- Stops respect the broker's minimum stop distance
- A spread filter blocks entries when trading costs spike
- Entries are evaluated once per bar, unless the strategy truly needs ticks
- A daily loss limit stops trading for the day
- Open positions and trades per day are capped
- Trading hours are restricted to the sessions you intend
- The EA uses a magic number and only manages its own positions
- Trade results are checked and logged
- The EA survives a restart without duplicating trades
1. Size positions from risk
A fixed 1.0 lot means very different risk on different symbols and stop distances. Calculate volume from the money you're willing to lose and the stop distance, then round down to the broker's volume step:
#include <Trade/Trade.mqh>
input double InpRiskPercent = 1.0; // Risk per trade, % of equity
input int InpStopPoints = 300; // Stop-loss distance in points
input int InpMaxSpread = 30; // Max spread in points
input ulong InpMagic = 260913; // Magic number
CTrade trade;
double LotsForRisk(const double riskPercent, const int stopPoints)
{
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
if(tickValue <= 0 || tickSize <= 0 || step <= 0 || stopPoints <= 0)
return 0.0;
double riskMoney = AccountInfoDouble(ACCOUNT_EQUITY) * riskPercent / 100.0;
double lossPerLot = (stopPoints * _Point / tickSize) * tickValue;
double lots = MathFloor(riskMoney / lossPerLot / step) * step;
if(lots < minLot)
return 0.0; // too small to respect the risk: skip the trade
return MathMin(lots, maxLot);
}
Returning zero when the minimum lot would exceed your risk is deliberate. Rounding up silently is how a "1% risk" EA ends up risking 3% on a small account. You can sanity-check the numbers with the position size calculator.
2. Respect broker limits
Brokers set a minimum distance between the current price and your stops (SYMBOL_TRADE_STOPS_LEVEL, in points). Orders with stops inside that distance are rejected as invalid stops. Check it before sending, and widen or skip the trade:
bool StopDistanceOk(const int stopPoints)
{
long minStop = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
return stopPoints > minStop;
}
3. Filter spread, permissions and hours
Spreads widen around the daily rollover, during news and in thin markets. A spread filter is a couple of lines that saves a lot of bad fills:
bool CanTrade()
{
if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) || !MQLInfoInteger(MQL_TRADE_ALLOWED))
return false; // Algo Trading is switched off
if(SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) > InpMaxSpread)
return false; // costs too high right now
return true;
}
Trading hours deserve the same treatment: compare TimeCurrent() — the broker's server time, not your local time — with the window you intend to trade, and return false outside it. The market hours tool helps you translate sessions into your broker's clock.
4. Evaluate once per bar
OnTick() runs on every price change. If your entry logic uses closed-bar conditions, check for a new bar first; otherwise the same signal can trigger repeatedly within one candle:
bool IsNewBar()
{
static datetime lastBar = 0;
datetime current = iTime(_Symbol, PERIOD_CURRENT, 0);
if(current == lastBar)
return false;
lastBar = current;
return true;
}
5. Add a daily loss guard
Record equity at the start of each trading day and stop opening trades — or close everything — once the day's loss reaches your limit. Store the starting value in a terminal global variable, or recompute it from the account history, so a restart in the middle of the day doesn't reset the guard. Cap the number of trades per day the same way. This matters even more on funded accounts; see prop firm challenge rules explained.
6. Send orders safely and check the result
Set the magic number and allowed slippage once, send every order with its stop, and log anything that isn't a success:
int OnInit()
{
trade.SetExpertMagicNumber(InpMagic);
trade.SetDeviationInPoints(20);
return INIT_SUCCEEDED;
}
void OpenBuy()
{
if(!CanTrade() || !StopDistanceOk(InpStopPoints))
return;
double lots = LotsForRisk(InpRiskPercent, InpStopPoints);
if(lots <= 0)
return;
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double sl = NormalizeDouble(ask - InpStopPoints * _Point, _Digits);
if(!trade.Buy(lots, _Symbol, ask, sl, 0.0, "risk-checked entry"))
PrintFormat("Buy failed: retcode %u (%s)", trade.ResultRetcode(), trade.ResultRetcodeDescription());
}
Return codes worth handling explicitly:
| Retcode | Meaning | Typical fix |
|---|---|---|
| 10014 | Invalid volume | Round to the volume step; respect the minimum and maximum |
| 10016 | Invalid stops | Respect the stops level; normalise prices to the symbol's digits |
| 10018 | Market closed | Check the session before sending |
| 10019 | Not enough money | Lower the risk or check free margin first |
| 10027 | Algo trading disabled in the terminal | Switch on the Algo Trading button |
7. Only touch your own positions
When looping through open positions, filter by symbol and magic number. Otherwise your EA may modify or close trades you opened manually or that another EA manages on the same account. Also check whether the account is netting or hedging (ACCOUNT_MARGIN_MODE): on a netting account, a "new" buy adds to the existing position instead of opening a second one. On restart, rebuild the EA's state from the positions that carry its magic number rather than from variables that were lost.
8. Test properly before going live
- Run the Strategy Tester with "Every tick based on real ticks" where your broker provides tick history.
- Include realistic spread and commission.
- Forward-test on a demo account for several weeks, in the same terminal setup you'll use live.
- Start live with the smallest size your broker allows.
Our article on backtesting mistakes that inflate results explains why each step matters. If you're new to installing EAs, start with how to install an Expert Advisor on MT5.
Try it with Edgoro
Ask Edgoro for an MT5 Expert Advisor and include your risk rules in the description — risk per trade, spread limit, daily loss stop, trading hours. You'll get MQL5 code you can read, refine by chatting and download as a .mq5 file. Generate your first EA free.
Automated trading can lose money quickly. Test every EA on a demo account first; nothing here is investment advice.