Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
optionletstripper2.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
21
22#include <ql/instruments/makecapfloor.hpp>
23#include <ql/math/solvers1d/brent.hpp>
24#include <ql/pricingengines/capfloor/bacheliercapfloorengine.hpp>
25#include <ql/pricingengines/capfloor/blackcapfloorengine.hpp>
26#include <ql/termstructures/volatility/optionlet/strippedoptionletadapter.hpp>
27
28#include <boost/make_shared.hpp>
29
30namespace QuantExt {
31
32OptionletStripper2::OptionletStripper2(const QuantLib::ext::shared_ptr<QuantExt::OptionletStripper>& optionletStripper,
33 const Handle<QuantLib::CapFloorTermVolCurve>& atmCapFloorTermVolCurve,
34 const Handle<YieldTermStructure>& discount, const VolatilityType type,
35 const Real displacement)
36 : OptionletStripper(optionletStripper->termVolSurface(), optionletStripper->index(), discount,
37 optionletStripper->volatilityType(), optionletStripper->displacement()),
38 stripper_(optionletStripper), atmCapFloorTermVolCurve_(atmCapFloorTermVolCurve),
39 dc_(stripper_->termVolSurface()->dayCounter()), nOptionExpiries_(atmCapFloorTermVolCurve->optionTenors().size()),
40 atmCapFloorStrikes_(nOptionExpiries_), atmCapFloorPrices_(nOptionExpiries_), spreadsVolImplied_(nOptionExpiries_),
41 caps_(nOptionExpiries_), maxEvaluations_(10000), accuracy_(1.e-6), inputVolatilityType_(type),
42 inputDisplacement_(displacement) {
43
44 registerWith(stripper_);
45 registerWith(atmCapFloorTermVolCurve_);
46
47 QL_REQUIRE(dc_ == atmCapFloorTermVolCurve->dayCounter(), "different day counters provided");
48}
49
51
52 // optionletStripper data
53 optionletDates_ = stripper_->optionletFixingDates();
54 optionletPaymentDates_ = stripper_->optionletPaymentDates();
55 optionletAccrualPeriods_ = stripper_->optionletAccrualPeriods();
56 optionletTimes_ = stripper_->optionletFixingTimes();
57 atmOptionletRate_ = stripper_->atmOptionletRates();
58 for (Size i = 0; i < optionletTimes_.size(); ++i) {
59 optionletStrikes_[i] = stripper_->optionletStrikes(i);
60 optionletVolatilities_[i] = stripper_->optionletVolatilities(i);
61 }
62
63 // atmCapFloorTermVolCurve data
64 const vector<Period>& optionExpiriesTenors = atmCapFloorTermVolCurve_->optionTenors();
65 const vector<Time>& optionExpiriesTimes = atmCapFloorTermVolCurve_->optionTimes();
66
67 // discount curve
68 const Handle<YieldTermStructure>& discountCurve =
69 discount_.empty() ? index_->forwardingTermStructure() : discount_;
70
71 for (Size j = 0; j < nOptionExpiries_; ++j) {
72 // Dummy strike, doesn't get used for ATM curve
73 Volatility atmOptionVol = atmCapFloorTermVolCurve_->volatility(optionExpiriesTimes[j], 33.3333);
74
75 // Create a cap for each pillar point on ATM curve and attach relevant pricing engine i.e. Black if
76 // quotes are shifted lognormal and Bachelier if quotes are normal
77 QuantLib::ext::shared_ptr<PricingEngine> engine;
78 if (inputVolatilityType_ == ShiftedLognormal) {
79 engine = QuantLib::ext::make_shared<BlackCapFloorEngine>(discountCurve, atmOptionVol, dc_, inputDisplacement_);
80 } else if (inputVolatilityType_ == Normal) {
81 engine = QuantLib::ext::make_shared<BachelierCapFloorEngine>(discountCurve, atmOptionVol, dc_);
82 } else {
83 QL_FAIL("unknown volatility type: " << volatilityType_);
84 }
85
86 // Using Null<Rate>() as strike => strike will be set to ATM rate. However, to calculate ATM rate, QL requires
87 // a BlackCapFloorEngine to be set (not a BachelierCapFloorEngine)! So, need a temp BlackCapFloorEngine with a
88 // dummy vol to calculate ATM rate. Needs to be fixed in QL.
89 QuantLib::ext::shared_ptr<PricingEngine> tempEngine = QuantLib::ext::make_shared<BlackCapFloorEngine>(discountCurve, 0.01);
90 caps_[j] = MakeCapFloor(CapFloor::Cap, optionExpiriesTenors[j], index_, Null<Rate>(), 0 * Days)
91 .withPricingEngine(tempEngine);
92
93 // Now set correct engine and get the ATM rate and the price
94 caps_[j]->setPricingEngine(engine);
95 atmCapFloorStrikes_[j] = caps_[j]->atmRate(**discountCurve);
96 atmCapFloorPrices_[j] = caps_[j]->NPV();
97 }
98
100
102 adapter.enableExtrapolation();
103
104 Volatility unadjustedVol, adjustedVol;
105 for (Size j = 0; j < nOptionExpiries_; ++j) {
106 for (Size i = 0; i < optionletVolatilities_.size(); ++i) {
107 if (i <= caps_[j]->floatingLeg().size()) {
108 unadjustedVol = adapter.volatility(optionletTimes_[i], atmCapFloorStrikes_[j]);
109 adjustedVol = unadjustedVol + spreadsVolImplied_[j];
110
111 // insert adjusted volatility
112 vector<Rate>::const_iterator previous =
113 lower_bound(optionletStrikes_[i].begin(), optionletStrikes_[i].end(), atmCapFloorStrikes_[j]);
114 Size insertIndex = previous - optionletStrikes_[i].begin();
115
116 optionletStrikes_[i].insert(optionletStrikes_[i].begin() + insertIndex, atmCapFloorStrikes_[j]);
117 optionletVolatilities_[i].insert(optionletVolatilities_[i].begin() + insertIndex, adjustedVol);
118 }
119 }
120 }
121}
122
123vector<Volatility> OptionletStripper2::spreadsVolImplied(const Handle<YieldTermStructure>& discount) const {
124
125 Brent solver;
126 vector<Volatility> result(nOptionExpiries_);
127 Volatility guess = 0.0001, minSpread = -0.1, maxSpread = 0.1;
128 for (Size j = 0; j < nOptionExpiries_; ++j) {
130 solver.setMaxEvaluations(maxEvaluations_);
131 Volatility root = solver.solve(f, accuracy_, guess, minSpread, maxSpread);
132 result[j] = root;
133 }
134 return result;
135}
136
137vector<Volatility> OptionletStripper2::spreadsVol() const {
138 calculate();
139 return spreadsVolImplied_;
140}
141
143 calculate();
144 return atmCapFloorStrikes_;
145}
146
148 calculate();
149 return atmCapFloorPrices_;
150}
151
152// OptionletStripper2::ObjectiveFunction
154 const QuantLib::ext::shared_ptr<QuantExt::OptionletStripper>& optionletStripper, const QuantLib::ext::shared_ptr<CapFloor>& cap,
155 Real targetValue, const Handle<YieldTermStructure>& discount)
156 : cap_(cap), targetValue_(targetValue), discount_(discount) {
157 QuantLib::ext::shared_ptr<OptionletVolatilityStructure> adapter(new StrippedOptionletAdapter(optionletStripper));
158 adapter->enableExtrapolation();
159
160 // set an implausible value, so that calculation is forced
161 // at first operator()(Volatility x) call
162 spreadQuote_ = QuantLib::ext::shared_ptr<SimpleQuote>(new SimpleQuote(-1.0));
163
164 QuantLib::ext::shared_ptr<OptionletVolatilityStructure> spreadedAdapter(
165 new SpreadedOptionletVolatility(Handle<OptionletVolatilityStructure>(adapter), Handle<Quote>(spreadQuote_)));
166
167 // Use the same volatility type as optionletStripper
168 // Anything else would not make sense
169 QuantLib::ext::shared_ptr<PricingEngine> engine;
170 if (optionletStripper->volatilityType() == ShiftedLognormal) {
171 engine = QuantLib::ext::make_shared<BlackCapFloorEngine>(
172 discount_, Handle<OptionletVolatilityStructure>(spreadedAdapter), optionletStripper->displacement());
173 } else if (optionletStripper->volatilityType() == Normal) {
174 engine = QuantLib::ext::make_shared<BachelierCapFloorEngine>(discount_,
175 Handle<OptionletVolatilityStructure>(spreadedAdapter));
176 } else {
177 QL_FAIL("Unknown volatility type: " << optionletStripper->volatilityType());
178 }
179
180 cap_->setPricingEngine(engine);
181}
182
184 if (s != spreadQuote_->value())
185 spreadQuote_->setValue(s);
186 return cap_->NPV() - targetValue_;
187}
188} // namespace QuantExt
Real targetValue_
Definition: cdsoption.cpp:79
ObjectiveFunction(const QuantLib::ext::shared_ptr< QuantExt::OptionletStripper > &, const QuantLib::ext::shared_ptr< QuantLib::CapFloor > &, Real targetValue, const Handle< YieldTermStructure > &discount)
const Handle< YieldTermStructure > discount_
QuantLib::ext::shared_ptr< SimpleQuote > spreadQuote_
QuantLib::ext::shared_ptr< QuantLib::CapFloor > cap_
void performCalculations() const override
OptionletStripper2(const QuantLib::ext::shared_ptr< QuantExt::OptionletStripper > &optionletStripper, const Handle< QuantLib::CapFloorTermVolCurve > &atmCapFloorTermVolCurve, const Handle< YieldTermStructure > &discount=Handle< YieldTermStructure >(), const VolatilityType type=ShiftedLognormal, const Real displacement=0.0)
vector< Real > atmCapFloorPrices() const
const Handle< QuantLib::CapFloorTermVolCurve > atmCapFloorTermVolCurve_
vector< Volatility > spreadsVolImplied(const Handle< YieldTermStructure > &discount) const
const QuantLib::ext::shared_ptr< QuantExt::OptionletStripper > stripper_
vector< Rate > atmCapFloorStrikes() const
vector< Volatility > spreadsVolImplied_
vector< QuantLib::ext::shared_ptr< QuantLib::CapFloor > > caps_
const VolatilityType inputVolatilityType_
vector< Volatility > spreadsVol() const
std::vector< Rate > atmOptionletRate_
std::vector< std::vector< Volatility > > optionletVolatilities_
Handle< YieldTermStructure > discount_
std::vector< Date > optionletPaymentDates_
std::vector< Time > optionletAccrualPeriods_
std::vector< Time > optionletTimes_
std::vector< Date > optionletDates_
const VolatilityType volatilityType_
std::vector< std::vector< Rate > > optionletStrikes_
ext::shared_ptr< IborIndex > index_
ATM optionlet (caplet/floorlet) volatility stripper.
Adds floor to QuantLib::SpreadedOptionletVolatility.