pub struct PortfolioMeasures {
r_p: f64,
r: f64,
beta_p: f64,
sigma_p: f64,
sigma_down: f64,
var: f64,
r_m: f64,
}
impl PortfolioMeasures {
#[must_use]
pub fn treynors_ratio(&self) -> f64 {
(self.r_p - self.r) / self.beta_p
}
#[must_use]
pub fn sharpe_ratio(&self) -> f64 {
(self.r_p - self.r) / self.sigma_p
}
#[must_use]
pub fn sortino_ratio(&self) -> f64 {
(self.r_p - self.r) / self.sigma_down
}
#[must_use]
pub fn burke_ratio(&self, drawdowns: &[f64]) -> f64 {
let ss_drawdowns = drawdowns.iter().map(|x| x.powi(2)).sum::<f64>();
(self.r_p - self.r) / ss_drawdowns
}
#[must_use]
pub fn return_on_var(&self) -> f64 {
self.r_p / self.var
}
#[must_use]
pub fn jensens_alpha(&self) -> f64 {
self.r_p - (self.r + self.beta_p * (self.r_m - self.r))
}
}
#[cfg(test)]
mod tests_risk_reward {
use super::*;
use RustQuant_utils::{assert_approx_equal, RUSTQUANT_EPSILON as EPS};
static PORTFOLIO: PortfolioMeasures = PortfolioMeasures {
r_p: 0.12,
r: 0.05,
beta_p: 1.2,
sigma_p: 0.2,
sigma_down: 0.1,
var: 0.15,
r_m: 0.1,
};
#[test]
fn test_treynors_ratio() {
assert_approx_equal!(PORTFOLIO.treynors_ratio(), (0.12 - 0.05) / 1.2, EPS);
}
#[test]
fn test_sharpe_ratio() {
assert_approx_equal!(PORTFOLIO.sharpe_ratio(), (0.12 - 0.05) / 0.2, EPS);
}
#[test]
fn test_sortino_ratio() {
assert_approx_equal!(PORTFOLIO.sortino_ratio(), (0.12 - 0.05) / 0.1, EPS);
}
#[test]
fn test_burke_ratio() {
let drawdowns = vec![0.05, 0.10, 0.20];
let ss_drawdowns = drawdowns.iter().map(|x| x * x).sum::<f64>();
assert_approx_equal!(
PORTFOLIO.burke_ratio(&drawdowns),
(0.12 - 0.05) / ss_drawdowns,
EPS
);
}
#[test]
fn test_return_on_var() {
assert_approx_equal!(PORTFOLIO.return_on_var(), 0.12 / 0.15, EPS);
}
#[test]
fn test_jensens_alpha() {
assert_approx_equal!(
PORTFOLIO.jensens_alpha(),
0.12 - (0.05 + 1.2 * (0.1 - 0.05)),
EPS
);
}
}