@@ -55,7 +55,10 @@ 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 , bool stickySabr = false );
58+ const QuantLib::Real maxAcceptableError = 0.05 ,
59+ const std::vector<std::vector<Real>>& strikes = {},
60+ const std::vector<std::vector<Handle<Quote>>>& volSpreads = {},
61+ bool stickySabr = false );
5962
6063 /* ! Constructor taking an explicit \p referenceDate and the term structure will therefore be not \e moving.
6164 */
@@ -68,7 +71,10 @@ class SabrStrippedOptionletAdapter : public QuantLib::OptionletVolatilityStructu
6871 const std::vector<std::vector<std::pair<Real, ParametricVolatility::ParameterCalibration>>>&
6972 initialModelParameters = {},
7073 const QuantLib::Size maxCalibrationAttempts = 10 , const QuantLib::Real exitEarlyErrorThreshold = 0.005 ,
71- const QuantLib::Real maxAcceptableError = 0.05 );
74+ const QuantLib::Real maxAcceptableError = 0.05 ,
75+ const std::vector<std::vector<Real>>& strikes = {},
76+ const std::vector<std::vector<Handle<Quote>>>& volSpreads = {},
77+ bool stickySabr = false );
7278
7379 // ! \name TermStructure interface
7480 // @{
@@ -111,6 +117,7 @@ class SabrStrippedOptionletAdapter : public QuantLib::OptionletVolatilityStructu
111117 initialModelParameters () const { return initialModelParameters_; }
112118 QuantLib::Size maxCalibrationAttempts () const { return maxCalibrationAttempts_; }
113119 QuantLib::Real exitEarlyErrorThreshold () const { return exitEarlyErrorThreshold_; }
120+ const std::vector<std::vector<Real>>& strikes () const { return strikes_; }
114121 QuantLib::Real maxAcceptableError () const { return maxAcceptableError_; }
115122 // @}
116123
@@ -122,6 +129,8 @@ class SabrStrippedOptionletAdapter : public QuantLib::OptionletVolatilityStructu
122129 // @}
123130
124131private:
132+ void init ();
133+
125134 // ! Base optionlet object that provides the stripped optionlet volatilities
126135 QuantLib::ext::shared_ptr<QuantLib::StrippedOptionletBase> optionletBase_;
127136
@@ -137,6 +146,8 @@ class SabrStrippedOptionletAdapter : public QuantLib::OptionletVolatilityStructu
137146 QuantLib::Size maxCalibrationAttempts_;
138147 QuantLib::Real exitEarlyErrorThreshold_;
139148 QuantLib::Real maxAcceptableError_;
149+ std::vector<std::vector<Real>> strikes_;
150+ std::vector<std::vector<Handle<Quote>>> volSpreads_;
140151 bool stickySabr_;
141152
142153 // ! State
@@ -145,6 +156,61 @@ class SabrStrippedOptionletAdapter : public QuantLib::OptionletVolatilityStructu
145156 mutable std::unique_ptr<FlatExtrapolation> atmInterpolation_;
146157};
147158
159+ template <class TimeInterpolator >
160+ inline void SabrStrippedOptionletAdapter<TimeInterpolator>::init() {
161+ registerWith (optionletBase_);
162+ Size nFixingDates = optionletBase_->optionletFixingDates ().size ();
163+
164+ // The dimension of input StrippedOptionletBase can be either
165+ //
166+ // - ATM only (only 1 optionlet strike for every fixing date)
167+ // SABR cube will be calibrated to the skew defined by strikes_ and volSpreads_
168+ //
169+ // or,
170+ //
171+ // - Smile (more than 1 optionlet strike for at least 1 fixing date)
172+ // SABR cube will be calibrated to the skew defined in the input StrippedOptionletBase
173+
174+ bool isAtm = true ;
175+ for (Size i = 0 ; i < nFixingDates; ++i)
176+ if (optionletBase_->optionletStrikes (i).size () > 1 )
177+ isAtm = isAtm && false ;
178+
179+ if (!isAtm) {
180+ QL_REQUIRE (strikes_.empty (),
181+ " When StrippedOptionletBase contains smiles, strikes "
182+ " inputs to SabrStrippedOptionletAdapter must be empty" );
183+ strikes_.resize (nFixingDates);
184+ for (Size i = 0 ; i < nFixingDates; ++i) {
185+ strikes_[i] = optionletBase_->optionletStrikes (i);
186+ }
187+ if (volSpreads_.empty ()) {
188+ volSpreads_.resize (nFixingDates);
189+ for (Size i = 0 ; i < nFixingDates; ++i) {
190+ volSpreads_[i] = std::vector<Handle<Quote>>(
191+ strikes_[i].size (), Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.0 )));
192+ }
193+ } else { /* do nothing, volSpreads_ will be validated below */ }
194+ }
195+
196+ QL_REQUIRE (nFixingDates == volSpreads_.size (),
197+ " mismatch between number of fixing dates (" <<
198+ nFixingDates << " ) and number of rows (" <<
199+ volSpreads_.size () << " )" );
200+ for (Size i = 0 ; i < volSpreads_.size (); i++) {
201+ Size nStrikes = strikes_[i].size ();
202+ QL_REQUIRE (nStrikes == volSpreads_[i].size (),
203+ " mismatch between number of strikes (" << nStrikes <<
204+ " ) and number of columns (" << volSpreads_[i].size () <<
205+ " ) in the " << io::ordinal (i+1 ) << " row" );
206+ }
207+ if (!stickySabr_) {
208+ for (auto const & v : volSpreads_)
209+ for (auto const & s : v)
210+ registerWith (s);
211+ }
212+ }
213+
148214template <class TimeInterpolator >
149215SabrStrippedOptionletAdapter<TimeInterpolator>::SabrStrippedOptionletAdapter(
150216 const QuantLib::ext::shared_ptr<QuantLib::StrippedOptionletBase>& sob,
@@ -153,14 +219,15 @@ SabrStrippedOptionletAdapter<TimeInterpolator>::SabrStrippedOptionletAdapter(
153219 const QuantLib::Real modelDisplacement,
154220 const std::vector<std::vector<std::pair<Real, ParametricVolatility::ParameterCalibration>>>& initialModelParameters,
155221 const QuantLib::Size maxCalibrationAttempts, const QuantLib::Real exitEarlyErrorThreshold,
156- const QuantLib::Real maxAcceptableError, bool stickySabr)
222+ const QuantLib::Real maxAcceptableError, const std::vector<std::vector<Real>>& strikes,
223+ const std::vector<std::vector<Handle<Quote>>>& volSpreads, bool stickySabr)
157224 : OptionletVolatilityStructure(sob->settlementDays (), sob->calendar(), sob->businessDayConvention(),
158225 sob->dayCounter()),
159226 optionletBase_(sob), ti_(ti), modelVariant_(modelVariant), outputVolatilityType_(outputVolatilityType),
160227 outputDisplacement_(outputDisplacement), initialModelParameters_(initialModelParameters),
161228 maxCalibrationAttempts_(maxCalibrationAttempts), exitEarlyErrorThreshold_(exitEarlyErrorThreshold),
162- maxAcceptableError_(maxAcceptableError), stickySabr_(stickySabr) {
163- registerWith (optionletBase_ );
229+ maxAcceptableError_(maxAcceptableError), strikes_(strikes), volSpreads_(volSpreads), stickySabr_(stickySabr) {
230+ init ( );
164231}
165232
166233template <class TimeInterpolator >
@@ -171,13 +238,14 @@ SabrStrippedOptionletAdapter<TimeInterpolator>::SabrStrippedOptionletAdapter(
171238 const QuantLib::Real modelDiscplacement,
172239 const std::vector<std::vector<std::pair<Real, ParametricVolatility::ParameterCalibration>>>& initialModelParameters,
173240 const QuantLib::Size maxCalibrationAttempts, const QuantLib::Real exitEarlyErrorThreshold,
174- const QuantLib::Real maxAcceptableError)
241+ const QuantLib::Real maxAcceptableError, const std::vector<std::vector<Real>>& strikes,
242+ const std::vector<std::vector<Handle<Quote>>>& volSpreads, bool stickySabr)
175243 : OptionletVolatilityStructure(referenceDate, sob->calendar (), sob->businessDayConvention(), sob->dayCounter()),
176244 optionletBase_(sob), ti_(ti), modelVariant_(modelVariant), outputVolatilityType_(outputVolatilityType),
177245 outputDisplacement_(outputDisplacement), initialModelParameters_(initialModelParameters),
178246 maxCalibrationAttempts_(maxCalibrationAttempts), exitEarlyErrorThreshold_(exitEarlyErrorThreshold),
179- maxAcceptableError_(maxAcceptableError) {
180- registerWith (optionletBase_ );
247+ maxAcceptableError_(maxAcceptableError), strikes_(strikes), volSpreads_(volSpreads), stickySabr_(stickySabr) {
248+ init ( );
181249}
182250
183251template <class TimeInterpolator >
@@ -233,13 +301,26 @@ inline void SabrStrippedOptionletAdapter<TimeInterpolator>::performCalculations(
233301 << this ->optionletBase ()->optionletFixingTimes ().size () << " )" );
234302 for (Size i = 0 ; i < this ->optionletBase ()->optionletFixingTimes ().size (); ++i) {
235303 Real forward = atmInterpolation_->operator ()(this ->optionletBase ()->optionletFixingTimes ()[i]);
304+ auto optionletStrikes = strikes_.empty () ? this ->optionletBase ()->optionletStrikes (i) : strikes_[i];
305+ QL_REQUIRE (!optionletStrikes.empty (),
306+ " SabrStrippedOptionletAdapter: no optionlet strikes for optionlet fixing time "
307+ << this ->optionletBase ()->optionletFixingTimes ()[i]);
308+ auto optionletVolatilities = this ->optionletBase ()->optionletVolatilities (i);
309+ QL_REQUIRE (!optionletVolatilities.empty (),
310+ " SabrStrippedOptionletAdapter: no optionlet volatilities for optionlet fixing time "
311+ << this ->optionletBase ()->optionletFixingTimes ()[i]);
312+ if (optionletVolatilities.size () == 1 && optionletStrikes.size () > 1 )
313+ optionletVolatilities = std::vector<Real>(optionletStrikes.size (), optionletVolatilities[0 ]);
314+ for (Size j = 0 ; j < optionletVolatilities.size (); ++j) {
315+ optionletVolatilities[j] += volSpreads_[i][j]->value ();
316+ }
236317 marketSmiles.push_back (ParametricVolatility::MarketSmile{this ->optionletBase ()->optionletFixingTimes ()[i],
237318 Null<Real>(),
238319 forward,
239320 optionletBase_->displacement (),
240321 {},
241- this -> optionletBase ()-> optionletStrikes (i) ,
242- this -> optionletBase ()-> optionletVolatilities (i) });
322+ optionletStrikes,
323+ optionletVolatilities});
243324 if (!initialModelParameters_.empty ()) {
244325 modelParameters[std::make_pair (this ->optionletBase ()->optionletFixingTimes ()[i], Null<Real>())] =
245326 initialModelParameters_.size () == 1 ? initialModelParameters_.front () : initialModelParameters_[i];
0 commit comments