Library/Technical Indicators/Volatility and Channels

D07-F04-A05 / Complete engineering topic

Donchian Channels

A production-minded guide to Donchian Channels.

D07 · TECHNICAL INDICATORS
D07-F04-A05Canonical / Tested / Open
D07 / D07-F04

Which high and low own the boundaries of the current rolling window? That is the practical question behind Donchian Channels. This tutorial builds the answer from finalized data, shows the exact arithmetic, and gives you a laboratory for breaking the indicator safely.

The mental model in one minute

The channel is the trailing highest high and lowest low, including the current row. Its middle is the midrange of those two extrema—not an average price.

The output is a measurement. It is not an order, forecast, confidence interval, or promise that a familiar-looking market pattern will repeat.

Contract before chart

  • Variant: The calculation window includes the current bar. A causal breakout detector often compares the current price with the previous bar's completed channel instead.
  • Defaults: period=20.
  • First publication: index 19 after the first complete 20-bar window.
  • Data: finalized, causal observations on one price basis.
  • Missing, malformed, or mixed-basis input: reject rather than repair silently.

Formula and symbols

Uppert=max(H)n,t, Lowert=min(L)n,t, Middlet=(Uppert+Lowert)/2Upper_t=\max(H)_{n,t},\ Lower_t=\min(L)_{n,t},\ Middle_t=(Upper_t+Lower_t)/2

SymbolMeaningUnit
HiH_ihigh inside the active windowprice units
LiL_ilow inside the active windowprice units
UtU_thighest high in the windowprice units
DtD_tlowest low in the windowprice units
MtM_tarithmetic midpoint of U and Dprice units

Calculate it by hand

For a three-row window, highs are [12, 15, 14] and lows are [9, 11, 10].

  1. Upper channel: max(12, 15, 14) = 15.
  2. Lower channel: min(9, 11, 10) = 9.
  3. Middle: (15 + 9) / 2 = 12.
  4. Width: 15 - 9 = 6.

If the next row arrives, the oldest high 12 and low 9 leave the window. The lower boundary changes if no tied 9 remains. The middle moves even if the upper boundary stays fixed.

That calculation is deliberately small enough to audit without code. The 240-row fixtures then show how the same rule behaves through warm-up, regime changes, ties, gaps, shocks, and expiry.

The complete algorithm

  1. Take the trailing n highs and lows, including the current row.
  2. Publish their maximum high and minimum low.
  3. Compute the midrange (upper + lower) / 2 and width upper - lower.
  4. On tied extrema, the value persists until the final tied owner expires.
  5. For a breakout detector, compare the current observation with the previous completed channel; do not silently change the channel formula.
Rendering system map…

The flow exposes the point where a result becomes unavailable, where recursive state begins, or where a rolling owner expires.

See the mechanism

Mechanism map for Donchian Channels

The map connects formula terms to the exact data they consume.

Follow a complete causal trace

Canonical 240-row trace for Donchian Channels

Only rows at or before the active observation may influence the displayed state. Warm-up is shown as unavailable rather than silently converted to zero.

Memory is part of the definition

Finite n-row ownership. A new extreme updates immediately; an old boundary changes only when its final owning observation expires.

The current high is always less than or equal to the current-inclusive upper channel. Therefore current high > current upper is impossible by construction.

Memory and family comparison for Donchian Channels

This is why two indicators can react differently to the same shock even when their plotted bands appear similar.

Six ways to try to break it

ScenarioTeaching jobExpected lesson
breakout-ladderFresh-extreme ladderNew highs and lows update only the affected boundary.
rolling-expiryRolling-window expiryA boundary changes when its owning extreme leaves the window.
tie-boundaryTied-extreme boundaryThe value survives until the last tied extreme expires.
flat-channelFlat channelUpper, lower, and middle coincide with zero width.
current-versus-priorCurrent-window versus prior-window boundaryThe current high can equal the current-inclusive upper channel; breakout logic must compare with a prior completed channel.
responsive-tenResponsive ten-bar channelA shorter window accepts and expires extrema faster.

Boundary atlas for Donchian Channels

Predict the active component before opening the lab. Then jump to the relevant checkpoint and inspect the formula trace rather than guessing from line shape.

Interactive laboratory

Open the Donchian Channels guided lab. It contains six 240-row scenarios. Change the period or multiplier where the formula exposes one; the lab recomputes immediately, stops playback, resets to an informative state, and preserves the same calculation path used by Step. Use the checkpoint selector to compare first publication, mechanism, boundary, memory, platform reconciliation, and mature state.

