Skip to main content

optionstratlib_backtest/backtesting/
metrics.rs

1/******************************************************************************
2   Author: Joaquín Béjar García
3   Email: jb@taunais.com
4   Date: 26/4/25
5******************************************************************************/
6
7use chrono::NaiveDateTime;
8use optionstratlib_core::model::Positive;
9use rust_decimal::Decimal;
10use serde::{Deserialize, Serialize};
11
12/// Represents core performance metrics applicable to various trading strategies.
13///
14/// This struct aggregates common financial metrics used to evaluate the
15/// effectiveness and risk profile of a trading strategy based on its historical
16/// or simulated performance.
17#[derive(Debug, Clone, Default, Serialize, Deserialize)]
18pub struct GeneralPerformanceMetrics {
19    // === Return Metrics ===
20    /// The total percentage return generated by the strategy over the entire period.
21    pub total_return: Decimal,
22    /// The geometric average amount of money earned by an investment each year over a given time period.
23    pub annualized_return: Decimal,
24
25    // === Risk Metrics ===
26    /// A statistical measure of the dispersion of returns for a given security or market index.
27    /// It represents the degree of variation of a trading price series over time.
28    /// Expressed as a positive decimal value. `None` if calculation is not possible (e.g., insufficient data).
29    pub volatility: Option<Positive>,
30    /// A measure of downside risk, focusing only on returns that fall below a minimum acceptable return (MAR),
31    /// typically the risk-free rate or zero. It quantifies the volatility of negative returns.
32    /// Expressed as a positive decimal value. `None` if calculation is not possible.
33    pub downside_deviation: Option<Positive>,
34
35    // === Risk-Adjusted Return Metrics ===
36    /// Measures the performance of an investment compared to a risk-free asset, after adjusting for its risk.
37    /// It is defined as the difference between the returns of the investment and the risk-free return,
38    /// divided by the standard deviation of the investment (volatility).
39    /// `None` if calculation is not possible (e.g., zero volatility or insufficient data).
40    pub sharpe_ratio: Option<Decimal>,
41    /// Similar to the Sharpe ratio, but it only penalizes returns falling below a user-specified target
42    /// or required rate of return (using downside deviation instead of total volatility).
43    /// `None` if calculation is not possible (e.g., zero downside deviation or insufficient data).
44    pub sortino_ratio: Option<Decimal>,
45    /// A risk-adjusted return metric based on maximum drawdown. It is calculated as the annualized return
46    /// divided by the absolute value of the maximum drawdown.
47    /// `None` if calculation is not possible (e.g., zero or positive maximum drawdown, insufficient data).
48    pub calmar_ratio: Option<Decimal>,
49
50    // === Win/Loss Metrics ===
51    /// The percentage of trades that resulted in a profit. Calculated as (Number of Winning Trades / Total Number of Trades).
52    /// `None` if there are no trades.
53    pub win_rate: Option<Decimal>,
54    /// The ratio of the total profit from winning trades to the total loss from losing trades.
55    /// Calculated as (Gross Profits / Gross Losses).
56    /// `None` if there are no losses.
57    pub profit_factor: Option<Decimal>,
58    /// The average profit amount for all winning trades.
59    /// `None` if there are no winning trades.
60    pub avg_gain: Option<Decimal>,
61    /// The average loss amount for all losing trades.
62    /// `None` if there are no losing trades.
63    pub avg_loss: Option<Decimal>,
64    /// The ratio of the average gain per trade to the average loss per trade.
65    /// Calculated as (Average Gain / Average Loss).
66    /// `None` if there are no losses or no gains.
67    pub gain_loss_ratio: Option<Decimal>,
68}
69
70/// Represents metrics specifically tailored for evaluating options trading strategies.
71///
72/// This struct encapsulates various performance and risk exposure metrics
73/// that are particularly relevant when analyzing strategies involving options contracts.
74#[derive(Debug, Clone, Default, Serialize, Deserialize)]
75pub struct OptionsSpecificMetrics {
76    /// The return generated based on the margin required to maintain the positions.
77    /// Calculated as `Total PnL / Maximum Margin Used`.
78    pub return_on_margin: Option<Decimal>,
79    /// The return generated based on the net premium received or paid for the options.
80    /// Calculated as `Total PnL / Net Premium`.
81    pub return_on_premium: Option<Decimal>,
82    /// The percentage of the initial potential profit (net premium for short strategies)
83    /// that was actually realized.
84    /// Calculated as `Actual PnL / Initial Potential Profit`. Relevant primarily for premium selling strategies.
85    pub premium_capture: Option<Decimal>,
86    /// The average sensitivity of the portfolio's value to changes in the underlying asset's price (Delta).
87    pub avg_delta_exposure: Option<Decimal>,
88    /// The average rate of change of the portfolio's Delta with respect to the underlying asset's price (Gamma).
89    pub avg_gamma_exposure: Option<Decimal>,
90    /// The average sensitivity of the portfolio's value to the passage of time (Theta).
91    /// Represents the daily time decay.
92    pub avg_theta_exposure: Option<Decimal>,
93    /// The average sensitivity of the portfolio's value to changes in the implied volatility of the underlying asset (Vega).
94    pub avg_vega_exposure: Option<Decimal>,
95    /// The average sensitivity of the portfolio's Delta to changes in the implied volatility of the underlying asset (Vanna).
96    pub avg_vanna_exposure: Option<Decimal>,
97    /// The average sensitivity of the portfolio's Vega to changes in the implied volatility of the underlying asset (Vomma).
98    pub avg_vomma_exposure: Option<Decimal>,
99    /// The average sensitivity of the portfolio's Vega to the passage of time (Veta).
100    pub avg_veta_exposure: Option<Decimal>,
101    /// The average sensitivity of the portfolio's Delta to the passage of time (Charm).
102    pub avg_charm_exposure: Option<Decimal>,
103    /// The average sensitivity of the portfolio's Gamma to the passage of time (Color).
104    pub avg_color_exposure: Option<Decimal>,
105    /// The percentage of trades or positions involving call options.
106    pub calls_percentage: Option<Decimal>,
107    /// The percentage of trades or positions involving put options.
108    pub puts_percentage: Option<Decimal>,
109    /// The percentage of trades or positions that are long options (bought).
110    pub long_percentage: Option<Decimal>,
111    /// The percentage of trades or positions that are short options (sold/written).
112    pub short_percentage: Option<Decimal>,
113}
114
115/// Represents metrics related to market conditions observed during a specific period, typically a backtest.
116///
117/// This struct captures counts of days categorized by different market trends (bull, bear, sideways)
118/// and volatility levels (high, low). It also stores the average market volatility over the period.
119/// The counts are represented using the `Positive` type, ensuring they are non-negative decimal values.
120#[derive(Debug, Clone, Default, Serialize, Deserialize)]
121pub struct MarketConditionMetrics {
122    /// The number of days identified as being in a bull market trend.
123    pub bull_market_days: Positive,
124    /// The number of days identified as being in a bear market trend.
125    pub bear_market_days: Positive,
126    /// The number of days identified as being in a sideways or range-bound market trend.
127    pub sideways_market_days: Positive,
128    /// The number of days identified as having high market volatility.
129    pub high_volatility_days: Positive,
130    /// The number of days identified as having low market volatility.
131    pub low_volatility_days: Positive,
132    /// The calculated average market volatility over the period. This might be `None` if volatility couldn't be calculated.
133    pub avg_market_volatility: Option<Decimal>,
134}
135
136/// Represents rolling window metrics calculated over a specific period.
137///
138/// This struct holds various performance metrics calculated using a sliding window approach,
139/// allowing for the analysis of how a strategy's performance characteristics change over time.
140#[derive(Debug, Clone, Serialize, Deserialize, Default)]
141pub struct RollingMetrics {
142    /// The size of the rolling window used for calculations.
143    /// This indicates the number of data points included in each rolling calculation.
144    /// It must be a positive value.
145    pub window_size: Positive,
146    /// A vector of timestamps corresponding to the end of each rolling window period.
147    pub timestamps: Vec<NaiveDateTime>,
148    /// A vector of rolling returns calculated for each window.
149    pub rolling_returns: Vec<Decimal>,
150    /// A vector of rolling volatility (standard deviation of returns) calculated for each window.
151    pub rolling_volatility: Vec<Decimal>,
152    /// A vector of rolling Sharpe ratios calculated for each window.
153    /// The Sharpe ratio measures risk-adjusted return, typically using volatility as the risk measure.
154    pub rolling_sharpe: Vec<Decimal>,
155    /// A vector of rolling Sortino ratios calculated for each window.
156    /// The Sortino ratio is similar to the Sharpe ratio but only considers downside volatility.
157    pub rolling_sortino: Vec<Decimal>,
158    /// A vector of rolling win rates calculated for each window.
159    /// The win rate represents the percentage of winning trades or periods within the window.
160    pub rolling_win_rate: Vec<Decimal>,
161}
162
163/// Represents the results of comparing a portfolio's performance against a specified benchmark.
164///
165/// This struct holds various statistical metrics commonly used in finance to evaluate
166/// how well a portfolio has performed relative to a market benchmark index.
167#[derive(Debug, Clone, Default, Serialize, Deserialize)]
168pub struct BenchmarkComparison {
169    /// The name or identifier of the benchmark used for comparison (e.g., "S&P 500").
170    pub benchmark_name: String,
171    /// The total return of the benchmark over the evaluated period.
172    pub benchmark_return: Decimal,
173    /// Alpha represents the excess return of the portfolio relative to the return of the benchmark,
174    /// after adjusting for risk (as measured by beta). A positive alpha suggests the portfolio
175    /// outperformed the benchmark on a risk-adjusted basis.
176    pub alpha: Decimal,
177    /// Beta measures the volatility or systematic risk of the portfolio in comparison to the benchmark.
178    /// A beta greater than 1 indicates the portfolio is more volatile than the benchmark, while a beta
179    /// less than 1 indicates less volatility.
180    pub beta: Decimal,
181    /// Correlation measures the degree to which the portfolio's returns move in relation to the benchmark's returns.
182    /// A value close to 1 indicates a strong positive relationship, -1 a strong negative relationship,
183    /// and 0 indicates no linear relationship.
184    pub correlation: Decimal,
185    /// Tracking Error measures the standard deviation of the difference between the portfolio's returns
186    /// and the benchmark's returns. It quantifies how closely the portfolio's performance follows the benchmark.
187    /// A lower tracking error indicates the portfolio is closely tracking the benchmark.
188    pub tracking_error: Decimal,
189    /// The Information Ratio measures the portfolio's risk-adjusted returns relative to the benchmark.
190    /// It is typically calculated as the portfolio's alpha divided by its tracking error.
191    /// A higher information ratio suggests better risk-adjusted performance relative to the benchmark.
192    /// This is optional as it cannot be calculated if the tracking error is zero.
193    pub information_ratio: Option<Decimal>,
194    /// Up Capture Ratio measures the portfolio's performance relative to the benchmark during periods
195    /// when the benchmark had positive returns. A value greater than 100 suggests the portfolio
196    /// outperformed the benchmark during up-market periods.
197    pub up_capture: Option<Decimal>,
198    /// Down Capture Ratio measures the portfolio's performance relative to the benchmark during periods
199    /// when the benchmark had negative returns. A value less than 100 suggests the portfolio
200    /// lost less than the benchmark during down-market periods.
201    pub down_capture: Option<Decimal>,
202}
203
204/// Represents a collection of advanced risk metrics for assessing investment performance,
205/// focusing on downside risk and tail events beyond simple standard deviation.
206#[derive(Debug, Clone, Default, Serialize, Deserialize)]
207pub struct AdvancedRiskMetrics {
208    /// Value at Risk (VaR) at the 95% confidence level.
209    /// Represents the maximum potential loss over a specific time period
210    /// with 95% confidence. `None` if not calculated.
211    pub value_at_risk_95: Option<Decimal>,
212    /// Value at Risk (VaR) at the 99% confidence level.
213    /// Represents the maximum potential loss over a specific time period
214    /// with 99% confidence. `None` if not calculated.
215    pub value_at_risk_99: Option<Decimal>,
216    /// Expected Shortfall (ES), also known as Conditional Value at Risk (CVaR).
217    /// Represents the expected loss given that the loss exceeds the VaR threshold
218    /// (typically calculated at the 95% or 99% level). `None` if not calculated.
219    pub expected_shortfall: Option<Decimal>,
220    /// The ratio of the 95th percentile gain to the absolute value of the 5th percentile loss.
221    /// Provides insight into the symmetry of the return distribution's tails. `None` if not calculated.
222    pub tail_ratio: Option<Decimal>,
223    /// The maximum number of consecutive periods (e.g., days, weeks) with negative returns.
224    pub max_consecutive_losses: usize,
225    /// The Ulcer Index, measuring the depth and duration of drawdowns from peak values.
226    /// Higher values indicate more significant and prolonged drawdowns. `None` if not calculated.
227    pub ulcer_index: Option<Decimal>,
228    /// The Pain Index, similar to the Ulcer Index, measuring the average depth and duration
229    /// of drawdowns. `None` if not calculated.
230    pub pain_index: Option<Decimal>,
231}