IC Markets

Tame the Whipsaws: Ridge Regression Regularized Moving Average (RRRMA)

Optimize MetaTrader 4, MetaTrader 5, cTrader, and TradingView for speed. Discuss Level II Market Depth (DOM), custom hotkeys, and volume indicators.
Post Reply
FTtrader
Posts: 151
Joined: Mon Aug 03, 2026 2:43 pm

Tame the Whipsaws: Ridge Regression Regularized Moving Average (RRRMA)

Post by FTtrader »

Hey everyone,

If you scalp on the M1 or M5 timeframes, you already know the ultimate moving average dilemma: you either get crushed by lag (SMA/EMA) or you get chopped to pieces by false signals and noise (LSMA/HMA).

Recently, I’ve been experimenting with applying machine learning regularization to traditional indicators, and I wanted to share a custom tool I coded: the Ridge Regression Regularized Moving Average (RRRMA).

How it Works:
A standard Linear Regression Moving Average (LSMA) calculates a line of best fit and plots the endpoint. It’s incredibly fast and has almost zero lag, but it overreacts to every single micro-spike, causing whipsaws.

RRRMA fixes this by applying an L2 penalty (Tikhonov regularization) to the regression slope. In plain English: we introduce a "Lambda" parameter that mathematically penalizes extreme changes in the MA's direction.

Lambda = 0: You get a standard, hyper-reactive LSMA.

Lambda = High (e.g., 500): The penalty is so high it flattens the slope, turning it into a smooth, traditional SMA.

The Sweet Spot (e.g., 20–100): You get the explosive responsiveness of an LSMA, but the regularization aggressively filters out the random tick-noise that usually triggers false entries.

How to Scalp with It:
I recommend pairing a fast RRRMA (Period 14, Lambda 30) with a slower one (Period 50, Lambda 100). Because the regularization strips out the noise, the crossovers are significantly cleaner than standard EMAs, keeping you on the right side of short-term momentum bursts without getting shaken out by a single erratic 1-minute candle.

Here is the source code for MT4. Just create a new custom indicator, paste this in, and compile.

Code: Select all

//+------------------------------------------------------------------+
//|                                                    Ridge_MA.mq4  |
//|                                      Ridge Regularized LSMA      |
//+------------------------------------------------------------------+
#property strict
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1 clrOrange
#property indicator_width1 2

input int InpPeriod = 14;      // MA Period
input double InpLambda = 50.0; // Ridge Penalty (Lambda)

double MABuffer[];

int OnInit() {
    SetIndexBuffer(0, MABuffer);
    SetIndexStyle(0, DRAW_LINE);
    IndicatorShortName("RRRMA(" + IntegerToString(InpPeriod) + ", " + DoubleToString(InpLambda, 1) + ")");
    return(INIT_SUCCEEDED);
}

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[]) {
                
    int limit = rates_total - prev_calculated;
    if(limit > 1) limit = rates_total - InpPeriod - 1;

    // Calculate constants for the regression denominator
    double x_mean = (InpPeriod - 1) / 2.0;
    
    // Sum of squared differences for X
    double sum_x2 = (InpPeriod * (MathPow(InpPeriod, 2) - 1)) / 12.0;
    
    // Apply L2 Regularization (Ridge) to the denominator
    double ridge_denom = sum_x2 + InpLambda; 

    for(int i = limit; i >= 0; i--) {
        double sum_y = 0;
        
        // Get the average price (Y mean) for the window
        for(int j = 0; j < InpPeriod; j++) {
            sum_y += iClose(Symbol(), 0, i + j);
        }
        double y_mean = sum_y / InpPeriod;

        // Calculate the regularized slope (Beta)
        double num = 0;
        for(int j = 0; j < InpPeriod; j++) {
            double y_val = iClose(Symbol(), 0, i + j);
            num += (j - x_mean) * (y_val - y_mean);
        }
        
        double beta = num / ridge_denom;
        
        // Forecast the current point (where x = 0 in our loop)
        MABuffer[i] = y_mean + beta * (0 - x_mean);
    }
    
    return(rates_total);
}
Play around with the Lambda settings on your favorite pairs and let me know what combinations work best for your sessions. Happy scalping!
FTtrader
Posts: 151
Joined: Mon Aug 03, 2026 2:43 pm

Re: Tame the Whipsaws: Ridge Regression Regularized Moving Average (RRRMA)

Post by FTtrader »

Here it is for MT5 traders :-)

One of the great things about compiling this in MT5 is that by using the price[] array in the OnCalculate function, you aren't forced to only use the Close price. When you load the indicator on your chart, the MT5 parameters box will let you apply it to the Open, High, Low, Median, or even the data of other indicators.

How to install in MT5:
Open MetaEditor in MT5 (press F4).

Go to File > New > Custom Indicator, and name it Ridge_MA.

Delete all the default template code.

Paste the code below, hit Compile (F7), and attach it to your chart.

Code: Select all

//+------------------------------------------------------------------+
//|                                                    Ridge_MA.mq5  |
//|                                      Ridge Regularized LSMA      |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1

// Plot formatting
#property indicator_label1  "RRRMA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrOrange
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

input int    InpPeriod = 14;      // MA Period
input double InpLambda = 50.0;    // Ridge Penalty (Lambda)

