//+------------------------------------------------------------------+ //| QQE_ATR_EA.mq5 | //| Generated by your AI Coding Partner | //+------------------------------------------------------------------+ #property copyright "Your AI Coding Partner" #property link "" #property version "1.03" #include //--- Custom Enumerations for Dropdown Menus enum ENUM_SIGNAL_MODE { MODE_CROSSOVER = 0, // Crossover Only MODE_THRESHOLD = 1, // Threshold (50 Level) Only MODE_BOTH = 2 // Both Crossover and Threshold }; //--- EA Inputs input string General_Settings = "--- Trading Settings ---"; input double LotSize = 0.1; // Trading Lot Size input ENUM_SIGNAL_MODE SignalMode = MODE_BOTH; // Entry Signal Logic input ulong MagicNumber = 123456; // Magic Number input string Risk_Settings = "--- Dynamic Risk (ATR) ---"; input int AtrPeriod = 14; // ATR Period input double AtrSlMultiplier = 1.5; // ATR Stop Loss Multiplier input double AtrTpMultiplier = 2.0; // ATR Take Profit Multiplier input string Indicator_Settings = "--- QQE Settings ---"; input int SF = 5; // Smoothing Factor input int RSI_Period = 14; // RSI Period //--- Global Variables & Objects CTrade trade; int myRSI; int myATR; int Wilders_Period; int StartBar; int prev_calculated = 0; datetime last_bar_time; //--- Arrays for Standalone Indicator Calculations double RsiMa[]; double TrLevelSlow[]; double AtrRsi[]; double MaAtrRsi[]; double Rsi[]; double MaMaAtrRsi[]; //+------------------------------------------------------------------+ //| Exponential Moving Average Engine | //+------------------------------------------------------------------+ void CalculateEMA(int begin, int period, const double &price[], double &result[]) { double SmoothFactor = 2.0 / (1.0 + period); for (int i = begin; i >= 0; i--) { if (price[i] == EMPTY_VALUE) result[i] = 0; else result[i] = price[i] * SmoothFactor + result[i + 1] * (1.0 - SmoothFactor); } } //+------------------------------------------------------------------+ //| Core QQE Mathematics translated from Indicator Loop | //+------------------------------------------------------------------+ bool UpdateQQEData() { int rates_total = iBars(_Symbol, _Period); if(rates_total <= StartBar) return false; // Manage dynamic arrays for EA processing if(ArraySize(Rsi) != rates_total) { ArrayResize(Rsi, rates_total); ArrayResize(RsiMa, rates_total); ArrayResize(TrLevelSlow, rates_total); ArrayResize(AtrRsi, rates_total); ArrayResize(MaAtrRsi, rates_total); ArrayResize(MaMaAtrRsi, rates_total); ArraySetAsSeries(Rsi, true); ArraySetAsSeries(RsiMa, true); ArraySetAsSeries(TrLevelSlow, true); ArraySetAsSeries(AtrRsi, true); ArraySetAsSeries(MaAtrRsi, true); ArraySetAsSeries(MaMaAtrRsi, true); } int counted = prev_calculated - 1; if (counted < 1) { for (int i = 0; i < rates_total; i++) { TrLevelSlow[i] = 0.0; AtrRsi[i] = 0.0; MaAtrRsi[i] = 0.0; Rsi[i] = 0.0; RsiMa[i] = 0.0; MaMaAtrRsi[i] = 0.0; } counted = rates_total - StartBar - 1; } else { counted = rates_total - counted - 1; if (counted > rates_total - StartBar - 1) counted = rates_total - StartBar - 1; } if (CopyBuffer(myRSI, 0, 0, counted + 2, Rsi) != counted + 2) return false; CalculateEMA(counted + 1, SF, Rsi, RsiMa); for (int i = counted; i >= 0; i--) AtrRsi[i] = MathAbs(RsiMa[i + 1] - RsiMa[i]); CalculateEMA(counted, Wilders_Period, AtrRsi, MaAtrRsi); int i = counted + 1; double tr = TrLevelSlow[i]; double rsi1 = RsiMa[i]; CalculateEMA(counted, Wilders_Period, MaAtrRsi, MaMaAtrRsi); while (i > 0) { i--; 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; } prev_calculated = rates_total; return true; } //+------------------------------------------------------------------+ //| Execute Trade with Dynamic ATR TP/SL | //+------------------------------------------------------------------+ void ExecuteTrade(ENUM_ORDER_TYPE type) { // Fetch Current Price double price = (type == ORDER_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID); // Fetch Current ATR Value double atrArray[]; ArraySetAsSeries(atrArray, true); if(CopyBuffer(myATR, 0, 0, 1, atrArray) <= 0) { Print("Failed to copy ATR data. Order aborted."); return; } double currentATR = atrArray[0]; // Calculate Dynamic Risk Distances double slDistance = currentATR * AtrSlMultiplier; double tpDistance = currentATR * AtrTpMultiplier; double sl = 0, tp = 0; int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); if (type == ORDER_TYPE_BUY) { sl = price - slDistance; tp = price + tpDistance; trade.Buy(LotSize, _Symbol, NormalizeDouble(price, digits), NormalizeDouble(sl, digits), NormalizeDouble(tp, digits), "QQE Buy"); } else if (type == ORDER_TYPE_SELL) { sl = price + slDistance; tp = price - tpDistance; trade.Sell(LotSize, _Symbol, NormalizeDouble(price, digits), NormalizeDouble(sl, digits), NormalizeDouble(tp, digits), "QQE Sell"); } } //+------------------------------------------------------------------+ //| Close Opposite Positions logic | //+------------------------------------------------------------------+ void CloseOppositePositions(ENUM_POSITION_TYPE pos_type) { for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber) { if(PositionGetInteger(POSITION_TYPE) == pos_type) { trade.PositionClose(ticket); } } } } //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Initialize Trade settings trade.SetExpertMagicNumber(MagicNumber); // Initialize QQE mathematical periods Wilders_Period = RSI_Period * 2 - 1; StartBar = MathMax(SF, Wilders_Period); // Request RSI handle myRSI = iRSI(_Symbol, _Period, RSI_Period, PRICE_CLOSE); if(myRSI == INVALID_HANDLE) { Print("Error creating RSI indicator handle."); return(INIT_FAILED); } // Request ATR handle myATR = iATR(_Symbol, _Period, AtrPeriod); if(myATR == INVALID_HANDLE) { Print("Error creating ATR indicator handle."); return(INIT_FAILED); } return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { IndicatorRelease(myRSI); IndicatorRelease(myATR); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // 1. Only process data on a new bar to avoid mid-candle repainting datetime current_time = iTime(_Symbol, _Period, 0); if(current_time == last_bar_time) return; // 2. Update QQE arrays mathematically if(!UpdateQQEData()) return; // 3. Evaluate Trading Logic on the last completed bar (Index 1) and the one before (Index 2) double currentRsiMa = RsiMa[1]; double prevRsiMa = RsiMa[2]; double currentTr = TrLevelSlow[1]; double prevTr = TrLevelSlow[2]; // Crossover Logic: RsiMA (Blue line) crosses TrLevelSlow (Yellow line) bool isCrossUp = (prevRsiMa <= prevTr && currentRsiMa > currentTr); bool isCrossDn = (prevRsiMa >= prevTr && currentRsiMa < currentTr); // Level 50 Logic (Per the original code's description) bool isLevelCrossUp = (prevRsiMa < 50 && currentRsiMa >= 50 && currentRsiMa > currentTr); bool isLevelCrossDn = (prevRsiMa > 50 && currentRsiMa <= 50 && currentRsiMa < currentTr); // Filter signals based on the user's selected input mode bool buySignal = false; bool sellSignal = false; if(SignalMode == MODE_CROSSOVER || SignalMode == MODE_BOTH) { if(isCrossUp) buySignal = true; if(isCrossDn) sellSignal = true; } if(SignalMode == MODE_THRESHOLD || SignalMode == MODE_BOTH) { if(isLevelCrossUp) buySignal = true; if(isLevelCrossDn) sellSignal = true; } // 4. Execute Trades if (buySignal) { CloseOppositePositions(POSITION_TYPE_SELL); if(PositionsTotal() == 0) ExecuteTrade(ORDER_TYPE_BUY); last_bar_time = current_time; // Mark bar as handled } else if (sellSignal) { CloseOppositePositions(POSITION_TYPE_BUY); if(PositionsTotal() == 0) ExecuteTrade(ORDER_TYPE_SELL); last_bar_time = current_time; // Mark bar as handled } } //+------------------------------------------------------------------+