Coverage Report

Created: 2025-11-04 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/pricingengines/bond/bondfunctions.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2009 Nathan Abbott
5
 Copyright (C) 2007, 2008, 2009, 2010 Ferdinando Ametrano
6
 Copyright (C) 2007 Chiara Fornarola
7
 Copyright (C) 2008 Simon Ibbotson
8
 Copyright (C) 2004 M-Dimension Consulting Inc.
9
 Copyright (C) 2005, 2006, 2007, 2008, 2009 StatPro Italia srl
10
 Copyright (C) 2004 Jeff Yu
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
 <https://www.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/math/solvers1d/newtonsafe.hpp>
27
#include <ql/pricingengines/bond/bondfunctions.hpp>
28
29
namespace QuantLib {
30
31
0
    Date BondFunctions::startDate(const Bond& bond) {
32
0
        return CashFlows::startDate(bond.cashflows());
33
0
    }
34
35
0
    Date BondFunctions::maturityDate(const Bond& bond) {
36
0
        return CashFlows::maturityDate(bond.cashflows());
37
0
    }
38
39
    bool BondFunctions::isTradable(const Bond& bond,
40
0
                                   Date settlement) {
41
0
        if (settlement == Date())
42
0
            settlement = bond.settlementDate();
43
44
0
        return bond.notional(settlement)!=0.0;
45
0
    }
46
47
    Leg::const_reverse_iterator
48
    BondFunctions::previousCashFlow(const Bond& bond,
49
0
                                    Date settlement) {
50
0
        if (settlement == Date())
51
0
            settlement = bond.settlementDate();
52
53
0
        return CashFlows::previousCashFlow(bond.cashflows(),
54
0
                                           false, settlement);
55
0
    }
56
57
    Leg::const_iterator BondFunctions::nextCashFlow(const Bond& bond,
58
0
                                                    Date settlement) {
59
0
        if (settlement == Date())
60
0
            settlement = bond.settlementDate();
61
62
0
        return CashFlows::nextCashFlow(bond.cashflows(),
63
0
                                       false, settlement);
64
0
    }
65
66
    Date BondFunctions::previousCashFlowDate(const Bond& bond,
67
0
                                             Date settlement) {
68
0
        if (settlement == Date())
69
0
            settlement = bond.settlementDate();
70
71
0
        return CashFlows::previousCashFlowDate(bond.cashflows(),
72
0
                                               false, settlement);
73
0
    }
74
75
    Date BondFunctions::nextCashFlowDate(const Bond& bond,
76
0
                                         Date settlement) {
77
0
        if (settlement == Date())
78
0
            settlement = bond.settlementDate();
79
80
0
        return CashFlows::nextCashFlowDate(bond.cashflows(),
81
0
                                           false, settlement);
82
0
    }
83
84
    Real BondFunctions::previousCashFlowAmount(const Bond& bond,
85
0
                                               Date settlement) {
86
0
        if (settlement == Date())
87
0
            settlement = bond.settlementDate();
88
89
0
        return CashFlows::previousCashFlowAmount(bond.cashflows(),
90
0
                                                 false, settlement);
91
0
    }
92
93
    Real BondFunctions::nextCashFlowAmount(const Bond& bond,
94
0
                                           Date settlement) {
95
0
        if (settlement == Date())
96
0
            settlement = bond.settlementDate();
97
98
0
        return CashFlows::nextCashFlowAmount(bond.cashflows(),
99
0
                                             false, settlement);
100
0
    }
101
102
    Rate BondFunctions::previousCouponRate(const Bond& bond,
103
0
                                           Date settlement) {
104
0
        if (settlement == Date())
105
0
            settlement = bond.settlementDate();
106
107
0
        return CashFlows::previousCouponRate(bond.cashflows(),
108
0
                                             false, settlement);
109
0
    }
110
111
    Rate BondFunctions::nextCouponRate(const Bond& bond,
112
0
                                       Date settlement) {
113
0
        if (settlement == Date())
114
0
            settlement = bond.settlementDate();
115
116
0
        return CashFlows::nextCouponRate(bond.cashflows(),
117
0
                                         false, settlement);
118
0
    }
119
120
    Date BondFunctions::accrualStartDate(const Bond& bond,
121
0
                                         Date settlement) {
122
0
        if (settlement == Date())
123
0
            settlement = bond.settlementDate();
124
125
0
        QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
126
0
                   "non tradable at " << settlement <<
127
0
                   " (maturity being " << bond.maturityDate() << ")");
128
129
0
        return CashFlows::accrualStartDate(bond.cashflows(),
130
0
                                           false, settlement);
131
0
    }
