Strategies

Base Classes

Base strategy classes, signal output, and universe filter.

This module defines the public contracts that every quaver strategy engine must satisfy:

class quaver.strategies.base.SignalOutput(direction, confidence, notes=None, metadata=None)[source]

Bases: object

Immutable 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.0 indicates no conviction; 1.0 indicates 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 confidence is outside the range [0.0, 1.0].

direction: SignalDirection
confidence: float
notes: str | None = None
metadata: dict[str, Any] | None = None
class quaver.strategies.base.UniverseFilter(exchange_codes=None, instrument_types=None, countries=None, sectors=None, listing_ids=None)[source]

Bases: object

Filters 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"]). None means no restriction.

  • instrument_types (list[str] | None) – Restrict the universe to these instrument type strings (values of InstrumentType). None means no restriction.

  • countries (list[str] | None) – Restrict the universe to listings domiciled in these ISO country codes (e.g. ["US", "GB"]). None means no restriction.

  • sectors (list[str] | None) – Restrict the universe to listings in these sector names. None means no restriction.

  • listing_ids (list[int] | None) – Pin the universe to an explicit set of listing primary keys. None means no restriction.

exchange_codes: list[str] | None = None
instrument_types: list[str] | None = None
countries: list[str] | None = None
sectors: list[str] | None = None
listing_ids: list[int] | None = None
classmethod from_params(parameters)[source]

Construct a UniverseFilter from 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 to None (no restriction).

Parameters:

parameters (dict[str, Any] | None) – Raw engine parameters dict, typically stored in the database alongside the engine configuration. May be None or omit the "universe" key entirely.

Returns:

A populated UniverseFilter; all fields default to None when parameters is absent or "universe" is missing.

Return type:

UniverseFilter

class quaver.strategies.base.BaseStrategy(parameters)[source]

Bases: ABC

Abstract base class for all single-asset trading strategies.

Engines receive candle data as a pandas.DataFrame with columns ts, open, high, low, close, volume (minimum required). Rows are ordered by ts ASC.

Subclasses must implement:

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 by validate_parameters() before compute() is called.

display_name: str = ''
description: str = ''
parameters: dict[str, Any]
abstractmethod validate_parameters()[source]

Validate engine parameters.

Inspect self.parameters and raise ValueError for any missing required keys or out-of-range values.

Raises:

ValueError – If self.parameters is invalid or incomplete.

Return type:

None

abstractmethod compute(candles, as_of)[source]

Run strategy logic on a single listing’s candle history.

Parameters:
  • candles (DataFrame) – OHLCV DataFrame ordered by ts ASC. Contains at minimum the columns open, high, low, close, and volume. 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 (not datetime.utcnow()) for all time-dependent logic to ensure reproducible back-tests.

Returns:

A SignalOutput when the strategy generates a signal, or None when conditions are not met.

Return type:

SignalOutput | None

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:

int

get_universe_filter()[source]

Build a UniverseFilter from the engine’s parameters.

Returns:

A UniverseFilter derived from self.parameters["universe"], or an unrestricted filter when the key is absent.

Return type:

UniverseFilter

validate_candles(candles)[source]

Return True if 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:

candles (DataFrame) – The candle DataFrame to validate.

Returns:

True when the DataFrame has at least get_required_candle_count() rows, False otherwise.

Return type:

bool

classmethod get_parameter_schema()[source]

Return a JSON Schema dict describing this engine’s parameters.

The returned schema is stored in parameter_schema and used by the backend for validation and UI form generation. Override to provide a concrete schema.

Returns:

A JSON Schema-compatible dictionary, or an empty dict when no schema is defined.

Return type:

dict[str, Any]

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).

Returns:

A dictionary of universe constraint definitions, or an empty dict when no constraints are defined.

Return type:

dict[str, Any]

classmethod get_default_parameters()[source]

Return default parameter values for this engine.

The returned dict is stored in default_parameters and applied when a user creates a new engine instance without specifying parameters.

Returns:

A dictionary of default parameter values, or an empty dict when no defaults are defined.

Return type:

dict[str, Any]

class quaver.strategies.base.MultiAssetStrategyOutput(signals, metadata=None)[source]

