Loading [MathJax]/extensions/tex2jax.js
QuantLib: a free/open-source library for quantitative finance
fully annotated source code - version 1.38
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
callablebond.cpp
Go to the documentation of this file.
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2008 Allen Kuo
5 Copyright (C) 2017 BN Algorithms Ltd
6
7 This file is part of QuantLib, a free-software/open-source library
8 for financial quantitative analysts and developers - http://quantlib.org/
9
10 QuantLib is free software: you can redistribute it and/or modify it
11 under the terms of the QuantLib license. You should have received a
12 copy of the license along with this program; if not, please email
13 <quantlib-dev@lists.sf.net>. The license is also available online at
14 <http://quantlib.org/license.shtml>.
15
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the license for more details.
19*/
20
26#include <utility>
27
28namespace QuantLib {
29
31 const Date& maturityDate,
32 const Calendar& calendar,
33 DayCounter paymentDayCounter,
34 Real faceAmount,
35 const Date& issueDate,
36 CallabilitySchedule putCallSchedule)
37 : Bond(settlementDays, calendar, issueDate),
38 paymentDayCounter_(std::move(paymentDayCounter)),
39 putCallSchedule_(std::move(putCallSchedule)), faceAmount_(faceAmount) {
40
42
43 if (!putCallSchedule_.empty()) {
44 Date finalOptionDate = Date::minDate();
45 for (auto& i : putCallSchedule_) {
46 finalOptionDate = std::max(finalOptionDate, i->date());
47 }
48 QL_REQUIRE(finalOptionDate <= maturityDate_ ,
49 "Bond cannot mature before last call/put date");
50 }
51
52 // derived classes must set cashflows_ and frequency_
53 }
54
55
57
59 "null settlement date");
60
61 QL_REQUIRE(redemption != Null<Real>(), "null redemption");
63 "positive redemption required: "
64 << redemption << " not allowed");
65
67 "different number of callability dates and prices");
68 QL_REQUIRE(couponDates.size() == couponAmounts.size(),
69 "different number of coupon dates and amounts");
70 }
71
72
73 class CallableBond::ImpliedVolHelper {
74 public:
75 ImpliedVolHelper(const CallableBond& bond,
76 const Handle<YieldTermStructure>& discountCurve,
77 Real targetValue,
78 bool matchNPV);
79 Real operator()(Volatility x) const;
80 private:
81 ext::shared_ptr<PricingEngine> engine_;
83 bool matchNPV_;
84 ext::shared_ptr<SimpleQuote> vol_;
86 };
87
88 CallableBond::ImpliedVolHelper::ImpliedVolHelper(
89 const CallableBond& bond,
90 const Handle<YieldTermStructure>& discountCurve,
91 Real targetValue,
92 bool matchNPV)
93 : targetValue_(targetValue), matchNPV_(matchNPV) {
94
95 vol_ = ext::make_shared<SimpleQuote>(0.0);
96 engine_ = ext::make_shared<BlackCallableFixedRateBondEngine>(Handle<Quote>(vol_),
97 discountCurve);
98
99 bond.setupArguments(engine_->getArguments());
100 results_ =
101 dynamic_cast<const CallableBond::results*>(engine_->getResults());
102 }
103
104 Real CallableBond::ImpliedVolHelper::operator()(Volatility x) const {
105 vol_->setValue(x);
106 engine_->calculate(); // get the Black NPV based on vol x
107 Real value = matchNPV_ ? results_->value : results_->settlementValue;
108 return value - targetValue_;
109 }
110
111
113 const Bond::Price& targetPrice,
114 const Handle<YieldTermStructure>& discountCurve,
115 Real accuracy,
116 Size maxEvaluations,
117 Volatility minVol,
118 Volatility maxVol) const {
119 QL_REQUIRE(!isExpired(), "instrument expired");
120
121 Real dirtyTargetPrice;
122 switch (targetPrice.type()) {
124 dirtyTargetPrice = targetPrice.amount();
125 break;
127 dirtyTargetPrice = targetPrice.amount() + accruedAmount();
128 break;
129 default:
130 QL_FAIL("unknown price type");
131 }
132
133 Real targetValue = dirtyTargetPrice * faceAmount_ / 100.0;
134 Volatility guess = 0.5 * (minVol + maxVol);
135 ImpliedVolHelper f(*this, discountCurve, targetValue, false);
136 Brent solver;
137 solver.setMaxEvaluations(maxEvaluations);
138 return solver.solve(f, accuracy, guess, minVol, maxVol);
139 }
140
141
142 namespace {
143
144 template<class T>
145 class RestoreVal { // NOLINT(cppcoreguidelines-special-member-functions)
148 public:
149 explicit RestoreVal(T &ref):
150 orig_(ref),
151 ref_(ref) { }
152 ~RestoreVal()
153 {
154 ref_=orig_;
155 }
156 };
157
158 class OASHelper {
159 public:
160 OASHelper(const std::function<Real(Real)>& npvhelper,
161 Real targetValue):
162 npvhelper_(npvhelper),
163 targetValue_(targetValue)
164 {
165 }
166
167 Real operator()(Spread x) const
168 {
169 return targetValue_ - npvhelper_(x);
170 }
171 private:
172 const std::function<Real(Real)>& npvhelper_;
174 };
175
176
177 /* Convert a continuous spread to a conventional spread to a
178 reference yield curve
179 */
180 Real continuousToConv(Real oas,
181 const Bond &b,
182 const Handle<YieldTermStructure>& yts,
183 const DayCounter& dayCounter,
184 Compounding compounding,
185 Frequency frequency)
186 {
187 Real zz=yts->zeroRate(b.maturityDate(),
188 dayCounter,
191 InterestRate baseRate(zz,
192 dayCounter,
195 InterestRate spreadedRate(oas+zz,
196 dayCounter,
199 Real br=baseRate.equivalentRate(dayCounter,
200 compounding,
201 frequency,
202 yts->referenceDate(),
203 b.maturityDate()).rate();
204 Real sr=spreadedRate.equivalentRate(dayCounter,
205 compounding,
206 frequency,
207 yts->referenceDate(),
208 b.maturityDate()).rate();
209 // Return the spread
210 return sr-br;
211 }
212
213 /* Convert a conventional spread to a reference yield curve to a
214 continuous spread
215 */
216 Real convToContinuous(Real oas,
217 const Bond &b,
218 const Handle<YieldTermStructure>& yts,
219 const DayCounter& dayCounter,
220 Compounding compounding,
221 Frequency frequency)
222 {
223 Real zz=yts->zeroRate(b.maturityDate(),
224 dayCounter,
225 compounding,
226 frequency);
227 InterestRate baseRate(zz,
228 dayCounter,
229 compounding,
230 frequency);
231
232 InterestRate spreadedRate(oas+zz,
233 dayCounter,
234 compounding,
235 frequency);
236 Real br=baseRate.equivalentRate(dayCounter,
239 yts->referenceDate(),
240 b.maturityDate()).rate();
241 Real sr=spreadedRate.equivalentRate(dayCounter,
244 yts->referenceDate(),
245 b.maturityDate()).rate();
246 // Return the spread
247 return sr-br;
248 }
249
250 }
251
252
253 class CallableBond::NPVSpreadHelper {
254 public:
255 explicit NPVSpreadHelper(CallableBond& bond);
256 Real operator()(Spread x) const;
257 private:
258 CallableBond& bond_;
259 const Instrument::results* results_;
260 };
261
262 CallableBond::NPVSpreadHelper::NPVSpreadHelper(CallableBond& bond):
263 bond_(bond),
264 results_(dynamic_cast<const Instrument::results*>(bond.engine_->getResults()))
265 {
266 bond.setupArguments(bond.engine_->getArguments());
267 }
268
269 Real CallableBond::NPVSpreadHelper::operator()(Real x) const
270 {
271 auto* args = dynamic_cast<CallableBond::arguments*>(bond_.engine_->getArguments());
272 // Pops the original value when function finishes
273 RestoreVal<Spread> restorer(args->spread);
274 args->spread=x;
275 bond_.engine_->calculate();
276 return results_->value;
277 }
278
279 Spread CallableBond::OAS(Real cleanPrice,
280 const Handle<YieldTermStructure>& engineTS,
281 const DayCounter& dayCounter,
282 Compounding compounding,
283 Frequency frequency,
284 Date settlement,
285 Real accuracy,
286 Size maxIterations,
287 Spread guess)
288 {
289 if (settlement == Date())
290 settlement = settlementDate();
291
292 Real dirtyPrice = cleanPrice + accruedAmount(settlement);
293 dirtyPrice /= 100.0 / notional(settlement);
294
295 std::function<Real(Real)> f = NPVSpreadHelper(*this);
296 OASHelper obj(f, dirtyPrice);
297
298 Brent solver;
299 solver.setMaxEvaluations(maxIterations);
300
301 Real step = 0.001;
302 Spread oas=solver.solve(obj, accuracy, guess, step);
303
304 return continuousToConv(oas,
305 *this,
306 engineTS,
307 dayCounter,
308 compounding,
309 frequency);
310 }
311
312
313
314 Real CallableBond::cleanPriceOAS(Real oas,
315 const Handle<YieldTermStructure>& engineTS,
316 const DayCounter& dayCounter,
317 Compounding compounding,
318 Frequency frequency,
319 Date settlement)
320 {
321 if (settlement == Date())
322 settlement = settlementDate();
323
324 oas=convToContinuous(oas,
325 *this,
326 engineTS,
327 dayCounter,
328 compounding,
329 frequency);
330
331 std::function<Real(Real)> f = NPVSpreadHelper(*this);
332
333 Real P = f(oas) * 100.0 / notional(settlement) - accruedAmount(settlement);
334
335 return P;
336 }
337
338 Real CallableBond::effectiveDuration(Real oas,
339 const Handle<YieldTermStructure>& engineTS,
340 const DayCounter& dayCounter,
341 Compounding compounding,
342 Frequency frequency,
343 Real bump)
344 {
345 Real P = cleanPriceOAS(oas,
346 engineTS,
347 dayCounter,
348 compounding,
349 frequency);
350
351 Real Ppp = cleanPriceOAS(oas+bump,
352 engineTS,
353 dayCounter,
354 compounding,
355 frequency);
356 Real Pmm = cleanPriceOAS(oas-bump,
357 engineTS,
358 dayCounter,
359 compounding,
360 frequency);
361
362 if ( P == 0.0 )
363 return 0;
364 else
365 {
366 return (Pmm-Ppp)/(2*P*bump);
367 }
368 }
369
370 Real CallableBond::effectiveConvexity(Real oas,
371 const Handle<YieldTermStructure>& engineTS,
372 const DayCounter& dayCounter,
373 Compounding compounding,
374 Frequency frequency,
375 Real bump)
376 {
377 Real P = cleanPriceOAS(oas,
378 engineTS,
379 dayCounter,
380 compounding,
381 frequency);
382
383 Real Ppp = cleanPriceOAS(oas+bump,
384 engineTS,
385 dayCounter,
386 compounding,
387 frequency);
388 Real Pmm = cleanPriceOAS(oas-bump,
389 engineTS,
390 dayCounter,
391 compounding,
392 frequency);
393
394 if ( P == 0.0 )
395 return 0;
396 else
397 {
398 return (Ppp + Pmm - 2*P) / ( std::pow(bump,2) * P);
399 }
400
401 }
402
403
404 void CallableBond::setupArguments(PricingEngine::arguments* args) const {
405
406 Bond::setupArguments(args);
407
408 auto* arguments = dynamic_cast<CallableBond::arguments*>(args);
409
410 QL_REQUIRE(arguments != nullptr, "no arguments given");
411
412 Date settlement = arguments->settlementDate;
413
414 arguments->faceAmount = faceAmount_;
415 arguments->redemption = redemption()->amount();
416 arguments->redemptionDate = redemption()->date();
417
418 const Leg& cfs = cashflows();
419
420 arguments->couponDates.clear();
421 arguments->couponDates.reserve(cfs.size()-1);
422 arguments->couponAmounts.clear();
423 arguments->couponAmounts.reserve(cfs.size()-1);
424
425 for (Size i=0; i<cfs.size()-1; i++) {
426 if (!cfs[i]->hasOccurred(settlement, false)
427 && !cfs[i]->tradingExCoupon(settlement)) {
428 arguments->couponDates.push_back(cfs[i]->date());
429 arguments->couponAmounts.push_back(cfs[i]->amount());
430 }
431 }
432
435 arguments->callabilityPrices.reserve(putCallSchedule_.size());
436 arguments->callabilityDates.reserve(putCallSchedule_.size());
437
438 arguments->paymentDayCounter = paymentDayCounter_;
439 arguments->frequency = frequency_;
440
441 arguments->putCallSchedule = putCallSchedule_;
442 for (const auto& i : putCallSchedule_) {
443 if (!i->hasOccurred(settlement, false)) {
444 arguments->callabilityDates.push_back(i->date());
445 arguments->callabilityPrices.push_back(i->price().amount());
446
447 if (i->price().type() == Bond::Price::Clean) {
448 /* calling accrued() forces accrued interest to be zero
449 if future option date is also coupon date, so that dirty
450 price = clean price. Use here because callability is
451 always applied before coupon in the tree engine.
452 */
453 arguments->callabilityPrices.back() += this->accrued(i->date());
454 }
455 }
456 }
457
458 arguments->spread = 0.0;
459 }
460
461
462 Real CallableBond::accrued(Date settlement) const {
463
464 if (settlement == Date()) settlement = settlementDate();
465
466 const bool IncludeToday = false;
467 for (const auto& cashflow : cashflows_) {
468 // the first coupon paying after d is the one we're after
469 if (!cashflow->hasOccurred(settlement, IncludeToday)) {
470 ext::shared_ptr<Coupon> coupon = ext::dynamic_pointer_cast<Coupon>(cashflow);
471 if (coupon != nullptr)
472 // !!!
473 return coupon->accruedAmount(settlement) /
474 notional(settlement) * 100.0;
475 else
476 return 0.0;
477 }
478 }
479 return 0.0;
480 }
481
482
483 CallableFixedRateBond::CallableFixedRateBond(
484 Natural settlementDays,
485 Real faceAmount,
486 Schedule schedule,
487 const std::vector<Rate>& coupons,
488 const DayCounter& accrualDayCounter,
489 BusinessDayConvention paymentConvention,
490 Real redemption,
491 const Date& issueDate,
492 const CallabilitySchedule& putCallSchedule,
493 const Period& exCouponPeriod,
494 const Calendar& exCouponCalendar,
495 BusinessDayConvention exCouponConvention,
496 bool exCouponEndOfMonth)
497 : CallableBond(settlementDays, schedule.dates().back(), schedule.calendar(),
498 accrualDayCounter, faceAmount, issueDate, putCallSchedule) {
499
500 frequency_ = schedule.hasTenor() ? schedule.tenor().frequency() : NoFrequency;
501
502 cashflows_ =
503 FixedRateLeg(std::move(schedule))
504 .withNotionals(faceAmount)
505 .withCouponRates(coupons, accrualDayCounter)
506 .withPaymentAdjustment(paymentConvention)
507 .withExCouponPeriod(exCouponPeriod,
508 exCouponCalendar,
509 exCouponConvention,
510 exCouponEndOfMonth);
511
513 }
514
515
517 Natural settlementDays,
518 Real faceAmount,
519 const Calendar& calendar,
520 const Date& maturityDate,
521 const DayCounter& dayCounter,
522 BusinessDayConvention paymentConvention,
523 Real redemption,
524 const Date& issueDate,
525 const CallabilitySchedule& putCallSchedule)
526 : CallableBond(settlementDays, maturityDate, calendar,
527 dayCounter, faceAmount, issueDate, putCallSchedule) {
528
530
531 Date redemptionDate = calendar_.adjust(maturityDate_,
532 paymentConvention);
533 setSingleRedemption(faceAmount, redemption, redemptionDate);
534 }
535
536}
Black-formula callable bond engines.
Brent 1-D solver.
const std::function< Real(Real)> & npvhelper_
T orig_
T & ref_
Real targetValue_
callable bond classes
Cash flow vector builders.
ext::shared_ptr< SimpleQuote > vol_
Definition: cdsoption.cpp:62
ext::shared_ptr< PricingEngine > engine_
Definition: cdsoption.cpp:60
const Instrument::results * results_
Definition: cdsoption.cpp:63
Bond price information.
Definition: bond.hpp:62
Type type() const
Definition: bond.hpp:71
Real amount() const
Definition: bond.hpp:67
Base bond class.
Definition: bond.hpp:59
Calendar calendar_
Definition: bond.hpp:285
bool isExpired() const override
returns whether the instrument might have value greater than zero.
Definition: bond.cpp:104
void addRedemptionsToCashflows(const std::vector< Real > &redemptions=std::vector< Real >())
Definition: bond.cpp:304
virtual Real accruedAmount(Date d=Date()) const
accrued amount at a given date
Definition: bond.cpp:256
Leg cashflows_
Definition: bond.hpp:288
const ext::shared_ptr< CashFlow > & redemption() const
Definition: bond.cpp:141
void setSingleRedemption(Real notional, Real redemption, const Date &date)
Definition: bond.cpp:331
Date maturityDate() const
Definition: bond.cpp:151
Date maturityDate_
Definition: bond.hpp:291
Brent 1-D solver
Definition: brent.hpp:37
calendar class
Definition: calendar.hpp:61
Date adjust(const Date &, BusinessDayConvention convention=Following) const
Definition: calendar.cpp:84
std::vector< Date > callabilityDates
Real redemption
redemption = face amount * redemption / 100.
std::vector< Real > callabilityPrices
bond full/dirty/cash prices
void validate() const override
CallabilitySchedule putCallSchedule
results for a callable bond calculation
Callable bond base class.
CallabilitySchedule putCallSchedule_
CallableBond(Natural settlementDays, const Date &maturityDate, const Calendar &calendar, DayCounter paymentDayCounter, Real faceAmount, const Date &issueDate=Date(), CallabilitySchedule putCallSchedule=CallabilitySchedule())
void setupArguments(PricingEngine::arguments *args) const override
Volatility impliedVolatility(const Bond::Price &targetPrice, const Handle< YieldTermStructure > &discountCurve, Real accuracy, Size maxEvaluations, Volatility minVol, Volatility maxVol) const
returns the Black implied forward yield volatility
CallableZeroCouponBond(Natural settlementDays, Real faceAmount, const Calendar &calendar, const Date &maturityDate, const DayCounter &dayCounter, BusinessDayConvention paymentConvention=Following, Real redemption=100.0, const Date &issueDate=Date(), const CallabilitySchedule &putCallSchedule={})
Concrete date class.
Definition: date.hpp:125
static Date minDate()
earliest allowed date
Definition: date.cpp:766
day counter class
Definition: daycounter.hpp:44
helper class building a sequence of fixed rate coupons
FixedRateLeg & withNotionals(Real)
FixedRateLeg & withPaymentAdjustment(BusinessDayConvention)
FixedRateLeg & withCouponRates(Rate, const DayCounter &paymentDayCounter, Compounding comp=Simple, Frequency freq=Annual)
FixedRateLeg & withExCouponPeriod(const Period &, const Calendar &, BusinessDayConvention, bool endOfMonth=false)
Shared handle to an observable.
Definition: handle.hpp:41
ext::shared_ptr< PricingEngine > engine_
Definition: instrument.hpp:110
template class providing a null value for a given type.
Definition: null.hpp:59
Frequency frequency() const
Definition: period.cpp:69
Payment schedule.
Definition: schedule.hpp:40
bool hasTenor() const
Definition: schedule.hpp:198
const Period & tenor() const
Definition: schedule.hpp:202
void setMaxEvaluations(Size evaluations)
Definition: solver1d.hpp:238
Real solve(const F &f, Real accuracy, Real guess, Real step) const
Definition: solver1d.hpp:84
#define QL_REQUIRE(condition, message)
throw an error if the given pre-condition is not verified
Definition: errors.hpp:117
#define QL_FAIL(message)
throw an error (possibly with file and line information)
Definition: errors.hpp:92
std::function< Real(Real)> b
Frequency
Frequency of events.
Definition: frequency.hpp:37
BusinessDayConvention
Business Day conventions.
@ Once
only once, e.g., a zero-coupon
Definition: frequency.hpp:38
@ NoFrequency
null frequency
Definition: frequency.hpp:37
QL_REAL Real
real number
Definition: types.hpp:50
unsigned QL_INTEGER Natural
positive integer
Definition: types.hpp:43
Real Volatility
volatility
Definition: types.hpp:78
Real Spread
spreads on interest rates
Definition: types.hpp:74
std::size_t Size
size of a container
Definition: types.hpp:58
Definition: any.hpp:37
Compounding
Interest rate coumpounding rule.
Definition: compounding.hpp:32
std::vector< ext::shared_ptr< Callability > > CallabilitySchedule
std::vector< ext::shared_ptr< CashFlow > > Leg
Sequence of cash-flows.
Definition: cashflow.hpp:78
STL namespace.
Zero spreaded term structure.