use crate::error::{PricingError, PricingResult};
use crate::pricing::black_76::black_76;
use crate::pricing::black_scholes_model::black_scholes;
use crate::pricing::garman_kohlhagen::garman_kohlhagen;
use optionstratlib_core::model::Options;
use optionstratlib_core::model::Positive;
pub trait MonteCarloPricer: Send + Sync {
fn price_monte_carlo(&self, option: &Options) -> PricingResult<Positive>;
}
impl<M> MonteCarloPricer for &M
where
M: MonteCarloPricer + ?Sized,
{
#[inline]
fn price_monte_carlo(&self, option: &Options) -> PricingResult<Positive> {
(**self).price_monte_carlo(option)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct NoMonteCarlo;
impl MonteCarloPricer for NoMonteCarlo {
#[cold]
#[inline(never)]
fn price_monte_carlo(&self, _option: &Options) -> PricingResult<Positive> {
Err(PricingError::simulation_error(
"no Monte Carlo pricer is configured for this pricing engine",
))
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum GenericPricingEngine<M: MonteCarloPricer = NoMonteCarlo> {
ClosedFormBS,
ClosedFormBlack76,
MonteCarlo {
simulator: M,
},
ClosedFormGK,
}
pub type ClosedFormEngine = GenericPricingEngine<NoMonteCarlo>;
#[inline]
fn price_closed_form_bs(option: &Options) -> PricingResult<Positive> {
let price_decimal = black_scholes(option)?;
Ok(Positive::new_decimal(price_decimal.abs())?)
}
#[inline]
fn price_closed_form_black76(option: &Options) -> PricingResult<Positive> {
let price_decimal = black_76(option)?;
Ok(Positive::new_decimal(price_decimal.abs())?)
}
#[inline]
fn price_closed_form_gk(option: &Options) -> PricingResult<Positive> {
let price_decimal = garman_kohlhagen(option)?;
Ok(Positive::new_decimal(price_decimal.abs())?)
}
#[must_use = "the price is the only product of this call"]
pub fn price_option_with<M>(
option: &Options,
engine: &GenericPricingEngine<M>,
) -> PricingResult<Positive>
where
M: MonteCarloPricer,
{
match engine {
GenericPricingEngine::ClosedFormBS => price_closed_form_bs(option),
GenericPricingEngine::ClosedFormBlack76 => price_closed_form_black76(option),
GenericPricingEngine::MonteCarlo { simulator } => simulator.price_monte_carlo(option),
GenericPricingEngine::ClosedFormGK => price_closed_form_gk(option),
}
}
pub trait Priceable {
fn price<M>(&self, engine: &GenericPricingEngine<M>) -> PricingResult<Positive>
where
M: MonteCarloPricer;
}
impl Priceable for Options {
#[inline]
fn price<M>(&self, engine: &GenericPricingEngine<M>) -> PricingResult<Positive>
where
M: MonteCarloPricer,
{
price_option_with(self, engine)
}
}