@@ -78,7 +78,7 @@ impl BSMCalibrator {
7878 println ! ( "Calibration report: {:?}" , result. params) ;
7979 }
8080
81- pub fn set_intial_guess ( & mut self , params : BSMParams ) {
81+ pub fn set_initial_guess ( & mut self , params : BSMParams ) {
8282 self . params = params;
8383 }
8484}
@@ -97,7 +97,9 @@ impl LeastSquaresProblem<f64, Dyn, Dyn> for BSMCalibrator {
9797 }
9898
9999 fn residuals ( & self ) -> Option < DVector < f64 > > {
100- let mut c_model = DVector :: zeros ( self . c_market . len ( ) ) ;
100+ let n = self . c_market . len ( ) ;
101+ let mut c_model = DVector :: zeros ( n) ;
102+ let mut vegas: Vec < f64 > = Vec :: with_capacity ( n) ;
101103 let mut derivates = Vec :: new ( ) ;
102104
103105 for ( idx, _) in self . c_market . iter ( ) . enumerate ( ) {
@@ -106,8 +108,8 @@ impl LeastSquaresProblem<f64, Dyn, Dyn> for BSMCalibrator {
106108 self . params . v ,
107109 self . k [ idx] ,
108110 self . r ,
109- None ,
110- None ,
111+ self . r_d ,
112+ self . r_f ,
111113 self . q ,
112114 Some ( self . tau ) ,
113115 None ,
@@ -122,6 +124,10 @@ impl LeastSquaresProblem<f64, Dyn, Dyn> for BSMCalibrator {
122124 OptionType :: Put => c_model[ idx] = put,
123125 }
124126
127+ // Collect vega for vega-weighted residuals (calibration in vol space)
128+ let vega = pricer. vega ( ) . abs ( ) . max ( 1e-8 ) ;
129+ vegas. push ( vega) ;
130+
125131 self
126132 . calibration_history
127133 . borrow_mut ( )
@@ -145,18 +151,52 @@ impl LeastSquaresProblem<f64, Dyn, Dyn> for BSMCalibrator {
145151 }
146152
147153 let _ = std:: mem:: replace ( & mut * self . derivates . borrow_mut ( ) , derivates) ;
148- Some ( c_model - self . c_market . clone ( ) )
154+
155+ // Vega-weighted residuals approximate minimizing implied vol differences
156+ let mut residuals = DVector :: zeros ( n) ;
157+ for i in 0 ..n {
158+ residuals[ i] = ( c_model[ i] - self . c_market [ i] ) / vegas[ i] ;
159+ }
160+
161+ Some ( residuals)
149162 }
150163
151164 fn jacobian ( & self ) -> Option < DMatrix < f64 > > {
152- let derivates = self . derivates . borrow ( ) ;
153- let derivates = derivates. iter ( ) . flatten ( ) . cloned ( ) . collect :: < Vec < f64 > > ( ) ;
165+ // For vega-weighted residuals r = (C_model - C_mkt)/Vega,
166+ // dr/dsigma = 1 - r * (Vomma / Vega)
167+ let n = self . c_market . len ( ) ;
168+ let mut J = DMatrix :: zeros ( n, 1 ) ;
154169
155- // The Jacobian matrix is a matrix of partial derivatives
156- // of the residuals with respect to the parameters.
157- let jacobian = DMatrix :: from_vec ( derivates. len ( ) / 5 , 5 , derivates) ;
170+ for idx in 0 ..n {
171+ let pricer = BSMPricer :: new (
172+ self . s [ idx] ,
173+ self . params . v ,
174+ self . k [ idx] ,
175+ self . r ,
176+ self . r_d ,
177+ self . r_f ,
178+ self . q ,
179+ Some ( self . tau ) ,
180+ None ,
181+ None ,
182+ self . option_type ,
183+ BSMCoc :: BSM1973 ,
184+ ) ;
185+
186+ let ( call, put) = pricer. calculate_call_put ( ) ;
187+ let c_model_i = match self . option_type {
188+ OptionType :: Call => call,
189+ OptionType :: Put => put,
190+ } ;
191+
192+ let vega = pricer. vega ( ) . abs ( ) . max ( 1e-8 ) ;
193+ let vomma = pricer. vomma ( ) ;
194+ let r_i = ( c_model_i - self . c_market [ idx] ) / vega;
195+
196+ J [ ( idx, 0 ) ] = 1.0 - r_i * ( vomma / vega) ;
197+ }
158198
159- Some ( jacobian )
199+ Some ( J )
160200 }
161201}
162202
0 commit comments