Coverage Report

Created: 2026-01-25 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/instruments/bonds/amortizingfixedratebond.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2008 Simon Ibbotson
5
6
 This file is part of QuantLib, a free-software/open-source library
7
 for financial quantitative analysts and developers - http://quantlib.org/
8
9
 QuantLib is free software: you can redistribute it and/or modify it
10
 under the terms of the QuantLib license.  You should have received a
11
 copy of the license along with this program; if not, please email
12
 <quantlib-dev@lists.sf.net>. The license is also available online at
13
 <https://www.quantlib.org/license.shtml>.
14
15
 This program is distributed in the hope that it will be useful, but WITHOUT
16
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17
 FOR A PARTICULAR PURPOSE.  See the license for more details.
18
*/
19
20
#include <ql/instruments/bonds/amortizingfixedratebond.hpp>
21
#include <ql/cashflows/cashflowvectors.hpp>
22
#include <ql/cashflows/simplecashflow.hpp>
23
#include <ql/time/schedule.hpp>
24
25
namespace QuantLib {
26
27
    AmortizingFixedRateBond::AmortizingFixedRateBond(
28
                                      Natural settlementDays,
29
                                      const std::vector<Real>& notionals,
30
                                      Schedule schedule,
31
                                      const std::vector<Rate>& coupons,
32
                                      const DayCounter& accrualDayCounter,
33
                                      BusinessDayConvention paymentConvention,
34
                                      const Date& issueDate,
35
                                      const Period& exCouponPeriod,
36
                                      const Calendar& exCouponCalendar,
37
                                      const BusinessDayConvention exCouponConvention,
38
                                      bool exCouponEndOfMonth,
39
                                      const std::vector<Real>& redemptions,
40
                                      Integer paymentLag)
41
69.9k
    : Bond(settlementDays, schedule.calendar(), issueDate),
42
69.9k
      frequency_(schedule.tenor().frequency()),
43
69.9k
      dayCounter_(accrualDayCounter) {
44
45
69.9k
        maturityDate_ = schedule.endDate();
46
47
69.9k
        cashflows_ = FixedRateLeg(std::move(schedule))
48
69.9k
            .withNotionals(notionals)
49
69.9k
            .withCouponRates(coupons, accrualDayCounter)
50
69.9k
            .withPaymentAdjustment(paymentConvention)
51
69.9k
            .withExCouponPeriod(exCouponPeriod,
52
69.9k
                                exCouponCalendar,
53
69.9k
                                exCouponConvention,
54
69.9k
                                exCouponEndOfMonth)
55
69.9k
            .withPaymentLag(paymentLag);
56
57
69.9k
        addRedemptionsToCashflows(redemptions);
58
59
69.9k
        QL_ENSURE(!cashflows().empty(), "bond with no cashflows!");
60
69.9k
    }
Unexecuted instantiation: QuantLib::AmortizingFixedRateBond::AmortizingFixedRateBond(unsigned int, std::__1::vector<double, std::__1::allocator<double> > const&, QuantLib::Schedule, std::__1::vector<double, std::__1::allocator<double> > const&, QuantLib::DayCounter const&, QuantLib::BusinessDayConvention, QuantLib::Date const&, QuantLib::Period const&, QuantLib::Calendar const&, QuantLib::BusinessDayConvention, bool, std::__1::vector<double, std::__1::allocator<double> > const&, int)
QuantLib::AmortizingFixedRateBond::AmortizingFixedRateBond(unsigned int, std::__1::vector<double, std::__1::allocator<double> > const&, QuantLib::Schedule, std::__1::vector<double, std::__1::allocator<double> > const&, QuantLib::DayCounter const&, QuantLib::BusinessDayConvention, QuantLib::Date const&, QuantLib::Period const&, QuantLib::Calendar const&, QuantLib::BusinessDayConvention, bool, std::__1::vector<double, std::__1::allocator<double> > const&, int)
Line
Count
Source
41
69.9k
    : Bond(settlementDays, schedule.calendar(), issueDate),
42
69.9k
      frequency_(schedule.tenor().frequency()),
43
69.9k
      dayCounter_(accrualDayCounter) {
44
45
69.9k
        maturityDate_ = schedule.endDate();
46
47
69.9k
        cashflows_ = FixedRateLeg(std::move(schedule))
48
69.9k
            .withNotionals(notionals)
49
69.9k
            .withCouponRates(coupons, accrualDayCounter)
50
69.9k
            .withPaymentAdjustment(paymentConvention)
51
69.9k
            .withExCouponPeriod(exCouponPeriod,
52
69.9k
                                exCouponCalendar,
53
69.9k
                                exCouponConvention,
54
69.9k
                                exCouponEndOfMonth)
55
69.9k
            .withPaymentLag(paymentLag);
56
57
69.9k
        addRedemptionsToCashflows(redemptions);
58
59
        QL_ENSURE(!cashflows().empty(), "bond with no cashflows!");
60
69.9k
    }
61
62
63
    Schedule sinkingSchedule(const Date& startDate,
64
                             const Period& bondLength,
65
                             const Frequency& frequency,
66
69.9k
                             const Calendar& paymentCalendar) {
67
69.9k
        Date maturityDate = startDate + bondLength;
68
69.9k
        Schedule retVal(startDate, maturityDate, Period(frequency),
69
69.9k
                        paymentCalendar, Unadjusted, Unadjusted,
70
69.9k
                        DateGeneration::Backward, false);
71
69.9k
        return retVal;
72
69.9k
    }
73
74
    namespace  {
75
76
139k
        std::pair<Integer,Integer> daysMinMax(const Period& p) {
77
139k
            switch (p.units()) {
78
0
              case Days:
79
0
                return std::make_pair(p.length(), p.length());
80
0
              case Weeks:
81
0
                return std::make_pair(7*p.length(), 7*p.length());
82
69.9k
              case Months:
83
69.9k
                return std::make_pair(28*p.length(), 31*p.length());
84
69.9k
              case Years:
85
69.9k
                return std::make_pair(365*p.length(), 366*p.length());
86
0
              default:
87
0
                QL_FAIL("unknown time unit (" << Integer(p.units()) << ")");
88
139k
            }
89
139k
        }
90
91
        bool isSubPeriod(const Period& subPeriod,
92
                         const Period& superPeriod,
93
69.9k
                         Integer& numSubPeriods) {
94
95
69.9k
            std::pair<Integer, Integer> superDays(daysMinMax(superPeriod));
96
69.9k
            std::pair<Integer, Integer> subDays(daysMinMax(subPeriod));
97
98
            //obtain the approximate time ratio
99
69.9k
            Real minPeriodRatio =
100
69.9k
                ((Real)superDays.first)/((Real)subDays.second);
101
69.9k
            Real maxPeriodRatio =
102
69.9k
                ((Real)superDays.second)/((Real)subDays.first);
103
69.9k
            auto lowRatio = static_cast<Integer>(std::floor(minPeriodRatio));
104
69.9k
            auto highRatio = static_cast<Integer>(std::ceil(maxPeriodRatio));
105
106
69.9k
            try {
107
559k
                for(Integer i=lowRatio; i <= highRatio; ++i) {
108
559k
                    Period testPeriod = subPeriod * i;
109
559k
                    if(testPeriod == superPeriod) {
110
69.9k
                        numSubPeriods = i;
111
69.9k
                        return true;
112
69.9k
                    }
113
559k
                }
114
69.9k
            } catch(Error&) {
115
0
                return false;
116
0
            }
117
118
0
            return false;
119
69.9k
        }
120
121
    }
122
123
    std::vector<Real> sinkingNotionals(const Period& bondLength,
124
                                       const Frequency& sinkingFrequency,
125
                                       Rate couponRate,
126
69.9k
                                       Real initialNotional) {
127
69.9k
        Integer nPeriods;
128
69.9k
        QL_REQUIRE(isSubPeriod(Period(sinkingFrequency), bondLength, nPeriods),
129
69.9k
                   "Bond frequency is incompatible with the maturity tenor");
130
131
69.9k
        std::vector<Real> notionals(nPeriods+1);
132
69.9k
        notionals.front() = initialNotional;
133
69.9k
        Real coupon = couponRate / static_cast<Real>(sinkingFrequency);
134
69.9k
        Real compoundedInterest = 1.0;
135
69.9k
        Real totalValue = std::pow(1.0+coupon, nPeriods);
136
25.1M
        for (Size i = 0; i < (Size)nPeriods-1; ++i) {
137
25.0M
            compoundedInterest *= (1.0 + coupon);
138
25.0M
            Real currentNotional = 0.0;
139
25.0M
            if(coupon < 1.0e-12) {
140
13.9M
                currentNotional = initialNotional*(1.0 - (i+1.0)/nPeriods);
141
13.9M
            } else {
142
11.1M
                currentNotional =
143
11.1M
                    initialNotional*(compoundedInterest - (compoundedInterest-1.0)/(1.0 - 1.0/totalValue));
144
11.1M
            }
145
25.0M
            notionals[i+1] = currentNotional;
146
25.0M
        }
147
69.9k
        notionals.back() = 0.0;
148
149
69.9k
        return notionals;
150
69.9k
    }
151
152
}