🚀 AI One-Click Generation of mql4 Strategy Code

How to Code a Volatility Filter in Mql4 with the iStdDev Indicator

mql4 | Published on: 2025/12/12
The following content is generated by EasyQuant .

Problem Description

How do I make my trading robot only trade when the market is volatile and avoid flat periods?

Solution

To make your trading robot avoid flat (ranging) markets and trade only during volatile periods, the most robust method is to implement a Volatility Filter.

The most common and effective indicators for this in MQL4 are:

  1. ATR (Average True Range): Measures the size of price movement. If ATR is low, the market is flat.
  2. ADX (Average Directional Movement Index): Measures trend strength. If ADX is below 20-25, the market is usually directionless.
  3. Bollinger Band Width: Measures the distance between bands. A "squeeze" indicates low volatility.

Below is a complete, functional MQL4 Expert Advisor template. It uses ATR to detect price range expansion and ADX to ensure there is enough directional momentum before allowing a trade.

Volatility Filter EA Template

//+------------------------------------------------------------------+
//|                                        Volatility_Filter_EA.mq4  |
//|                        Copyright 2023, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

//--- INPUT PARAMETERS ---
input string   Filter_Settings   = "--- Volatility Settings ---";
input bool     Use_ATR_Filter    = true;        // Enable ATR Filter
input int      ATR_Period        = 14;          // ATR Period
input double   Min_ATR_Level     = 0.0010;      // Minimum ATR Value (e.g. 10 pips on EURUSD)

input bool     Use_ADX_Filter    = true;        // Enable ADX Filter
input int      ADX_Period        = 14;          // ADX Period
input double   Min_ADX_Level     = 25.0;        // Minimum ADX Level (Trend Strength)

input string   Trade_Settings    = "--- Trading Settings ---";
input double   LotSize           = 0.1;
input int      MagicNumber       = 123456;
input int      Slippage          = 3;

//--- GLOBAL VARIABLES ---
int Ticket = 0;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   // 1. Check if we have open orders to avoid opening multiple trades in this example
   if(OrdersTotal() > 0) return;

   // 2. CHECK VOLATILITY CONDITIONS
   if(!IsMarketVolatile()) 
   {
      // Optional: Print comment to chart to see why it's not trading
      Comment("Market is Flat. Trading Paused.");
      return; 
   }
   
   Comment("Market is Volatile. Searching for signals...");

   // 3. YOUR STRATEGY LOGIC HERE (Example: Simple MA Crossover)
   // This is just a placeholder strategy to demonstrate the filter
   double maFast = iMA(NULL, 0, 10, 0, MODE_SMA, PRICE_CLOSE, 1);
   double maSlow = iMA(NULL, 0, 50, 0, MODE_SMA, PRICE_CLOSE, 1);
   double maFastPrev = iMA(NULL, 0, 10, 0, MODE_SMA, PRICE_CLOSE, 2);
   double maSlowPrev = iMA(NULL, 0, 50, 0, MODE_SMA, PRICE_CLOSE, 2);

   // Buy Signal
   if(maFast > maSlow && maFastPrev <= maSlowPrev)
   {
      Ticket = OrderSend(Symbol(), OP_BUY, LotSize, Ask, Slippage, 0, 0, "Volatile Buy", MagicNumber, 0, clrGreen);
   }
   
   // Sell Signal
   if(maFast < maSlow && maFastPrev >= maSlowPrev)
   {
      Ticket = OrderSend(Symbol(), OP_SELL, LotSize, Bid, Slippage, 0, 0, "Volatile Sell", MagicNumber, 0, clrRed);
   }
}

//+------------------------------------------------------------------+
//| Function to check if market meets volatility requirements        |
//+------------------------------------------------------------------+
bool IsMarketVolatile()
{
   // --- Check ATR Filter ---
   if(Use_ATR_Filter)
   {
      // Get current ATR value
      double currentATR = iATR(NULL, 0, ATR_Period, 1);
      
      // If current volatility is less than the threshold, return false
      if(currentATR < Min_ATR_Level) return(false);
   }

   // --- Check ADX Filter ---
   if(Use_ADX_Filter)
   {
      // Get current ADX Main line value
      double currentADX = iADX(NULL, 0, ADX_Period, PRICE_CLOSE, MODE_MAIN, 1);
      
      // If trend strength is weak (below threshold), return false
      if(currentADX < Min_ADX_Level) return(false);
   }

   // If we passed all enabled checks, the market is volatile
   return(true);
}
//+------------------------------------------------------------------+

How to Configure the Filters

1. The ATR Filter (Range Filter)

The ATR measures the average size of the candles.

  • Logic: iATR(...) < Min_ATR_Level
  • Configuration: You need to set Min_ATR_Level based on the timeframe and symbol you are trading.
    • Example: On EURUSD H1, an ATR of 0.0005 (5 pips) is very flat. An ATR of 0.0020 (20 pips) is volatile.
    • How to find the number: Attach the standard ATR indicator to your chart. Look at the values during the night (flat) vs. the day (volatile). Set your input slightly above the "night" value.

2. The ADX Filter (Trend Filter)

The ADX measures the strength of the trend, regardless of direction.

  • Logic: iADX(...) < Min_ADX_Level
  • Configuration:
    • < 20: The market is ranging/flat.
    • > 25: A trend is forming.
    • > 40: Strong trend.
    • Recommendation: Set Min_ADX_Level to 20 or 25. This effectively blocks trades when the market is chopping sideways.

Integration into Your Existing Robot

If you already have a robot, copy the IsMarketVolatile() function from the code above to the bottom of your file. Then, inside your OnTick() function, wrap your trade logic like this:

void OnTick()
{
   // ... existing code ...

   // Only proceed to trade logic if volatility is high enough
   if(IsMarketVolatile() == false) return;

   // ... your entry logic (OrderSend) ...
}

Q&A: Volatility Filtering

Q: Why does my robot still trade in flat markets even with ADX?
A: ADX is a lagging indicator. If a sudden spike occurs, ADX might still be rising from a low number. Combining ADX with ATR helps because ATR reacts faster to immediate price expansion.

Q: Can I use time filters instead?
A: Yes. Often, "flat" markets are simply specific times of day (e.g., Asian Session for GBPUSD). You can add a simple time check: if(Hour() < 8 || Hour() > 20) return;. However, technical indicators (ATR/ADX) are safer because they handle holidays or unusual calm periods during active hours.

Q: How do I handle different currency pairs with ATR?
A: ATR is an absolute value. 0.0010 on EURUSD is different from 0.50 on Gold (XAUUSD). If you trade multiple pairs, you must adjust the Min_ATR_Level input for each specific chart.