@@ -61,7 +61,7 @@ pub struct CalibrationHistory<T> {
6161#[ derive( ImplNew , Clone ) ]
6262pub struct HestonCalibrator {
6363 /// Params to calibrate.
64- pub params : HestonParams ,
64+ pub params : Option < HestonParams > ,
6565 /// Option prices from the market.
6666 pub c_market : DVector < f64 > ,
6767 /// Asset price vector.
@@ -88,6 +88,10 @@ impl HestonCalibrator {
8888 pub fn calibrate ( & self ) -> Result < Vec < CalibrationHistory < HestonParams > > > {
8989 println ! ( "Initial guess: {:?}" , self . params) ;
9090
91+ if self . params . is_none ( ) {
92+ return panic ! ( "Initial parameters are not set. You can use set_initial_params method to guess the initial parameters." ) ;
93+ }
94+
9195 let ( result, ..) = LevenbergMarquardt :: new ( ) . minimize ( self . clone ( ) ) ;
9296
9397 // Print the c_market
@@ -111,7 +115,7 @@ impl HestonCalibrator {
111115 ///
112116 /// Using NMLE (Normal Maximum Likelihood Estimation) method
113117 pub fn set_initial_params ( & mut self , s : Array1 < f64 > , v : Array1 < f64 > , r : f64 ) {
114- self . params = nmle_heston ( s, v, r) ;
118+ self . params = Some ( nmle_heston ( s, v, r) ) ;
115119 }
116120}
117121
@@ -121,28 +125,29 @@ impl<'a> LeastSquaresProblem<f64, Dyn, Dyn> for HestonCalibrator {
121125 type ResidualStorage = Owned < f64 , Dyn > ;
122126
123127 fn set_params ( & mut self , params : & DVector < f64 > ) {
124- self . params = HestonParams :: from ( params. clone ( ) ) ;
128+ self . params = Some ( HestonParams :: from ( params. clone ( ) ) ) ;
125129 }
126130
127131 fn params ( & self ) -> DVector < f64 > {
128- self . params . clone ( ) . into ( )
132+ self . params . clone ( ) . unwrap ( ) . into ( )
129133 }
130134
131135 fn residuals ( & self ) -> Option < DVector < f64 > > {
132136 let mut c_model = DVector :: zeros ( self . c_market . len ( ) ) ;
133137 let mut derivates = Vec :: new ( ) ;
138+ let params = self . params . clone ( ) . unwrap ( ) ;
134139
135140 for ( idx, _) in self . c_market . iter ( ) . enumerate ( ) {
136141 let pricer = HestonPricer :: new (
137142 self . s [ idx] ,
138- self . params . v0 ,
143+ params. v0 ,
139144 self . k [ idx] ,
140145 self . r ,
141146 self . q ,
142- self . params . rho ,
143- self . params . kappa ,
144- self . params . theta ,
145- self . params . sigma ,
147+ params. rho ,
148+ params. kappa ,
149+ params. theta ,
150+ params. sigma ,
146151 None ,
147152 Some ( self . tau ) ,
148153 None ,
@@ -161,7 +166,7 @@ impl<'a> LeastSquaresProblem<f64, Dyn, Dyn> for HestonCalibrator {
161166 . push ( CalibrationHistory {
162167 residuals : c_model. clone ( ) - self . c_market . clone ( ) ,
163168 call_put : vec ! [ ( call, put) ] . into ( ) ,
164- params : self . params . clone ( ) . into ( ) ,
169+ params : params. clone ( ) ,
165170 loss_scores : CalibrationLossScore {
166171 mae : self . mae ( & self . c_market , & c_model) ,
167172 mse : self . mse ( & self . c_market , & c_model) ,
@@ -195,6 +200,8 @@ impl<'a> LeastSquaresProblem<f64, Dyn, Dyn> for HestonCalibrator {
195200
196201#[ cfg( test) ]
197202mod tests {
203+ use std:: cmp:: Ordering ;
204+
198205 use super :: * ;
199206
200207 use anyhow:: Result ;
@@ -221,13 +228,52 @@ mod tests {
221228
222229 for v in v0. iter ( ) {
223230 let calibrator = HestonCalibrator :: new (
224- HestonParams {
231+ Some ( HestonParams {
225232 v0 : * v,
226233 theta : 6.47e-5 ,
227234 rho : -1.98e-3 ,
228235 kappa : 6.57e-3 ,
229236 sigma : 5.09e-4 ,
230- } ,
237+ } ) ,
238+ c_market. clone ( ) . into ( ) ,
239+ s. clone ( ) . into ( ) ,
240+ k. clone ( ) . into ( ) ,
241+ tau,
242+ 6.40e-4 ,
243+ None ,
244+ OptionType :: Call ,
245+ ) ;
246+
247+ let data = calibrator. calibrate ( ) ?;
248+ println ! ( "Calibration data: {:?}" , data) ;
249+ }
250+
251+ Ok ( ( ) )
252+ }
253+
254+ #[ test]
255+ fn test_heston_calibrate_guess_params ( ) -> Result < ( ) > {
256+ let tau = 24.0 / 365.0 ;
257+ println ! ( "Time to maturity: {}" , tau) ;
258+
259+ let s = vec ! [
260+ 425.73 , 425.73 , 425.73 , 425.67 , 425.68 , 425.65 , 425.65 , 425.68 , 425.65 , 425.16 , 424.78 ,
261+ 425.19 ,
262+ ] ;
263+
264+ let k = vec ! [
265+ 395.0 , 400.0 , 405.0 , 410.0 , 415.0 , 420.0 , 425.0 , 430.0 , 435.0 , 440.0 , 445.0 , 450.0 ,
266+ ] ;
267+
268+ let c_market = vec ! [
269+ 30.75 , 25.88 , 21.00 , 16.50 , 11.88 , 7.69 , 4.44 , 2.10 , 0.78 , 0.25 , 0.10 , 0.10 ,
270+ ] ;
271+
272+ let v0 = Array1 :: linspace ( 0.0 , 0.01 , 1 ) ;
273+
274+ for v in v0. iter ( ) {
275+ let mut calibrator = HestonCalibrator :: new (
276+ None ,
231277 c_market. clone ( ) . into ( ) ,
232278 s. clone ( ) . into ( ) ,
233279 k. clone ( ) . into ( ) ,
@@ -236,6 +282,7 @@ mod tests {
236282 None ,
237283 OptionType :: Call ,
238284 ) ;
285+ calibrator. set_initial_params ( s. clone ( ) . into ( ) , Array1 :: from_elem ( s. len ( ) , * v) , 6.40e-4 ) ;
239286
240287 let data = calibrator. calibrate ( ) ?;
241288 println ! ( "Calibration data: {:?}" , data) ;
0 commit comments