Volatility calculations for trading systems.
Pure functions for calculating historical (realized) volatility, comparing implied vs realized volatility, and computing volatility metrics.
Example
prices = [100, 102, 99, 103, 101, 105, 102]
ZenQuant.Volatility.realized(prices)
# => 0.023 (2.3% daily volatility)
ZenQuant.Volatility.realized(prices, annualize: true)
# => 0.365 (36.5% annualized)API Functions
| Function | Arity | Description | Param Kinds |
|---|---|---|---|
rolling | 3 | Calculate rolling volatility over a price series. | prices: value, window: value |
yang_zhang | 2 | Calculate Yang-Zhang volatility estimator from OHLC data. | candles: exchange_data |
garman_klass | 2 | Calculate Garman-Klass volatility estimator from OHLC data. | candles: exchange_data |
elevated? | 3 | Check if volatility regime is elevated. | current_vol: value, baseline_vol: value |
cone | 2 | Calculate volatility cone for term structure analysis. | prices: value, periods: value |
iv_vs_rv | 3 | Compare implied volatility to realized volatility. | implied_vol: value, realized_vol: value |
iv_rank | 2 | Calculate IV rank (normalized position in min-max range). | current_iv: value, historical_ivs: value |
iv_percentile | 2 | Calculate IV percentile relative to historical distribution. | current_iv: value, historical_ivs: value |
parkinson | 2 | Calculate Parkinson volatility estimator from high-low ranges. | candles: exchange_data |
realized | 2 | Calculate realized (historical) volatility from close-to-close returns. | prices: value |
normalize_candles | 1 | Normalize positional OHLCV rows into candle maps. | rows: exchange_data |
Summary
Types
OHLC candle for volatility estimation
IV comparison output format
Positional OHLCV row returned by Bourse 0.6.x
Volatility as decimal (e.g., 0.25 = 25%)
Functions
Calculate volatility cone for term structure analysis.
Check if volatility regime is elevated.
Calculate Garman-Klass volatility estimator from OHLC data.
Calculate IV percentile relative to historical distribution.
Calculate IV rank (normalized position in min-max range).
Compare implied volatility to realized volatility.
Normalize positional OHLCV rows into candle maps.
Calculate Parkinson volatility estimator from high-low ranges.
Calculate realized (historical) volatility from close-to-close returns.
Calculate rolling volatility over a price series.
Calculate Yang-Zhang volatility estimator from OHLC data.
Types
@type candle() :: %{ optional(:timestamp) => number(), optional(:open) => number(), :high => number(), :low => number(), optional(:close) => number(), optional(:volume) => number() }
OHLC candle for volatility estimation
@type iv_format() :: :ratio | :premium | :premium_pct
IV comparison output format
@type ohlcv_row() :: [number()]
Positional OHLCV row returned by Bourse 0.6.x
@type volatility() :: float()
Volatility as decimal (e.g., 0.25 = 25%)
Functions
@spec cone([number()], [pos_integer()]) :: %{ required(pos_integer()) => volatility() | nil }
Calculate volatility cone for term structure analysis.
Parameters
prices- List of prices (chronological order) (value)periods- List of lookback periods to calculate (value)
Returns
Map of period => volatility value (or nil if insufficient data) (map)
Example
%{7 => 0.42, 30 => 0.36}# descripex:contract
%{
params: %{
periods: %{
description: "List of lookback periods to calculate",
kind: :value
},
prices: %{description: "List of prices (chronological order)", kind: :value}
},
returns: %{
type: :map,
description: "Map of period => volatility value (or nil if insufficient data)"
},
returns_example: %{7 => 0.42, 30 => 0.36}
}
Check if volatility regime is elevated.
Parameters
current_vol- Current volatility (value)baseline_vol- Normal/baseline volatility (value)
Options
threshold- Multiple of baseline considered elevated (default:1.5)
Returns
true if current_vol > baseline_vol * threshold (boolean)
Example
true# descripex:contract
%{
opts: %{
threshold: %{
default: 1.5,
type: :number,
description: "Multiple of baseline considered elevated"
}
},
params: %{
current_vol: %{description: "Current volatility", kind: :value},
baseline_vol: %{description: "Normal/baseline volatility", kind: :value}
},
returns: %{
type: :boolean,
description: "true if current_vol > baseline_vol * threshold"
},
returns_example: true
}
@spec garman_klass( [candle()], keyword() ) :: volatility() | nil | {:error, :invalid_prices}
Calculate Garman-Klass volatility estimator from OHLC data.
Parameters
candles- List of candles with :open, :high, :low, :close fields (exchange_data)
Options
annualize- Whether to annualize (default:false)trading_days- Trading days per year (default:365)
Returns
Garman-Klass volatility, nil if insufficient data, or {:error, :invalid_prices} (float)
Example
0.1095Errors
:invalid_prices
# descripex:contract
%{
opts: %{
annualize: %{
default: false,
type: :boolean,
description: "Whether to annualize"
},
trading_days: %{
default: 365,
type: :integer,
description: "Trading days per year"
}
},
params: %{
candles: %{
description: "List of candles with :open, :high, :low, :close fields",
source: "fetch_ohlcv(symbol)",
kind: :exchange_data
}
},
errors: [:invalid_prices],
returns: %{
type: :float,
description: "Garman-Klass volatility, nil if insufficient data, or {:error, :invalid_prices}"
},
returns_example: 0.1095
}
Calculate IV percentile relative to historical distribution.
Parameters
current_iv- Current implied volatility (value)historical_ivs- List of historical IV values (value)
Returns
Percentile (0-100) of historical values below current, or nil if empty (float)
Example
0.1095# descripex:contract
%{
params: %{
current_iv: %{description: "Current implied volatility", kind: :value},
historical_ivs: %{description: "List of historical IV values", kind: :value}
},
returns: %{
type: :float,
description: "Percentile (0-100) of historical values below current, or nil if empty"
},
returns_example: 0.1095
}
Calculate IV rank (normalized position in min-max range).
Parameters
current_iv- Current implied volatility (value)historical_ivs- List of historical IV values (value)
Returns
Rank (0-100) showing position in min-max range, or nil if insufficient data (float)
Example
0.1095# descripex:contract
%{
params: %{
current_iv: %{description: "Current implied volatility", kind: :value},
historical_ivs: %{description: "List of historical IV values", kind: :value}
},
returns: %{
type: :float,
description: "Rank (0-100) showing position in min-max range, or nil if insufficient data"
},
returns_example: 0.1095
}
@spec iv_vs_rv(volatility(), volatility(), iv_format()) :: float() | nil
Compare implied volatility to realized volatility.
Parameters
implied_vol- Current implied volatility (value)realized_vol- Realized volatility over same period (value)
Options
format- :ratio (IV/RV), :premium (IV-RV), or :premium_pct ((IV-RV)/RV*100) (default::ratio)
Returns
Comparison result in specified format, or nil if RV is zero (float)
Example
0.1095# descripex:contract
%{
opts: %{
format: %{
default: :ratio,
type: :atom,
description: ":ratio (IV/RV), :premium (IV-RV), or :premium_pct ((IV-RV)/RV*100)"
}
},
params: %{
implied_vol: %{description: "Current implied volatility", kind: :value},
realized_vol: %{
description: "Realized volatility over same period",
kind: :value
}
},
returns: %{
type: :float,
description: "Comparison result in specified format, or nil if RV is zero"
},
returns_example: 0.1095
}
Normalize positional OHLCV rows into candle maps.
Parameters
rows- List of [timestamp, open, high, low, close, volume] rows (exchange_data)
Returns
List of candle maps with atom keys (list)
Example
[
%{
close: 103.0,
high: 105.0,
low: 98.0,
open: 100.0,
timestamp: 1700000000000,
volume: 42.0
}
]# descripex:contract
%{
params: %{
rows: %{
description: "List of [timestamp, open, high, low, close, volume] rows",
source: "fetch_ohlcv(symbol)",
kind: :exchange_data
}
},
returns: %{type: :list, description: "List of candle maps with atom keys"},
returns_example: [
%{
close: 103.0,
high: 105.0,
low: 98.0,
open: 100.0,
timestamp: 1700000000000,
volume: 42.0
}
]
}
@spec parkinson( [candle()], keyword() ) :: volatility() | nil | {:error, :invalid_prices}
Calculate Parkinson volatility estimator from high-low ranges.
Parameters
candles- List of candles with :high and :low fields (exchange_data)
Options
annualize- Whether to annualize (default:false)trading_days- Trading days per year (default:365)
Returns
Parkinson volatility estimate, nil if insufficient data, or {:error, :invalid_prices} (float)
Example
0.1095Errors
:invalid_prices
# descripex:contract
%{
opts: %{
annualize: %{
default: false,
type: :boolean,
description: "Whether to annualize"
},
trading_days: %{
default: 365,
type: :integer,
description: "Trading days per year"
}
},
params: %{
candles: %{
description: "List of candles with :high and :low fields",
source: "fetch_ohlcv(symbol)",
kind: :exchange_data
}
},
errors: [:invalid_prices],
returns: %{
type: :float,
description: "Parkinson volatility estimate, nil if insufficient data, or {:error, :invalid_prices}"
},
returns_example: 0.1095
}
@spec realized( [number()], keyword() ) :: volatility() | nil | {:error, :invalid_prices}
Calculate realized (historical) volatility from close-to-close returns.
Parameters
prices- List of prices (chronological order, oldest first) (value)
Options
annualize- Whether to annualize the result (default:false)trading_days- Trading days per year for annualization (default:365)
Returns
Standard deviation of returns, nil if insufficient data, or {:error, :invalid_prices} (float)
Example
0.1095Errors
:invalid_prices
# descripex:contract
%{
opts: %{
annualize: %{
default: false,
type: :boolean,
description: "Whether to annualize the result"
},
trading_days: %{
default: 365,
type: :integer,
description: "Trading days per year for annualization"
}
},
params: %{
prices: %{
description: "List of prices (chronological order, oldest first)",
kind: :value
}
},
errors: [:invalid_prices],
returns: %{
type: :float,
description: "Standard deviation of returns, nil if insufficient data, or {:error, :invalid_prices}"
},
returns_example: 0.1095
}
@spec rolling([number()], pos_integer(), keyword()) :: [volatility()]
Calculate rolling volatility over a price series.
Parameters
prices- List of prices (chronological order) (value)window- Rolling window size (> 2) (value)
Options
annualize- Whether to annualize (default:false)trading_days- Trading days per year (default:365)
Returns
List of volatility values, empty if insufficient data (list)
Example
[0.38, 0.41]# descripex:contract
%{
opts: %{
annualize: %{
default: false,
type: :boolean,
description: "Whether to annualize"
},
trading_days: %{
default: 365,
type: :integer,
description: "Trading days per year"
}
},
params: %{
window: %{description: "Rolling window size (> 2)", kind: :value},
prices: %{description: "List of prices (chronological order)", kind: :value}
},
returns: %{
type: :list,
description: "List of volatility values, empty if insufficient data"
},
returns_example: [0.38, 0.41]
}
@spec yang_zhang( [candle()], keyword() ) :: volatility() | nil | {:error, :invalid_prices}
Calculate Yang-Zhang volatility estimator from OHLC data.
Parameters
candles- List of consecutive candles with :open, :high, :low, :close fields (exchange_data)
Options
annualize- Whether to annualize (default:false)trading_days- Trading days per year (default:365)
Returns
Yang-Zhang volatility, nil if insufficient data, or {:error, :invalid_prices} (float)
Example
0.1095Errors
:invalid_prices
# descripex:contract
%{
opts: %{
annualize: %{
default: false,
type: :boolean,
description: "Whether to annualize"
},
trading_days: %{
default: 365,
type: :integer,
description: "Trading days per year"
}
},
params: %{
candles: %{
description: "List of consecutive candles with :open, :high, :low, :close fields",
source: "fetch_ohlcv(symbol)",
kind: :exchange_data
}
},
errors: [:invalid_prices],
returns: %{
type: :float,
description: "Yang-Zhang volatility, nil if insufficient data, or {:error, :invalid_prices}"
},
returns_example: 0.1095
}