pub mod engine;
pub mod models;
pub mod risk;
pub mod error;
pub use error::{MonteCarloError, Result};
pub mod prelude {
pub use super::engine::{
percentile, Budget, ConvergenceDiagnostics, MonteCarloEngine, MonteCarloRng, PathMetadata,
Percentiles, SimulationModel, SimulationPath, SimulationResult, Statistics, TimeHorizon,
TimeStep, VarianceReduction,
};
pub use super::error::{MonteCarloError, Result};
pub use super::models::{EmpiricalBootstrap, GeometricBrownianMotion, MertonJumpDiffusion};
pub use super::risk::{
calmar_ratio, gain_to_pain_ratio, information_ratio, jensens_alpha, omega_ratio,
sharpe_ratio, sharpe_ratio_annualized, sortino_ratio, treynor_ratio, CVaR,
DrawdownAnalysis, DrawdownStatistics, RiskReport, VaR,
};
}
#[cfg(test)]
mod tests {
use super::prelude::*;
#[test]
fn test_prelude_imports() {
let _rng = MonteCarloRng::new(42);
let _engine = MonteCarloEngine::reproducible(42);
let _horizon = TimeHorizon::years(1);
}
#[test]
fn test_end_to_end_simulation() {
let engine = MonteCarloEngine::reproducible(42)
.with_n_simulations(100)
.with_variance_reduction(VarianceReduction::Antithetic);
let model = GeometricBrownianMotion::new(100.0, 0.08, 0.20);
let horizon = TimeHorizon::years(1);
let result = engine.simulate(&model, &horizon);
assert_eq!(result.n_paths(), 100);
let stats = result.final_value_statistics();
assert!(stats.mean > 0.0);
}
#[test]
fn test_risk_report_integration() {
let mut rng = MonteCarloRng::new(42);
let paths: Vec<SimulationPath> = (0..100)
.map(|i| {
let values: Vec<f64> = (0..12)
.map(|j| {
if j == 0 {
100.0
} else {
100.0 * (1.0 + rng.normal(0.01, 0.05))
}
})
.collect();
SimulationPath::new(
(0..12).map(|j| j as f64 / 12.0).collect(),
values,
PathMetadata {
path_id: i,
seed: 42,
is_antithetic: false,
},
)
})
.collect();
let report = RiskReport::from_paths(&paths, 0.02).expect("Should generate report");
assert!(report.var_90 >= 0.0);
assert!(report.cvar_90 >= report.var_90 - 0.01);
assert!(report.sharpe_ratio.is_finite());
}
}