Damiani Volatmeter

Parameters: 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(); else DamianiVolatmeterError::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; else NotEnoughValidData.
  • Outputs are NaN through warmup; post‑warmup values are finite when inputs are.
  • In-sample NaN closes are treated as 0.0 in 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

Comparison:
View:
Loading chart...

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.

Related Indicators