use frenchrs::FamaMacBeth;
use greeners::CovarianceType;
use ndarray::Array2;
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("\n{}", "=".repeat(80));
println!("FAMA-MACBETH TWO-PASS REGRESSION");
println!("{}", "=".repeat(80));
println!("\nEstimatestion of prêmios of risk usando metodologia of Fama-MacBeth (1973)");
println!("with correção of Shanken for standard errors.");
let t = 60; let n_portfolios = 25;
let n_factors = 3;
println!("\n{}", "-".repeat(80));
println!("Configuração:");
println!(" • Portfolios: {}", n_portfolios);
println!(" • Factors: {}", n_factors);
println!(" • Periods: {} meses", t);
println!("{}", "-".repeat(80));
let mut rng = 12345u64;
let mut rand = || {
rng = rng.wrapping_mul(1103515245).wrapping_add(12345);
((rng / 65536) % 32768) as f64 / 32768.0 - 0.5
};
let factor_names = vec!["Market".to_string(), "SMB".to_string(), "HML".to_string()];
let factors = Array2::from_shape_fn((t, n_factors), |(_i, j)| {
let base = match j {
0 => 0.008, 1 => 0.002, 2 => 0.003, _ => 0.0,
};
base + rand() * 0.04
});
println!("\nFactors gerados:");
println!("{:<15} {:>12} {:>12}", "Factor", "Mean", "Std Dev");
println!("{}", "-".repeat(40));
for (i, name) in factor_names.iter().enumerate() {
let col = factors.column(i);
let mean = col.mean().unwrap_or(0.0);
let std = col.std(1.0);
println!(
"{:<15} {:>11.2}% {:>11.2}%",
name,
mean * 100.0,
std * 100.0
);
}
let mut portfolio_names = Vec::new();
for size in &["Small", "2", "3", "4", "Big"] {
for value in &["Growth", "2", "3", "4", "Value"] {
portfolio_names.push(format!("{}/{}", size, value));
}
}
let mut returns = Array2::<f64>::zeros((t, n_portfolios));
let mut true_betas = Vec::new();
for p in 0..n_portfolios {
let size_quintile = p / 5; let value_quintile = p % 5;
let beta_market = 0.8 + (size_quintile as f64) * 0.1;
let beta_smb = -0.5 + (4 - size_quintile) as f64 * 0.25; let beta_hml = -0.4 + (value_quintile as f64) * 0.2;
true_betas.push(vec![beta_market, beta_smb, beta_hml]);
let alpha = 0.001 + rand() * 0.002;
for time in 0..t {
let mut ret = alpha;
ret += beta_market * factors[[time, 0]];
ret += beta_smb * factors[[time, 1]];
ret += beta_hml * factors[[time, 2]];
ret += rand() * 0.015;
returns[[time, p]] = ret;
}
}
println!("\nReturns gerados for {} portfolios", n_portfolios);
println!("\n{}", "=".repeat(80));
println!("ESTIMANDthe modelO FAMA-MACBETH...");
println!("{}", "=".repeat(80));
let result = FamaMacBeth::fit(
&returns,
&factors,
CovarianceType::HC3,
Some(portfolio_names.clone()),
Some(factor_names.clone()),
)?;
println!("{}", result);
println!("\n{}", "=".repeat(80));
println!("INTERPRETATION DOS PRÊMIOS DE RISCO");
println!("{}", "=".repeat(80));
println!("\n1. CONSTANTE (λ_0):");
println!(" Value: {:.4}% ao mês", result.lambda_const() * 100.0);
println!(" Interpretation: Taxa of return for asset with beta zero");
println!(
" Significance (Shanken): {}",
if result.pval_shanken[0] < 0.05 {
"Significativo a 5%"
} else {
"Não significant a 5%"
}
);
for (i, name) in factor_names.iter().enumerate() {
println!("\n{}. {} (λ_{}):", i + 2, name, i + 1);
println!(" Prêmio of risk: {:.4}% ao mês", result.lambda(i) * 100.0);
println!(
" Annualized: {:.2}% ao ano",
result.lambda(i) * 12.0 * 100.0
);
if result.is_significant_shanken(i, 0.05) {
println!(" ✓ Significativo a 5% (Shanken)");
println!(" Interpretation: Factor é precificado pelthe market");
} else {
println!(" ✗ Não significant a 5% (Shanken)");
println!(" Interpretation: Factor can not be relevante for pricing");
}
let t_shanken = result.tstat_shanken[i + 1];
println!(" t-statistic (Shanken): {:.2}", t_shanken);
}
println!("\n{}", "=".repeat(80));
println!("COMPARAÇÃO: BETAS VERDADEIROS vs ESTIMATED (Primeiros 5 portfolios)");
println!("{}", "=".repeat(80));
println!(
"\n{:<20} {:>12} {:>12} {:>12} {:>12} {:>12} {:>12}",
"Portfolio",
"True β_Mkt",
"Est β_Mkt",
"True β_SMB",
"Est β_SMB",
"True β_HML",
"Est β_HML"
);
println!("{}", "-".repeat(100));
for p in 0..5.min(n_portfolios) {
let true_beta = &true_betas[p];
let est_beta = result.betas.row(p);
println!(
"{:<20} {:>12.4} {:>12.4} {:>12.4} {:>12.4} {:>12.4} {:>12.4}",
&portfolio_names[p],
true_beta[0],
est_beta[0],
true_beta[1],
est_beta[1],
true_beta[2],
est_beta[2]
);
}
println!("\n... ({} portfolios restbefore)", n_portfolios - 5);
println!("\n{}", "=".repeat(80));
println!("PRICING ERRORS (Primeiros 10 portfolios)");
println!("{}", "=".repeat(80));
println!(
"\n{:<20} {:>15} {:>15} {:>15}",
"Portfolio", "Mean Return", "Model Return", "Pricing Error"
);
println!("{}", "-".repeat(70));
for (p, name) in portfolio_names
.iter()
.enumerate()
.take(10.min(n_portfolios))
{
println!(
"{:<20} {:>14.2}% {:>14.2}% {:>14.4}%",
name,
result.mean_returns[p] * 100.0,
result.model_returns[p] * 100.0,
result.pricing_errors[p] * 100.0
);
}
if n_portfolios > 10 {
println!("\n... ({} portfolios restbefore)", n_portfolios - 10);
}
println!("\n{}", "=".repeat(80));
println!("METRICS DE FIT QUALITY");
println!("{}", "=".repeat(80));
println!("\nCross-Sectional:");
println!(
" • R² médio: {:.2}%",
result.r2_cross_sectional_mean * 100.0
);
println!(" • Periods efetivos: {}", result.t_eff);
println!("\nPricing:");
println!(" • R² pricing: {:.2}%", result.r2_pricing() * 100.0);
println!(" • RMSE: {:.4}%", result.rmse_pricing() * 100.0);
println!(" • MAE: {:.4}%", result.mae_pricing() * 100.0);
println!("\n{}", "=".repeat(80));
println!("CONCLUSÕES");
println!("{}", "=".repeat(80));
println!("\n1. METODOLOGIA:");
println!(" • Primeira passagem: Estimates betas via regresare time-beies");
println!(" • Segunda passagem: Estimates prêmios of risk via regresare cross-section");
println!(" • Shanken correction: Ajusta erros padrão for beta estimated");
println!("\n2. PRÊMIOS DE RISCO:");
let significant_factors: Vec<String> = (0..n_factors)
.filter(|&i| result.is_significant_shanken(i, 0.05))
.map(|i| factor_names[i].clone())
.collect();
if significant_factors.is_empty() {
println!(" • Nenhum factor é thististicamente significant a 5%");
} else {
let factors_str: Vec<&str> = significant_factors.iter().map(|s| s.as_str()).collect();
println!(
" • Factors significativos (5%): {}",
factors_str.join(", ")
);
}
println!("\n3. QUALIDADE Dthe model:");
if result.r2_cross_sectional_mean > 0.7 {
println!(" ✓ Bom fit cross-sectional (R² > 70%)");
} else if result.r2_cross_sectional_mean > 0.4 {
println!(" ~ Fit moderado cross-sectional");
} else {
println!(" ✗ Fit fraco cross-sectional");
}
if result.rmse_pricing() < 0.01 {
println!(" ✓ Pricing errors pequenos (RMSE < 1%)");
} else {
println!(" ~ Pricing errors moderados");
}
println!("\n{}", "=".repeat(80));
Ok(())
}