Skip to main content

rustrade_backtest/
metrics.rs

1//! Trade-level outcome captured during the replay loop.
2//!
3//! [`BacktestResult`](crate::BacktestResult) aggregates these into
4//! summary statistics. Closing more than one position per trade (e.g.
5//! flipping from long to short) emits multiple [`TradeOutcome`]s — one
6//! per closed quantity.
7
8use chrono::{DateTime, Utc};
9use rustrade_core::Side;
10use serde::{Deserialize, Serialize};
11
12/// A single realised trade — open → close — recorded by the engine.
13///
14/// `gross_pnl` is in quote currency, before `fee`. `net_pnl = gross_pnl
15/// - fee`. The "side" is the side of the *closing* fill (so a long
16/// position is closed with `Side::Sell`).
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
18pub struct TradeOutcome {
19    /// Symbol the trade was on.
20    pub symbol: String,
21    /// Side of the *closing* fill (`Sell` to close a long, etc.).
22    pub close_side: Side,
23    /// Closed quantity.
24    pub qty: f64,
25    /// Average entry price of the closed quantity.
26    pub entry_price: f64,
27    /// Fill price of the close.
28    pub exit_price: f64,
29    /// Gross PnL on the closed quantity, in quote currency, before fees.
30    pub gross_pnl: f64,
31    /// Fee charged to this close, in quote currency.
32    pub fee: f64,
33    /// When the close fill occurred.
34    pub closed_at: DateTime<Utc>,
35}
36
37impl TradeOutcome {
38    /// Net of fees.
39    pub fn net_pnl(&self) -> f64 {
40        self.gross_pnl - self.fee
41    }
42
43    /// Win (`true`) / loss / breakeven on net PnL.
44    pub fn outcome(&self) -> Outcome {
45        let n = self.net_pnl();
46        if n > 0.0 {
47            Outcome::Win
48        } else if n < 0.0 {
49            Outcome::Loss
50        } else {
51            Outcome::Breakeven
52        }
53    }
54}
55
56/// Trade classification on net PnL — used by metric aggregation.
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub enum Outcome {
59    /// Net PnL > 0.
60    Win,
61    /// Net PnL < 0.
62    Loss,
63    /// Net PnL == 0.
64    Breakeven,
65}