2026-03-03 01:41:15 +03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Utils.mqh |
|
|
|
|
|
//| Copyright 2026, MasterOfPuppets |
|
|
|
|
|
//| https://forge.mql5.io/masterofpuppets/mql5 |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#ifndef MASTER_OF_PUPPETS_LIB_UTILS_MQH
|
|
|
|
|
#define MASTER_OF_PUPPETS_LIB_UTILS_MQH
|
|
|
|
|
|
2026-03-03 01:47:50 +03:00
|
|
|
#include <MasterOfPuppetsLib\DuplicateFinder.mqh>
|
|
|
|
|
|
2026-03-03 01:41:15 +03:00
|
|
|
#property copyright "Copyright 2026, MasterOfPuppets"
|
|
|
|
|
#property link "https://forge.mql5.io/masterofpuppets/mql5"
|
|
|
|
|
|
2026-03-03 01:47:50 +03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Is key mapping correct function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool IsKeyMappingCorrect()
|
|
|
|
|
{
|
|
|
|
|
int actionKeys[];
|
|
|
|
|
actions.GetActionKeys(actionKeys);
|
|
|
|
|
string actionNames[];
|
|
|
|
|
actions.GetActionNames(actionNames);
|
|
|
|
|
DuplicateResult<int> duplicateResults[];
|
|
|
|
|
DuplicateFinder<int>::Find(actionKeys, duplicateResults);
|
|
|
|
|
if(duplicateResults.Size() == 0)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
string error;
|
|
|
|
|
for(uint i = 0; i < duplicateResults.Size(); i++)
|
|
|
|
|
{
|
|
|
|
|
string mappedActionNames;
|
|
|
|
|
for(uint j = 0; j < duplicateResults[i].indices.Size(); j++)
|
|
|
|
|
{
|
|
|
|
|
if(j > 0)
|
|
|
|
|
{
|
|
|
|
|
mappedActionNames += ", ";
|
|
|
|
|
}
|
|
|
|
|
mappedActionNames += actionNames[duplicateResults[i].indices[j]];
|
|
|
|
|
}
|
|
|
|
|
error += StringFormat("Error: duplicated key [%c] for %s\n", duplicateResults[i].value, mappedActionNames);
|
|
|
|
|
}
|
|
|
|
|
Alert(error);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-03 01:41:15 +03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Is standard deal reason function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool IsStandardDealReason(int dealReason)
|
|
|
|
|
{
|
|
|
|
|
switch(dealReason)
|
|
|
|
|
{
|
|
|
|
|
case DEAL_REASON_CLIENT:
|
|
|
|
|
case DEAL_REASON_EXPERT:
|
|
|
|
|
case DEAL_REASON_MOBILE:
|
|
|
|
|
case DEAL_REASON_SL:
|
|
|
|
|
case DEAL_REASON_TP:
|
|
|
|
|
case DEAL_REASON_WEB:
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Is zero function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool IsZero(const double value)
|
|
|
|
|
{
|
|
|
|
|
return MathAbs(value) < DBL_EPSILON ? true : false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Play sound file function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void PlaySoundFile(bool soundEnabled, string soundFileName)
|
|
|
|
|
{
|
|
|
|
|
if(soundEnabled)
|
|
|
|
|
{
|
|
|
|
|
PlaySound(soundFileName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Trailing stop function |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//OnTick() TrailingStep
|
|
|
|
|
void TrailingStop(const int distance)
|
|
|
|
|
{
|
|
|
|
|
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
|
|
|
|
double trailingStep = distance * point;
|
|
|
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
|
|
|
{
|
|
|
|
|
ulong ticket = PositionGetTicket(i);
|
|
|
|
|
if(PositionSelectByTicket(ticket))
|
|
|
|
|
{
|
|
|
|
|
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
|
|
|
double currentSL = PositionGetDouble(POSITION_SL);
|
|
|
|
|
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
|
|
|
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
|
|
|
|
// Для покупок (Buy)
|
|
|
|
|
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
|
|
|
|
|
{
|
|
|
|
|
if(bid - openPrice > trailingStep) // Проверяем, в прибыли ли мы
|
|
|
|
|
{
|
|
|
|
|
if(currentSL < bid - trailingStep)
|
|
|
|
|
{
|
|
|
|
|
MqlTradeRequest request = {};
|
|
|
|
|
MqlTradeResult result = {};
|
|
|
|
|
request.action = TRADE_ACTION_SLTP;
|
|
|
|
|
request.position = ticket;
|
|
|
|
|
request.sl = NormalizeDouble(bid - trailingStep, _Digits);
|
|
|
|
|
if(OrderSend(request, result))
|
|
|
|
|
{
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Для продаж (Sell)
|
|
|
|
|
else
|
|
|
|
|
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
|
|
|
|
|
{
|
|
|
|
|
if(openPrice - ask > trailingStep)
|
|
|
|
|
{
|
|
|
|
|
if(currentSL > ask + trailingStep || currentSL == 0)
|
|
|
|
|
{
|
|
|
|
|
MqlTradeRequest request = {};
|
|
|
|
|
MqlTradeResult result = {};
|
|
|
|
|
request.action = TRADE_ACTION_SLTP;
|
|
|
|
|
request.position = ticket;
|
|
|
|
|
request.sl = NormalizeDouble(ask + trailingStep, _Digits);
|
|
|
|
|
if(OrderSend(request, result))
|
|
|
|
|
{
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
//+------------------------------------------------------------------+
|