Donchian Channels: When the Infrastructure Does All the Work

Richard Donchian’s channels are among the oldest systematic trading tools. The Turtle Traders used them in the 1980s. The rules were simple: buy when price breaks above the 20-bar high, sell when it breaks below the 20-bar low.

The math is equally simple:

upper  = highest_high(period)
lower  = lowest_low(period)
middle = (upper + lower) / 2

Three lines. No smoothing, no ratios, no weighting. Just the extremes and their midpoint.

Adding this to quantedge-ta took about ten lines of new code.


What Ten Lines Looks Like

The full compute() method:

fn compute(&mut self, ohlcv: &impl crate::Ohlcv) -> Option<Self::Output> {
    let is_next_bar = self.last_open_time.is_none_or(|t| t < ohlcv.open_time());
    let extremes = if is_next_bar {
        self.last_open_time = Some(ohlcv.open_time());
        self.extremes.push(ohlcv)
    } else {
        self.extremes.replace(ohlcv)
    };
    extremes.map(|(highest_high, lowest_low)| DcValue {
        upper: highest_high,
        middle: highest_high.midpoint(lowest_low),
        lower: lowest_low,
    })
}

That’s it. But there’s a lot hiding behind self.extremes.

RollingExtremes is the same struct that powers Stochastic and Williams %R. Inside, it maintains two ring buffers (one for highs, one for lows) and tracks the position and value of the current extreme in each. On most updates, the new value is compared against the tracked extreme and the position is incremented. That’s O(1). When the tracked extreme expires from the window, a full rescan finds the new one. That’s O(n), but it only happens when the extreme ages out.

The result: amortised O(1) with an occasional linear scan. A pragmatic trade-off: simpler to implement and reason about than a monotonic deque, and for typical TA periods (14, 20, 50 bars) the rescan cost is negligible.

From Donchian’s perspective, none of this matters. It calls push or replace and gets back the extremes. The complexity is real. It’s just not Donchian’s problem.

And notice what else comes for free: live bar repainting. The push/replace pattern based on open_time to distinguish a new bar from an update to the current one. Same bar? Replace its contribution. New bar? Advance the window. Donchian didn’t need a single line of repainting logic. It inherited it from the infrastructure.

Get the abstractions right, and the rest follows. Donchian didn’t implement repainting. It didn’t need to worry about O(1). It just called push and replace. The infrastructure did the work.

The library is developing an infrastructure layer that wasn’t planned. It emerged from the constraint of not duplicating non-trivial logic. Each extraction makes the next indicator cheaper.

quantedge-ta is at crates.io. Donchian Channels shipped in v0.7.0. The library is now at v0.15.1 with 17 indicators: SMA, EMA, Bollinger Bands, RSI, MACD, ATR, Stochastic, Keltner Channels, Donchian Channels, ADX, Williams %R, CCI, CHOP, Ichimoku Cloud, OBV, VWAP, and StochRSI.

Leave a Reply

Your email address will not be published. Required fields are marked *