Dynamic Support and Resistance That Breathes With Volatility
Building ATR-Adaptive Buy/Sell Zones in MQL5 — A Design Walkthrough
How I turned a simple idea — "draw zones at swing highs and lows, sized by volatility" — into a clean, non-cluttering indicator, and the design principles behind it.
The Idea Behind ATR Zones
Every trader has drawn a horizontal line at a swing high and called it resistance. It works until it doesn't — because a fixed line ignores the one thing that changes every single session: volatility.
ATR Zones started as a simple experiment. Instead of drawing a single line at a swing extreme, I wanted to draw a band around it. The band's thickness would scale with the ATR, so during quiet sessions the zone stays tight, and during volatile sessions it widens automatically. No manual adjustment. No redrawing every week.
The result is an indicator that plots two zones — a buy zone near the swing low and a sell zone near the swing high — with optional Fibonacci levels drawn between them and Previous Day/Week/Month high-low lines for higher-timeframe context.
This blog walks through the design decisions behind it. I will not be posting the source — the indicator is on the MQL5 Market — but the ideas here are worth sharing with anyone building similar tools.
The Core Inputs
Everything in the indicator is driven by a small set of inputs. Five of them do most of the work:
Swing Timeframe — the timeframe where swing high/low is anchored Swing Lookback — how many bars back to search for the swing ATR Timeframe — the timeframe where volatility is measured ATR Period — how many bars the ATR averages over Zone Width (ATR × N) — how thick the zone is, in ATR units Buy Retracement — how far above the swing low the buy zone sits Sell Retracement — how far below the swing high the sell zone sits
Three design choices make this flexible:
- Swing timeframe is separate from the chart timeframe. You can attach the indicator to an M5 chart but anchor the zones to an M30 swing high — so intraday noise does not redraw your levels.
- ATR timeframe is also independent. Zone thickness can be measured on a different timeframe than the chart or the swing anchor.
- Zone position is a fraction of the swing range, not a fixed price offset. So as the range grows, the zone stays proportionally placed.
How the Zones Are Calculated
The calculation follows four conceptual steps:
Step 1 — Find the highest high and lowest low on the anchor timeframe,
over the configured lookback window.
Step 2 — Measure the current ATR on the ATR timeframe.
Step 3 — Convert the ATR into a half-width: half = (ATR × Width) / 2.
Step 4 — Place the buy zone center a small fraction above the swing low,
and the sell zone center a small fraction below the swing high.
The half-width extends each zone symmetrically around its center.
That is the entire logic. No moving averages, no regressions, no curve fitting. Just a swing anchor, a fraction, and an ATR-scaled band.
The important insight here is that ATR is a better scaling unit than points or pips. A 20-pip zone on EURUSD is meaningful; a 20-pip zone on Gold is invisible. ATR-relative sizing works on every symbol without parameter tuning.
Preventing Zone Overlap
On small ranges with large ATR, the two zones can collide. I added a small guard that clamps both zones to the midpoint between their centers if they would otherwise overlap.
If the top of the buy zone is above the bottom of the sell zone:
mid = (buy center + sell center) / 2
set buy top = mid
set sell bottom = mid
It is a small touch, but it prevents the two rectangles from ever intersecting — which was one of the first complaints I got when testing early versions. In practice the guard rarely fires, but when it does, the chart stays readable instead of turning into a single blurred block.
How the Zones Are Drawn Cleanly
Each zone is built from three chart objects: a filled rectangle and two boundary lines. The rectangle visually shows the zone; the lines extend slightly past the label so the label text does not sit on top of the chart's right edge.
Three implementation principles I follow for every drawing routine:
1. Reuse existing objects. Before creating anything, check whether the object already exists. If it does, move it. If it does not, create it once. 2. Disable selection. Every object is marked non-selectable so you cannot accidentally drag a zone while panning the chart. 3. Use the hidden flag. Objects are hidden from the terminal's object list but still visible on the chart. This keeps the object list clean without sacrificing visibility.
The first point matters more than it sounds. Early versions of the indicator created and deleted objects on every update — the chart flickered visibly and the object list grew and shrank constantly. Switching to a find-then-move pattern eliminated both problems entirely. The object count stays fixed; only the coordinates change.
Adding Fibonacci Levels Between the Swing Points
The indicator draws Fibonacci retracement levels between the swing low and swing high. Rather than hardcoding the levels, the indicator accepts them as a comma-separated string — for example, "0.236,0.382,0.5,0.618,0.786" for the standard set, or "0.25,0.5,0.75" for a simpler view.
On initialization:
Split the input string by commas.
For each part:
Trim surrounding spaces.
Convert to a number.
If the value is between 0 and 1, keep it.
Otherwise, skip it.
Store the parsed levels in an array for use during drawing.
Parsing happens once at startup, not on every tick. This is a small pattern that pays off in indicators with lots of user-configurable lists — you pay the parsing cost exactly once and then use the array for the life of the chart.
Previous Day / Week / Month Levels
The indicator also draws PDH/PDL, PWH/PWL, and PMH/PML lines. These are simple trendline objects placed from the previous bar's time to the current chart's right edge.
For each enabled period (day, week, month):
Read the previous bar's high, low, and time.
Draw a horizontal trendline from that time to the right edge.
Draw a text label at the right edge showing the abbreviation.
Use a common object prefix (e.g. "DWM_") for all of them
so cleanup on removal is a single call.
Using a shared prefix for a family of objects is a habit worth building. It means the entire group can be deleted in one line during deinitialization, without tracking each object individually.
Redrawing Only When Needed
One of the design goals was to not burn CPU redrawing on every tick. The indicator redraws only when a new bar appears on the chart.
On first calculation:
Run the full update.
Set a "has drawn" flag so we do not run again immediately.
On every subsequent calculation:
Read the current bar's open time.
If it differs from the last seen bar time:
Run the update.
Store the new bar time.
The bar-time comparison is the key. On a quiet market with no new bar, the indicator does literally nothing — no ATR copy, no swing search, no object movement. On a new bar, it runs the full update once.
There is a subtle lesson here: bar time is more reliable than bar index. Bar indices shift when the terminal drops the oldest bars from history; bar times do not. Any time you need to know "has a new bar appeared since I last checked," compare times, not indices.
What the Indicator Does Not Do
To be clear about what this is:
- It does not fire buy or sell signals.
- It does not predict price direction.
- It does not use any repainting logic — every value comes from closed bars on the anchor timeframe.
It is a context tool. The zones mark areas where a reversal or continuation is more likely to be decided. How you trade those areas is entirely up to you.
What I Learned Building It
- Separating the anchor timeframe from the chart timeframe is powerful. Most swing-based indicators force you to change the chart timeframe to change the anchor. Decoupling them is a small change with a big usability gain.
- Object reuse matters more than I expected. Flicker is not a cosmetic problem — it is a signal that your update loop is doing more work than it needs to.
- ATR is a better scaling unit than points or pips. One set of parameters works across every symbol without retuning.
- Less is more. My first drafts had five or six zones. It looked impressive in a screenshot and was unusable in practice. Two zones — one buy, one sell — is what actually gets read at a glance.
Wrapping Up
ATR Zones is a small indicator with a clear purpose: mark the two price areas where a swing decision is most likely, and scale them with volatility so they stay relevant across sessions and symbols.
The full version is available on the MQL5 Market — the link is in my profile. If you find any of the design principles here useful — timeframe decoupling, object reuse, ATR-relative sizing, or bar-time caching — feel free to apply them in your own indicators. Those patterns are not unique to this tool; they are worth knowing regardless of what you build.
Trade safely.
— Jose Rodrigues Chaves
About the author: Independent MQL5 developer. Interested in clean indicator design and volatility-based context tools. All opinions are my own.



