| Crates.io | frenchrs |
| lib.rs | frenchrs |
| version | 0.1.0 |
| created_at | 2026-01-05 01:25:37.325646+00 |
| updated_at | 2026-01-05 01:25:37.325646+00 |
| description | A high-performance Rust library for asset pricing and financial analysis, built on the robust econometric infrastructure of [Greeners](https://crates.io/crates/greeners). |
| homepage | |
| repository | https://github.com/sheep-farm/Frenchrs |
| max_upload_size | |
| id | 2022925 |
| size | 669,899 |
A high-performance Rust library for asset pricing and financial analysis, built on the robust econometric infrastructure of Greeners.
Fama-MacBeth Two-Pass Regression (1973): Cross-sectional analysis for estimating risk premia.
Hansen-Jagannathan Distance (1997): Tests whether a factor model correctly prices assets.
GRS Test (Gibbons, Ross & Shanken, 1989): Joint test of whether all alphas are zero.
ndarray arraysAdd the following to your Cargo.toml:
[dependencies]
frenchrs = "0.1.0"
greeners = "1.3.2"
ndarray = "0.17.1"
use frenchrs::CAPM;
use greeners::CovarianceType;
use ndarray::array;
let asset_returns = array![0.01, 0.02, -0.01, 0.03];
let market_returns = array![0.008, 0.015, -0.005, 0.025];
let risk_free_rate = 0.0001;
let result = CAPM::fit(
&asset_returns,
&market_returns,
risk_free_rate,
CovarianceType::HC3,
).unwrap();
println!("Beta: {:.4}", result.beta);
println!("Alpha: {:.4}", result.alpha);
println!("RΒ²: {:.4}", result.r_squared);
use frenchrs::APT;
use greeners::CovarianceType;
use ndarray::{array, Array2};
let returns = array![0.01, 0.02, -0.01, 0.03, 0.015, -0.005, 0.025];
// Factor Matrix (n_obs Γ n_factors)
let factors = Array2::from_shape_vec((7, 3), vec![
0.008, 0.002, 0.001,
0.015, -0.001, 0.002,
-0.005, 0.003, -0.002,
0.025, 0.001, 0.003,
0.012, -0.002, 0.001,
-0.003, 0.001, -0.001,
0.020, 0.002, 0.002,
]).unwrap();
let result = APT::fit(
&returns,
&factors,
0.0001,
CovarianceType::HC3,
Some(vec!["Market".into(), "Size".into(), "Value".into()]),
).unwrap();
use frenchrs::OOSPerformance;
use greeners::CovarianceType;
// returns_excess: T x N matrix (time x assets)
// factors: T x K matrix (time x factors)
let result = OOSPerformance::fit(
&returns_excess,
&factors,
0.7, // 70% in-sample, 30% out-of-sample
CovarianceType::HC3,
Some(asset_names),
).unwrap();
// Get summary statistics
let stats = result.summary_stats();
println!("Assets beating benchmark: {} ({:.1}%)",
stats.assets_beating_benchmark,
stats.pct_beating_benchmark
);
// Export to CSV
let csv = result.to_csv_string();
use frenchrs::IVOLTrackingMulti;
use greeners::CovarianceType;
// Analyze multiple assets at once
let result = IVOLTrackingMulti::fit(
&returns_excess, // T x N
&factors, // T x K
Some(&benchmark), // Optional benchmark for tracking error
CovarianceType::HC3,
Some(asset_names),
12.0, // 12 periods per year (monthly data)
).unwrap();
// Export results
let csv = result.to_csv_string();
use frenchrs::RollingBetasMulti;
use greeners::CovarianceType;
// Rolling betas for multiple assets
let result = RollingBetasMulti::fit(
&returns_excess, // T x N
&factors, // T x K
36, // 36-month window
CovarianceType::HC3,
Some(asset_names),
Some(factor_names),
).unwrap();
// Analyze beta stability
for (asset_name, asset_result) in &result.results {
for (i, factor_name) in factor_names.iter().enumerate() {
let stability = asset_result.beta_stability(i);
println!("{} - {}: CV = {:.4}, Trend = {:.4}",
asset_name, factor_name,
stability.coefficient_of_variation,
stability.trend
);
}
}
use frenchrs::ResidualCorrelation;
use greeners::CovarianceType;
// Analyze residual correlation to detect missing factors
let result = ResidualCorrelation::fit(
&returns_excess, // T x N
&factors, // T x K
CovarianceType::HC3,
Some(asset_names),
).unwrap();
// Check model specification quality
let summary = result.summary_stats();
println!("Avg off-diagonal correlation: {:.4}", summary.avg_off_diag_corr);
println!("Classification: {}", summary.correlation_classification());
// Find most correlated assets (may indicate missing common factor)
let top_correlated = result.most_correlated_assets("Asset1", 3);
for (name, corr) in top_correlated {
println!("{}: {:.4}", name, corr);
}
// Export correlation matrix
let csv = result.correlation_to_csv_string();
use frenchrs::HJDistance;
use greeners::CovarianceType;
// Test whether factor model correctly prices assets
let result = HJDistance::fit(
&returns_excess, // T x N
&factors, // T x K
CovarianceType::HC3,
Some(asset_names),
).unwrap();
// Check model quality
println!("HJ Distance: {:.6}", result.hj_distance);
println!("P-value: {:.4}", result.p_value);
println!("Model Quality: {}", result.model_quality_classification());
// Test model specification
if result.reject_model(0.05) {
println!("Model has significant pricing errors");
// Identify assets with largest pricing errors
for name in &result.asset_names {
let alpha = result.get_alpha(name).unwrap();
println!("{}: alpha = {:.6}", name, alpha);
}
}
// Export results
let csv = result.to_csv_string();
use frenchrs::GRSTest;
use greeners::CovarianceType;
// Joint test: Are all alphas simultaneously zero?
let result = GRSTest::fit(
&returns_excess, // T x N
&factors, // T x K
CovarianceType::HC3,
Some(asset_names),
).unwrap();
// Check test results
println!("GRS F-statistic: {:.4}", result.grs_f_stat);
println!("P-value: {:.4}", result.p_value);
println!("DF: F({}, {})", result.df1, result.df2);
// Test decision
if result.reject_model(0.05) {
println!("Reject Hβ: At least one alpha β 0");
println!("Model fails to price all assets jointly");
} else {
println!("Do not reject Hβ: All alphas jointly zero");
println!("Model adequately prices the cross-section");
}
// Individual alphas (for diagnostics)
for name in &result.asset_names {
let alpha = result.get_alpha(name).unwrap();
println!("{}: {:.6}", name, alpha);
}
use frenchrs::ResidualDiagnostics;
use greeners::CovarianceType;
// Run comprehensive diagnostic tests on residuals
let result = ResidualDiagnostics::fit(
&returns_excess, // T x N
&factors, // T x K
CovarianceType::HC3,
Some(asset_names),
).unwrap();
// Check diagnostics for each asset
for (asset_name, diag) in &result.diagnostics {
println!("\nAsset: {}", asset_name);
// Autocorrelation tests
println!("Durbin-Watson: {:.4}", diag.durbin_watson);
if diag.has_positive_autocorr() {
println!(" β Positive autocorrelation detected");
}
println!("Ljung-Box: {:.4} (p = {:.4})", diag.lb_stat, diag.lb_p_value);
// Heteroscedasticity tests
println!("Breusch-Pagan: {:.4} (p = {:.4})", diag.bp_stat, diag.bp_p_value);
if diag.has_heteroscedasticity() {
println!(" β Heteroscedasticity detected");
}
println!("White: {:.4} (p = {:.4})", diag.white_stat, diag.white_p_value);
println!("ARCH: {:.4} (p = {:.4})", diag.arch_stat, diag.arch_p_value);
// Specification tests
println!("RESET: {:.4} (p = {:.4})", diag.reset_f, diag.reset_p_value);
println!("Chow: {:.4} (p = {:.4})", diag.chow_f, diag.chow_p_value);
// Normality test
println!("Jarque-Bera: {:.4} (p = {:.4})", diag.jb_stat, diag.jb_p_value);
if diag.non_normal_residuals() {
println!(" β Non-normal residuals");
}
// Overall assessment
let issues = diag.count_issues();
if issues == 0 {
println!(" β All diagnostic tests passed");
} else {
println!(" β {} diagnostic test(s) failed", issues);
}
}
// Export to CSV
let csv = result.to_csv_string();
All models return:
Frenchrs is optimized for maximum efficiency:
ndarray-linalgImplemented:
Planned:
Developed with β€οΈ in Rust for the quantitative finance community.