Bases: object

Signals produced by a multi-asset compute() call.

Keys of signals are instrument identifiers (e.g. listing_id cast to str, or a ticker string). All signals for a paired or grouped trade should be emitted together in a single MultiAssetStrategyOutput so the engine can apply them atomically.

Parameters:
  • signals (dict[str, SignalOutput]) – Mapping of instrument_id to the corresponding SignalOutput for 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]
metadata: dict[str, Any] | None = None
class quaver.strategies.base.MultiAssetStrategy(parameters)[source]

Bases: ABC

Base 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 of instrument_id to a DataFrame of candles and returns signals for zero or more instruments.

Important

All signals in a single MultiAssetStrategyOutput are 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 by validate_parameters() before compute() is called.

display_name: str = ''
description: str = ''
parameters: dict[str, Any]
abstractmethod validate_parameters()[source]

Validate engine parameters.

Inspect self.parameters and raise ValueError for any missing required keys or out-of-range values.

Raises:

ValueError – If self.parameters is invalid or incomplete.

Return type:

None

abstractmethod compute(candles_map, as_of)[source]

Run strategy logic across multiple instruments simultaneously.

Parameters:
  • candles_map (dict[str, DataFrame]) – Mapping of instrument_id to an OHLCV DataFrame ordered by ts ASC. 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 MultiAssetStrategyOutput with signals keyed by instrument_id, or None when no signal conditions are met.

Return type:

MultiAssetStrategyOutput | None

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.

Returns:

List of required instrument identifier strings. Defaults to an empty list.

Return type:

list[str]

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:

int

get_universe_filter()[source]

Build a UniverseFilter from the engine’s parameters.

Returns:

A UniverseFilter derived from self.parameters["universe"], or an unrestricted filter when the key is absent.

Return type:

UniverseFilter

classmethod get_parameter_schema()[source]

Return a JSON Schema dict describing this engine’s parameters.

Returns:

A JSON Schema-compatible dictionary, or an empty dict when no schema is defined.

Return type:

dict[str, Any]

classmethod get_default_parameters()[source]

Return default parameter values for this engine.

Returns:

A dictionary of default parameter values, or an empty dict when no defaults are defined.

Return type:

dict[str, Any]

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

exception quaver.strategies.registry.DuplicateEngineError[source]

Bases: Exception

Raised 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: Exception

Raised 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: object

Central registry mapping engine names to BaseStrategy subclasses.

All state is stored in the _engines class 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. Importing quaver.strategies causes 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 BaseStrategy or MultiAssetStrategy subclass 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 BaseStrategy or MultiAssetStrategy subclass registered under engine_name.

Return type:

type[BaseStrategy] | type[MultiAssetStrategy]

Raises:

EngineNotFoundError – If engine_name is not present in the registry. The error message lists all available engine names.

classmethod list_engines()[source]

Return a sorted list of all registered engine names.

Returns:

Alphabetically sorted list of engine name strings.

Return type:

list[str]

classmethod all()[source]

Return the full engine registry as a shallow copy.

The returned dict maps engine name strings to their strategy classes. Modifications to the returned dict do not affect the registry.

Returns:

Shallow copy of the internal _engines mapping.

Return type:

dict[str, type[BaseStrategy] | type[MultiAssetStrategy]]

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 a MultiAssetStrategy subclass, "single" if it is a BaseStrategy subclass.

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 EngineNotFoundError on any subsequent look-up. It exists to prevent cross-test pollution when stub engines are registered inside individual test cases.

Return type:

None

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(). Positions 0 through period - 2 are filled with NaN because fewer than period samples are available.

Parameters:
  • values (ndarray[tuple[Any, ...], dtype[double]]) – One-dimensional array of input values.

  • period (int) – Number of elements in the rolling window. Must be >= 1.

Returns:

Array of the same length as values containing the SMA values, with NaN for the first period - 1 positions.

Return type:

ndarray[tuple[Any, ...], dtype[double]]

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 NaN because no previous close exists.

Parameters:
Returns:

Array of the same length as high containing True Range values, with NaN at index 0.

Return type:

ndarray[tuple[Any, ...], dtype[double]]

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 1 through period, because index 0 is NaN for 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(), not numpy.sum(). Using np.sum produces a seed that is period times too large and corrupts all downstream ATR and ADX values.

Parameters:
  • values (ndarray[tuple[Any, ...], dtype[double]]) – One-dimensional array to smooth. Index 0 is expected to be NaN (consistent with True Range and Directional Movement outputs).

  • period (int) – Smoothing period (Wilder period). Must be >= 1 and the array must contain at least period + 1 elements.

Returns:

Array of the same length as values containing the smoothed values, with NaN for all positions before the first valid seed.

Return type:

ndarray[tuple[Any, ...], dtype[double]]

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:

  1. Computes raw Directional Movement (+DM and -DM).

  2. Smooths True Range and Directional Movement with wilder_smooth().

  3. Derives +DI and -DI as percentages of smoothed ATR.

  4. Computes DX from the divergence of +DI and -DI.

  5. Smooths DX with a second Wilder pass to produce the ADX line.

At least 2 * period + 1 bars are required; a tuple of three all-NaN arrays is returned when this threshold is not met.

Parameters:
Returns:

A three-element tuple (adx_arr, plus_di, minus_di) where

  • adx_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 NaN wherever 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:
  • close (ndarray[tuple[Any, ...], dtype[double]]) – One-dimensional array of closing prices.

  • period (int) – Rolling window length for the SMA and standard deviation. Must be >= 1. Defaults to 20.

  • num_std (float) – Number of standard deviations for the band width. Defaults to 2.0.

Returns:

A three-element tuple (upper, middle, lower) where

  • upper – 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 NaN for the first period - 1 positions.

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 NaN wherever middle equals zero or any of the three input arrays carry a NaN value at that position.

Parameters:
Returns:

Array of BBW values with the same length as upper, containing NaN where middle is zero or any input is NaN.

Return type:

ndarray[tuple[Any, ...], dtype[double]]

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 over values[i - window + 1 : i + 1], excluding any NaN elements within the window. Positions before window - 1 are filled with NaN.

Parameters:
  • values (ndarray[tuple[Any, ...], dtype[double]]) – One-dimensional array of input values.

  • window (int) – Number of elements in the rolling window. Must be >= 1 and <= len(values).

  • percentile (float) – Desired percentile in the range [0, 100].

Returns:

Array of the same length as values containing the rolling percentile, with NaN for the first window - 1 positions or whenever the window contains no valid (non-NaN) values.

Return type:

ndarray[tuple[Any, ...], dtype[double]]

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 NaN because no prior close is available. Positions where the previous close is zero are also set to NaN to avoid division-by-zero artefacts.

Parameters:

close (ndarray[tuple[Any, ...], dtype[double]]) – One-dimensional array of closing prices.

Returns:

Array of the same length as close containing percentage returns, with NaN at index 0 and wherever the previous close equals zero.

Return type:

ndarray[tuple[Any, ...], dtype[double]]

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 NaN wherever the SMA is zero or NaN (i.e. for the first period - 1 positions).

Parameters:
  • volume (ndarray[tuple[Any, ...], dtype[double]]) – One-dimensional array of bar volume values.

  • period (int) – Rolling window length for the volume SMA. Defaults to 20.

Returns:

Array of the same length as volume containing the relative volume ratio, with NaN for the first period - 1 positions or wherever the SMA is zero.

Return type:

ndarray[tuple[Any, ...], dtype[double]]

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 index period (because True Range index 0 is NaN).

Parameters:
Returns:

Array of the same length as high containing ATR values, with NaN for leading positions where insufficient data exists.

Return type:

ndarray[tuple[Any, ...], dtype[double]]

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.0 when average loss is zero (all gains).

Parameters:
  • close (ndarray[tuple[Any, ...], dtype[double]]) – One-dimensional array of closing prices.

  • period (int) – Lookback period for gain/loss averages. Must be >= 1. Defaults to 14.

Returns:

Array of the same length as close containing RSI values in [0, 100], with NaN for the first period positions.

Return type:

ndarray[tuple[Any, ...], dtype[double]]

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 of values[i - period + 1 : i + 1]. Leading positions are NaN.

