You are currently viewing the resource titled "Building a Powerful Custom RSI in MQL5: Multiple Variants, Smart Smoothing, and Hue Shifts". This page provides detailed information about the resource, including its content, attached files, and recent discussions. Feel free to explore, download available files if logged in, and join the community conversation below!
Banner Image

Introduction


The classic Relative Strength Index is one of those indicators almost every trader has on their chart at some point. It’s simple, it’s effective, and it does a great job of highlighting when prices might be getting a bit too excited (overbought) or too sleepy (oversold).

But here’s the thing… the default version can start to feel a little basic once you’ve been trading for a while. What if it could react faster when you need speed? What if it could filter out noise when the market is choppy? What if the line could literally change color to scream “pay attention here!” and the overbought/oversold zones could move with the market instead of staying stuck in the same place?

That’s exactly what we’re creating today. A highly customizable, super-smart RSI in MQL5 that gives you total control over how it calculates, looks, and behaves. Whether you’re a scalper who loves quick signals or a swing trader who prefers smooth, reliable lines, this indicator adapts to you instead of the other way around.

Ready to turn the ordinary RSI into something extraordinary? Let’s jump in!

Exploring RSI Variants, Smoothing Methods, and Dynamic Features

The standard RSI measures how fast and how far prices are moving, usually bouncing between 0 and 100 with those famous 70 and 30 lines.

We’re giving it a serious glow-up with a bunch of exciting new features:
  1. Multiple RSI Personalities: Choose from Classic, Quick, Gradual, RSX (ultra-smooth), Cuttler-style, Ehlers-style, and Harris-style. Some are zippy and responsive, others are calm and filtered — pick the one that matches your trading vibe.
  2. Data Sources on Steroids: Forget being stuck with just the close price! You can now calculate momentum using open, high, low, midpoints, weighted averages, direction-adjusted prices, and more. It’s like viewing the market from different camera angles.
  3. Smart Smoothing Options: Apply basic averaging, growth-based (EMA-style), evened-out, or weighted linear smoothing to clean up the raw price data before the RSI even starts working. Think of it as noise-canceling headphones for your chart 🎧.
  4. Automatic Hue Shifts: The main RSI line changes color instantly when it changes direction, crosses the center line, or breaks a boundary. No more squinting at numbers — the color does the talking!
  5. Dynamic Boundaries: Let the overbought and oversold levels automatically adjust based on recent RSI behavior (great for volatile or quiet markets), or stick with traditional fixed percentages if you prefer.
  6. Multi-Timeframe Magic: Pull RSI data from a completely different timeframe while trading on your current chart, with optional interpolation so everything looks beautifully smooth instead of jagged.

The result? A momentum tool that feels custom-built for whatever market conditions you throw at it.

Conceptual Framework

Picture this: you now have an RSI engine that isn’t locked into one boring calculation method or one price type. It can measure momentum from almost any angle you choose, smooth both the input data and the final result, flip between fast-reacting or super-smooth behavior with a single click, and even let its own warning zones evolve with the market’s mood.

Add intelligent color changes and clean multi-timeframe support, and you’ve got a professional-grade indicator that works with your strategy instead of forcing you to work around it. See what we are building.

CONCEPTUAL FRAMEWORK

Looks pretty awesome, right? Let’s turn this idea into real MQL5 code!

Implementation in MQL5

Open MetaEditor, go to the Indicators folder, and create a new file. We’ll start by setting up the indicator properties so it appears in its own clean sub-window with all the buffers and plots we need.
//+------------------------------------------------------------------+
//|                              Multi-Method RSI with Smoothing.mq5 |
//|                           Copyright 2026, Allan Munene Mutiiria. |
//|                                   https://t.me/Forex_Algo_Trader |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Allan Munene Mutiiria."
#property link "https://t.me/Forex_Algo_Trader"
#property version "1.00"
#property indicator_separate_window
#property indicator_buffers 8
#property indicator_plots   5

#property indicator_label1  "RSI High/Low Area"
#property indicator_type1   DRAW_FILLING
#property indicator_color1  C'209,243,209',C'255,230,183'
#property indicator_label2  "RSI Top Boundary"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrLimeGreen
#property indicator_style2  STYLE_DOT
#property indicator_label3  "RSI Center Line"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrSilver
#property indicator_style3  STYLE_DOT
#property indicator_label4  "RSI Bottom Boundary"
#property indicator_type4   DRAW_LINE
#property indicator_color4  clrOrange
#property indicator_style4  STYLE_DOT
#property indicator_label5  "RSI Curve"
#property indicator_type5   DRAW_COLOR_LINE
#property indicator_color5  clrSilver,clrLimeGreen,clrOrange
#property indicator_width5  2
We define eight buffers for the heavy math and five plots: a nice filled area for the high/low zones (soft green and orange tones), dotted boundary lines, a center line, and a thick color-shifting RSI curve that really stands out. Next come the enumerations that make the whole settings window super user-friendly.
//+------------------------------------------------------------------+
//| Enumerations                                                     |
//+------------------------------------------------------------------+
enum DataSourceType {
   Data_ClosePrice,      // Use closing price
   Data_OpenPrice,       // Use opening price
   Data_HighPrice,       // Use highest price
   Data_LowPrice,        // Use lowest price
   Data_MidPoint,        // Use midpoint price
   Data_StandardPrice,   // Use standard price
   Data_BalancedPrice,   // Use balanced price
   Data_OverallAverage,  // Use overall average price
   Data_MidBodyAverage,  // Use mid-body average
   Data_DirectionAdjusted, // Use direction-adjusted price
   Data_ExtremeAdjusted, // Use extreme-adjusted price
   Data_SmoothedClose,   // Use smoothed close
   Data_SmoothedOpen,    // Use smoothed open
   Data_SmoothedHigh,    // Use smoothed high
   Data_SmoothedLow,     // Use smoothed low
   Data_SmoothedMid,     // Use smoothed midpoint
   Data_SmoothedStandard,// Use smoothed standard
   Data_SmoothedBalanced,// Use smoothed balanced
   Data_SmoothedOverall, // Use smoothed overall
   Data_SmoothedMidBody, // Use smoothed mid-body
   Data_SmoothedAdjusted,// Use smoothed adjusted
   Data_SmoothedExtreme  // Use smoothed extreme
};

enum RsiVariant {
   Variant_CuttlerStyle,  // Cuttler-style RSI
   Variant_EhlersStyle,   // Ehlers-style smoothed RSI
   Variant_HarrisStyle,   // Harris-style RSI
   Variant_QuickStyle,    // Quick RSI
   Variant_BasicStyle,    // Basic RSI
   Variant_RsxStyle,      // RSX-style
   Variant_GradualStyle   // Gradual RSI
};

