605faf5310
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>
218 lines
7.8 KiB
Plaintext
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);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|