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
35namespace 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 : discountCurve_(std::move(discountCurve)), targetValue_(targetValue),
62 vol_(ext::make_shared<SimpleQuote>(args: -1.0)) {
63
64 // vol_ is set an implausible value, so that calculation is forced
65 // at first ImpliedCapVolHelper::operator()(Volatility x) call
66 Handle<Quote> h(vol_);
67
68 switch (type) {
69 case ShiftedLognormal:
70 engine_ = ext::shared_ptr<PricingEngine>(new
71 BlackCapFloorEngine(discountCurve_, h, Actual365Fixed(),
72 displacement));
73 break;
74 case Normal:
75 engine_ = ext::shared_ptr<PricingEngine>(new
76 BachelierCapFloorEngine(discountCurve_, h,
77 Actual365Fixed()));
78 break;
79 default:
80 QL_FAIL("unknown VolatilityType (" << type << ")");
81 break;
82 }
83
84 cap.setupArguments(engine_->getArguments());
85
86 results_ =
87 dynamic_cast<const Instrument::results*>(engine_->getResults());
88 }
89
90 Real ImpliedCapVolHelper::operator()(Volatility x) const {
91 if (x!=vol_->value()) {
92 vol_->setValue(x);
93 engine_->calculate();
94 }
95 return results_->value-targetValue_;
96 }
97
98 Real ImpliedCapVolHelper::derivative(Volatility x) const {
99 if (x!=vol_->value()) {
100 vol_->setValue(x);
101 engine_->calculate();
102 }
103 auto vega_ = results_->additionalResults.find(x: "vega");
104 QL_REQUIRE(vega_ != results_->additionalResults.end(),
105 "vega not provided");
106 return ext::any_cast<Real>(operand: vega_->second);
107 }
108 }
109
110 std::ostream& operator<<(std::ostream& out,
111 CapFloor::Type t) {
112 switch (t) {
113 case CapFloor::Cap:
114 return out << "Cap";
115 case CapFloor::Floor:
116 return out << "Floor";
117 case CapFloor::Collar:
118 return out << "Collar";
119 default:
120 QL_FAIL("unknown CapFloor::Type (" << Integer(t) << ")");
121 }
122 }
123
124 CapFloor::CapFloor(CapFloor::Type type,
125 Leg floatingLeg,
126 std::vector<Rate> capRates,
127 std::vector<Rate> floorRates)
128 : type_(type), floatingLeg_(std::move(floatingLeg)), capRates_(std::move(capRates)),
129 floorRates_(std::move(floorRates)) {
130 if (type_ == Cap || type_ == Collar) {
131 QL_REQUIRE(!capRates_.empty(), "no cap rates given");
132 capRates_.reserve(n: floatingLeg_.size());
133 while (capRates_.size() < floatingLeg_.size())
134 capRates_.push_back(x: capRates_.back());
135 }
136 if (type_ == Floor || type_ == Collar) {
137 QL_REQUIRE(!floorRates_.empty(), "no floor rates given");
138 floorRates_.reserve(n: floatingLeg_.size());
139 while (floorRates_.size() < floatingLeg_.size())
140 floorRates_.push_back(x: floorRates_.back());
141 }
142 Leg::const_iterator i;
143 for (i = floatingLeg_.begin(); i != floatingLeg_.end(); ++i)
144 registerWith(h: *i);
145
146 registerWith(h: Settings::instance().evaluationDate());
147 }
148
149 CapFloor::CapFloor(CapFloor::Type type, Leg floatingLeg, const std::vector<Rate>& strikes)
150 : type_(type), floatingLeg_(std::move(floatingLeg)) {
151 QL_REQUIRE(!strikes.empty(), "no strikes given");
152 if (type_ == Cap) {
153 capRates_ = strikes;
154 capRates_.reserve(n: floatingLeg_.size());
155 while (capRates_.size() < floatingLeg_.size())
156 capRates_.push_back(x: capRates_.back());
157 } else if (type_ == Floor) {
158 floorRates_ = strikes;
159 floorRates_.reserve(n: floatingLeg_.size());
160 while (floorRates_.size() < floatingLeg_.size())
161 floorRates_.push_back(x: floorRates_.back());
162 } else
163 QL_FAIL("only Cap/Floor types allowed in this constructor");
164
165 Leg::const_iterator i;
166 for (i = floatingLeg_.begin(); i != floatingLeg_.end(); ++i)
167 registerWith(h: *i);
168
169 registerWith(h: Settings::instance().evaluationDate());
170 }
171
172 bool CapFloor::isExpired() const {
173 for (Size i=floatingLeg_.size(); i>0; --i)
174 if (!floatingLeg_[i-1]->hasOccurred())
175 return false;
176 return true;
177 }
178
179 Date CapFloor::startDate() const {
180 return CashFlows::startDate(leg: floatingLeg_);
181 }
182
183 Date CapFloor::maturityDate() const {
184 return CashFlows::maturityDate(leg: floatingLeg_);
185 }
186
187 ext::shared_ptr<FloatingRateCoupon>
188 CapFloor::lastFloatingRateCoupon() const {
189 ext::shared_ptr<CashFlow> lastCF(floatingLeg_.back());
190 ext::shared_ptr<FloatingRateCoupon> lastFloatingCoupon =
191 ext::dynamic_pointer_cast<FloatingRateCoupon>(r: lastCF);
192 return lastFloatingCoupon;
193 }
194
195 ext::shared_ptr<CapFloor> CapFloor::optionlet(const Size i) const {
196 QL_REQUIRE(i < floatingLeg().size(),
197 io::ordinal(i+1) << " optionlet does not exist, only " <<
198 floatingLeg().size());
199 Leg cf(1, floatingLeg()[i]);
200
201 std::vector<Rate> cap, floor;
202 if (type() == Cap || type() == Collar)
203 cap.push_back(x: capRates()[i]);
204 if (type() == Floor || type() == Collar)
205 floor.push_back(x: floorRates()[i]);
206
207 return ext::make_shared<CapFloor>(args: type(), args&: cf, args&: cap, args&: floor);
208 }
209
210 void CapFloor::setupArguments(PricingEngine::arguments* args) const {
211 auto* arguments = dynamic_cast<CapFloor::arguments*>(args);
212 QL_REQUIRE(arguments != nullptr, "wrong argument type");
213
214 Size n = floatingLeg_.size();
215
216 arguments->startDates.resize(new_size: n);
217 arguments->fixingDates.resize(new_size: n);
218 arguments->endDates.resize(new_size: n);
219 arguments->accrualTimes.resize(new_size: n);
220 arguments->forwards.resize(new_size: n);
221 arguments->nominals.resize(new_size: n);
222 arguments->gearings.resize(new_size: n);
223 arguments->capRates.resize(new_size: n);
224 arguments->floorRates.resize(new_size: n);
225 arguments->spreads.resize(new_size: n);
226 arguments->indexes.resize(new_size: n);
227
228 arguments->type = type_;
229
230 Date today = Settings::instance().evaluationDate();
231
232 for (Size i=0; i<n; ++i) {
233 ext::shared_ptr<FloatingRateCoupon> coupon =
234 ext::dynamic_pointer_cast<FloatingRateCoupon>(
235 r: floatingLeg_[i]);
236 QL_REQUIRE(coupon, "non-FloatingRateCoupon given");
237 arguments->startDates[i] = coupon->accrualStartDate();
238 arguments->fixingDates[i] = coupon->fixingDate();
239 arguments->endDates[i] = coupon->date();
240
241 // this is passed explicitly for precision
242 arguments->accrualTimes[i] = coupon->accrualPeriod();
243
244 // this is passed explicitly for precision...
245 if (arguments->endDates[i] >= today) { // ...but only if needed
246 arguments->forwards[i] = coupon->adjustedFixing();
247 } else {
248 arguments->forwards[i] = Null<Rate>();
249 }
250
251 arguments->nominals[i] = coupon->nominal();
252 Spread spread = coupon->spread();
253 Real gearing = coupon->gearing();
254 arguments->gearings[i] = gearing;
255 arguments->spreads[i] = spread;
256
257 if (type_ == Cap || type_ == Collar)
258 arguments->capRates[i] = (capRates_[i]-spread)/gearing;
259 else
260 arguments->capRates[i] = Null<Rate>();
261
262 if (type_ == Floor || type_ == Collar)
263 arguments->floorRates[i] = (floorRates_[i]-spread)/gearing;
264 else
265 arguments->floorRates[i] = Null<Rate>();
266
267 arguments->indexes[i] = coupon->index();
268 }
269 }
270
271 void CapFloor::deepUpdate() {
272 for (auto& i : floatingLeg_) {
273 i->deepUpdate();
274 }
275 update();
276 }
277
278 void CapFloor::arguments::validate() const {
279 QL_REQUIRE(endDates.size() == startDates.size(),
280 "number of start dates (" << startDates.size()
281 << ") different from that of end dates ("
282 << endDates.size() << ")");
283 QL_REQUIRE(accrualTimes.size() == startDates.size(),
284 "number of start dates (" << startDates.size()
285 << ") different from that of accrual times ("
286 << accrualTimes.size() << ")");
287 QL_REQUIRE(type == CapFloor::Floor ||
288 capRates.size() == startDates.size(),
289 "number of start dates (" << startDates.size()
290 << ") different from that of cap rates ("
291 << capRates.size() << ")");
292 QL_REQUIRE(type == CapFloor::Cap ||
293 floorRates.size() == startDates.size(),
294 "number of start dates (" << startDates.size()
295 << ") different from that of floor rates ("
296 << floorRates.size() << ")");
297 QL_REQUIRE(gearings.size() == startDates.size(),
298 "number of start dates (" << startDates.size()
299 << ") different from that of gearings ("
300 << gearings.size() << ")");
301 QL_REQUIRE(spreads.size() == startDates.size(),
302 "number of start dates (" << startDates.size()
303 << ") different from that of spreads ("
304 << spreads.size() << ")");
305 QL_REQUIRE(nominals.size() == startDates.size(),
306 "number of start dates (" << startDates.size()
307 << ") different from that of nominals ("
308 << nominals.size() << ")");
309 QL_REQUIRE(forwards.size() == startDates.size(),
310 "number of start dates (" << startDates.size()
311 << ") different from that of forwards ("
312 << forwards.size() << ")");
313 }
314
315 Rate CapFloor::atmRate(const YieldTermStructure& discountCurve) const {
316 bool includeSettlementDateFlows = false;
317 Date settlementDate = discountCurve.referenceDate();
318 return CashFlows::atmRate(leg: floatingLeg_, discountCurve,
319 includeSettlementDateFlows,
320 settlementDate);
321 }
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 Real displacement) const {
332 //calculate();
333 QL_REQUIRE(!isExpired(), "instrument expired");
334
335 ImpliedCapVolHelper f(*this, d, targetValue, displacement, type);
336 //Brent solver;
337 NewtonSafe solver;
338 solver.setMaxEvaluations(maxEvaluations);
339 return solver.solve(f, accuracy, guess, xMin: minVol, xMax: maxVol);
340 }
341
342}
343

source code of quantlib/ql/instruments/capfloor.cpp

Morty Proxy This is a proxified and sanitized view of the page, visit original site.