Page 1 of 1

Taming the Noise: My MTF Stochastic RSI Scalping Strategy (+ MT4 Code!)

Posted: Thu Jul 30, 2026 7:46 pm
by PTScalper
Hey everyone,

If you’ve been scalping the M1 or M5 timeframes, you know the biggest enemy is market noise. You get a perfect buy signal, jump in, and immediately get stopped out by a micro-pullback.

Lately, I’ve been using a Multi-Timeframe (MTF) Stochastic RSI approach, and it has drastically improved my win rate.

The Stochastic RSI is incredibly sensitive to momentum, which makes it an amazing entry trigger. However, on lower timeframes, it overreacts. By overlaying higher timeframe (HTF) data directly onto your scalping chart, you get the ultimate trend filter without taking your eyes off the price action.

The Core Strategy:
The Setup: Attach the MTF StochRSI to your M1 chart, but set the indicator's input timeframe to M15.

The Trend Filter: Only look for long positions if the M15 StochRSI is pointing UP and crossing out of the oversold zone (below 20) or holding above the 50 midline.

The Trigger: Wait for your standard M1 StochRSI to dip into the oversold zone (below 20) and cross upward.

The Exit: Ride the momentum until the M1 StochRSI hits the overbought zone (above 80), keeping a tight 1:2 Risk/Reward ratio.

This keeps you trading strictly in the direction of the macro momentum while allowing you to snipe the micro pullbacks.

I've written a custom MT4 indicator for this. You can find the MQL4 code below—just compile it in MetaEditor and test it out. Let me know what periods and pairs you guys find work best!

Happy hunting! 📈

MT4 Indicator Code (MQL4)
To use this, open MetaEditor in MT4, create a new Custom Indicator, and replace the default code with the script below. Save and compile.

Code: Select all

//+------------------------------------------------------------------+
//|                                              MTF_StochRSI.mq4    |
//|                                              Forum Community     |
//+------------------------------------------------------------------+
#property strict
#property indicator_separate_window
#property indicator_minimum 0
#property indicator_maximum 100
#property indicator_buffers 2
#property indicator_color1 clrDodgerBlue
#property indicator_color2 clrRed

//--- Input Parameters
input ENUM_TIMEFRAMES InpTimeFrame = PERIOD_M15; // Higher Timeframe
input int InpRSI_Period = 14;                    // RSI Period
input int InpStoch_Period = 14;                  // Stochastic Period
input int InpSmoothK = 3;                        // %K Smoothing
input int InpSmoothD = 3;                        // %D Smoothing

//--- Indicator Buffers
double ExtKBuffer[];
double ExtDBuffer[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   SetIndexBuffer(0, ExtKBuffer);
   SetIndexStyle(0, DRAW_LINE);
   SetIndexLabel(0, "%K");
   
   SetIndexBuffer(1, ExtDBuffer);
   SetIndexStyle(1, DRAW_LINE);
   SetIndexLabel(1, "%D");
   
   // Add levels for Overbought/Oversold
   IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, 20);
   IndicatorSetDouble(INDICATOR_LEVELVALUE, 1, 80);
   
   IndicatorShortName("MTF StochRSI (" + EnumToString(InpTimeFrame) + ")");
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| 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[])
  {
   // Ensure enough bars are present
   if(rates_total < InpRSI_Period + InpStoch_Period) return 0;
   
   // MTF repainting logic: always recalculate current active bars
   int limit = rates_total - prev_calculated;
   if(limit > 0) limit = rates_total - 1; 

   for(int i = limit; i >= 0; i--)
     {
      // Find corresponding bar on the higher timeframe
      int shiftHTF = iBarShift(NULL, InpTimeFrame, time[i], false);
      
      // --- Calculate %K ---
      double sumK = 0.0;
      for(int k = 0; k < InpSmoothK; k++)
        {
         double minRSI = 100.0, maxRSI = 0.0;
         double currentRSI = iRSI(NULL, InpTimeFrame, InpRSI_Period, PRICE_CLOSE, shiftHTF + k);
         
         for(int j = 0; j < InpStoch_Period; j++)
           {
            double rsi = iRSI(NULL, InpTimeFrame, InpRSI_Period, PRICE_CLOSE, shiftHTF + k + j);
            if(rsi < minRSI) minRSI = rsi;
            if(rsi > maxRSI) maxRSI = rsi;
           }
         
         double raw = 0.0;
         if(maxRSI - minRSI > 0) 
            raw = 100.0 * (currentRSI - minRSI) / (maxRSI - minRSI);
         sumK += raw;
        }
      ExtKBuffer[i] = sumK / InpSmoothK;
      
      // --- Calculate %D (Moving Average of %K) ---
      double sumD = 0.0;
      for(int d = 0; d < InpSmoothD; d++)
        {
         double localSumK = 0.0;
         for(int k = 0; k < InpSmoothK; k++)
           {
            double minRSI = 100.0, maxRSI = 0.0;
            double currentRSI = iRSI(NULL, InpTimeFrame, InpRSI_Period, PRICE_CLOSE, shiftHTF + d + k);
            
            for(int j = 0; j < InpStoch_Period; j++)
              {
               double rsi = iRSI(NULL, InpTimeFrame, InpRSI_Period, PRICE_CLOSE, shiftHTF + d + k + j);
               if(rsi < minRSI) minRSI = rsi;
               if(rsi > maxRSI) maxRSI = rsi;
              }
            double raw = 0.0;
            if(maxRSI - minRSI > 0) 
               raw = 100.0 * (currentRSI - minRSI) / (maxRSI - minRSI);
            localSumK += raw;
           }
         sumD += (localSumK / InpSmoothK);
        }
      ExtDBuffer[i] = sumD / InpSmoothD;
     }
     
   return(rates_total);
  }
