IC Markets

🛡️ Stop Trading in "Dead" Markets: The Volatility Squeeze Filter

Discuss 1-minute to 15-minute price action setups, fading intraday momentum, key support/resistance zones, and proven short-term trading methodologies.
Post Reply
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

🛡️ Stop Trading in "Dead" Markets: The Volatility Squeeze Filter

Post by PTScalper »

Hi traders, scalpers,

One of the biggest "scalper killers" isn't a bad entry—it's trading in a flat market.

When the market has no volume and no volatility, price just "choops" sideways. You get hit by the spread, your stops get hunted by noise, and you lose money even if your direction is technically correct.

To solve this, I’ve created the Volatility Squeeze indicator for MT4.

How it works: The indicator compares Bollinger Bands with Keltner Channels.

The Squeeze (Orange Dots): When the Bollinger Bands "contract" inside the Keltner Channels, it means the market is "coiling" like a spring. This is a warning that a massive move is coming soon.
The Expansion (Green Dots): When the bands "break out" of the Keltner Channel, it means the volatility is exploding.

The Scalping Strategy:

The Filter: Don't enter a trade unless the Squeeze has just occurred. You are looking for that "coiled" energy.
The Entry: Once the "Squeeze" is released and the price breaks a local high/low with a momentum surge, that is your high-probability scalping entry.

By using this filter, you stop taking "random" trades and only take trades when the market has the "fuel" to move.

Download the code below and let me know on the charts: How many "fake-outs" did this filter help you avoid this week?
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: 🛡️ Stop Trading in "Dead" Markets: The Volatility Squeeze Filter

Post by PTScalper »

The MQL4 Code (MT4)

This code identifies the Bollinger Band Squeeze. When the market is "squeezed," the chart will show a specific signal (or the band colors will change), telling the scalper that a big move is coming.

Code: Select all

//+------------------------------------------------------------------+
//|                                            VolatilitySqueeze.mq4|
//|                                          Copyright 2024, Forex_Scalp|
//|                                             https://forex-scalping.com|
//+------------------------------------------------------------------+
#property copyright "Forex-Scalping.com"
#property link      "https://forex-scalping.com"
#property strict

#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1  clrOrange
#property indicator_color2  clrLime

//--- Inputs
input int      BB_Period      = 20;       // Bollinger Band Period
input double   BB_Dev         = 2.0;      // Bollinger Band Deviation
input int      Kelt_Period    = 20;       // Keltner Channel Period
input double   Kelt_Mult      = 1.5;      // Keltner Multiplier (Squeeze Filter)

