1use crate::{covariance, mean, sd, variance};
2
3pub struct Beta(f64);
4
5impl Beta {
24 pub fn new(series: &[f64], market: &[f64]) -> Self {
25 assert_eq!(
27 series.len(),
28 market.len(),
29 "series[{}] and market[{}] must have the same mumber of data points",
30 series.len(),
31 market.len()
32 );
33
34 if series.is_empty() {
35 return Self(0.0);
36 }
37
38 let beta = covariance!(series, market) / variance!(market);
39 Self(beta)
40 }
41
42 pub const fn is_one(&self) -> bool {
45 (self.0 - 1.0).abs() < 1e-6 }
50
51 pub const fn is_negative(&self) -> bool {
53 self.0.is_sign_negative()
54 }
55
56 pub const fn is_positive(&self) -> bool {
57 self.0.is_sign_positive()
58 }
59
60 pub const fn value(&self) -> f64 {
61 self.0
62 }
63}
64
65impl From<f64> for Beta {
66 fn from(value: f64) -> Self {
67 Self(value)
68 }
69}
70
71impl From<Beta> for f64 {
72 fn from(value: Beta) -> Self {
73 value.0
74 }
75}
76
77pub fn sharpe(series: &[f64], rf: f64) -> f64 {
106 internal_sharpe(Some(series), rf, None, None)
107}
108
109fn internal_sharpe(series: Option<&[f64]>, rf: f64, rp: Option<f64>, sd: Option<f64>) -> f64 {
110 let portfolio_ret: f64;
111 let std_div: f64;
112 if let Some(series) = series {
113 portfolio_ret = mean!(series);
114 std_div = sd!(variance!(series, portfolio_ret));
115 } else {
116 assert!(rp.is_some(), "");
117 assert!(sd.is_some(), "");
118 portfolio_ret = rp.unwrap();
119 std_div = sd.unwrap();
120 }
121
122 if std_div < f64::EPSILON {
123 return 0.0;
124 }
125
126 (portfolio_ret - rf) / std_div
127}
128
129#[macro_export]
155macro_rules! sharpe {
156 ($series: expr, $rf: expr) => {
157 $crate::ratios::risk_metrics::internal_sharpe(Some($series), $rf, None, None)
158 };
159 ($rp: expr, $rf: expr, $sd: expr) => {
160 $crate::ratios::risk_metrics::internal_sharpe(None, $rf, Some($rp), Some($sd))
161 };
162}
163
164pub fn downside_deviation(x: &[f64], mar: f64) -> f64 {
166 let no_of_year = x.len() as f64;
167 let result = x
168 .iter()
169 .map(|xi| {
170 let diff = xi - mar;
171 if diff.is_sign_negative() {
172 diff * diff
173 } else {
174 0.0
175 }
176 })
177 .sum::<f64>();
178 let re = result / no_of_year;
179 re.sqrt()
180}
181
182#[cfg(test)]
183mod test {
184 use crate::F64Extras;
185 use crate::ratios::risk_metrics::sharpe;
186
187 use super::{Beta, downside_deviation};
188
189 const NIFTY_50: [f64; 19] = [
191 0.006960765170238605,
192 -0.002972058760474052,
193 -0.002727647317136396,
194 -0.0014496220164682432,
195 -0.01009536415844993,
196 -0.007479613285493556,
197 0.004164153963733427,
198 -0.0022469428853927357,
199 -0.0025921184600641006,
200 0.0011201764399651254,
201 -0.0042363247573810724,
202 -0.013796877137441129,
203 -0.002972357079163777,
204 0.0052628596094604,
205 -0.009539381186706124,
206 0.005060152863462813,
207 0.006647346488174181,
208 0.003004819548983437,
209 -0.003865234077404724,
210 ];
211 const ITC: [f64; 19] = [
213 -0.03792780822846855,
214 -0.0009997348459663343,
215 -0.02073202469727801,
216 -0.0035042128799998833,
217 -0.001025698054907989,
218 -0.011000239733699091,
219 0.003707530300019869,
220 -0.0109337303722129,
221 0.000149316018136497,
222 -0.016579515057863883,
223 0.012150627231730476,
224 -0.02070828665292772,
225 -0.00475024862225316,
226 0.00030797372763436743,
227 -0.004463668706180607,
228 -0.014687699127850123,
229 0.007845658409425468,
230 -0.007940199219514013,
231 0.011142447553835816,
232 ];
233 #[test]
234 fn beta_t() {
235 let beta: f64 = Beta::new(&ITC, &NIFTY_50).into();
236 assert_eq!(beta, -0.13098715705340794);
237 }
238
239 #[test]
240 fn sharpe_t() {
241 let protfolio_return = 0.18; let rf = 0.03; let annaulized_sd = 0.12;
245
246 let s1 = sharpe!(&ITC, rf);
247 let s3 = sharpe(&ITC, rf);
248 let s2 = sharpe!(protfolio_return, rf, annaulized_sd);
249 assert_eq!(s2, 1.25);
250 assert_eq!(s1, -3.024907069875915);
251 assert_eq!(s3, -3.024907069875915);
252 }
253
254 #[test]
256 fn downside_t() {
257 let _years = [2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019];
259
260 let returns = [-0.02, 0.16, 0.31, 0.17, -0.11, 0.21, 0.26, -0.03, 0.38];
261 let mar = 0.01;
262
263 let dd = downside_deviation(&returns, mar);
264 assert_eq!(dd.round_4(), 0.0433)
265 }
266}