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