Files
zhutoutoutousan 605faf5310 Prepare source-only public release for develop.
Add cluster audit pipeline, united EA updates, brochure generators, and publication hygiene (gitignore, MT5 path desensitization, pre-upload scan). Remove tracked reports, models, and binary artifacts from the repo.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 15:03:43 +02:00

218 lines
7.8 KiB
Plaintext

//+------------------------------------------------------------------+
//| 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);
}
//+------------------------------------------------------------------+