Donchian Channel
period = 20 Overview
The Donchian Channel, developed by Richard Donchian, creates a price channel system by tracking the highest high and lowest low over a specified lookback period. This straightforward yet powerful indicator forms three bands: the upper band marks the highest price reached during the period, the lower band shows the lowest price, and the middle band represents the average of these extremes. Originally designed for commodities trading in the 1960s, Donchian Channels became famous through the Turtle Trading system which used breakouts beyond the channels for systematic trend following. The simplicity of tracking rolling extremes makes this one of the most robust and reliable trend indicators available.
Price interaction with the channel bands provides clear trading signals and market insights. When price breaks above the upper band, it signals a new high for the period and potential trend continuation or acceleration. Breaks below the lower band indicate new lows and possible downtrend development. The channel width reflects market volatility, with wider channels during volatile periods and narrower channels during consolidation. The middle band serves as a dynamic equilibrium level, with price above suggesting bullish bias and below indicating bearish pressure. The default 20 period setting captures roughly one month of daily price action, though traders adjust this based on their timeframe and strategy.
Traders use Donchian Channels primarily for breakout trading and trend following strategies. Classic systems enter long when price exceeds the upper band and short when it drops below the lower band, using the opposite band or middle line for exits. The channels also excel at identifying support and resistance levels, as price often respects these rolling extremes during retracements. Many traders combine Donchian Channels with other indicators, using channel breakouts for entry signals while employing additional filters to reduce false breaks. The indicator proves particularly effective in strongly trending markets where breakouts lead to sustained moves beyond the channels.
Implementation Examples
Compute Donchian bands from high/low data or candles:
use vectorta::indicators::donchian::{donchian, DonchianInput, DonchianParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using with high/low slices
let high = vec![10.0, 11.5, 11.0, 12.0, 11.8];
let low = vec![ 9.0, 10.2, 10.1, 10.8, 10.5];
let params = DonchianParams { period: Some(20) }; // default = 20
let input = DonchianInput::from_slices(&high, &low, params);
let out = donchian(&input)?;
// Using with Candles (high/low columns are used internally)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = DonchianInput::with_default_candles(&candles); // period = 20
let out = donchian(&input)?;
// Access bands
for i in 0..out.upperband.len() {
println!("upper={} middle={} lower={}", out.upperband[i], out.middleband[i], out.lowerband[i]);
} API Reference
Input Methods ▼
// From high/low slices
DonchianInput::from_slices(&[f64], &[f64], DonchianParams) -> DonchianInput
// From candles (uses high/low columns internally)
DonchianInput::from_candles(&Candles, DonchianParams) -> DonchianInput
// With default params (period = 20)
DonchianInput::with_default_candles(&Candles) -> DonchianInput Parameters Structure ▼
pub struct DonchianParams {
pub period: Option<usize>, // Default: 20
} Output Structure ▼
pub struct DonchianOutput {
pub upperband: Vec<f64>,
pub middleband: Vec<f64>,
pub lowerband: Vec<f64>,
} Validation, Warmup & NaNs ▼
- Requires both
highandlowslices with equal length; otherwiseDonchianError::MismatchedLength. period > 0andperiod ≤ len; otherwiseDonchianError::InvalidPeriod { period, data_len }.- If all values are
NaN, returnsDonchianError::AllValuesNaN. - There must be at least
periodvalid points after the first finite value; otherwiseDonchianError::NotEnoughValidData { needed, valid }. - Warmup: indices
[0 .. first_valid + period - 1]areNaN. - Any window containing a
NaNyieldsNaNfor that index. - Streaming:
DonchianStream::update(high, low)returnsNoneuntil warmup;NaNinputs clear internal deques and propagateNaNonce warmed.
Error Handling ▼
use vectorta::indicators::donchian::{donchian, DonchianError};
match donchian(&input) {
Ok(output) => process(output.upperband, output.middleband, output.lowerband),
Err(DonchianError::EmptyData) => eprintln!("empty data"),
Err(DonchianError::InvalidPeriod { period, data_len }) => eprintln!("invalid period {period} for len {data_len}"),
Err(DonchianError::NotEnoughValidData { needed, valid }) => eprintln!("need {needed}, have {valid}"),
Err(DonchianError::AllValuesNaN) => eprintln!("all values NaN"),
Err(DonchianError::MismatchedLength) => eprintln!("high/low length mismatch"),
Err(DonchianError::MismatchedOutputLength) => eprintln!("output slice length mismatch"),
Err(DonchianError::InvalidKernel) => eprintln!("invalid kernel for batch"),
} Python Bindings
Quick Start ▼
import numpy as np
from vectorta import donchian, DonchianStream
# High/Low arrays
high = np.array([10.0, 11.5, 11.0, 12.0, 11.8], dtype=np.float64)
low = np.array([ 9.0, 10.2, 10.1, 10.8, 10.5], dtype=np.float64)
# Compute bands (returns upper, middle, lower)
upper, middle, lower = donchian(high, low, period=20)
# Streaming
stream = DonchianStream(period=20)
for h, l in zip(high, low):
val = stream.update(h, l) # None until warmup, then (upper, middle, lower)
if val is not None:
print(val) Batch Parameter Optimization ▼
Compute multiple period values in one pass:
import numpy as np
from vectorta import donchian_batch
high = np.array([...], dtype=np.float64)
low = np.array([...], dtype=np.float64)
res = donchian_batch(high, low, period_range=(10, 30, 5))
# res contains: 'upper', 'middle', 'lower' shaped as (rows, cols), and 'periods'
upper = res['upper']
periods = res['periods']
print(upper.shape, periods) CUDA Acceleration ▼
CUDA support for Donchian is currently under development.
# Coming soon: CUDA-accelerated Donchian calculations JavaScript/WASM Bindings
Basic Usage ▼
Calculate Donchian bands in JavaScript/TypeScript:
import { donchian_js } from 'vectorta-wasm';
const high = new Float64Array([/* highs */]);
const low = new Float64Array([/* lows */]);
const result = donchian_js(high, low, 20); // period = 20
// Result is a DonchianResult with flattened values
const rows = result.rows; // 3
const cols = result.cols; // len
const values = result.values; // Float64Array length = 3 * len
const upper = values.slice(0, cols);
const middle = values.slice(cols, 2 * cols);
const lower = values.slice(2 * cols, 3 * cols);
console.log(upper, middle, lower); Memory-Efficient Operations ▼
Use zero-copy operations into pre-allocated WASM memory:
import { donchian_alloc, donchian_free, donchian_into, memory } from 'vectorta-wasm';
const high = new Float64Array([/* highs */]);
const low = new Float64Array([/* lows */]);
const len = high.length;
// Allocate and copy inputs
const highPtr = donchian_alloc(len);
const lowPtr = donchian_alloc(len);
new Float64Array(memory.buffer, highPtr, len).set(high);
new Float64Array(memory.buffer, lowPtr, len).set(low);
// Allocate outputs
const upperPtr = donchian_alloc(len);
const middlePtr = donchian_alloc(len);
const lowerPtr = donchian_alloc(len);
// Compute directly into WASM memory
donchian_into(highPtr, lowPtr, upperPtr, middlePtr, lowerPtr, len, 20);
// Read results
const upper = new Float64Array(memory.buffer, upperPtr, len).slice();
const middle = new Float64Array(memory.buffer, middlePtr, len).slice();
const lower = new Float64Array(memory.buffer, lowerPtr, len).slice();
// Free memory
donchian_free(highPtr, len);
donchian_free(lowPtr, len);
donchian_free(upperPtr, len);
donchian_free(middlePtr, len);
donchian_free(lowerPtr, len); Batch Processing ▼
Test multiple period values efficiently:
import { donchian_batch } from 'vectorta-wasm';
const high = new Float64Array([/* highs */]);
const low = new Float64Array([/* lows */]);
const res = donchian_batch(high, low, { period_range: [10, 30, 5] });
// res has: { upper, middle, lower, periods, rows, cols }
const { upper, periods, rows, cols } = res;
// upper/middle/lower are flattened row-major arrays of length rows*cols
const row0 = upper.slice(0, cols); // first period combination
console.log(periods[0], row0.length); Performance Analysis
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05