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}