Damiani Volatmeter
vis_atr = 13 (5–40) | vis_std = 20 (10–60) | sed_atr = 40 (20–120) | sed_std = 100 (50–250) | threshold = 1.4 (0.5–3) Overview
The Damiani Volatmeter, developed by the Damiani brothers, filters market conditions to identify when trends are strong enough for directional trading versus when markets are too choppy for trend following strategies. The indicator combines Average True Range (ATR) and standard deviation measurements across two timeframes to create a sophisticated volatility filter. Short term volatility, called viscosity, captures immediate market movement using faster ATR and standard deviation calculations. Long term volatility, termed sedation, establishes a baseline using slower parameters. The relationship between these two volatility measures determines whether the market exhibits trending or ranging characteristics.
The indicator outputs two lines that work together to signal market regime. The vol line represents trend strength by comparing short term to long term volatility ratios. When vol rises above the anti line, it signals sufficient directional movement for trend following strategies. The anti line acts as a threshold, incorporating an offset parameter to filter out marginal signals. Markets are considered suitable for trending strategies when vol exceeds anti, while periods where vol remains below anti suggest choppy, directionless conditions better suited for range trading or sitting aside. This dual line approach helps traders avoid whipsaws during consolidation periods.
Traders use Damiani Volatmeter as a regime filter to enable or disable trading systems based on market conditions. During trending periods when vol exceeds anti, momentum and breakout strategies typically perform well. When vol drops below anti, the indicator warns that price action lacks direction, suggesting traders should tighten stops, reduce position sizes, or switch to mean reversion strategies. The indicator works across all timeframes and markets, making it valuable for adaptive trading systems. Many algorithmic traders incorporate Damiani Volatmeter as a master filter, only taking signals from other indicators when market conditions are favorable according to this volatility analysis.
Implementation Examples
Compute vol and anti from prices or candles:
use vectorta::indicators::damiani_volatmeter::{damiani_volatmeter, DamianiVolatmeterInput, DamianiVolatmeterParams};
use vectorta::utilities::data_loader::{Candles, read_candles_from_csv};
// Using a price slice (high/low/close all treated as this series)
let prices = vec![100.0, 101.0, 99.5, 102.0, 103.0];
let params = DamianiVolatmeterParams { vis_atr: Some(13), vis_std: Some(20), sed_atr: Some(40), sed_std: Some(100), threshold: Some(1.4) };
let input = DamianiVolatmeterInput::from_slice(&prices, params);
let out = damiani_volatmeter(&input)?;
for (v, a) in out.vol.iter().zip(out.anti.iter()) {
println!("vol={}, anti={}", v, a);
}
// Using candle data with default params (source="close")
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = DamianiVolatmeterInput::with_default_candles(&candles);
let out = damiani_volatmeter(&input)?; API Reference
Input Methods ▼
// From price slice (treated as high=low=close)
DamianiVolatmeterInput::from_slice(&[f64], DamianiVolatmeterParams) -> DamianiVolatmeterInput
// From candles with explicit source for close (high/low used for TR)
DamianiVolatmeterInput::from_candles(&Candles, &str, DamianiVolatmeterParams) -> DamianiVolatmeterInput
// From candles with defaults (close, 13/20 vs 40/100, threshold 1.4)
DamianiVolatmeterInput::with_default_candles(&Candles) -> DamianiVolatmeterInput Parameters Structure ▼
pub struct DamianiVolatmeterParams {
pub vis_atr: Option<usize>, // Default: 13
pub vis_std: Option<usize>, // Default: 20
pub sed_atr: Option<usize>, // Default: 40
pub sed_std: Option<usize>, // Default: 100
pub threshold: Option<f64>, // Default: 1.4
} Output Structure ▼
pub struct DamianiVolatmeterOutput {
pub vol: Vec<f64>, // Trend-strength line (ATR(vis)/ATR(sed) with lag component)
pub anti: Vec<f64>, // Anti-trend threshold: threshold - (StdDev(vis)/StdDev(sed))
} Validation, Warmup & NaNs ▼
vis_atr > 0,vis_std > 0,sed_atr > 0,sed_std > 0, and each≤ data.len(); elseDamianiVolatmeterError::InvalidPeriod.- Finds the first finite close; if none:
DamianiVolatmeterError::AllValuesNaN. - Requires at least
max(vis_atr, vis_std, sed_atr, sed_std, 3)valid points after that; elseNotEnoughValidData. - Outputs are
NaNthrough warmup; post‑warmup values are finite when inputs are. - In-sample
NaNcloses are treated as0.0in StdDev windows; ATR updates only on finite closes.
Error Handling ▼
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum DamianiVolatmeterError {
AllValuesNaN,
InvalidPeriod { data_len: usize, vis_atr: usize, vis_std: usize, sed_atr: usize, sed_std: usize },
NotEnoughValidData { needed: usize, valid: usize },
EmptyData,
NonBatchKernel { kernel: Kernel },
}
// Example handling
match damiani_volatmeter(&input) {
Ok(out) => { /* use out.vol, out.anti */ }
Err(e) => eprintln!("Error: {}", e),
} Python Bindings
Basic Usage ▼
import numpy as np
from vectorta import damiani, damiani_batch
from vectorta import DamianiVolatmeterStream, DamianiVolatmeterFeedStream
prices = np.array([100.0, 101.0, 99.5, 102.0, 103.0], dtype=np.float64)
# Single run (returns vol, anti)
vol, anti = damiani(prices, vis_atr=13, vis_std=20, sed_atr=40, sed_std=100, threshold=1.4, kernel='auto')
# Streaming over arrays (preloaded high/low/close)
stream = DamianiVolatmeterStream(
high=prices, low=prices, close=prices,
vis_atr=13, vis_std=20, sed_atr=40, sed_std=100, threshold=1.4,
)
while True:
pair = stream.update()
if pair is None:
break
vol_v, anti_v = pair
# Feed stream for live bars
feed = DamianiVolatmeterFeedStream(13, 20, 40, 100, 1.4)
for (h, l, c) in [(101.0, 99.0, 100.5), (102.0, 100.0, 101.5)]:
maybe = feed.update(h, l, c)
if maybe is not None:
vol_v, anti_v = maybe
# Batch sweep
res = damiani_batch(
prices,
vis_atr_range=(10, 20, 5),
vis_std_range=(20, 30, 10),
sed_atr_range=(40, 60, 10),
sed_std_range=(100, 120, 20),
threshold_range=(1.2, 1.6, 0.2),
kernel='auto',
)
vol_matrix = res['vol']
anti_matrix = res['anti'] CUDA Acceleration ▼
CUDA support for Damiani Volatmeter is coming soon.
JavaScript/WASM Bindings
Basic Usage ▼
Compute both outputs in the browser or Node.js:
import { damiani_volatmeter_js } from 'vectorta-wasm';
const prices = new Float64Array([100, 101, 99.5, 102, 103]);
const res = damiani_volatmeter_js(prices, 13, 20, 40, 100, 1.4);
// res.values is [vol..., anti...] with rows=2, cols=prices.length
const { values, rows, cols } = res as { values: Float64Array, rows: number, cols: number };
const vol = values.slice(0, cols);
const anti = values.slice(cols); Memory-Efficient Operations ▼
Use zero-copy buffers for large datasets:
import { damiani_volatmeter_alloc, damiani_volatmeter_free, damiani_volatmeter_into, memory } from 'vectorta-wasm';
const prices = new Float64Array([/* data */]);
const n = prices.length;
const inPtr = damiani_volatmeter_alloc(n);
const volPtr = damiani_volatmeter_alloc(n);
const antiPtr = damiani_volatmeter_alloc(n);
new Float64Array(memory.buffer, inPtr, n).set(prices);
damiani_volatmeter_into(inPtr, volPtr, antiPtr, n, 13, 20, 40, 100, 1.4);
const vol = new Float64Array(memory.buffer, volPtr, n).slice();
const anti = new Float64Array(memory.buffer, antiPtr, n).slice();
damiani_volatmeter_free(inPtr, n);
damiani_volatmeter_free(volPtr, n);
damiani_volatmeter_free(antiPtr, n); Batch Processing ▼
Parameter grid computation with metadata:
import { damiani_volatmeter_batch_js, damiani_volatmeter_batch_into, damiani_volatmeter_alloc, damiani_volatmeter_free, memory } from 'vectorta-wasm';
const prices = new Float64Array([/* data */]);
// JSON config interface mirrors DamianiVolatmeterBatchRange
const cfg = { vis_atr: [10, 20, 5], vis_std: [20, 40, 10], sed_atr: [40, 60, 10], sed_std: [100, 120, 20], threshold: [1.2, 1.6, 0.2] };
const out = damiani_volatmeter_batch_js(prices, cfg);
// out.vol and out.anti are flat arrays [rows*cols]
// Zero-copy variant writing into pre-allocated memory
const n = prices.length;
const inPtr = damiani_volatmeter_alloc(n);
new Float64Array(memory.buffer, inPtr, n).set(prices);
const rows = damiani_volatmeter_batch_into(inPtr, /* vol_ptr */ inPtr, /* anti_ptr must differ */ damiani_volatmeter_alloc(n), n,
10, 20, 5, 20, 40, 10, 40, 60, 10, 100, 120, 20, 1.2, 1.6, 0.2);
// Note: vol_ptr and anti_ptr cannot alias
damiani_volatmeter_free(inPtr, n); Performance Analysis
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-01-05
CUDA note
In our benchmark workload, the Rust CPU implementation is faster than CUDA for this indicator. Prefer the Rust/CPU path unless your workload differs.