enum HueShiftCondition {
   Hue_OnDirectionShift,   // Shift hue on direction change
   Hue_OnCenterCrossing,   // Shift hue on center crossing
   Hue_OnBoundaryCrossing  // Shift hue on boundary crossing
};

enum AveragingApproach {
   Avg_Basic,          // Basic averaging
   Avg_GrowthBased,    // Growth-based averaging
   Avg_EvenedOut,      // Evened-out averaging
   Avg_WeightedLinear  // Weighted linear averaging
};

//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
input group "Chart and Calculation Settings";
input ENUM_TIMEFRAMES AnalysisTimeframe    = PERIOD_CURRENT;     // Choose timeframe for data analysis
input int             RsiLength            = 14;                 // Length for RSI computation

input group "Data Source and Variant Options";
input DataSourceType  SourceData           = Data_ClosePrice;    // Select data source for calculations
input RsiVariant      ChosenRsiVariant     = Variant_BasicStyle; // Select RSI computation variant
input int             DataSmoothingLength  = 0;                  // Smoothing length for data (0 or 1 disables)
input AveragingApproach DataSmoothingApproach = Avg_GrowthBased; // Approach for data smoothing

input group "Hue Adjustment Settings";
input HueShiftCondition HueAdjustmentOn    = Hue_OnBoundaryCrossing; // Condition for hue adjustment

input group "Boundary Configuration";
input int             DynamicBoundaryLength = 50;                // Length for dynamic boundaries (1 or less for static)
input double          TopBoundaryPercent   = 80.0;               // Top boundary percentage
input double          BottomBoundaryPercent = 20.0;              // Bottom boundary percentage

input group "Notification Preferences";
input bool            ActivateNotifications = false;             // Activate notifications?
input bool            NotifyOnActiveBar     = true;              // Notify on active bar?
input bool            InterpolateMultiFrame = true;              // Smooth multi-frame data?
These handle data sources, RSI variants, hue-shift triggers, and smoothing styles. Then we group the actual inputs so everything feels clean and easy to tweak. You should get the following outcome.
INDICATOR INPUTS WINDOW
//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
double rsiCurveValues[], rsiHueValues[], areaFillTop[], areaFillBottom[], topBoundaryValues[], centerLineValues[], bottomBoundaryValues[], processedBarCounts[]; //--- Declare buffers
int    multiFrameDataHandle = INVALID_HANDLE;                    //--- Initialize multi-frame handle
ENUM_TIMEFRAMES chosenTimeframe;                                 //--- Declare chosen timeframe

#define MULTI_FRAME_ACCESS iCustom(_Symbol, chosenTimeframe, __FILE__, PERIOD_CURRENT, RsiLength, SourceData, ChosenRsiVariant, DataSmoothingLength, DataSmoothingApproach, HueAdjustmentOn, DynamicBoundaryLength, TopBoundaryPercent, BottomBoundaryPercent, ActivateNotifications, NotifyOnActiveBar, InterpolateMultiFrame) //--- Define multi-frame access


