finance_solution/derivatives/
state.rs1use crate::derivatives::black_scholes::{bsm_greeks, bsm_price, BsmGreeks};
61use crate::derivatives::implied_vol::bsm_implied_vol;
62use crate::derivatives::types::{validate_bsm_params, BsmParams, OptionType};
63use crate::util::error::{require_finite, FinanceResult};
64
65#[derive(Clone, Debug, PartialEq)]
69pub struct BsmState {
70 params: BsmParams,
71 option_type: OptionType,
72}
73
74impl BsmState {
75 pub fn new(params: BsmParams, option_type: OptionType) -> FinanceResult<Self> {
77 validate_bsm_params(params)?;
78 Ok(Self {
79 params,
80 option_type,
81 })
82 }
83
84 pub fn params(&self) -> BsmParams {
85 self.params
86 }
87
88 pub fn option_type(&self) -> OptionType {
89 self.option_type
90 }
91
92 pub fn set_spot(&mut self, spot: f64) -> FinanceResult<()> {
94 require_finite("spot", spot)?;
95 let mut p = self.params;
96 p.spot = spot;
97 validate_bsm_params(p)?;
98 self.params = p;
99 Ok(())
100 }
101
102 pub fn set_vol(&mut self, vol: f64) -> FinanceResult<()> {
104 require_finite("vol", vol)?;
105 let mut p = self.params;
106 p.vol = vol;
107 validate_bsm_params(p)?;
108 self.params = p;
109 Ok(())
110 }
111
112 pub fn set_time_years(&mut self, time_years: f64) -> FinanceResult<()> {
114 require_finite("time_years", time_years)?;
115 let mut p = self.params;
116 p.time_years = time_years;
117 validate_bsm_params(p)?;
118 self.params = p;
119 Ok(())
120 }
121
122 pub fn set_strike(&mut self, strike: f64) -> FinanceResult<()> {
124 require_finite("strike", strike)?;
125 let mut p = self.params;
126 p.strike = strike;
127 validate_bsm_params(p)?;
128 self.params = p;
129 Ok(())
130 }
131
132 pub fn set_rate(&mut self, rate: f64) -> FinanceResult<()> {
133 require_finite("rate", rate)?;
134 let mut p = self.params;
135 p.rate = rate;
136 validate_bsm_params(p)?;
137 self.params = p;
138 Ok(())
139 }
140
141 pub fn set_dividend_yield(&mut self, dividend_yield: f64) -> FinanceResult<()> {
142 require_finite("dividend_yield", dividend_yield)?;
143 let mut p = self.params;
144 p.dividend_yield = dividend_yield;
145 validate_bsm_params(p)?;
146 self.params = p;
147 Ok(())
148 }
149
150 pub fn set_vol_from_price(&mut self, market_price: f64) -> FinanceResult<f64> {
152 let iv = bsm_implied_vol(self.params, self.option_type, market_price)?;
153 self.set_vol(iv)?;
154 Ok(iv)
155 }
156
157 pub fn price(&self) -> FinanceResult<f64> {
159 bsm_price(self.params, self.option_type)
160 }
161
162 pub fn greeks(&self) -> FinanceResult<BsmGreeks> {
164 bsm_greeks(self.params, self.option_type)
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn set_spot_moves_delta() {
174 let p = BsmParams::atm_one_year(100.0, 0.05, 0.2);
175 let mut s = BsmState::new(p, OptionType::Call).unwrap();
176 let d0 = s.greeks().unwrap().delta;
177 s.set_spot(110.0).unwrap();
178 let d1 = s.greeks().unwrap().delta;
179 assert!(d1 > d0);
180 }
181
182 #[test]
183 fn set_vol_from_price_round_trip() {
184 let p = BsmParams::atm_one_year(100.0, 0.05, 0.22);
185 let mut s = BsmState::new(p, OptionType::Call).unwrap();
186 let px = s.price().unwrap();
187 s.set_vol(0.10).unwrap();
188 let iv = s.set_vol_from_price(px).unwrap();
189 assert!((iv - 0.22).abs() < 1e-5);
190 }
191}