QuantLib: a free/open-source library for quantitative finance
Fully annotated sources - version 1.32
Loading...
Searching...
No Matches
couponpricer.cpp
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2007 Giorgio Facchinetti
5 Copyright (C) 2007 Cristina Duminuco
6 Copyright (C) 2011 Ferdinando Ametrano
7 Copyright (C) 2015 Peter Caspers
8
9 This file is part of QuantLib, a free-software/open-source library
10 for financial quantitative analysts and developers - http://quantlib.org/
11
12 QuantLib is free software: you can redistribute it and/or modify it
13 under the terms of the QuantLib license. You should have received a
14 copy of the license along with this program; if not, please email
15 <quantlib-dev@lists.sf.net>. The license is also available online at
16 <http://quantlib.org/license.shtml>.
17
18 This program is distributed in the hope that it will be useful, but WITHOUT
19 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
20 FOR A PARTICULAR PURPOSE. See the license for more details.
21*/
22
23#include <ql/cashflows/capflooredcoupon.hpp>
24#include <ql/cashflows/couponpricer.hpp>
25#include <ql/cashflows/digitalcmscoupon.hpp>
26#include <ql/cashflows/digitalcoupon.hpp>
27#include <ql/cashflows/digitaliborcoupon.hpp>
28#include <ql/cashflows/rangeaccrual.hpp>
29#include <ql/cashflows/subperiodcoupon.hpp>
30#include <ql/experimental/coupons/cmsspreadcoupon.hpp> /* internal */
31#include <ql/experimental/coupons/digitalcmsspreadcoupon.hpp> /* internal */
32#include <ql/pricingengines/blackformula.hpp>
33#include <ql/termstructures/yieldtermstructure.hpp>
34#include <ql/optional.hpp>
35#include <utility>
36
37namespace QuantLib {
38
39//===========================================================================//
40// IborCouponPricer //
41//===========================================================================//
42
45 ext::optional<bool> useIndexedCoupon)
46 : capletVol_(std::move(v)),
47 useIndexedCoupon_(useIndexedCoupon ?
48 *useIndexedCoupon :
49 !IborCoupon::Settings::instance().usingAtParCoupons()) {
51 }
52
54
56 return;
57
58 coupon.fixingValueDate_ = coupon.iborIndex()->fixingCalendar().advance(
59 coupon.fixingDate_, coupon.iborIndex()->fixingDays(), Days);
60 coupon.fixingMaturityDate_ = coupon.iborIndex()->maturityDate(coupon.fixingValueDate_);
61
63 coupon.fixingEndDate_ = coupon.fixingMaturityDate_;
64 } else {
65 if (coupon.isInArrears_)
66 coupon.fixingEndDate_ = coupon.fixingMaturityDate_;
67 else { // par coupon approximation
68 Date nextFixingDate = coupon.iborIndex()->fixingCalendar().advance(
69 coupon.accrualEndDate(), -static_cast<Integer>(coupon.fixingDays_), Days);
70 coupon.fixingEndDate_ = coupon.iborIndex()->fixingCalendar().advance(
71 nextFixingDate, coupon.iborIndex()->fixingDays(), Days);
72 // make sure the estimation period contains at least one day
73 coupon.fixingEndDate_ =
74 std::max(coupon.fixingEndDate_, coupon.fixingValueDate_ + 1);
75 }
76 }
77
78 coupon.spanningTime_ = coupon.iborIndex()->dayCounter().yearFraction(
79 coupon.fixingValueDate_, coupon.fixingEndDate_);
80
81 QL_REQUIRE(coupon.spanningTime_ > 0.0,
82 "\n cannot calculate forward rate between "
83 << coupon.fixingValueDate_ << " and " << coupon.fixingEndDate_
84 << ":\n non positive time (" << coupon.spanningTime_ << ") using "
85 << coupon.iborIndex()->dayCounter().name() << " daycounter");
86
87 coupon.spanningTimeIndexMaturity_ = coupon.iborIndex()->dayCounter().yearFraction(
89
90 coupon.cachedDataIsInitialized_ = true;
91 }
92
94 coupon_ = dynamic_cast<const IborCoupon *>(&coupon);
95 QL_REQUIRE(coupon_, "IborCouponPricer: expected IborCoupon");
96
98
103 QL_REQUIRE(accrualPeriod_ != 0.0, "null accrual period");
104
110 }
111
112
113//===========================================================================//
114// BlackIborCouponPricer //
115//===========================================================================//
116
118
120
121 const Handle<YieldTermStructure>& rateCurve = index_->forwardingTermStructure();
122
123 if (rateCurve.empty()) {
124 discount_ = Null<Real>(); // might not be needed, will be checked later
125 } else {
126 Date paymentDate = coupon_->date();
127 if (paymentDate > rateCurve->referenceDate())
128 discount_ = rateCurve->discount(paymentDate);
129 else
130 discount_ = 1.0;
131 }
132
133 }
134
136 if (fixingDate_ <= Settings::instance().evaluationDate()) {
137 // the amount is determined
138 Real a, b;
139 if (optionType==Option::Call) {
140 a = coupon_->indexFixing();
141 b = effStrike;
142 } else {
143 a = effStrike;
144 b = coupon_->indexFixing();
145 }
146 return std::max(a - b, 0.0);
147 } else {
148 // not yet determined, use Black model
149 QL_REQUIRE(!capletVolatility().empty(),
150 "missing optionlet volatility");
151 Real stdDev =
152 std::sqrt(capletVolatility()->blackVariance(fixingDate_,
153 effStrike));
154 Real shift = capletVolatility()->displacement();
155 bool shiftedLn =
156 capletVolatility()->volatilityType() == ShiftedLognormal;
157 Rate fixing =
158 shiftedLn
159 ? blackFormula(optionType, effStrike, adjustedFixing(),
160 stdDev, 1.0, shift)
161 : bachelierBlackFormula(optionType, effStrike,
162 adjustedFixing(), stdDev, 1.0);
163 return fixing;
164 }
165 }
166
168 Real effStrike) const {
169 QL_REQUIRE(discount_ != Null<Rate>(), "no forecast curve provided");
170 return optionletRate(optionType, effStrike) * accrualPeriod_ * discount_;
171 }
172
174
175 if (fixing == Null<Rate>())
176 fixing = coupon_->indexFixing();
177
178 // if the pay date is equal to the index estimation end date
179 // there is no convexity; in all other cases in principle an
180 // adjustment has to be applied, but the Black76 method only
181 // applies the standard in arrears adjustment; the bivariate
182 // lognormal method is more accurate in this regard.
184 return fixing;
185 const Date& d1 = fixingDate_;
186 const Date& d2 = fixingValueDate_;
187 const Date& d3 = fixingMaturityDate_;
188 if (coupon_->date() == d3)
189 return fixing;
190
191 QL_REQUIRE(!capletVolatility().empty(),
192 "missing optionlet volatility");
193 Date referenceDate = capletVolatility()->referenceDate();
194 // no variance has accumulated, so the convexity is zero
195 if (d1 <= referenceDate)
196 return fixing;
197 const Time& tau = spanningTimeIndexMaturity_;
198 Real variance = capletVolatility()->blackVariance(d1, fixing);
199
200 Real shift = capletVolatility()->displacement();
201 bool shiftedLn =
202 capletVolatility()->volatilityType() == ShiftedLognormal;
203
204 Spread adjustment = shiftedLn
205 ? Real((fixing + shift) * (fixing + shift) *
206 variance * tau / (1.0 + fixing * tau))
207 : Real(variance * tau / (1.0 + fixing * tau));
208
210 QL_REQUIRE(!correlation_.empty(), "no correlation given");
211 const Date& d4 = coupon_->date();
212 const Date& d5 = d4 >= d3 ? d3 : d2;
213 Time tau2 = index_->dayCounter().yearFraction(d5, d4);
214 if (d4 >= d3)
215 adjustment = 0.0;
216 // if d4 < d2 (payment before index start) we just apply the
217 // Black76 in arrears adjustment
218 if (tau2 > 0.0) {
219 Real fixing2 =
220 (index_->forwardingTermStructure()->discount(d5) /
221 index_->forwardingTermStructure()->discount(d4) -
222 1.0) /
223 tau2;
224 adjustment -= shiftedLn
225 ? Real(correlation_->value() * tau2 * variance *
226 (fixing + shift) * (fixing2 + shift) /
227 (1.0 + fixing2 * tau2))
228 : Real(correlation_->value() * tau2 * variance /
229 (1.0 + fixing2 * tau2));
230 }
231 }
232 return fixing + adjustment;
233 }
234
235//===========================================================================//
236// CouponSelectorToSetPricer //
237//===========================================================================//
238
239 namespace {
240
241 class PricerSetter : public AcyclicVisitor,
242 public Visitor<CashFlow>,
243 public Visitor<Coupon>,
244 public Visitor<FloatingRateCoupon>,
245 public Visitor<CappedFlooredCoupon>,
246 public Visitor<IborCoupon>,
247 public Visitor<CmsCoupon>,
248 public Visitor<CmsSpreadCoupon>,
249 public Visitor<CappedFlooredIborCoupon>,
250 public Visitor<CappedFlooredCmsCoupon>,
251 public Visitor<CappedFlooredCmsSpreadCoupon>,
252 public Visitor<DigitalIborCoupon>,
253 public Visitor<DigitalCmsCoupon>,
254 public Visitor<DigitalCmsSpreadCoupon>,
255 public Visitor<RangeAccrualFloatersCoupon>,
256 public Visitor<SubPeriodsCoupon> {
257 private:
258 ext::shared_ptr<FloatingRateCouponPricer> pricer_;
259 public:
260 explicit PricerSetter(ext::shared_ptr<FloatingRateCouponPricer> pricer)
261 : pricer_(std::move(pricer)) {}
262
263 void visit(CashFlow& c) override;
264 void visit(Coupon& c) override;
265 void visit(FloatingRateCoupon& c) override;
266 void visit(CappedFlooredCoupon& c) override;
267 void visit(IborCoupon& c) override;
268 void visit(CappedFlooredIborCoupon& c) override;
269 void visit(DigitalIborCoupon& c) override;
270 void visit(CmsCoupon& c) override;
271 void visit(CmsSpreadCoupon& c) override;
272 void visit(CappedFlooredCmsCoupon& c) override;
273 void visit(CappedFlooredCmsSpreadCoupon& c) override;
274 void visit(DigitalCmsCoupon& c) override;
275 void visit(DigitalCmsSpreadCoupon& c) override;
276 void visit(RangeAccrualFloatersCoupon& c) override;
277 void visit(SubPeriodsCoupon& c) override;
278 };
279
280 void PricerSetter::visit(CashFlow&) {
281 // nothing to do
282 }
283
284 void PricerSetter::visit(Coupon&) {
285 // nothing to do
286 }
287
288 void PricerSetter::visit(FloatingRateCoupon& c) {
289 c.setPricer(pricer_);
290 }
291
292 void PricerSetter::visit(CappedFlooredCoupon& c) {
293 // we might end up here because a CappedFlooredCoupon
294 // was directly constructed; we should then check
295 // the underlying for consistency with the pricer
296 if (ext::dynamic_pointer_cast<IborCoupon>(c.underlying()) != nullptr) {
297 QL_REQUIRE(ext::dynamic_pointer_cast<IborCouponPricer>(pricer_),
298 "pricer not compatible with Ibor Coupon");
299 } else if (ext::dynamic_pointer_cast<CmsCoupon>(c.underlying()) != nullptr) {
300 QL_REQUIRE(ext::dynamic_pointer_cast<CmsCouponPricer>(pricer_),
301 "pricer not compatible with CMS Coupon");
302 } else if (ext::dynamic_pointer_cast<CmsSpreadCoupon>(c.underlying()) != nullptr) {
303 QL_REQUIRE(ext::dynamic_pointer_cast<CmsSpreadCouponPricer>(pricer_),
304 "pricer not compatible with CMS spread Coupon");
305 }
306 c.setPricer(pricer_);
307 }
308
309 void PricerSetter::visit(IborCoupon& c) {
310 const ext::shared_ptr<IborCouponPricer> iborCouponPricer =
311 ext::dynamic_pointer_cast<IborCouponPricer>(pricer_);
312 QL_REQUIRE(iborCouponPricer,
313 "pricer not compatible with Ibor coupon");
314 c.setPricer(iborCouponPricer);
315 }
316
317 void PricerSetter::visit(DigitalIborCoupon& c) {
318 const ext::shared_ptr<IborCouponPricer> iborCouponPricer =
319 ext::dynamic_pointer_cast<IborCouponPricer>(pricer_);
320 QL_REQUIRE(iborCouponPricer,
321 "pricer not compatible with Ibor coupon");
322 c.setPricer(iborCouponPricer);
323 }
324
325 void PricerSetter::visit(CappedFlooredIborCoupon& c) {
326 const ext::shared_ptr<IborCouponPricer> iborCouponPricer =
327 ext::dynamic_pointer_cast<IborCouponPricer>(pricer_);
328 QL_REQUIRE(iborCouponPricer,
329 "pricer not compatible with Ibor coupon");
330 c.setPricer(iborCouponPricer);
331 }
332
333 void PricerSetter::visit(CmsCoupon& c) {
334 const ext::shared_ptr<CmsCouponPricer> cmsCouponPricer =
335 ext::dynamic_pointer_cast<CmsCouponPricer>(pricer_);
336 QL_REQUIRE(cmsCouponPricer,
337 "pricer not compatible with CMS coupon");
338 c.setPricer(cmsCouponPricer);
339 }
340
341 void PricerSetter::visit(CmsSpreadCoupon& c) {
342 const ext::shared_ptr<CmsSpreadCouponPricer> cmsSpreadCouponPricer =
343 ext::dynamic_pointer_cast<CmsSpreadCouponPricer>(pricer_);
344 QL_REQUIRE(cmsSpreadCouponPricer,
345 "pricer not compatible with CMS spread coupon");
346 c.setPricer(cmsSpreadCouponPricer);
347 }
348
349 void PricerSetter::visit(CappedFlooredCmsCoupon& c) {
350 const ext::shared_ptr<CmsCouponPricer> cmsCouponPricer =
351 ext::dynamic_pointer_cast<CmsCouponPricer>(pricer_);
352 QL_REQUIRE(cmsCouponPricer,
353 "pricer not compatible with CMS coupon");
354 c.setPricer(cmsCouponPricer);
355 }
356
357 void PricerSetter::visit(CappedFlooredCmsSpreadCoupon& c) {
358 const ext::shared_ptr<CmsSpreadCouponPricer> cmsSpreadCouponPricer =
359 ext::dynamic_pointer_cast<CmsSpreadCouponPricer>(pricer_);
360 QL_REQUIRE(cmsSpreadCouponPricer,
361 "pricer not compatible with CMS spread coupon");
362 c.setPricer(cmsSpreadCouponPricer);
363 }
364
365 void PricerSetter::visit(DigitalCmsCoupon& c) {
366 const ext::shared_ptr<CmsCouponPricer> cmsCouponPricer =
367 ext::dynamic_pointer_cast<CmsCouponPricer>(pricer_);
368 QL_REQUIRE(cmsCouponPricer,
369 "pricer not compatible with CMS coupon");
370 c.setPricer(cmsCouponPricer);
371 }
372
373 void PricerSetter::visit(DigitalCmsSpreadCoupon& c) {
374 const ext::shared_ptr<CmsSpreadCouponPricer> cmsSpreadCouponPricer =
375 ext::dynamic_pointer_cast<CmsSpreadCouponPricer>(pricer_);
376 QL_REQUIRE(cmsSpreadCouponPricer,
377 "pricer not compatible with CMS spread coupon");
378 c.setPricer(cmsSpreadCouponPricer);
379 }
380
381 void PricerSetter::visit(RangeAccrualFloatersCoupon& c) {
382 const ext::shared_ptr<RangeAccrualPricer> rangeAccrualPricer =
383 ext::dynamic_pointer_cast<RangeAccrualPricer>(pricer_);
384 QL_REQUIRE(rangeAccrualPricer,
385 "pricer not compatible with range-accrual coupon");
386 c.setPricer(rangeAccrualPricer);
387 }
388
389 void PricerSetter::visit(SubPeriodsCoupon& c) {
390 const ext::shared_ptr<SubPeriodsPricer> subPeriodsPricer =
391 ext::dynamic_pointer_cast<SubPeriodsPricer>(pricer_);
392 QL_REQUIRE(subPeriodsPricer,
393 "pricer not compatible with sub-period coupon");
394 c.setPricer(subPeriodsPricer);
395 }
396
397 void setCouponPricersFirstMatching(const Leg& leg,
398 const std::vector<ext::shared_ptr<FloatingRateCouponPricer> >& p) {
399 std::vector<PricerSetter> setter;
400 setter.reserve(p.size());
401 for (const auto& i : p) {
402 setter.emplace_back(i);
403 }
404 for (const auto& i : leg) {
405 Size j = 0;
406 do {
407 try {
408 i->accept(setter[j]);
409 j = p.size();
410 } catch (...) {
411 ++j;
412 }
413 } while (j < p.size());
414 }
415 }
416
417 } // anonymous namespace
418
419 void setCouponPricer(const Leg& leg, const ext::shared_ptr<FloatingRateCouponPricer>& pricer) {
420 PricerSetter setter(pricer);
421 for (const auto& i : leg) {
422 i->accept(setter);
423 }
424 }
425
427 const Leg& leg,
428 const std::vector<ext::shared_ptr<FloatingRateCouponPricer> >&
429 pricers) {
430 Size nCashFlows = leg.size();
431 QL_REQUIRE(nCashFlows>0, "no cashflows");
432
433 Size nPricers = pricers.size();
434 QL_REQUIRE(nCashFlows >= nPricers,
435 "mismatch between leg size (" << nCashFlows <<
436 ") and number of pricers (" << nPricers << ")");
437
438 for (Size i=0; i<nCashFlows; ++i) {
439 PricerSetter setter(i<nPricers ? pricers[i] : pricers[nPricers-1]);
440 leg[i]->accept(setter);
441 }
442 }
443
445 const Leg& leg,
446 const ext::shared_ptr<FloatingRateCouponPricer>& p1,
447 const ext::shared_ptr<FloatingRateCouponPricer>& p2) {
448 std::vector<ext::shared_ptr<FloatingRateCouponPricer> > p;
449 p.push_back(p1);
450 p.push_back(p2);
451 setCouponPricersFirstMatching(leg, p);
452 }
453
455 const Leg& leg,
456 const ext::shared_ptr<FloatingRateCouponPricer>& p1,
457 const ext::shared_ptr<FloatingRateCouponPricer>& p2,
458 const ext::shared_ptr<FloatingRateCouponPricer>& p3) {
459 std::vector<ext::shared_ptr<FloatingRateCouponPricer> > p;
460 p.push_back(p1);
461 p.push_back(p2);
462 p.push_back(p3);
463 setCouponPricersFirstMatching(leg, p);
464 }
465
467 const Leg& leg,
468 const ext::shared_ptr<FloatingRateCouponPricer>& p1,
469 const ext::shared_ptr<FloatingRateCouponPricer>& p2,
470 const ext::shared_ptr<FloatingRateCouponPricer>& p3,
471 const ext::shared_ptr<FloatingRateCouponPricer>& p4) {
472 std::vector<ext::shared_ptr<FloatingRateCouponPricer> > p;
473 p.push_back(p1);
474 p.push_back(p2);
475 p.push_back(p3);
476 p.push_back(p4);
477 setCouponPricersFirstMatching(leg, p);
478 }
479
480
481}
degenerate base class for the Acyclic Visitor pattern
Definition: visitor.hpp:33
const Handle< Quote > correlation_
void initialize(const FloatingRateCoupon &coupon) override
virtual Rate adjustedFixing(Rate fixing=Null< Rate >()) const
Real optionletPrice(Option::Type optionType, Real effStrike) const
const TimingAdjustment timingAdjustment_
Real optionletRate(Option::Type optionType, Real effStrike) const
const Date & accrualEndDate() const
end of the accrual period
Definition: coupon.hpp:108
Date date() const override
Definition: coupon.hpp:53
Time accrualPeriod() const
accrual period as fraction of year
Definition: coupon.cpp:44
Concrete date class.
Definition: date.hpp:125
base floating-rate coupon class
Real gearing() const
index gearing, i.e. multiplicative coefficient for the index
Spread spread() const
spread paid over the fixing of the underlying index
bool isInArrears() const
whether or not the coupon fixes in arrears
Shared handle to an observable.
Definition: handle.hpp:41
bool empty() const
checks if the contained shared pointer points to anything
Definition: handle.hpp:166
Coupon paying a Libor-type index
Definition: iborcoupon.hpp:41
Rate indexFixing() const override
fixing of the underlying index
Definition: iborcoupon.cpp:87
const ext::shared_ptr< IborIndex > & iborIndex() const
Definition: iborcoupon.hpp:58
void initializeCachedData(const IborCoupon &coupon) const
void initialize(const FloatingRateCoupon &coupon) override
Handle< OptionletVolatilityStructure > capletVolatility() const
Handle< OptionletVolatilityStructure > capletVol_
const IborCoupon * coupon_
IborCouponPricer(Handle< OptionletVolatilityStructure > v=Handle< OptionletVolatilityStructure >(), ext::optional< bool > useIndexedCoupon=ext::nullopt)
ext::shared_ptr< IborIndex > index_
template class providing a null value for a given type.
Definition: null.hpp:76
std::pair< iterator, bool > registerWith(const ext::shared_ptr< Observable > &)
Definition: observable.hpp:228
global repository for run-time library settings
Definition: settings.hpp:37
static Settings & instance()
access to the unique instance
Definition: singleton.hpp:104
Visitor for a specific class
Definition: visitor.hpp:40
Real Time
continuous quantity with 1-year units
Definition: types.hpp:62
QL_REAL Real
real number
Definition: types.hpp:50
QL_INTEGER Integer
integer number
Definition: types.hpp:35
Real Spread
spreads on interest rates
Definition: types.hpp:74
Real Rate
interest rates
Definition: types.hpp:70
std::size_t Size
size of a container
Definition: types.hpp:58
Definition: any.hpp:35
void setCouponPricers(const Leg &leg, const std::vector< ext::shared_ptr< FloatingRateCouponPricer > > &pricers)
Real bachelierBlackFormula(Option::Type optionType, Real strike, Real forward, Real stdDev, Real discount)
void setCouponPricer(const Leg &leg, const ext::shared_ptr< FloatingRateCouponPricer > &pricer)
Real blackFormula(Option::Type optionType, Real strike, Real forward, Real stdDev, Real discount, Real displacement)
std::vector< ext::shared_ptr< CashFlow > > Leg
Sequence of cash-flows.
Definition: cashflow.hpp:78
STL namespace.