//+------------------------------------------------------------------+ //| ATR QQE Conservative Trading System.mq5 | //| Conservative QQE with Enhanced Risk Management | //+------------------------------------------------------------------+ #property copyright "Copyright 2025" #property link "" #property version "4.00" #property strict #property description "Conservative QQE system with enhanced filters and risk management" #include #include #include // EA input parameters input group "Risk Management (Conservative Settings)" input double Risk_Percent = 1.0; // Risk % of equity per trade (reduced) input double SL_ATR_Multiplier = 4.0; // Stop Loss ATR Multiplier (increased) input double TP_ATR_Multiplier = 8.0; // Take Profit ATR Multiplier (1:2 ratio) input double Trail_Start_Multiplier = 4.0; // Trailing Start ATR Multiplier input double Trail_Step_Multiplier = 1.0; // Trailing Step ATR Multiplier input double Extra_Buffer_Pips = 10; // Extra buffer in pips (increased) input int Max_Consecutive_Losses = 3; // Stop trading after X losses input int Min_Hours_Between_Trades = 4; // Minimum hours between trades input group "Market Condition Filters" input bool Use_Trend_Filter = true; // Only trade with trend input int Trend_MA_Period = 200; // Trend MA period input bool Use_Volatility_Filter = true; // Use ADX volatility filter input int ADX_Period = 14; // ADX period input double ADX_Threshold = 25; // Minimum ADX for trading input bool Use_Time_Filter = true; // Trade only during active hours input int Start_Hour = 7; // Trading start hour (GMT) input int End_Hour = 17; // Trading end hour (GMT) input group "Enhanced QQE Parameters" input int QQE_RSI_Period = 14; // QQE RSI Period input int QQE_Smoothing_Factor = 8; // QQE Smoothing Factor (increased) input double QQE_Signal_Threshold = 5; // Minimum distance between QQE lines input bool Use_QQE_Divergence_Filter = true; // Additional divergence filter input group "ATR and Trading Parameters" input int ATR_Period = 14; // ATR Period input int ATR_Shift = 1; // ATR shift 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 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 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 lastTradeTime = 0; datetime lastBarTime = 0; int atrHandle; int rsiHandle; int trendMAHandle; int adxHandle; int positionCount = 0; ulong positionTickets[]; int consecutiveLosses = 0; bool tradingHalted = false; // 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() { trade.SetExpertMagicNumber(123456); 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); trendMAHandle = iMA(_Symbol, PERIOD_CURRENT, Trend_MA_Period, 0, MODE_SMA, PRICE_CLOSE); adxHandle = iADX(_Symbol, PERIOD_CURRENT, ADX_Period); if(atrHandle == INVALID_HANDLE || rsiHandle == INVALID_HANDLE || trendMAHandle == INVALID_HANDLE || adxHandle == 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); RefreshPositions(); Print("Conservative ATR QQE Trading System initialized"); Print("Risk per trade: ", Risk_Percent, "%"); Print("Risk/Reward ratio: 1:", (TP_ATR_Multiplier/SL_ATR_Multiplier)); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { IndicatorRelease(atrHandle); IndicatorRelease(rsiHandle); IndicatorRelease(trendMAHandle); IndicatorRelease(adxHandle); Comment(""); } //+------------------------------------------------------------------+ //| 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(); } } } //+------------------------------------------------------------------+ //| Check if trading is allowed based on filters | //+------------------------------------------------------------------+ bool IsTradingAllowed() { // Check if trading is halted due to consecutive losses if(tradingHalted) { Print("Trading halted due to ", Max_Consecutive_Losses, " consecutive losses"); return false; } // Check minimum time between trades if(TimeCurrent() - lastTradeTime < Min_Hours_Between_Trades * 3600) { return false; } // Check trading hours if(Use_Time_Filter) { MqlDateTime dt; TimeToStruct(TimeCurrent(), dt); if(dt.hour < Start_Hour || dt.hour >= End_Hour) return false; } // Check volatility filter (ADX) if(Use_Volatility_Filter) { double adxBuffer[]; if(CopyBuffer(adxHandle, 0, 1, 1, adxBuffer) <= 0) return false; if(adxBuffer[0] < ADX_Threshold) { Print("ADX too low for trading: ", adxBuffer[0]); return false; } } return true; } //+------------------------------------------------------------------+ //| Check trend direction | //+------------------------------------------------------------------+ int GetTrendDirection() { if(!Use_Trend_Filter) return 0; // No filter double maBuffer[], closeBuffer[]; if(CopyBuffer(trendMAHandle, 0, 1, 1, maBuffer) <= 0) return 0; if(CopyClose(_Symbol, PERIOD_CURRENT, 1, 1, closeBuffer) <= 0) return 0; if(closeBuffer[0] > maBuffer[0]) return 1; // Uptrend else if(closeBuffer[0] < maBuffer[0]) return -1; // Downtrend else return 0; // Neutral } //+------------------------------------------------------------------+ //| Calculate lot size based on equity risk | //+------------------------------------------------------------------+ double CalculateLotSize(double entryPrice, double stopLoss) { double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE); double riskAmount = accountBalance * Risk_Percent / 100.0; double stopDistance = MathAbs(entryPrice - stopLoss); if(stopDistance == 0) return 0; double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); if(tickValue == 0 || tickSize == 0) return 0; 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); 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--) { if(i >= priceSize || i >= resultSize) continue; if(i + 1 >= resultSize) { 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; int available_bars = iBars(_Symbol, PERIOD_CURRENT); if(available_bars < total_bars) { total_bars = available_bars - 1; if(total_bars < StartBar + 10) { return false; } } if(total_bars < 50) return false; 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)) { return false; } if(CopyBuffer(rsiHandle, 0, 0, total_bars, Rsi) != total_bars) return false; ArrayInitialize(RsiMa, 0.0); ArrayInitialize(AtrRsi, 0.0); ArrayInitialize(MaAtrRsi, 0.0); ArrayInitialize(MaMaAtrRsi, 0.0); ArrayInitialize(TrLevelSlow, 0.0); if(total_bars > QQE_Smoothing_Factor) CalculateEMA(total_bars - 1, QQE_Smoothing_Factor, Rsi, RsiMa); for(int i = total_bars - 2; i >= 0; i--) { if(i + 1 < total_bars && i >= 0) AtrRsi[i] = MathAbs(RsiMa[i + 1] - RsiMa[i]); } if(total_bars > Wilders_Period) { CalculateEMA(total_bars - 2, Wilders_Period, AtrRsi, MaAtrRsi); CalculateEMA(total_bars - 2, Wilders_Period, MaAtrRsi, MaMaAtrRsi); } 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) atrValue = 0.0001; } else { Print("Error getting ATR value: ", GetLastError()); } if(iBars(_Symbol, PERIOD_CURRENT) > StartBar + 50) { CalculateQQE(50); } } //+------------------------------------------------------------------+ //| Check for QQE entry signals with enhanced filters | //+------------------------------------------------------------------+ void CheckForEntrySignals() { if(positionCount >= Max_Positions || !IsTradingAllowed()) return; if(ArraySize(RsiMa) < 3 || ArraySize(TrLevelSlow) < 3 || atrValue <= 0) return; // Additional validation if(RsiMa[1] == 0 || TrLevelSlow[1] == 0 || RsiMa[2] == 0 || TrLevelSlow[2] == 0) return; double currentRsiMa = RsiMa[1]; double prevRsiMa = RsiMa[2]; double currentSmoothed = TrLevelSlow[1]; double prevSmoothed = TrLevelSlow[2]; // Check QQE signal strength double signalStrength = MathAbs(currentRsiMa - currentSmoothed); if(signalStrength < QQE_Signal_Threshold) { Print("QQE signal too weak: ", signalStrength); return; } // Get trend direction int trendDirection = GetTrendDirection(); bool buySignal = false; bool sellSignal = false; // Enhanced QQE signal detection if(prevRsiMa <= prevSmoothed && currentRsiMa > currentSmoothed) { // Additional divergence filter if(Use_QQE_Divergence_Filter) { if(currentRsiMa < 30) // Coming from oversold buySignal = true; } else { buySignal = true; } } if(prevRsiMa >= prevSmoothed && currentRsiMa < currentSmoothed) { if(Use_QQE_Divergence_Filter) { if(currentRsiMa > 70) // Coming from overbought sellSignal = true; } else { sellSignal = true; } } // Apply trend filter if(Use_Trend_Filter) { if(buySignal && trendDirection != 1) { Print("Buy signal filtered out - not in uptrend"); buySignal = false; } if(sellSignal && trendDirection != -1) { Print("Sell signal filtered out - not in downtrend"); sellSignal = false; } } 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 = NormalizeDouble(currentPrice - (atrValue * SL_ATR_Multiplier) - bufferInPoints, digits); takeProfit = NormalizeDouble(currentPrice + (atrValue * TP_ATR_Multiplier), digits); double lotSize = CalculateLotSize(currentPrice, stopLoss); if(lotSize > 0 && !trade.Buy(lotSize, _Symbol, 0, stopLoss, takeProfit, "Conservative QQE Buy")) { Print("Buy order failed with error: ", GetLastError()); } else if(lotSize > 0) { Print("Conservative QQE Buy executed - Lot: ", lotSize, " Entry: ", currentPrice); Print("SL: ", stopLoss, " TP: ", takeProfit, " Risk: ", Risk_Percent, "%"); lastTradeTime = TimeCurrent(); } } // Execute Sell Signal if(sellSignal && Allow_Short_Positions) { currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); stopLoss = NormalizeDouble(currentPrice + (atrValue * SL_ATR_Multiplier) + bufferInPoints, digits); takeProfit = NormalizeDouble(currentPrice - (atrValue * TP_ATR_Multiplier), digits); double lotSize = CalculateLotSize(currentPrice, stopLoss); if(lotSize > 0 && !trade.Sell(lotSize, _Symbol, 0, stopLoss, takeProfit, "Conservative QQE Sell")) { Print("Sell order failed with error: ", GetLastError()); } else if(lotSize > 0) { Print("Conservative QQE Sell executed - Lot: ", lotSize, " Entry: ", currentPrice); Print("SL: ", stopLoss, " TP: ", takeProfit, " Risk: ", Risk_Percent, "%"); lastTradeTime = TimeCurrent(); } } } //+------------------------------------------------------------------+ //| Track trade results and consecutive losses | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) { if(trans.type == TRADE_TRANSACTION_DEAL_ADD) { CDealInfo deal; if(deal.SelectByIndex(trans.deal)) { if(deal.Magic() == 123456 && deal.Entry() == DEAL_ENTRY_OUT) { double profit = deal.Profit(); if(profit < 0) { consecutiveLosses++; Print("Consecutive losses: ", consecutiveLosses); if(consecutiveLosses >= Max_Consecutive_Losses) { tradingHalted = true; Print("Trading halted after ", Max_Consecutive_Losses, " consecutive losses"); } } else { consecutiveLosses = 0; // Reset counter on winning trade tradingHalted = false; // Resume trading } } } } } //+------------------------------------------------------------------+ //| 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 = "Conservative QQE ATR System\n"; info += "Risk per trade: " + DoubleToString(Risk_Percent, 1) + "%\n"; info += "Risk/Reward: 1:" + DoubleToString(TP_ATR_Multiplier/SL_ATR_Multiplier, 1) + "\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 += "Consecutive Losses: " + IntegerToString(consecutiveLosses) + "/" + IntegerToString(Max_Consecutive_Losses) + "\n"; info += "Positions: " + IntegerToString(positionCount); Comment(info); // Process existing positions with trailing stops 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 with trailing stops | //+------------------------------------------------------------------+ void ProcessPosition(ENUM_POSITION_TYPE posType, double openPrice, double currentSL, double currentTP, ulong ticket, double bufferInPoints) { if(!Use_Trailing_Stop || !Manage_SL) return; double newSL = currentSL; 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; 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; 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; } } } if(modifyPosition) { if(trade.PositionModify(ticket, newSL, currentTP)) { Print("Trailing stop updated for position #", ticket, " New SL: ", newSL); } } }