132
133
    Date BondFunctions::accrualEndDate(const Bond& bond,
134
0
                                       Date settlement) {
135
0
        if (settlement == Date())
136
0
            settlement = bond.settlementDate();
137
138
0
        QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
139
0
                   "non tradable at " << settlement <<
140
0
                   " (maturity being " << bond.maturityDate() << ")");
141
142
0
        return CashFlows::accrualEndDate(bond.cashflows(),
143
0
                                         false, settlement);
144
0
    }
145
146
    Date BondFunctions::referencePeriodStart(const Bond& bond,
147
0
                                             Date settlement) {
148
0
        if (settlement == Date())
149
0
            settlement = bond.settlementDate();
150
151
0
        QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
152
0
                   "non tradable at " << settlement <<
153
0
                   " (maturity being " << bond.maturityDate() << ")");
154
155
0
        return CashFlows::referencePeriodStart(bond.cashflows(),
156
0
                                               false, settlement);
157
0
    }
158
159
    Date BondFunctions::referencePeriodEnd(const Bond& bond,
160
0
                                           Date settlement) {
161
0
        if (settlement == Date())
162
0
            settlement = bond.settlementDate();
163
164
0
        QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
165
0
                   "non tradable at " << settlement <<
166
0
                   " (maturity being " << bond.maturityDate() << ")");
167
168
0
        return CashFlows::referencePeriodEnd(bond.cashflows(),
169
0
                                             false, settlement);
170
0
    }
171
172
    Time BondFunctions::accrualPeriod(const Bond& bond,
173
0
                                      Date settlement) {
174
0
        if (settlement == Date())
175
0
            settlement = bond.settlementDate();
176
177
0
        QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
178
0
                   "non tradable at " << settlement <<
179
0
                   " (maturity being " << bond.maturityDate() << ")");
180
181
0
        return CashFlows::accrualPeriod(bond.cashflows(),
182
0
                                        false, settlement);
183
0
    }
184
185
    Date::serial_type BondFunctions::accrualDays(const Bond& bond,
186
0
                                                 Date settlement) {
187
0
        if (settlement == Date())
188
0
            settlement = bond.settlementDate();
189
190
0
        QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
191
0
                   "non tradable at " << settlement <<
192
0
                   " (maturity being " << bond.maturityDate() << ")");
193
194
0
        return CashFlows::accrualDays(bond.cashflows(),
195
0
                                      false, settlement);
196
0
    }
197
198
    Time BondFunctions::accruedPeriod(const Bond& bond,
199
0
                                      Date settlement) {
200
0
        if (settlement == Date())
201
0
            settlement = bond.settlementDate();
202
203
0
        QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
204
0
                   "non tradable at " << settlement <<
205
0
                   " (maturity being " << bond.maturityDate() << ")");
206
207
0
        return CashFlows::accruedPeriod(bond.cashflows(),
208
0
                                        false, settlement);
209
0
    }
210
211
    Date::serial_type BondFunctions::accruedDays(const Bond& bond,
212
0
                                                 Date settlement) {
213
0
        if (settlement == Date())
214
0
            settlement = bond.settlementDate();
215
216
0
        QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
217
0
                   "non tradable at " << settlement <<
218
0
                   " (maturity being " << bond.maturityDate() << ")");
219
220
0
        return CashFlows::accruedDays(bond.cashflows(),
221
0
                                      false, settlement);
222
0
    }
223
224
    Real BondFunctions::accruedAmount(const Bond& bond,
225
0
                                      Date settlement) {
226
0
        if (settlement == Date())
227
0
            settlement = bond.settlementDate();
228
229
0
        if (!BondFunctions::isTradable(bond, settlement))
230
0
            return 0.0;
231
232
0
        return CashFlows::accruedAmount(bond.cashflows(),
233
0
                                        false, settlement) *
234
0
            100.0 / bond.notional(settlement);
235
0
    }
