//+------------------------------------------------------------------+ //| ATR QQE Trading System.mq5 | //| QQE-Based Entry with ATR Risk Management | //+------------------------------------------------------------------+ #property copyright "Copyright 2025" #property link "" #property version "1.00" #property strict #property description "QQE-based trading system with dynamic ATR risk management" #include #include #include // EA input parameters 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 "QQE Parameters" input int QQE_RSI_Period = 14; // QQE RSI Period input int QQE_Smoothing_Factor = 5; // QQE Smoothing Factor input double QQE_Fast_Period = 2.618; // QQE Fast Period input double QQE_Slow_Period = 4.236; // QQE Slow Period input double QQE_Signal_Level = 50; // QQE Signal Level (50 = center line) input bool Use_QQE_Divergence = true; // Use QQE divergence signals input bool Use_QQE_Crossover = true; // Use QQE crossover signals input group "Trading Parameters" input bool Manage_SL = true; // Manage Stop Loss input bool Manage_TP = true; // Manage Take Profit input bool Use_Trailing_Stop = true; // Use Trailing Stop 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 arrays double qqeMain[]; double qqeSignal[]; double rsiValues[]; double qqePrevMain[]; double qqePrevSignal[]; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Initialize trade operations trade.SetExpertMagicNumber(123456); // Use a fixed magic number // Initialize market information digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); // 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 arrays ArraySetAsSeries(qqeMain, true); ArraySetAsSeries(qqeSignal, true); ArraySetAsSeries(rsiValues, true); ArraySetAsSeries(qqePrevMain, true); ArraySetAsSeries(qqePrevSignal, true); // Set timer if(!EventSetTimer(Check_Interval_Seconds)) { Print("Error setting timer"); return(INIT_FAILED); } // Initial identification of positions RefreshPositions(); Print("ATR QQE Trading System initialized successfully"); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Release indicator handles IndicatorRelease(atrHandle); IndicatorRelease(rsiHandle); // Remove timer EventKillTimer(); // Clean up Comment(""); } //+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { // Check if it's time to update if(!IsTimeToCheck()) return; // Update indicators and manage positions UpdateIndicators(); ManagePositions(); CheckForEntrySignals(); // Record the check time lastCheckTime = TimeCurrent(); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Check if we process only on bar close if(Process_On_Bar_Close) { datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0); if(currentBarTime != lastBarTime) { lastBarTime = currentBarTime; UpdateIndicators(); ManagePositions(); CheckForEntrySignals(); } return; } // Otherwise, check if it's time to update based on timer if(IsTimeToCheck()) { UpdateIndicators(); ManagePositions(); CheckForEntrySignals(); lastCheckTime = TimeCurrent(); } } //+------------------------------------------------------------------+ //| Check if it's time to process | //+------------------------------------------------------------------+ bool IsTimeToCheck() { if(TimeCurrent() - lastCheckTime >= Check_Interval_Seconds) return true; return false; } //+------------------------------------------------------------------+ //| Calculate QQE indicator | //+------------------------------------------------------------------+ void CalculateQQE() { // Get RSI values if(CopyBuffer(rsiHandle, 0, 0, 100, rsiValues) <= 0) { Print("Error getting RSI values"); return; } // Resize QQE arrays ArrayResize(qqeMain, 100); ArrayResize(qqeSignal, 100); // Calculate QQE for(int i = 99; i >= 0; i--) { if(i == 99) // First calculation { qqeMain[i] = rsiValues[i]; qqeSignal[i] = rsiValues[i]; } else { // QQE Main calculation (smoothed RSI) double smoothingFactor = 2.0 / (QQE_Smoothing_Factor + 1.0); qqeMain[i] = qqeMain[i+1] + smoothingFactor * (rsiValues[i] - qqeMain[i+1]); // QQE Signal calculation double fastFactor = 2.0 / (QQE_Fast_Period + 1.0); double slowFactor = 2.0 / (QQE_Slow_Period + 1.0); // Calculate intermediate value double intermediate = qqeSignal[i+1] + fastFactor * (qqeMain[i] - qqeSignal[i+1]); // Apply slow smoothing qqeSignal[i] = qqeSignal[i+1] + slowFactor * (intermediate - qqeSignal[i+1]); } } } //+------------------------------------------------------------------+ //| Update indicators | //+------------------------------------------------------------------+ void UpdateIndicators() { double atrBuffer[]; // Get current ATR value if(CopyBuffer(atrHandle, 0, ATR_Shift, 1, atrBuffer) > 0) { atrValue = NormalizeDouble(atrBuffer[0], digits); if(atrValue <= 0) { Print("Warning: ATR calculation returned zero or negative value. Using fallback value."); atrValue = 0.0001; // Fallback to prevent errors } } else { Print("Error getting ATR value: ", GetLastError()); } // Calculate QQE values CalculateQQE(); } //+------------------------------------------------------------------+ //| Check for QQE entry signals | //+------------------------------------------------------------------+ void CheckForEntrySignals() { // Check if we have reached maximum positions if(positionCount >= Max_Positions) return; // Make sure we have enough QQE data if(ArraySize(qqeMain) < 3 || ArraySize(qqeSignal) < 3) return; // Get current QQE values double currentQQEMain = qqeMain[0]; double currentQQESignal = qqeSignal[0]; double prevQQEMain = qqeMain[1]; double prevQQESignal = qqeSignal[1]; bool buySignal = false; bool sellSignal = false; // QQE Crossover signals if(Use_QQE_Crossover) { // Buy signal: QQE Main crosses above QQE Signal and both are below the signal level if(prevQQEMain <= prevQQESignal && currentQQEMain > currentQQESignal && currentQQEMain < QQE_Signal_Level) { buySignal = true; } // Sell signal: QQE Main crosses below QQE Signal and both are above the signal level if(prevQQEMain >= prevQQESignal && currentQQEMain < currentQQESignal && currentQQEMain > QQE_Signal_Level) { sellSignal = true; } } // QQE Divergence signals (simplified version) if(Use_QQE_Divergence && !buySignal && !sellSignal) { // Buy signal: QQE bounces off oversold level if(prevQQEMain < 30 && currentQQEMain > 30 && currentQQEMain > prevQQEMain) { buySignal = true; } // Sell signal: QQE bounces off overbought level if(prevQQEMain > 70 && currentQQEMain < 70 && currentQQEMain < prevQQEMain) { sellSignal = true; } } 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); // Calculate SL and TP 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; // Execute the buy order if(!trade.Buy(Lot_Size, _Symbol, 0, stopLoss, takeProfit, "QQE ATR Strategy Buy")) { Print("Buy order failed with error: ", GetLastError()); } else { Print("QQE Buy signal executed at ", currentPrice, " QQE Main: ", currentQQEMain, " QQE Signal: ", currentQQESignal); Print("SL: ", stopLoss, " TP: ", takeProfit); } } // Execute Sell Signal if(sellSignal && Allow_Short_Positions) { currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); // Calculate SL and TP 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; // Execute the sell order if(!trade.Sell(Lot_Size, _Symbol, 0, stopLoss, takeProfit, "QQE ATR Strategy Sell")) { Print("Sell order failed with error: ", GetLastError()); } else { Print("QQE Sell signal executed at ", currentPrice, " QQE Main: ", currentQQEMain, " QQE Signal: ", currentQQESignal); Print("SL: ", stopLoss, " TP: ", takeProfit); } } } //+------------------------------------------------------------------+ //| Refresh list of position tickets | //+------------------------------------------------------------------+ void RefreshPositions() { int total = PositionsTotal(); int count = 0; // First, count valid positions for(int i = 0; i < total; i++) { if(posInfo.SelectByIndex(i)) { if(posInfo.Symbol() == _Symbol && posInfo.Magic() == 123456) count++; } } // Resize array and fill with position tickets 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() { // Get current open positions RefreshPositions(); // Convert buffer from pips to points double bufferInPoints = Extra_Buffer_Pips * 10 * point; // Display current info string info = "QQE ATR Trading System\n"; info += "Current ATR: " + DoubleToString(atrValue, digits) + "\n"; if(ArraySize(qqeMain) > 0 && ArraySize(qqeSignal) > 0) { info += "QQE Main: " + DoubleToString(qqeMain[0], 2) + "\n"; info += "QQE Signal: " + DoubleToString(qqeSignal[0], 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])) { // Process the position 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) { double newSL = 0, newTP = 0; double trailingStartLevel = 0; double currentPrice = 0; bool modifyPosition = false; // Get current bid/ask double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); // Set SL and TP based on position type if(posType == POSITION_TYPE_BUY) { currentPrice = bid; // Calculate new stop loss if needed if(Manage_SL) { newSL = NormalizeDouble(openPrice - (atrValue * SL_ATR_Multiplier) - bufferInPoints, digits); } else { newSL = currentSL; } // Calculate new take profit if needed if(Manage_TP && !Close_At_Profit_Target) { newTP = NormalizeDouble(openPrice + (atrValue * TP_ATR_Multiplier), digits); } else { newTP = currentTP; } // Check for profit target if closing instead of TP if(Close_At_Profit_Target) { double profitTarget = openPrice + (atrValue * TP_ATR_Multiplier); if(currentPrice >= profitTarget) { if(trade.PositionClose(ticket)) { Print("Position #", ticket, " closed at profit target!"); } return; } } // Check for trailing stop if enabled if(Use_Trailing_Stop && 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; // Calculate new stop loss if needed if(Manage_SL) { newSL = NormalizeDouble(openPrice + (atrValue * SL_ATR_Multiplier) + bufferInPoints, digits); } else { newSL = currentSL; } // Calculate new take profit if needed if(Manage_TP && !Close_At_Profit_Target) { newTP = NormalizeDouble(openPrice - (atrValue * TP_ATR_Multiplier), digits); } else { newTP = currentTP; } // Check for profit target if closing instead of TP if(Close_At_Profit_Target) { double profitTarget = openPrice - (atrValue * TP_ATR_Multiplier); if(currentPrice <= profitTarget) { if(trade.PositionClose(ticket)) { Print("Position #", ticket, " closed at profit target!"); } return; } } // Check for trailing stop if enabled if(Use_Trailing_Stop && 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 we're not moving SL 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; } // Check if SL or TP needs to be changed if((MathAbs(currentSL - newSL) > Point() && Manage_SL) || (MathAbs(currentTP - newTP) > Point() && Manage_TP) || modifyPosition) { if(trade.PositionModify(ticket, newSL, newTP)) { string action = modifyPosition ? "Trailing Stop" : "SL/TP Update"; Print(action, " applied to position #", ticket, " New SL: ", newSL, " New TP: ", newTP); } else { Print("Failed to modify position #", ticket, " Error: ", GetLastError()); } } }