//+------------------------------------------------------------------+

Re: Taming the Noise: My MTF Stochastic RSI Scalping Strategy (+ MT4 Code!)

Posted: Thu Jul 30, 2026 7:48 pm
by PTScalper
Here is version for MT5 traders :-)

MetaTrader 5 (MQL5)
In MT5, custom indicators require handles and array buffering. To use this, open MetaEditor 5, create a new Custom Indicator, paste this code, and compile.

Code: Select all

//+------------------------------------------------------------------+
//|                                              MTF_StochRSI.mq5    |
//|                                              Forum Community     |
//+------------------------------------------------------------------+
#property indicator_separate_window
#property indicator_minimum 0
#property indicator_maximum 100
#property indicator_buffers 2
#property indicator_plots   2

#property indicator_level1  20
#property indicator_level2  80
#property indicator_levelcolor clrGray
#property indicator_levelstyle STYLE_DOT

//--- Plot %K
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_label1  "%K"

//--- Plot %D
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_label2  "%D"

//--- Input Parameters
input ENUM_TIMEFRAMES InpTimeFrame = PERIOD_M15; // Higher Timeframe
input int InpRSI_Period = 14;                    // RSI Period
input int InpStoch_Period = 14;                  // Stochastic Period
input int InpSmoothK = 3;                        // %K Smoothing
input int InpSmoothD = 3;                        // %D Smoothing

//--- Indicator Buffers
double ExtKBuffer[];
double ExtDBuffer[];

//--- Handles
int rsiHandle;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   SetIndexBuffer(0, ExtKBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, ExtDBuffer, INDICATOR_DATA);
   
   IndicatorSetString(INDICATOR_SHORTNAME, "MTF StochRSI (" + EnumToString(InpTimeFrame) + ")");
   
   rsiHandle = iRSI(_Symbol, InpTimeFrame, InpRSI_Period, PRICE_CLOSE);
   if(rsiHandle == INVALID_HANDLE)
     {
      Print("Failed to create RSI handle");
      return(INIT_FAILED);
     }
     
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| 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 < InpRSI_Period + InpStoch_Period) return 0;
   
   // Convert arrays to Series (index 0 is the most recent bar)
   ArraySetAsSeries(time, true);
   ArraySetAsSeries(ExtKBuffer, true);
   ArraySetAsSeries(ExtDBuffer, true);
   
   // Determine how many bars to calculate
   int limit = rates_total - prev_calculated;
   if(limit > 0) limit = rates_total - 1; 
   else limit = 0; // Always update the current active bar
   
   // Fetch HTF RSI Data
   double rsiBuffer[];
   int max_shiftHTF = iBarShift(_Symbol, InpTimeFrame, time[limit], false);
   int htf_bars_needed = max_shiftHTF + InpStoch_Period + InpSmoothK + InpSmoothD + 5;
   
   if(CopyBuffer(rsiHandle, 0, 0, htf_bars_needed, rsiBuffer) <= 0) return 0;
   ArraySetAsSeries(rsiBuffer, true); // Align RSI buffer with Series indexing
   
   // Calculation Loop
   for(int i = limit; i >= 0 && !IsStopped(); i--)
     {
      datetime currentTime = time[i];
      int shiftHTF = iBarShift(_Symbol, InpTimeFrame, currentTime, false);
      
      if(shiftHTF < 0 || shiftHTF + InpSmoothK + InpSmoothD + InpStoch_Period >= ArraySize(rsiBuffer))
        {
         ExtKBuffer[i] = EMPTY_VALUE;
         ExtDBuffer[i] = EMPTY_VALUE;
         continue;
        }
      
      // --- Calculate %K ---
      double sumK = 0.0;
      for(int k = 0; k < InpSmoothK; k++)
        {
         double minRSI = 100.0, maxRSI = 0.0;
         double currentRSI = rsiBuffer[shiftHTF + k];
         
         for(int j = 0; j < InpStoch_Period; j++)
           {
            double rsi = rsiBuffer[shiftHTF + k + j];
            if(rsi < minRSI) minRSI = rsi;
            if(rsi > maxRSI) maxRSI = rsi;
           }
         
         double raw = 0.0;
         if(maxRSI - minRSI > 0) 
            raw = 100.0 * (currentRSI - minRSI) / (maxRSI - minRSI);
         sumK += raw;
        }
      ExtKBuffer[i] = sumK / InpSmoothK;
      
      // --- Calculate %D (Moving Average of %K) ---
      double sumD = 0.0;
      for(int d = 0; d < InpSmoothD; d++)
        {
         double localSumK = 0.0;
         for(int k = 0; k < InpSmoothK; k++)
           {
            double minRSI = 100.0, maxRSI = 0.0;
            double currentRSI = rsiBuffer[shiftHTF + d + k];
            
            for(int j = 0; j < InpStoch_Period; j++)
              {
               double rsi = rsiBuffer[shiftHTF + d + k + j];
               if(rsi < minRSI) minRSI = rsi;
               if(rsi > maxRSI) maxRSI = rsi;
              }
            double raw = 0.0;
            if(maxRSI - minRSI > 0) 
               raw = 100.0 * (currentRSI - minRSI) / (maxRSI - minRSI);
            localSumK += raw;
           }
         sumD += (localSumK / InpSmoothK);
        }
      ExtDBuffer[i] = sumD / InpSmoothD;
     }
     
   return(rates_total);
  }