236
237
238
239
    Real BondFunctions::cleanPrice(const Bond& bond,
240
                                   const YieldTermStructure& discountCurve,
241
0
                                   Date settlement) {
242
0
        if (settlement == Date())
243
0
            settlement = bond.settlementDate();
244
245
0
        return dirtyPrice(bond, discountCurve, settlement) - bond.accruedAmount(settlement);
246
0
    }
247
248
    Real BondFunctions::dirtyPrice(const Bond& bond,
249
                                   const YieldTermStructure& discountCurve,
250
0
                                   Date settlement) {
251
0
        if (settlement == Date())
252
0
            settlement = bond.settlementDate();
253
254
0
        QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
255
0
                   "non tradable at " << settlement <<
256
0
                   " settlement date (maturity being " <<
257
0
                   bond.maturityDate() << ")");
258
259
0
        Real dirtyPrice = CashFlows::npv(bond.cashflows(), discountCurve,
260
0
                                         false, settlement) *
261
0
            100.0 / bond.notional(settlement);
262
0
        return dirtyPrice;
263
0
    }
264
265
    Real BondFunctions::bps(const Bond& bond,
266
                            const YieldTermStructure& discountCurve,
267
0
                            Date settlement) {
268
0
        if (settlement == Date())
269
0
            settlement = bond.settlementDate();
270
271
0
        QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
272
0
                   "non tradable at " << settlement <<
273
0
                   " (maturity being " << bond.maturityDate() << ")");
274
275
0
        return CashFlows::bps(bond.cashflows(), discountCurve,
276
0
                              false, settlement) *
277
0
            100.0 / bond.notional(settlement);
278
0
    }
279
280
    Rate BondFunctions::atmRate(const Bond& bond,
281
                                const YieldTermStructure& discountCurve,
282
                                Date settlement,
283
0
                                const Bond::Price price) {
284
0
        if (settlement == Date())
285
0
            settlement = bond.settlementDate();
286
287
0
        QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
288
0
                   "non tradable at " << settlement <<
289
0
                   " (maturity being " << bond.maturityDate() << ")");
290
291
0
        Real npv = Null<Real>();
292
0
        if (price.isValid()) {
293
0
            Real dirtyPrice =
294
0
                price.amount() +
295
0
                (price.type() == Bond::Price::Clean ? bond.accruedAmount(settlement) : 0);
296
297
0
            Real currentNotional = bond.notional(settlement);
298
0
            npv = dirtyPrice / 100.0 * currentNotional;
299
300
0
        }
301
0
        return CashFlows::atmRate(bond.cashflows(), discountCurve,
302
0
                                  false, settlement, settlement,
303
0
                                  npv);
304
0
    }
305
306
    Real BondFunctions::cleanPrice(const Bond& bond,
307
                                   const InterestRate& yield,
308
0
                                   Date settlement) {
309
0
        return dirtyPrice(bond, yield, settlement) - bond.accruedAmount(settlement);
310
0
    }
311
312
    Real BondFunctions::cleanPrice(const Bond& bond,
313
                                   Rate yield,
314
                                   const DayCounter& dayCounter,
315
                                   Compounding compounding,
316
                                   Frequency frequency,
317
0
                                   Date settlement) {
318
0
        InterestRate y(yield, dayCounter, compounding, frequency);
319
0
        return cleanPrice(bond, y, settlement);
320
0
    }
321
322
    Real BondFunctions::dirtyPrice(const Bond& bond,
323
                                   const InterestRate& yield,
324
0
                                   Date settlement) {
325
0
        if (settlement == Date())
326
0
            settlement = bond.settlementDate();
327
328
0
        QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
329
0
                   "non tradable at " << settlement <<
330
0
                   " (maturity being " << bond.maturityDate() << ")");
331
332
0
        Real dirtyPrice = CashFlows::npv(bond.cashflows(), yield,
333
0
                                         false, settlement) *
334
0
            100.0 / bond.notional(settlement);
335
0
        return dirtyPrice;
336
0
    }
