pine_lang/backtest.rs
1//! The outcome of replaying a `strategy`: its equity curve and trade log.
2
3use pine_broker::Trade;
4
5/// What a `strategy` produced over a run: the equity curve, the trade log, and
6/// the summary values Pine exposes as `strategy.*`. Field names follow Pine's.
7#[derive(Debug, Clone, Default)]
8pub struct Backtest {
9 pub initial_capital: f64,
10 /// Account value at each bar's close.
11 pub equity: Vec<f64>,
12 /// Every trade, closed ones (in the order they closed) before still-open
13 /// ones. `exit_price` is `None` while open; `profit(price)` values it.
14 pub trades: Vec<Trade>,
15 pub net_profit: f64,
16 pub open_profit: f64,
17 pub gross_profit: f64,
18 /// Total loss of the losing trades, as a positive magnitude.
19 pub gross_loss: f64,
20 pub max_drawdown: f64,
21 pub max_runup: f64,
22 pub win_trades: usize,
23 pub loss_trades: usize,
24 pub even_trades: usize,
25 /// Signed: positive long, negative short.
26 pub position_size: f64,
27 /// The last bar's close, at which open trades are valued.
28 pub mark_price: f64,
29}
30
31impl Backtest {
32 /// The final account value, or the initial capital if no bar ran.
33 pub fn final_equity(&self) -> f64 {
34 self.equity.last().copied().unwrap_or(self.initial_capital)
35 }
36
37 /// The trades already closed, in the order they closed.
38 pub fn closed_trades(&self) -> impl Iterator<Item = &Trade> {
39 self.trades.iter().filter(|t| !t.is_open())
40 }
41
42 /// The trades still open at the end of the run.
43 pub fn open_trades(&self) -> impl Iterator<Item = &Trade> {
44 self.trades.iter().filter(|t| t.is_open())
45 }
46}