//+------------------------------------------------------------------+

Re: Taming the Noise: My MTF Stochastic RSI Scalping Strategy (+ MT4 Code!)

Posted: Thu Jul 30, 2026 7:48 pm
by PTScalper
And finally here is version for IC trader:

Open cTrader Automate, create a new Custom Indicator, and overwrite the file with the code below.

Code: Select all

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

namespace cAlgo
{
    [Indicator(IsOverlay = false, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class MTF_StochRSI : Indicator
    {
        [Parameter("Higher Timeframe", DefaultValue = "Minute15")]
        public TimeFrame HTF { get; set; }

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

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

        [Parameter("%K Smooth", DefaultValue = 3)]
        public int KSmooth { get; set; }

        [Parameter("%D Smooth", DefaultValue = 3)]
        public int DSmooth { get; set; }

        [Output("%K", LineColor = "DodgerBlue")]
        public IndicatorDataSeries PercentK { get; set; }

        [Output("%D", LineColor = "Red")]
        public IndicatorDataSeries PercentD { get; set; }

        private Bars htfBars;
        private RelativeStrengthIndex htfRsi;

        protected override void Initialize()
        {
            // Initialize Higher Timeframe data
            htfBars = MarketData.GetBars(HTF);
            htfRsi = Indicators.RelativeStrengthIndex(htfBars.ClosePrices, RsiPeriod);
        }

        public override void Calculate(int index)
        {
            // Sync Current Timeframe with Higher Timeframe
            int htfIndex = htfBars.OpenTimes.GetIndexByTime(Bars.OpenTimes[index]);
            
            // If exact time match isn't found, find the closing previous bar
            if (htfIndex < 0)
            {
                htfIndex = ~htfIndex - 1;
            }

            // Ensure enough HTF history exists to calculate
            if (htfIndex < RsiPeriod + StochPeriod + KSmooth + DSmooth)
                return;

            // --- Calculate %K ---
            double sumK = 0;
            for (int k = 0; k < KSmooth; k++)
            {
                double minRsi = double.MaxValue;
                double maxRsi = double.MinValue;
                double currentRsi = htfRsi.Result[htfIndex - k];

                for (int j = 0; j < StochPeriod; j++)
                {
                    double rsi = htfRsi.Result[htfIndex - k - j];
                    if (rsi < minRsi) minRsi = rsi;
                    if (rsi > maxRsi) maxRsi = rsi;
                }

                double raw = (maxRsi - minRsi > 0) ? 100.0 * (currentRsi - minRsi) / (maxRsi - minRsi) : 0;
                sumK += raw;
            }
            PercentK[index] = sumK / KSmooth;

            // --- Calculate %D ---
            double sumD = 0;
            for (int d = 0; d < DSmooth; d++)
            {
                double localSumK = 0;
                for (int k = 0; k < KSmooth; k++)
                {
                    double minRsi = double.MaxValue;
                    double maxRsi = double.MinValue;
                    double currentRsi = htfRsi.Result[htfIndex - d - k];

                    for (int j = 0; j < StochPeriod; j++)
                    {
                        double rsi = htfRsi.Result[htfIndex - d - k - j];
                        if (rsi < minRsi) minRsi = rsi;
                        if (rsi > maxRsi) maxRsi = rsi;
                    }
                    double raw = (maxRsi - minRsi > 0) ? 100.0 * (currentRsi - minRsi) / (maxRsi - minRsi) : 0;
                    localSumK += raw;
                }
                sumD += (localSumK / KSmooth);
            }
            PercentD[index] = sumD / DSmooth;
        }
    }
}