Most traders treat volatility like weather — they complain about it but never check the forecast. You'll slap an ATR stop on a trade, maybe size position by ATR, and then wonder why the same setup that worked last week gets shredded this week. The answer isn't a better entry. It's knowing what state the market is in before you even look for a setup.
That's where a volatility regime indicator comes in. Instead of just plotting ATR as a line that wiggles around, you classify the current market state — low, normal, or high volatility — and color the chart accordingly. When you can see at a glance that we're in a compressed, low-volatility regime, you stop forcing breakout trades that never fire. When the indicator flips to high volatility, you tighten your stops and cut position size before the whipsaw eats you alive.
In this post, I'll walk you through building a custom ATR volatility regime indicator MQL5 from scratch. You'll get the full source logic, the parameter choices that actually matter, and the honest limitations of this approach. This isn't a copy-paste-and-pray job — you'll understand every line so you can adapt it to your own trading style.
Why a Volatility Regime Indicator Matters More Than You Think
Here's a scenario you've lived through. Price breaks a resistance level, momentum looks great, you enter long with a standard 2% risk. Then volatility expands violently and price reverses 80 pips in an hour, stopping you out. Was your setup wrong? Maybe. But more likely, you entered during a volatility expansion phase where the ATR was already double its 20-day average. Your stop was placed based on normal conditions, but the market wasn't in a normal state.
Regime detection solves this by giving you a context layer on top of your price action. It's not a standalone signal — it's a filter. Here's how I use it in practice:
- Low volatility regime: Range-bound strategies, mean reversion, and straddle-style setups work. Breakouts are suspect until ATR starts expanding.
- Normal volatility regime: Your standard trend-following and pullback setups. This is where most of your A-B testing and optimization data is probably from.
- High volatility regime: Trend continuation can be powerful, but spreads widen, stops get hit more easily, and position sizing must shrink. News events and earnings typically spike this state.
Most traders overlook the transition between regimes. The most profitable window is often the expansion from low to normal — that's when breakouts finally work. A good regime indicator doesn't just tell you where you are; it should help you spot that transition.
Think about how this changes your decision-making. If you're a mean-reversion trader and the indicator shows high volatility, you know that fading the move is dangerous — those are the days when trends extend far beyond what "overbought" suggests. Conversely, if you're a breakout trader and the indicator shows low volatility, you can either wait for the expansion signal or reduce your breakout size knowing that false breakouts are more common in compressed markets.
The Problem with Fixed ATR Thresholds
Before we jump into the percentile approach, let me explain why the naive method fails. A lot of traders try to define regimes with fixed values: "If ATR is above 50 pips, it's high volatility." That works for exactly one instrument on one timeframe, and only until market conditions change structurally.
Consider EURUSD on H1. A 25-pip ATR is completely normal. But on gold (XAUUSD) H1, 25 pips is a ghost of a move — you'd be in "low volatility" forever and never trade. Now flip to the daily chart of a calm FX pair like EURCHF. A 40-pip ATR there is a screaming high-volatility event. Fixed thresholds can't adapt. They're brittle, and they break the moment you switch symbols or timeframes.
There's also the structural drift problem. Volatility itself changes over months and years. The average ATR on EURUSD in 2022 was much higher than in 2019. If you hard-code a threshold based on last year's data, you're trading with outdated assumptions. A relative measure — comparing current ATR to its own recent history — sidesteps all of this.
I've seen traders try to work around this with a "volatility ratio" — dividing current ATR by the average ATR over a longer period. That's a step in the right direction, but it still has issues. The ratio can be skewed by a single massive spike, and it doesn't tell you where you sit in the distribution. If the ratio is 1.2, is that high or normal? Without a distributional context, you're guessing.
Building the ATR Volatility Regime Indicator MQL5
The core idea is simple: compare the current ATR value to its own historical range. We're not looking at ATR in isolation — we're looking at where today's ATR sits relative to the last N periods. This relative measure is what makes it a regime indicator rather than just another volatility line.
The Logic: Percentile-Based Classification
There are a few ways to classify regimes. You could use fixed thresholds (ATR above X means high volatility), but that breaks down across different instruments and timeframes as I just described. A 20-pip ATR is high for EURUSD on M15 but trivial on daily gold. Fixed thresholds are brittle.
A better approach is percentile ranking. You look at the last N ATR values, find where the current value falls in that distribution, and classify accordingly. This normalizes across instruments and timeframes automatically. Here's the breakdown:
| Regime | Percentile Range | Typical Market Behavior | Trading Bias |
|---|---|---|---|
| Low | 0 – 25th | Tight ranges, compression, low momentum | Mean reversion, prepare for breakout |
| Normal | 25th – 75th | Balanced volatility, typical conditions | Standard setups, normal risk |
| High | 75th – 100th | Expansion, news spikes, panic moves | Reduce size, widen stops or stand aside |
The 25th and 75th percentiles as boundaries are a reasonable starting point, but I'll show you how to make them adjustable. Some traders prefer a tighter "normal" band (20th–80th) to catch more extremes. That's a parameter you'll want to tune per instrument.
One thing I want to emphasize: the percentile calculation itself needs to be done carefully. You're not just sorting an array once — you're doing it on every bar, which means the computational cost matters, especially if you're running this on a low timeframe with a long lookback. In MQL5, you have access to fast sorting algorithms, but you should still be mindful of how often you're recalculating.
Why Percentile Ranking Beats Standard Deviation
You might be wondering why not just use a z-score — how many standard deviations the current ATR is from its mean. That's a valid alternative, and I've seen it used. But percentile ranking has one practical advantage: it's bounded. A percentile is always between 0 and 100, so your classification thresholds are always meaningful. A z-score of 2.5 means something different on a 50-bar sample than on a 500-bar sample because the distribution shape changes. Percentiles don't have that problem.
There's also a robustness argument. ATR distributions are not normal — they're skewed right, with occasional massive spikes during news events. Standard deviation is sensitive to those outliers. A single fat-tail event can pull the mean up so much that everything else looks "low volatility" for weeks afterward. Percentile ranking is more resistant to this because it's based on ordering, not on the actual values. The 90th percentile is still the 90th percentile whether the biggest value is 1.5x or 5x the median.
Let me give you a concrete example. Say you have 100 ATR values, and 99 of them are between 10 and 20 pips. One day, a news event pushes ATR to 80 pips. With standard deviation, that single value inflates the mean and the standard deviation significantly. A current ATR of 18 pips might now look "normal" when it's actually in the 98th percentile of typical conditions. With percentile ranking, 18 pips would rank at the 98th or 99th percentile and correctly flag as high volatility. That's a meaningful difference in how you'd approach the market.
Visual Design: Color Zones and Alerts
A volatility regime indicator that just prints "HIGH" in a corner is useless. You need to see the state change at a glance. Here's what the visual layer should include:
- Background tint: A subtle color wash across the chart — light blue for low, transparent for normal, light red for high. This is the quick-glance layer.
- ATR line colored by regime: The ATR value line itself changes color based on the current state, so you can see the historical transitions.
- Threshold bands: Horizontal lines showing the 25th and 75th percentile ATR values from the lookback window. These give context for how far current volatility is from the boundaries.
- Alert triggers: A push notification and on-chart alert when the regime changes, not on every bar. You don't need to be pinged 50 times a day.
This design gives you both a real-time status and historical context. When you see the ATR line cross from the blue zone into the neutral zone, you know the expansion phase is starting.
I'd also recommend adding a small text label in the top-left corner of the chart showing the current regime and the exact percentile value. Sometimes you want to know not just "high" but "87th percentile" — that extra precision helps when you're deciding whether to reduce size by 20% or 50%.
Practical MQL5 Implementation
Now let's get into the code. This is a custom indicator using the OnCalculate function, which means it runs on every tick and bar close. I'm using MQL5 syntax, but the logic translates directly to MQL4 if you're still on the old platform.
Input Parameters
Here are the key inputs you'll want to expose. The defaults work well for H1 on major forex pairs, but you'll need to adjust for your timeframe and instrument.
| Parameter | Type | Default | Description |
|---|---|---|---|
| ATR Period | int | 14 | Standard ATR lookback period. 14 is the default for most platforms. |
| Regime Lookback | int | 100 | Number of past ATR values used for percentile ranking. Longer = smoother but slower to react. |
| Low Percentile |