// Indicator buffer
double MABuffer[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   // Map the array to the indicator data buffer
   SetIndexBuffer(0, MABuffer, INDICATOR_DATA);
   
   // Set the name that appears in the top left of the subwindow
   IndicatorSetString(INDICATOR_SHORTNAME, "RRRMA(" + IntegerToString(InpPeriod) + ", " + DoubleToString(InpLambda, 1) + ")");
   
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
   // Wait until we have enough candles to calculate the period
   if(rates_total < InpPeriod)
      return(0);

   // MT5 arrays are left-to-right by default. We set them as series 
   // so index [0] is the current active candle, matching MT4 logic.
   ArraySetAsSeries(price, true);
   ArraySetAsSeries(MABuffer, true);

   // Determine how many bars need calculating
   int limit = rates_total - prev_calculated;
   if(limit > rates_total - InpPeriod) 
       limit = rates_total - InpPeriod;

   // Calculate constants for the regression denominator
   double x_mean = (InpPeriod - 1) / 2.0;
   
   // Sum of squared differences for X
   double sum_x2 = (InpPeriod * (MathPow(InpPeriod, 2) - 1)) / 12.0;
   
   // Apply L2 Regularization (Ridge) to the denominator
   double ridge_denom = sum_x2 + InpLambda; 

   // Main calculation loop
   for(int i = limit; i >= 0 && !IsStopped(); i--)
     {
      double sum_y = 0;
      
      // Get the average price (Y mean) for the lookback window
      for(int j = 0; j < InpPeriod; j++)
        {
         sum_y += price[i + j];
        }
      double y_mean = sum_y / InpPeriod;

      // Calculate the regularized slope (Beta)
      double num = 0;
      for(int j = 0; j < InpPeriod; j++)
        {
         double y_val = price[i + j];
         num += (j - x_mean) * (y_val - y_mean);
        }
      
      double beta = num / ridge_denom;
      
      // Forecast the current point (where x = 0 in our loop)
      MABuffer[i] = y_mean + beta * (0 - x_mean);
     }
   
   return(rates_total);
  }
//+------------------------------------------------------------------+
FTtrader
Posts: 151
Joined: Mon Aug 03, 2026 2:43 pm

Re: Tame the Whipsaws: Ridge Regression Regularized Moving Average (RRRMA)

Post by FTtrader »

And finally for IC traders/scalpers in Ctrader:

Moving over to cTrader is a massive step up, especially since the cAlgo environment runs on standard C# and the .NET framework. This gives you a much cleaner, event-driven architecture to work with compared to MQL's procedural loops.

Because cTrader's Calculate method is called on every single tick for the current index, we can heavily optimize this indicator by caching the heavy mathematical constants inside the Initialize method. This keeps the CPU overhead extremely light, which is crucial if you are running this on low-latency ECN feeds for quick scalping.

How to install in cTrader:
1.) Open the Automate tab (cAlgo) in cTrader.

2.) Click New > Indicator and name it RidgeRegressionMA.

3.) Paste the C# code below over the default template.

4.) Click Build (or press Ctrl+B).

Code: Select all

using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class RidgeRegressionMA : Indicator
    {
        // By using DataSeries, you can apply this to Open, High, Low, Close, or even other indicators
        [Parameter("Source")]
        public DataSeries Source { get; set; }

        [Parameter("Period", DefaultValue = 14, MinValue = 2)]
        public int Period { get; set; }

        [Parameter("Lambda (Penalty)", DefaultValue = 50.0, MinValue = 0.0)]
        public double Lambda { get; set; }

        [Output("RRRMA", LineColor = "Orange", Thickness = 2)]
        public IndicatorDataSeries Result { get; set; }

        // Cached variables for performance
        private double _xMean;
        private double _ridgeDenom;

        protected override void Initialize()
        {
            // Pre-calculate constants here so we don't waste CPU cycles on every tick
            _xMean = (Period - 1) / 2.0;
            
            // Sum of squared differences for X
            double sumX2 = (Period * (Math.Pow(Period, 2) - 1)) / 12.0;
            
            // Apply L2 Regularization (Ridge) to the denominator
            _ridgeDenom = sumX2 + Lambda;
        }

        public override void Calculate(int index)
        {
            // Wait until we have enough historical data
            if (index < Period)
                return;

            double sumY = 0.0;

            // 1. Get the average price (Y mean) for the lookback window
            for (int j = 0; j < Period; j++)
            {
                // In cTrader, index is the current bar, and we look back 'j' bars
                sumY += Source[index - j];
            }
            
            double yMean = sumY / Period;
            double num = 0.0;

            // 2. Calculate the regularized slope (Beta)
            for (int j = 0; j < Period; j++)
            {
                double yVal = Source[index - j];
                num += (j - _xMean) * (yVal - yMean);
            }

            double beta = num / _ridgeDenom;

            // 3. Forecast the endpoint (where x = 0 in our loop context)
            Result[index] = yMean + beta * (0 - _xMean);
        }
    }
}
Like the MT5 version, utilizing the DataSeries Source parameter natively exposes the drop-down menu in cTrader's UI, allowing you to feed RSI, MACD, or any custom series directly into the regularized calculation without needing to rewrite the logic.

Let me know, if it was usefull for you. Happy trading, scalping :-)
Take a care.
Post Reply