Risk

The MQL5 Expert Advisor risk checklist: 12 checks before you go live

A practical pre-flight checklist for MetaTrader 5 EAs — risk-based lot sizing, broker limits, spread and session filters, daily loss guards and safe order handling, with MQL5 code.

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:

RetcodeMeaningTypical fix
10014Invalid volumeRound to the volume step; respect the minimum and maximum
10016Invalid stopsRespect the stops level; normalise prices to the symbol's digits
10018Market closedCheck the session before sending
10019Not enough moneyLower the risk or check free margin first
10027Algo trading disabled in the terminalSwitch 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

  1. Run the Strategy Tester with "Every tick based on real ticks" where your broker provides tick history.
  2. Include realistic spread and commission.
  3. Forward-test on a demo account for several weeks, in the same terminal setup you'll use live.
  4. 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.

Build it with Edgoro

Describe your strategy in plain English and get a TradingView indicator in Pine Script v6 or a MetaTrader 5 Expert Advisor in minutes. Three generations are free.

Start free