//+------------------------------------------------------------------+ //| MagicNumberHelpers.mqh | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Select position by symbol and magic number | //+------------------------------------------------------------------+ bool PositionSelectByMagic(string symbol, ulong magic_number) { for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(ticket == 0) continue; if(!PositionSelectByTicket(ticket)) continue; if(PositionGetString(POSITION_SYMBOL) == symbol && (ulong)PositionGetInteger(POSITION_MAGIC) == magic_number) return true; } return false; } //+------------------------------------------------------------------+ //| Select position by ticket and verify magic number and symbol | //+------------------------------------------------------------------+ bool PositionSelectByTicketAndMagic(ulong ticket, ulong magic_number) { if(!PositionSelectByTicket(ticket)) return false; return (PositionGetInteger(POSITION_MAGIC) == magic_number); } //+------------------------------------------------------------------+ //| Select position by ticket and verify symbol, magic number | //+------------------------------------------------------------------+ bool PositionSelectByTicketSymbolAndMagic(ulong ticket, string symbol, ulong magic_number) { if(!PositionSelectByTicket(ticket)) return false; return (PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic_number); } //+------------------------------------------------------------------+ //| Check if position exists with correct magic number | //+------------------------------------------------------------------+ bool PositionExistsByMagic(string symbol, ulong magic_number) { return PositionSelectByMagic(symbol, magic_number); } //+------------------------------------------------------------------+ //| Get position ticket by symbol and magic number | //+------------------------------------------------------------------+ ulong GetPositionTicketByMagic(string symbol, ulong magic_number) { for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(ticket == 0) continue; if(!PositionSelectByTicket(ticket)) continue; if(PositionGetString(POSITION_SYMBOL) == symbol && (ulong)PositionGetInteger(POSITION_MAGIC) == magic_number) return ticket; } return 0; } //+------------------------------------------------------------------+ //| Close position by symbol and magic number | //+------------------------------------------------------------------+ bool ClosePositionByMagic(CTrade &trade_obj, string symbol, ulong magic_number) { ulong ticket = GetPositionTicketByMagic(symbol, magic_number); if(ticket == 0) return false; return trade_obj.PositionClose(ticket); } //+------------------------------------------------------------------+ //| Modify position by symbol and magic number | //+------------------------------------------------------------------+ bool ModifyPositionByMagic(CTrade &trade_obj, string symbol, ulong magic_number, double sl, double tp) { ulong ticket = GetPositionTicketByMagic(symbol, magic_number); if(ticket == 0) return false; return trade_obj.PositionModify(ticket, sl, tp); } //+------------------------------------------------------------------+ //| Get position profit by symbol and magic number | //+------------------------------------------------------------------+ double GetPositionProfitByMagic(string symbol, ulong magic_number) { if(!PositionSelectByMagic(symbol, magic_number)) return 0.0; return PositionGetDouble(POSITION_PROFIT); } //+------------------------------------------------------------------+ //| Net P/L including swap (and commission when exposed by broker) | //+------------------------------------------------------------------+ double GetPositionNetProfitByMagic(string symbol, ulong magic_number) { if(!PositionSelectByMagic(symbol, magic_number)) return 0.0; double net = PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP); return net; } //+------------------------------------------------------------------+ //| True when an open position for this magic is not in profit | //+------------------------------------------------------------------+ bool IsUnprofitablePositionByMagic(string symbol, ulong magic_number) { if(!PositionExistsByMagic(symbol, magic_number)) return false; return (GetPositionNetProfitByMagic(symbol, magic_number) <= 0.0); } //+------------------------------------------------------------------+ //| Close unprofitable position to make room for a new entry signal | //| Returns true when no position remains (flat or closed successfully)| //+------------------------------------------------------------------+ bool United_PrepareEntrySlot(CTrade &trade_obj, string symbol, ulong magic_number, bool closeUnprofitableOnSignal) { if(!PositionExistsByMagic(symbol, magic_number)) return true; if(!closeUnprofitableOnSignal) return false; if(!IsUnprofitablePositionByMagic(symbol, magic_number)) return false; if(!ClosePositionByMagic(trade_obj, symbol, magic_number)) return false; return !PositionExistsByMagic(symbol, magic_number); } //+------------------------------------------------------------------+ //| Get position type by symbol and magic number | //+------------------------------------------------------------------+ ENUM_POSITION_TYPE GetPositionTypeByMagic(string symbol, ulong magic_number) { if(!PositionSelectByMagic(symbol, magic_number)) return WRONG_VALUE; return (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); } //+------------------------------------------------------------------+ //| Count positions by symbol and magic number | //+------------------------------------------------------------------+ int CountPositionsByMagic(string symbol, ulong magic_number) { int count = 0; for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(ticket == 0) continue; if(!PositionSelectByTicket(ticket)) continue; if(PositionGetString(POSITION_SYMBOL) == symbol && (ulong)PositionGetInteger(POSITION_MAGIC) == magic_number) count++; } return count; } //+------------------------------------------------------------------+ //| Align volume to SYMBOL_VOLUME_STEP / min / max (avoids Invalid volume) | //+------------------------------------------------------------------+ double United_NormalizeVolume(const string symbol, double volume) { double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX); double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); if(lotStep <= 0.0) lotStep = 0.01; double v = MathFloor(volume / lotStep) * lotStep; if(v < minLot) v = minLot; if(v > maxLot) v = maxLot; int digits = (int)MathCeil(-MathLog10(lotStep)); if(digits < 0) digits = 0; if(digits > 8) digits = 8; return NormalizeDouble(v, digits); } //+------------------------------------------------------------------+