Reversal Signals

Parameters: lookback_period = 12 | confirmation_period = 3 | use_volume_confirmation = true | trend_ma_period = 50 | trend_ma_type = EMA | ma_step_period = 33

Overview

Reversal Signals looks for turning points inside structured price action instead of treating every crossover as a standalone event. It tracks rolling swing highs and lows, checks whether price is attempting a local reversal, and then confirms that reversal with a configurable number of bars and, optionally, above-average volume.

Alongside the discrete buy and sell markers, the indicator maintains a trend moving average and a stepped version of that average that only advances after a configurable bar interval. The state output reflects the relationship between price and the stepped trend context, making the indicator usable as both a trigger and a trend filter.

Defaults: `lookback_period = 12`, `confirmation_period = 3`, `use_volume_confirmation = true`, `trend_ma_period = 50`, `trend_ma_type = "EMA"`, and `ma_step_period = 33`.

Implementation Examples

Run the indicator on candles or explicit OHLCV slices and inspect the four output series.

use vector_ta::indicators::reversal_signals::{
    reversal_signals,
    ReversalSignalsInput,
    ReversalSignalsParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

let output = reversal_signals(&ReversalSignalsInput::from_slices(
    &open,
    &high,
    &low,
    &close,
    &volume,
    ReversalSignalsParams {
        lookback_period: Some(12),
        confirmation_period: Some(3),
        use_volume_confirmation: Some(true),
        trend_ma_period: Some(50),
        trend_ma_type: Some("EMA".to_string()),
        ma_step_period: Some(33),
    },
))?;

let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let candle_output = reversal_signals(&ReversalSignalsInput::with_default_candles(&candles))?;

println!("buy = {:?}", output.buy_signal.last());
println!("sell = {:?}", output.sell_signal.last());
println!("stepped ma = {:?}", candle_output.stepped_ma.last());
println!("state = {:?}", candle_output.state.last());

API Reference

Input Methods
ReversalSignalsInput::from_candles(&Candles, ReversalSignalsParams)
    -> ReversalSignalsInput

ReversalSignalsInput::from_slices(
    &[f64],
    &[f64],
    &[f64],
    &[f64],
    &[f64],
    ReversalSignalsParams,
) -> ReversalSignalsInput

ReversalSignalsInput::with_default_candles(&Candles)
    -> ReversalSignalsInput
Parameters Structure
pub struct ReversalSignalsParams {
    pub lookback_period: Option<usize>,
    pub confirmation_period: Option<usize>,
    pub use_volume_confirmation: Option<bool>,
    pub trend_ma_period: Option<usize>,
    pub trend_ma_type: Option<String>,
    pub ma_step_period: Option<usize>,
}
Output Structure
pub struct ReversalSignalsOutput {
    pub buy_signal: Vec<f64>,
    pub sell_signal: Vec<f64>,
    pub stepped_ma: Vec<f64>,
    pub state: Vec<f64>,
}
Validation, Warmup & Modes
  • The open, high, low, close, and volume slices must all be non-empty, share the same length, and contain a long enough valid OHLCV run to satisfy the lookback and trend-average requirements.
  • lookback_period and trend_ma_period must be greater than 0.
  • The supported trend moving-average types are SMA, EMA, WMA, and VWMA.
  • If volume confirmation is enabled, the stream maintains a 20-bar volume SMA and requires the live bar volume to exceed that average before confirmation succeeds.
  • Invalid or non-finite OHLCV bars reset the streaming runtime and yield None for that update.
  • Batch mode validates every numeric sweep axis and rejects unsupported kernels.
Builder, Streaming & Batch APIs
ReversalSignalsBuilder::new()
    .lookback_period(usize)
    .confirmation_period(usize)
    .use_volume_confirmation(bool)
    .trend_ma_period(usize)
    .trend_ma_type(&str)?
    .ma_step_period(usize)
    .kernel(Kernel)
    .apply(&Candles)
    .into_stream()

ReversalSignalsStream::try_new(params)
stream.update(f64, f64, f64, f64, f64) -> Option<(f64, f64, f64, f64)>

ReversalSignalsBatchBuilder::new()
    .lookback_period_range(start, end, step)
    .confirmation_period_range(start, end, step)
    .trend_ma_period_range(start, end, step)
    .ma_step_period_range(start, end, step)
    .use_volume_confirmation(bool)
    .trend_ma_type(&str)?
    .kernel(Kernel)
    .apply_slices(&[f64], &[f64], &[f64], &[f64], &[f64])
    .apply_candles(&Candles)
Error Handling
pub enum ReversalSignalsError {
    EmptyInputData,
    InputLengthMismatch {
        open_len: usize,
        high_len: usize,
        low_len: usize,
        close_len: usize,
        volume_len: usize,
    },
    AllValuesNaN,
    InvalidLookbackPeriod { lookback_period: usize },
    InvalidTrendMaPeriod { trend_ma_period: usize },
    InvalidTrendMaType { value: String },
    NotEnoughValidData { needed: usize, valid: usize },
    OutputLengthMismatch { expected: usize, got: usize },
    MismatchedOutputLen { dst_len: usize, expected_len: usize },
    InvalidRange { start: String, end: String, step: String },
    InvalidKernel(Kernel),
    InvalidKernelForBatch(Kernel),
    InvalidInput { msg: String },
}

Python Bindings

Python exposes a scalar function, a stream class, and a batch helper. The scalar call returns four NumPy arrays, the stream emits a four-value tuple or None, and the batch helper returns flattened matrices plus the parameter combinations that were tested.

from vector_ta import (
    reversal_signals,
    reversal_signals_batch,
    ReversalSignalsStream,
)

buy_signal, sell_signal, stepped_ma, state = reversal_signals(
    open,
    high,
    low,
    close,
    volume,
    lookback_period=12,
    confirmation_period=3,
    use_volume_confirmation=True,
    trend_ma_period=50,
    trend_ma_type="EMA",
    ma_step_period=33,
    kernel="auto",
)

stream = ReversalSignalsStream(trend_ma_type="VWMA")
print(stream.update(open[-1], high[-1], low[-1], close[-1], volume[-1]))

batch = reversal_signals_batch(
    open,
    high,
    low,
    close,
    volume,
    lookback_period_range=(10, 14, 2),
    confirmation_period_range=(2, 4, 1),
    trend_ma_period_range=(34, 50, 8),
    ma_step_period_range=(20, 33, 13),
    use_volume_confirmation=True,
    trend_ma_type="EMA",
    kernel="auto",
)

print(batch["rows"], batch["cols"])

JavaScript/WASM Bindings

The WASM surface exposes scalar, batch, allocation, and into-buffer helpers. The high-level calls return plain JS objects containing the four output arrays, and the low-level APIs write those arrays into caller-managed memory.

import init, {
  reversal_signals_js,
  reversal_signals_batch_js,
  reversal_signals_alloc,
  reversal_signals_free,
  reversal_signals_into,
  reversal_signals_batch_into,
} from "vector-ta-wasm";

await init();

const out = reversal_signals_js(
  open,
  high,
  low,
  close,
  volume,
  12,
  3,
  true,
  50,
  "EMA",
  33,
);

console.log(out.buy_signal, out.state);

const batch = reversal_signals_batch_js(open, high, low, close, volume, {
  lookback_period_range: [10, 14, 2],
  confirmation_period_range: [2, 4, 1],
  trend_ma_period_range: [34, 50, 8],
  ma_step_period_range: [20, 33, 13],
  use_volume_confirmation: true,
  trend_ma_type: "EMA",
});

console.log(batch.rows, batch.cols);

CUDA Bindings (Rust)

Additional details for the CUDA bindings can be found inside the VectorTA repository.

Performance Analysis

Comparison:
View:
Placeholder data (no recorded benchmarks for this indicator)

Across sizes, Rust CPU runs about 1.14× faster than Tulip C in this benchmark.

Loading chart...

AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU)

Related Indicators