/src/quantlib/ql/instruments/capfloor.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2006, 2014 Ferdinando Ametrano |
5 | | Copyright (C) 2006 François du Vignaud |
6 | | Copyright (C) 2001, 2002, 2003 Sadruddin Rejeb |
7 | | Copyright (C) 2006, 2007 StatPro Italia srl |
8 | | Copyright (C) 2016 Paolo Mazzocchi |
9 | | |
10 | | This file is part of QuantLib, a free-software/open-source library |
11 | | for financial quantitative analysts and developers - http://quantlib.org/ |
12 | | |
13 | | QuantLib is free software: you can redistribute it and/or modify it |
14 | | under the terms of the QuantLib license. You should have received a |
15 | | copy of the license along with this program; if not, please email |
16 | | <quantlib-dev@lists.sf.net>. The license is also available online at |
17 | | <http://quantlib.org/license.shtml>. |
18 | | |
19 | | This program is distributed in the hope that it will be useful, but WITHOUT |
20 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
21 | | FOR A PARTICULAR PURPOSE. See the license for more details. |
22 | | */ |
23 | | |
24 | | #include <ql/any.hpp> |
25 | | #include <ql/cashflows/cashflows.hpp> |
26 | | #include <ql/instruments/capfloor.hpp> |
27 | | #include <ql/math/solvers1d/newtonsafe.hpp> |
28 | | #include <ql/pricingengines/capfloor/bacheliercapfloorengine.hpp> |
29 | | #include <ql/pricingengines/capfloor/blackcapfloorengine.hpp> |
30 | | #include <ql/quotes/simplequote.hpp> |
31 | | #include <ql/termstructures/yieldtermstructure.hpp> |
32 | | #include <ql/utilities/dataformatters.hpp> |
33 | | #include <utility> |
34 | | |
35 | | namespace QuantLib { |
36 | | |
37 | | namespace { |
38 | | |
39 | | class ImpliedCapVolHelper { |
40 | | public: |
41 | | ImpliedCapVolHelper(const CapFloor&, |
42 | | Handle<YieldTermStructure> discountCurve, |
43 | | Real targetValue, |
44 | | Real displacement, |
45 | | VolatilityType type); |
46 | | Real operator()(Volatility x) const; |
47 | | Real derivative(Volatility x) const; |
48 | | private: |
49 | | ext::shared_ptr<PricingEngine> engine_; |
50 | | Handle<YieldTermStructure> discountCurve_; |
51 | | Real targetValue_; |
52 | | ext::shared_ptr<SimpleQuote> vol_; |
53 | | const Instrument::results* results_; |
54 | | }; |
55 | | |
56 | | ImpliedCapVolHelper::ImpliedCapVolHelper(const CapFloor& cap, |
57 | | Handle<YieldTermStructure> discountCurve, |
58 | | Real targetValue, |
59 | | Real displacement, |
60 | | VolatilityType type) |
61 | 0 | : discountCurve_(std::move(discountCurve)), targetValue_(targetValue), |
62 | 0 | vol_(ext::make_shared<SimpleQuote>(-1.0)) { |
63 | | |
64 | | // vol_ is set an implausible value, so that calculation is forced |
65 | | // at first ImpliedCapVolHelper::operator()(Volatility x) call |
66 | 0 | Handle<Quote> h(vol_); |
67 | |
|
68 | 0 | switch (type) { |
69 | 0 | case ShiftedLognormal: |
70 | 0 | engine_ = ext::shared_ptr<PricingEngine>(new |
71 | 0 | BlackCapFloorEngine(discountCurve_, h, Actual365Fixed(), |
72 | 0 | displacement)); |
73 | 0 | break; |
74 | 0 | case Normal: |
75 | 0 | engine_ = ext::shared_ptr<PricingEngine>(new |
76 | 0 | BachelierCapFloorEngine(discountCurve_, h, |
77 | 0 | Actual365Fixed())); |
78 | 0 | break; |
79 | 0 | default: |
80 | 0 | QL_FAIL("unknown VolatilityType (" << type << ")"); |
81 | 0 | break; |
82 | 0 | } |
83 | | |
84 | 0 | cap.setupArguments(engine_->getArguments()); |
85 | |
|
86 | 0 | results_ = |
87 | 0 | dynamic_cast<const Instrument::results*>(engine_->getResults()); |
88 | 0 | } |
89 | | |
90 | 0 | Real ImpliedCapVolHelper::operator()(Volatility x) const { |
91 | 0 | if (x!=vol_->value()) { |
92 | 0 | vol_->setValue(x); |
93 | 0 | engine_->calculate(); |
94 | 0 | } |
95 | 0 | return results_->value-targetValue_; |
96 | 0 | } |
97 | | |
98 | 0 | Real ImpliedCapVolHelper::derivative(Volatility x) const { |
99 | 0 | if (x!=vol_->value()) { |
100 | 0 | vol_->setValue(x); |
101 | 0 | engine_->calculate(); |
102 | 0 | } |
103 | 0 | auto vega_ = results_->additionalResults.find("vega"); |
104 | 0 | QL_REQUIRE(vega_ != results_->additionalResults.end(), |
105 | 0 | "vega not provided"); |
106 | 0 | return ext::any_cast<Real>(vega_->second); |
107 | 0 | } |
108 | | } |
109 | | |
110 | | std::ostream& operator<<(std::ostream& out, |
111 | 0 | CapFloor::Type t) { |
112 | 0 | switch (t) { |
113 | 0 | case CapFloor::Cap: |
114 | 0 | return out << "Cap"; |
115 | 0 | case CapFloor::Floor: |
116 | 0 | return out << "Floor"; |
117 | 0 | case CapFloor::Collar: |
118 | 0 | return out << "Collar"; |
119 | 0 | default: |
120 | 0 | QL_FAIL("unknown CapFloor::Type (" << Integer(t) << ")"); |
121 | 0 | } |
122 | 0 | } |
123 | | |
124 | | CapFloor::CapFloor(CapFloor::Type type, |
125 | | Leg floatingLeg, |
126 | | std::vector<Rate> capRates, |
127 | | std::vector<Rate> floorRates) |
128 | 0 | : type_(type), floatingLeg_(std::move(floatingLeg)), capRates_(std::move(capRates)), |
129 | 0 | floorRates_(std::move(floorRates)) { |
130 | 0 | if (type_ == Cap || type_ == Collar) { |
131 | 0 | QL_REQUIRE(!capRates_.empty(), "no cap rates given"); |
132 | 0 | capRates_.reserve(floatingLeg_.size()); |
133 | 0 | while (capRates_.size() < floatingLeg_.size()) |
134 | 0 | capRates_.push_back(capRates_.back()); |
135 | 0 | } |
136 | 0 | if (type_ == Floor || type_ == Collar) { |
137 | 0 | QL_REQUIRE(!floorRates_.empty(), "no floor rates given"); |
138 | 0 | floorRates_.reserve(floatingLeg_.size()); |
139 | 0 | while (floorRates_.size() < floatingLeg_.size()) |
140 | 0 | floorRates_.push_back(floorRates_.back()); |
141 | 0 | } |
142 | 0 | Leg::const_iterator i; |
143 | 0 | for (i = floatingLeg_.begin(); i != floatingLeg_.end(); ++i) |
144 | 0 | registerWith(*i); |
145 | |
|
146 | 0 | registerWith(Settings::instance().evaluationDate()); |
147 | 0 | } Unexecuted instantiation: QuantLib::CapFloor::CapFloor(QuantLib::CapFloor::Type, std::__1::vector<boost::shared_ptr<QuantLib::CashFlow>, std::__1::allocator<boost::shared_ptr<QuantLib::CashFlow> > >, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<double, std::__1::allocator<double> >) Unexecuted instantiation: QuantLib::CapFloor::CapFloor(QuantLib::CapFloor::Type, std::__1::vector<boost::shared_ptr<QuantLib::CashFlow>, std::__1::allocator<boost::shared_ptr<QuantLib::CashFlow> > >, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<double, std::__1::allocator<double> >) |
148 | | |
149 | | CapFloor::CapFloor(CapFloor::Type type, Leg floatingLeg, const std::vector<Rate>& strikes) |
150 | 0 | : type_(type), floatingLeg_(std::move(floatingLeg)) { |
151 | 0 | QL_REQUIRE(!strikes.empty(), "no strikes given"); |
152 | 0 | if (type_ == Cap) { |
153 | 0 | capRates_ = strikes; |
154 | 0 | capRates_.reserve(floatingLeg_.size()); |
155 | 0 | while (capRates_.size() < floatingLeg_.size()) |
156 | 0 | capRates_.push_back(capRates_.back()); |
157 | 0 | } else if (type_ == Floor) { |
158 | 0 | floorRates_ = strikes; |
159 | 0 | floorRates_.reserve(floatingLeg_.size()); |
160 | 0 | while (floorRates_.size() < floatingLeg_.size()) |
161 | 0 | floorRates_.push_back(floorRates_.back()); |
162 | 0 | } else |
163 | 0 | QL_FAIL("only Cap/Floor types allowed in this constructor"); |
164 | | |
165 | 0 | Leg::const_iterator i; |
166 | 0 | for (i = floatingLeg_.begin(); i != floatingLeg_.end(); ++i) |
167 | 0 | registerWith(*i); |
168 | |
|
169 | 0 | registerWith(Settings::instance().evaluationDate()); |
170 | 0 | } Unexecuted instantiation: QuantLib::CapFloor::CapFloor(QuantLib::CapFloor::Type, std::__1::vector<boost::shared_ptr<QuantLib::CashFlow>, std::__1::allocator<boost::shared_ptr<QuantLib::CashFlow> > >, std::__1::vector<double, std::__1::allocator<double> > const&) Unexecuted instantiation: QuantLib::CapFloor::CapFloor(QuantLib::CapFloor::Type, std::__1::vector<boost::shared_ptr<QuantLib::CashFlow>, std::__1::allocator<boost::shared_ptr<QuantLib::CashFlow> > >, std::__1::vector<double, std::__1::allocator<double> > const&) |
171 | | |
172 | 0 | bool CapFloor::isExpired() const { |
173 | 0 | for (Size i=floatingLeg_.size(); i>0; --i) |
174 | 0 | if (!floatingLeg_[i-1]->hasOccurred()) |
175 | 0 | return false; |
176 | 0 | return true; |
177 | 0 | } |
178 | | |
179 | 0 | Date CapFloor::startDate() const { |
180 | 0 | return CashFlows::startDate(floatingLeg_); |
181 | 0 | } |
182 | | |
183 | 0 | Date CapFloor::maturityDate() const { |
184 | 0 | return CashFlows::maturityDate(floatingLeg_); |
185 | 0 | } |
186 | | |
187 | | ext::shared_ptr<FloatingRateCoupon> |
188 | 0 | CapFloor::lastFloatingRateCoupon() const { |
189 | 0 | ext::shared_ptr<CashFlow> lastCF(floatingLeg_.back()); |
190 | 0 | ext::shared_ptr<FloatingRateCoupon> lastFloatingCoupon = |
191 | 0 | ext::dynamic_pointer_cast<FloatingRateCoupon>(lastCF); |
192 | 0 | return lastFloatingCoupon; |
193 | 0 | } |
194 | | |
195 | 0 | ext::shared_ptr<CapFloor> CapFloor::optionlet(const Size i) const { |
196 | 0 | QL_REQUIRE(i < floatingLeg().size(), |
197 | 0 | io::ordinal(i+1) << " optionlet does not exist, only " << |
198 | 0 | floatingLeg().size()); |
199 | 0 | Leg cf(1, floatingLeg()[i]); |
200 | |
|
201 | 0 | std::vector<Rate> cap, floor; |
202 | 0 | if (type() == Cap || type() == Collar) |
203 | 0 | cap.push_back(capRates()[i]); |
204 | 0 | if (type() == Floor || type() == Collar) |
205 | 0 | floor.push_back(floorRates()[i]); |
206 | |
|
207 | 0 | return ext::make_shared<CapFloor>(type(), cf, cap, floor); |
208 | 0 | } |
209 | | |
210 | 0 | void CapFloor::setupArguments(PricingEngine::arguments* args) const { |
211 | 0 | auto* arguments = dynamic_cast<CapFloor::arguments*>(args); |
212 | 0 | QL_REQUIRE(arguments != nullptr, "wrong argument type"); |
213 | | |
214 | 0 | Size n = floatingLeg_.size(); |
215 | |
|
216 | 0 | arguments->startDates.resize(n); |
217 | 0 | arguments->fixingDates.resize(n); |
218 | 0 | arguments->endDates.resize(n); |
219 | 0 | arguments->accrualTimes.resize(n); |
220 | 0 | arguments->forwards.resize(n); |
221 | 0 | arguments->nominals.resize(n); |
222 | 0 | arguments->gearings.resize(n); |
223 | 0 | arguments->capRates.resize(n); |
224 | 0 | arguments->floorRates.resize(n); |
225 | 0 | arguments->spreads.resize(n); |
226 | 0 | arguments->indexes.resize(n); |
227 | |
|
228 | 0 | arguments->type = type_; |
229 | |
|
230 | 0 | Date today = Settings::instance().evaluationDate(); |
231 | |
|
232 | 0 | for (Size i=0; i<n; ++i) { |
233 | 0 | ext::shared_ptr<FloatingRateCoupon> coupon = |
234 | 0 | ext::dynamic_pointer_cast<FloatingRateCoupon>( |
235 | 0 | floatingLeg_[i]); |
236 | 0 | QL_REQUIRE(coupon, "non-FloatingRateCoupon given"); |
237 | 0 | arguments->startDates[i] = coupon->accrualStartDate(); |
238 | 0 | arguments->fixingDates[i] = coupon->fixingDate(); |
239 | 0 | arguments->endDates[i] = coupon->date(); |
240 | | |
241 | | // this is passed explicitly for precision |
242 | 0 | arguments->accrualTimes[i] = coupon->accrualPeriod(); |
243 | | |
244 | | // this is passed explicitly for precision... |
245 | 0 | if (arguments->endDates[i] >= today) { // ...but only if needed |
246 | 0 | arguments->forwards[i] = coupon->adjustedFixing(); |
247 | 0 | } else { |
248 | 0 | arguments->forwards[i] = Null<Rate>(); |
249 | 0 | } |
250 | |
|
251 | 0 | arguments->nominals[i] = coupon->nominal(); |
252 | 0 | Spread spread = coupon->spread(); |
253 | 0 | Real gearing = coupon->gearing(); |
254 | 0 | arguments->gearings[i] = gearing; |
255 | 0 | arguments->spreads[i] = spread; |
256 | |
|
257 | 0 | if (type_ == Cap || type_ == Collar) |
258 | 0 | arguments->capRates[i] = (capRates_[i]-spread)/gearing; |
259 | 0 | else |
260 | 0 | arguments->capRates[i] = Null<Rate>(); |
261 | |
|
262 | 0 | if (type_ == Floor || type_ == Collar) |
263 | 0 | arguments->floorRates[i] = (floorRates_[i]-spread)/gearing; |
264 | 0 | else |
265 | 0 | arguments->floorRates[i] = Null<Rate>(); |
266 | |
|
267 | 0 | arguments->indexes[i] = coupon->index(); |
268 | 0 | } |
269 | 0 | } |
270 | | |
271 | 0 | void CapFloor::deepUpdate() { |
272 | 0 | for (auto& i : floatingLeg_) { |
273 | 0 | i->deepUpdate(); |
274 | 0 | } |
275 | 0 | update(); |
276 | 0 | } |
277 | | |
278 | 0 | void CapFloor::arguments::validate() const { |
279 | 0 | QL_REQUIRE(endDates.size() == startDates.size(), |
280 | 0 | "number of start dates (" << startDates.size() |
281 | 0 | << ") different from that of end dates (" |
282 | 0 | << endDates.size() << ")"); |
283 | 0 | QL_REQUIRE(accrualTimes.size() == startDates.size(), |
284 | 0 | "number of start dates (" << startDates.size() |
285 | 0 | << ") different from that of accrual times (" |
286 | 0 | << accrualTimes.size() << ")"); |
287 | 0 | QL_REQUIRE(type == CapFloor::Floor || |
288 | 0 | capRates.size() == startDates.size(), |
289 | 0 | "number of start dates (" << startDates.size() |
290 | 0 | << ") different from that of cap rates (" |
291 | 0 | << capRates.size() << ")"); |
292 | 0 | QL_REQUIRE(type == CapFloor::Cap || |
293 | 0 | floorRates.size() == startDates.size(), |
294 | 0 | "number of start dates (" << startDates.size() |
295 | 0 | << ") different from that of floor rates (" |
296 | 0 | << floorRates.size() << ")"); |
297 | 0 | QL_REQUIRE(gearings.size() == startDates.size(), |
298 | 0 | "number of start dates (" << startDates.size() |
299 | 0 | << ") different from that of gearings (" |
300 | 0 | << gearings.size() << ")"); |
301 | 0 | QL_REQUIRE(spreads.size() == startDates.size(), |
302 | 0 | "number of start dates (" << startDates.size() |
303 | 0 | << ") different from that of spreads (" |
304 | 0 | << spreads.size() << ")"); |
305 | 0 | QL_REQUIRE(nominals.size() == startDates.size(), |
306 | 0 | "number of start dates (" << startDates.size() |
307 | 0 | << ") different from that of nominals (" |
308 | 0 | << nominals.size() << ")"); |
309 | 0 | QL_REQUIRE(forwards.size() == startDates.size(), |
310 | 0 | "number of start dates (" << startDates.size() |
311 | 0 | << ") different from that of forwards (" |
312 | 0 | << forwards.size() << ")"); |
313 | 0 | } |
314 | | |
315 | 0 | Rate CapFloor::atmRate(const YieldTermStructure& discountCurve) const { |
316 | 0 | bool includeSettlementDateFlows = false; |
317 | 0 | Date settlementDate = discountCurve.referenceDate(); |
318 | 0 | return CashFlows::atmRate(floatingLeg_, discountCurve, |
319 | 0 | includeSettlementDateFlows, |
320 | 0 | settlementDate); |
321 | 0 | } |
322 | | |
323 | | Volatility CapFloor::impliedVolatility(Real targetValue, |
324 | | const Handle<YieldTermStructure>& d, |
325 | | Volatility guess, |
326 | | Real accuracy, |
327 | | Natural maxEvaluations, |
328 | | Volatility minVol, |
329 | | Volatility maxVol, |
330 | | VolatilityType type, |
331 | 0 | Real displacement) const { |
332 | | //calculate(); |
333 | 0 | QL_REQUIRE(!isExpired(), "instrument expired"); |
334 | | |
335 | 0 | ImpliedCapVolHelper f(*this, d, targetValue, displacement, type); |
336 | | //Brent solver; |
337 | 0 | NewtonSafe solver; |
338 | 0 | solver.setMaxEvaluations(maxEvaluations); |
339 | 0 | return solver.solve(f, accuracy, guess, minVol, maxVol); |
340 | 0 | } |
341 | | |
342 | | } |