QuantLib: a free/open-source library for quantitative finance
Fully annotated sources - version 1.32
Loading...
Searching...
No Matches
bond.cpp
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
26#include <ql/cashflows/cashflows.hpp>
27#include <ql/cashflows/floatingratecoupon.hpp>
28#include <ql/cashflows/simplecashflow.hpp>
29#include <ql/instruments/bond.hpp>
30#include <ql/math/solvers1d/brent.hpp>
31#include <ql/pricingengines/bond/bondfunctions.hpp>
32#include <ql/pricingengines/bond/discountingbondengine.hpp>
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()) {
46 QL_REQUIRE(issueDate_<cashflows_[0]->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()) {
80 QL_REQUIRE(issueDate_<cashflows_[0]->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();
188 QL_REQUIRE(settlementValue_ != Null<Real>(),
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 Real price = priceType == Bond::Price::Clean ? cleanPrice() : dirtyPrice();
210
211 return BondFunctions::yield(*this, price, dc, comp, freq,
213 accuracy, maxEvaluations,
214 guess, priceType);
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 Real currentNotional = notional(settlement);
248 if (currentNotional == 0.0)
249 return 0.0;
250
251 return BondFunctions::yield(*this, price, dc, comp, freq,
252 settlement, accuracy, maxEvaluations,
253 guess, priceType);
254 }
255
256 Real Bond::accruedAmount(Date settlement) const {
257 Real currentNotional = notional(settlement);
258 if (currentNotional == 0.0)
259 return 0.0;
260
261 return BondFunctions::accruedAmount(*this, settlement);
262 }
263
264 Rate Bond::nextCouponRate(Date settlement) const {
265 return BondFunctions::nextCouponRate(*this, settlement);
266 }
267
269 return BondFunctions::previousCouponRate(*this, settlement);
270 }
271
272 Date Bond::nextCashFlowDate(Date settlement) const {
273 return BondFunctions::nextCashFlowDate(*this, settlement);
274 }
275
277 return BondFunctions::previousCashFlowDate(*this, settlement);
278 }
279
280 void Bond::setupExpired() const {
282 settlementValue_ = 0.0;
283 }
284
286 auto* arguments = dynamic_cast<Bond::arguments*>(args);
287 QL_REQUIRE(arguments != nullptr, "wrong argument type");
288
292 }
293
295
297
298 const auto* results = dynamic_cast<const Bond::results*>(r);
299 QL_ENSURE(results != nullptr, "wrong result type");
300
302 }
303
304 void Bond::addRedemptionsToCashflows(const std::vector<Real>& redemptions) {
305 // First, we gather the notional information from the cashflows
307 // Then, we create the redemptions based on the notional
308 // information and we add them to the cashflows vector after
309 // the coupons.
310 redemptions_.clear();
311 for (Size i=1; i<notionalSchedule_.size(); ++i) {
312 Real R = i < redemptions.size() ? redemptions[i] :
313 !redemptions.empty() ? redemptions.back() :
314 100.0;
315 Real amount = (R/100.0)*(notionals_[i-1]-notionals_[i]);
316 ext::shared_ptr<CashFlow> payment;
317 if (i < notionalSchedule_.size()-1)
318 payment.reset(new AmortizingPayment(amount,
320 else
321 payment.reset(new Redemption(amount, notionalSchedule_[i]));
322 cashflows_.push_back(payment);
323 redemptions_.push_back(payment);
324 }
325 // stable_sort now moves the redemptions to the right places
326 // while ensuring that they follow coupons with the same date.
327 std::stable_sort(cashflows_.begin(), cashflows_.end(),
328 earlier_than<ext::shared_ptr<CashFlow> >());
329 }
330
332 Real redemption,
333 const Date& date) {
334
335 ext::shared_ptr<CashFlow> redemptionCashflow(
336 new Redemption(notional*redemption/100.0, date));
337 setSingleRedemption(notional, redemptionCashflow);
338 }
339
341 const ext::shared_ptr<CashFlow>& redemption) {
342 notionals_.resize(2);
343 notionalSchedule_.resize(2);
344 redemptions_.clear();
345
346 notionalSchedule_[0] = Date();
347 notionals_[0] = notional;
348
349 notionalSchedule_[1] = redemption->date();
350 notionals_[1] = 0.0;
351
352 cashflows_.push_back(redemption);
353 redemptions_.push_back(redemption);
354 }
355
357 for (auto& cashflow : cashflows_) {
358 cashflow->deepUpdate();
359 }
360 update();
361 }
362
364 notionalSchedule_.clear();
365 notionals_.clear();
366
367 Date lastPaymentDate = Date();
368 notionalSchedule_.emplace_back();
369 for (auto& cashflow : cashflows_) {
370 ext::shared_ptr<Coupon> coupon = ext::dynamic_pointer_cast<Coupon>(cashflow);
371 if (!coupon)
372 continue;
373
374 Real notional = coupon->nominal();
375 // we add the notional only if it is the first one...
376 if (notionals_.empty()) {
377 notionals_.push_back(coupon->nominal());
378 lastPaymentDate = coupon->date();
379 } else if (!close(notional, notionals_.back())) {
380 // ...or if it has changed.
381 notionals_.push_back(coupon->nominal());
382 // in this case, we also add the last valid date for
383 // the previous one...
384 notionalSchedule_.push_back(lastPaymentDate);
385 // ...and store the candidate for this one.
386 lastPaymentDate = coupon->date();
387 } else {
388 // otherwise, we just extend the valid range of dates
389 // for the current notional.
390 lastPaymentDate = coupon->date();
391 }
392 }
393 QL_REQUIRE(!notionals_.empty(), "no coupons provided");
394 notionals_.push_back(0.0);
395 notionalSchedule_.push_back(lastPaymentDate);
396 }
397
398
400 QL_REQUIRE(settlementDate != Date(), "no settlement date provided");
401 QL_REQUIRE(!cashflows.empty(), "no cash flow provided");
402 for (const auto & cf: cashflows)
403 QL_REQUIRE(cf, "null cash flow provided");
404 }
405
406}
void validate() const override
Definition: bond.cpp:399
Calendar calendar_
Definition: bond.hpp:285
Real cleanPrice() const
theoretical clean price
Definition: bond.cpp:174
Leg redemptions_
Definition: bond.hpp:289
Real settlementValue_
Definition: bond.hpp:292
void setupArguments(PricingEngine::arguments *) const override
Definition: bond.cpp:285
Rate previousCouponRate(Date d=Date()) const
Previous coupon already paid at a given date.
Definition: bond.cpp:268
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:304
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:256
void deepUpdate() override
Definition: bond.cpp:356
Date startDate() const
Definition: bond.cpp:146
const Leg & cashflows() const
Definition: bond.hpp:330
Date nextCashFlowDate(Date d=Date()) const
Definition: bond.cpp:272
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:287
Real dirtyPrice() const
theoretical dirty price
Definition: bond.cpp:178
Natural settlementDays_
Definition: bond.hpp:284
Leg cashflows_
Definition: bond.hpp:288
const ext::shared_ptr< CashFlow > & redemption() const
Definition: bond.cpp:140
void setSingleRedemption(Real notional, Real redemption, const Date &date)
Definition: bond.cpp:331
Date previousCashFlowDate(Date d=Date()) const
Definition: bond.cpp:276
Date issueDate_
Definition: bond.hpp:291
Date maturityDate() const
Definition: bond.cpp:150
bool isTradable(Date d=Date()) const
Definition: bond.cpp:157
const Leg & redemptions() const
Definition: bond.hpp:334
void setupExpired() const override
Definition: bond.cpp:280
void fetchResults(const PricingEngine::results *) const override
Definition: bond.cpp:294
std::vector< Date > notionalSchedule_
Definition: bond.hpp:286
Date maturityDate_
Definition: bond.hpp:291
void calculateNotionalsFromCashflows()
Definition: bond.cpp:363
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:264
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
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.
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