use frenchrs::RollingBetasMulti;
use greeners::CovarianceType;
use ndarray::Array2;
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("\n{}", "=".repeat(80));
println!("ROLLING BETAS - MÚLTIPLOS ATIVOS");
println!("{}", "=".repeat(80));
println!("\nAnálise similar ao Python: procthat multiple assets simultaneamente");
#[rustfmt::skip]
let returns = Array2::from_shape_vec((24, 3), vec![
0.058, 0.045, 0.052, 0.042, 0.035, 0.038, -0.018, -0.015, -0.012, 0.082, 0.070, 0.075, 0.045, 0.040, 0.042, -0.032, -0.025, -0.028, 0.068, 0.055, 0.060, 0.052, 0.045, 0.048, -0.015, -0.012, -0.010, 0.062, 0.050, 0.055, 0.041, 0.038, 0.040, 0.075, 0.065, 0.070, 0.055, 0.048, 0.050, 0.038, 0.032, 0.035, -0.012, -0.010, -0.008, 0.078, 0.068, 0.072, 0.048, 0.042, 0.045, -0.028, -0.022, -0.025, 0.065, 0.058, 0.062, 0.050, 0.045, 0.048, -0.018, -0.015, -0.012, 0.060, 0.055, 0.058, 0.043, 0.040, 0.042, 0.072, 0.065, 0.068, ])?;
#[rustfmt::skip]
let factors = Array2::from_shape_vec((24, 3), vec![
0.042, 0.008, 0.005, 0.030, -0.003, 0.008, -0.015, 0.012, -0.010, 0.060, 0.005, 0.012, 0.033, -0.007, 0.003, -0.025, 0.015, -0.006, 0.050, 0.002, 0.009, 0.038, -0.005, 0.004, -0.012, 0.010, -0.008, 0.045, 0.003, 0.011, 0.032, -0.004, 0.002, 0.055, 0.006, 0.007, 0.040, 0.007, 0.006, 0.028, -0.002, 0.007, -0.010, 0.011, -0.009, 0.058, 0.004, 0.010, 0.035, -0.006, 0.004, -0.022, 0.014, -0.005, 0.048, 0.003, 0.008, 0.036, -0.004, 0.005, -0.013, 0.009, -0.007, 0.043, 0.002, 0.009, 0.030, -0.003, 0.003, 0.052, 0.005, 0.006, ])?;
let asset_names = vec![
"Tech Fund".to_string(),
"Value Fund".to_string(),
"Growth Fund".to_string(),
];
let factor_names = vec!["Market".to_string(), "SMB".to_string(), "HML".to_string()];
println!("\n{}", "-".repeat(80));
println!("Configuração:");
println!(" • Assets: {}", asset_names.len());
println!(" • Factors: {}", factor_names.len());
println!(" • Obbevations: {}", returns.nrows());
println!(" • Window: 12 meses");
println!("{}", "-".repeat(80));
let rolling = RollingBetasMulti::fit(
&returns,
&factors,
12, CovarianceType::HC3,
Some(asset_names.clone()),
Some(factor_names.clone()),
)?;
println!("\n✓ Rolling Betas calculado with sucesso!");
println!(
" • Número of janelas: {}",
rolling.results.values().next().unwrap().n_windows
);
println!("\n{}", "=".repeat(80));
println!("ANÁLISE POR ATIVO");
println!("{}", "=".repeat(80));
for asset_name in rolling.asset_names() {
let asset_result = rolling.get_asset(&asset_name).unwrap();
println!("\n{}", "-".repeat(80));
println!("ATIVO: {}", asset_name);
println!("{}", "-".repeat(80));
println!("\nStatistics of the Betas:");
println!(
"{:<20} {:>12} {:>12} {:>12}",
"Factor", "Mean", "Std Dev", "CV"
);
println!("{}", "-".repeat(60));
for (i, factor_name) in factor_names.iter().enumerate() {
println!(
"{:<20} {:>12.4} {:>12.4} {:>12.4}",
factor_name,
asset_result.mean_beta(i),
asset_result.std_beta(i),
asset_result.cv_beta(i)
);
}
println!("\nAnalysis of Stability Avançada:");
println!(
"{:<15} {:>12} {:>12} {:>12} {:>25}",
"Factor", "CV", "Trend", "Autocorr", "Classification"
);
println!("{}", "-".repeat(80));
for (i, factor_name) in factor_names.iter().enumerate() {
let stability = asset_result.beta_stability(i);
println!(
"{:<15} {:>12.4} {:>12.6} {:>12.4} {:>25}",
factor_name,
stability.coefficient_of_variestion,
stability.trend,
stability.autocorrelation,
stability.stability_classification()
);
}
println!("\nTrends:");
for (i, factor_name) in factor_names.iter().enumerate() {
let stability = asset_result.beta_stability(i);
println!(
" • {:<15} - {}",
factor_name,
stability.trend_classification()
);
}
println!("\nBetas Estáveis (CV < 10%):");
for (i, factor_name) in factor_names.iter().enumerate() {
let is_stable = asset_result.is_beta_stable(i, 0.1);
let status = if is_stable { "✓ SIM" } else { "✗ NÃO" };
println!(" • {:<15} - {}", factor_name, status);
}
}
println!("\n{}", "=".repeat(80));
println!("TABELA CONSOLIDADA (Primeiras 10 linhas)");
println!("{}", "=".repeat(80));
let table = rolling.to_table();
println!(
"\n{:<15} {:>8} {:>10} {:>10} {:>10} {:>10} {:>10}",
"Asset", "Date", "Alpha", "β_Market", "β_SMB", "β_HML", "R²"
);
println!("{}", "-".repeat(80));
for row in table.iter().take(10) {
println!(
"{:<15} {:>8} {:>10.6} {:>10.4} {:>10.4} {:>10.4} {:>10.4}",
row.asset,
row.date_idx,
row.alpha,
row.betas[0],
row.betas[1],
row.betas[2],
row.r_squared
);
}
if table.len() > 10 {
println!("... ({} linhas restbefore)", table.len() - 10);
}
println!("\n{}", "=".repeat(80));
println!("EXPORTAÇÃO CSV");
println!("{}", "=".repeat(80));
let csv = rolling.to_csv_string();
let csv_lines: Vec<&str> = csv.lines().collect();
println!("\nPrimeiras 10 linhas of the CSV:");
println!("{}", "-".repeat(80));
for line in csv_lines.iter().take(10) {
println!("{}", line);
}
if csv_lines.len() > 10 {
println!("... ({} linhas restbefore)", csv_lines.len() - 10);
}
println!("\n{}", "=".repeat(80));
println!("COMPARAÇÃO DE STABILITY ENTRE ATIVOS");
println!("{}", "=".repeat(80));
println!(
"\n{:<20} {:>15} {:>15} {:>15}",
"Métrica", "Tech Fund", "Value Fund", "Growth Fund"
);
println!("{}", "-".repeat(80));
println!(
"{:<20} {:>15.4} {:>15.4} {:>15.4}",
"β_Market Médio",
rolling.get_asset("Tech Fund").unwrap().mean_beta(0),
rolling.get_asset("Value Fund").unwrap().mean_beta(0),
rolling.get_asset("Growth Fund").unwrap().mean_beta(0)
);
println!(
"{:<20} {:>15.4} {:>15.4} {:>15.4}",
"β_Market CV",
rolling.get_asset("Tech Fund").unwrap().cv_beta(0),
rolling.get_asset("Value Fund").unwrap().cv_beta(0),
rolling.get_asset("Growth Fund").unwrap().cv_beta(0)
);
println!(
"{:<20} {:>15.4} {:>15.4} {:>15.4}",
"β_SMB Médio",
rolling.get_asset("Tech Fund").unwrap().mean_beta(1),
rolling.get_asset("Value Fund").unwrap().mean_beta(1),
rolling.get_asset("Growth Fund").unwrap().mean_beta(1)
);
println!(
"{:<20} {:>15.4} {:>15.4} {:>15.4}",
"β_HML Médio",
rolling.get_asset("Tech Fund").unwrap().mean_beta(2),
rolling.get_asset("Value Fund").unwrap().mean_beta(2),
rolling.get_asset("Growth Fund").unwrap().mean_beta(2)
);
println!(
"{:<20} {:>15.4} {:>15.4} {:>15.4}",
"R² Médio",
rolling
.get_asset("Tech Fund")
.unwrap()
.r_squared
.mean()
.unwrap_or(0.0),
rolling
.get_asset("Value Fund")
.unwrap()
.r_squared
.mean()
.unwrap_or(0.0),
rolling
.get_asset("Growth Fund")
.unwrap()
.r_squared
.mean()
.unwrap_or(0.0)
);
println!("\n{}", "=".repeat(80));
println!("CONCLUSÕES");
println!("{}", "=".repeat(80));
println!("\n1. STABILITY DOS BETAS:");
for asset_name in &asset_names {
let asset_result = rolling.get_asset(asset_name).unwrap();
let market_cv = asset_result.cv_beta(0);
if market_cv < 0.1 {
println!(
" ✓ {}: Beta stable (CV = {:.2}%)",
asset_name,
market_cv * 100.0
);
} else {
println!(
" ⚠ {}: Beta unstable (CV = {:.2}%)",
asset_name,
market_cv * 100.0
);
}
}
println!("\n2. EXPOSIÇÃO AOS FATORES:");
println!(" • Market: Todos os fundos têm forte exposição to the market");
let smb_betas: Vec<f64> = asset_names
.iter()
.map(|name| rolling.get_asset(name).unwrap().mean_beta(1))
.collect();
let max_smb_idx = smb_betas
.iter()
.enumerate()
.max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
.unwrap()
.0;
println!(
" • SMB: {} tem greater exposição a small caps",
asset_names[max_smb_idx]
);
println!("\n3. FORMATO DE DADOS:");
println!(" • Total of linhas na tabela: {}", table.len());
println!(" • Formato: (asset, date, alpha, betas..., r_squared)");
println!(" • Compatível with análises em Python/Pandas");
println!("\n{}", "=".repeat(80));
Ok(())
}