Skip to main content

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}