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>
impl<'a> StreamingBacktest<'a>
Sourcepub fn new(spec: &'a StrategySpec, capital: f64) -> Result<Self>
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);Sourcepub fn step(&mut self, candle: &Candle) -> Result<()>
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.
Sourcepub fn equity(&self) -> &[EquityPoint]
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.
Sourcepub fn latest_equity(&self) -> Option<EquityPoint>
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.
Sourcepub fn num_trades(&self) -> usize
pub fn num_trades(&self) -> usize
The number of completed trades so far.
Sourcepub fn step_with_ref(
&mut self,
candle: &Candle,
reference: Option<f64>,
) -> Result<()>
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.
Sourcepub fn finish(self) -> BacktestReport
pub fn finish(self) -> BacktestReport
Close any open position at the last bar’s close and produce the report.
Source§impl StreamingBacktest<'static>
impl StreamingBacktest<'static>
Sourcepub fn new_owned(spec: StrategySpec, capital: f64) -> Result<Self>
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.
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.
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> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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