Parameters:
  • values (ndarray[tuple[Any, ...], dtype[double]]) – One-dimensional array of input values.

  • period (int) – Number of elements in the rolling window. Must be >= 1.

Returns:

Array of the same length as values containing rolling maximum values, with NaN for the first period - 1 positions.

Return type:

ndarray[tuple[Any, ...], dtype[double]]

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 of values[i - period + 1 : i + 1]. Leading positions are NaN.

Parameters:
  • values (ndarray[tuple[Any, ...], dtype[double]]) – One-dimensional array of input values.

  • period (int) – Number of elements in the rolling window. Must be >= 1.

Returns:

Array of the same length as values containing rolling minimum values, with NaN for the first period - 1 positions.

Return type:

ndarray[tuple[Any, ...], dtype[double]]

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 NaN for the first period - 2 positions (first valid value at index period - 1).

Return type:

ndarray[tuple[Any, ...], dtype[double]]

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
Parameters:
  • close (ndarray[tuple[Any, ...], dtype[double]]) – One-dimensional array of closing prices.

  • fast (int) – Fast EMA period. Defaults to 12.

  • slow (int) – Slow EMA period. Defaults to 26.

  • signal (int) – Signal EMA period. Defaults to 9.

Returns:

(macd_line, signal_line, histogram) 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]]]

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:
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 NaN at index 0.

Return type:

ndarray[tuple[Any, ...], dtype[double]]

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:
Returns:

Array of the same length as close. NaN wherever cumulative volume is zero.

Return type:

ndarray[tuple[Any, ...], dtype[double]]

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:
Returns:

Array of the same length as close with NaN for the first period - 1 positions.

Return type:

ndarray[tuple[Any, ...], dtype[double]]

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
Parameters:
Returns:

(upper, middle, lower) tuple. All arrays have the same length as high.

Return type:

tuple[ndarray[tuple[Any, ...], dtype[double]], ndarray[tuple[Any, ...], dtype[double]], ndarray[tuple[Any, ...], dtype[double]]]

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:
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: BaseStrategy

Dual 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_ma by more than threshold (oversold).

  • SELL when fast_ma > slow_ma by 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_shorting flag (default False).

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 to 0.02.

  • parameters (dict[str, Any])

display_name: str = 'Mean Reversion'
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:

None

get_required_candle_count()[source]

Return the minimum number of historical candles required.

The value is slow_period + 10 to 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:

int

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 None when there are insufficient bars, when the slow MA is zero, or when the divergence is below the threshold.

Parameters:
  • candles (DataFrame) – OHLCV DataFrame ordered by timestamp ascending. Must contain at least a close column. The current bar being evaluated is not included.

  • as_of (datetime) – Point-in-time timestamp of the current bar being evaluated.

Returns:

A SignalOutput with direction, confidence, notes, and metadata when a signal condition is met; None otherwise.

Return type:

SignalOutput | None

classmethod get_parameter_schema()[source]

Return a JSON Schema describing accepted parameters.

Returns:

JSON Schema object with parameter types, constraints, and defaults.

Return type:

dict[str, Any]

classmethod get_default_parameters()[source]

Return a copy of the default parameter dictionary.

Returns:

Mapping of parameter names to their default values: fast_period=20, slow_period=50, threshold=0.02.

Return type:

dict[str, Any]

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: object

Expanding-window probability result for a direction (long or short).

Produced by RegimeMeanReversionStrategy._compute_probabilities() and consumed by RegimeMeanReversionStrategy._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.

prob_base: float
prob_regime: float
winloss_base: float
winloss_regime: float
events_base: int
events_regime: int
class quaver.strategies.regime_mean_reversion.RegimeMeanReversionStrategy(parameters)[source]

Bases: BaseStrategy

Regime-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_*_UP and the current return dips below -return_threshold, subject to probability/win-loss checks.

  • SELL when regime is TREND_*_DOWN and 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 to 0.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.

  • parameters (dict[str, Any])

display_name: str = 'Regime Mean Reversion'
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 that sma_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:

None

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:

