Skip to main content

StreamingBacktest

Struct StreamingBacktest 

Source
pub struct StreamingBacktest<'a> { /* private fields */ }
Expand description

A streaming backtest: feed bars one at a time with StreamingBacktest::step, then StreamingBacktest::finish. The historical runner is exactly this fed from a slice, so backtest and live share one code path — point step at a live feed and the same engine becomes the live bot.

§Memory over a long run

Bar history is bounded: only as many rows are retained as the spec’s rules can reach back, so feeding it forever does not grow it.

The equity curve and closed trades are not bounded, and deliberately so — StreamingBacktest::finish computes every metric over the whole series, so discarding points would quietly narrow the report rather than shrink it. An equity point is 16 bytes, so a year of 1-minute bars costs roughly 8 MB. A live consumer that reads StreamingBacktest::latest_equity each bar and persists it elsewhere never needs the accumulated copy; finish is what releases it, and starting a fresh run costs the indicators their warmup again.

Implementations§

Source§

impl<'a> StreamingBacktest<'a>

Source

pub fn new(spec: &'a StrategySpec, capital: f64) -> Result<Self>

Build a streaming backtest from a validated spec and starting capital.

use wickra_backtest_core::{run_with_capital, Candle, StreamingBacktest, StrategySpec};

let spec = StrategySpec::parse(
    r#"{"symbol":"x","timeframe":"1h","indicators":{},
        "entry":{"gt":[{"price":"close"},100]},
        "exit":{"lt":[{"price":"close"},100]},
        "sizing":{"type":"fixed_qty","qty":1}}"#,
)?;
let bar = |time, open: f64, close: f64| Candle {
    time,
    open,
    high: open.max(close),
    low: open.min(close),
    close,
    volume: 0.0,
};
let candles = [bar(0, 100.0, 101.0), bar(1, 102.0, 103.0), bar(2, 104.0, 97.0)];

let mut live = StreamingBacktest::new(&spec, 1_000.0)?;
for candle in &candles {
    live.step(candle)?;
    // Everything a live loop wants is readable between bars.
    let _ = (live.num_trades(), live.latest_equity());
}
let streamed = live.finish();

// Feeding the same bars from a slice is the historical runner, and with
// the same capital it produces the same report -- the whole claim of this
// crate. (`run` would use the default capital and disagree, which is a
// difference in inputs, not in engines.)
let batch = run_with_capital(&spec, &candles, 1_000.0)?;
assert_eq!(streamed.equity, batch.equity);
assert_eq!(streamed.metrics.pnl, batch.metrics.pnl);
Source

pub fn step(&mut self, candle: &Candle) -> Result<()>

Process one bar: fill the working order, update indicators, check intrabar stops, mark equity and decide the next action. Look-ahead-free.

Source

pub fn equity(&self) -> &[EquityPoint]

The equity points produced so far, oldest first. Readable after each step for a live tail of the equity curve.

Source

pub fn latest_equity(&self) -> Option<EquityPoint>

The most recent equity point, or None before the first bar is marked. This is the value to emit per bar in a streaming / live run.

Source

pub fn num_trades(&self) -> usize

The number of completed trades so far.

Source

pub fn step_with_ref( &mut self, candle: &Candle, reference: Option<f64>, ) -> Result<()>

Like StreamingBacktest::step, but also supplies the reference series’ close for this bar, which pairwise indicators consume as their second input. Single-instrument indicators ignore it.

Source

pub fn step_with_feeds( &mut self, candle: &Candle, feeds: &Feeds<'_>, ) -> Result<()>

Process one bar with its optional non-OHLCV Feeds. Pairwise indicators consume the reference; derivatives / order-book indicators consume the tick / snapshot; other indicators ignore them. Advance the simulation by one bar.

§Errors

Returns an error if the bar cannot be priced the way the spec asks.

Source

pub fn finish(self) -> BacktestReport

Close any open position at the last bar’s close and produce the report.

Source§

impl StreamingBacktest<'static>

Source

pub fn new_owned(spec: StrategySpec, capital: f64) -> Result<Self>

Build a streaming backtest that owns its spec, so the handle carries no borrow and can be held across steps indefinitely — for embedders that cannot thread a borrow through their own lifetime, such as a #[wasm_bindgen] handle driving the engine bar-by-bar in the browser. Otherwise identical to StreamingBacktest::new.

§Errors

Returns an error if the spec fails validation.

Trait Implementations§

Source§

impl Debug for StreamingBacktest<'_>

Hand-written because the indicator map holds Box<dyn EvalIndicator>, which no derive can reach. The evaluators are shown by name: their internal state is the indicator’s business, and printing it would make this unreadable at any real bar count.

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<'a> !RefUnwindSafe for StreamingBacktest<'a>

§

impl<'a> !UnwindSafe for StreamingBacktest<'a>

§

impl<'a> Freeze for StreamingBacktest<'a>

§

impl<'a> Send for StreamingBacktest<'a>

§

impl<'a> Sync for StreamingBacktest<'a>

§

impl<'a> Unpin for StreamingBacktest<'a>

§

impl<'a> UnsafeUnpin for StreamingBacktest<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.