337
338
    Real BondFunctions::dirtyPrice(const Bond& bond,
339
                                   Rate yield,
340
                                   const DayCounter& dayCounter,
341
                                   Compounding compounding,
342
                                   Frequency frequency,
343
0
                                   Date settlement) {
344
0
        InterestRate y(yield, dayCounter, compounding, frequency);
345
0
        return dirtyPrice(bond, y, settlement);
346
0
    }
347
348
    Real BondFunctions::bps(const Bond& bond,
349
                            const InterestRate& yield,
350
0
                            Date settlement) {
351
0
        if (settlement == Date())
352
0
            settlement = bond.settlementDate();
353
354
0
        QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
355
0
                   "non tradable at " << settlement <<
356
0
                   " (maturity being " << bond.maturityDate() << ")");
357
358
0
        return CashFlows::bps(bond.cashflows(), yield,
359
0
                              false, settlement) *
360
0
            100.0 / bond.notional(settlement);
361
0
    }
362
363
    Real BondFunctions::bps(const Bond& bond,
364
                            Rate yield,
365
                            const DayCounter& dayCounter,
366
                            Compounding compounding,
367
                            Frequency frequency,
368
0
                            Date settlement) {
369
0
        InterestRate y(yield, dayCounter, compounding, frequency);
370
0
        return bps(bond, y, settlement);
371
0
    }
372
373
    Rate BondFunctions::yield(const Bond& bond,
374
                              Bond::Price price,
375
                              const DayCounter& dayCounter,
376
                              Compounding compounding,
377
                              Frequency frequency,
378
                              Date settlement,
379
                              Real accuracy,
380
                              Size maxIterations,
381
0
                              Rate guess) {
382
0
        NewtonSafe solver;
383
0
        solver.setMaxEvaluations(maxIterations);
384
0
        return yield<NewtonSafe>(solver, bond, price, dayCounter,
385
0
                                 compounding, frequency, settlement,
386
0
                                 accuracy, guess);
387
0
    }
388
389
    Time BondFunctions::duration(const Bond& bond,
390
                                 const InterestRate& yield,
391
                                 Duration::Type type,
392
0
                                 Date settlement) {
393
0
        if (settlement == Date())
394
0
            settlement = bond.settlementDate();
395
396
0
        QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
397
0
                   "non tradable at " << settlement <<
398
0
                   " (maturity being " << bond.maturityDate() << ")");
399
400
0
        return CashFlows::duration(bond.cashflows(), yield,
401
0
                                   type,
402
0
                                   false, settlement);
403
0
    }
404
405
    Time BondFunctions::duration(const Bond& bond,
406
                                 Rate yield,
407
                                 const DayCounter& dayCounter,
408
                                 Compounding compounding,
409
                                 Frequency frequency,
410
                                 Duration::Type type,
411
0
                                 Date settlement) {
412
0
        InterestRate y(yield, dayCounter, compounding, frequency);
413
0
        return duration(bond, y, type, settlement);
414
0
    }
415
416
    Real BondFunctions::convexity(const Bond& bond,
417
                                  const InterestRate& yield,
418
0
                                  Date settlement) {
419
0
        if (settlement == Date())
420
0
            settlement = bond.settlementDate();
421
422
0
        QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
423
0
                   "non tradable at " << settlement <<
424
0
                   " (maturity being " << bond.maturityDate() << ")");
425
426
0
        return CashFlows::convexity(bond.cashflows(), yield,
427
0
                                    false, settlement);
428
0
    }
429
430
    Real BondFunctions::convexity(const Bond& bond,
431
                                  Rate yield,
432
                                  const DayCounter& dayCounter,
433
                                  Compounding compounding,
434
                                  Frequency frequency,
435
0
                                  Date settlement) {
436
0
        InterestRate y(yield, dayCounter, compounding, frequency);
437
0
        return convexity(bond, y, settlement);
438
0
    }
439
440
    Real BondFunctions::basisPointValue(const Bond& bond,
441
                                        const InterestRate& yield,
442
0
                                        Date settlement) {
443
0
        if (settlement == Date())
444
0
            settlement = bond.settlementDate();
445
446
0
        QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
447
0
                   "non tradable at " << settlement <<
448
0
                   " (maturity being " << bond.maturityDate() << ")");
