use libitofin::exercise::{AmericanExercise, EuropeanExercise, Exercise};
use libitofin::handle::Handle;
use libitofin::instrument::Instrument;
use libitofin::instruments::{PlainVanillaPayoff, StrikedTypePayoff, VanillaOption};
use libitofin::interestrate::Compounding;
use libitofin::math::randomnumbers::rngtraits::PseudoRandom;
use libitofin::option::OptionType;
use libitofin::pricingengine::PricingEngine;
use libitofin::pricingengines::vanilla::{
AnalyticEuropeanEngine, MakeMcAmericanEngine, MakeMcEuropeanEngine,
};
use libitofin::processes::GeneralizedBlackScholesProcess;
use libitofin::quotes::{Quote, SimpleQuote};
use libitofin::settings::Settings;
use libitofin::shared::{Shared, SharedMut, shared, shared_mut};
use libitofin::termstructures::volatility::{BlackConstantVol, BlackVolTermStructure};
use libitofin::termstructures::yields::FlatForward;
use libitofin::termstructures::yieldtermstructure::YieldTermStructure;
use libitofin::time::date::{Date, Month};
use libitofin::time::daycounters::actual365fixed::Actual365Fixed;
use libitofin::time::frequency::Frequency;
use libitofin::types::{Rate, Real, Volatility};
fn flat_curve(reference: Date, rate: Rate) -> Handle<dyn YieldTermStructure> {
Handle::new(shared(FlatForward::with_rate(
reference,
rate,
Actual365Fixed::new(),
Compounding::Continuous,
Frequency::Annual,
)) as Shared<dyn YieldTermStructure>)
}
fn bs_process(
reference: Date,
spot: Real,
q: Rate,
r: Rate,
vol: Volatility,
) -> Shared<GeneralizedBlackScholesProcess> {
let spot = Handle::new(shared(SimpleQuote::new(spot)) as Shared<dyn Quote>);
let vol = Handle::new(shared(BlackConstantVol::new(
reference,
None, vol,
Actual365Fixed::new(),
)) as Shared<dyn BlackVolTermStructure>);
shared(GeneralizedBlackScholesProcess::new(
spot,
flat_curve(reference, q),
flat_curve(reference, r),
vol,
))
}
fn main() {
{
let today = Date::new(15, Month::June, 2026);
let maturity = Date::new(15, Month::June, 2027); let settings = shared(Settings::new());
settings.set_evaluation_date(today);
let process = bs_process(today, 100.0, 0.02, 0.05, 0.20);
let strike = 100.0;
let mut analytic_opt = VanillaOption::new(
shared(PlainVanillaPayoff::new(OptionType::Call, strike))
as Shared<dyn StrikedTypePayoff>,
shared(EuropeanExercise::new(maturity)) as Shared<dyn Exercise>,
Shared::clone(&settings),
);
analytic_opt
.base_mut()
.set_pricing_engine(
shared_mut(AnalyticEuropeanEngine::new(Shared::clone(&process)))
as SharedMut<dyn PricingEngine>,
);
let analytic = analytic_opt.npv().unwrap();
let mc_engine = MakeMcEuropeanEngine::<PseudoRandom>::new(Shared::clone(&process))
.with_steps(1)
.with_samples(40_000)
.with_seed(42)
.build()
.unwrap();
let mut mc_opt = VanillaOption::new(
shared(PlainVanillaPayoff::new(OptionType::Call, strike))
as Shared<dyn StrikedTypePayoff>,
shared(EuropeanExercise::new(maturity)) as Shared<dyn Exercise>,
Shared::clone(&settings),
);
mc_opt
.base_mut()
.set_pricing_engine(shared_mut(mc_engine) as SharedMut<dyn PricingEngine>);
let mc = mc_opt.npv().unwrap();
let se = mc_opt.error_estimate().unwrap();
println!("=== European call (S=100, K=100, r=5%, q=2%, sigma=20%, T~1y) ===");
println!(" analytic NPV = {analytic:.6}");
println!(" MC NPV = {mc:.6}");
println!(" MC error estimate = {se:.6}");
println!(
" |MC - analytic| = {:.6} (converged: {})",
(mc - analytic).abs(),
(mc - analytic).abs() < 3.0 * se
);
}
{
let eval_date = Date::new(15, Month::May, 1998);
let settlement = Date::new(17, Month::May, 1998); let maturity = Date::new(17, Month::May, 1999);
let settings = shared(Settings::new());
settings.set_evaluation_date(eval_date);
let process = bs_process(settlement, 36.0, 0.0, 0.06, 0.20);
let payoff =
shared(PlainVanillaPayoff::new(OptionType::Put, 36.0)) as Shared<dyn StrikedTypePayoff>;
let exercise =
shared(AmericanExercise::over(settlement, maturity).unwrap()) as Shared<dyn Exercise>;
let engine = MakeMcAmericanEngine::<PseudoRandom>::new(Shared::clone(&process))
.with_steps(75)
.with_antithetic_variate(true)
.with_absolute_tolerance(0.02)
.with_seed(42)
.with_polynomial_order(3)
.build()
.unwrap();
let mut american = VanillaOption::new(payoff, exercise, Shared::clone(&settings));
american
.base_mut()
.set_pricing_engine(shared_mut(engine) as SharedMut<dyn PricingEngine>);
let npv = american.npv().unwrap();
let se = american.error_estimate().unwrap();
let exercise_prob = american.result::<Real>("exerciseProbability").unwrap();
println!();
println!("=== American put (S=36, K=36, r=6%, q=0, sigma=20%, T~1y) ===");
println!(" MC NPV = {npv:.6} (QuantLib ref ~2.054422)");
println!(" MC error estimate = {se:.6}");
println!(" exercise probability = {exercise_prob:.6}");
}
}