Average True Range in Quant Charts. The Python version runs on OHLC bars via ta.atr; the Rust version computes ATR on TBBO ticks. Both are complete, runnable files.
Quant Charts ATR Indicator Example (Python and Rust)
Python (OHLC): atr.py
# qc-api: 1.0.7
# 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: Average True Range with regime tags (Python / OHLC).
Required columns: high, low, close.
Data mode: OHLC.
Baseline indicator. Plots ATR as a line and emits three regime tags
(low_atr / medium_atr / high_atr) defined relative to the day's median ATR.
Use these tags as filter / record sources on any strategy.
Default thresholds (multipliers of the day's median ATR):
low_atr : ATR < 0.7 * median
medium_atr : 0.7 * median <= ATR <= 1.4 * median
high_atr : ATR > 1.4 * median
"""
import numpy as np
from quant_charts import indicator, input, plot, define_tag, ta
@indicator(
name="ATR",
description="ATR line + low/medium/high regime tags relative to day's median",
overlay=False,
data_mode="ohlc",
required_columns=["high", "low", "close"],
)
class Atr:
period = input.int(14, "ATR Period", min=2, max=500,
tooltip="ATR lookback in bars.")
low_mult = input.float(0.7, "Low Threshold (x median)", min=0.0, max=10.0, step=0.05,
tooltip="ATR below this many medians = low_atr.")
high_mult = input.float(1.4, "High Threshold (x median)", min=0.0, max=10.0, step=0.05,
tooltip="ATR above this many medians = high_atr.")
line_color = input.color("#E0AF68", "Line Color")
def calculate(self, df):
high_arr = np.asarray(df["high"], dtype=np.float64)
low_arr = np.asarray(df["low"], dtype=np.float64)
close_arr = np.asarray(df["close"], dtype=np.float64)
atr = ta.atr(high_arr, low_arr, close_arr, self.period)
finite = atr[np.isfinite(atr)]
median_atr = float(np.median(finite)) if finite.size else float("nan")
low_thresh = median_atr * self.low_mult if np.isfinite(median_atr) else float("nan")
high_thresh = median_atr * self.high_mult if np.isfinite(median_atr) else float("nan")
plot(atr, "ATR", color=self.line_color, linewidth=2)
finite_atr = np.isfinite(atr)
low_atr = (atr < low_thresh) & finite_atr
high_atr = (atr > high_thresh) & finite_atr
medium_atr = finite_atr & ~low_atr & ~high_atr
define_tag("low_atr", f"ATR(<{self.low_mult:.2f}x median)", color="#7AA2F7")
define_tag("medium_atr", f"{self.low_mult:.2f}x .. {self.high_mult:.2f}x median", color="#A1A1AA")
define_tag("high_atr", f"ATR(>{self.high_mult:.2f}x median)", color="#F7768E")
return {
"low_atr": low_atr,
"medium_atr": medium_atr,
"high_atr": high_atr,
}
Rust (TBBO): atr.rs
//! qc-api: 1.0.7
// 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 (Rust)",
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.