Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
optionletstripper1.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2016 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 <ql/instruments/makecapfloor.hpp>
20#include <ql/option.hpp>
21#include <ql/pricingengines/blackformula.hpp>
22#include <ql/pricingengines/capfloor/bacheliercapfloorengine.hpp>
23#include <ql/pricingengines/capfloor/blackcapfloorengine.hpp>
24#include <ql/utilities/dataformatters.hpp>
26
27#include <boost/make_shared.hpp>
28
29using QuantLib::ext::shared_ptr;
30
31namespace QuantExt {
32
33OptionletStripper1::OptionletStripper1(const shared_ptr<QuantExt::CapFloorTermVolSurface>& termVolSurface,
34 const shared_ptr<IborIndex>& index, Rate switchStrike, Real accuracy,
35 Natural maxIter, const Handle<YieldTermStructure>& discount,
36 const VolatilityType type, const Real displacement,
37 const optional<VolatilityType> targetVolatilityType,
38 const optional<Real> targetDisplacement)
39 : OptionletStripper(termVolSurface, index, discount, targetVolatilityType ? *targetVolatilityType : type,
40 targetDisplacement ? *targetDisplacement : displacement),
41 volQuotes_(nOptionletTenors_, std::vector<shared_ptr<SimpleQuote> >(nStrikes_)),
42 floatingSwitchStrike_(switchStrike == Null<Rate>() ? true : false), capFlooMatrixNotInitialized_(true),
43 switchStrike_(switchStrike), accuracy_(accuracy), maxIter_(maxIter), inputVolatilityType_(type),
44 inputDisplacement_(displacement) {
45
50
51 Real firstGuess = 0.14; // guess is only used for shifted lognormal vols
52 optionletStDevs_ = Matrix(nOptionletTenors_, nStrikes_, firstGuess);
53
55 capFloorEngines_ = std::vector<std::vector<QuantLib::ext::shared_ptr<PricingEngine> > >(nOptionletTenors_);
56}
57
59
60 // update dates
62
64 Rate averageAtmOptionletRate = 0.0;
65 for (Size i = 0; i < nOptionletTenors_; ++i) {
66 averageAtmOptionletRate += atmOptionletRate_[i];
67 }
68 switchStrike_ = averageAtmOptionletRate / nOptionletTenors_;
69 }
70
71 const Handle<YieldTermStructure>& discountCurve =
72 discount_.empty() ? index_->forwardingTermStructure() : discount_;
73
74 const std::vector<Rate>& strikes = termVolSurface_->strikes();
75 DayCounter dc = termVolSurface_->dayCounter();
76 // initialize CapFloorMatrix
78 for (Size i = 0; i < nOptionletTenors_; ++i) {
79 capFloors_[i].resize(nStrikes_);
80 capFloorEngines_[i].resize(nStrikes_);
81 }
82 // construction might go here
83 for (Size j = 0; j < nStrikes_; ++j) {
84 for (Size i = 0; i < nOptionletTenors_; ++i) {
85 volQuotes_[i][j] = shared_ptr<SimpleQuote>(new SimpleQuote());
86 if (inputVolatilityType_ == ShiftedLognormal) {
87 capFloorEngines_[i][j] = QuantLib::ext::make_shared<BlackCapFloorEngine>(
88 discountCurve, Handle<Quote>(volQuotes_[i][j]), dc, inputDisplacement_);
89 } else if (inputVolatilityType_ == Normal) {
90 capFloorEngines_[i][j] =
91 QuantLib::ext::make_shared<BachelierCapFloorEngine>(discountCurve, Handle<Quote>(volQuotes_[i][j]), dc);
92 } else {
93 QL_FAIL("unknown volatility type: " << volatilityType_);
94 }
95 }
96 }
98 }
99
100 for (Size j = 0; j < nStrikes_; ++j) {
101 // using out-of-the-money options - but these are not always out of the money, for different tenors we may need
102 // to switch
103 CapFloor::Type capFloorType = strikes[j] < switchStrike_ ? CapFloor::Floor : CapFloor::Cap;
104
105 // we do this with the above to keep the variables capFloors_ etc consistent, but really its the
106 // optionletStdDevs_ below that we want.
107 Real previousCapFloorPrice = 0.0;
108 for (Size i = 0; i < nOptionletTenors_; ++i) {
109
110 capFloorVols_[i][j] = termVolSurface_->volatility(capFloorLengths_[i], strikes[j], true);
111 volQuotes_[i][j]->setValue(capFloorVols_[i][j]);
112 capFloors_[i][j] = MakeCapFloor(capFloorType, capFloorLengths_[i], index_, strikes[j], -0 * Days)
113 .withPricingEngine(capFloorEngines_[i][j]);
114 capFloorPrices_[i][j] = capFloors_[i][j]->NPV();
115 optionletPrices_[i][j] = capFloorPrices_[i][j] - previousCapFloorPrice;
116 previousCapFloorPrice = capFloorPrices_[i][j];
117 }
118
119 // now try to strip
120 std::vector<Real> optionletStrip(nOptionletTenors_);
121 Real firstGuess = optionletStDevs_[0][j]; // assumes we have a constant first guess here (as we do)
122 bool ok = stripOptionlets(optionletStrip, capFloorType, j, discountCurve, firstGuess);
123 if (!ok) {
124 // try the reverse
125 capFloorType = capFloorType == CapFloor::Cap ? CapFloor::Floor : CapFloor::Cap;
126 ok = stripOptionlets(optionletStrip, capFloorType, j, discountCurve, firstGuess);
127 QL_REQUIRE(ok, "Failed to strip Caplet vols");
128 }
129 // now copy
130 for (Size i = 0; i < nOptionletTenors_; ++i) {
131 optionletStDevs_[i][j] = optionletStrip[i];
132 optionletVolatilities_[i][j] = optionletStDevs_[i][j] / std::sqrt(optionletTimes_[i]);
133 }
134 }
135}
136
137bool OptionletStripper1::stripOptionlets(std::vector<Real>& out, CapFloor::Type capFloorType, Size j,
138 const Handle<YieldTermStructure>& discountCurve, Real firstGuess) const {
139
140 Real strike = termVolSurface_->strikes()[j];
141
142 // floor is put, cap is call
143 Option::Type optionletType = capFloorType == CapFloor::Floor ? Option::Put : Option::Call;
144
145 Real previousCapFloorPrice = 0.0;
146 for (Size i = 0; i < nOptionletTenors_; ++i) {
147
148 // we have capFloorVols_[i][j] & volQuotes_[i][j]
149 CapFloor capFloor = MakeCapFloor(capFloorType, capFloorLengths_[i], index_, strike, -0 * Days)
150 .withPricingEngine(capFloorEngines_[i][j]);
151 Real capFloorPrice = capFloor.NPV();
152 Real optionletPrice = std::max(0.0, capFloorPrice - previousCapFloorPrice);
153 previousCapFloorPrice = capFloorPrice;
154
155 DiscountFactor d = discountCurve->discount(optionletPaymentDates_[i]);
156 DiscountFactor optionletAnnuity = optionletAccrualPeriods_[i] * d;
157 try {
158 if (volatilityType_ == ShiftedLognormal) {
159 out[i] = blackFormulaImpliedStdDev(optionletType, strike, atmOptionletRate_[i], optionletPrice,
160 optionletAnnuity, displacement_, firstGuess, accuracy_, maxIter_);
161 } else if (volatilityType_ == Normal) {
162 out[i] = std::sqrt(optionletTimes_[i]) *
163 bachelierBlackFormulaImpliedVol(optionletType, strike, atmOptionletRate_[i],
164 optionletTimes_[i], optionletPrice, optionletAnnuity);
165 } else {
166 QL_FAIL("Unknown target volatility type: " << volatilityType_);
167 }
168 } catch (std::exception& /*e*/) {
169 /*
170 QL_FAIL("could not bootstrap optionlet:"
171 << "\n type: " << optionletType
172 << "\n strike: " << io::rate(strike)
173 << "\n atm: " << io::rate(atmOptionletRate_[i])
174 << "\n price: " << optionletPrice << "\n annuity: " << optionletAnnuity
175 << "\n expiry: " << optionletDates_[i] << "\n error: " << e.what());
176 */
177 // No need to wipe the output (?)
178 return false;
179 }
180 }
181 return true;
182}
183
184const Matrix& OptionletStripper1::capletVols() const {
185 calculate();
186 return capletVols_;
187}
188
190 calculate();
191 return capFloorPrices_;
192}
193
195 calculate();
196 return capFloorVols_;
197}
198
200 calculate();
201 return optionletPrices_;
202}
203
206 calculate();
207 return switchStrike_;
208}
209} // namespace QuantExt
const Matrix & capletVols() const
void performCalculations() const override
std::vector< std::vector< QuantLib::ext::shared_ptr< SimpleQuote > > > volQuotes_
std::vector< std::vector< QuantLib::ext::shared_ptr< PricingEngine > > > capFloorEngines_
const Matrix & optionletPrices() const
OptionletStripper1(const QuantLib::ext::shared_ptr< QuantExt::CapFloorTermVolSurface > &, const QuantLib::ext::shared_ptr< IborIndex > &index, Rate switchStrikes=Null< Rate >(), Real accuracy=1.0e-6, Natural maxIter=100, const Handle< YieldTermStructure > &discount=Handle< YieldTermStructure >(), const VolatilityType type=ShiftedLognormal, const Real displacement=0.0, const optional< VolatilityType > targetVolatilityType=boost::none, const optional< Real > targetDisplacement=boost::none)
bool stripOptionlets(std::vector< Real > &, CapFloor::Type, Size, const Handle< YieldTermStructure > &, Real) const
const Matrix & capFloorPrices() const
const Handle< YieldTermStructure > & discountCurve() const
const VolatilityType inputVolatilityType_
const Matrix & capFloorVolatilities() const
std::vector< Rate > atmOptionletRate_
std::vector< std::vector< Volatility > > optionletVolatilities_
std::vector< Period > capFloorLengths_
Handle< YieldTermStructure > discount_
std::vector< Date > optionletPaymentDates_
std::vector< Time > optionletAccrualPeriods_
std::vector< Time > optionletTimes_
const VolatilityType volatilityType_
virtual void populateDates() const
Method to populate the dates, times and accruals that can be overridden in derived classes.
ext::shared_ptr< CapFloorTermVolSurface > termVolSurface_
ext::shared_ptr< IborIndex > index_
std::vector< std::vector< QuantLib::ext::shared_ptr< QuantLib::CapFloor > > > CapFloorMatrix
Optionlet (caplet/floorlet) volatility strippers.
vector< Real > strikes