Range geometry and momentum mechanics over chronologically ordered candles.
Every lookback, smoothing length, and pivot-selection rule is supplied by the caller. Range boundaries are envelopes over the selected pivots (not mid-lines through them), and distances to those boundaries are signed. The functions report quantities only: they do not name chart patterns, apply thresholds, or emit trading signals.
Pivot prominence is measured within each requested range window. A side with
no more-extreme pivot uses the window border as its search limit. If a pivot
lies on a window border, its own value is that side's base, so its prominence
is zero. range_geometry/5 itself detects pivots only where the requested
lookback exists on both sides, so it does not return border extrema.
dss_bressert/3 follows the
published ProRealTime construction:
stochastic close position, EMA, normalization of that smoothed series against
its own rolling bounds, then a second EMA. This differs from prose descriptions
that instead smooth separate stochastic numerators and denominators.
Positional OHLCV rows can be converted to the shared candle-map shape with
ZenQuant.Volatility.normalize_candles/1 before calling this module.
API Functions
| Function | Arity | Description | Param Kinds |
|---|---|---|---|
dss_bressert | 3 | Calculate DSS Bressert using the published double-smoothed construction. | candles: exchange_data, period: value, ema_length: value |
stoch_rsi | 3 | Normalize Wilder RSI within a caller-selected rolling RSI range. | candles: exchange_data, rsi_period: value, stochastic_period: value |
rsi | 2 | Calculate Wilder's Relative Strength Index from candle closes. | candles: exchange_data, period: value |
rate_of_change | 2 | Calculate close-to-close percentage rate of change. | candles: exchange_data, period: value |
range_geometry | 5 | Measure pivot-envelope range boundaries for every requested window. | candles: exchange_data, windows: value, pivot_lookback: value, price: value, select_pivots: value |
prominence_selector | 1 | Build a pivot selector using a caller-supplied minimum prominence. | minimum_prominence: value |
Summary
Types
An envelope boundary evaluated at both ends of its range window
Measured range geometry for one requested window
A local high or low with topographic prominence in price units
Caller-supplied rule that chooses which detected pivots enter the envelope fit
Functions
Calculate DSS Bressert using the published double-smoothed construction.
Build a pivot selector using a caller-supplied minimum prominence.
Measure pivot-envelope range boundaries for every requested window.
Calculate close-to-close percentage rate of change.
Calculate Wilder's Relative Strength Index from candle closes.
Normalize Wilder RSI within a caller-selected rolling RSI range.
Types
@type boundary() :: %{ slope: float(), intercept: float(), start_value: float(), end_value: float() }
An envelope boundary evaluated at both ends of its range window
@type geometry() :: %{ upper_pivots: [pivot()], lower_pivots: [pivot()], upper_boundary: boundary() | nil, lower_boundary: boundary() | nil, upper_slope: float() | nil, lower_slope: float() | nil, converging?: boolean() | nil, width_start: float() | nil, width_end: float() | nil, distance_to_upper: float() | nil, distance_to_lower: float() | nil }
Measured range geometry for one requested window
@type pivot() :: %{index: non_neg_integer(), price: float(), prominence: float()}
A local high or low with topographic prominence in price units
Caller-supplied rule that chooses which detected pivots enter the envelope fit
Functions
@spec dss_bressert([ZenQuant.Volatility.candle()], pos_integer(), pos_integer()) :: [ float() ]
Calculate DSS Bressert using the published double-smoothed construction.
Parameters
candles- Chronological candle maps with a :close field (exchange_data)period- Caller-selected stochastic and normalization period (value)ema_length- Caller-selected length for both EMA smoothing passes (value)
Returns
Chronological DSS values on a 0-100 scale (list)
Example
[36.2, 48.9, 67.4]# descripex:contract
%{
params: %{
period: %{
description: "Caller-selected stochastic and normalization period",
kind: :value
},
candles: %{
description: "Chronological candle maps with a :close field",
source: "fetch_ohlcv(symbol) |> ZenQuant.Volatility.normalize_candles()",
kind: :exchange_data
},
ema_length: %{
description: "Caller-selected length for both EMA smoothing passes",
kind: :value
}
},
returns: %{
type: :list,
description: "Chronological DSS values on a 0-100 scale"
},
returns_example: [36.2, 48.9, 67.4]
}
@spec prominence_selector(number()) :: pivot_selector()
Build a pivot selector using a caller-supplied minimum prominence.
Parameters
minimum_prominence- Minimum pivot prominence in the same units as candle prices (value)
Returns
Function ([pivot] -> [pivot]) retaining pivots at or above the supplied threshold (function)
Example
"fn pivots -> Enum.filter(pivots, &(&1.prominence >= minimum_prominence)) end"# descripex:contract
%{
params: %{
minimum_prominence: %{
description: "Minimum pivot prominence in the same units as candle prices",
kind: :value
}
},
returns: %{
type: :function,
description: "Function ([pivot] -> [pivot]) retaining pivots at or above the supplied threshold"
},
returns_example: "fn pivots -> Enum.filter(pivots, &(&1.prominence >= minimum_prominence)) end"
}
@spec range_geometry( [ZenQuant.Volatility.candle()], [pos_integer()], pos_integer(), number(), pivot_selector() ) :: %{required(pos_integer()) => geometry()} | {:error, {:insufficient_candles, pos_integer()}}
Measure pivot-envelope range boundaries for every requested window.
Parameters
candles- Chronological candle maps with :high and :low fields (exchange_data)windows- Set of trailing window lengths to measure (value)pivot_lookback- Bars required on each side of a strict local high or low (value)price- Price whose signed distance to each ending boundary is measured (price - end_value; positive means above the boundary) (value)select_pivots- Function ([pivot] -> [pivot]) choosing which detected pivots enter each envelope fit; applied independently to upper and lower pivots (value)
Returns
Map of window to prominent pivots, envelope boundaries, slopes, convergence, widths, and signed price distances; error if a window exceeds the series (map)
Example
%{
60 => %{
upper_slope: -0.08,
lower_slope: 0.03,
converging?: true,
width_start: 12.4,
width_end: 5.91,
distance_to_upper: -1.2,
distance_to_lower: 2.4
}
}Errors
:insufficient_candles
# descripex:contract
%{
params: %{
windows: %{
description: "Set of trailing window lengths to measure",
kind: :value
},
price: %{
description: "Price whose signed distance to each ending boundary is measured (price - end_value; positive means above the boundary)",
kind: :value
},
candles: %{
description: "Chronological candle maps with :high and :low fields",
source: "fetch_ohlcv(symbol) |> ZenQuant.Volatility.normalize_candles()",
kind: :exchange_data
},
pivot_lookback: %{
description: "Bars required on each side of a strict local high or low",
kind: :value
},
select_pivots: %{
description: "Function ([pivot] -> [pivot]) choosing which detected pivots enter each envelope fit; applied independently to upper and lower pivots",
kind: :value
}
},
errors: [:insufficient_candles],
returns: %{
type: :map,
description: "Map of window to prominent pivots, envelope boundaries, slopes, convergence, widths, and signed price distances; error if a window exceeds the series"
},
returns_example: %{
60 => %{
upper_slope: -0.08,
lower_slope: 0.03,
converging?: true,
width_start: 12.4,
width_end: 5.91,
distance_to_upper: -1.2,
distance_to_lower: 2.4
}
}
}
@spec rate_of_change([ZenQuant.Volatility.candle()], pos_integer()) :: [float()]
Calculate close-to-close percentage rate of change.
Parameters
candles- Chronological candle maps with a :close field (exchange_data)period- Caller-selected comparison period (value)
Returns
Chronological percentage rate-of-change values (list)
Example
[2.5, -1.2, 0.8]# descripex:contract
%{
params: %{
period: %{description: "Caller-selected comparison period", kind: :value},
candles: %{
description: "Chronological candle maps with a :close field",
source: "fetch_ohlcv(symbol) |> ZenQuant.Volatility.normalize_candles()",
kind: :exchange_data
}
},
returns: %{
type: :list,
description: "Chronological percentage rate-of-change values"
},
returns_example: [2.5, -1.2, 0.8]
}
@spec rsi([ZenQuant.Volatility.candle()], pos_integer()) :: [float()]
Calculate Wilder's Relative Strength Index from candle closes.
Parameters
candles- Chronological candle maps with a :close field (exchange_data)period- Caller-selected Wilder smoothing period (value)
Returns
Chronological RSI values on a 0-100 scale (list)
Example
[48.3, 51.7, 55.1]# descripex:contract
%{
params: %{
period: %{
description: "Caller-selected Wilder smoothing period",
kind: :value
},
candles: %{
description: "Chronological candle maps with a :close field",
source: "fetch_ohlcv(symbol) |> ZenQuant.Volatility.normalize_candles()",
kind: :exchange_data
}
},
returns: %{
type: :list,
description: "Chronological RSI values on a 0-100 scale"
},
returns_example: [48.3, 51.7, 55.1]
}
@spec stoch_rsi([ZenQuant.Volatility.candle()], pos_integer(), pos_integer()) :: [ float() ]
Normalize Wilder RSI within a caller-selected rolling RSI range.
Parameters
candles- Chronological candle maps with a :close field (exchange_data)rsi_period- Caller-selected Wilder RSI period (value)stochastic_period- Caller-selected RSI normalization period (value)
Returns
Chronological StochRSI values on a 0-100 scale (list)
Example
[18.4, 42.7, 100.0]# descripex:contract
%{
params: %{
candles: %{
description: "Chronological candle maps with a :close field",
source: "fetch_ohlcv(symbol) |> ZenQuant.Volatility.normalize_candles()",
kind: :exchange_data
},
rsi_period: %{
description: "Caller-selected Wilder RSI period",
kind: :value
},
stochastic_period: %{
description: "Caller-selected RSI normalization period",
kind: :value
}
},
returns: %{
type: :list,
description: "Chronological StochRSI values on a 0-100 scale"
},
returns_example: [18.4, 42.7, 100.0]
}