int

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:
  • candles (DataFrame) – OHLCV DataFrame ordered by timestamp ascending. Must contain open, high, low, close, and volume columns. The current bar being evaluated is not included.

  • as_of (datetime) – Point-in-time timestamp of the current bar being evaluated.

Returns:

A SignalOutput when all signal conditions are met; None if data is insufficient, the current regime is non-trending, or probability thresholds are not satisfied.

Return type:

SignalOutput | None

classmethod get_parameter_schema()[source]

Return a JSON Schema object describing all strategy parameters.

Returns:

A dict conforming to JSON Schema "type": "object" with a "properties" key enumerating every supported parameter together with its type, constraints, and default value.

Return type:

dict[str, Any]

classmethod get_default_parameters()[source]

Return a copy of the default parameter dictionary.

Returns:

Mapping of every parameter name to its default value as defined in the module-level _DEFAULTS constant.

Return type:

dict[str, Any]

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: BaseStrategy

VSA-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.1 when the bar is also classified as an absorption_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 near 1.0 mean the close must be near the top of the bar. Defaults to 0.4.

  • sell_close_pos_max (float) – Maximum close position ([0, 1]) for the SELL pattern to fire. Values near 0.0 mean the close must be near the bottom of the bar. Defaults to 0.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.

  • parameters (dict[str, Any])

display_name: str = 'VSA Stopping Volume'
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 int type, positive-number parameters for > 0 numeric type, unit-interval parameters (buy_close_pos_min, sell_close_pos_max) for membership in [0, 1], and boolean flags for bool type.

Raises:

ValueError – If any parameter fails its type or range check.

Return type:

None

get_required_candle_count()[source]

Return the minimum number of historical candles required.

The value is max(sma_window, trend_sma) + 5 to ensure both SMAs are warm with a small safety buffer.

Returns:

Minimum candle count needed before compute() will produce a signal.

Return type:

int

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 None when the candle DataFrame is empty, required indicators are NaN, or neither pattern fires.

Parameters:
  • candles (DataFrame) – OHLCV DataFrame ordered by timestamp ascending. Must contain open, high, low, close, and volume columns. The current bar being evaluated is not included.

  • as_of (datetime) – Point-in-time timestamp of the current bar being evaluated. Stored verbatim in the signal metadata.

Returns:

A SignalOutput when the stopping-volume BUY or symmetric SELL pattern fires on the latest bar; None otherwise.

Return type:

SignalOutput | None

classmethod get_parameter_schema()[source]

Return a JSON Schema object describing all strategy parameters.

Returns:

A dict conforming to JSON Schema "type": "object" with a "properties" key mapping each parameter name to its type, constraints, and human-readable description, and a "required" key listing all parameter names.

Return type:

dict[str, Any]

classmethod get_default_parameters()[source]

Return a copy of the default parameter dictionary.

Returns:

Mapping of every parameter name to its default value as defined in the module-level _DEFAULTS constant.

Return type:

dict[str, Any]

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: MultiAssetStrategy

Classic 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 MultiAssetStrategyOutput so 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 to 60.

  • 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.

  • parameters (dict[str, Any])

display_name: str = 'Pairs Mean Reversion'
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 with exit_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 if exit_z >= entry_z.

Return type:

None

get_required_instrument_ids()[source]

Return the two instrument identifiers required by this strategy.

Returns:

A two-element list [instrument_a, instrument_b] as configured in self.parameters.

Return type:

list[str]

get_required_candle_count()[source]

Return the minimum number of historical candles required per instrument.

The value is spread_window + 10 to 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:

int

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 None when 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 MultiAssetStrategyOutput with signals for both legs when an entry or exit condition is met; None otherwise. Possible signal directions per leg:

  • CLOSE / CLOSE — when |z_score| < exit_z.

  • SELL / BUY — when z_score > +entry_z.

  • BUY / SELL — when z_score < -entry_z.

Return type:

MultiAssetStrategyOutput | None

classmethod get_parameter_schema()[source]

Return a JSON Schema describing accepted parameters.

Returns:

JSON Schema object with parameter types, constraints, and defaults.

Return type:

dict[str, Any]

classmethod get_default_parameters()[source]

Return a copy of the default parameter dictionary.

Returns:

