Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
capfloorhelper.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2019 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
19#include <boost/bind/bind.hpp>
20#include <ql/functional.hpp>
21#include <ql/instruments/makecapfloor.hpp>
22#include <ql/pricingengines/capfloor/bacheliercapfloorengine.hpp>
23#include <ql/pricingengines/capfloor/blackcapfloorengine.hpp>
24#include <ql/quotes/derivedquote.hpp>
25#include <ql/utilities/null_deleter.hpp>
27
28using namespace QuantLib;
29using std::ostream;
30
31namespace QuantExt {
32
33// The argument to RelativeDateBootstrapHelper below is not simply the `quote`. Instead we create a DerivedQuote that,
34// each time it is asked for its value, it returns a premium by calling CapFloorHelper::npv with the `quote` value. In
35// this way, the `BootstrapHelper<T>::quoteError()` is always based on the cap floor premium and we do not have imply
36// the volatility. This leads to all kinds of issues when deciding on max and min values in the iterative bootstrap
37// where the quote volatility type input is of one type and the optionlet structure that we are trying to build is of
38// another different type.
39CapFloorHelper::CapFloorHelper(Type type, const Period& tenor, Rate strike, const Handle<Quote>& quote,
40 const QuantLib::ext::shared_ptr<IborIndex>& iborIndex,
41 const Handle<YieldTermStructure>& discountingCurve, bool moving,
42 const QuantLib::Date& effectiveDate, QuoteType quoteType,
43 QuantLib::VolatilityType quoteVolatilityType, QuantLib::Real quoteDisplacement,
44 bool endOfMonth, bool firstCapletExcluded)
45 : RelativeDateBootstrapHelper<OptionletVolatilityStructure>(
46 Handle<Quote>(QuantLib::ext::make_shared<DerivedQuote<QuantLib::ext::function<Real(Real)> > >(
47 quote, QuantLib::ext::bind(&CapFloorHelper::npv, this, QuantLib::ext::placeholders::_1)))),
48 type_(type), tenor_(tenor), strike_(strike), iborIndex_(iborIndex), discountHandle_(discountingCurve),
49 moving_(moving), effectiveDate_(effectiveDate), quoteType_(quoteType), quoteVolatilityType_(quoteVolatilityType),
50 quoteDisplacement_(quoteDisplacement), endOfMonth_(endOfMonth), firstCapletExcluded_(firstCapletExcluded),
51 rawQuote_(quote), initialised_(false) {
52
53 if (quoteType_ == Premium) {
54 QL_REQUIRE(type_ != Automatic, "Cannot have CapFloorHelper type 'Automatic' with quote type of Premium");
55 }
56
57 QL_REQUIRE(!(moving_ && effectiveDate_ != Date()),
58 "A fixed effective date does not make sense for a moving helper");
59
60 registerWith(iborIndex_);
61 registerWith(discountHandle_);
62
64 initialised_ = true;
65}
66
68
69 if (!initialised_ || moving_) {
70 CapFloor::Type capFloorType = CapFloor::Cap;
72 capFloorType = CapFloor::Floor;
73 }
74
75 // Initialise the instrument and a copy
76 // The strike can be Null<Real>() to indicate an ATM cap floor helper
77 Rate dummyStrike = strike_ == Null<Real>() ? 0.01 : strike_;
78 capFloor_ = MakeCapFloor(capFloorType, tenor_, iborIndex_, dummyStrike, 0 * Days)
79 .withEndOfMonth(endOfMonth_)
80 .withEffectiveDate(effectiveDate_, firstCapletExcluded_);
81 capFloorCopy_ = MakeCapFloor(capFloorType, tenor_, iborIndex_, dummyStrike, 0 * Days)
82 .withEndOfMonth(endOfMonth_)
83 .withEffectiveDate(effectiveDate_, firstCapletExcluded_);
84
85 // Maturity date is just the maturity date of the cap floor
86 maturityDate_ = capFloor_->maturityDate();
87
88 // We need the leg underlying the cap floor to determine the remaining date members
89 const Leg& leg = capFloor_->floatingLeg();
90
91 // Earliest date is the first optionlet fixing date
92 QuantLib::ext::shared_ptr<CashFlow> cf = leg.front();
93 QuantLib::ext::shared_ptr<FloatingRateCoupon> frc = QuantLib::ext::dynamic_pointer_cast<FloatingRateCoupon>(cf);
94 QL_REQUIRE(frc, "Expected the first cashflow on the cap floor instrument to be a FloatingRateCoupon");
95 earliestDate_ = frc->fixingDate();
96
97 // Remaining dates are each equal to the fixing date on the final optionlet
98 cf = leg.back();
99 frc = QuantLib::ext::dynamic_pointer_cast<FloatingRateCoupon>(cf);
100 QL_REQUIRE(frc, "Expected the final cashflow on the cap floor instrument to be a FloatingRateCoupon");
101 pillarDate_ = latestDate_ = latestRelevantDate_ = frc->fixingDate();
102 }
103}
104
105void CapFloorHelper::setTermStructure(OptionletVolatilityStructure* ovts) {
106
107 if (strike_ == Null<Real>()) {
108 // If the strike is Null<Real>(), we want an ATM helper
109 Rate atm = capFloor_->atmRate(**discountHandle_);
110 capFloor_ = MakeCapFloor(capFloor_->type(), tenor_, iborIndex_, atm, 0 * Days)
111 .withEndOfMonth(endOfMonth_)
112 .withEffectiveDate(effectiveDate_, firstCapletExcluded_);
113 capFloorCopy_ = MakeCapFloor(capFloor_->type(), tenor_, iborIndex_, atm, 0 * Days)
114 .withEndOfMonth(endOfMonth_)
115 .withEffectiveDate(effectiveDate_, firstCapletExcluded_);
116
118 // If the helper is set to automatically choose the underlying instrument type, do it now based on the ATM rate
119 Rate atm = capFloor_->atmRate(**discountHandle_);
120 CapFloor::Type capFloorType = atm > strike_ ? CapFloor::Floor : CapFloor::Cap;
121 if (capFloor_->type() != capFloorType) {
122 capFloor_ = MakeCapFloor(capFloorType, tenor_, iborIndex_, strike_, 0 * Days)
123 .withEndOfMonth(endOfMonth_)
124 .withEffectiveDate(effectiveDate_, firstCapletExcluded_);
125 capFloorCopy_ = MakeCapFloor(capFloorType, tenor_, iborIndex_, strike_, 0 * Days)
126 .withEndOfMonth(endOfMonth_)
127 .withEffectiveDate(effectiveDate_, firstCapletExcluded_);
128 }
129 }
130
131 // Set this helper's optionlet volatility structure
132 QuantLib::ext::shared_ptr<OptionletVolatilityStructure> temp(ovts, null_deleter());
133 ovtsHandle_.linkTo(temp, false);
134
135 // Set the term structure pointer member variable in the base class
136 RelativeDateBootstrapHelper<OptionletVolatilityStructure>::setTermStructure(ovts);
137
138 // Set this helper's pricing engine depending on the type of the optionlet volatilities
139 if (ovts->volatilityType() == ShiftedLognormal) {
140 capFloor_->setPricingEngine(QuantLib::ext::make_shared<BlackCapFloorEngine>(discountHandle_, ovtsHandle_));
141 } else {
142 capFloor_->setPricingEngine(QuantLib::ext::make_shared<BachelierCapFloorEngine>(discountHandle_, ovtsHandle_));
143 }
144
145 // If the quote type is not a premium, we will need to use capFloorCopy_ to return the premium from the volatility
146 // quote
147 if (quoteType_ != Premium) {
148 if (quoteVolatilityType_ == ShiftedLognormal) {
149 capFloorCopy_->setPricingEngine(QuantLib::ext::make_shared<BlackCapFloorEngine>(
151 } else {
152 capFloorCopy_->setPricingEngine(
153 QuantLib::ext::make_shared<BachelierCapFloorEngine>(discountHandle_, rawQuote_, ovtsHandle_->dayCounter()));
154 }
155 }
156}
157
159 QL_REQUIRE(termStructure_ != 0, "CapFloorHelper's optionlet volatility term structure has not been set");
160 capFloor_->deepUpdate();
161 return capFloor_->NPV();
162}
163
164void CapFloorHelper::accept(AcyclicVisitor& v) {
165 if (Visitor<CapFloorHelper>* v1 = dynamic_cast<Visitor<CapFloorHelper>*>(&v))
166 v1->visit(*this);
167 else
168 RelativeDateBootstrapHelper<OptionletVolatilityStructure>::accept(v);
169}
170
171Real CapFloorHelper::npv(Real quoteValue) {
172 if (quoteType_ == Premium) {
173 return quoteValue;
174 } else {
175 // If the quote value is a volatility, return the premium
176 return capFloorCopy_->NPV();
177 }
178}
179
180ostream& operator<<(ostream& out, CapFloorHelper::Type type) {
181 switch (type) {
183 return out << "Cap";
185 return out << "Floor";
187 return out << "Automatic";
188 default:
189 QL_FAIL("Unknown CapFloorHelper::Type (" << Integer(type) << ")");
190 }
191}
192
193ostream& operator<<(ostream& out, CapFloorHelper::QuoteType type) {
194 switch (type) {
196 return out << "Volatility";
198 return out << "Premium";
199 default:
200 QL_FAIL("Unknown CapFloorHelper::QuoteType (" << Integer(type) << ")");
201 }
202}
203
204} // namespace QuantExt
Helper for bootstrapping optionlet volatilities from cap floor volatilities.
QuantLib::Handle< QuantLib::Quote > rawQuote_
QuantLib::Real npv(QuantLib::Real quote)
A method to calculate the cap floor premium from a flat cap floor volatility value.
void accept(QuantLib::AcyclicVisitor &) override
QuantLib::ext::shared_ptr< QuantLib::IborIndex > iborIndex_
CapFloorHelper(Type type, const QuantLib::Period &tenor, QuantLib::Rate strike, const QuantLib::Handle< QuantLib::Quote > &quote, const QuantLib::ext::shared_ptr< QuantLib::IborIndex > &iborIndex, const QuantLib::Handle< QuantLib::YieldTermStructure > &discountingCurve, bool moving=true, const QuantLib::Date &effectiveDate=QuantLib::Date(), QuoteType quoteType=Premium, QuantLib::VolatilityType quoteVolatilityType=QuantLib::Normal, QuantLib::Real quoteDisplacement=0.0, bool endOfMonth=false, bool firstCapletExcluded=true)
QuantLib::Real quoteDisplacement_
QuantLib::ext::shared_ptr< QuantLib::CapFloor > capFloorCopy_
A copy of the underlying instrument that is used in the npv method.
void initializeDates() override
RelativeDateBootstrapHelper interface.
void setTermStructure(QuantLib::OptionletVolatilityStructure *ovts) override
Sets the helper's OptionletVolatilityStructure to ovts and sets up the pricing engine for capFloor_.
QuantLib::ext::shared_ptr< QuantLib::CapFloor > capFloor_
The underlying instrument.
QuoteType
Enum to indicate the type of the quote provided with the CapFloorHelper.
QuantLib::Real impliedQuote() const override
Returns the capFloor_ instrument's premium.
QuantLib::VolatilityType quoteVolatilityType_
QuantLib::Handle< QuantLib::YieldTermStructure > discountHandle_
QuantLib::RelinkableHandle< QuantLib::OptionletVolatilityStructure > ovtsHandle_
The OptionletVolatilityStructure Handle that we link to the capFloor_ instrument.
std::ostream & operator<<(std::ostream &out, EquityReturnType t)