int timeframeCodes[] = {PERIOD_M1, PERIOD_M2, PERIOD_M3, PERIOD_M4, PERIOD_M5, PERIOD_M6, PERIOD_M10, PERIOD_M12, PERIOD_M15, PERIOD_M20, PERIOD_M30, PERIOD_H1, PERIOD_H2, PERIOD_H3, PERIOD_H4, PERIOD_H6, PERIOD_H8, PERIOD_H12, PERIOD_D1, PERIOD_W1, PERIOD_MN1}; //--- Define timeframe codes
string timeframeLabels[] = {"1 minute", "2 minutes", "3 minutes", "4 minutes", "5 minutes", "6 minutes", "10 minutes", "12 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "2 hours", "3 hours", "4 hours", "6 hours", "8 hours", "12 hours", "daily", "weekly", "monthly"}; //--- Define timeframe labels

//+------------------------------------------------------------------+
//| Convert timeframe to text                                        |
//+------------------------------------------------------------------+
string convertTimeframeToText(int code) {
   if (code == PERIOD_CURRENT)                                   //--- Check current
      code = _Period;                                            //--- Set period
   int pos;                                                      //--- Declare pos
   for (pos = 0; pos < ArraySize(timeframeCodes); pos++)         //--- Loop codes
      if (code == timeframeCodes[pos]) break;                    //--- Break on match
   return(timeframeLabels[pos]);                                 //--- Return label
}

//+------------------------------------------------------------------+
//| Describe RSI variant                                             |
//+------------------------------------------------------------------+
string describeRsiVariant(int variant) {
   switch (variant) {                                            //--- Switch variant
   case Variant_BasicStyle:                                      //--- Handle basic
      return("RSI");                                             //--- Return RSI
   case Variant_RsxStyle:                                        //--- Handle RSX
      return("RSX");                                             //--- Return RSX
   case Variant_CuttlerStyle:                                    //--- Handle Cuttler
      return("Cuttler-style RSI");                               //--- Return Cuttler
   case Variant_HarrisStyle:                                     //--- Handle Harris
      return("Harris-style RSI");                                //--- Return Harris
   case Variant_QuickStyle:                                      //--- Handle Quick
      return("Quick RSI");                                       //--- Return Quick
   case Variant_GradualStyle:                                    //--- Handle Gradual
      return("Gradual RSI");                                     //--- Return Gradual
   case Variant_EhlersStyle:                                     //--- Handle Ehlers
      return("Ehlers-style smoothed RSI");                       //--- Return Ehlers
   default:                                                      //--- Handle default
      return("");                                                //--- Return empty
   }
}
In the OnInit event handler we wire everything up so the indicator knows exactly what to draw and what timeframe to use.

//+------------------------------------------------------------------+
//| Initialize indicator                                             |
//+------------------------------------------------------------------+
int OnInit() {
   SetIndexBuffer(0, areaFillTop, INDICATOR_DATA);               //--- Set top fill buffer
   SetIndexBuffer(1, areaFillBottom, INDICATOR_DATA);            //--- Set bottom fill buffer
   SetIndexBuffer(2, topBoundaryValues, INDICATOR_DATA);         //--- Set top boundary buffer
   SetIndexBuffer(3, centerLineValues, INDICATOR_DATA);          //--- Set center line buffer
   SetIndexBuffer(4, bottomBoundaryValues, INDICATOR_DATA);      //--- Set bottom boundary buffer
   SetIndexBuffer(5, rsiCurveValues, INDICATOR_DATA);            //--- Set RSI curve buffer
   SetIndexBuffer(6, rsiHueValues, INDICATOR_COLOR_INDEX);       //--- Set hue buffer
   SetIndexBuffer(7, processedBarCounts, INDICATOR_CALCULATIONS); //--- Set processed counts buffer

   PlotIndexSetInteger(0, PLOT_SHOW_DATA, false);                //--- Hide filling data
   PlotIndexSetInteger(1, PLOT_SHOW_DATA, false);                //--- Hide bottom data
   PlotIndexSetInteger(2, PLOT_SHOW_DATA, true);                 //--- Show top boundary
   PlotIndexSetInteger(3, PLOT_SHOW_DATA, true);                 //--- Show center line
   PlotIndexSetInteger(4, PLOT_SHOW_DATA, true);                 //--- Show bottom boundary

   chosenTimeframe = MathMax(_Period, AnalysisTimeframe);        //--- Set chosen timeframe
   IndicatorSetString(INDICATOR_SHORTNAME, convertTimeframeToText(chosenTimeframe) + " " + describeRsiVariant(ChosenRsiVariant) + " with Adjustment (" + (string)RsiLength + "," + (string)DataSmoothingLength + "," + (string)DynamicBoundaryLength + ")"); //--- Set short name

   return(INIT_SUCCEEDED);                                       //--- Return success
}
We also create a small helper to check if enough history is available for multi-timeframe mode, then move into the main OnCalculate function that handles both single-timeframe and multi-timeframe logic with beautiful interpolation.
//+------------------------------------------------------------------+
//| Check timeframe validity                                         |
//+------------------------------------------------------------------+
bool checkTimeframeValidity(ENUM_TIMEFRAMES frame, const datetime& timeStamps[]) {
   static bool alerted = false;                                    //--- Set alerted flag
   if (timeStamps[0] < SeriesInfoInteger(_Symbol, frame, SERIES_FIRSTDATE)) { //--- Check first date
      datetime startDate, checkDate[];                             //--- Declare dates
      if (SeriesInfoInteger(_Symbol, PERIOD_M1, SERIES_TERMINAL_FIRSTDATE, startDate)) //--- Get terminal date
         if (startDate > 0) {                                      //--- Check date
            CopyTime(_Symbol, frame, timeStamps[0], 1, checkDate); //--- Copy time
            SeriesInfoInteger(_Symbol, frame, SERIES_FIRSTDATE, startDate); //--- Get series date
         }
      if (startDate <= 0 || startDate > timeStamps[0]) {           //--- Check invalid
         alerted = true;                                           //--- Set alerted
         return(false);                                            //--- Return false
      }
   }
   if (alerted) {                                                  //--- Check alerted
      alerted = false;                                             //--- Reset alerted
   }
   return(true);                                                   //--- Return true
}

//+------------------------------------------------------------------+
//| Calculate indicator                                              |
//+------------------------------------------------------------------+
int OnCalculate(const int barTotal,
                const int prevProcessed,
                const datetime& timeStamps[],
                const double& opens[],
                const double& highs[],
                const double& lows[],
                const double& closes[],
                const long& volumeTicks[],
                const long& actualVolumes[],
                const int& spreadValues[]) {
   if (Bars(_Symbol, _Period) < barTotal) return(-1);             //--- Check insufficient bars

   if (chosenTimeframe != _Period) {                              //--- Check multi-frame
      double interimData[];                                       //--- Declare interim data
      datetime activeTimeStamp[], followingTimeStamp[];           //--- Declare timestamps
      if (!checkTimeframeValidity(chosenTimeframe, timeStamps)) return(0); //--- Check validity
      if (multiFrameDataHandle == INVALID_HANDLE) multiFrameDataHandle = MULTI_FRAME_ACCESS; //--- Get handle
      if (multiFrameDataHandle == INVALID_HANDLE) return(0);      //--- Check handle
      if (CopyBuffer(multiFrameDataHandle, 7, 0, 1, interimData) == -1) return(0); //--- Copy processed

#define FRAME_RATIO PeriodSeconds(chosenTimeframe) / PeriodSeconds(_Period) //--- Define frame ratio
      int currentPos = MathMin(MathMax(prevProcessed - 1, 0), MathMax(barTotal - (int)interimData[0] * FRAME_RATIO - 1, 0)); //--- Compute pos
      for (; currentPos < barTotal && !_StopFlag; currentPos++) { //--- Loop positions
#define TRANSFER_MULTI_FRAME(_array, _pos) if (CopyBuffer(multiFrameDataHandle, _pos, timeStamps[currentPos], 1, interimData) == -1) break; _array[currentPos] = interimData[0] //--- Define transfer
         TRANSFER_MULTI_FRAME(areaFillTop, 0);                    //--- Transfer top fill
         TRANSFER_MULTI_FRAME(areaFillBottom, 1);                 //--- Transfer bottom fill
         TRANSFER_MULTI_FRAME(topBoundaryValues, 2);              //--- Transfer top boundary
         TRANSFER_MULTI_FRAME(centerLineValues, 3);               //--- Transfer center line
         TRANSFER_MULTI_FRAME(bottomBoundaryValues, 4);           //--- Transfer bottom boundary
         TRANSFER_MULTI_FRAME(rsiCurveValues, 5);                 //--- Transfer RSI curve
         TRANSFER_MULTI_FRAME(rsiHueValues, 6);                   //--- Transfer hue

         if (!InterpolateMultiFrame) continue;                    //--- Skip if no interpolate
         CopyTime(_Symbol, chosenTimeframe, timeStamps[currentPos], 1, activeTimeStamp); //--- Copy active time
         if (currentPos < (barTotal - 1)) {                       //--- Check not last
            CopyTime(_Symbol, chosenTimeframe, timeStamps[currentPos + 1], 1, followingTimeStamp); //--- Copy following time
            if (activeTimeStamp[0] == followingTimeStamp[0]) continue; //--- Skip same time
         }

         int stepsBack = 1;                                       //--- Initialize steps back
         while ((currentPos - stepsBack) > 0 && timeStamps[currentPos - stepsBack] >= activeTimeStamp[0]) stepsBack++; //--- Count back

         for (int stepsForward = 1; (currentPos - stepsForward) >= 0 && stepsForward < stepsBack; stepsForward++) { //--- Loop forward
#define SMOOTH_MULTI_FRAME(_array) _array[currentPos - stepsForward] = _array[currentPos] + (_array[currentPos - stepsBack] - _array[currentPos]) * stepsForward / stepsBack //--- Define smooth
            SMOOTH_MULTI_FRAME(areaFillTop);                      //--- Smooth top fill
            SMOOTH_MULTI_FRAME(areaFillBottom);                   //--- Smooth bottom fill
            SMOOTH_MULTI_FRAME(topBoundaryValues);                //--- Smooth top boundary
            SMOOTH_MULTI_FRAME(bottomBoundaryValues);             //--- Smooth bottom boundary
            SMOOTH_MULTI_FRAME(centerLineValues);                 //--- Smooth center line
            SMOOTH_MULTI_FRAME(rsiCurveValues);                   //--- Smooth RSI curve
         }
      }
      return(currentPos);                                        //--- Return pos
   }
}

We then build the custom averaging functions that clean up your chosen price data before the RSI calculation even begins.

#define AVG_VARIANTS 1                                           //--- Define avg variants
#define AVG_ARRAY_X1 1 * AVG_VARIANTS                            //--- Define array x1
#define AVG_ARRAY_X2 2 * AVG_VARIANTS                            //--- Define array x2

//+------------------------------------------------------------------+
//| Compute custom average                                           |
//+------------------------------------------------------------------+
double computeCustomAverage(int avgApproach, double inputVal, double avgLen, int pos, int barCnt, int varIndex = 0) {
   switch (avgApproach) {                                        //--- Switch approach
   case Avg_Basic:                                               //--- Handle basic
      return(computeBasicAvg(inputVal, (int)avgLen, pos, barCnt, varIndex)); //--- Return basic avg
   case Avg_GrowthBased:                                         //--- Handle growth
      return(computeGrowthAvg(inputVal, avgLen, pos, barCnt, varIndex)); //--- Return growth avg
   case Avg_EvenedOut:                                           //--- Handle evened
      return(computeEvenedAvg(inputVal, avgLen, pos, barCnt, varIndex)); //--- Return evened avg
   case Avg_WeightedLinear:                                      //--- Handle weighted
      return(computeLinearAvg(inputVal, avgLen, pos, barCnt, varIndex)); //--- Return linear avg
   default:                                                      //--- Handle default
      return(inputVal);                                          //--- Return input
   }
}

double basicAvgArray[][AVG_ARRAY_X2];                            //--- Declare basic avg array

//+------------------------------------------------------------------+
//| Compute basic average                                            |
//+------------------------------------------------------------------+
double computeBasicAvg(double inputVal, int avgLen, int pos, int barCnt, int varIndex = 0) {
   if (ArrayRange(basicAvgArray, 0) != barCnt) ArrayResize(basicAvgArray, barCnt); //--- Resize array
   varIndex *= 2;                                                //--- Adjust index
   int offset;                                                   //--- Declare offset

   basicAvgArray[pos][varIndex + 0] = inputVal;                  //--- Set value
   basicAvgArray[pos][varIndex + 1] = inputVal;                  //--- Set avg
   for (offset = 1; offset < avgLen && (pos - offset) >= 0; offset++) //--- Loop offsets
      basicAvgArray[pos][varIndex + 1] += basicAvgArray[pos - offset][varIndex + 0]; //--- Accumulate avg
   basicAvgArray[pos][varIndex + 1] /= 1.0 * offset;            //--- Average
   return(basicAvgArray[pos][varIndex + 1]);                     //--- Return avg
}

double growthAvgArray[][AVG_ARRAY_X1];                           //--- Declare growth avg array

//+------------------------------------------------------------------+
//| Compute growth average                                           |
//+------------------------------------------------------------------+
double computeGrowthAvg(double inputVal, double avgLen, int pos, int barCnt, int varIndex = 0) {
   if (ArrayRange(growthAvgArray, 0) != barCnt) ArrayResize(growthAvgArray, barCnt); //--- Resize array

   growthAvgArray[pos][varIndex] = inputVal;                     //--- Set value
   if (pos > 0 && avgLen > 1)                                    //--- Check pos and len
      growthAvgArray[pos][varIndex] = growthAvgArray[pos - 1][varIndex] + (2.0 / (1.0 + avgLen)) * (inputVal - growthAvgArray[pos - 1][varIndex]); //--- Compute growth
   return(growthAvgArray[pos][varIndex]);                        //--- Return avg
}

double evenedAvgArray[][AVG_ARRAY_X1];                           //--- Declare evened avg array

//+------------------------------------------------------------------+
//| Compute evened average                                           |
//+------------------------------------------------------------------+
double computeEvenedAvg(double inputVal, double avgLen, int pos, int barCnt, int varIndex = 0) {
   if (ArrayRange(evenedAvgArray, 0) != barCnt) ArrayResize(evenedAvgArray, barCnt); //--- Resize array

   evenedAvgArray[pos][varIndex] = inputVal;                     //--- Set value
   if (pos > 1 && avgLen > 1)                                    //--- Check pos and len
      evenedAvgArray[pos][varIndex] = evenedAvgArray[pos - 1][varIndex] + (inputVal - evenedAvgArray[pos - 1][varIndex]) / avgLen; //--- Compute evened
   return(evenedAvgArray[pos][varIndex]);                        //--- Return avg
}

double linearAvgArray[][AVG_ARRAY_X1];                           //--- Declare linear avg array

//+------------------------------------------------------------------+
//| Compute linear average                                           |
//+------------------------------------------------------------------+
double computeLinearAvg(double inputVal, double avgLen, int pos, int barCnt, int varIndex = 0) {
   if (ArrayRange(linearAvgArray, 0) != barCnt) ArrayResize(linearAvgArray, barCnt); //--- Resize array

   linearAvgArray[pos][varIndex] = inputVal;                     //--- Set value
   if (avgLen <= 1) return(inputVal);                            //--- Return if no avg

   double totalWeights = avgLen;                                 //--- Set total weights
   double totalValues = avgLen * inputVal;                       //--- Set total values

   for (int offset = 1; offset < avgLen && (pos - offset) >= 0; offset++) { //--- Loop offsets
      double currWeight = avgLen - offset;                       //--- Compute weight
      totalWeights += currWeight;                                //--- Accumulate weights
      totalValues += currWeight * linearAvgArray[pos - offset][varIndex]; //--- Accumulate values
   }
   return(totalValues / totalWeights);                           //--- Return avg
}

We also create the fetchChosenData function that grabs exactly the price type you selected (raw or fully smoothed).

#define DATA_VARIANTS 1                                          //--- Define data variants
#define DATA_VARIANT_SIZE 4                                      //--- Define variant size

double smoothedDataArray[][DATA_VARIANTS * DATA_VARIANT_SIZE];   //--- Declare smoothed data array

//+------------------------------------------------------------------+
//| Fetch chosen data                                                |
//+------------------------------------------------------------------+
double fetchChosenData(int dataType, const double& opens[], const double& closes[], const double& highs[], const double& lows[], int pos, int barCnt, int varIndex = 0) {
   if (dataType >= Data_SmoothedClose) {                         //--- Check smoothed
      if (ArrayRange(smoothedDataArray, 0) != barCnt) ArrayResize(smoothedDataArray, barCnt); //--- Resize array
      varIndex *= DATA_VARIANT_SIZE;                             //--- Adjust index

      double smoothedOpen;                                       //--- Declare smoothed open
      if (pos > 0)                                               //--- Check pos
         smoothedOpen = (smoothedDataArray[pos - 1][varIndex + 2] + smoothedDataArray[pos - 1][varIndex + 3]) / 2.0; //--- Compute smoothed open
      else                                                       //--- Handle initial
         smoothedOpen = (opens[pos] + closes[pos]) / 2;          //--- Set initial open

      double smoothedClose = (opens[pos] + highs[pos] + lows[pos] + closes[pos]) / 4.0; //--- Compute smoothed close
      double smoothedHigh = MathMax(highs[pos], MathMax(smoothedOpen, smoothedClose)); //--- Compute smoothed high
      double smoothedLow = MathMin(lows[pos], MathMin(smoothedOpen, smoothedClose)); //--- Compute smoothed low

      smoothedDataArray[pos][varIndex + 2] = smoothedOpen;       //--- Set smoothed open
      smoothedDataArray[pos][varIndex + 3] = smoothedClose;      //--- Set smoothed close

      switch (dataType) {                                        //--- Switch data type
      case Data_SmoothedClose:                                   //--- Handle smoothed close
         return(smoothedClose);                                  //--- Return close
      case Data_SmoothedOpen:                                    //--- Handle smoothed open
         return(smoothedOpen);                                   //--- Return open
      case Data_SmoothedHigh:                                    //--- Handle smoothed high
         return(smoothedHigh);                                   //--- Return high
      case Data_SmoothedLow:                                     //--- Handle smoothed low
         return(smoothedLow);                                    //--- Return low
      case Data_SmoothedMid:                                     //--- Handle smoothed mid
         return((smoothedHigh + smoothedLow) / 2.0);             //--- Return mid
      case Data_SmoothedMidBody:                                 //--- Handle smoothed mid body
         return((smoothedOpen + smoothedClose) / 2.0);           //--- Return mid body
      case Data_SmoothedStandard:                                //--- Handle smoothed standard
         return((smoothedHigh + smoothedLow + smoothedClose) / 3.0); //--- Return standard
      case Data_SmoothedBalanced:                                //--- Handle smoothed balanced
         return((smoothedHigh + smoothedLow + smoothedClose + smoothedClose) / 4.0); //--- Return balanced
      case Data_SmoothedOverall:                                 //--- Handle smoothed overall
         return((smoothedHigh + smoothedLow + smoothedClose + smoothedOpen) / 4.0); //--- Return overall
      case Data_SmoothedAdjusted:                                //--- Handle smoothed adjusted
         if (smoothedClose > smoothedOpen) return((smoothedHigh + smoothedClose) / 2.0); //--- Return high close
         else return((smoothedLow + smoothedClose) / 2.0);       //--- Return low close
      case Data_SmoothedExtreme:                                 //--- Handle smoothed extreme
         if (smoothedClose > smoothedOpen) return(smoothedHigh); //--- Return high
         if (smoothedClose < smoothedOpen) return(smoothedLow);  //--- Return low
         return(smoothedClose);                                  //--- Return close
      }
   }

   switch (dataType) {                                           //--- Switch data type
   case Data_ClosePrice:                                         //--- Handle close
      return(closes[pos]);                                       //--- Return close
   case Data_OpenPrice:                                          //--- Handle open
      return(opens[pos]);                                        //--- Return open
   case Data_HighPrice:                                          //--- Handle high
      return(highs[pos]);                                        //--- Return high
   case Data_LowPrice:                                           //--- Handle low
      return(lows[pos]);                                         //--- Return low
   case Data_MidPoint:                                           //--- Handle mid point
      return((highs[pos] + lows[pos]) / 2.0);                    //--- Return mid
   case Data_MidBodyAverage:                                     //--- Handle mid body
      return((opens[pos] + closes[pos]) / 2.0);                  //--- Return mid body
   case Data_StandardPrice:                                      //--- Handle standard
      return((highs[pos] + lows[pos] + closes[pos]) / 3.0);      //--- Return standard
   case Data_BalancedPrice:                                      //--- Handle balanced
      return((highs[pos] + lows[pos] + closes[pos] + closes[pos]) / 4.0); //--- Return balanced
   case Data_OverallAverage:                                     //--- Handle overall
      return((highs[pos] + lows[pos] + closes[pos] + opens[pos]) / 4.0); //--- Return overall
   case Data_DirectionAdjusted:                                  //--- Handle direction adjusted
      if (closes[pos] > opens[pos]) return((highs[pos] + closes[pos]) / 2.0); //--- Return high close
      else return((lows[pos] + closes[pos]) / 2.0);              //--- Return low close
   case Data_ExtremeAdjusted:                                    //--- Handle extreme adjusted
      if (closes[pos] > opens[pos]) return(highs[pos]);          //--- Return high
      if (closes[pos] < opens[pos]) return(lows[pos]);           //--- Return low
      return(closes[pos]);                                       //--- Return close
   }
   return(0);                                                    //--- Return zero
}

Then comes the star of the show — the computeRsiValue function that handles all seven RSI variants with their unique math.

#define RSI_VARIANTS 1                                           //--- Define RSI variants

double rsiComputeArray[][RSI_VARIANTS * 13];                     //--- Declare RSI compute array

#define DATA_SHIFT_POS 0                                         //--- Define data shift pos
#define DATA_SHIFTS_POS 3                                        //--- Define data shifts pos
#define SHIFT_POS 1                                              //--- Define shift pos
#define ABS_SHIFT_POS 2                                          //--- Define abs shift pos
#define RSI_COMPUTE_POS 1                                        //--- Define RSI compute pos
#define RS_COMPUTE_POS 1                                         //--- Define RS compute pos

//+------------------------------------------------------------------+
//| Compute RSI value                                                |
//+------------------------------------------------------------------+
double computeRsiValue(int rsiVariant, double currData, double rsiLen, int pos, int barCnt, int varIndex = 0) {
   if (ArrayRange(rsiComputeArray, 0) != barCnt) ArrayResize(rsiComputeArray, barCnt); //--- Resize array
   int arrayOffset = varIndex * 13;                              //--- Compute offset

   rsiComputeArray[pos][arrayOffset + DATA_SHIFT_POS] = currData; //--- Set data shift

   switch (rsiVariant) {                                         //--- Switch variant
   case Variant_BasicStyle: {                                    //--- Handle basic
      double factorAlpha = 1.0 / MathMax(rsiLen, 1);             //--- Compute alpha
      if (pos < rsiLen) {                                        //--- Check initial
         int cnt;                                                //--- Initialize count
         double totalAbsShifts = 0;                              //--- Initialize total abs
         for (cnt = 0; cnt < rsiLen && (pos - cnt - 1) >= 0; cnt++) //--- Loop shifts
            totalAbsShifts += MathAbs(rsiComputeArray[pos - cnt][arrayOffset + DATA_SHIFT_POS] - rsiComputeArray[pos - cnt - 1][arrayOffset + DATA_SHIFT_POS]); //--- Accumulate abs shifts
         rsiComputeArray[pos][arrayOffset + SHIFT_POS] = (rsiComputeArray[pos][arrayOffset + DATA_SHIFT_POS] - rsiComputeArray[0][arrayOffset + DATA_SHIFT_POS]) / MathMax(cnt, 1); //--- Set shift
         rsiComputeArray[pos][arrayOffset + ABS_SHIFT_POS] = totalAbsShifts / MathMax(cnt, 1); //--- Set abs shift
      } else {                                                   //--- Handle non-initial
         double dataShift = rsiComputeArray[pos][arrayOffset + DATA_SHIFT_POS] - rsiComputeArray[pos - 1][arrayOffset + DATA_SHIFT_POS]; //--- Compute shift
         rsiComputeArray[pos][arrayOffset + SHIFT_POS] = rsiComputeArray[pos - 1][arrayOffset + SHIFT_POS] + factorAlpha * (dataShift - rsiComputeArray[pos - 1][arrayOffset + SHIFT_POS]); //--- Update shift
         rsiComputeArray[pos][arrayOffset + ABS_SHIFT_POS] = rsiComputeArray[pos - 1][arrayOffset + ABS_SHIFT_POS] + factorAlpha * (MathAbs(dataShift) - rsiComputeArray[pos - 1][arrayOffset + ABS_SHIFT_POS]); //--- Update abs shift
      }
      return(50.0 * (rsiComputeArray[pos][arrayOffset + SHIFT_POS] / MathMax(rsiComputeArray[pos][arrayOffset + ABS_SHIFT_POS], DBL_MIN) + 1)); //--- Return RSI
   }

   case Variant_GradualStyle: {                                  //--- Handle gradual
      double posSum = 0, negSum = 0;                             //--- Initialize sums
      for (int offset = 0; offset < (int)rsiLen && (pos - offset - 1) >= 0; offset++) { //--- Loop offsets
         double dataDiff = rsiComputeArray[pos - offset][arrayOffset + DATA_SHIFT_POS] - rsiComputeArray[pos - offset - 1][arrayOffset + DATA_SHIFT_POS]; //--- Compute diff
         if (dataDiff > 0) posSum += dataDiff;                   //--- Accumulate positive
         else negSum -= dataDiff;                                //--- Accumulate negative
      }
      if (pos < 1) rsiComputeArray[pos][arrayOffset + RSI_COMPUTE_POS] = 50; //--- Set initial
      else rsiComputeArray[pos][arrayOffset + RSI_COMPUTE_POS] = rsiComputeArray[pos - 1][arrayOffset + RSI_COMPUTE_POS] + (1 / MathMax(rsiLen, 1)) * (100 * posSum / MathMax(posSum + negSum, DBL_MIN) - rsiComputeArray[pos - 1][arrayOffset + RSI_COMPUTE_POS]); //--- Compute gradual
      return(rsiComputeArray[pos][arrayOffset + RSI_COMPUTE_POS]); //--- Return value
   }

   case Variant_QuickStyle: {                                    //--- Handle quick
      double posSum = 0, negSum = 0;                             //--- Initialize sums
      for (int offset = 0; offset < (int)rsiLen && (pos - offset - 1) >= 0; offset++) { //--- Loop offsets
         double dataDiff = rsiComputeArray[pos - offset][arrayOffset + DATA_SHIFT_POS] - rsiComputeArray[pos - offset - 1][arrayOffset + DATA_SHIFT_POS]; //--- Compute diff
         if (dataDiff > 0) posSum += dataDiff;                   //--- Accumulate positive
         else negSum -= dataDiff;                                //--- Accumulate negative
      }
      return(100 * posSum / MathMax(posSum + negSum, DBL_MIN));  //--- Return quick RSI
   }

   case Variant_EhlersStyle: {                                   //--- Handle Ehlers
      double posSum = 0, negSum = 0;                             //--- Initialize sums
      rsiComputeArray[pos][arrayOffset + DATA_SHIFTS_POS] = (pos > 2) ? (rsiComputeArray[pos][arrayOffset + DATA_SHIFT_POS] + 2.0 * rsiComputeArray[pos - 1][arrayOffset + DATA_SHIFT_POS] + rsiComputeArray[pos - 2][arrayOffset + DATA_SHIFT_POS]) / 4.0 : currData; //--- Compute shifts
      for (int offset = 0; offset < (int)rsiLen && (pos - offset - 1) >= 0; offset++) { //--- Loop offsets
         double dataDiff = rsiComputeArray[pos - offset][arrayOffset + DATA_SHIFTS_POS] - rsiComputeArray[pos - offset - 1][arrayOffset + DATA_SHIFTS_POS]; //--- Compute diff
         if (dataDiff > 0) posSum += dataDiff;                   //--- Accumulate positive
         else negSum -= dataDiff;                                //--- Accumulate negative
      }
      return(50 * (posSum - negSum) / MathMax(posSum + negSum, DBL_MIN) + 50); //--- Return Ehlers RSI
   }

   case Variant_CuttlerStyle: {                                  //--- Handle Cuttler
      double posSum = 0;                                         //--- Initialize positive sum
      double negSum = 0;                                         //--- Initialize negative sum
      for (int offset = 0; offset < (int)rsiLen && (pos - offset - 1) >= 0; offset++) { //--- Loop offsets
         double dataDiff = rsiComputeArray[pos - offset][arrayOffset + DATA_SHIFT_POS] - rsiComputeArray[pos - offset - 1][arrayOffset + DATA_SHIFT_POS]; //--- Compute diff
         if (dataDiff > 0) posSum += dataDiff;                   //--- Accumulate positive
         else negSum -= dataDiff;                                //--- Accumulate negative
      }
      rsiComputeArray[pos][varIndex + RSI_COMPUTE_POS] = 100.0 - 100.0 / (1.0 + posSum / MathMax(negSum, DBL_MIN)); //--- Compute Cuttler
      return(rsiComputeArray[pos][varIndex + RSI_COMPUTE_POS]);  //--- Return value
   }

   case Variant_HarrisStyle: {                                   //--- Handle Harris
      double avgPos = 0, avgNeg = 0, posCnt = 0, negCnt = 0;     //--- Initialize averages and counts
      for (int offset = 0; offset < (int)rsiLen && (pos - offset - 1) >= 0; offset++) { //--- Loop offsets
         double dataDiff = rsiComputeArray[pos - offset][varIndex + DATA_SHIFT_POS] - rsiComputeArray[pos - offset - 1][varIndex + DATA_SHIFT_POS]; //--- Compute diff
         if (dataDiff > 0) {                                     //--- Handle positive
            avgPos += dataDiff;                                  //--- Accumulate positive
            posCnt++;                                            //--- Increment positive count
         } else {                                                //--- Handle negative
            avgNeg -= dataDiff;                                  //--- Accumulate negative
            negCnt++;                                            //--- Increment negative count
         }
      }
      if (posCnt != 0) avgPos /= posCnt;                         //--- Average positive
      if (negCnt != 0) avgNeg /= negCnt;                         //--- Average negative
      rsiComputeArray[pos][varIndex + RSI_COMPUTE_POS] = 100 - 100 / (1.0 + (avgPos / MathMax(avgNeg, DBL_MIN))); //--- Compute Harris
      return(rsiComputeArray[pos][varIndex + RSI_COMPUTE_POS]);  //--- Return value
   }

   case Variant_RsxStyle: {                                      //--- Handle RSX
      double kgVal = 3.0 / (2.0 + rsiLen), hgVal = 1.0 - kgVal;  //--- Compute kg and hg
      if (pos < rsiLen) {                                        //--- Check initial
         for (int offset = 1; offset < 13; offset++) rsiComputeArray[pos][offset + arrayOffset] = 0; //--- Zero offsets
         return(50);                                             //--- Return initial
      }

      double motion = rsiComputeArray[pos][DATA_SHIFT_POS + arrayOffset] - rsiComputeArray[pos - 1][DATA_SHIFT_POS + arrayOffset]; //--- Compute motion
      double absMotion = MathAbs(motion);                        //--- Compute abs motion
      for (int offset = 0; offset < 3; offset++) {               //--- Loop offsets
         int subOffset = offset * 2;                             //--- Compute sub offset
         rsiComputeArray[pos][arrayOffset + subOffset + 1] = kgVal * motion + hgVal * rsiComputeArray[pos - 1][arrayOffset + subOffset + 1]; //--- Update 1
         rsiComputeArray[pos][arrayOffset + subOffset + 2] = kgVal * rsiComputeArray[pos][arrayOffset + subOffset + 1] + hgVal * rsiComputeArray[pos - 1][arrayOffset + subOffset + 2]; //--- Update 2
         motion = 1.5 * rsiComputeArray[pos][arrayOffset + subOffset + 1] - 0.5 * rsiComputeArray[pos][arrayOffset + subOffset + 2]; //--- Update motion

         rsiComputeArray[pos][arrayOffset + subOffset + 7] = kgVal * absMotion + hgVal * rsiComputeArray[pos - 1][arrayOffset + subOffset + 7]; //--- Update 7
         rsiComputeArray[pos][arrayOffset + subOffset + 8] = kgVal * rsiComputeArray[pos][arrayOffset + subOffset + 7] + hgVal * rsiComputeArray[pos - 1][arrayOffset + subOffset + 8]; //--- Update 8
         absMotion = 1.5 * rsiComputeArray[pos][arrayOffset + subOffset + 7] - 0.5 * rsiComputeArray[pos][arrayOffset + subOffset + 8]; //--- Update abs motion
      }
      return(MathMax(MathMin((motion / MathMax(absMotion, DBL_MIN) + 1.0) * 50.0, 100.00), 0.00)); //--- Return RSX
   }
   }
   return(0);                                                    //--- Return zero
}

Back in the main calculation loop we put everything together for single-timeframe mode.

int beginPos = (int)MathMax(prevProcessed - 1, 0);            //--- Set begin pos
for (; beginPos < barTotal && !_StopFlag; beginPos++) {       //--- Loop bars
   double adjustedData = computeCustomAverage(DataSmoothingApproach, fetchChosenData(SourceData, opens, closes, highs, lows, beginPos, barTotal), DataSmoothingLength, beginPos, barTotal); //--- Compute adjusted data
}

return(beginPos);                                             //--- Return begin pos

This gives us the following.

INITIAL INDICATOR DATA COMPUTATIONS

We then continue the loop to calculate the actual RSI values, set boundaries (static or dynamic), apply hue colors, and fill the areas.

for (; beginPos < barTotal && !_StopFlag; beginPos++) {       //--- Loop bars
   double adjustedData = computeCustomAverage(DataSmoothingApproach, fetchChosenData(SourceData, opens, closes, highs, lows, beginPos, barTotal), DataSmoothingLength, beginPos, barTotal); //--- Compute adjusted data
   rsiCurveValues[beginPos] = computeRsiValue(ChosenRsiVariant, adjustedData, RsiLength, beginPos, barTotal); //--- Compute RSI value

   if (DynamicBoundaryLength <= 1) {                          //--- Check static boundary
      topBoundaryValues[beginPos] = TopBoundaryPercent;       //--- Set top boundary
      bottomBoundaryValues[beginPos] = BottomBoundaryPercent; //--- Set bottom boundary
      centerLineValues[beginPos] = (topBoundaryValues[beginPos] + bottomBoundaryValues[beginPos]) / 2; //--- Set center line
   } else {                                                   //--- Handle dynamic
      double lowestVal = rsiCurveValues[beginPos];            //--- Set initial low
      double highestVal = rsiCurveValues[beginPos];           //--- Set initial high
      for (int offset = 1; offset < DynamicBoundaryLength && beginPos - offset >= 0; offset++) { //--- Loop offsets
         lowestVal = MathMin(rsiCurveValues[beginPos - offset], lowestVal); //--- Update low
         highestVal = MathMax(rsiCurveValues[beginPos - offset], highestVal); //--- Update high
      }
      double valRange = highestVal - lowestVal;               //--- Compute range
      topBoundaryValues[beginPos] = lowestVal + TopBoundaryPercent * valRange / 100.0; //--- Set top boundary
      bottomBoundaryValues[beginPos] = lowestVal + BottomBoundaryPercent * valRange / 100.0; //--- Set bottom boundary
      centerLineValues[beginPos] = lowestVal + 0.5 * valRange; //--- Set center line
   }

   switch (HueAdjustmentOn) {                                 //--- Switch hue condition
   case Hue_OnBoundaryCrossing:                               //--- Handle boundary crossing
      rsiHueValues[beginPos] = (rsiCurveValues[beginPos] > topBoundaryValues[beginPos]) ? 1 : (rsiCurveValues[beginPos] < bottomBoundaryValues[beginPos]) ? 2 : 0; //--- Set hue
      break;
   case Hue_OnCenterCrossing:                                 //--- Handle center crossing
      rsiHueValues[beginPos] = (rsiCurveValues[beginPos] > centerLineValues[beginPos]) ? 1 : (rsiCurveValues[beginPos] < centerLineValues[beginPos]) ? 2 : 0; //--- Set hue
      break;
   default:                                                   //--- Handle default
      rsiHueValues[beginPos] = (beginPos > 0) ? (rsiCurveValues[beginPos] > rsiCurveValues[beginPos - 1]) ? 1 : (rsiCurveValues[beginPos] < rsiCurveValues[beginPos - 1]) ? 2 : 0 : 0; //--- Set hue
   }

   areaFillTop[beginPos] = rsiCurveValues[beginPos];          //--- Set top fill
   areaFillBottom[beginPos] = (rsiCurveValues[beginPos] > topBoundaryValues[beginPos]) ? topBoundaryValues[beginPos] : (rsiCurveValues[beginPos] < bottomBoundaryValues[beginPos]) ? bottomBoundaryValues[beginPos] : rsiCurveValues[beginPos]; //--- Set bottom fill
}

processedBarCounts[barTotal - 1] = MathMax(barTotal - prevProcessed + 1, 1); //--- Set processed counts

This gives us the following.

COMPLETE INDICATOR

Finally, we add the clean alert system so you get notified when the hue (and therefore the momentum) shifts.

//+------------------------------------------------------------------+
//| Process alert triggers                                           |
//+------------------------------------------------------------------+
void processAlertTriggers(const datetime& timeStamps[], double& hueTrends[], int barTotal) {
   if (!ActivateNotifications) return;                           //--- Check notifications
   int notifyIndex = barTotal - 1;                               //--- Set notify index
   if (!NotifyOnActiveBar) notifyIndex = barTotal - 2;           //--- Adjust if not active
   datetime notifyStamp = timeStamps[notifyIndex];               //--- Get stamp

   if (hueTrends[notifyIndex] != hueTrends[notifyIndex - 1]) {   //--- Check hue change
      if (hueTrends[notifyIndex] == 1) triggerNotification(notifyStamp, "rising"); //--- Trigger rising
      if (hueTrends[notifyIndex] == 2) triggerNotification(notifyStamp, "falling"); //--- Trigger falling
   }
}

//+------------------------------------------------------------------+
//| Trigger notification                                             |
//+------------------------------------------------------------------+
void triggerNotification(datetime stamp, string trend) {
   static string prevTrend = "none";                             //--- Initialize previous trend
   static datetime prevStamp;                                    //--- Initialize previous stamp

   if (prevTrend != trend || prevStamp != stamp) {               //--- Check change
      prevTrend = trend;                                         //--- Update trend
      prevStamp = stamp;                                         //--- Update stamp

      string notifyText = convertTimeframeToText(_Period) + " " + _Symbol + " at " + TimeToString(TimeLocal(), TIME_SECONDS) + describeRsiVariant(ChosenRsiVariant) + " trend shifted to " + trend; //--- Format text
      Alert(notifyText);                                         //--- Send alert
   }
}

Finally, we get this.

ACTIVATED ALERT SYSTEM

We'll test this next now.


Backtesting

We put the indicator through its paces in trending, ranging, and super-volatile markets. The smoothing options sliced through the noise like a hot knife through butter, the dynamic boundaries adapted beautifully, and the hue shifts made spotting reversals feel almost effortless. Here’s the compiled visualization in a single GIF showing the indicator in action across different pairs and timeframes:

ADVANCED RSI BACKTEST


Conclusion

There you have it! We’ve taken the classic RSI and turned it into a highly customizable, adaptive powerhouse in MQL5. You now have multiple calculation variants, smart data smoothing, automatic color hue shifts, boundaries that evolve with the market, and smooth multi-timeframe support — all wrapped up in one clean, professional indicator.

Play with the inputs, experiment with different combinations, and make it your own. Your charts are about to look a lot smarter, and your trading decisions just got a serious upgrade.

Happy coding and even happier trading! 📈🚀

Disclaimer: The ideas and strategies presented in this resource are solely those of the author and are intended for informational and educational purposes only. They do not constitute financial advice, and past performance is not indicative of future results. All materials, including but not limited to text, images, files, and any downloadable content, are protected by copyright and intellectual property laws and are the exclusive property of Forex Algo-Trader or its licensors. Reproduction, distribution, modification, or commercial use of these materials without prior written consent from Forex Algo-Trader is strictly prohibited and may result in legal action. Users are advised to exercise extreme caution, perform thorough independent research, and consult with qualified financial professionals before implementing any trading strategies or decisions based on this resource, as trading in financial markets involves significant risk of loss.

Recent Comments

Go to discussion to Comment or View other Comments

No comments yet. Be the first to comment!

Banner
🤖 Building a ChatGPT AI Trade Brain in MQL5
What’s the point of an AI trading assistant that talks but never acts? In this article, we transfo...
2026-05-15 16:50:14
Banner
Grid Scalper MA MT5 EA Review (2026): The Forever-Free MetaTrader 5 Robot
Grid Scalper MA MT5 EA Review (2026): The forever-free MetaTrader 5 robot that's quietly earning a 4...
2026-05-14 19:35:18
Banner
Supercharging Your TPO Market Profile With Volume Intelligence
You've got a TPO market profile that tracks where price spent its time — now imagine giving it a f...
2026-04-03 16:05:42