Mapping of parameter names to their default values: spread_window=60, entry_z=2.0, exit_z=0.5. Note that instrument_a and instrument_b have no defaults and must be provided explicitly.

Return type:

dict[str, Any]

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: BaseStrategy

Breakout 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 filterclose > 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 compressionATR(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 to 0.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.

  • parameters (dict[str, Any])

display_name: str = 'Breakout from Consolidation'
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:

None

get_required_candle_count()[source]

Return the minimum number of historical candles required.

Returns:

Minimum candle count.

Return type:

int

compute(candles, as_of)[source]

Run breakout-from-consolidation logic on a single listing’s candles.

Parameters:
  • candles (DataFrame) – OHLCV DataFrame ordered by timestamp ascending.

  • as_of (datetime) – Point-in-time timestamp of the current bar.

Returns:

A SignalOutput when all breakout conditions are met; None otherwise.

Return type:

SignalOutput | None

classmethod get_parameter_schema()[source]

Return a JSON Schema describing accepted parameters.

Returns:

JSON Schema object with parameter types, constraints, and defaults.

Return type:

dict[str, Any]

classmethod get_default_parameters()[source]

Return a copy of the default parameter dictionary.

Returns:

Mapping of parameter names to their default values.

Return type:

dict[str, Any]

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: BaseStrategy

Pullback 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 uptrendclose > MA(ma_medium), MA(ma_medium) > MA(ma_slow), and MA(ma_medium) slope is positive (today > slope_lookback bars ago).

  • Near short-term MAclose <= MA(ma_fast) * (1 + near_ma_pct), indicating price has pulled back toward the short-term average.

  • RSI pullback zoneRSI(rsi_period) is between rsi_low and rsi_high, confirming a healthy dip rather than a collapse.

  • Entry triggerclose > prior bar's high or close > 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 to 0.02.

  • parameters (dict[str, Any])

display_name: str = 'Pullback in Trend'
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:

None

get_required_candle_count()[source]

Return the minimum number of historical candles required.

Returns:

Minimum candle count (driven by the slowest MA).

Return type:

int

compute(candles, as_of)[source]

Run pullback-in-trend logic on a single listing’s candles.

Parameters:
  • candles (DataFrame) – OHLCV DataFrame ordered by timestamp ascending.

  • as_of (datetime) – Point-in-time timestamp of the current bar.

Returns:

A SignalOutput when all pullback conditions are met; None otherwise.

Return type:

SignalOutput | None

classmethod get_parameter_schema()[source]

Return a JSON Schema describing accepted parameters.

Returns:

JSON Schema object with parameter types, constraints, and defaults.

Return type:

dict[str, Any]

classmethod get_default_parameters()[source]

Return a copy of the default parameter dictionary.

Returns:

Mapping of parameter names to their default values.

Return type:

dict[str, Any]

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: BaseStrategy

Reversal 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 oversoldRSI(rsi_period) < rsi_threshold, indicating the stock has fallen sharply enough for seller exhaustion.

  • At or near supportclose <= rolling_min(low, support_period) * (1 + support_tolerance), meaning price is near or at its lowest level over the lookback period.

  • Bullish entry triggerclose > 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 %).

  • parameters (dict[str, Any])

display_name: str = 'Reversal at Support'
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:

None

get_required_candle_count()[source]

Return the minimum number of historical candles required.

Returns:

Minimum candle count (driven by the slowest MA).

Return type:

int

compute(candles, as_of)[source]

Run reversal-at-support logic on a single listing’s candles.

Parameters:
  • candles (DataFrame) – OHLCV DataFrame ordered by timestamp ascending.

  • as_of (datetime) – Point-in-time timestamp of the current bar.

Returns:

A SignalOutput when all reversal conditions are met; None otherwise.

Return type:

SignalOutput | None

classmethod get_parameter_schema()[source]

Return a JSON Schema describing accepted parameters.

Returns:

JSON Schema object with parameter types, constraints, and defaults.

Return type:

dict[str, Any]

classmethod get_default_parameters()[source]

Return a copy of the default parameter dictionary.

Returns:

Mapping of parameter names to their default values.

Return type:

dict[str, Any]