Strategies
Base Classes
Base strategy classes, signal output, and universe filter.
This module defines the public contracts that every quaver strategy engine must satisfy:
SignalOutput— immutable result of a single-assetcompute()call.UniverseFilter— instrument-set filter derived from engine parameters.BaseStrategy— abstract base for single-asset engines.MultiAssetStrategyOutput— immutable result of a multi-assetcompute()call.MultiAssetStrategy— abstract base for multi-asset / pairs engines.
- class quaver.strategies.base.SignalOutput(direction, confidence, notes=None, metadata=None)[source]
Bases:
objectImmutable signal produced by a strategy’s
compute()method.- Parameters:
direction (
SignalDirection) – The trading direction recommended by the engine.confidence (
float) – Conviction score in the closed interval[0.0, 1.0].0.0indicates no conviction;1.0indicates maximum conviction.notes (
str|None) – Optional free-text explanation or debug information attached to the signal.metadata (
dict[str,Any] |None) – Optional arbitrary key/value pairs for strategy-specific data that the backend may persist or forward downstream.
- Raises:
ValueError – If
confidenceis outside the range[0.0, 1.0].
- direction: SignalDirection
- class quaver.strategies.base.UniverseFilter(exchange_codes=None, instrument_types=None, countries=None, sectors=None, listing_ids=None)[source]
Bases:
objectFilters the set of listings a strategy operates on.
Pure data container. The ORM-coupled
.apply()logic lives in the backend.- Parameters:
exchange_codes (
list[str] |None) – Restrict the universe to listings on these exchange codes (e.g.["XNYS", "XNAS"]).Nonemeans no restriction.instrument_types (
list[str] |None) – Restrict the universe to these instrument type strings (values ofInstrumentType).Nonemeans no restriction.countries (
list[str] |None) – Restrict the universe to listings domiciled in these ISO country codes (e.g.["US", "GB"]).Nonemeans no restriction.sectors (
list[str] |None) – Restrict the universe to listings in these sector names.Nonemeans no restriction.listing_ids (
list[int] |None) – Pin the universe to an explicit set of listing primary keys.Nonemeans no restriction.
- classmethod from_params(parameters)[source]
Construct a
UniverseFilterfrom a raw parameters dictionary.Reads the nested
"universe"key from parameters, mapping its sub-keys to the corresponding filter fields. Missing or malformed values fall back toNone(no restriction).- Parameters:
parameters (
dict[str,Any] |None) – Raw engine parameters dict, typically stored in the database alongside the engine configuration. May beNoneor omit the"universe"key entirely.- Returns:
A populated
UniverseFilter; all fields default toNonewhen parameters is absent or"universe"is missing.- Return type:
- class quaver.strategies.base.BaseStrategy(parameters)[source]
Bases:
ABCAbstract base class for all single-asset trading strategies.
Engines receive candle data as a
pandas.DataFramewith columnsts,open,high,low,close,volume(minimum required). Rows are ordered bytsASC.Subclasses must implement:
validate_parameters()— check thatparameterscontains the required keys and valid value types.compute()— produce aSignalOutput(orNone) for a single listing’s candle history.
Subclasses should define class attributes:
display_name: str— human-readable name shown in UIs.description: str— prose explanation of the strategy logic.
- Parameters:
parameters (
dict[str,Any]) – Engine configuration key/value pairs supplied by the user. Validated byvalidate_parameters()beforecompute()is called.
- abstractmethod validate_parameters()[source]
Validate engine parameters.
Inspect
self.parametersand raiseValueErrorfor any missing required keys or out-of-range values.- Raises:
ValueError – If
self.parametersis invalid or incomplete.- Return type:
- abstractmethod compute(candles, as_of)[source]
Run strategy logic on a single listing’s candle history.
- Parameters:
candles (
DataFrame) – OHLCVDataFrameordered bytsASC. Contains at minimum the columnsopen,high,low,close, andvolume. Does not include the current bar being evaluated — look-ahead into the current bar is forbidden.as_of (
datetime) – Point-in-time timestamp of the current bar being evaluated. Use this value (notdatetime.utcnow()) for all time-dependent logic to ensure reproducible back-tests.
- Returns:
A
SignalOutputwhen the strategy generates a signal, orNonewhen conditions are not met.- Return type:
- get_required_candle_count()[source]
Return the minimum number of historical candles this strategy needs.
The engine runner will not call
compute()when fewer candles are available. Override this method to raise or lower the default.- Returns:
Minimum required candle count. Defaults to
200.- Return type:
- get_universe_filter()[source]
Build a
UniverseFilterfrom the engine’s parameters.- Returns:
A
UniverseFilterderived fromself.parameters["universe"], or an unrestricted filter when the key is absent.- Return type:
- validate_candles(candles)[source]
Return
Trueif candles meets the minimum row requirement.Override this method to apply custom validation logic (e.g. checking for required columns or data quality constraints).
- Parameters:
- Returns:
Truewhen the DataFrame has at leastget_required_candle_count()rows,Falseotherwise.- Return type:
- classmethod get_parameter_schema()[source]
Return a JSON Schema
dictdescribing this engine’s parameters.The returned schema is stored in
parameter_schemaand used by the backend for validation and UI form generation. Override to provide a concrete schema.
- classmethod get_universe_constraints()[source]
Return constraints or defaults for universe filtering.
The returned dict is stored in
universe_constraints. Override to express hard limits on the instrument universe (e.g. allowed instrument types, minimum market-cap tier).
- classmethod get_default_parameters()[source]
Return default parameter values for this engine.
The returned dict is stored in
default_parametersand applied when a user creates a new engine instance without specifying parameters.
- class quaver.strategies.base.MultiAssetStrategyOutput(signals, metadata=None)[source]
Bases:
objectSignals produced by a multi-asset
compute()call.Keys of
signalsare instrument identifiers (e.g.listing_idcast tostr, or a ticker string). All signals for a paired or grouped trade should be emitted together in a singleMultiAssetStrategyOutputso the engine can apply them atomically.- Parameters:
signals (
dict[str,SignalOutput]) – Mapping ofinstrument_idto the correspondingSignalOutputfor that instrument.metadata (
dict[str,Any] |None) – Optional strategy-level metadata shared across all signals in this output (e.g. spread value, regime label).
- signals: dict[str, SignalOutput]
- class quaver.strategies.base.MultiAssetStrategy(parameters)[source]
Bases:
ABCBase class for strategies that need candles from multiple instruments simultaneously.
Intended for use cases such as pairs trading and basket strategies where the signal for one instrument depends on the price history of one or more other instruments.
compute()receives a mapping ofinstrument_idto aDataFrameof candles and returns signals for zero or more instruments.Important
All signals in a single
MultiAssetStrategyOutputare applied atomically by the engine. Emit signals for all legs of a trade together so that partial fills cannot occur.- Parameters:
parameters (
dict[str,Any]) – Engine configuration key/value pairs supplied by the user. Validated byvalidate_parameters()beforecompute()is called.
- abstractmethod validate_parameters()[source]
Validate engine parameters.
Inspect
self.parametersand raiseValueErrorfor any missing required keys or out-of-range values.- Raises:
ValueError – If
self.parametersis invalid or incomplete.- Return type:
- abstractmethod compute(candles_map, as_of)[source]
Run strategy logic across multiple instruments simultaneously.
- Parameters:
candles_map (
dict[str,DataFrame]) – Mapping ofinstrument_idto an OHLCVDataFrameordered bytsASC. Each DataFrame excludes the current bar — no look-ahead into the bar being evaluated.as_of (
datetime) – Point-in-time timestamp of the current bar being evaluated. Use this value for all time-dependent calculations to ensure reproducible back-tests.
- Returns:
A
MultiAssetStrategyOutputwith signals keyed byinstrument_id, orNonewhen no signal conditions are met.- Return type:
- get_required_instrument_ids()[source]
Declare which instrument IDs this strategy requires.
The engine runner uses this list to fetch candle data for all required instruments before calling
compute(). Override to return the specific identifiers the strategy depends on.
- get_required_candle_count()[source]
Return the minimum number of candles needed per instrument.
The engine runner will not call
compute()when any instrument has fewer candles available than this threshold.- Returns:
Minimum required candle count per instrument. Defaults to
200.- Return type:
- get_universe_filter()[source]
Build a
UniverseFilterfrom the engine’s parameters.- Returns:
A
UniverseFilterderived fromself.parameters["universe"], or an unrestricted filter when the key is absent.- Return type:
Registry
Strategy engine registry — maps engine names to strategy classes.
StrategyRegistry is a class-level singleton (backed by a plain
dict class variable) that maps engine name strings to their corresponding
BaseStrategy or
MultiAssetStrategy subclasses.
Engines are registered via the StrategyRegistry.register() decorator at
module import time. Importing quaver.strategies triggers all built-in
registrations automatically.
Exceptions
DuplicateEngineError— raised when a name collision occurs during registration.EngineNotFoundError— raised when a look-up finds no matching engine.
- exception quaver.strategies.registry.DuplicateEngineError[source]
Bases:
ExceptionRaised when registering an engine name that already exists.
Prevents silent overwrites of previously registered engines. The error message includes both the conflicting name and the class that owns the original registration.
- Parameters:
message (str) – Explanation of which engine name is duplicated and which class holds the original registration.
- exception quaver.strategies.registry.EngineNotFoundError[source]
Bases:
ExceptionRaised when looking up an engine name that is not registered.
The error message includes the requested name and the full list of currently registered engine names to assist with debugging typos.
- Parameters:
message (str) – Explanation of which engine name was not found and what names are currently available.
- class quaver.strategies.registry.StrategyRegistry[source]
Bases:
objectCentral registry mapping engine names to
BaseStrategysubclasses.All state is stored in the
_enginesclass variable so that a single shared registry is available for the lifetime of the process without requiring explicit instantiation.Engines are added via the
register()class-method decorator, which is typically applied at module level in each engine’s source file. Importingquaver.strategiescauses all built-in engine modules to be imported, populating this registry as a side-effect.- classmethod register(engine_name)[source]
Decorator factory that registers a strategy engine class.
Apply to a
BaseStrategyorMultiAssetStrategysubclass to add it to the registry under engine_name.@StrategyRegistry.register("my_engine") class MyEngine(BaseStrategy): ...
- Parameters:
engine_name (
str) – Unique string key for this engine. Must not already exist in the registry.- Returns:
A class decorator that registers the decorated class and returns it unchanged.
- Return type:
Callable[[TypeVar(_T, bound=type)],TypeVar(_T, bound=type)]- Raises:
DuplicateEngineError – If engine_name is already present in the registry.
- classmethod get(engine_name)[source]
Look up a registered engine class by name.
- Parameters:
engine_name (
str) – The engine name used during registration.- Returns:
The
BaseStrategyorMultiAssetStrategysubclass registered under engine_name.- Return type:
- Raises:
EngineNotFoundError – If engine_name is not present in the registry. The error message lists all available engine names.
- classmethod all()[source]
Return the full engine registry as a shallow copy.
The returned
dictmaps engine name strings to their strategy classes. Modifications to the returned dict do not affect the registry.- Returns:
Shallow copy of the internal
_enginesmapping.- Return type:
- classmethod get_strategy_kind(engine_name)[source]
Return the kind of strategy registered under engine_name.
- Parameters:
engine_name (
str) – The engine name to inspect.- Returns:
"multi"if the engine is aMultiAssetStrategysubclass,"single"if it is aBaseStrategysubclass.- Return type:
Literal['single','multi']- Raises:
EngineNotFoundError – If engine_name is not present in the registry.
- classmethod clear()[source]
Remove all registered engines from the registry.
Warning
This method is intended for use in tests only (teardown fixtures). Calling it in production code will leave the registry empty and cause
EngineNotFoundErroron any subsequent look-up. It exists to prevent cross-test pollution when stub engines are registered inside individual test cases.- Return type:
- Returns:
None
Indicators
Shared pure-numpy indicator library for strategy engines.
This module provides a collection of technical analysis indicator functions
implemented exclusively with NumPy. It is intended to be imported by any
strategy that requires price-based signal computation. All functions operate on
numpy.ndarray objects and return arrays of the same length as their
primary input, padding leading positions with NaN wherever insufficient
data exists to produce a meaningful value.
- quaver.strategies.indicators.sma(values, period)[source]
Compute the Simple Moving Average using convolution.
Calculates a rolling arithmetic mean of period consecutive elements via
numpy.convolve(). Positions0throughperiod - 2are filled withNaNbecause fewer than period samples are available.- Parameters:
- Returns:
Array of the same length as values containing the SMA values, with
NaNfor the firstperiod - 1positions.- Return type:
- quaver.strategies.indicators.true_range(high, low, close)[source]
Compute the True Range for each bar.
The True Range at index i is defined as:
TR[i] = max(H[i] - L[i], |H[i] - C[i-1]|, |L[i] - C[i-1]|)
Index 0 is always
NaNbecause no previous close exists.- Parameters:
- Returns:
Array of the same length as high containing True Range values, with
NaNat index 0.- Return type:
- quaver.strategies.indicators.wilder_smooth(values, period)[source]
Apply Wilder’s smoothing (RMA) to an array.
The seed value is the arithmetic mean of the first period valid elements (indices
1throughperiod, because index 0 isNaNfor True Range and Directional Movement series). Subsequent values are computed as:out[i] = out[i-1] * (period - 1) / period + values[i]
Note
CORRECTNESS NOTE: The seed must use
numpy.mean(), notnumpy.sum(). Usingnp.sumproduces a seed that is period times too large and corrupts all downstream ATR and ADX values.- Parameters:
- Returns:
Array of the same length as values containing the smoothed values, with
NaNfor all positions before the first valid seed.- Return type:
- quaver.strategies.indicators.adx(high, low, close, period=14)[source]
Compute Wilder’s Average Directional Index (ADX) and Directional Indicators.
Implements the full ADX calculation pipeline:
Computes raw Directional Movement (+DM and -DM).
Smooths True Range and Directional Movement with
wilder_smooth().Derives +DI and -DI as percentages of smoothed ATR.
Computes DX from the divergence of +DI and -DI.
Smooths DX with a second Wilder pass to produce the ADX line.
At least
2 * period + 1bars are required; a tuple of three all-NaNarrays is returned when this threshold is not met.- Parameters:
high (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of bar high prices.low (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of bar low prices.close (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of bar closing prices.period (
int) – Wilder smoothing period. Defaults to14.
- Returns:
A three-element tuple
(adx_arr, plus_di, minus_di)whereadx_arr– Wilder-smoothed ADX values (0-100 scale).plus_di– Positive Directional Indicator (+DI, 0-100 scale).minus_di– Negative Directional Indicator (-DI, 0-100 scale).
All three arrays have the same length as high and contain
NaNwherever insufficient data is available.- Return type:
tuple[ndarray[tuple[Any,...],dtype[double]],ndarray[tuple[Any,...],dtype[double]],ndarray[tuple[Any,...],dtype[double]]]
- quaver.strategies.indicators.bollinger_bands(close, period=20, num_std=2.0)[source]
Compute Bollinger Bands around a Simple Moving Average.
The middle band is the SMA of close over period bars. The upper and lower bands are offset by num_std population standard deviations (
ddof=0) of the same rolling window:upper = SMA + num_std * std middle = SMA lower = SMA - num_std * std
- Parameters:
- Returns:
A three-element tuple
(upper, middle, lower)whereupper– Upper Bollinger Band array.middle– Middle band (SMA) array.lower– Lower Bollinger Band array.
All three arrays have the same length as close and contain
NaNfor the firstperiod - 1positions.- Return type:
tuple[ndarray[tuple[Any,...],dtype[double]],ndarray[tuple[Any,...],dtype[double]],ndarray[tuple[Any,...],dtype[double]]]
- quaver.strategies.indicators.bollinger_band_width(upper, middle, lower)[source]
Compute the Bollinger Band Width (BBW) normalised by the middle band.
Band width is calculated as:
BBW = (upper - lower) / middle
The result is
NaNwherever middle equals zero or any of the three input arrays carry aNaNvalue at that position.- Parameters:
upper (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of upper Bollinger Band values.middle (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of middle band (SMA) values.lower (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of lower Bollinger Band values.
- Returns:
Array of BBW values with the same length as upper, containing
NaNwhere middle is zero or any input isNaN.- Return type:
- quaver.strategies.indicators.rolling_percentile(values, window, percentile)[source]
Compute a rolling percentile over a fixed-size sliding window.
For each position i >=
window - 1, the percentile is computed overvalues[i - window + 1 : i + 1], excluding anyNaNelements within the window. Positions beforewindow - 1are filled withNaN.- Parameters:
- Returns:
Array of the same length as values containing the rolling percentile, with
NaNfor the firstwindow - 1positions or whenever the window contains no valid (non-NaN) values.- Return type:
- quaver.strategies.indicators.daily_returns(close)[source]
Compute bar-over-bar percentage returns.
Returns are defined as:
ret[t] = (close[t] - close[t-1]) / close[t-1]
Index 0 is always
NaNbecause no prior close is available. Positions where the previous close is zero are also set toNaNto avoid division-by-zero artefacts.
- quaver.strategies.indicators.volume_relative(volume, period=20)[source]
Compute the Relative Volume ratio against a simple moving average.
Relative Volume is defined as:
RVOL = volume / SMA(volume, period)
The result is
NaNwherever the SMA is zero orNaN(i.e. for the firstperiod - 1positions).- Parameters:
- Returns:
Array of the same length as volume containing the relative volume ratio, with
NaNfor the firstperiod - 1positions or wherever the SMA is zero.- Return type:
- quaver.strategies.indicators.atr(high, low, close, period=14)[source]
Compute the Average True Range using a simple moving average.
Calculates the True Range for each bar and then smooths it with a
sma()of length period. The first valid ATR value appears at indexperiod(because True Range index 0 isNaN).- Parameters:
high (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of bar high prices.low (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of bar low prices.close (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of bar closing prices.period (
int) – Rolling window length for the SMA smoothing. Must be >= 1. Defaults to14.
- Returns:
Array of the same length as high containing ATR values, with
NaNfor leading positions where insufficient data exists.- Return type:
- quaver.strategies.indicators.rsi(close, period=14)[source]
Compute the Relative Strength Index (SMA-based).
Uses simple moving averages of gains and losses over period bars:
RSI = 100 - 100 / (1 + avg_gain / avg_loss)
The first valid RSI appears at index period. Returns
100.0when average loss is zero (all gains).- Parameters:
- Returns:
Array of the same length as close containing RSI values in
[0, 100], withNaNfor the first period positions.- Return type:
- quaver.strategies.indicators.rolling_max(values, period)[source]
Compute the rolling maximum over a fixed-size sliding window.
For each position i >=
period - 1, the result is the maximum ofvalues[i - period + 1 : i + 1]. Leading positions areNaN.- Parameters:
- Returns:
Array of the same length as values containing rolling maximum values, with
NaNfor the firstperiod - 1positions.- Return type:
- quaver.strategies.indicators.rolling_min(values, period)[source]
Compute the rolling minimum over a fixed-size sliding window.
For each position i >=
period - 1, the result is the minimum ofvalues[i - period + 1 : i + 1]. Leading positions areNaN.- Parameters:
- Returns:
Array of the same length as values containing rolling minimum values, with
NaNfor the firstperiod - 1positions.- Return type:
- quaver.strategies.indicators.ema(values, period)[source]
Compute the Exponential Moving Average.
The seed value is the SMA of the first period elements. Subsequent values use the standard EMA recursion:
EMA[i] = close[i] * k + EMA[i-1] * (1 - k)
where
k = 2 / (period + 1).- Parameters:
- Returns:
Array of the same length as values with
NaNfor the firstperiod - 2positions (first valid value at indexperiod - 1).- Return type:
- quaver.strategies.indicators.macd(close, fast=12, slow=26, signal=9)[source]
Compute Moving Average Convergence Divergence.
The MACD line is the difference between the fast and slow EMAs. The signal line is an EMA of the MACD line. The histogram is their difference:
macd_line = EMA(close, fast) - EMA(close, slow) signal_line = EMA(macd_line, signal) histogram = macd_line - signal_line
- quaver.strategies.indicators.stochastic(high, low, close, period_k=14, period_d=3)[source]
Compute the Stochastic Oscillator (%K and %D).
%K measures where the close sits relative to the high-low range over period_k bars:
%K = (close - lowest_low) / (highest_high - lowest_low) * 100
%D is the SMA of %K over period_d bars.
- Parameters:
high (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of bar high prices.low (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of bar low prices.close (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of bar closing prices.period_k (
int) – Lookback period for %K. Defaults to14.period_d (
int) – SMA period for %D smoothing. Defaults to3.
- Returns:
(%K, %D)tuple. Both arrays have the same length as close.- Return type:
tuple[ndarray[tuple[Any,...],dtype[double]],ndarray[tuple[Any,...],dtype[double]]]
- quaver.strategies.indicators.obv(close, volume)[source]
Compute On-Balance Volume.
OBV is a cumulative total of volume, where volume is added on up-close bars and subtracted on down-close bars:
OBV[0] = NaN OBV[i] = OBV[i-1] + sign(close[i] - close[i-1]) * volume[i]
- Parameters:
- Returns:
Array of the same length as close with
NaNat index 0.- Return type:
- quaver.strategies.indicators.vwap(high, low, close, volume)[source]
Compute cumulative Volume-Weighted Average Price.
VWAP is the ratio of cumulative typical-price-weighted volume to cumulative volume:
TP = (high + low + close) / 3 VWAP = cumsum(TP * volume) / cumsum(volume)
- Parameters:
high (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of bar high prices.low (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of bar low prices.close (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of bar closing prices.volume (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of bar volumes.
- Returns:
Array of the same length as close.
NaNwherever cumulative volume is zero.- Return type:
- quaver.strategies.indicators.cci(high, low, close, period=20, scalar=0.015)[source]
Compute the Commodity Channel Index.
CCI measures how far the typical price deviates from its SMA, normalised by mean absolute deviation:
TP = (high + low + close) / 3 CCI = (TP - SMA(TP, period)) / (scalar * MAD(TP, period))
- Parameters:
high (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of bar high prices.low (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of bar low prices.close (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of bar closing prices.period (
int) – Lookback period. Defaults to20.scalar (
float) – Scaling constant. Defaults to0.015.
- Returns:
Array of the same length as close with
NaNfor the firstperiod - 1positions.- Return type:
- quaver.strategies.indicators.donchian(high, low, period=20)[source]
Compute Donchian Channels.
The upper channel is the rolling max of high and the lower channel is the rolling min of low. The middle is their average:
upper = rolling_max(high, period) lower = rolling_min(low, period) middle = (upper + lower) / 2
- quaver.strategies.indicators.keltner(high, low, close, period=20, multiplier=2.0)[source]
Compute Keltner Channels.
The middle band is an EMA of close. Upper and lower bands are offset by a multiple of the ATR:
middle = EMA(close, period) upper = middle + multiplier * ATR(high, low, close, period) lower = middle - multiplier * ATR(high, low, close, period)
- Parameters:
high (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of bar high prices.low (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of bar low prices.close (
ndarray[tuple[Any,...],dtype[double]]) – One-dimensional array of bar closing prices.period (
int) – EMA and ATR lookback period. Defaults to20.multiplier (
float) – ATR multiplier for channel width. Defaults to2.0.
- Returns:
(upper, middle, lower)tuple. All arrays have the same length as close.- Return type:
tuple[ndarray[tuple[Any,...],dtype[double]],ndarray[tuple[Any,...],dtype[double]],ndarray[tuple[Any,...],dtype[double]]]
Mean Reversion
Mean reversion strategy engine — reference implementation.
This module provides MeanReversionStrategy, a dual moving-average
mean-reversion strategy that emits BUY/SELL signals based on the relative
divergence between a fast and a slow simple moving average.
- class quaver.strategies.mean_reversion.MeanReversionStrategy(parameters)[source]
Bases:
BaseStrategyDual moving-average mean reversion strategy.
Computes a fast SMA and a slow SMA over closing prices and emits a signal whenever their relative divergence exceeds threshold.
Signal logic
BUY when
fast_ma < slow_maby more than threshold (oversold).SELL when
fast_ma > slow_maby more than threshold (overbought).Confidence scales with divergence magnitude, capped at
1.0.
Note
SELL signals mean “overbought — expect mean reversion downward”. Whether the backtest engine opens a short or simply ignores SELL-from-flat is controlled by the engine’s
allow_shortingflag (defaultFalse).- Parameters:
fast_period (int) – Short MA window. Must be a positive integer less than slow_period. Defaults to
20.slow_period (int) – Long MA window. Must be a positive integer greater than fast_period. Defaults to
50.threshold (float) – Relative divergence threshold to trigger a signal (e.g.
0.02= 2 %). Must be a positive number. Defaults to0.02.
- description: str = 'Dual moving-average mean reversion. BUY when fast MA is below slow MA by more than threshold (oversold). SELL when above (overbought).'
- validate_parameters()[source]
Validate all strategy parameters.
Checks that fast_period and slow_period are positive integers, that fast_period is strictly less than slow_period, and that threshold is a positive number.
- Raises:
ValueError – If any parameter fails its type or range check, or if
fast_period >= slow_period.- Return type:
- get_required_candle_count()[source]
Return the minimum number of historical candles required.
The value is
slow_period + 10to allow the slow MA to be computed with a small safety buffer.- Returns:
Minimum candle count needed before
compute()will produce a signal.- Return type:
- compute(candles, as_of)[source]
Run mean-reversion logic on a single listing’s candles.
Computes a fast SMA and a slow SMA from the closing prices in candles, then emits a BUY or SELL signal when their relative divergence exceeds the configured threshold. Returns
Nonewhen there are insufficient bars, when the slow MA is zero, or when the divergence is below the threshold.- Parameters:
- Returns:
A
SignalOutputwithdirection,confidence,notes, andmetadatawhen a signal condition is met;Noneotherwise.- Return type:
Regime Mean Reversion
Regime-based probabilistic mean-reversion strategy engine.
Classifies markets into regimes using ADX + Bollinger Band Width + Volume, then generates BUY/SELL signals only when expanding-window conditional probabilities confirm a high likelihood of reversal after a dip/pop.
- class quaver.strategies.regime_mean_reversion.ProbabilityResult(prob_base, prob_regime, winloss_base, winloss_regime, events_base, events_regime)[source]
Bases:
objectExpanding-window probability result for a direction (long or short).
Produced by
RegimeMeanReversionStrategy._compute_probabilities()and consumed byRegimeMeanReversionStrategy._generate_signal().- Parameters:
prob_base (
float) – Unconditional reversal success probability across all bars that met the return trigger, regardless of regime.prob_regime (
float) – Conditional reversal success probability restricted to bars that were in the same regime as the current bar.winloss_base (
float) – Average-win / average-loss ratio computed over all triggered base events.winloss_regime (
float) – Average-win / average-loss ratio restricted to the current regime.events_base (
int) – Total number of base events used to compute prob_base and winloss_base.events_regime (
int) – Number of regime-specific events used to compute prob_regime and winloss_regime.
- class quaver.strategies.regime_mean_reversion.RegimeMeanReversionStrategy(parameters)[source]
Bases:
BaseStrategyRegime-based probabilistic mean-reversion strategy.
Classifies every bar into one of ten market regimes by combining ADX strength, Bollinger Band Width expansion/compression, and relative volume. Signals are emitted only for trending regimes (
TREND_STRONG_*/TREND_WEAK_*) and only when expanding-window conditional probabilities satisfy all configured thresholds.Regime classification (10 labels)
TREND_STRONG_UP/TREND_STRONG_DOWN/TREND_STRONG_UNDEFINED— ADX above adx_trend_threshold, BBW expanding, volume above volume_strong_threshold.TREND_WEAK_UP/TREND_WEAK_DOWN/TREND_WEAK_UNDEFINED— ADX above adx_trend_threshold but without strong volume/expansion.TRANSITION_CONFIRMED/TRANSITION_WEAK— ADX between adx_transition_low and adx_trend_threshold.COMPRESSION— ADX below adx_transition_low, BBW low, normal volume.RANGE— ADX below adx_transition_low, remaining bars.
Signal logic
BUY when regime is
TREND_*_UPand the current return dips below-return_threshold, subject to probability/win-loss checks.SELL when regime is
TREND_*_DOWNand the current return pops above+return_threshold, subject to probability/win-loss checks.
- Parameters:
adx_period (int) – Lookback period for the ADX indicator. Defaults to
14.bb_period (int) – Lookback period for Bollinger Bands. Defaults to
20.bb_std (float) – Standard deviation multiplier for Bollinger Bands. Defaults to
2.0.bbw_percentile_window (int) – Rolling window used to compute the 20th percentile of BBW (compression detection). Defaults to
250.bbw_sma_period (int) – SMA period applied to BBW for expansion detection. Defaults to
5.bbw_lookback (int) – Number of bars to look back when checking whether BBW is increasing. Defaults to
3.sma_fast (int) – Fast SMA period for trend direction. Defaults to
20.sma_slow (int) – Slow SMA period for trend direction. Must be greater than sma_fast. Defaults to
50.volume_sma_period (int) – SMA period for relative volume normalisation. Defaults to
20.adx_trend_threshold (float) – ADX value above which the market is considered trending. Defaults to
21.0.adx_transition_low (float) – Lower ADX bound for the transition regime. Defaults to
20.0.volume_strong_threshold (float) – Relative volume multiplier required for a “strong” trend regime. Defaults to
1.2.volume_normal_threshold (float) – Minimum relative volume for a “normal” confirmation. Defaults to
1.0.return_threshold (float) – Minimum absolute daily return that qualifies as a dip (for BUY) or a pop (for SELL). Defaults to
0.02.success_threshold (float) – Minimum next-bar return that counts as a successful reversal. Defaults to
0.005.prob_threshold_base (float) – Minimum unconditional probability required. Defaults to
0.50.prob_threshold_weak (float) – Minimum regime probability for weak-trend regimes. Defaults to
0.50.prob_threshold_strong (float) – Minimum regime probability for strong-trend regimes. Defaults to
0.50.winloss_threshold_weak (float) – Minimum win/loss ratio for weak-trend regimes. Defaults to
1.3.winloss_threshold_strong (float) – Minimum win/loss ratio for strong-trend regimes. Defaults to
1.3.safemargin (float) – Fractional safety margin applied to all threshold comparisons (
0.0= no margin). Defaults to0.0.min_events (int) – Minimum number of historical events required for both base and regime probability estimates. Defaults to
12.candle_count (int) – Total number of historical candles requested from the data provider. Defaults to
500.
- description: str = 'Regime-based probabilistic mean-reversion strategy. Classifies markets into regimes using ADX, Bollinger Band Width, and volume, then generates signals only when expanding-window conditional probabilities confirm reversal likelihood.'
- validate_parameters()[source]
Validate all strategy parameters.
Iterates over integer parameters and ensures each is a positive
int. Iterates over float parameters and ensures each is a positive number. Also validates that safemargin is non-negative and thatsma_fast < sma_slow.- Raises:
ValueError – If any integer parameter is not a positive integer, if any float parameter is not a positive number, if safemargin is negative, or if
sma_fast >= sma_slow.- Return type:
- get_required_candle_count()[source]
Return the number of historical candles required by this strategy.
Delegates directly to the candle_count parameter.
- Returns:
The configured candle_count value.
- Return type:
- compute(candles, as_of)[source]
Run regime-based mean-reversion logic on a single listing’s candles.
Computes all necessary indicators (ADX, Bollinger Bands, SMA, relative volume, daily returns), classifies every bar into a market regime, and emits a BUY or SELL signal for the latest bar when all probability and win/loss thresholds are satisfied.
- Parameters:
- Returns:
A
SignalOutputwhen all signal conditions are met;Noneif data is insufficient, the current regime is non-trending, or probability thresholds are not satisfied.- Return type:
VSA Stopping Volume
VSA Stopping Volume strategy engine.
Implements a Volume Spread Analysis pattern:
Quantitative features from OHLCV (spread, close_position, vol_rel, spread_rel)
BUY (Stopping Volume) pattern in a local downtrend
Optional symmetric SELL (distribution on highs)
Candles must be ordered by ts ASC. Emits at most one signal per compute() call (latest candle).
- class quaver.strategies.vsa_stopping_volume.VSAStoppingVolumeStrategy(parameters)[source]
Bases:
BaseStrategyVSA-style pattern engine using stopping-volume reversal heuristic.
Computes four quantitative features from OHLCV data — candle spread, close position within the bar, relative volume, and relative spread — and applies a stopping-volume pattern to identify potential reversals.
Signal logic
BUY (Stopping Volume) — fires when the market is in a local downtrend (
close < trend_sma), the current candle is bearish, relative volume exceeds stopping_vol_rel, the spread is narrow (below spread_small), and the close is not near the lows of the bar (close_position > buy_close_pos_min). This combination signals absorbed selling pressure.SELL (Distribution) — symmetric mirror of the BUY pattern, active only when enable_sell is
True. Fires in an uptrend with a bullish bar, high volume, narrow spread, and close near the bottom of the range (close_position < sell_close_pos_max).
Confidence is derived from excess relative volume above stopping_vol_rel, with a bonus
+0.1when the bar is also classified as anabsorption_trap(high volume, narrow spread).- Parameters:
sma_window (int) – Lookback period for volume and spread normalisation SMAs. Defaults to
20.trend_sma (int) – Lookback period for the closing-price SMA used to determine local trend direction. Defaults to
20.vol_high (float) – Relative-volume threshold above which volume is classified as “high” in the matrix state. Defaults to
1.5.vol_low (float) – Relative-volume threshold below which volume is classified as “low” in the matrix state. Defaults to
0.7.spread_big (float) – Relative-spread threshold above which a candle is considered wide. Defaults to
1.3.spread_small (float) – Relative-spread threshold below which a candle is considered narrow (absorption candidate). Defaults to
0.7.stopping_vol_rel (float) – Minimum relative volume for a stopping-volume event on either side. Defaults to
2.0.buy_close_pos_min (float) – Minimum close position (
[0, 1]) for the BUY pattern to fire. Values near1.0mean the close must be near the top of the bar. Defaults to0.4.sell_close_pos_max (float) – Maximum close position (
[0, 1]) for the SELL pattern to fire. Values near0.0mean the close must be near the bottom of the bar. Defaults to0.6.enable_buy (bool) – Whether to evaluate the BUY stopping-volume pattern. Defaults to
True.enable_sell (bool) – Whether to evaluate the SELL symmetric pattern. Defaults to
True.
- description: str = 'VSA-style pattern engine: computes spread/close-position and relative volume/spread. Generates BUY (and optional SELL) signals using a stopping-volume reversal heuristic with mandatory absorption (narrow spread).'
- validate_parameters()[source]
Validate all strategy parameters.
Checks integer parameters (sma_window, trend_sma) for positive
inttype, positive-number parameters for> 0numeric type, unit-interval parameters (buy_close_pos_min, sell_close_pos_max) for membership in[0, 1], and boolean flags forbooltype.- Raises:
ValueError – If any parameter fails its type or range check.
- Return type:
- get_required_candle_count()[source]
Return the minimum number of historical candles required.
The value is
max(sma_window, trend_sma) + 5to ensure both SMAs are warm with a small safety buffer.- Returns:
Minimum candle count needed before
compute()will produce a signal.- Return type:
- compute(candles, as_of)[source]
Run VSA stopping-volume logic on a single listing’s candles.
Computes spread, close position, relative volume, and relative spread for the entire series, then evaluates the BUY and SELL pattern conditions on the latest bar only. Returns
Nonewhen the candle DataFrame is empty, required indicators areNaN, or neither pattern fires.- Parameters:
- Returns:
A
SignalOutputwhen the stopping-volume BUY or symmetric SELL pattern fires on the latest bar;Noneotherwise.- Return type:
Pairs Mean Reversion
Pairs mean-reversion strategy (statistical arbitrage example).
This module provides PairsMeanReversionStrategy, a classical
two-leg statistical arbitrage strategy that trades the normalised price
spread between two instruments using z-score entry and exit thresholds.
- class quaver.strategies.pairs_mean_reversion.PairsMeanReversionStrategy(parameters)[source]
Bases:
MultiAssetStrategyClassic two-leg spread (pairs) mean-reversion strategy.
Computes the raw price spread between two instruments (
close_A - close_B), normalises it to a z-score using a rolling window, and emits paired BUY/SELL or CLOSE signals based on entry and exit z-score thresholds.Spread definition:
spread[t] = close_A[t] - close_B[t] z_score[t] = (spread[t] - rolling_mean(spread, window)) / rolling_std(spread, window)
Signal logic
z_score > +entry_z— SELL A, BUY B (spread expected to fall).z_score < -entry_z— BUY A, SELL B (spread expected to rise).|z_score| < exit_z— CLOSE A, CLOSE B (convergence achieved).
Important
Both legs’ signals are always emitted together in one
MultiAssetStrategyOutputso that the engine applies them atomically.- Parameters:
instrument_a (str) – Identifier of the first leg. Must match a key in the candles_map supplied to
compute().instrument_b (str) – Identifier of the second leg. Must match a key in the candles_map supplied to
compute(). Must differ from instrument_a.spread_window (int) – Rolling window for computing the z-score mean and standard deviation. Must be an integer
>= 2. Defaults to60.entry_z (float) – Z-score magnitude threshold to open a position. Must be a positive number greater than exit_z. Defaults to
2.0.exit_z (float) – Z-score magnitude threshold to close an open position. Must be a positive number less than entry_z. Defaults to
0.5.
- description: str = 'Classical two-leg statistical arbitrage. Trades the normalised spread between two instruments using z-score entry/exit thresholds.'
- validate_parameters()[source]
Validate all strategy parameters.
Checks that instrument_a and instrument_b are non-empty strings and are different from each other. Validates that spread_window is an integer
>= 2, and that both entry_z and exit_z are positive numbers withexit_z < entry_z.- Raises:
ValueError – If instrument_a or instrument_b are missing, empty, or identical; if spread_window is not an integer
>= 2; if entry_z or exit_z are not positive numbers; or ifexit_z >= entry_z.- Return type:
- get_required_instrument_ids()[source]
Return the two instrument identifiers required by this strategy.
- get_required_candle_count()[source]
Return the minimum number of historical candles required per instrument.
The value is
spread_window + 10to allow the rolling statistics to be computed with a small safety buffer.- Returns:
Minimum candle count needed per instrument before
compute()will produce a signal.- Return type:
- compute(candles_map, as_of)[source]
Run pairs mean-reversion logic for both instruments.
Aligns the two closing-price series on their shorter tail, computes the rolling z-score of the price spread, and emits atomically paired signals when an entry or exit condition is triggered. Returns
Nonewhen either DataFrame is missing, when either series has fewer than spread_window bars, or when the rolling standard deviation is zero.- Parameters:
candles_map (
dict[str,DataFrame]) – Mapping of instrument identifier to its OHLCV DataFrame (ordered by timestamp ascending). Must contain entries for both instrument_a and instrument_b. Each DataFrame excludes the current bar (no look-ahead).as_of (
datetime) – Point-in-time timestamp of the current bar being evaluated.
- Returns:
A
MultiAssetStrategyOutputwith signals for both legs when an entry or exit condition is met;Noneotherwise. Possible signal directions per leg:CLOSE/CLOSE— when|z_score| < exit_z.SELL/BUY— whenz_score > +entry_z.BUY/SELL— whenz_score < -entry_z.
- Return type:
Breakout from Consolidation
Breakout from Consolidation strategy engine.
Detects low-volatility consolidation phases followed by a high-volume breakout above resistance, in the direction of the prevailing trend.
- class quaver.strategies.breakout_consolidation.BreakoutConsolidationStrategy(parameters)[source]
Bases:
BaseStrategyBreakout from Consolidation strategy.
Identifies stocks in a tight, low-volatility price range (consolidation) and generates a BUY signal when price breaks above the range ceiling with above-average volume and declining ATR.
Signal logic
All five conditions must be true on the latest bar:
Trend filter –
close > SMA(ma_period)ensures the breakout is in the direction of the medium-term trend.Consolidation – the 20-day price range (highest high minus lowest low) as a fraction of price must be <= range_max_pct.
Volatility compression –
ATR(atr_period)today must be lower than atr_lookback bars ago, confirming narrowing daily swings.Breakout trigger – today’s close exceeds the highest high of the prior consolidation_period bars.
Volume confirmation – today’s volume exceeds the volume_sma_period-day average volume.
Confidence scales with the volume surge above average, capped at 1.0.
- Parameters:
ma_period (int) – Trend-filter SMA window. Defaults to
50.consolidation_period (int) – Lookback for range and breakout ceiling. Defaults to
20.range_max_pct (float) – Maximum allowed range as a fraction of price (e.g.
0.10= 10 %). Defaults to0.10.atr_period (int) – ATR lookback period. Defaults to
14.atr_lookback (int) – Number of bars to look back for ATR decline check. Defaults to
10.volume_sma_period (int) – Volume SMA window for confirmation. Defaults to
20.
- description: str = 'Detects low-volatility consolidation phases and generates BUY signals when price breaks above the range ceiling with above-average volume and declining ATR, in the direction of the prevailing trend.'
- validate_parameters()[source]
Validate all strategy parameters.
- Raises:
ValueError – If any parameter fails its type or range check.
- Return type:
- get_required_candle_count()[source]
Return the minimum number of historical candles required.
- Returns:
Minimum candle count.
- Return type:
- compute(candles, as_of)[source]
Run breakout-from-consolidation logic on a single listing’s candles.
- Parameters:
- Returns:
A
SignalOutputwhen all breakout conditions are met;Noneotherwise.- Return type:
Pullback in Trend
Pullback in Trend (Trend Continuation) strategy engine.
Waits for a temporary price retrace toward the short-term moving average within a confirmed multi-timeframe uptrend, then enters when momentum resumes.
- class quaver.strategies.pullback_trend.PullbackTrendStrategy(parameters)[source]
Bases:
BaseStrategyPullback in Trend strategy (trend continuation).
Identifies healthy pullbacks within a confirmed uptrend and generates a BUY signal when the price shows signs of resuming its upward move.
Signal logic
All conditions must be true on the latest bar:
Multi-timeframe uptrend –
close > MA(ma_medium),MA(ma_medium) > MA(ma_slow), and MA(ma_medium) slope is positive (today > slope_lookback bars ago).Near short-term MA –
close <= MA(ma_fast) * (1 + near_ma_pct), indicating price has pulled back toward the short-term average.RSI pullback zone –
RSI(rsi_period)is between rsi_low and rsi_high, confirming a healthy dip rather than a collapse.Entry trigger –
close > prior bar's highorclose > MA(ma_fast), indicating resumed upward momentum.
Confidence scales with RSI proximity to the lower bound (more oversold = higher conviction).
- Parameters:
ma_fast (int) – Short-term MA window. Defaults to
20.ma_medium (int) – Medium-term MA window. Defaults to
50.ma_slow (int) – Long-term MA window. Defaults to
200.rsi_period (int) – RSI lookback period. Defaults to
14.rsi_low (int) – Lower bound of the RSI pullback zone. Defaults to
40.rsi_high (int) – Upper bound of the RSI pullback zone. Defaults to
50.atr_period (int) – ATR lookback for stop-loss buffer. Defaults to
14.atr_stop_mult (float) – Multiplier of ATR for stop-loss buffer below the pullback low. Defaults to
0.5.slope_lookback (int) – Number of bars to look back for MA slope check. Defaults to
5.near_ma_pct (float) – Maximum distance above MA(ma_fast) as a fraction of MA (e.g.
0.02= 2 %). Defaults to0.02.
- description: str = "Trend continuation strategy. BUY when price pulls back to the short-term MA within a confirmed multi-timeframe uptrend and RSI indicates a healthy dip (40-50), with momentum resumption confirmed by a close above the prior bar's high or the short-term MA."
- validate_parameters()[source]
Validate all strategy parameters.
- Raises:
ValueError – If any parameter fails its type or range check.
- Return type:
- get_required_candle_count()[source]
Return the minimum number of historical candles required.
- Returns:
Minimum candle count (driven by the slowest MA).
- Return type:
- compute(candles, as_of)[source]
Run pullback-in-trend logic on a single listing’s candles.
- Parameters:
- Returns:
A
SignalOutputwhen all pullback conditions are met;Noneotherwise.- Return type:
Reversal at Support
Reversal at Support (Mean Reversion Swing) strategy engine.
Counter-trend strategy that buys extreme oversold dips at key support levels when the stock is NOT in a structural downtrend.
- class quaver.strategies.reversal_support.ReversalSupportStrategy(parameters)[source]
Bases:
BaseStrategyReversal at Support strategy (mean reversion swing).
Identifies extreme oversold conditions near a support level and generates a BUY signal when a bullish reversal candle confirms buyer participation, provided the stock is not in a structural downtrend.
Signal logic
All conditions must be true on the latest bar:
No structural downtrend – the absolute distance between close and MA(ma_slow) as a fraction of MA(ma_slow) must be less than max_dist_ma200.
Extreme oversold –
RSI(rsi_period) < rsi_threshold, indicating the stock has fallen sharply enough for seller exhaustion.At or near support –
close <= rolling_min(low, support_period) * (1 + support_tolerance), meaning price is near or at its lowest level over the lookback period.Bullish entry trigger –
close > prior bar's high, confirming real buying pressure has emerged.
Confidence scales with how deeply oversold the RSI is below the threshold.
- Parameters:
ma_fast (int) – Short-term MA window (used as target). Defaults to
20.ma_medium (int) – Medium-term MA window (used as target). Defaults to
50.ma_slow (int) – Long-term MA window for structural-health filter. Defaults to
200.rsi_period (int) – RSI lookback period. Defaults to
14.rsi_threshold (int) – RSI must be below this value for an oversold condition. Defaults to
30.max_dist_ma200 (float) – Maximum allowed fractional distance from MA(ma_slow). Defaults to
0.20(20 %).support_period (int) – Lookback for the rolling low (support level). Defaults to
20.support_tolerance (float) – Maximum fraction above the rolling low to still be considered “near support”. Defaults to
0.03(3 %).
- description: str = 'Counter-trend mean reversion strategy. BUY when the stock is extremely oversold (RSI < 30), near a key support level, and NOT in a structural downtrend, confirmed by a bullish reversal candle (close > prior high).'
- validate_parameters()[source]
Validate all strategy parameters.
- Raises:
ValueError – If any parameter fails its type or range check.
- Return type:
- get_required_candle_count()[source]
Return the minimum number of historical candles required.
- Returns:
Minimum candle count (driven by the slowest MA).
- Return type:
- compute(candles, as_of)[source]
Run reversal-at-support logic on a single listing’s candles.
- Parameters:
- Returns:
A
SignalOutputwhen all reversal conditions are met;Noneotherwise.- Return type: