surface_lib/models/
mod.rs1pub mod bs;
2pub mod linear_iv;
3pub mod svi;
4
5pub mod traits {
7 use anyhow::Result;
8
9 pub trait SurfaceModel {
11 type Parameters;
12
13 fn parameters(&self) -> &Self::Parameters;
14 fn validate_params(&self) -> Result<()>;
15 fn total_variance(&self, k: f64, t: f64) -> Result<f64>;
16 fn check_calendar_arbitrage(&self, k: f64, t1: f64, t2: f64) -> Result<()>;
17 fn check_butterfly_arbitrage_at_k(&self, k: f64, t: f64) -> Result<()>;
18 }
19}
20
21pub mod utils {
23 use crate::models::traits::SurfaceModel;
24 use anyhow::{anyhow, Result};
25
26 pub fn log_moneyness(strike: f64, spot: f64) -> f64 {
28 (strike / spot).ln()
29 }
30
31 pub struct OptionPricingResult {
33 pub price: f64,
34 pub model_iv: f64,
35 }
36
37 pub fn price_option<T: SurfaceModel>(
39 option_type: &str,
40 strike: f64,
41 spot: f64,
42 r: f64,
43 q: f64,
44 t: f64,
45 model: &T,
46 ) -> Result<OptionPricingResult> {
47 let k = log_moneyness(strike, spot);
48 let total_var = model.total_variance(k, t)?;
49
50 if total_var <= 0.0 {
51 return Err(anyhow!("Non-positive total variance: {}", total_var));
52 }
53
54 let model_iv = (total_var / t).sqrt();
55 let price = black_scholes_price(option_type, spot, strike, r, q, t, model_iv)?;
56
57 Ok(OptionPricingResult { price, model_iv })
58 }
59
60 fn black_scholes_price(
62 option_type: &str,
63 s: f64,
64 k: f64,
65 r: f64,
66 q: f64,
67 t: f64,
68 sigma: f64,
69 ) -> Result<f64> {
70 if sigma <= 0.0 || t <= 0.0 {
71 return Err(anyhow!("Invalid parameters: sigma={}, t={}", sigma, t));
72 }
73
74 let d1 = ((s / k).ln() + (r - q + 0.5 * sigma * sigma) * t) / (sigma * t.sqrt());
75 let d2 = d1 - sigma * t.sqrt();
76
77 let price = match option_type.to_lowercase().as_str() {
78 "call" => s * (-q * t).exp() * normal_cdf(d1) - k * (-r * t).exp() * normal_cdf(d2),
79 "put" => k * (-r * t).exp() * normal_cdf(-d2) - s * (-q * t).exp() * normal_cdf(-d1),
80 _ => return Err(anyhow!("Invalid option type: {}", option_type)),
81 };
82
83 Ok(price)
84 }
85
86 fn normal_cdf(x: f64) -> f64 {
88 0.5 * (1.0 + erf(x / 2.0_f64.sqrt()))
89 }
90
91 fn erf(x: f64) -> f64 {
93 let a1 = 0.254829592;
94 let a2 = -0.284496736;
95 let a3 = 1.421413741;
96 let a4 = -1.453152027;
97 let a5 = 1.061405429;
98 let p = 0.3275911;
99
100 let sign = if x < 0.0 { -1.0 } else { 1.0 };
101 let x = x.abs();
102
103 let t = 1.0 / (1.0 + p * x);
104 let y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * (-x * x).exp();
105
106 sign * y
107 }
108}