A lot of MQL5 code found online was written for netting accounts, where there is at most one position per symbol. Run the same EA on a hedging account, which is what most retail forex brokers give you, and some functions start returning answers that look right but are not.
The trap
if(PositionSelect(_Symbol)) { double vol = PositionGetDouble(POSITION_VOLUME); // ... } On a netting account this selects the single position on the symbol. On a hedging account there can be several positions on the same symbol, from this EA, from another EA, or opened by hand. PositionSelect then picks one of them, and the documentation does not promise which. Your EA may read the volume, profit or stop loss of a position it never opened.
Check the account mode first
bool IsHedging() { return (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE) == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING; } Log it in OnInit so you always know which world the EA is running in.
Loop by ticket and filter by magic number
The pattern that works on both account types is to walk every open position and keep only the ones that belong to this EA:
input long InpMagic = 260113; int CollectMyPositions(ulong &tickets[]) { ArrayResize(tickets, 0); int total = PositionsTotal(); for(int i = total - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); // also selects it if(ticket == 0) continue; if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue; if(PositionGetInteger(POSITION_MAGIC) != InpMagic) continue; int n = ArraySize(tickets); ArrayResize(tickets, n + 1); tickets[n] = ticket; } return ArraySize(tickets); } Then work with PositionSelectByTicket(ticket) whenever you need a specific one.
Closing and modifying
On a hedging account a close request must name the position:
MqlTradeRequest req = {}; MqlTradeResult res = {}; req.action = TRADE_ACTION_DEAL; req.position = ticket; // without this a new opposite position opens req.symbol = _Symbol; req.volume = PositionGetDouble(POSITION_VOLUME); req.type = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY; req.price = (req.type == ORDER_TYPE_SELL) ? SymbolInfoDouble(_Symbol, SYMBOL_BID) : SymbolInfoDouble(_Symbol, SYMBOL_ASK); req.deviation = 20; req.magic = InpMagic; if(!OrderSend(req, res)) PrintFormat("close %I64u failed, retcode %u", ticket, res.retcode); If you leave req.position empty on a hedging account, the broker treats the sell as a brand new short. You end up hedged instead of flat, with double the swap and margin. The CTrade class does this for you with PositionClose(ticket), which is one more reason to prefer it.
Net exposure is your job now
On netting accounts the terminal nets buys and sells for you. On hedging accounts your EA must add up its own long and short volume if it has a maximum exposure rule. Sum the volume from CollectMyPositions with a sign per position type and compare that with the limit before every new entry.
Checklist
- Log the margin mode in OnInit.
- Never rely on PositionSelect(symbol) when the account can hold more than one position.
- Filter every loop by symbol and magic number.
- Always pass the position ticket when closing or modifying.
- Compute net exposure yourself.
These five lines prevent most of the "my EA closed the wrong trade" reports I see.