//--- Buffers
double Squeeze_Buffer[];
double Expansion_Buffer[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   SetIndexBuffer(0, Squeeze_Buffer);
   SetIndexBuffer(1, Expansion_Buffer);
   
   SetIndexStyle(0, DRAW_ARROW);
   SetIndexArrow(0, 159); // A small dot/circle for squeeze
   
   SetIndexStyle(1, DRAW_ARROW);
   SetIndexArrow(1, 159); // A signal for expansion
   
   IndicatorSetString(INDICATOR_SHORTNAME, "Vol_Squeeze_Scalp");
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator iteration                                        |
//+------------------------------------------------------------------+
int OnCalculate(const int counts)
{
   int limit = Bars - prev_calculated;
   
   for(int i = 0; i < counts; i++)
   {
      double bb_upper = iBands(NULL, 0, BB_Period, BB_Dev, 0, MODE_UPPER, i);
      double bb_lower = iBands(NULL, 0, BB_Period, BB_Dev, 0, MODE_LOWER, i);
      
      // Keltner Calculation (Simplified for MQL4)
      double ma = iMA(NULL, 0, Kelt_Period, 0, MODE_SMA, i);
      double atr = iATR(NULL, 0, Kelt_Period, i);
      double kc_upper = ma + (atr * Kelt_Mult);
      double kc_lower = ma - (atr * Kelt_Mult);
      
      // Squeeze Logic: If Bollinger Bands are inside Keltner Channels
      if(bb_upper < kc_upper && bb_lower > kc_lower)
      {
         Squeeze_Buffer[i] = High[i];     // Highlight Squeeze
         Expansion_Buffer[i] = 0;
      }
      else
      {
         Squeeze_Buffer[i] = 0;
         Expansion_Buffer[i] = High[i];   // Highlight Expansion
      }
   }
   return(counts);
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: 🛡️ Stop Trading in "Dead" Markets: The Volatility Squeeze Filter

Post by PTScalper »

To provide the MT5 (MQL5) version of the Volatility Squeeze, we need to transition to the handle-based system that MQL5 uses. This is more robust and efficient for multi-core processing, which is a key feature of MetaTrader 5.

In this version, we check if the Bollinger Bands are "inside" the Keltner Channels. When they are, the market is "squeezed" (low volatility); when they break out, the market is "expanding" (high volatility).

The MQL5 Code

Save this as VolatilitySqueeze_MT5.mq5 in your MQL5\Indicators\Custom\ folder.

Code: Select all

//+------------------------------------------------------------------+
//|                                          VolatilitySqueeze.mq5  |
//|                                          Copyright 2024, Forex_Scalp|
//|                                             https://forex-scalping.com|
//+------------------------------------------------------------------+
#property copyright "Forex-Scalping.com"
#property link      "https://forex-scalping.com"
#property version   "1.00"
#property indicator_chart_window

//--- Indicator Buffers
#property indicator_buffers 2
#property indicator_plots   2

#property indicator_label1  "Squeeze Signal"
#property indicator_type1   DRAW_ARROW
#property indicator_color1  clrOrange
#property indicator_width1  2

#property indicator_label2  "Expansion Signal"
#property indicator_type2   DRAW_ARROW
#property indicator_color2  clrLime
#property indicator_width2  2

//--- Input Parameters
input int      InpBB_Period    = 20;       // Bollinger Band Period
input double   InpBB_Dev       = 2.0;      // Bollinger Band Dev
input int      InpKelt_Period  = 20;       // Keltner Period
input double   InpKelt_Mult    = 1.5;      // Keltner Multiplier (Squeeze Filter)

//--- Global Variables
int            handle_bb;
int            handle_atr;
double         BufferSqueeze[];
double         BufferExpansion[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   // Set Buffers
   SetIndexBuffer(0, BufferSqueeze, INDICATOR_DATA);
   SetIndexBuffer(1, BufferExpansion, INDICATOR_DATA);
   
   // Set Arrow Codes (Small dots/arrows above/below high)
   PlotIndexSetInteger(0, PLOT_ARROW, 159); 
   PlotIndexSetInteger(1, PLOT_ARROW, 159);

   // Initialize Handles
   handle_bb = iBands(_Symbol, _Period, InpBB_Period, 0, false, InpBB_Dev);
   handle_atr = iATR(_Symbol, _Period, InpKelt_Period);

   if(handle_bb == INVALID_HANDLE || handle_atr == INVALID_HANDLE)
   {
      Print("Failed to create handles. Check settings.");
      return(INIT_FAILED);
    }

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator iteration                                        |
//+------------------------------------------------------------------+
int OnCalculate(const int bars_total,
                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 = bars_total - prev_calculated;
   
   // Arrays to hold indicator data
   double bb_upper[], bb_lower[], atr_values[];
   
   // Copy data from handles
   if(CopyBuffer(handle_bb, 1, 0, bars_total, bb_upper) <= 0) return(0); // Upper Band
   if(CopyBuffer(handle_bb, 2, 0, bars_total, bb_lower) <= 0) return(0); // Lower Band
   if(CopyBuffer(handle_atr, 0, 0, bars_total, atr_values) <= 0) return(0);

   for(int i = 0; i < bars_total; i++)
   {
      // Logic: Keltner Channel is roughly (MA + ATR * Multiplier)
      // In MT5, we calculate the Keltner threshold based on the ATR.
      double ma_val = iMA_Simple_Buffer(i); // Example logic for Keltner
      
      // Simpler check: If the Bollinger Band Range is narrower than the Keltner calculation
      // If BB is inside Keltner, it's a "Squeeze"
      double current_bb_range = bb_upper[i] - bb_lower[i];
      double kelt_range = atr_values[i] * (InpKelt_Mult * 2); // Scaled multiplier

      if(current_bb_range < kelt_range)
      {
         BufferSqueeze[i] = high[i];
         BufferExpansion[i] = 0.0;
      }
      else
      {
         BufferSqueeze[i] = 0.0;
         BufferExpansion[i] = high[i];
      }
   }

   return(bars_total);
}

// Helper for Moving Average (Standard calculation)
double iMA_Simple_Buffer(int index) {
   // Logic to pull a standard MA for the Keltner base
   return 0.0; // Simplified for this example; usually, you'd use a handle
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: 🛡️ Stop Trading in "Dead" Markets: The Volatility Squeeze Filter

Post by PTScalper »

In cTrader, we can write this very efficiently. I have designed it to show "Squeeze" points and "Expansion" points clearly on your chart.

Part 1: The cTrader Code (C#)

Create a new indicator in cTrader (e.g., VolatilitySqueeze.cs) and replace the code:

Code: Select all

using System;
using System.Linq;
using cAlgo.Engine;
using cAlgo.Indicators;

namespace cAlgo.Indicators
{
    [AllowNone]
    [Indicator(Brushes_Red, Resolution.Any, AnalysisMode.None)]
    public class VolatilitySqueeze : Indicator
    {
        // --- Parameters ---
        [Parameter("BB Period", DefaultValue = 20, Group = "Bollinger Bands")]
        public int BBPeriod { get; set; }

        [Parameter("BB Deviation", DefaultValue = 2.0, Group = "Bollinger Bands")]
        public double BBDeviation { get; set; }

        [Parameter("Keltner Period", DefaultValue = 20, Group = "Keltner Channels")]
        public int KeltnerPeriod { get; set; }

        [Parameter("Keltner Multiplier", DefaultValue = 1.5, Group = "Keltner Channels")]
        public double KeltnerMultiplier { get; set; }

        // --- Outputs ---
        [Output("Squeeze Signal", LineColor.Orange, Thickness = 3)]
        public DataSeries SqueezeBuffer { get; set; }

        [Output("Expansion Signal", LineColor.Lime, Thickness = 3)]
        public DataSeries ExpansionBuffer { get; set; }

        // --- Internal Indicators ---
        private Bollinger_Average _bb;
        private AverageTrueRange _atr;

        public VolatilitySqueeze()
        {
            Title = "Volatility Squeeze (cTrader Edition)";
        }

        protected override void Initialize()
        {
            // Initialize internal indicators
            _bb = Indicators.Bollinger_Average(BBPeriod, BBDeviation);
            _atr = Indicators.AverageTrueRange(KeltnerPeriod, MovingAverageType.Simple);
        }

        public override void Calculate(int index)
        {
            // Calculate Keltner Channel bounds
            // Keltner = Moving Average +/- (ATR * Multiplier)
            double ma = (_bb.Main_Average.GetValue(index)); 
            double currentAtr = _atr.AverageTrueRange.GetValue(index);
            
            double kcUpper = ma + (currentAtr * KeltnerMultiplier);
            double kcLower = ma - (currentAtr * KeltnerMultiplier);

            // Get Bollinger Band values
            double bbUpper = _bb.Main_Upper.GetValue(index);
            double bbLower = _bb.Main_Lower.GetValue(index);

            // Squeeze Logic: 
            // If the Bollinger Band range is smaller than the Keltner range, the market is "squeezed".
            if (bbUpper < kcUpper && bbLower > kcLower)
            {
                SqueezeBuffer[index] = High[index];
                ExpansionBuffer[index] = double.NaN;
            }
            else
            {
                SqueezeBuffer[index] = double.NaN;
                ExpansionBuffer[index] = High[index];
            }
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: 🛡️ Stop Trading in "Dead" Markets: The Volatility Squeeze Filter

Post by PTScalper »

In Pine Script, we use the Bollinger Bands and Keltner Channels. When the Bollinger Bands (fast-moving) go inside the Keltner Channels (slower-moving), the market is "squeezed." This tells a scalper: "Stop trading now; the market is waiting for a big move."

Part 1: The Pine Script Code (v5)

Copy and paste this into the TradingView Pine Editor.

Code: Select all

//@version=5
indicator("Volatility Squeeze (Scalp-Master)", overlay=true, precision=2)

// --- Inputs ---
// Bollinger Bands Settings
bb_length = input.int(20, "BB Length", minval=1, group="Bollinger Bands")
bb_mult   = input.float(2.0, "BB StdDev", minval=0.1, step=0.1, group="Bollinger Bands")

// Keltner Channels Settings
kc_length = input.int(20, "Keltner Length", minval=1, group="Keltner Channels")
kc_mult   = input.float(1.5, "Keltner Multiplier", minval=0.1, step=0.1, group="Keltner Channels")

// --- Calculations ---
// Calculate Bollinger Bands
[bb_mid, bb_upper, bb_lower] = ta.bb(close, bb_length, bb_mult)

// Calculate Keltner Channels
// Keltner is essentially a Moving Average +/- (ATR * Multiplier)
ma_base = ta.sma(close, kc_length)
atr_val = ta.atr(kc_length)
kc_upper = ma_base + (atr_val * kc_mult)
kc_lower = ma_base - (atr_val * kc_mult)

// --- Squeeze Logic ---
// A "Squeeze" happens when the BB is inside the KC.
is_squeezed = bb_upper < kc_upper and bb_lower > kc_lower

// --- Visuals ---
// Background color: Red/Orange when squeezed (Preparation), Green when expanding (Action)
bg_color = is_squeezed ? color.new(color.orange, 85) : color.new(color.lime, 85)
bgcolor(bg_color, title="Squeeze Zone")

// Plot Shapes for the Scalper
// A "Diamond" or "Dot" appears when the squeeze is active
plotshape(is_squeezed, title="Squeeze Active", style=shape.diamond, location=location.bottom, color=color.orange, size=size.small)

// Plot the Bands (Optional, but helpful for visual confirmation)
plot(bb_upper, "BB Upper", color=color.new(color.blue, 80))
plot(bb_lower, "BB Lower", color=color.new(color.blue, 80))
plot(kc_upper, "KC Upper", color=color.new(color.gray, 80))
plot(kc_lower, "KC Lower", color=color.new(color.gray, 80))

// --- Alerts ---
alertcondition(is_squeezed, title="Squeeze Started", message="Market is squeezing - prepare for volatility!")
alertcondition(not is_squeezed and not (not is_squeezed[1]), title="Squeeze Released", message="Squeeze released - trade the breakout!")
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply