mql5/Experts/Previous/ATR_QQE_EA_v3.mq5

858 lines
28 KiB
MQL5
Raw Permalink Normal View History

2025-07-20 16:41:20 +01:00
//+------------------------------------------------------------------+
//| ATR QQE Trading System.mq5 |
//| Advanced QQE with Flexible Exit & Risk Management |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025"
#property link ""
#property version "3.00"
#property strict
#property description "Advanced QQE system with QQE/ATR exits and equity-based risk management"
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\OrderInfo.mqh>
// Enums (must be declared before inputs)
enum ENUM_EXIT_METHOD
{
EXIT_ATR, // ATR-based exits only
EXIT_QQE, // QQE-based exits only
EXIT_HYBRID // Both QQE and ATR exits
};
// EA input parameters
input group "Risk Management Method"
input bool Use_Equity_Risk = false; // Use equity % risk instead of ATR
input double Risk_Percent = 2.0; // Risk % of equity per trade
input bool Use_Fixed_Lot = true; // Use fixed lot size when not using equity risk
input group "ATR Risk Parameters"
input int ATR_Period = 14; // ATR Period
input double SL_ATR_Multiplier = 3.0; // Stop Loss ATR Multiplier
input double TP_ATR_Multiplier = 3.5; // Take Profit ATR Multiplier
input double Trail_Start_Multiplier = 2.5; // Trailing Start ATR Multiplier
input double Trail_Step_Multiplier = 0.5; // Trailing Step ATR Multiplier
input int ATR_Shift = 1; // ATR shift (1 for previous bar)
input double Extra_Buffer_Pips = 5; // Extra buffer in pips
input group "Exit Strategy Selection"
input ENUM_EXIT_METHOD Exit_Method = EXIT_ATR; // Exit method
input bool Use_QQE_Exit_Signals = false; // Enable QQE-based exits
input bool Use_ATR_Trailing = true; // Enable ATR trailing stops
input bool Use_Fixed_TP = false; // Use fixed take profit
input group "QQE Parameters (Proper QQE Implementation)"
input int QQE_RSI_Period = 14; // QQE RSI Period
input int QQE_Smoothing_Factor = 5; // QQE Smoothing Factor (SF)
input double QQE_Signal_Level = 50; // QQE Signal Level (center line)
input bool Use_QQE_Crossover = true; // Use QQE Main/Smoothed crossover signals
input bool Use_QQE_Level = false; // Use QQE level crossing signals
input group "QQE Exit Parameters"
input bool QQE_Exit_On_Opposite_Signal = true; // Close on opposite QQE signal
input bool QQE_Exit_On_Overbought = false; // Close longs at overbought (>70)
input bool QQE_Exit_On_Oversold = false; // Close shorts at oversold (<30)
input double QQE_Overbought_Level = 70; // Overbought level for exits
input double QQE_Oversold_Level = 30; // Oversold level for exits
input group "Trading Parameters"
input bool Manage_SL = true; // Manage Stop Loss
input bool Manage_TP = true; // Manage Take Profit
input bool Close_At_Profit_Target = false; // Close position at profit target instead of setting TP
input double Lot_Size = 0.1; // Fixed Lot Size
input bool Allow_Long_Positions = true; // Allow Long Positions
input bool Allow_Short_Positions = true; // Allow Short Positions
input int Max_Positions = 1; // Maximum concurrent positions
input group "Timing Parameters"
input int Check_Interval_Seconds = 5; // Interval between checks (seconds)
input bool Process_On_Bar_Close = true; // Process only on bar close
// Global variables
CTrade trade;
CPositionInfo posInfo;
COrderInfo orderInfo;
double atrValue;
double point;
int digits;
datetime lastCheckTime = 0;
datetime lastBarTime = 0;
int atrHandle;
int rsiHandle;
int positionCount = 0;
ulong positionTickets[];
// QQE calculation variables
int Wilders_Period;
int StartBar;
// QQE buffers
double TrLevelSlow[]; // QQE Smoothed line
double AtrRsi[]; // ATR of RSI
double MaAtrRsi[]; // MA of ATR RSI
double Rsi[]; // Raw RSI values
double RsiMa[]; // QQE Main line (MA of RSI)
double MaMaAtrRsi[]; // MA of MA of ATR RSI
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Initialize trade operations
trade.SetExpertMagicNumber(123456);
// Initialize market information
digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
// Initialize QQE calculation parameters
Wilders_Period = QQE_RSI_Period * 2 - 1;
StartBar = MathMax(QQE_Smoothing_Factor, Wilders_Period);
// Create indicator handles
atrHandle = iATR(_Symbol, PERIOD_CURRENT, ATR_Period);
rsiHandle = iRSI(_Symbol, PERIOD_CURRENT, QQE_RSI_Period, PRICE_CLOSE);
if(atrHandle == INVALID_HANDLE || rsiHandle == INVALID_HANDLE)
{
Print("Error creating indicator handles");
return(INIT_FAILED);
}
// Initialize QQE arrays
ArraySetAsSeries(TrLevelSlow, true);
ArraySetAsSeries(AtrRsi, true);
ArraySetAsSeries(MaAtrRsi, true);
ArraySetAsSeries(Rsi, true);
ArraySetAsSeries(RsiMa, true);
ArraySetAsSeries(MaMaAtrRsi, true);
// Validate settings (cannot modify input parameters, only check them)
if(Use_Equity_Risk && Risk_Percent <= 0)
{
Print("Error: Risk percentage must be greater than 0. Please set Risk_Percent > 0 in inputs.");
return(INIT_PARAMETERS_INCORRECT);
}
// Validate other critical parameters
if(ATR_Period <= 0)
{
Print("Error: ATR_Period must be greater than 0");
return(INIT_PARAMETERS_INCORRECT);
}
if(QQE_RSI_Period <= 0)
{
Print("Error: QQE_RSI_Period must be greater than 0");
return(INIT_PARAMETERS_INCORRECT);
}
if(QQE_Smoothing_Factor <= 0)
{
Print("Error: QQE_Smoothing_Factor must be greater than 0");
return(INIT_PARAMETERS_INCORRECT);
}
// Set timer
if(!EventSetTimer(Check_Interval_Seconds))
{
Print("Error setting timer");
return(INIT_FAILED);
}
RefreshPositions();
Print("ATR QQE Advanced Trading System initialized successfully");
Print("Exit Method: ", EnumToString(Exit_Method));
Print("Risk Management: ", Use_Equity_Risk ? "Equity-based" : "ATR-based");
Print("ATR Period: ", ATR_Period, " QQE RSI Period: ", QQE_RSI_Period);
Print("StartBar requirement: ", StartBar, " bars");
// Initial update to check data availability
UpdateIndicators();
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
IndicatorRelease(atrHandle);
IndicatorRelease(rsiHandle);
EventKillTimer();
Comment("");
}
//+------------------------------------------------------------------+
//| Timer function |
//+------------------------------------------------------------------+
void OnTimer()
{
if(!IsTimeToCheck())
return;
UpdateIndicators();
ManagePositions();
CheckForEntrySignals();
lastCheckTime = TimeCurrent();
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
if(Process_On_Bar_Close)
{
datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
if(currentBarTime != lastBarTime)
{
lastBarTime = currentBarTime;
UpdateIndicators();
ManagePositions();
CheckForEntrySignals();
}
return;
}
if(IsTimeToCheck())
{
UpdateIndicators();
ManagePositions();
CheckForEntrySignals();
lastCheckTime = TimeCurrent();
}
}
//+------------------------------------------------------------------+
//| Check if it's time to process |
//+------------------------------------------------------------------+
bool IsTimeToCheck()
{
return (TimeCurrent() - lastCheckTime >= Check_Interval_Seconds);
}
//+------------------------------------------------------------------+
//| Calculate lot size based on equity risk |
//+------------------------------------------------------------------+
double CalculateLotSize(double entryPrice, double stopLoss)
{
if(!Use_Equity_Risk || Use_Fixed_Lot)
{
return Lot_Size; // Use fixed lot size
}
double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskAmount = accountBalance * Risk_Percent / 100.0;
double stopDistance = MathAbs(entryPrice - stopLoss);
if(stopDistance == 0) return Lot_Size;
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
if(tickValue == 0 || tickSize == 0) return Lot_Size;
double lotSize = riskAmount / (stopDistance / tickSize * tickValue);
// Apply lot size limits
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
lotSize = MathMax(lotSize, minLot);
lotSize = MathMin(lotSize, maxLot);
// Round to lot step
lotSize = MathFloor(lotSize / lotStep) * lotStep;
return lotSize;
}
//+------------------------------------------------------------------+
//| Exponential Moving Average |
//+------------------------------------------------------------------+
void CalculateEMA(int begin, int period, const double &price[], double &result[])
{
if(period <= 0) return;
if(begin < 0) begin = 0;
int priceSize = ArraySize(price);
int resultSize = ArraySize(result);
// Ensure we don't go out of bounds
if(priceSize == 0 || resultSize == 0) return;
if(begin >= priceSize || begin >= resultSize) return;
double SmoothFactor = 2.0 / (1.0 + period);
for(int i = begin; i >= 0; i--)
{
// Check bounds before accessing arrays
if(i >= priceSize || i >= resultSize) continue;
if(i + 1 >= resultSize)
{
// Initialize first value
if(price[i] == EMPTY_VALUE)
result[i] = 0;
else
result[i] = price[i];
continue;
}
if(price[i] == EMPTY_VALUE)
result[i] = 0;
else
result[i] = price[i] * SmoothFactor + result[i + 1] * (1.0 - SmoothFactor);
}
}
//+------------------------------------------------------------------+
//| Calculate proper QQE values |
//+------------------------------------------------------------------+
bool CalculateQQE(int bars_needed)
{
int total_bars = bars_needed + StartBar + 10;
// Check if we have enough bars
int available_bars = iBars(_Symbol, PERIOD_CURRENT);
if(available_bars < total_bars)
{
total_bars = available_bars - 1;
if(total_bars < StartBar + 10)
{
Print("Not enough historical data. Need at least ", StartBar + 10, " bars, have ", available_bars);
return false;
}
}
// Ensure minimum size
if(total_bars < 50)
{
Print("Insufficient bars for QQE calculation: ", total_bars);
return false;
}
// Resize arrays safely
if(!ArrayResize(Rsi, total_bars) ||
!ArrayResize(RsiMa, total_bars) ||
!ArrayResize(AtrRsi, total_bars) ||
!ArrayResize(MaAtrRsi, total_bars) ||
!ArrayResize(MaMaAtrRsi, total_bars) ||
!ArrayResize(TrLevelSlow, total_bars))
{
Print("Failed to resize QQE arrays");
return false;
}
// Get RSI values with error checking
if(CopyBuffer(rsiHandle, 0, 0, total_bars, Rsi) != total_bars)
{
Print("Error getting RSI values. Requested: ", total_bars, " Error: ", GetLastError());
return false;
}
// Initialize arrays
ArrayInitialize(RsiMa, 0.0);
ArrayInitialize(AtrRsi, 0.0);
ArrayInitialize(MaAtrRsi, 0.0);
ArrayInitialize(MaMaAtrRsi, 0.0);
ArrayInitialize(TrLevelSlow, 0.0);
// Calculate RSI MA with bounds checking
if(total_bars > QQE_Smoothing_Factor)
{
CalculateEMA(total_bars - 1, QQE_Smoothing_Factor, Rsi, RsiMa);
}
// Calculate ATR of RSI MA
for(int i = total_bars - 2; i >= 0; i--)
{
if(i + 1 < total_bars && i >= 0)
AtrRsi[i] = MathAbs(RsiMa[i + 1] - RsiMa[i]);
}
// Calculate smoothed values with bounds checking
if(total_bars > Wilders_Period)
{
CalculateEMA(total_bars - 2, Wilders_Period, AtrRsi, MaAtrRsi);
CalculateEMA(total_bars - 2, Wilders_Period, MaAtrRsi, MaMaAtrRsi);
}
// Calculate QQE Smoothed line
int start_index = total_bars - StartBar - 1;
if(start_index < 0) start_index = total_bars - 1;
double tr = 0;
double rsi1 = 0;
if(start_index >= 0 && start_index < total_bars)
{
tr = TrLevelSlow[start_index];
rsi1 = RsiMa[start_index];
}
for(int i = start_index - 1; i >= 0; i--)
{
if(i < 0 || i >= total_bars) continue;
double rsi0 = RsiMa[i];
double dar = MaMaAtrRsi[i] * 4.236;
double dv = tr;
if(rsi0 < tr)
{
tr = rsi0 + dar;
if((rsi1 < dv) && (tr > dv)) tr = dv;
}
else if(rsi0 > tr)
{
tr = rsi0 - dar;
if((rsi1 > dv) && (tr < dv)) tr = dv;
}
TrLevelSlow[i] = tr;
rsi1 = rsi0;
}
return true;
}
//+------------------------------------------------------------------+
//| Update indicators |
//+------------------------------------------------------------------+
void UpdateIndicators()
{
double atrBuffer[];
if(CopyBuffer(atrHandle, 0, ATR_Shift, 1, atrBuffer) > 0)
{
atrValue = NormalizeDouble(atrBuffer[0], digits);
if(atrValue <= 0)
{
Print("Warning: ATR calculation returned zero. Using fallback value.");
atrValue = 0.0001;
}
}
else
{
Print("Error getting ATR value: ", GetLastError());
atrValue = 0.0001; // Use fallback
return;
}
// Only calculate QQE if we have enough bars
if(iBars(_Symbol, PERIOD_CURRENT) > StartBar + 50)
{
if(!CalculateQQE(50)) // Reduced from 100 to 50
{
Print("Error calculating QQE values");
}
}
else
{
Print("Waiting for more historical data. Current bars: ", iBars(_Symbol, PERIOD_CURRENT), " Need: ", StartBar + 50);
}
}
//+------------------------------------------------------------------+
//| Check for QQE entry signals |
//+------------------------------------------------------------------+
void CheckForEntrySignals()
{
if(positionCount >= Max_Positions)
return;
// Check if we have enough data and valid indicators
if(ArraySize(RsiMa) < 3 || ArraySize(TrLevelSlow) < 3 || atrValue <= 0)
{
Print("Insufficient data for signal generation. RSI MA size: ", ArraySize(RsiMa), " ATR: ", atrValue);
return;
}
// Additional validation
if(RsiMa[1] == 0 || TrLevelSlow[1] == 0 || RsiMa[2] == 0 || TrLevelSlow[2] == 0)
{
Print("Invalid QQE values detected. Skipping signal check.");
return;
}
double currentRsiMa = RsiMa[1];
double prevRsiMa = RsiMa[2];
double currentSmoothed = TrLevelSlow[1];
double prevSmoothed = TrLevelSlow[2];
bool buySignal = false;
bool sellSignal = false;
// QQE Crossover signals
if(Use_QQE_Crossover)
{
if(prevRsiMa <= prevSmoothed && currentRsiMa > currentSmoothed)
{
buySignal = true;
Print("QQE Crossover Buy Signal");
}
if(prevRsiMa >= prevSmoothed && currentRsiMa < currentSmoothed)
{
sellSignal = true;
Print("QQE Crossover Sell Signal");
}
}
// QQE Level crossing signals
if(Use_QQE_Level && !buySignal && !sellSignal)
{
double prevLevel = RsiMa[2];
double currentLevel = RsiMa[1];
if(prevLevel < QQE_Signal_Level && currentLevel > QQE_Signal_Level)
{
buySignal = true;
Print("QQE Level Buy Signal");
}
if(prevLevel > QQE_Signal_Level && currentLevel < QQE_Signal_Level)
{
sellSignal = true;
Print("QQE Level Sell Signal");
}
}
double currentPrice;
double stopLoss, takeProfit;
double bufferInPoints = Extra_Buffer_Pips * 10 * point;
// Execute Buy Signal
if(buySignal && Allow_Long_Positions)
{
currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
stopLoss = Manage_SL ? NormalizeDouble(currentPrice - (atrValue * SL_ATR_Multiplier) - bufferInPoints, digits) : 0;
takeProfit = Manage_TP && !Close_At_Profit_Target ? NormalizeDouble(currentPrice + (atrValue * TP_ATR_Multiplier), digits) : 0;
double lotSize = CalculateLotSize(currentPrice, stopLoss);
if(!trade.Buy(lotSize, _Symbol, 0, stopLoss, takeProfit, "QQE ATR Buy"))
{
Print("Buy order failed with error: ", GetLastError());
}
else
{
Print("QQE Buy executed - Lot Size: ", lotSize, " Entry: ", currentPrice);
if(Use_Equity_Risk)
Print("Risk: ", Risk_Percent, "% of equity = $", (AccountInfoDouble(ACCOUNT_BALANCE) * Risk_Percent / 100.0));
}
}
// Execute Sell Signal
if(sellSignal && Allow_Short_Positions)
{
currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
stopLoss = Manage_SL ? NormalizeDouble(currentPrice + (atrValue * SL_ATR_Multiplier) + bufferInPoints, digits) : 0;
takeProfit = Manage_TP && !Close_At_Profit_Target ? NormalizeDouble(currentPrice - (atrValue * TP_ATR_Multiplier), digits) : 0;
double lotSize = CalculateLotSize(currentPrice, stopLoss);
if(!trade.Sell(lotSize, _Symbol, 0, stopLoss, takeProfit, "QQE ATR Sell"))
{
Print("Sell order failed with error: ", GetLastError());
}
else
{
Print("QQE Sell executed - Lot Size: ", lotSize, " Entry: ", currentPrice);
if(Use_Equity_Risk)
Print("Risk: ", Risk_Percent, "% of equity = $", (AccountInfoDouble(ACCOUNT_BALANCE) * Risk_Percent / 100.0));
}
}
}
//+------------------------------------------------------------------+
//| Check for QQE exit signals |
//+------------------------------------------------------------------+
bool CheckQQEExitSignals(ENUM_POSITION_TYPE posType)
{
if(!Use_QQE_Exit_Signals)
return false;
if(ArraySize(RsiMa) < 3 || ArraySize(TrLevelSlow) < 3)
return false;
double currentRsiMa = RsiMa[1];
double prevRsiMa = RsiMa[2];
double currentSmoothed = TrLevelSlow[1];
double prevSmoothed = TrLevelSlow[2];
// Opposite crossover signals
if(QQE_Exit_On_Opposite_Signal)
{
if(posType == POSITION_TYPE_BUY)
{
// Exit long on bearish crossover
if(prevRsiMa >= prevSmoothed && currentRsiMa < currentSmoothed)
{
Print("QQE Exit Signal: Long position - bearish crossover detected");
return true;
}
}
else if(posType == POSITION_TYPE_SELL)
{
// Exit short on bullish crossover
if(prevRsiMa <= prevSmoothed && currentRsiMa > currentSmoothed)
{
Print("QQE Exit Signal: Short position - bullish crossover detected");
return true;
}
}
}
// Overbought/Oversold exits
if(QQE_Exit_On_Overbought && posType == POSITION_TYPE_BUY)
{
if(currentRsiMa > QQE_Overbought_Level)
{
Print("QQE Exit Signal: Long position - overbought level reached");
return true;
}
}
if(QQE_Exit_On_Oversold && posType == POSITION_TYPE_SELL)
{
if(currentRsiMa < QQE_Oversold_Level)
{
Print("QQE Exit Signal: Short position - oversold level reached");
return true;
}
}
return false;
}
//+------------------------------------------------------------------+
//| Refresh list of position tickets |
//+------------------------------------------------------------------+
void RefreshPositions()
{
int total = PositionsTotal();
int count = 0;
for(int i = 0; i < total; i++)
{
if(posInfo.SelectByIndex(i))
{
if(posInfo.Symbol() == _Symbol && posInfo.Magic() == 123456)
count++;
}
}
ArrayResize(positionTickets, count);
positionCount = 0;
for(int i = 0; i < total; i++)
{
if(posInfo.SelectByIndex(i))
{
if(posInfo.Symbol() == _Symbol && posInfo.Magic() == 123456)
{
positionTickets[positionCount] = posInfo.Ticket();
positionCount++;
}
}
}
}
//+------------------------------------------------------------------+
//| Manage all positions |
//+------------------------------------------------------------------+
void ManagePositions()
{
RefreshPositions();
double bufferInPoints = Extra_Buffer_Pips * 10 * point;
// Display current info
string info = "QQE ATR Advanced Trading System\n";
info += "Exit Method: " + EnumToString(Exit_Method) + "\n";
info += "Risk Method: " + (Use_Equity_Risk ? "Equity " + DoubleToString(Risk_Percent, 1) + "%" : "ATR-based") + "\n";
info += "Current ATR: " + DoubleToString(atrValue, digits) + "\n";
if(ArraySize(RsiMa) > 1 && ArraySize(TrLevelSlow) > 1)
{
info += "RSI MA: " + DoubleToString(RsiMa[1], 2) + "\n";
info += "QQE Smoothed: " + DoubleToString(TrLevelSlow[1], 2) + "\n";
}
info += "Positions: " + IntegerToString(positionCount) + "/" + IntegerToString(Max_Positions);
Comment(info);
// Process existing positions
for(int i = 0; i < positionCount; i++)
{
if(posInfo.SelectByTicket(positionTickets[i]))
{
ProcessPosition(posInfo.PositionType(), posInfo.PriceOpen(),
posInfo.StopLoss(), posInfo.TakeProfit(),
posInfo.Ticket(), bufferInPoints);
}
}
}
//+------------------------------------------------------------------+
//| Process a single position |
//+------------------------------------------------------------------+
void ProcessPosition(ENUM_POSITION_TYPE posType, double openPrice,
double currentSL, double currentTP, ulong ticket,
double bufferInPoints)
{
// Check for QQE-based exits first
if((Exit_Method == EXIT_QQE || Exit_Method == EXIT_HYBRID) && CheckQQEExitSignals(posType))
{
if(trade.PositionClose(ticket))
{
Print("Position #", ticket, " closed by QQE exit signal");
return;
}
}
// Only proceed with ATR management if using ATR exits
if(Exit_Method == EXIT_QQE)
return;
double newSL = 0, newTP = 0;
double trailingStartLevel = 0;
double currentPrice = 0;
bool modifyPosition = false;
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(posType == POSITION_TYPE_BUY)
{
currentPrice = bid;
if(Manage_SL)
{
newSL = NormalizeDouble(openPrice - (atrValue * SL_ATR_Multiplier) - bufferInPoints, digits);
}
else
{
newSL = currentSL;
}
if(Manage_TP && !Close_At_Profit_Target)
{
newTP = NormalizeDouble(openPrice + (atrValue * TP_ATR_Multiplier), digits);
}
else
{
newTP = currentTP;
}
if(Close_At_Profit_Target)
{
double profitTarget = openPrice + (atrValue * TP_ATR_Multiplier);
if(currentPrice >= profitTarget)
{
if(trade.PositionClose(ticket))
{
Print("Position #", ticket, " closed at ATR profit target");
}
return;
}
}
if(Use_ATR_Trailing && Manage_SL)
{
trailingStartLevel = NormalizeDouble(openPrice + (atrValue * Trail_Start_Multiplier), digits);
if(currentPrice >= trailingStartLevel)
{
double trailingSL = NormalizeDouble(currentPrice - (atrValue * Trail_Step_Multiplier) - bufferInPoints, digits);
if(trailingSL > currentSL || currentSL == 0)
{
newSL = trailingSL;
modifyPosition = true;
}
}
}
}
else if(posType == POSITION_TYPE_SELL)
{
currentPrice = ask;
if(Manage_SL)
{
newSL = NormalizeDouble(openPrice + (atrValue * SL_ATR_Multiplier) + bufferInPoints, digits);
}
else
{
newSL = currentSL;
}
if(Manage_TP && !Close_At_Profit_Target)
{
newTP = NormalizeDouble(openPrice - (atrValue * TP_ATR_Multiplier), digits);
}
else
{
newTP = currentTP;
}
if(Close_At_Profit_Target)
{
double profitTarget = openPrice - (atrValue * TP_ATR_Multiplier);
if(currentPrice <= profitTarget)
{
if(trade.PositionClose(ticket))
{
Print("Position #", ticket, " closed at ATR profit target");
}
return;
}
}
if(Use_ATR_Trailing && Manage_SL)
{
trailingStartLevel = NormalizeDouble(openPrice - (atrValue * Trail_Start_Multiplier), digits);
if(currentPrice <= trailingStartLevel)
{
double trailingSL = NormalizeDouble(currentPrice + (atrValue * Trail_Step_Multiplier) + bufferInPoints, digits);
if(trailingSL < currentSL || currentSL == 0)
{
newSL = trailingSL;
modifyPosition = true;
}
}
}
}
// Ensure SL doesn't move in wrong direction
if(posType == POSITION_TYPE_BUY && currentSL > 0 && newSL < currentSL)
{
newSL = currentSL;
}
else if(posType == POSITION_TYPE_SELL && currentSL > 0 && newSL > currentSL)
{
newSL = currentSL;
}
// Apply modifications
if((MathAbs(currentSL - newSL) > Point() && Manage_SL) ||
(MathAbs(currentTP - newTP) > Point() && Manage_TP) ||
modifyPosition)
{
if(trade.PositionModify(ticket, newSL, newTP))
{
string action = modifyPosition ? "ATR Trailing Stop" : "ATR SL/TP Update";
Print(action, " applied to position #", ticket);
}
else
{
Print("Failed to modify position #", ticket, " Error: ", GetLastError());
}
}
}