24#ifndef quantext_optionletstripper_with_atm_hpp
25#define quantext_optionletstripper_with_atm_hpp
34#include <ql/instruments/capfloor.hpp>
35#include <ql/instruments/makecapfloor.hpp>
36#include <ql/math/solvers1d/brent.hpp>
37#include <ql/pricingengines/capfloor/bacheliercapfloorengine.hpp>
38#include <ql/pricingengines/capfloor/blackcapfloorengine.hpp>
39#include <ql/quotes/simplequote.hpp>
40#include <ql/cashflows/cashflows.hpp>
41#include <ql/termstructures/volatility/optionlet/constantoptionletvol.hpp>
49template <
class TimeInterpolator,
class SmileInterpolator>
55 const QuantLib::Handle<CapFloorTermVolCurve>& atmCurve,
56 const QuantLib::Handle<QuantLib::YieldTermStructure>& discount =
57 QuantLib::Handle<QuantLib::YieldTermStructure>(),
58 const QuantLib::VolatilityType atmVolatilityType = QuantLib::ShiftedLognormal,
59 QuantLib::Real atmDisplacement = 0.0, QuantLib::Size maxEvaluations = 10000,
60 QuantLib::Real accuracy = 1.0e-12,
const TimeInterpolator& ti = TimeInterpolator(),
61 const SmileInterpolator& si = SmileInterpolator());
64 std::vector<QuantLib::Rate>
atmStrikes()
const;
65 std::vector<QuantLib::Real>
atmPrices()
const;
66 std::vector<QuantLib::Volatility>
volSpreads()
const;
76 std::vector<QuantLib::Volatility>
volSpreads(
const QuantLib::Handle<QuantLib::YieldTermStructure>& discount,
77 const QuantLib::Handle<QuantLib::OptionletVolatilityStructure>& ovs,
78 const bool isOis)
const;
83 ObjectiveFunction(
const QuantLib::Handle<QuantLib::OptionletVolatilityStructure>& ovs,
84 const QuantLib::ext::shared_ptr<QuantLib::CapFloor>& cap, QuantLib::Real targetValue,
85 const QuantLib::Handle<QuantLib::YieldTermStructure>& discount);
87 Real
operator()(QuantLib::Volatility volSpread)
const;
91 QuantLib::ext::shared_ptr<QuantLib::CapFloor>
cap_;
93 QuantLib::Handle<QuantLib::YieldTermStructure>
discount_;
98 ObjectiveFunctionOIS(
const QuantLib::Handle<QuantLib::OptionletVolatilityStructure>& ovs,
const Leg& cap,
99 QuantLib::Real targetValue,
100 const QuantLib::Handle<QuantLib::YieldTermStructure>& discount);
102 Real
operator()(QuantLib::Volatility volSpread)
const;
108 QuantLib::Handle<QuantLib::YieldTermStructure>
discount_;
112 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper>
osBase_;
144 mutable std::vector<QuantLib::ext::shared_ptr<QuantLib::CapFloor> >
caps_;
148template <
class TimeInterpolator,
class SmileInterpolator>
150 const QuantLib::ext::shared_ptr<QuantExt::OptionletStripper>& osBase,
151 const QuantLib::Handle<CapFloorTermVolCurve>& atmCurve,
152 const QuantLib::Handle<QuantLib::YieldTermStructure>& discount,
const QuantLib::VolatilityType atmVolatilityType,
153 QuantLib::Real atmDisplacement, QuantLib::Size maxEvaluations, QuantLib::Real accuracy,
const TimeInterpolator& ti,
154 const SmileInterpolator& si)
156 osBase->displacement(), osBase->rateComputationPeriod()),
157 osBase_(osBase), atmCurve_(atmCurve), atmVolatilityType_(atmVolatilityType), atmDisplacement_(atmDisplacement),
158 maxEvaluations_(maxEvaluations), accuracy_(accuracy), dayCounter_(osBase_->termVolSurface()->dayCounter()),
159 nAtmExpiries_(atmCurve_->optionTenors().size()), atmStrikes_(nAtmExpiries_), atmPrices_(nAtmExpiries_),
160 volSpreads_(nAtmExpiries_), caps_(nAtmExpiries_), capsOIS_(nAtmExpiries_) {
166 "The ATM curve day counter should equal that of the underlying base optionlet stripper");
169template <
class TimeInterpolator,
class SmileInterpolator>
175template <
class TimeInterpolator,
class SmileInterpolator>
181template <
class TimeInterpolator,
class SmileInterpolator>
182inline std::vector<QuantLib::Volatility>
188template <
class TimeInterpolator,
class SmileInterpolator>
192 using QuantLib::BachelierCapFloorEngine;
193 using QuantLib::BlackCapFloorEngine;
194 using QuantLib::Handle;
195 using QuantLib::MakeCapFloor;
196 using QuantLib::OptionletVolatilityStructure;
197 using QuantLib::Period;
198 using QuantLib::Size;
199 using QuantLib::Time;
200 using QuantLib::Volatility;
201 using QuantLib::YieldTermStructure;
204 bool isOis = QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(index_) !=
nullptr;
207 optionletDates_ = osBase_->optionletFixingDates();
208 optionletPaymentDates_ = osBase_->optionletPaymentDates();
209 optionletAccrualPeriods_ = osBase_->optionletAccrualPeriods();
210 optionletTimes_ = osBase_->optionletFixingTimes();
211 atmOptionletRate_ = osBase_->atmOptionletRates();
212 for (Size i = 0; i < optionletTimes_.size(); ++i) {
213 optionletStrikes_[i] = osBase_->optionletStrikes(i);
214 optionletVolatilities_[i] = osBase_->optionletVolatilities(i);
218 const vector<Period>& atmTenors = atmCurve_->optionTenors();
219 vector<Time> atmTimes(atmTenors.size());
220 for (Size i = 0; i < atmTenors.size(); ++i) {
221 Date d = atmCurve_->optionDateFromTenor(atmTenors[i]);
222 atmTimes[i] = atmCurve_->timeFromReference(d);
226 const Handle<YieldTermStructure>& discountCurve = discount_.empty() ? index_->forwardingTermStructure() : discount_;
229 for (Size j = 0; j < nAtmExpiries_; ++j) {
233 Volatility atmVol = atmCurve_->volatility(atmTimes[j], 0.01);
234 auto pricer = QuantLib::ext::make_shared<BlackOvernightIndexedCouponPricer>(
235 Handle<OptionletVolatilityStructure>(QuantLib::ext::make_shared<ConstantOptionletVolatility>(
236 0, NullCalendar(), Unadjusted, atmVol, Actual365Fixed(), volatilityType(),
false)));
238 MakeOISCapFloor(CapFloor::Cap, atmTenors[j], QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(index_),
239 osBase_->rateComputationPeriod(), Null<Real>(), discountCurve)
241 QL_REQUIRE(!capsOIS_[j].empty(),
242 "OptionletStripperWithAtm: internal error: empty cap for expiry " << atmTenors[j]);
244 atmPrices_[j] = QuantLib::CashFlows::npv(capsOIS_[j], **discountCurve,
false);
249 Volatility atmVol = atmCurve_->volatility(atmTimes[j], 0.01);
250 QuantLib::ext::shared_ptr<PricingEngine> engine;
251 if (atmVolatilityType_ == ShiftedLognormal) {
252 engine = QuantLib::ext::make_shared<BlackCapFloorEngine>(discountCurve, atmVol, dayCounter_, atmDisplacement_);
253 }
else if (atmVolatilityType_ == Normal) {
254 engine = QuantLib::ext::make_shared<BachelierCapFloorEngine>(discountCurve, atmVol, dayCounter_);
256 QL_FAIL(
"Unknown volatility type: " << atmVolatilityType_);
262 QuantLib::ext::shared_ptr<PricingEngine> tempEngine = QuantLib::ext::make_shared<BlackCapFloorEngine>(discountCurve, 0.01);
264 MakeCapFloor(CapFloor::Cap, atmTenors[j], index_, Null<Rate>(), 0 * Days).withPricingEngine(tempEngine);
267 caps_[j]->setPricingEngine(engine);
268 atmStrikes_[j] = caps_[j]->atmRate(**discountCurve);
269 atmPrices_[j] = caps_[j]->NPV();
274 QuantLib::ext::shared_ptr<OptionletVolatilityStructure> ovs =
275 QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<TimeInterpolator, SmileInterpolator> >(
276 atmCurve_->referenceDate(), osBase_);
277 ovs->enableExtrapolation();
280 volSpreads_ = volSpreads(discountCurve, Handle<OptionletVolatilityStructure>(ovs), isOis);
283 for (Size j = 0; j < nAtmExpiries_; ++j) {
284 for (Size i = 0; i < optionletTimes_.size(); ++i) {
285 if (i < (isOis ? capsOIS_[j].size() : caps_[j]->floatingLeg().size())) {
287 Volatility unadjustedVol = ovs->volatility(optionletTimes_[i], atmStrikes_[j]);
288 Volatility adjustedVol = unadjustedVol + volSpreads_[j];
291 vector<Rate>::const_iterator previous =
292 lower_bound(optionletStrikes_[i].begin(), optionletStrikes_[i].end(), atmStrikes_[j]);
293 Size insertIndex = previous - optionletStrikes_[i].begin();
294 optionletStrikes_[i].insert(optionletStrikes_[i].begin() + insertIndex, atmStrikes_[j]);
295 optionletVolatilities_[i].insert(optionletVolatilities_[i].begin() + insertIndex, adjustedVol);
301template <
class TimeInterpolator,
class SmileInterpolator>
303 const QuantLib::Handle<QuantLib::YieldTermStructure>& discount,
304 const QuantLib::Handle<QuantLib::OptionletVolatilityStructure>& ovs,
const bool isOis)
const {
307 using QuantLib::Size;
308 using QuantLib::Volatility;
311 QuantLib::Brent solver;
312 vector<Volatility> result(nAtmExpiries_);
313 Volatility guess = 0.0001;
314 Volatility minSpread = -0.1;
315 Volatility maxSpread = 0.1;
316 solver.setMaxEvaluations(maxEvaluations_);
318 for (Size j = 0; j < nAtmExpiries_; ++j) {
321 result[j] = solver.solve(f, accuracy_, guess, minSpread, maxSpread);
324 result[j] = solver.solve(f, accuracy_, guess, minSpread, maxSpread);
331template <
class TimeInterpolator,
class SmileInterpolator>
333 const QuantLib::Handle<QuantLib::OptionletVolatilityStructure>& ovs,
334 const QuantLib::ext::shared_ptr<QuantLib::CapFloor>& cap, QuantLib::Real targetValue,
335 const QuantLib::Handle<QuantLib::YieldTermStructure>& discount)
336 : cap_(cap),
targetValue_(targetValue), discount_(discount) {
339 using QuantLib::Handle;
340 using QuantLib::OptionletVolatilityStructure;
341 using QuantLib::Quote;
342 using QuantLib::SimpleQuote;
345 spreadQuote_ = QuantLib::ext::make_shared<SimpleQuote>(-1.0);
348 Handle<OptionletVolatilityStructure> spreadedOvs = Handle<OptionletVolatilityStructure>(
349 QuantLib::ext::make_shared<QuantExt::SpreadedOptionletVolatility>(ovs, Handle<Quote>(
spreadQuote_)));
352 if (ovs->volatilityType() == ShiftedLognormal) {
353 cap_->setPricingEngine(QuantLib::ext::make_shared<BlackCapFloorEngine>(
discount_, spreadedOvs, ovs->displacement()));
354 }
else if (ovs->volatilityType() == Normal) {
355 cap_->setPricingEngine(QuantLib::ext::make_shared<BachelierCapFloorEngine>(
discount_, spreadedOvs));
357 QL_FAIL(
"Unknown volatility type: " << ovs->volatilityType());
361template <
class TimeInterpolator,
class SmileInterpolator>
363 const QuantLib::Handle<QuantLib::OptionletVolatilityStructure>& ovs,
const Leg& cap, QuantLib::Real targetValue,
364 const QuantLib::Handle<QuantLib::YieldTermStructure>& discount)
368 using QuantLib::Handle;
369 using QuantLib::OptionletVolatilityStructure;
370 using QuantLib::Quote;
371 using QuantLib::SimpleQuote;
374 spreadQuote_ = QuantLib::ext::make_shared<SimpleQuote>(-1.0);
377 Handle<OptionletVolatilityStructure> spreadedOvs = Handle<OptionletVolatilityStructure>(
378 QuantLib::ext::make_shared<QuantExt::SpreadedOptionletVolatility>(ovs, Handle<Quote>(
spreadQuote_)));
380 auto pricer = QuantLib::ext::make_shared<BlackOvernightIndexedCouponPricer>(spreadedOvs,
false);
381 for (
auto& c :
cap_) {
382 if (
auto f = QuantLib::ext::dynamic_pointer_cast<FloatingRateCoupon>(c))
383 f->setPricer(pricer);
387template <
class TimeInterpolator,
class SmileInterpolator>
389 QuantLib::Volatility volSpread)
const {
390 if (volSpread != spreadQuote_->value()) {
391 spreadQuote_->setValue(volSpread);
396template <
class TimeInterpolator,
class SmileInterpolator>
398 QuantLib::Volatility volSpread)
const {
399 if (volSpread != spreadQuote_->value()) {
400 spreadQuote_->setValue(volSpread);
black coupon pricer for capped / floored ON indexed coupons
Cap floor at-the-money term volatility curve.
MakeOISCapFloor & withCouponPricer(const ext::shared_ptr< CappedFlooredOvernightIndexedCouponPricer > &pricer)
Handle< YieldTermStructure > discount_
Class that is used to imply the spreads at each tenor such that the ATM cap floor volatilities are re...
ObjectiveFunction(const QuantLib::Handle< QuantLib::OptionletVolatilityStructure > &ovs, const QuantLib::ext::shared_ptr< QuantLib::CapFloor > &cap, QuantLib::Real targetValue, const QuantLib::Handle< QuantLib::YieldTermStructure > &discount)
Real operator()(QuantLib::Volatility volSpread) const
QuantLib::Handle< QuantLib::YieldTermStructure > discount_
QuantLib::ext::shared_ptr< QuantLib::SimpleQuote > spreadQuote_
QuantLib::Real targetValue_
QuantLib::ext::shared_ptr< QuantLib::CapFloor > cap_
Real operator()(QuantLib::Volatility volSpread) const
ObjectiveFunctionOIS(const QuantLib::Handle< QuantLib::OptionletVolatilityStructure > &ovs, const Leg &cap, QuantLib::Real targetValue, const QuantLib::Handle< QuantLib::YieldTermStructure > &discount)
QuantLib::Handle< QuantLib::YieldTermStructure > discount_
QuantLib::ext::shared_ptr< QuantLib::SimpleQuote > spreadQuote_
QuantLib::Real targetValue_
QuantLib::VolatilityType atmVolatilityType_
ATM volatility type.
OptionletStripperWithAtm(const QuantLib::ext::shared_ptr< QuantExt::OptionletStripper > &osBase, const QuantLib::Handle< CapFloorTermVolCurve > &atmCurve, const QuantLib::Handle< QuantLib::YieldTermStructure > &discount=QuantLib::Handle< QuantLib::YieldTermStructure >(), const QuantLib::VolatilityType atmVolatilityType=QuantLib::ShiftedLognormal, QuantLib::Real atmDisplacement=0.0, QuantLib::Size maxEvaluations=10000, QuantLib::Real accuracy=1.0e-12, const TimeInterpolator &ti=TimeInterpolator(), const SmileInterpolator &si=SmileInterpolator())
Constructor.
void performCalculations() const override
std::vector< QuantLib::Leg > capsOIS_
TimeInterpolator ti_
The interpolation object in the time direction.
std::vector< QuantLib::Rate > atmStrikes_
std::vector< QuantLib::Volatility > volSpreads() const
std::vector< QuantLib::ext::shared_ptr< QuantLib::CapFloor > > caps_
DayCounter dayCounter_
The day counter for atmCurve_ and osBase_.
Size nAtmExpiries_
The number of ATM instruments in the ATM curve, atmCurve_.
SmileInterpolator si_
The interpolation object in the strike direction.
std::vector< QuantLib::Volatility > volSpreads_
QuantLib::Real atmDisplacement_
ATM volatility shift if the ATM volatility type is ShiftedLognormal.
QuantLib::Real accuracy_
Required accuracy when searching for spread to match ATM volatilities.
QuantLib::Handle< CapFloorTermVolCurve > atmCurve_
ATM volatility curve.
QuantLib::Size maxEvaluations_
Maximum number of evaluations when searching for spread to match ATM volatilities.
QuantLib::ext::shared_ptr< QuantExt::OptionletStripper > osBase_
Base optionlet stripper.
std::vector< QuantLib::Real > atmPrices_
std::vector< QuantLib::Real > atmPrices() const
std::vector< QuantLib::Rate > atmStrikes() const
helper class to instantiate standard market OIS cap / floors
std::vector< std::pair< Real, Real > > getOisCapFloorStrikes(const Leg &oisCapFloor)
get the (cap, floor) - strikes from an OIS cf
optionlet (caplet/floorlet) volatility stripper
Adds floor to QuantLib::SpreadedOptionletVolatility.
Convert a StrippedOptionletBase in to an OptionletVolatilityStructure.