Production implementation

The Python and TypeScript references are topic-scoped and intentionally readable. They validate before calculating, return aligned arrays, and use None/null for unavailable states. Shared fixtures establish parity; independent family checks establish properties without trusting either implementation as the oracle.

When production output disagrees with a chart, log:

  1. symbol and source ownership;
  2. timeframe, session, timestamp, and bar finality;
  3. adjustment basis;
  4. parameter values and first-ready index;
  5. the earliest divergent component;
  6. window inclusion, seed, denominator, multiplier, equality, and null policy;
  7. the earliest revised observation.

Compare, do not conflate

Nearby topicWhat changes
Bollinger Bandsuse a mean and dispersion, not extremum ownership.
Keltner Channelsmove recursively instead of expiring finite-window owners.
A breakout strategyadds prior-window comparison and order rules outside this calculation.

Misuse boundary

  • Do not: Calling the midpoint a moving average of price.
  • Do not: Testing breakout against the same current-inclusive boundary and expecting a strict exceedance.
  • Do not: Ignoring tied extremes when diagnosing the exact expiry row.

The synthetic examples establish implementation behavior only. No historical association, transaction cost, out-of-sample result, or investment suitability claim is made.

Historical example decision

No named historical episode is used. The mechanism is learned more cleanly from controlled synthetic paths, and a named case would require point-in-time, licensed, adjustment-consistent OHLC evidence before publication.

Summary

You can now state the precise question Donchian Channels answers, calculate it, explain its memory, diagnose a platform mismatch, and separate a measurement from a trading claim. Use the family comparison rather than chart resemblance to choose the next concept.

Primary evidence

See the topic reference ledger for source roles, dates, and limitations.

Donchian Channels calculation flow

Purpose: show the exact selected state, readiness branch, and output.

Rendering system map…

Takeaway: the formula, memory, and unavailable state are part of the contract, not chart formatting.

ReferencesPrimary sources and evidence notes

Expand the source trail, evidence role, and limitations behind the engineering choices.

Donchian Channels (DC)

  • Organization or authors: TradingView
  • Source type: Maintained platform documentation
  • Publication, version, or access date: accessed 2026-07-26
  • URL or identifier: Donchian Channels (DC)
  • Accessed: 2026-07-26
  • Jurisdiction: not jurisdiction-specific
  • Evidence role: sourced definition, maintained implementation, or bounded historical context
  • Supports: Documents highest-high, lowest-low, and midpoint channel formulas.
  • Limitations: Breakout strategy timing is separated as a package-derived causal boundary.

A Century of Profitable Trends

  • Organization or authors: CMT Association
  • Source type: Institutional research paper
  • Publication, version, or access date: 2026
  • URL or identifier: A Century of Profitable Trends
  • Accessed: 2026-07-26
  • Jurisdiction: not jurisdiction-specific
  • Evidence role: sourced definition, maintained implementation, or bounded historical context
  • Supports: Provides historical context and describes highest-high/lowest-low Donchian boundaries.
  • Limitations: Performance research is not reproduced or used as evidence that this implementation is profitable.

Public evidence boundary

The fixture is author-generated synthetic teaching material. Every displayed number is an author-derived calculation from those inputs. No provider observation, historical return, causal claim, forecast, or profitability result is published. Platform documentation supports only the named platform convention; local seeds, nulls, equality, and scope remain implementation choices.

volatility_channels.ts
function finite(v:number[],n:string){if(!Array.isArray(v)||!v.length||v.some(x=>!Number.isFinite(x)))throw new Error(`${n} must contain finite numbers`)}
function period(v:number,n="period"){if(!Number.isInteger(v)||v<1)throw new Error(`${n} must be a positive integer`)}
export function donchian_channels(high:number[],low:number[],p=20){finite(high,"high");finite(low,"low");period(p);if(high.length!==low.length||high.some((h,i)=>h<low[i]))throw new Error("high and low must align with high >= low");const upper:(number|null)[]=Array(high.length).fill(null),lower:(number|null)[]=Array(high.length).fill(null),middle:(number|null)[]=Array(high.length).fill(null),width:(number|null)[]=Array(high.length).fill(null);for(let i=p-1;i<high.length;i++){upper[i]=Math.max(...high.slice(i-p+1,i+1));lower[i]=Math.min(...low.slice(i-p+1,i+1));middle[i]=((upper[i] as number)+(lower[i] as number))/2;width[i]=(upper[i] as number)-(lower[i] as number)}return{upper,lower,middle,width}}
Full-height labdonchian channels labOpen full screen