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