449
450
0
        return CashFlows::basisPointValue(bond.cashflows(), yield,
451
0
                                          false, settlement);
452
0
    }
453
454
    Real BondFunctions::basisPointValue(const Bond& bond,
455
                              Rate yield,
456
                              const DayCounter& dayCounter,
457
                              Compounding compounding,
458
                              Frequency frequency,
459
0
                                        Date settlement) {
460
0
        InterestRate y(yield, dayCounter, compounding, frequency);
461
0
        return basisPointValue(bond, y, settlement);
462
0
    }
463
464
    Real BondFunctions::yieldValueBasisPoint(const Bond& bond,
465
                                             const InterestRate& yield,
466
0
                                             Date settlement) {
467
0
        if (settlement == Date())
468
0
            settlement = bond.settlementDate();
469
470
0
        QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
471
0
                   "non tradable at " << settlement <<
472
0
                   " (maturity being " << bond.maturityDate() << ")");
473
474
0
        return CashFlows::yieldValueBasisPoint(bond.cashflows(), yield,
475
0
                                               false, settlement);
476
0
    }
477
478
    Real BondFunctions::yieldValueBasisPoint(const Bond& bond,
479
                                             Rate yield,
480
                                             const DayCounter& dayCounter,
481
                                             Compounding compounding,
482
                                             Frequency frequency,
483
0
                                             Date settlement) {
484
0
        InterestRate y(yield, dayCounter, compounding, frequency);
485
0
        return yieldValueBasisPoint(bond, y, settlement);
486
0
    }
487
488
    Real BondFunctions::cleanPrice(const Bond& bond,
489
                                   const ext::shared_ptr<YieldTermStructure>& d,
490
                                   Spread zSpread,
491
                                   const DayCounter& dc,
492
                                   Compounding comp,
493
                                   Frequency freq,
494
0
                                   Date settlement) {
495
0
        if (settlement == Date())
496
0
            settlement = bond.settlementDate();
497
498
0
        return dirtyPrice(bond, d, zSpread, dc, comp, freq, settlement) - bond.accruedAmount(settlement);
499
0
    }
500
501
    Real BondFunctions::dirtyPrice(const Bond& bond,
502
                                   const ext::shared_ptr<YieldTermStructure>& d,
503
                                   Spread zSpread,
504
                                   const DayCounter& dc,
505
                                   Compounding comp,
506
                                   Frequency freq,
507
0
                                   Date settlement) {
508
0
        if (settlement == Date())
509
0
            settlement = bond.settlementDate();
510
511
0
        QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
512
0
                   "non tradable at " << settlement <<
513
0
                   " (maturity being " << bond.maturityDate() << ")");
514
515
0
        Real dirtyPrice = CashFlows::npv(bond.cashflows(), d,
516
0
                                         zSpread, dc, comp, freq,
517
0
                                         false, settlement) *
518
0
            100.0 / bond.notional(settlement);
519
0
        return dirtyPrice;
520
0
    }
521
522
    Spread BondFunctions::zSpread(const Bond& bond,
523
                                  Bond::Price price,
524
                                  const ext::shared_ptr<YieldTermStructure>& d,
525
                                  const DayCounter& dayCounter,
526
                                  Compounding compounding,
527
                                  Frequency frequency,
528
                                  Date settlement,
529
                                  Real accuracy,
530
                                  Size maxIterations,
531
0
                                  Rate guess) {
532
0
        if (settlement == Date())
533
0
            settlement = bond.settlementDate();
534
535
0
        QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
536
0
                   "non tradable at " << settlement <<
537
0
                   " (maturity being " << bond.maturityDate() << ")");
538
539
0
        Real dirtyPrice =
540
0
            price.amount() +
541
0
            (price.type() == Bond::Price::Clean ? bond.accruedAmount(settlement) : 0);
542
543
0
        dirtyPrice /= 100.0 / bond.notional(settlement);
544
545
0
        return CashFlows::zSpread(bond.cashflows(),
546
0
                                  dirtyPrice,
547
0
                                  d,
548
0
                                  dayCounter, compounding, frequency,
549
0
                                  false, settlement, settlement,
550
0
                                  accuracy, maxIterations, guess);
551
0
    }
552
}