//+------------------------------------------------------------------+ //| QQE_Cross_v6.0_SubWindow.mq5 | //| QQE Visualization SubWindow | //| by JustUncleL | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Based on JustUncleL's work" #property link "https://www.mql5.com" #property version "6.00" #property indicator_separate_window #property indicator_minimum 0 #property indicator_maximum 100 #property indicator_buffers 5 #property indicator_plots 3 #property indicator_level1 40 #property indicator_level2 50 #property indicator_level3 60 #property indicator_levelcolor clrDarkSlateGray #property indicator_levelstyle STYLE_DOT #property indicator_height 150 // Input parameters input int RSILen = 14; // RSI Length input int SF = 8; // RSI Smoothing Factor input double QQEfactor = 5.0; // Fast QQE Factor input int threshhold = 10; // RSI Threshold input bool showHistogram = true; // Show QQE Histogram input color colorBullish = clrLime; // Bullish Color input color colorNeutral = clrGray; // Neutral Color input color colorBearish = clrRed; // Bearish Color // Indicator buffers double RSIndexBuffer[]; double FastAtrRsiTLBuffer[]; double HistogramBuffer[]; double HistogramColors[]; double RSIBuffer[]; // Internal arrays double AtrRsi[]; double MaAtrRsi[]; double DeltaFastAtrRsi[]; double longband[]; double shortband[]; int trend[]; // Global variables int rsi_handle; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { // Set indicator name IndicatorSetString(INDICATOR_SHORTNAME, "QQE(" + IntegerToString(RSILen) + "," + IntegerToString(SF) + ")"); // Set buffers SetIndexBuffer(0, RSIndexBuffer, INDICATOR_DATA); SetIndexBuffer(1, FastAtrRsiTLBuffer, INDICATOR_DATA); SetIndexBuffer(2, HistogramBuffer, INDICATOR_DATA); SetIndexBuffer(3, HistogramColors, INDICATOR_COLOR_INDEX); SetIndexBuffer(4, RSIBuffer, INDICATOR_CALCULATIONS); // Configure RSI Line PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_LINE); PlotIndexSetInteger(0, PLOT_LINE_COLOR, clrYellow); PlotIndexSetInteger(0, PLOT_LINE_WIDTH, 2); PlotIndexSetString(0, PLOT_LABEL, "QQE RSI"); // Configure Fast ATR Line PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_LINE); PlotIndexSetInteger(1, PLOT_LINE_COLOR, clrAqua); PlotIndexSetInteger(1, PLOT_LINE_STYLE, STYLE_DASH); PlotIndexSetString(1, PLOT_LABEL, "QQE Line"); // Configure Histogram PlotIndexSetInteger(2, PLOT_DRAW_TYPE, showHistogram ? DRAW_COLOR_HISTOGRAM : DRAW_NONE); PlotIndexSetInteger(2, PLOT_LINE_WIDTH, 2); PlotIndexSetString(2, PLOT_LABEL, "QQE Histogram"); PlotIndexSetInteger(2, PLOT_COLOR_INDEXES, 3); PlotIndexSetInteger(2, PLOT_LINE_COLOR, 0, colorBullish); PlotIndexSetInteger(2, PLOT_LINE_COLOR, 1, colorNeutral); PlotIndexSetInteger(2, PLOT_LINE_COLOR, 2, colorBearish); // Set arrays as series ArraySetAsSeries(RSIndexBuffer, true); ArraySetAsSeries(FastAtrRsiTLBuffer, true); ArraySetAsSeries(HistogramBuffer, true); ArraySetAsSeries(HistogramColors, true); ArraySetAsSeries(RSIBuffer, true); // Create RSI handle rsi_handle = iRSI(NULL, 0, RSILen, PRICE_CLOSE); if(rsi_handle == INVALID_HANDLE) { Print("Failed to create RSI handle"); return(INIT_FAILED); } // Resize arrays int bars = Bars(_Symbol, _Period); ArrayResize(AtrRsi, bars); ArrayResize(MaAtrRsi, bars); ArrayResize(DeltaFastAtrRsi, bars); ArrayResize(longband, bars); ArrayResize(shortband, bars); ArrayResize(trend, bars); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { IndicatorRelease(rsi_handle); } //+------------------------------------------------------------------+ //| Calculate EMA | //+------------------------------------------------------------------+ double CalculateEMA(const double &price[], int period, int shift, double prev_ema) { if(shift >= ArraySize(price) - 1) return price[shift]; double alpha = 2.0 / (period + 1); if(prev_ema == 0) { // Calculate SMA for first value double sum = 0; for(int i = 0; i < period && shift + i < ArraySize(price); i++) sum += price[shift + i]; return sum / period; } return alpha * price[shift] + (1 - alpha) * prev_ema; } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { if(rates_total < RSILen + SF + 10) return(0); // Copy RSI data if(CopyBuffer(rsi_handle, 0, 0, rates_total, RSIBuffer) <= 0) return(0); int limit = rates_total - prev_calculated; if(prev_calculated > 0) limit++; // Calculate QQE int Wilders_Period = RSILen * 2 - 1; // First pass: Calculate RSIndex for(int i = rates_total - 1; i >= 0; i--) { if(i == rates_total - 1) RSIndexBuffer[i] = RSIBuffer[i]; else RSIndexBuffer[i] = CalculateEMA(RSIBuffer, SF, i, RSIndexBuffer[i+1]); // Calculate AtrRsi if(i < rates_total - 1) AtrRsi[i] = MathAbs(RSIndexBuffer[i+1] - RSIndexBuffer[i]); else AtrRsi[i] = 0; } // Second pass: Calculate MaAtrRsi and DeltaFastAtrRsi for(int i = rates_total - 1; i >= 0; i--) { if(i == rates_total - 1) { MaAtrRsi[i] = AtrRsi[i]; DeltaFastAtrRsi[i] = AtrRsi[i] * QQEfactor; } else { MaAtrRsi[i] = CalculateEMA(AtrRsi, Wilders_Period, i, MaAtrRsi[i+1]); double temp = CalculateEMA(MaAtrRsi, Wilders_Period, i, (i < rates_total - 2 ? DeltaFastAtrRsi[i+1] / QQEfactor : MaAtrRsi[i])); DeltaFastAtrRsi[i] = temp * QQEfactor; } } // Third pass: Calculate bands and trend for(int i = rates_total - 1; i >= 0; i--) { double newshortband = RSIndexBuffer[i] + DeltaFastAtrRsi[i]; double newlongband = RSIndexBuffer[i] - DeltaFastAtrRsi[i]; if(i == rates_total - 1) { longband[i] = newlongband; shortband[i] = newshortband; trend[i] = 1; } else { // Longband calculation if(RSIndexBuffer[i+1] > longband[i+1] && RSIndexBuffer[i] > longband[i+1]) longband[i] = MathMax(longband[i+1], newlongband); else longband[i] = newlongband; // Shortband calculation if(RSIndexBuffer[i+1] < shortband[i+1] && RSIndexBuffer[i] < shortband[i+1]) shortband[i] = MathMin(shortband[i+1], newshortband); else shortband[i] = newshortband; // Trend calculation if(RSIndexBuffer[i] > shortband[i+1]) trend[i] = 1; else if(RSIndexBuffer[i] < longband[i+1]) trend[i] = -1; else trend[i] = trend[i+1]; } // Set Fast ATR line FastAtrRsiTLBuffer[i] = (trend[i] == 1) ? longband[i] : shortband[i]; // Set Histogram HistogramBuffer[i] = RSIndexBuffer[i]; // Color the histogram based on threshold levels if(RSIndexBuffer[i] > 50 + threshhold) HistogramColors[i] = 0; // Bullish else if(RSIndexBuffer[i] < 50 - threshhold) HistogramColors[i] = 2; // Bearish else HistogramColors[i] = 1; // Neutral } return(rates_total); } //+------------------------------------------------------------------+ //| Get QQE values for external use | //+------------------------------------------------------------------+ bool GetQQEValues(int shift, double &rsi_value, double &qqe_line, int &qqe_trend) { if(shift < 0 || shift >= ArraySize(RSIndexBuffer)) return false; rsi_value = RSIndexBuffer[shift]; qqe_line = FastAtrRsiTLBuffer[shift]; qqe_trend = trend[shift]; return true; } //+------------------------------------------------------------------+ //| Check for QQE cross signals | //+------------------------------------------------------------------+ int CheckQQESignal(int shift) { if(shift < 1 || shift >= ArraySize(RSIndexBuffer)) return 0; // Check for bullish cross if(RSIndexBuffer[shift] > FastAtrRsiTLBuffer[shift] && RSIndexBuffer[shift+1] <= FastAtrRsiTLBuffer[shift+1]) return 1; // Check for bearish cross if(RSIndexBuffer[shift] < FastAtrRsiTLBuffer[shift] && RSIndexBuffer[shift+1] >= FastAtrRsiTLBuffer[shift+1]) return -1; return 0; } //+------------------------------------------------------------------+ //| Check for threshold crosses | //+------------------------------------------------------------------+ int CheckThresholdCross(int shift) { if(shift < 1 || shift >= ArraySize(RSIndexBuffer)) return 0; double upper_threshold = 50 + threshhold; double lower_threshold = 50 - threshhold; // Check for exit above upper threshold if(RSIndexBuffer[shift] > upper_threshold && RSIndexBuffer[shift+1] <= upper_threshold) return 1; // Check for exit below lower threshold if(RSIndexBuffer[shift] < lower_threshold && RSIndexBuffer[shift+1] >= lower_threshold) return -1; return 0; } //+------------------------------------------------------------------+ //| Check for zero line crosses | //+------------------------------------------------------------------+ int CheckZeroCross(int shift) { if(shift < 1 || shift >= ArraySize(RSIndexBuffer)) return 0; // Check for bullish zero cross if(RSIndexBuffer[shift] > 50 && RSIndexBuffer[shift+1] <= 50) return 1; // Check for bearish zero cross if(RSIndexBuffer[shift] < 50 && RSIndexBuffer[shift+1] >= 50) return -1; return 0; } //+------------------------------------------------------------------+