Run two Expert Advisors on one MetaTrader 5 account and sooner or later one of them closes a trade that belongs to the other. Or an EA restarts, forgets which positions are its own, and opens a duplicate. The fix for both is the magic number, a small integer that most EA code mentions and few explanations cover properly.
This article explains what a magic number is, how to set it and filter by it in MQL5, how to choose numbers that won't collide, and the one account type where it can't keep EAs apart.
What a magic number is
A magic number is an integer (an unsigned 64-bit ulong in MQL5) attached to every order an Expert Advisor sends. It travels with the order, the deals it produces and the position that results, so you can later ask "which of these did my EA open?".
- Trades you place by hand carry magic number 0.
- MetaTrader doesn't enforce uniqueness. Two EAs can use the same number, and then they can't tell each other's trades apart.
- The number is yours to choose. It has no meaning to the broker.
Setting it
With the standard CTrade class, set it once and every order sent through that object carries it:
trade.SetExpertMagicNumber(InpMagic);
If you build requests by hand with MqlTradeRequest, set request.magic on every request. Forgetting it on one code path, such as a pending order or a partial close, is a classic source of orphaned trades.
Filtering by it
The other half is reading: whenever the EA loops through positions, orders or history, it should skip anything without its number. Positions and orders also need a symbol filter, because the same EA may run on several charts.
#include <Trade/Trade.mqh>
input ulong InpMagic = 260100; // Unique for each EA, symbol and setup
CTrade trade;
// Open positions that belong to this EA on this chart's symbol.
int CountOwnPositions()
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i); // also selects the position
if(ticket == 0)
continue;
if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == (long)InpMagic)
count++;
}
return count;
}
// Pending orders placed by this EA on this symbol.
int CountOwnOrders()
{
int count = 0;
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
ulong ticket = OrderGetTicket(i); // also selects the order
if(ticket == 0)
continue;
if(OrderGetString(ORDER_SYMBOL) == _Symbol && OrderGetInteger(ORDER_MAGIC) == (long)InpMagic)
count++;
}
return count;
}
// Realised result of this EA's deals since a given time: profit, swap and commission.
double ClosedProfitSince(const datetime from)
{
if(!HistorySelect(from, TimeCurrent()))
return 0.0;
double total = 0.0;
for(int i = HistoryDealsTotal() - 1; i >= 0; i--)
{
ulong ticket = HistoryDealGetTicket(i);
if(ticket == 0)
continue;
if(HistoryDealGetInteger(ticket, DEAL_MAGIC) != (long)InpMagic || HistoryDealGetString(ticket, DEAL_SYMBOL) != _Symbol)
continue;
total += HistoryDealGetDouble(ticket, DEAL_PROFIT) + HistoryDealGetDouble(ticket, DEAL_SWAP) + HistoryDealGetDouble(ticket, DEAL_COMMISSION);
}
return total;
}
int OnInit()
{
trade.SetExpertMagicNumber(InpMagic); // every order sent through this object carries the number
if(AccountInfoInteger(ACCOUNT_MARGIN_MODE) != ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
Print("Netting account: one position per symbol, shared by every EA that trades it.");
// After a restart, the EA's state comes from the account, not from variables that were lost.
PrintFormat("Found %d open position(s) and %d pending order(s) with magic %I64u", CountOwnPositions(), CountOwnOrders(), InpMagic);
return INIT_SUCCEEDED;
}
void OnTick()
{
}
Note the cast (long)InpMagic: the getters return long while the input is a ulong. For the small numbers people actually use, the comparison is exact.
Choosing numbers that won't collide
Pick a scheme and write it down. One simple pattern encodes the EA, the symbol and the setup:
| Part | Digits | Example |
|---|---|---|
| EA identifier | First two | 26 = your trend EA |
| Symbol slot | Next two | 01 = EUR/USD, 02 = gold |
| Setup or timeframe | Last two | 00 = H1 default, 01 = H4 variant |
So 260100 is the trend EA on EUR/USD with its default setup, and 260201 the same EA on gold with the H4 variant. Make the number an input so each chart can get its own value, and keep a list next to your VPS notes.
Also avoid 0, which is what manual trades use, and don't copy the magic numbers from published code: someone else's EA on the same account may be using them too.
Why not use the order comment?
Some EAs tag their trades with a comment such as "trend-EA" and filter on that instead. It's fragile: comments are free text, brokers may overwrite or append to them (for example when a stop-loss or take-profit closes the trade), and two EAs can easily choose the same word. The magic number is an exact integer that travels with the order unchanged. Use comments for humans reading the history, and magic numbers for code.
Several setups in one EA
One Expert Advisor can run more than one logic, say a breakout leg built on a Donchian channel and a mean-reversion leg on Bollinger Bands, both on the same symbol. Give each its own number, either with one CTrade object per leg or by calling SetExpertMagicNumber() before each order. Each leg then counts, trails and closes only its own positions, and the history shows which leg made or lost money.
Restarts and state
Terminals restart, VPSs reboot and EAs get recompiled. Anything kept only in ordinary variables is lost. A robust EA rebuilds its view of the world from the account in OnInit(): its open positions and pending orders (filtered by magic number), and, if it needs them, today's deals from the history. Persistent values like the day's starting equity can live in terminal global variables whose names include the magic number, as in the prop-firm daily loss guard.
Netting accounts: where magic numbers can't help
MetaTrader 5 accounts are either hedging (several positions per symbol allowed) or netting (one position per symbol). On a netting account, a buy from one EA and a sell from another on the same symbol don't create two positions; they offset into one. No magic number can split that shared position back into two EAs' trades.
If you run several EAs on a netting account, give each its own symbol, or use a hedging account. The example's OnInit() prints a warning when the account isn't hedging.
Common mistakes
| Mistake | What goes wrong | Fix |
|---|---|---|
| No magic filter in a close-all loop | The EA closes manual trades and other EAs' positions | Filter by magic number and symbol |
| Same magic on two charts | Two copies of the EA manage each other's trades | One number per EA, symbol and setup |
Using OrdersTotal() to count positions | In MQL5, orders are pending orders only; positions are separate | Use PositionsTotal() for positions |
| Magic set on entries but not on pending orders | Orders the EA can't recognise later | Set it once on the CTrade object, or on every request |
| State only in variables | Duplicate trades after a restart | Rebuild state from positions in OnInit() |
These checks are part of the MQL5 Expert Advisor risk checklist, and they matter for any trade management code, such as a trailing stop.
Sources and further reading
- MQL5 Reference: Trade request structure — the
magicfield of every order. - MQL5 Reference: CTrade::SetExpertMagicNumber() — setting it once on the trade object.
- MQL5 Reference: Position properties —
POSITION_MAGICand the other values you filter on. - MetaTrader 5 Help: Basic principles of trading operations — netting and hedging position accounting.
Try it with Edgoro
Every MT5 Expert Advisor Edgoro generates comes with a magic-number input and filters its own trades by it. Ask for "an EA that only manages its own positions on this symbol and rebuilds its state after a restart" and check the code yourself. Generate your EA.
Test every EA on a demo account before going live. Automated trading carries substantial risk of loss; nothing here is investment advice.