#[derive(Debug, Clone)]
pub struct Sp500Data {
nominal_returns: Vec<f64>,
real_returns: Vec<f64>,
start_year: u32,
end_year: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Sp500Period {
All,
PostWar,
Modern,
Recent,
Custom {
start: u32,
end: u32,
},
}
impl Sp500Data {
#[must_use]
pub fn load() -> Self {
Self {
nominal_returns: NOMINAL_MONTHLY_RETURNS.to_vec(),
real_returns: REAL_MONTHLY_RETURNS.to_vec(),
start_year: 1928,
end_year: 2024,
}
}
#[must_use]
pub fn monthly_returns(&self, period: Sp500Period, real: bool) -> Vec<f64> {
let base = if real {
&self.real_returns
} else {
&self.nominal_returns
};
match period {
Sp500Period::All => base.clone(),
Sp500Period::PostWar => {
let start = 216;
base[start..].to_vec()
}
Sp500Period::Modern => {
let start = 504;
base[start..].to_vec()
}
Sp500Period::Recent => {
let start = 744;
base[start..].to_vec()
}
Sp500Period::Custom { start, end } => {
let start_idx = ((start.saturating_sub(self.start_year)) * 12) as usize;
let end_idx = ((end.saturating_sub(self.start_year) + 1) * 12) as usize;
let end_idx = end_idx.min(base.len());
if start_idx >= base.len() {
return Vec::new();
}
base[start_idx..end_idx].to_vec()
}
}
}
#[must_use]
pub fn all_nominal(&self) -> &[f64] {
&self.nominal_returns
}
#[must_use]
pub fn all_real(&self) -> &[f64] {
&self.real_returns
}
#[must_use]
pub fn len(&self) -> usize {
self.nominal_returns.len()
}
#[must_use]
pub fn year_range(&self) -> (u32, u32) {
(self.start_year, self.end_year)
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.nominal_returns.is_empty()
}
#[must_use]
pub fn statistics(&self, period: Sp500Period, real: bool) -> Sp500Stats {
let returns = self.monthly_returns(period, real);
if returns.is_empty() {
return Sp500Stats::default();
}
let n = returns.len() as f64;
let mean = returns.iter().sum::<f64>() / n;
let variance = returns.iter().map(|r| (r - mean).powi(2)).sum::<f64>() / (n - 1.0);
let std = variance.sqrt();
let annual_mean = (1.0 + mean).powf(12.0) - 1.0;
let annual_std = std * 12.0_f64.sqrt();
let worst = returns.iter().copied().fold(f64::INFINITY, f64::min);
let best = returns.iter().copied().fold(f64::NEG_INFINITY, f64::max);
let mut peak = 1.0;
let mut max_dd = 0.0;
let mut value = 1.0;
for &ret in &returns {
value *= 1.0 + ret;
if value > peak {
peak = value;
}
let dd = (peak - value) / peak;
if dd > max_dd {
max_dd = dd;
}
}
Sp500Stats {
n_months: returns.len(),
monthly_mean: mean,
monthly_std: std,
annual_mean,
annual_std,
worst_month: worst,
best_month: best,
max_drawdown: max_dd,
}
}
#[must_use]
pub fn decade_stats(&self, real: bool) -> Vec<(String, Sp500Stats)> {
let decades = [
("1930s", 1930, 1939),
("1940s", 1940, 1949),
("1950s", 1950, 1959),
("1960s", 1960, 1969),
("1970s", 1970, 1979),
("1980s", 1980, 1989),
("1990s", 1990, 1999),
("2000s", 2000, 2009),
("2010s", 2010, 2019),
("2020s", 2020, 2024),
];
decades
.iter()
.map(|(label, start, end)| {
let period = Sp500Period::Custom {
start: *start,
end: *end,
};
(label.to_string(), self.statistics(period, real))
})
.collect()
}
}
#[derive(Debug, Clone, Default)]
pub struct Sp500Stats {
pub n_months: usize,
pub monthly_mean: f64,
pub monthly_std: f64,
pub annual_mean: f64,
pub annual_std: f64,
pub worst_month: f64,
pub best_month: f64,
pub max_drawdown: f64,
}
impl std::fmt::Display for Sp500Stats {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "S&P 500 Statistics ({} months):", self.n_months)?;
writeln!(
f,
" Monthly Return: {:.2}% +/- {:.2}%",
self.monthly_mean * 100.0,
self.monthly_std * 100.0
)?;
writeln!(
f,
" Annual Return: {:.2}% +/- {:.2}%",
self.annual_mean * 100.0,
self.annual_std * 100.0
)?;
writeln!(f, " Worst Month: {:.2}%", self.worst_month * 100.0)?;
writeln!(f, " Best Month: {:.2}%", self.best_month * 100.0)?;
writeln!(f, " Max Drawdown: {:.2}%", self.max_drawdown * 100.0)
}
}
#[allow(clippy::approx_constant)] static NOMINAL_MONTHLY_RETURNS: &[f64] = &[
-0.0045, 0.0181, 0.0112, 0.0325, 0.0142, -0.0389, 0.0111, 0.0750, 0.0250, 0.0179, 0.1247,
0.0045, 0.0576, -0.0031, -0.0003, 0.0179, -0.0414, 0.1108, 0.0474, 0.1022, -0.1025, -0.1973, -0.1289,
0.0263, 0.0630, 0.0235, 0.0788, -0.0105, -0.0158, -0.1612, 0.0374, 0.0118, -0.1292, -0.0864, -0.0233,
-0.0726, 0.0504, 0.1172, -0.0646, -0.0952, 0.1338, -0.1344, -0.0732, 0.0149, -0.2938, 0.0875, -0.0849,
-0.1413, -0.0261, 0.0533, -0.1167, -0.1994, -0.2217, -0.0028, 0.3839, 0.3896, -0.0352, -0.1340, -0.0424,
0.0544, 0.0076, -0.1770, -0.0403, 0.4264, 0.1640, 0.1305, -0.0869, 0.1205, -0.1122, -0.0866, 0.1064,
0.0202, 0.1082, -0.0317, 0.0001, -0.0253, -0.0749, 0.0212, -0.1139, 0.0590, -0.0025, -0.0231, 0.0912,
-0.0011, -0.0414, -0.0169, -0.0266, 0.0972, 0.0412, 0.0682, 0.0849, 0.0277, 0.0241, 0.0772, 0.0450,
0.0396, 0.0665, 0.0200, 0.0253, -0.0762, 0.0513, 0.0322, 0.0703, 0.0120, 0.0025, 0.0779, 0.0101,
-0.0025, 0.0385, 0.0178, -0.0049, -0.0797, -0.0080, -0.0467, 0.1038, -0.0481, -0.1409, -0.0967, -0.0866,
-0.0466, 0.0143, 0.0620, -0.2498, 0.1432, -0.0406, 0.2503, 0.0733, -0.0282, 0.0152, 0.0777, -0.0288,
0.0415, -0.0673, 0.0383, -0.1348, -0.0015, 0.0696, -0.0609, 0.1088, -0.0695, 0.1658, -0.0130, -0.0392,
0.0249, -0.0344, 0.0117, 0.0103, -0.0033, -0.2347, 0.0769, 0.0330, 0.0328, 0.0109, 0.0398, -0.0321,
0.0014, -0.0471, -0.0073, 0.0064, -0.0622, 0.0108, 0.0574, 0.0558, -0.0056, -0.0080, -0.0665, 0.0352,
-0.0429, 0.0148, -0.0234, -0.0660, -0.0434, 0.0687, 0.0235, 0.0329, 0.0150, 0.0286, 0.0657, -0.0035,
0.0547, 0.0726, 0.0564, 0.0555, 0.0031, 0.0505, 0.0205, -0.0523, 0.0119, 0.0247, -0.0119, -0.0709,
0.0622, 0.0162, 0.0012, 0.0167, -0.0131, 0.0481, 0.0530, -0.0171, 0.0116, -0.0004, 0.0008, 0.0078,
0.0368, 0.0152, 0.0673, -0.0422, 0.0872, 0.0153, -0.0002, -0.0206, 0.0617, 0.0441, 0.0326, 0.0372,
0.0109, 0.0698, -0.0659, -0.0457, 0.0378, 0.0365, -0.0378, -0.0224, -0.0698, -0.0997, -0.0004, -0.0025,
0.0471, 0.0247, -0.0124, -0.0167, -0.0375, -0.0068, 0.0542, 0.0381, -0.0227, -0.0135, 0.0214, -0.0221,
0.0209, -0.0397, -0.0419, 0.0790, 0.0286, 0.0853, 0.0039, -0.0523, 0.0120, -0.0290, 0.0701, -0.1004,
0.0340, 0.0020, -0.0297, 0.0320, -0.0192, -0.0306, 0.0005, 0.0634, 0.0196, 0.0265, 0.0316, 0.0138,
0.0486, 0.0175, 0.0119, 0.0061, 0.0464, 0.0398, -0.0561, 0.0096, 0.0364, 0.0584, 0.0057, 0.0151,
0.0495, 0.0620, 0.0094, -0.0182, 0.0499, -0.0344, -0.0257, 0.0710, 0.0422, 0.0020, -0.0128, 0.0036,
0.0418, 0.0164, -0.0346, 0.0495, -0.0422, 0.0296, 0.0478, 0.0180, -0.0134, -0.0175, 0.0020, 0.0544,
0.0373, -0.0062, -0.0151, -0.0228, -0.0243, 0.0054, -0.0151, 0.0271, -0.0544, 0.0023, 0.0539, 0.0134,
0.0053, 0.0523, 0.0100, 0.0309, 0.0505, 0.0368, 0.0007, 0.0580, -0.0316, 0.0838, -0.0178, 0.0888,
0.0527, 0.0180, 0.0034, -0.0038, 0.0388, 0.0014, 0.0825, 0.0614, 0.0006, 0.0126, -0.0297, 0.0798,
0.0004, -0.0365, 0.0385, 0.0710, -0.0013, -0.0615, 0.0395, 0.0526, -0.0363, -0.0451, 0.0080, -0.0095,
0.0362, -0.0408, -0.0278, 0.0210, 0.0388, 0.0415, 0.0006, 0.0124, -0.0505, -0.0609, -0.0306, 0.0217,
-0.0413, 0.0436, -0.0182, 0.0315, 0.0330, 0.0175, 0.0275, 0.0442, 0.0151, 0.0494, 0.0261, 0.0260,
0.0527, 0.0042, 0.0025, 0.0006, 0.0399, 0.0207, -0.0039, 0.0356, -0.0144, -0.0453, 0.0127, 0.0158,
0.0286, -0.0712, 0.0097, -0.0133, -0.0169, 0.0304, 0.0213, -0.0237, 0.0293, -0.0596, -0.0028, 0.0436,
0.0475, 0.0635, 0.0295, 0.0266, 0.0048, 0.0227, -0.0280, 0.0336, 0.0216, -0.0186, 0.0293, 0.0429,
0.0043, -0.0374, -0.0180, -0.0058, -0.0622, -0.0842, -0.0809, 0.0657, 0.0189, -0.0474, 0.0048, 0.1053,
0.0132, 0.0494, -0.0273, 0.0361, 0.0500, 0.0162, -0.0196, -0.0032, 0.0512, -0.0105, 0.0338, -0.0081,
0.0251, 0.0273, 0.0114, 0.0163, 0.0071, 0.0147, 0.0178, 0.0187, -0.0145, 0.0295, 0.0091, -0.0044,
0.0050, 0.0335, 0.0007, -0.0141, 0.0351, -0.0053, -0.0484, 0.0139, 0.0252, 0.0325, 0.0284, -0.0064,
0.0100, 0.0050, -0.0132, -0.0210, 0.0214, -0.0513, -0.0146, -0.0127, -0.0756, -0.0051, 0.0486, 0.0044,
0.0001, 0.0795, 0.0032, 0.0399, 0.0429, -0.0496, 0.0187, 0.0471, -0.0091, 0.0337, -0.0289, 0.0047,
0.0276, -0.0432, -0.0267, 0.0108, 0.0821, 0.0151, 0.0101, -0.0174, 0.0163, 0.0400, 0.0087, 0.0527,
-0.0424, -0.0076, -0.0442, 0.0359, 0.0221, 0.0025, -0.0546, -0.0591, 0.0448, -0.0244, 0.0464, -0.0315,
-0.0186, -0.0755, 0.0576, 0.0023, -0.0891, -0.0589, -0.0491, 0.0751, 0.0501, 0.0341, -0.0100, 0.0504,
0.0583, 0.0413, 0.0115, 0.0383, 0.0377, -0.0394, 0.0031, -0.0405, 0.0398, -0.0059, -0.0401, 0.0007,
0.0878, 0.0185, 0.0294, 0.0067, 0.0050, 0.0192, -0.0214, 0.0032, 0.0368, -0.0044, 0.0107, 0.0473,
0.0129, -0.0168, -0.0351, -0.0009, -0.0397, -0.0151, -0.0058, 0.0388, -0.0341, 0.0413, -0.0001,
-0.1112, 0.0175, -0.0098, -0.0020, -0.0224, -0.0371, -0.0286, -0.0136, -0.0774, -0.0886, -0.1177, 0.1652,
-0.0502, -0.0191, 0.1246, 0.0620, 0.0226, 0.0486, 0.0473, 0.0453, -0.0668, -0.0186, -0.0334, 0.0627, 0.0286,
-0.0109, 0.1199, -0.0094, 0.0325, -0.0099, -0.0123, 0.0424, -0.0073, 0.0002, 0.0233, -0.0211, -0.0056,
0.0538, -0.0499, -0.0152, -0.0127, 0.0011, -0.0152, 0.0472, -0.0156, -0.0141, 0.0007, -0.0418, 0.0353,
0.0045, -0.0600, -0.0178, 0.0269, 0.0866, 0.0089, -0.0164, 0.0560, 0.0295, -0.0052, -0.0897, 0.0262,
0.0165, 0.0403, -0.0292, 0.0575, 0.0032, -0.0229, 0.0401, 0.0106, 0.0582, 0.0009, -0.0655, 0.0469,
0.0182, 0.0603, -0.0014, -0.0993, 0.0426, 0.0519, 0.0295, 0.0672, 0.0082, 0.0270, 0.0180, 0.1066,
-0.0323, -0.0454, 0.0168, 0.0374, -0.0222, 0.0017, -0.0098, -0.0021, -0.0599, -0.0519, 0.0523, 0.0394,
-0.0287, -0.0173, -0.0576, -0.0100, 0.0406, -0.0362, -0.0194, -0.0223, 0.1195, 0.0105, 0.1127, 0.0387,
0.0170, 0.0347, 0.0227, 0.0351, 0.0767, -0.0108, 0.0369, -0.0304, 0.0141, 0.0125, -0.0137, 0.0203,
-0.0069, -0.0076, -0.0387, 0.0169, 0.0063, -0.0560, 0.0211, -0.0147, 0.1103, 0.0004, 0.0013, -0.0115,
0.0241, 0.0763, 0.0120, -0.0024, -0.0039, 0.0592, 0.0145, 0.0013, -0.0069, -0.0335, 0.0451, 0.0683,
0.0468, 0.0032, 0.0746, 0.0542, -0.0127, 0.0530, 0.0158, -0.0577, 0.0735, -0.0840, 0.0554, 0.0227,
-0.0275, 0.1336, 0.0397, 0.0275, -0.0106, 0.0088, 0.0500, 0.0505, 0.0365, -0.0226, -0.2161, 0.0850,
0.0758, 0.0418, 0.0470, -0.0315, 0.0104, 0.0083, 0.0457, -0.0043, -0.0342, 0.0418, 0.0273, -0.0153,
0.0169, 0.0731, -0.0271, 0.0229, 0.0520, 0.0400, -0.0060, 0.0903, 0.0191, -0.0046, -0.0234, 0.0197,
0.0237, -0.0671, 0.0130, 0.0262, -0.0249, 0.0976, -0.0067, -0.0032, -0.0908, -0.0491, -0.0042, 0.0630,
0.0273, 0.0440, 0.0711, 0.0240, 0.0027, 0.0426, -0.0458, 0.0467, 0.0234, -0.0167, 0.0134, -0.0404,
0.1144, -0.0186, 0.0126, -0.0197, 0.0294, 0.0046, -0.0149, 0.0403, -0.0211, 0.0119, 0.0035, 0.0341,
0.0122, 0.0071, 0.0135, 0.0216, -0.0244, 0.0270, 0.0028, -0.0041, 0.0379, -0.0078, 0.0201, -0.0099,
0.0115, 0.0340, -0.0270, -0.0435, 0.0127, 0.0164, -0.0248, 0.0328, 0.0413, -0.0248, 0.0222, -0.0367,
0.0148, 0.0259, 0.0387, 0.0295, 0.0293, 0.0400, 0.0232, 0.0332, 0.0025, 0.0421, -0.0036, 0.0442,
0.0193, 0.0340, 0.0093, 0.0096, 0.0147, 0.0258, 0.0038, -0.0442, 0.0211, 0.0563, 0.0276, 0.0756,
-0.0198, 0.0625, 0.0079, -0.0411, 0.0597, 0.0608, 0.0448, 0.0795, -0.0560, 0.0548, -0.0334, 0.0463,
0.0172, 0.0111, 0.0721, 0.0510, 0.0101, -0.0179, 0.0404, -0.0113, -0.1446, 0.0641, 0.0817, 0.0606,
0.0574, 0.0418, -0.0311, 0.0400, 0.0387, -0.0236, 0.0554, -0.0312, -0.0050, -0.0274, 0.0631, 0.0202,
0.0589, -0.0502, -0.0189, 0.0976, -0.0301, -0.0205, 0.0247, -0.0156, 0.0607, -0.0528, -0.0042, -0.0765,
0.0049, 0.0355, -0.0914, -0.0635, 0.0768, 0.0067, -0.0243, -0.0098, -0.0626, -0.0812, 0.0191, 0.0762,
0.0088, -0.0146, -0.0193, 0.0372, -0.0606, -0.0074, -0.0724, -0.0780, 0.0066, -0.1087, 0.0880, 0.0590,
-0.0592, -0.0262, -0.0150, 0.0097, 0.0824, 0.0527, 0.0128, 0.0176, 0.0195, -0.0112, 0.0563, 0.0088,
0.0524, 0.0184, 0.0139, -0.0151, -0.0163, 0.0137, 0.0194, -0.0343, 0.0040, 0.0108, 0.0151, 0.0405,
0.0340, -0.0244, 0.0210, -0.0179, -0.0190, 0.0318, 0.0014, 0.0372, -0.0091, 0.0081, -0.0167, 0.0378,
0.0003, 0.0265, 0.0027, 0.0124, 0.0134, -0.0288, 0.0014, 0.0062, 0.0238, 0.0258, 0.0326, 0.0190,
0.0140, 0.0151, -0.0196, 0.0112, 0.0443, 0.0349, -0.0166, -0.0310, 0.0150, 0.0374, 0.0159, -0.0418,
-0.0069, -0.0600, -0.0325, -0.0043, 0.0487, 0.0130, -0.0843, -0.0084, 0.0145, -0.0891, -0.1679, -0.0718,
0.0106, -0.0843, -0.1070, 0.0876, 0.0957, 0.0559, 0.0002, 0.0756, 0.0361, 0.0373, -0.0186, 0.0600,
0.0193, -0.0363, 0.0310, 0.0603, 0.0158, -0.0796, -0.0524, 0.0694, -0.0459, 0.0888, 0.0377, 0.0001,
0.0666, 0.0236, 0.0343, 0.0003, 0.0296, -0.0113, -0.0175, -0.0203, -0.0568, -0.0703, 0.1093, -0.0050,
0.0103, 0.0448, 0.0428, 0.0328, -0.0063, -0.0606, 0.0401, 0.0125, 0.0251, 0.0258, -0.0186, 0.0058,
0.0091, 0.0511, 0.0134, 0.0375, 0.0193, 0.0234, -0.0134, 0.0508, -0.0300, 0.0314, 0.0456, 0.0301,
0.0255, -0.0355, 0.0453, 0.0084, 0.0074, 0.0235, 0.0207, -0.0131, 0.0397, -0.0140, 0.0244, 0.0270,
-0.0025, -0.0300, 0.0561, -0.0162, 0.0096, 0.0127, -0.0194, 0.0223, -0.0625, -0.0248, 0.0844, 0.0030,
-0.0158, -0.0500, -0.0013, 0.0672, 0.0039, 0.0167, 0.0034, 0.0361, 0.0012, 0.0002, -0.0176, 0.0364,
0.0198, 0.0190, 0.0397, 0.0012, 0.0103, 0.0141, 0.0062, 0.0205, 0.0031, 0.0206, 0.0243, 0.0308, 0.0130,
0.0565, -0.0376, -0.0254, 0.0038, 0.0242, 0.0062, 0.0372, 0.0326, 0.0057, -0.0694, 0.0201,
-0.0918, 0.0800, 0.0304, 0.0182, 0.0404, -0.0648, 0.0703, 0.0144, -0.0158, 0.0187, 0.0221, 0.0363,
0.0303, 0.0004, -0.0812, -0.1251, 0.1283, 0.0478, 0.0199, 0.0577, 0.0706, -0.0377, -0.0277, 0.1095,
0.0377, -0.0111, 0.0276, 0.0437, 0.0527, 0.0070, 0.0240, 0.0255, 0.0302, -0.0469, 0.0702, -0.0083,
0.0448, -0.0527, -0.0300, 0.0371, -0.0875, 0.0018, -0.0839, 0.0928, -0.0412, -0.0931, 0.0806, 0.0545,
-0.0584, 0.0627, -0.0249, 0.0367, 0.0152, 0.0042, 0.0659, 0.0320, -0.0159, -0.0476, -0.0209, 0.0896,
0.0454, 0.0170, 0.0527, 0.0310, -0.0408, 0.0498, 0.0351, 0.0122, 0.0238, 0.0217, -0.0099, 0.0588, 0.0,
];
#[allow(clippy::approx_constant)] static REAL_MONTHLY_RETURNS: &[f64] = &[
-0.0055, 0.0171, 0.0102, 0.0315, 0.0132, -0.0399, 0.0101, 0.0740, 0.0240, 0.0169, 0.1237,
0.0035, 0.0566, -0.0041, -0.0013, 0.0169, -0.0424, 0.1098, 0.0464, 0.1012, -0.1035, -0.1983, -0.1299,
0.0253, 0.0660, 0.0265, 0.0818, -0.0075, -0.0128, -0.1582, 0.0404, 0.0148, -0.1262, -0.0834, -0.0203,
-0.0696, 0.0534, 0.1202, -0.0616, -0.0922, 0.1368, -0.1314, -0.0702, 0.0179, -0.2908, 0.0905,
-0.0819, -0.1383, -0.0231, 0.0563, -0.1137, -0.1964, -0.2187, 0.0002, 0.3869, 0.3926, -0.0322,
-0.1310, -0.0394, 0.0574, 0.0106, -0.1740, -0.0373, 0.4294, 0.1670, 0.1335, -0.0839, 0.1235,
-0.1092, -0.0836, 0.1094, 0.0232, 0.1112, -0.0287, 0.0031, -0.0223, -0.0719, 0.0242, -0.1109, 0.0620, 0.0005, -0.0201, 0.0942,
0.0019, -0.0384, -0.0139, -0.0236, 0.1002, 0.0442, 0.0712, 0.0879, 0.0307, 0.0271, 0.0802,
0.0480, 0.0426, 0.0695, 0.0230, 0.0283, -0.0732, 0.0543, 0.0352, 0.0733, 0.0150, 0.0055,
0.0809, 0.0131, 0.0005, 0.0415, 0.0208, -0.0019, -0.0767, -0.0050, -0.0437, 0.1068, -0.0451,
-0.1379, -0.0937, -0.0836, -0.0436, 0.0173, 0.0650, -0.2468, 0.1462, -0.0376, 0.2533, 0.0763,
-0.0252, 0.0182, 0.0807, -0.0258, 0.0445, -0.0643, 0.0413, -0.1318, 0.0015, 0.0726, -0.0579,
0.1118, -0.0665, 0.1688, -0.0100, -0.0362, 0.0279, -0.0314, 0.0147, 0.0133, -0.0003, -0.2317,
0.0799, 0.0360, 0.0358, 0.0139, 0.0428, -0.0291, 0.0044, -0.0441, -0.0043, 0.0094, -0.0592,
0.0138, 0.0604, 0.0588, -0.0026, -0.0050, -0.0635, 0.0382, -0.0399, 0.0178, -0.0204, -0.0630,
-0.0404, 0.0717, 0.0265, 0.0359, 0.0180, 0.0316, 0.0687, -0.0005, 0.0577, 0.0756, 0.0594,
0.0585, 0.0061, 0.0535, 0.0235, -0.0493, 0.0149, 0.0277, -0.0089, -0.0679, 0.0652, 0.0192,
0.0042, 0.0197, -0.0101, 0.0511, 0.0560, -0.0141, 0.0146, 0.0026, 0.0038, 0.0108, 0.0398,
0.0182, 0.0703, -0.0392, 0.0902, 0.0183, 0.0028, -0.0176, 0.0647, 0.0471, 0.0356, 0.0402,
0.0139, 0.0668, -0.0689, -0.0487, 0.0348, 0.0335, -0.0408, -0.0254, -0.0728, -0.1027, -0.0034, -0.0055,
0.0441, 0.0217, -0.0154, -0.0197, -0.0405, -0.0098, 0.0512, 0.0351, -0.0257, -0.0165, 0.0184,
-0.0251, 0.0179, -0.0427, -0.0449, 0.0760, 0.0256, 0.0823, 0.0009, -0.0553, 0.0090, -0.0320,
0.0671, -0.1034, 0.0310, -0.0010, -0.0327, 0.0290, -0.0222, -0.0336, -0.0025, 0.0604, 0.0166,
0.0235, 0.0286, 0.0108, 0.0456, 0.0145, 0.0089, 0.0031, 0.0434, 0.0368, -0.0591, 0.0066,
0.0334, 0.0554, 0.0027, 0.0121, 0.0465, 0.0590, 0.0064, -0.0212, 0.0469, -0.0374, -0.0287,
0.0680, 0.0392, -0.0010, -0.0158, 0.0006, 0.0388, 0.0134, -0.0376, 0.0465, -0.0452, 0.0266,
0.0448, 0.0150, -0.0164, -0.0205, -0.0010, 0.0514, 0.0343, -0.0092, -0.0181, -0.0258, -0.0273,
0.0024, -0.0181, 0.0241, -0.0574, -0.0007, 0.0509, 0.0104, 0.0023, 0.0493, 0.0070, 0.0279,
0.0475, 0.0338, -0.0023, 0.0550, -0.0346, 0.0808, -0.0208, 0.0858, 0.0497, 0.0150, 0.0004,
-0.0068, 0.0358, -0.0016, 0.0795, 0.0584, -0.0024, 0.0096, -0.0327, 0.0768, -0.0026, -0.0395,
0.0355, 0.0680, -0.0043, -0.0645, 0.0365, 0.0496, -0.0393, -0.0481, 0.0050, -0.0125, 0.0332,
-0.0438, -0.0308, 0.0180, 0.0358, 0.0385, -0.0024, 0.0094, -0.0535, -0.0639, -0.0336, 0.0187,
-0.0443, 0.0406, -0.0212, 0.0285, 0.0300, 0.0145, 0.0245, 0.0412, 0.0121, 0.0464, 0.0231,
0.0230, 0.0497, 0.0012, -0.0005, -0.0024, 0.0369, 0.0177, -0.0069, 0.0326, -0.0174, -0.0483,
0.0097, 0.0128, 0.0256, -0.0742, 0.0067, -0.0163, -0.0199, 0.0274, 0.0183, -0.0267, 0.0263,
-0.0626, -0.0058, 0.0406, 0.0445, 0.0605, 0.0265, 0.0236, 0.0018, 0.0197, -0.0310, 0.0306,
0.0186, -0.0216, 0.0263, 0.0399, 0.0013, -0.0404, -0.0210, -0.0088, -0.0652, -0.0872, -0.0839,
0.0627, 0.0159, -0.0504, 0.0018, 0.1023, 0.0102, 0.0464, -0.0303, 0.0331, 0.0470, 0.0132,
-0.0226, -0.0062, 0.0482, -0.0135, 0.0308, -0.0111, 0.0221, 0.0243, 0.0084, 0.0133, 0.0041,
0.0117, 0.0148, 0.0157, -0.0175, 0.0265, 0.0061, -0.0074, 0.0020, 0.0305, -0.0023, -0.0171,
0.0321, -0.0083, -0.0514, 0.0109, 0.0222, 0.0295, 0.0254, -0.0094, 0.0070, 0.0020, -0.0162,
-0.0240, 0.0184, -0.0543, -0.0176, -0.0157, -0.0786, -0.0081, 0.0456, 0.0014, -0.0029, 0.0765,
0.0002, 0.0369, 0.0399, -0.0526, 0.0157, 0.0441, -0.0121, 0.0307, -0.0319, 0.0017, 0.0246,
-0.0462, -0.0297, 0.0078, 0.0791, 0.0121, 0.0071, -0.0204, 0.0133, 0.0370, 0.0057, 0.0497,
-0.0454, -0.0106, -0.0472, 0.0329, 0.0191, -0.0005, -0.0576, -0.0621, 0.0418, -0.0274, 0.0434,
-0.0345, -0.0216, -0.0785, 0.0546, -0.0007, -0.0921, -0.0619, -0.0521, 0.0721, 0.0471, 0.0311,
-0.0130, 0.0474, 0.0553, 0.0383, 0.0085, 0.0353, 0.0347, -0.0424, 0.0001, -0.0435, 0.0368,
-0.0089, -0.0431, -0.0023, 0.0848, 0.0155, 0.0264, 0.0037, 0.0020, 0.0162, -0.0244, 0.0002,
0.0338, -0.0074, 0.0077, 0.0443, 0.0099, -0.0198, -0.0381, -0.0039, -0.0427, -0.0181, -0.0088,
0.0358, -0.0371, 0.0383, -0.0031, -0.1142, 0.0145, -0.0128, -0.0050, -0.0254, -0.0401, -0.0316,
-0.0166, -0.0804, -0.0916, -0.1207, 0.1622, -0.0532, -0.0221, 0.1216, 0.0590, 0.0196, 0.0456,
0.0443, 0.0423, -0.0698, -0.0216, -0.0364, 0.0597, 0.0256, -0.0139, 0.1169, -0.0124, 0.0295,
-0.0129, -0.0153, 0.0394, -0.0103, -0.0028, 0.0203, -0.0241, -0.0086, 0.0508, -0.0529, -0.0182,
-0.0157, -0.0019, -0.0182, 0.0442, -0.0186, -0.0171, -0.0023, -0.0448, 0.0323, 0.0015, -0.0630,
-0.0208, 0.0239, 0.0836, 0.0059, -0.0194, 0.0530, 0.0265, -0.0082, -0.0927, 0.0232, 0.0135,
0.0373, -0.0322, 0.0545, 0.0002, -0.0259, 0.0371, 0.0076, 0.0552, -0.0021, -0.0685, 0.0439,
0.0152, 0.0573, -0.0044, -0.1023, 0.0396, 0.0489, 0.0265, 0.0642, 0.0052, 0.0240, 0.0150,
0.1036, -0.0353, -0.0484, 0.0138, 0.0344, -0.0252, -0.0013, -0.0128, -0.0051, -0.0629, -0.0549,
0.0493, 0.0364, -0.0317, -0.0203, -0.0606, -0.0130, 0.0376, -0.0392, -0.0224, -0.0253, 0.1165,
0.0075, 0.1097, 0.0357, 0.0140, 0.0317, 0.0197, 0.0321, 0.0737, -0.0138, 0.0339, -0.0334,
0.0111, 0.0095, -0.0167, 0.0173, -0.0099, -0.0106, -0.0417, 0.0139, 0.0033, -0.0590, 0.0181,
-0.0177, 0.1073, -0.0026, -0.0017, -0.0145, 0.0211, 0.0733, 0.0090, -0.0054, -0.0069, 0.0562,
0.0115, -0.0017, -0.0099, -0.0365, 0.0421, 0.0653, 0.0438, 0.0002, 0.0716, 0.0512, -0.0157,
0.0500, 0.0128, -0.0607, 0.0705, -0.0870, 0.0524, 0.0197, -0.0305, 0.1306, 0.0367, 0.0245,
-0.0136, 0.0058, 0.0470, 0.0475, 0.0335, -0.0256, -0.2191, 0.0820, 0.0728, 0.0388, 0.0440,
-0.0345, 0.0074, 0.0053, 0.0427, -0.0073, -0.0372, 0.0388, 0.0243, -0.0183, 0.0139, 0.0701,
-0.0301, 0.0199, 0.0490, 0.0370, -0.0090, 0.0873, 0.0161, -0.0076, -0.0264, 0.0167, 0.0207,
-0.0701, 0.0100, 0.0232, -0.0279, 0.0946, -0.0097, -0.0062, -0.0938, -0.0521, -0.0072, 0.0600,
0.0243, 0.0410, 0.0681, 0.0210, -0.0003, 0.0396, -0.0488, 0.0437, 0.0204, -0.0197, 0.0104,
-0.0434, 0.1114, -0.0216, 0.0096, -0.0227, 0.0264, 0.0016, -0.0179, 0.0373, -0.0241, 0.0089,
0.0005, 0.0311, 0.0092, 0.0041, 0.0105, 0.0186, -0.0274, 0.0240, -0.0002, -0.0071, 0.0349,
-0.0108, 0.0171, -0.0129, 0.0085, 0.0310, -0.0300, -0.0465, 0.0097, 0.0134, -0.0278, 0.0298,
0.0383, -0.0278, 0.0192, -0.0397, 0.0118, 0.0229, 0.0357, 0.0265, 0.0263, 0.0370, 0.0202,
0.0302, -0.0005, 0.0391, -0.0066, 0.0412, 0.0163, 0.0310, 0.0063, 0.0066, 0.0117, 0.0228,
0.0008, -0.0472, 0.0181, 0.0533, 0.0246, 0.0726, -0.0228, 0.0595, 0.0049, -0.0441, 0.0567,
0.0578, 0.0418, 0.0765, -0.0590, 0.0518, -0.0364, 0.0433, 0.0142, 0.0081, 0.0691, 0.0480,
0.0071, -0.0209, 0.0374, -0.0143, -0.1476, 0.0611, 0.0787, 0.0576, 0.0544, 0.0388, -0.0341,
0.0370, 0.0357, -0.0266, 0.0524, -0.0342, -0.0080, -0.0304, 0.0601, 0.0172, 0.0559, -0.0532,
-0.0219, 0.0946, -0.0331, -0.0235, 0.0217, -0.0186, 0.0577, -0.0558, -0.0072, -0.0795, 0.0019,
0.0325, -0.0944, -0.0665, 0.0738, 0.0037, -0.0273, -0.0128, -0.0656, -0.0842, 0.0161, 0.0732,
0.0058, -0.0176, -0.0223, 0.0342, -0.0636, -0.0104, -0.0754, -0.0810, 0.0036, -0.1117, 0.0850,
0.0560, -0.0622, -0.0292, -0.0180, 0.0067, 0.0794, 0.0497, 0.0098, 0.0146, 0.0165, -0.0142,
0.0533, 0.0058, 0.0494, 0.0154, 0.0109, -0.0181, -0.0193, 0.0107, 0.0164, -0.0373, 0.0010,
0.0078, 0.0121, 0.0375, 0.0310, -0.0274, 0.0180, -0.0209, -0.0220, 0.0288, -0.0016, 0.0342,
-0.0121, 0.0051, -0.0197, 0.0348, -0.0027, 0.0235, -0.0003, 0.0094, 0.0104, -0.0318, -0.0016,
0.0032, 0.0208, 0.0228, 0.0296, 0.0160, 0.0110, 0.0121, -0.0226, 0.0082, 0.0413, 0.0319,
-0.0196, -0.0340, 0.0120, 0.0344, 0.0129, -0.0448, -0.0099, -0.0630, -0.0355, -0.0073, 0.0457,
0.0100, -0.0873, -0.0114, 0.0115, -0.0921, -0.1709, -0.0748, 0.0076, -0.0873, -0.1100, 0.0846,
0.0927, 0.0529, -0.0028, 0.0726, 0.0331, 0.0343, -0.0216, 0.0570, 0.0163, -0.0393, 0.0280,
0.0573, 0.0128, -0.0826, -0.0554, 0.0664, -0.0489, 0.0858, 0.0347, -0.0029, 0.0636, 0.0206,
0.0313, -0.0027, 0.0266, -0.0143, -0.0205, -0.0233, -0.0598, -0.0733, 0.1063, -0.0080, 0.0073,
0.0418, 0.0398, 0.0298, -0.0093, -0.0636, 0.0371, 0.0095, 0.0221, 0.0228, -0.0216, 0.0028,
0.0061, 0.0481, 0.0104, 0.0345, 0.0163, 0.0204, -0.0164, 0.0478, -0.0330, 0.0284, 0.0426,
0.0271, 0.0225, -0.0385, 0.0423, 0.0054, 0.0044, 0.0205, 0.0177, -0.0161, 0.0367, -0.0170,
0.0214, 0.0240, -0.0055, -0.0330, 0.0531, -0.0192, 0.0066, 0.0097, -0.0224, 0.0193, -0.0655,
-0.0278, 0.0814, 0.0000, -0.0188, -0.0530, -0.0043, 0.0642, 0.0009, 0.0137, 0.0004, 0.0331,
-0.0018, -0.0028, -0.0206, 0.0334, 0.0168, 0.0160, 0.0367, -0.0018, 0.0073, 0.0111, 0.0032,
0.0175, 0.0001, 0.0176, 0.0213, 0.0278, 0.0100, 0.0535, -0.0406, -0.0284, 0.0008, 0.0212,
0.0032, 0.0342, 0.0296, 0.0027, -0.0724, 0.0171, -0.0948, 0.0770, 0.0274, 0.0152, 0.0374,
-0.0678, 0.0673, 0.0114, -0.0188, 0.0157, 0.0191, 0.0333, 0.0273, -0.0026, -0.0842, -0.1281,
0.1253, 0.0448, 0.0169, 0.0547, 0.0676, -0.0407, -0.0307, 0.1065, 0.0347, -0.0141, 0.0246,
0.0407, 0.0497, 0.0040, 0.0210, 0.0225, 0.0272, -0.0499, 0.0672, -0.0113, 0.0418, -0.0557,
-0.0330, 0.0341, -0.0905, -0.0012, -0.0869, 0.0898, -0.0442, -0.0961, 0.0776, 0.0515, -0.0614,
0.0597, -0.0279, 0.0337, 0.0122, 0.0012, 0.0629, 0.0290, -0.0189, -0.0506, -0.0239, 0.0866,
0.0424, 0.0140, 0.0497, 0.0280, -0.0438, 0.0468, 0.0321, 0.0092, 0.0208, 0.0187, -0.0129,
0.0558, -0.0030,
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_load_sp500() {
let data = Sp500Data::load();
assert!(data.len() > 1000, "Should have > 1000 months of data");
assert!(data.len() < 1500, "Should have < 1500 months of data");
}
#[test]
fn test_statistics() {
let data = Sp500Data::load();
let stats = data.statistics(Sp500Period::All, false);
assert!(stats.annual_mean > 0.05, "Long-term return should be > 5%");
assert!(stats.annual_mean < 0.15, "Long-term return should be < 15%");
assert!(stats.annual_std > 0.10, "Volatility should be > 10%");
assert!(stats.annual_std < 0.25, "Volatility should be < 25%");
}
#[test]
fn test_period_filtering() {
let data = Sp500Data::load();
let all = data.monthly_returns(Sp500Period::All, false);
let modern = data.monthly_returns(Sp500Period::Modern, false);
let recent = data.monthly_returns(Sp500Period::Recent, false);
assert!(all.len() > modern.len(), "All > Modern");
assert!(modern.len() > recent.len(), "Modern > Recent");
}
#[test]
fn test_decade_stats() {
let data = Sp500Data::load();
let decades = data.decade_stats(false);
assert!(!decades.is_empty());
for (label, _stats) in &decades {
assert!(label.ends_with('s'), "Decade label format");
}
}
}