QuantLib: a free/open-source library for quantitative finance
fully annotated source code - version 1.34
Loading...
Searching...
No Matches
bond.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) 2004 Jeff Yu
5 Copyright (C) 2004 M-Dimension Consulting Inc.
6 Copyright (C) 2005, 2006, 2007, 2008, 2010 StatPro Italia srl
7 Copyright (C) 2007, 2008, 2009 Ferdinando Ametrano
8 Copyright (C) 2007 Chiara Fornarola
9 Copyright (C) 2008 Simon Ibbotson
10 Copyright (C) 2022 Oleg Kulkov
11
12 This file is part of QuantLib, a free-software/open-source library
13 for financial quantitative analysts and developers - http://quantlib.org/
14
15 QuantLib is free software: you can redistribute it and/or modify it
16 under the terms of the QuantLib license. You should have received a
17 copy of the license along with this program; if not, please email
18 <quantlib-dev@lists.sf.net>. The license is also available online at
19 <http://quantlib.org/license.shtml>.
20
21 This program is distributed in the hope that it will be useful, but WITHOUT
22 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
23 FOR A PARTICULAR PURPOSE. See the license for more details.
24*/
25
33#include <utility>
34
35namespace QuantLib {
36
37 Bond::Bond(Natural settlementDays, Calendar calendar, const Date& issueDate, const Leg& coupons)
38 : settlementDays_(settlementDays), calendar_(std::move(calendar)), cashflows_(coupons),
39 issueDate_(issueDate) {
40
41 if (!coupons.empty()) {
42 std::sort(cashflows_.begin(), cashflows_.end(),
43 earlier_than<ext::shared_ptr<CashFlow> >());
44
45 if (issueDate_ != Date()) {
47 "issue date (" << issueDate_ <<
48 ") must be earlier than first payment date (" <<
49 cashflows_[0]->date() << ")");
50 }
51
52 maturityDate_ = coupons.back()->date();
53
55 }
56
57 registerWith(Settings::instance().evaluationDate());
58 for (const auto& cashflow : cashflows_)
59 registerWith(cashflow);
60 }
61
62 Bond::Bond(Natural settlementDays,
63 Calendar calendar,
64 Real faceAmount,
65 const Date& maturityDate,
66 const Date& issueDate,
67 const Leg& cashflows)
68 : settlementDays_(settlementDays), calendar_(std::move(calendar)), cashflows_(cashflows),
69 maturityDate_(maturityDate), issueDate_(issueDate) {
70
71 if (!cashflows.empty()) {
72
73 std::sort(cashflows_.begin(), cashflows_.end()-1,
74 earlier_than<ext::shared_ptr<CashFlow> >());
75
76 if (maturityDate_ == Date())
78
79 if (issueDate_ != Date()) {
81 "issue date (" << issueDate_ <<
82 ") must be earlier than first payment date (" <<
83 cashflows_[0]->date() << ")");
84 }
85
86 notionals_.resize(2);
87 notionalSchedule_.resize(2);
88
90 notionals_[0] = faceAmount;
91
93 notionals_[1] = 0.0;
94
95 redemptions_.push_back(cashflows.back());
96 }
97
98 registerWith(Settings::instance().evaluationDate());
99 for (const auto& cashflow : cashflows_)
100 registerWith(cashflow);
101 }
102
103 bool Bond::isExpired() const {
104 // this is the Instrument interface, so it doesn't use
105 // BondFunctions, and includeSettlementDateFlows is true
106 // (unless QL_TODAY_PAYMENTS will set it to false later on)
108 true,
109 Settings::instance().evaluationDate());
110 }
111
113 if (d == Date())
114 d = settlementDate();
115
116 if (d > notionalSchedule_.back()) {
117 // after maturity
118 return 0.0;
119 }
120
121 // After the check above, d is between the schedule
122 // boundaries. We search starting from the second notional
123 // date, since the first is null. After the call to
124 // lower_bound, *i is the earliest date which is greater or
125 // equal than d. Its index is greater or equal to 1.
126 auto i = std::lower_bound(notionalSchedule_.begin() + 1, notionalSchedule_.end(), d);
127 Size index = std::distance(notionalSchedule_.begin(), i);
128
129 if (d < notionalSchedule_[index]) {
130 // no doubt about what to return
131 return notionals_[index-1];
132 } else {
133 // d is equal to a redemption date.
134 // As per bond conventions, the payment has occurred;
135 // the bond already changed notional.
136 return notionals_[index];
137 }
138 }
139
140 const ext::shared_ptr<CashFlow>& Bond::redemption() const {
141 QL_REQUIRE(redemptions_.size() == 1,
142 "multiple redemption cash flows given");
143 return redemptions_.back();
144 }
145
147 return BondFunctions::startDate(*this);
148 }
149
152 return maturityDate_;
153 else
154 return BondFunctions::maturityDate(*this);
155 }
156
157 bool Bond::isTradable(Date d) const {
158 return BondFunctions::isTradable(*this, d);
159 }
160
162 if (d==Date())
164
165 // usually, the settlement is at T+n...
166 Date settlement = calendar_.advance(d, settlementDays_, Days);
167 // ...but the bond won't be traded until the issue date (if given.)
168 if (issueDate_ == Date())
169 return settlement;
170 else
171 return std::max(settlement, issueDate_);
172 }
173
176 }
177
179 Real currentNotional = notional(settlementDate());
180 if (currentNotional == 0.0)
181 return 0.0;
182 else
183 return settlementValue()*100.0/currentNotional;
184 }
185
187 calculate();
189 "settlement value not provided");
190 return settlementValue_;
191 }
192
193 Real Bond::settlementValue(Real cleanPrice) const {
195 return dirtyPrice / 100.0 * notional(settlementDate());
196 }
197
199 Compounding comp,
200 Frequency freq,
201 Real accuracy,
202 Size maxEvaluations,
203 Real guess,
204 Bond::Price::Type priceType) const {
205 Real currentNotional = notional(settlementDate());
206 if (currentNotional == 0.0)
207 return 0.0;
208
209 Bond::Price price(priceType == Bond::Price::Clean ? cleanPrice() : dirtyPrice(), priceType);
210
211 return BondFunctions::yield(*this, price, dc, comp, freq,
213 accuracy, maxEvaluations,
214 guess);
215 }
216
218 const DayCounter& dc,
219 Compounding comp,
220 Frequency freq,
221 Date settlement) const {
222 return BondFunctions::cleanPrice(*this, y, dc, comp, freq, settlement);
223 }
224
226 const DayCounter& dc,
227 Compounding comp,
228 Frequency freq,
229 Date settlement) const {
230 Real currentNotional = notional(settlement);
231 if (currentNotional == 0.0)
232 return 0.0;
233
234 return BondFunctions::cleanPrice(*this, y, dc, comp, freq, settlement)
235 + accruedAmount(settlement);
236 }
237
239 const DayCounter& dc,
240 Compounding comp,
241 Frequency freq,
242 Date settlement,
243 Real accuracy,
244 Size maxEvaluations,
245 Real guess,
246 Bond::Price::Type priceType) const {
247 return yield({price, priceType}, dc, comp, freq, settlement, accuracy,
248 maxEvaluations, guess);
249 }
251 const DayCounter& dc,
252 Compounding comp,
253 Frequency freq,
254 Date settlement,
255 Real accuracy,
256 Size maxEvaluations,
257 Real guess) const {
258 Real currentNotional = notional(settlement);
259 if (currentNotional == 0.0)
260 return 0.0;
261
262 return BondFunctions::yield(*this, price, dc, comp, freq,
263 settlement, accuracy, maxEvaluations,
264 guess);
265 }
266
267 Real Bond::accruedAmount(Date settlement) const {
268 Real currentNotional = notional(settlement);
269 if (currentNotional == 0.0)
270 return 0.0;
271
272 return BondFunctions::accruedAmount(*this, settlement);
273 }
274
275 Rate Bond::nextCouponRate(Date settlement) const {
276 return BondFunctions::nextCouponRate(*this, settlement);
277 }
278
280 return BondFunctions::previousCouponRate(*this, settlement);
281 }
282
283 Date Bond::nextCashFlowDate(Date settlement) const {
284 return BondFunctions::nextCashFlowDate(*this, settlement);
285 }
286
288 return BondFunctions::previousCashFlowDate(*this, settlement);
289 }
290
291 void Bond::setupExpired() const {
293 settlementValue_ = 0.0;
294 }
295
297 auto* arguments = dynamic_cast<Bond::arguments*>(args);
298 QL_REQUIRE(arguments != nullptr, "wrong argument type");
299
303 }
304
306
308
309 const auto* results = dynamic_cast<const Bond::results*>(r);
310 QL_ENSURE(results != nullptr, "wrong result type");
311
313 }
314
315 void Bond::addRedemptionsToCashflows(const std::vector<Real>& redemptions) {
316 // First, we gather the notional information from the cashflows
318 // Then, we create the redemptions based on the notional
319 // information and we add them to the cashflows vector after
320 // the coupons.
321 redemptions_.clear();
322 for (Size i=1; i<notionalSchedule_.size(); ++i) {
323 Real R = i < redemptions.size() ? redemptions[i] :
324 !redemptions.empty() ? redemptions.back() :
325 100.0;
326 Real amount = (R/100.0)*(notionals_[i-1]-notionals_[i]);
327 ext::shared_ptr<CashFlow> payment;
328 if (i < notionalSchedule_.size()-1)
329 payment.reset(new AmortizingPayment(amount,
331 else
332 payment.reset(new Redemption(amount, notionalSchedule_[i]));
333 cashflows_.push_back(payment);
334 redemptions_.push_back(payment);
335 }
336 // stable_sort now moves the redemptions to the right places
337 // while ensuring that they follow coupons with the same date.
338 std::stable_sort(cashflows_.begin(), cashflows_.end(),
339 earlier_than<ext::shared_ptr<CashFlow> >());
340 }
341
343 Real redemption,
344 const Date& date) {
345
346 ext::shared_ptr<CashFlow> redemptionCashflow(
347 new Redemption(notional*redemption/100.0, date));
348 setSingleRedemption(notional, redemptionCashflow);
349 }
350
352 const ext::shared_ptr<CashFlow>& redemption) {
353 notionals_.resize(2);
354 notionalSchedule_.resize(2);
355 redemptions_.clear();
356
357 notionalSchedule_[0] = Date();
358 notionals_[0] = notional;
359
360 notionalSchedule_[1] = redemption->date();
361 notionals_[1] = 0.0;
362
363 cashflows_.push_back(redemption);
364 redemptions_.push_back(redemption);
365 }
366
368 for (auto& cashflow : cashflows_) {
369 cashflow->deepUpdate();
370 }
371 update();
372 }
373
375 notionalSchedule_.clear();
376 notionals_.clear();
377
378 Date lastPaymentDate = Date();
379 notionalSchedule_.emplace_back();
380 for (auto& cashflow : cashflows_) {
381 ext::shared_ptr<Coupon> coupon = ext::dynamic_pointer_cast<Coupon>(cashflow);
382 if (!coupon)
383 continue;
384
385 Real notional = coupon->nominal();
386 // we add the notional only if it is the first one...
387 if (notionals_.empty()) {
388 notionals_.push_back(coupon->nominal());
389 lastPaymentDate = coupon->date();
390 } else if (!close(notional, notionals_.back())) {
391 // ...or if it has changed.
392 notionals_.push_back(coupon->nominal());
393 // in this case, we also add the last valid date for
394 // the previous one...
395 notionalSchedule_.push_back(lastPaymentDate);
396 // ...and store the candidate for this one.
397 lastPaymentDate = coupon->date();
398 } else {
399 // otherwise, we just extend the valid range of dates
400 // for the current notional.
401 lastPaymentDate = coupon->date();
402 }
403 }
404 QL_REQUIRE(!notionals_.empty(), "no coupons provided");
405 notionals_.push_back(0.0);
406 notionalSchedule_.push_back(lastPaymentDate);
407 }
408
409
411 QL_REQUIRE(settlementDate != Date(), "no settlement date provided");
412 QL_REQUIRE(!cashflows.empty(), "no cash flow provided");
413 for (const auto & cf: cashflows)
414 QL_REQUIRE(cf, "null cash flow provided");
415 }
416
417}
concrete bond class
bond functions
Brent 1-D solver.
Cash-flow analysis functions.
Bond price information.
Definition: bond.hpp:62
void validate() const override
Definition: bond.cpp:410
Calendar calendar_
Definition: bond.hpp:299
Real cleanPrice() const
theoretical clean price
Definition: bond.cpp:174
Leg redemptions_
Definition: bond.hpp:303
Real settlementValue_
Definition: bond.hpp:306
void setupArguments(PricingEngine::arguments *) const override
Definition: bond.cpp:296
Rate previousCouponRate(Date d=Date()) const
Previous coupon already paid at a given date.
Definition: bond.cpp:279
bool isExpired() const override
returns whether the instrument might have value greater than zero.
Definition: bond.cpp:103
void addRedemptionsToCashflows(const std::vector< Real > &redemptions=std::vector< Real >())
Definition: bond.cpp:315
Rate yield(const DayCounter &dc, Compounding comp, Frequency freq, Real accuracy=1.0e-8, Size maxEvaluations=100, Real guess=0.05, Bond::Price::Type priceType=Bond::Price::Clean) const
theoretical bond yield
Definition: bond.cpp:198
virtual Real accruedAmount(Date d=Date()) const
accrued amount at a given date
Definition: bond.cpp:267
void deepUpdate() override
Definition: bond.cpp:367
Date startDate() const
Definition: bond.cpp:146
const Leg & cashflows() const
Definition: bond.hpp:344
Date nextCashFlowDate(Date d=Date()) const
Definition: bond.cpp:283
Bond(Natural settlementDays, Calendar calendar, const Date &issueDate=Date(), const Leg &coupons=Leg())
constructor for amortizing or non-amortizing bonds.
Definition: bond.cpp:37
Real settlementValue() const
theoretical settlement value
Definition: bond.cpp:186
std::vector< Real > notionals_
Definition: bond.hpp:301
Real dirtyPrice() const
theoretical dirty price
Definition: bond.cpp:178
Natural settlementDays_
Definition: bond.hpp:298
Leg cashflows_
Definition: bond.hpp:302
const ext::shared_ptr< CashFlow > & redemption() const
Definition: bond.cpp:140
void setSingleRedemption(Real notional, Real redemption, const Date &date)
Definition: bond.cpp:342
Date previousCashFlowDate(Date d=Date()) const
Definition: bond.cpp:287
Date issueDate_
Definition: bond.hpp:305
Date maturityDate() const
Definition: bond.cpp:150
bool isTradable(Date d=Date()) const
Definition: bond.cpp:157
const Leg & redemptions() const
Definition: bond.hpp:348
void setupExpired() const override
Definition: bond.cpp:291
void fetchResults(const PricingEngine::results *) const override
Definition: bond.cpp:305
std::vector< Date > notionalSchedule_
Definition: bond.hpp:300
Date maturityDate_
Definition: bond.hpp:305
void calculateNotionalsFromCashflows()
Definition: bond.cpp:374
virtual Real notional(Date d=Date()) const
Definition: bond.cpp:112
Date settlementDate(Date d=Date()) const
Definition: bond.cpp:161
virtual Rate nextCouponRate(Date d=Date()) const
Definition: bond.cpp:275
calendar class
Definition: calendar.hpp:61
Date advance(const Date &, Integer n, TimeUnit unit, BusinessDayConvention convention=Following, bool endOfMonth=false) const
Definition: calendar.cpp:130
static Date maturityDate(const Leg &leg)
Definition: cashflows.cpp:52
static bool isExpired(const Leg &leg, bool includeSettlementDateFlows, Date settlementDate=Date())
Definition: cashflows.cpp:66
Concrete date class.
Definition: date.hpp:125
day counter class
Definition: daycounter.hpp:44
void calculate() const override
Definition: instrument.hpp:129
virtual void fetchResults(const PricingEngine::results *) const
Definition: instrument.hpp:155
virtual void setupExpired() const
Definition: instrument.hpp:140
void update() override
Definition: lazyobject.hpp:188
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
Bond redemption.
DateProxy & evaluationDate()
the date at which pricing is to be performed.
Definition: settings.hpp:147
static Settings & instance()
access to the unique instance
Definition: singleton.hpp:104
discounting bond engine
#define QL_ENSURE(condition, message)
throw an error if the given post-condition is not verified
Definition: errors.hpp:130
#define QL_REQUIRE(condition, message)
throw an error if the given pre-condition is not verified
Definition: errors.hpp:117
Date d
Coupon paying a variable index-based rate.
Frequency
Frequency of events.
Definition: frequency.hpp:37
QL_REAL Real
real number
Definition: types.hpp:50
unsigned QL_INTEGER Natural
positive integer
Definition: types.hpp:43
Real Rate
interest rates
Definition: types.hpp:70
std::size_t Size
size of a container
Definition: types.hpp:58
Definition: any.hpp:35
Compounding
Interest rate coumpounding rule.
Definition: compounding.hpp:32
bool close(const Quantity &m1, const Quantity &m2, Size n)
Definition: quantity.cpp:163
std::vector< ext::shared_ptr< CashFlow > > Leg
Sequence of cash-flows.
Definition: cashflow.hpp:78
STL namespace.
ext::shared_ptr< YieldTermStructure > r
Predetermined cash flow.
static Rate yield(const Bond &bond, Real price, const DayCounter &dayCounter, Compounding compounding, Frequency frequency, Date settlementDate=Date(), Real accuracy=1.0e-10, Size maxIterations=100, Rate guess=0.05, Bond::Price::Type priceType=Bond::Price::Clean)
static Rate nextCouponRate(const Bond &bond, Date settlementDate=Date())
static Date startDate(const Bond &bond)
static bool isTradable(const Bond &bond, Date settlementDate=Date())
static Date maturityDate(const Bond &bond)
static Date previousCashFlowDate(const Bond &bond, Date refDate=Date())
static Date nextCashFlowDate(const Bond &bond, Date refDate=Date())
static Real accruedAmount(const Bond &bond, Date settlementDate=Date())
static Real cleanPrice(const Bond &bond, const YieldTermStructure &discountCurve, Date settlementDate=Date())
static Rate previousCouponRate(const Bond &bond, Date settlementDate=Date())
compare two objects by date
Definition: comparison.hpp:130