IndianTrailingStopLoss/StopLossManagement.mqh
super.admin 88be02cad4 convert
2025-05-30 15:00:53 +02:00

436 lines
20 KiB
MQL5

//+------------------------------------------------------------------+
//| StopLossManagement.mqh |
//| Jay Davis |
//| 512jay.github.io |
//+------------------------------------------------------------------+
#property copyright "Jay Davis"
#property link "512jay.github.io"
// v1
bool Debug_StopLossManagment = false;
input group ""
input group "|----------Stop Loss Management----------|"
input bool useVirtualStops = true; // Use virtual stops
input int MinimumPointsForStopLossMove = 1; // Minimum number of points to move any Stoploss
input int SendNoticeTimer = 5; // Minutes between stoploss notices
input group "||--- Average True Range Settings"
input bool useATRStopLoss = true; // Use ATR for stop loss?
input double atrMultiplier = 1; // ATR Multiplier
input ENUM_TIMEFRAMES TimeframeOfATR = PERIOD_CURRENT; // Timeframe for ATR calculations
input int PeriodsInATR = 14; // periods in calculation
input group "||--- Percentage Stop Loss Settings"
input bool usePercentageStopLoss = false; // Use a percentage stop loss?
input double BuyStopLossPercentage = 0.1; // BUY Stop-Loss Percentage, non zero, non negative;
input double SellStopLossPercentage = 0.1; // SELL Stop-Loss Percentage, non zero, non negative;
input group "||--- Idle Stoploss movement"
input bool BuyMoveStopLossIfIdle = false; // Move BUY Stop-Loss if Idle?
input int BuyIdleMinutesRequiringAMove = 25; // Number of Idle minutes requiring a BUY Stop-Loss move
input int BuyIdleMovements = 5; // Number of possible BUY idle movments
input double BuyPercentageToMoveStopLossIfIdle = 0.01; // % to move BUY Stop-Loss when idle
input bool SellMoveStopLossIfIdle = false; // Move SELL Stop-Loss if Idle?
input int SellIdleMinutesRequiringAMove = 15; // Number of Idle minutes requiring a SELL Stop-Loss move
input double SellPercentageToMoveStopLossIfIdle = 0.01; // % to move SELL Stop-Loss when idle
input int SellIdleMovements = 5; // Number of possible SELL idle movments
datetime MostRecentNoticeTime = 0;
// Global variables
double trailingDistance = 0;
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void SendModifyNotice(long orderType, double newStopLoss, double size)
{
if(TimeCurrent() - (SendNoticeTimer * 60) > MostRecentNoticeTime)
{
NoticeMessage("ISTL", 3, orderType, newStopLoss, size);
//SendToWhatsAppAPI(theMessage);
MostRecentNoticeTime = TimeCurrent();
//Print(theMessage);
}
}
//+------------------------------------------------------------------+
//| Gets and returns the ATR value |
//| Returns ATR value |
//+------------------------------------------------------------------+
double AverageTrueRange(string name, ENUM_TIMEFRAMES period, int atr_periods)
{
int handle = iATR(name, period, atr_periods);
double atr[];
//--- if the handle is not created
if(handle == INVALID_HANDLE)
{
//--- tell about the failure and output the error code
PrintFormat("Failed to create handle of the iATR indicator for the symbol %s/%s, error code %d",
name,
EnumToString(period),
GetLastError());
//--- the indicator is stopped early
return(INIT_FAILED);
}
CopyBuffer(handle, 0, 0, 1, atr);
trailingDistance = NormalizeDouble(atr[0] * atrMultiplier,
(int)SymbolInfoInteger(name, SYMBOL_DIGITS));
return trailingDistance;
}
//+------------------------------------------------------------------+
//| Returns the amount for the given percentage. |
//+------------------------------------------------------------------+
double GetPercentage(string name, ENUM_ORDER_TYPE dir, double percentage)
{
double bid, ask, amount = 0.0;
if(dir == ORDER_TYPE_BUY)
{
bid = SymbolInfoDouble(name, SYMBOL_BID);
amount = bid * percentage / 100;
}
if(dir == ORDER_TYPE_SELL)
{
ask = SymbolInfoDouble(name, SYMBOL_ASK);
amount = ask * percentage / 100;
}
trailingDistance = NormalizeDouble(amount,
(int)SymbolInfoInteger(name, SYMBOL_DIGITS));
return trailingDistance;
}
//+------------------------------------------------------------------+
//| Returns the stoploss based on input values |
//+------------------------------------------------------------------+
double GetStopLoss(string name, ENUM_ORDER_TYPE dir, bool checkIdle, bool print = false)
{
double bid = 0, ask = 0, percentage_amount = 0, atr_amount = 0, stopLoss = 0;
if(dir == ORDER_TYPE_BUY)
{
if(useATRStopLoss && !checkIdle)
{
atr_amount = AverageTrueRange(name, TimeframeOfATR, PeriodsInATR);
stopLoss = atr_amount;
//if(percentage_amount != 0)
// stopLoss = fmin(percentage_amount, atr_amount);
}
if(usePercentageStopLoss && !checkIdle)
{
percentage_amount = GetPercentage(name, dir, BuyStopLossPercentage);
stopLoss = percentage_amount;
}
if(checkIdle && BuyMoveStopLossIfIdle)
{
percentage_amount = GetPercentage(name, dir, BuyPercentageToMoveStopLossIfIdle);
stopLoss = percentage_amount;
}
bid = SymbolInfoDouble(Sym, SYMBOL_BID);
if(print)
Print("Bid ", bid, ", stoploss ", stopLoss, " bid - sl ", bid - stopLoss);
stopLoss = bid - stopLoss;//trailingDistance;
}
else
if(dir == ORDER_TYPE_SELL)
{
if(useATRStopLoss && !checkIdle)
{
atr_amount = AverageTrueRange(name, TimeframeOfATR, PeriodsInATR);
stopLoss = atr_amount;
//if(percentage_amount != 0)
// stopLoss = fmin(percentage_amount, atr_amount);
}
if(usePercentageStopLoss && !checkIdle)
{
percentage_amount = GetPercentage(name, dir, SellStopLossPercentage);
stopLoss = percentage_amount;
}
if(SellMoveStopLossIfIdle && checkIdle)
{
percentage_amount = GetPercentage(name, dir, SellStopLossPercentage);
stopLoss = percentage_amount;
}
ask = SymbolInfoDouble(Sym, SYMBOL_ASK);
if(print)
Print("Ask ", ask, ", stoploss ", stopLoss, " ask + sl ", ask + stopLoss);
stopLoss = ask + stopLoss;//trailingDistance;
}
if(stopLoss == ask || stopLoss == bid)
return (0);
else
return NormalizeDouble(stopLoss, (int)SymbolInfoInteger(name, SYMBOL_DIGITS));
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ModifyStopLoss()
{
if(BuyMoveStopLossIfIdle || SellMoveStopLossIfIdle)
ModifyStopLossByIdleTime();
if(usePercentageStopLoss || useATRStopLoss)
ModifyStops();
}
//+------------------------------------------------------------------+
//| Returns the current stoploss and creates one if it does not exist|
//+------------------------------------------------------------------+
double CurrentServerStopLoss()
{
double currentStopLoss = PositionGetDouble(POSITION_SL);
if(currentStopLoss == 0 && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
currentStopLoss = GetStopLoss(Sym, ORDER_TYPE_BUY, false);
if(!trade.PositionModify(Sym, currentStopLoss, 0))
Print(" Buy Modify fails, bid: ", SymbolInfoDouble(Sym, SYMBOL_BID), " current stop loss: ", currentStopLoss);
}
if(currentStopLoss == 0 && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
{
currentStopLoss = GetStopLoss(Sym, ORDER_TYPE_SELL, false);;
if(!trade.PositionModify(Sym, currentStopLoss, 0))
Print(" Sell Modify fails, ask: ", SymbolInfoDouble(Sym, SYMBOL_ASK), " current stop loss: ", currentStopLoss);
}
return currentStopLoss;
}
//+------------------------------------------------------------------+
//| Returns the current stoploss and creates one if it does not exist|
//+------------------------------------------------------------------+
double CurrentVirtualStopLoss(int i)
{
if(Virt[i].request.sl == 0)
{
if(Virt[i].request.type == ORDER_TYPE_BUY)
Virt[i].request.sl = GetStopLoss(Sym, ORDER_TYPE_BUY, false);
if(Virt[i].request.type == ORDER_TYPE_SELL)
Virt[i].request.sl = GetStopLoss(Sym, ORDER_TYPE_SELL, false);
}
return Virt[i].request.sl;
}
//+------------------------------------------------------------------+
//| Returns the current buy stop loss value with trailing |
//+------------------------------------------------------------------+
double TrailingBuyStopLoss()
{
double bid = SymbolInfoDouble(Sym, SYMBOL_BID);
return bid - trailingDistance;
}
//+------------------------------------------------------------------+
//| Returns the current sell stop loss value with trailing |
//+------------------------------------------------------------------+
double TrailingSellStopLoss()
{
double ask = SymbolInfoDouble(Sym, SYMBOL_ASK);
return ask + trailingDistance;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ModifyServerSideStops()
{
double buyStopLoss = 0; // = GetStopLoss(Sym, Buy, false);
double sellStopLoss = 0; // = GetStopLoss(Sym, Sell, false);
for(int i = PositionsTotal() - 1; i >= 0; i--)
if(Sym == PositionGetSymbol(i))
{
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
if(TrailingBuyStopLoss() - CurrentServerStopLoss() > MinMovement)
if(!trade.PositionModify(Sym, TrailingBuyStopLoss(), 0))
Print("[FAIL]*** Failed BUY Stop Loss at ", TrailingBuyStopLoss(),
" Current was ", CurrentServerStopLoss());
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
if(CurrentServerStopLoss() - TrailingSellStopLoss() > MinMovement)
if(!trade.PositionModify(Sym, TrailingSellStopLoss(), 0))
Print("[FAIL]*** Failed SELL Stop Loss at ", TrailingSellStopLoss(),
" Current was ", CurrentServerStopLoss());
}
}
//+------------------------------------------------------------------+
//| Modifies virtual stop losses |
//+------------------------------------------------------------------+
void ModifyVirtualStops()
{
for(int i = ArraySize(Virt) - 1; i >= 0; i--)
if(Virt[i].active)
{
double oldStopLoss = Virt[i].request.sl;
if(oldStopLoss == 0.0)
oldStopLoss = GetStopLoss(Virt[i].request.symbol, Virt[i].request.type, false);
if(Virt[i].request.type == ORDER_TYPE_BUY)
if(TrailingBuyStopLoss() - CurrentVirtualStopLoss(i) > MinMovement)
{
Virt[i].request.sl = TrailingBuyStopLoss();
if(Virt[i].request.sl > Virt[i].request.price)
ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrNavy);
if(!MQLInfoInteger(MQL_TESTER))
{
double dist = Virt[i].request.sl - oldStopLoss;
dist /= SymbolInfoDouble(Virt[i].request.symbol, SYMBOL_POINT);
int distance = (int) dist;
string s = "s";
if(distance == 1)
s = "";
string stoplossMessage = MQLInfoString(MQL_PROGRAM_NAME) + " " + Virt[i].request.symbol +
" stoploss: " + (string)oldStopLoss + " moved " +
(string)distance + " points" + s + " to: "
+ (string)Virt[i].request.sl;
SendModifyNotice(Virt[i].request.type, Virt[i].request.sl, Virt[i].request.volume);
//Print(Virt[i].request.symbol, " stoploss: ", oldStopLoss, " moved ", distance, " points", s, " to: ", Virt[i].request.sl);
}
HLineMove(0, "StopLoss", Virt[i].request.sl);
}
if(Virt[i].request.type == ORDER_TYPE_SELL)
if(CurrentVirtualStopLoss(i) - TrailingSellStopLoss() > MinMovement)
{
Virt[i].request.sl = TrailingSellStopLoss();
if(Virt[i].request.sl < Virt[i].request.price)
ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrNavy);
if(!MQLInfoInteger(MQL_TESTER))
{
double dist = Virt[i].request.sl - oldStopLoss;
dist /= SymbolInfoDouble(Virt[i].request.symbol, SYMBOL_POINT);
int distance = (int) dist;
string s = "s";
if(distance == 1 || distance == -1)
s = "";
string stoplossMessage = MQLInfoString(MQL_PROGRAM_NAME) + " " + Virt[i].request.symbol +
" stoploss: " + (string)oldStopLoss + " moved " +
(string)distance + " points" + s + " to: "
+ (string)Virt[i].request.sl;
SendModifyNotice(Virt[i].request.type, Virt[i].request.sl, Virt[i].request.volume);
//Print(Virt[i].request.symbol, " stoploss: ", oldStopLoss, " moved ", distance, " point", s, " to: ", Virt[i].request.sl);
}
HLineMove(0, "StopLoss", Virt[i].request.sl);
}
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ModifyStops()
{
if(!useVirtualStops)
ModifyServerSideStops();
ModifyVirtualStops();
}
//+------------------------------------------------------------------+
//| Checks and modifies stop losses that are too long idle |
//+------------------------------------------------------------------+
void ModifyStopLossByIdleTime()
{
datetime idleTime = TimeCurrent() - TimeStopLossWasLastModified;
double idleTimeInMinutes = (long) idleTime / 60.0; // 1 min = 60 seconds;
bool aModificationHappened = false;
string type = " BUY ";
if((idleTimeInMinutes > BuyIdleMinutesRequiringAMove
&& IdleMovements < BuyIdleMovements
&& BuyMoveStopLossIfIdle)
|| (idleTimeInMinutes > SellIdleMinutesRequiringAMove
&& IdleMovements < SellIdleMovements
&& SellMoveStopLossIfIdle))
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
if(Sym == PositionGetSymbol(i))
{
// Get the current stop loss
double currentStopLoss = PositionGetDouble(POSITION_SL);
// If current stop loss = 0 skip
if(currentStopLoss == 0)
continue;
ulong ticket = PositionGetInteger(POSITION_TICKET);
//double currentStopLoss = PositionGetDouble(POSITION_SL);
// Get the direction
if(BuyMoveStopLossIfIdle)
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
if(GetStopLoss(Sym, ORDER_TYPE_BUY, true) - currentStopLoss > MinMovement)
if(trade.PositionModify(ticket, GetStopLoss(Sym, ORDER_TYPE_BUY, true), 0))
aModificationHappened = true;
else
Print("[FAIL]*** Failed BUY Stop Loss at ", GetStopLoss(Sym, ORDER_TYPE_BUY, true),
" Current was ", currentStopLoss);
if(SellMoveStopLossIfIdle)
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
if(currentStopLoss - GetStopLoss(Sym, ORDER_TYPE_SELL, true) > MinMovement)
if(trade.PositionModify(ticket, GetStopLoss(Sym, ORDER_TYPE_SELL, true), 0))
{
type = " SELL ";
aModificationHappened = true;
}
else
Print("[FAIL]*** Failed SELL Stop Loss at ", GetStopLoss(Sym, ORDER_TYPE_SELL, true),
" Current was ", currentStopLoss);
}
for(int i = ArraySize(Virt) - 1; i >= 0; i--)
if(Virt[i].active)
{
// Get the current stop loss
double currentStopLoss = Virt[i].request.sl;
// Get the current stop loss
ulong ticket = Virt[i].request.order;
// Get the direction
if(BuyMoveStopLossIfIdle)
if(Virt[i].request.type == ORDER_TYPE_BUY)
if(GetStopLoss(Sym, ORDER_TYPE_BUY, true) - currentStopLoss > MinMovement)
{
Virt[i].request.sl = GetStopLoss(Sym, ORDER_TYPE_BUY, true);
aModificationHappened = true;
}
if(SellMoveStopLossIfIdle)
if(Virt[i].request.type == ORDER_TYPE_SELL)
if(currentStopLoss - GetStopLoss(Sym, ORDER_TYPE_SELL, true) > MinMovement)
{
Virt[i].request.sl = GetStopLoss(Sym, ORDER_TYPE_SELL, true);
aModificationHappened = true;
}
}
}
if(aModificationHappened)
{
if(!MQLInfoInteger(MQL_TESTER))
Print("[MOVED", type, "STOPLOSS BY IDLE TIME] movement #", IdleMovements);
IdleMovements++;
TimeStopLossWasLastModified = TimeCurrent();
}
}
//+------------------------------------------------------------------+
////+------------------------------------------------------------------+
////| Modifies the stoploss if needed |
////+------------------------------------------------------------------+
//void ModifyStopLossByPercentage()
// {
// double buyStopLoss = GetStopLoss(Sym, Buy, false);
// double sellStopLoss = GetStopLoss(Sym, Sell, false);
//
// for(int i = PositionsTotal() - 1; i >= 0; i--)
// if(Sym == PositionGetSymbol(i))
// {
// // Get the current stop loss
// double currentStopLoss = PositionGetDouble(POSITION_SL);
// // If current stop loss = 0 skip
// if(currentStopLoss == 0)
// continue;
//
// ulong ticket = PositionGetInteger(POSITION_TICKET);
// //double currentStopLoss = PositionGetDouble(POSITION_SL);
// // Get the direction
// if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY
// && currentStopLoss < buyStopLoss)
// if(buyStopLoss - currentStopLoss > MinMovement)
// if(!trade.PositionModify(ticket, buyStopLoss, 0))
// Print("[FAIL]*** Failed BUY Stop Loss at ", buyStopLoss,
// " Current was ", currentStopLoss);
// if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL
// && currentStopLoss > sellStopLoss)
// if(currentStopLoss - sellStopLoss > MinMovement)
// if(!trade.PositionModify(ticket, sellStopLoss, 0))
// Print("[FAIL]*** Failed SELL Stop Loss at ", sellStopLoss,
// " Current was ", currentStopLoss);
// }
//
// for(int i = ArraySize(Virt) - 1; i >= 0; i--)
// if(Virt[i].active)
// {
// double currentStopLoss = Virt[i].request.sl;
// // Get the current stop loss
// ulong ticket = Virt[i].request.order;
// //double currentStopLoss = PositionGetDouble(POSITION_SL);
// // Get the direction
// if(Virt[i].request.type == ORDER_TYPE_BUY
// && currentStopLoss < buyStopLoss)
// if(buyStopLoss - currentStopLoss > MinMovement)
// Virt[i].request.sl = buyStopLoss;
// if(Virt[i].request.type == ORDER_TYPE_SELL
// && currentStopLoss > sellStopLoss)
// if(currentStopLoss - sellStopLoss > MinMovement)
// Virt[i].request.sl = sellStopLoss;
// }
// }