Quant Charts ATR Indicator Example (Rust, TBBO)

Average True Range in Quant Charts. This Rust indicator computes ATR on TBBO ticks. It is a complete, runnable file.

Rust (TBBO): atr.rs

//! qc-api: 1.0.8
// DISCLAIMER: This software is for educational and informational purposes only and does not constitute
// financial advice, investment advice, or trading advice. Past performance is not indicative of future
// results. Trading futures and other financial instruments involves substantial risk of loss. You are
// solely responsible for your own trading decisions. Quant Charts LLC assumes no liability for any
// losses incurred. All rights reserved. (c) Quant Charts LLC

//! ATR (Rust / OHLC bars).
//!
//! Required columns: high, low, close. Data mode: OHLC bars.
//!
//! Wilder ATR over `period` bars. The chart timeframe pill picks the bar
//! cadence (1m, 5m, ...). Three regime tags fire based on ATR vs the day's
//! median bar-ATR. Renders only in OHLC chart views.

use qc_strategy_api::prelude::*;

#[indicator(
    name = "ATR",
    description = "Wilder ATR with low/medium/high regime tags. OHLC-mode.",
    overlay = false,
    data_mode = "ohlc",
    timeframe = "1m"
)]
#[tag(name = "low_atr",    label = "Low ATR",    color = "#7AA2F7", description = "Bar ATR below low_mult x median")]
#[tag(name = "medium_atr", label = "Medium ATR", color = "#A1A1AA", description = "Bar ATR between low_mult and high_mult x median")]
#[tag(name = "high_atr",   label = "High ATR",   color = "#F7768E", description = "Bar ATR above high_mult x median")]
#[derive(Default)]
pub struct Atr {
    #[param(default = 14, min = 2, max = 500, label = "ATR Period (bars)",
            tooltip = "ATR lookback in bars at the indicator's declared timeframe.")]
    pub period: usize,

    #[param(default = 0.7, min = 0.0, max = 10.0, step = 0.05, label = "Low Threshold (x median)",
            tooltip = "Bar ATR below this many medians = low_atr.")]
    pub low_mult: f64,

    #[param(default = 1.4, min = 0.0, max = 10.0, step = 0.05, label = "High Threshold (x median)",
            tooltip = "Bar ATR above this many medians = high_atr.")]
    pub high_mult: f64,
}

impl OhlcIndicator for Atr {
    fn calculate(&self, data: &BarData, _prep: &DayPrep) -> IndicatorOutput {
        let n = data.len();
        if n == 0 || self.period < 2 {
            return IndicatorOutput::new().with_overlay(false);
        }

        // Wilder ATR over bar high/low/close.
        let mut atr = vec![f64::NAN; n];
        if n > self.period {
            let mut tr = vec![0.0; n];
            tr[0] = (data.high[0] - data.low[0]).max(0.0);
            for i in 1..n {
                let h = data.high[i];
                let l = data.low[i];
                let pc = data.close[i - 1];
                tr[i] = (h - l).max((h - pc).abs()).max((l - pc).abs());
            }
            let p = self.period as f64;
            let seed: f64 = tr[..self.period].iter().sum::<f64>() / p;
            atr[self.period - 1] = seed;
            for i in self.period..n {
                atr[i] = (atr[i - 1] * (p - 1.0) + tr[i]) / p;
            }
        }

        // Regime thresholds from the day's median bar ATR.
        let mut finite: Vec<f64> = atr.iter().copied().filter(|v| v.is_finite()).collect();
        let median_atr = if finite.is_empty() {
            f64::NAN
        } else {
            finite.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
            finite[finite.len() / 2]
        };
        let low_thresh = median_atr * self.low_mult;
        let high_thresh = median_atr * self.high_mult;

        let mut low_atr = vec![false; n];
        let mut medium_atr = vec![false; n];
        let mut high_atr = vec![false; n];
        for i in 0..n {
            let v = atr[i];
            if !v.is_finite() {
                continue;
            }
            if v < low_thresh {
                low_atr[i] = true;
            } else if v > high_thresh {
                high_atr[i] = true;
            } else {
                medium_atr[i] = true;
            }
        }

        IndicatorOutput::new()
            .with_overlay(false)
            .plot_line("ATR", atr, "#E0AF68")
            .with_tag("low_atr", low_atr)
            .with_tag("medium_atr", medium_atr)
            .with_tag("high_atr", high_atr)
    }
}
Machine-readable: full API at /llms-full.txt · examples on GitHub.