Tame the Whipsaws: Ridge Regression Regularized Moving Average (RRRMA)
Posted: Mon Aug 10, 2026 3:52 pm
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.
Play around with the Lambda settings on your favorite pairs and let me know what combinations work best for your sessions. Happy scalping!
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);
}