@@ -55,7 +55,7 @@ class SabrStrippedOptionletAdapter : public QuantLib::OptionletVolatilityStructu
5555 const std::vector<std::vector<std::pair<Real, ParametricVolatility::ParameterCalibration>>>&
5656 initialModelParameters = {},
5757 const QuantLib::Size maxCalibrationAttempts = 10 , const QuantLib::Real exitEarlyErrorThreshold = 0.005 ,
58- const QuantLib::Real maxAcceptableError = 0.05 );
58+ const QuantLib::Real maxAcceptableError = 0.05 , bool stickySabr = false );
5959
6060 /* ! Constructor taking an explicit \p referenceDate and the term structure will therefore be not \e moving.
6161 */
@@ -105,6 +105,13 @@ class SabrStrippedOptionletAdapter : public QuantLib::OptionletVolatilityStructu
105105 calculate ();
106106 return parametricVolatility_;
107107 }
108+ QuantExt::SabrParametricVolatility::ModelVariant modelVariant () const { return modelVariant_; }
109+ QuantLib::Real modelDisplacement () const { return modelDisplacement_; }
110+ const std::vector<std::vector<std::pair<Real, ParametricVolatility::ParameterCalibration>>>&
111+ initialModelParameters () const { return initialModelParameters_; }
112+ QuantLib::Size maxCalibrationAttempts () const { return maxCalibrationAttempts_; }
113+ QuantLib::Real exitEarlyErrorThreshold () const { return exitEarlyErrorThreshold_; }
114+ QuantLib::Real maxAcceptableError () const { return maxAcceptableError_; }
108115 // @}
109116
110117protected:
@@ -130,6 +137,7 @@ class SabrStrippedOptionletAdapter : public QuantLib::OptionletVolatilityStructu
130137 QuantLib::Size maxCalibrationAttempts_;
131138 QuantLib::Real exitEarlyErrorThreshold_;
132139 QuantLib::Real maxAcceptableError_;
140+ bool stickySabr_;
133141
134142 // ! State
135143 mutable std::map<Real, QuantLib::ext::shared_ptr<ParametricVolatilitySmileSection>> cache_;
@@ -145,13 +153,13 @@ SabrStrippedOptionletAdapter<TimeInterpolator>::SabrStrippedOptionletAdapter(
145153 const QuantLib::Real modelDisplacement,
146154 const std::vector<std::vector<std::pair<Real, ParametricVolatility::ParameterCalibration>>>& initialModelParameters,
147155 const QuantLib::Size maxCalibrationAttempts, const QuantLib::Real exitEarlyErrorThreshold,
148- const QuantLib::Real maxAcceptableError)
156+ const QuantLib::Real maxAcceptableError, bool stickySabr )
149157 : OptionletVolatilityStructure(sob->settlementDays (), sob->calendar(), sob->businessDayConvention(),
150158 sob->dayCounter()),
151159 optionletBase_(sob), ti_(ti), modelVariant_(modelVariant), outputVolatilityType_(outputVolatilityType),
152160 outputDisplacement_(outputDisplacement), initialModelParameters_(initialModelParameters),
153161 maxCalibrationAttempts_(maxCalibrationAttempts), exitEarlyErrorThreshold_(exitEarlyErrorThreshold),
154- maxAcceptableError_(maxAcceptableError) {
162+ maxAcceptableError_(maxAcceptableError), stickySabr_(stickySabr) {
155163 registerWith (optionletBase_);
156164}
157165
@@ -238,6 +246,14 @@ inline void SabrStrippedOptionletAdapter<TimeInterpolator>::performCalculations(
238246 }
239247 }
240248
249+ // For sticky SABR, we only need to re-imply the alpha parameter after initial calibration
250+ if (stickySabr_) {
251+ if (auto sabr = QuantLib::ext::dynamic_pointer_cast<SabrParametricVolatility>(parametricVolatility_)) {
252+ parametricVolatility_ = sabr->clone (marketSmiles, {});
253+ return ;
254+ }
255+ }
256+
241257 std::map<Real, Real> modelShift;
242258 if (modelDisplacement_ != Null<Real>()) {
243259 modelShift[Null<Real>()] = modelDisplacement_;
@@ -250,6 +266,18 @@ inline void SabrStrippedOptionletAdapter<TimeInterpolator>::performCalculations(
250266 : ParametricVolatility::MarketQuoteType::ShiftedLognormalVolatility,
251267 Handle<YieldTermStructure>(), modelParameters, modelShift, maxCalibrationAttempts_, exitEarlyErrorThreshold_,
252268 maxAcceptableError_);
269+
270+ // for sticky SABR, after initial calibration, we re-create parametric volatility with only alpha to be implied
271+ // this ensures that basis between the two parametric volatilities is eliminated
272+ if (stickySabr_) {
273+ if (auto sabr = QuantLib::ext::dynamic_pointer_cast<SabrParametricVolatility>(parametricVolatility_)) {
274+ parametricVolatility_ = sabr->clone (marketSmiles,
275+ { ParametricVolatility::ParameterCalibration::Implied,
276+ ParametricVolatility::ParameterCalibration::Fixed,
277+ ParametricVolatility::ParameterCalibration::Fixed,
278+ ParametricVolatility::ParameterCalibration::Fixed });
279+ }
280+ }
253281}
254282
255283template <class TimeInterpolator > inline void SabrStrippedOptionletAdapter<TimeInterpolator>::deepUpdate() {
0 commit comments