Coverage Report

Created: 2025-08-11 06:28

/src/quantlib/ql/cashflows/averagebmacoupon.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2007 Roland Lichters
5
 Copyright (C) 2007 StatPro Italia srl
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
21
#include <ql/cashflows/averagebmacoupon.hpp>
22
#include <ql/cashflows/couponpricer.hpp>
23
#include <ql/utilities/vectors.hpp>
24
#include <utility>
25
26
namespace QuantLib {
27
28
    namespace {
29
30
        Integer bmaCutoffDays = 0; // to be verified
31
32
        class AverageBMACouponPricer : public FloatingRateCouponPricer {
33
          public:
34
0
            void initialize(const FloatingRateCoupon& coupon) override {
35
0
                coupon_ = dynamic_cast<const AverageBMACoupon*>(&coupon);
36
0
                QL_ENSURE(coupon_, "wrong coupon type");
37
0
            }
38
0
            Rate swapletRate() const override {
39
0
                const std::vector<Date>& fixingDates = coupon_->fixingDates();
40
0
                const ext::shared_ptr<InterestRateIndex>& index =
41
0
                    coupon_->index();
42
43
0
                Natural cutoffDays = 0; // to be verified
44
0
                Date startDate = coupon_->accrualStartDate() - cutoffDays,
45
0
                     endDate = coupon_->accrualEndDate() - cutoffDays,
46
0
                     d1 = startDate,
47
0
                     d2 = startDate;
48
49
0
                QL_REQUIRE(!fixingDates.empty(), "fixing date list empty");
50
0
                QL_REQUIRE (index->valueDate(fixingDates.front()) <= startDate,
51
0
                            "first fixing date valid after period start");
52
0
                QL_REQUIRE (index->valueDate(fixingDates.back()) >= endDate,
53
0
                            "last fixing date valid before period end");
54
55
0
                Rate avgBMA = 0.0;
56
0
                Integer days = 0;
57
0
                for (Size i=0; i<fixingDates.size() - 1; ++i) {
58
0
                    Date valueDate = index->valueDate(fixingDates[i]);
59
0
                    Date nextValueDate = index->valueDate(fixingDates[i+1]);
60
61
0
                    if (fixingDates[i] >= endDate || valueDate >= endDate)
62
0
                        break;
63
0
                    if (fixingDates[i+1] < startDate
64
0
                        || nextValueDate <= startDate)
65
0
                        continue;
66
67
0
                    d2 = std::min(nextValueDate, endDate);
68
69
0
                    avgBMA += index->fixing(fixingDates[i]) * (d2 - d1);
70
71
0
                    days += d2 - d1;
72
0
                    d1 = d2;
73
0
                }
74
0
                avgBMA /= (endDate - startDate);
75
76
0
                QL_ENSURE(days == endDate - startDate,
77
0
                          "averaging days " << days << " differ from "
78
0
                          "interest days " << (endDate - startDate));
79
80
0
                return coupon_->gearing()*avgBMA + coupon_->spread();
81
0
            }
82
83
0
            Real swapletPrice() const override { QL_FAIL("not available"); }
84
0
            Real capletPrice(Rate) const override { QL_FAIL("not available"); }
85
0
            Rate capletRate(Rate) const override { QL_FAIL("not available"); }
86
0
            Real floorletPrice(Rate) const override { QL_FAIL("not available"); }
87
0
            Rate floorletRate(Rate) const override { QL_FAIL("not available"); }
88
89
          private:
90
            const AverageBMACoupon* coupon_;
91
        };
92
93
    }
94
95
    namespace {
96
0
    void adjustToPreviousValidFixingDate(Date& d, const ext::shared_ptr<BMAIndex>& index) {
97
0
        while (!index->isValidFixingDate(d) && d > Date::minDate())
98
0
            d--;
99
0
    }
100
    } // namespace
101
102
    AverageBMACoupon::AverageBMACoupon(const Date& paymentDate,
103
                                       Real nominal,
104
                                       const Date& startDate,
105
                                       const Date& endDate,
106
                                       const ext::shared_ptr<BMAIndex>& index,
107
                                       Real gearing, Spread spread,
108
                                       const Date& refPeriodStart,
109
                                       const Date& refPeriodEnd,
110
                                       const DayCounter& dayCounter)
111
0
    : FloatingRateCoupon(paymentDate, nominal, startDate, endDate,
112
0
                         index->fixingDays(), index, gearing, spread,
113
0
                         refPeriodStart, refPeriodEnd, dayCounter, false)
114
0
    {
115
0
        Calendar cal = index->fixingCalendar();
116
0
        auto fixingDays = Integer(index->fixingDays());
117
0
        fixingDays += bmaCutoffDays;
118
0
        Date fixingStart = cal.advance(startDate, -fixingDays*Days, Preceding);
119
120
        // make sure that the value date associated to fixingStart is <= startDate
121
0
        adjustToPreviousValidFixingDate(fixingStart, index);
122
0
        while (index->valueDate(fixingStart) > startDate && fixingStart > Date::minDate()) {
123
0
            adjustToPreviousValidFixingDate(--fixingStart, index);
124
0
        }
125
126
0
        fixingSchedule_ = index->fixingSchedule(fixingStart, endDate);
127
128
0
        setPricer(ext::shared_ptr<FloatingRateCouponPricer>(
129
0
                                                 new AverageBMACouponPricer));
130
0
    }
Unexecuted instantiation: QuantLib::AverageBMACoupon::AverageBMACoupon(QuantLib::Date const&, double, QuantLib::Date const&, QuantLib::Date const&, boost::shared_ptr<QuantLib::BMAIndex> const&, double, double, QuantLib::Date const&, QuantLib::Date const&, QuantLib::DayCounter const&)
Unexecuted instantiation: QuantLib::AverageBMACoupon::AverageBMACoupon(QuantLib::Date const&, double, QuantLib::Date const&, QuantLib::Date const&, boost::shared_ptr<QuantLib::BMAIndex> const&, double, double, QuantLib::Date const&, QuantLib::Date const&, QuantLib::DayCounter const&)
131
132
0
    Date AverageBMACoupon::fixingDate() const {
133
0
        QL_FAIL("no single fixing date for average-BMA coupon");
134
0
    }
135
136
0
    std::vector<Date> AverageBMACoupon::fixingDates() const {
137
0
        return fixingSchedule_.dates();
138
0
    }
139
140
0
    Rate AverageBMACoupon::indexFixing() const {
141
0
        QL_FAIL("no single fixing for average-BMA coupon");
142
0
    }
143
144
0
    std::vector<Rate> AverageBMACoupon::indexFixings() const {
145
0
        std::vector<Rate> fixings(fixingSchedule_.size());
146
0
        for (Size i=0; i<fixings.size(); ++i)
147
0
            fixings[i] = index_->fixing(fixingSchedule_.date(i));
148
0
        return fixings;
149
0
    }
150
151
0
    Rate AverageBMACoupon::convexityAdjustment() const {
152
0
        QL_FAIL("not defined for average-BMA coupon");
153
0
    }
154
155
0
    void AverageBMACoupon::accept(AcyclicVisitor& v) {
156
0
        auto* v1 = dynamic_cast<Visitor<AverageBMACoupon>*>(&v);
157
0
        if (v1 != nullptr) {
158
0
            v1->visit(*this);
159
0
        } else {
160
0
            FloatingRateCoupon::accept(v);
161
0
        }
162
0
    }
163
164
165
    AverageBMALeg::AverageBMALeg(Schedule schedule, ext::shared_ptr<BMAIndex> index)
166
0
    : schedule_(std::move(schedule)), index_(std::move(index)) {}
167
168
0
    AverageBMALeg& AverageBMALeg::withNotionals(Real notional) {
169
0
        notionals_ = std::vector<Real>(1,notional);
170
0
        return *this;
171
0
    }
172
173
    AverageBMALeg& AverageBMALeg::withNotionals(
174
0
                                         const std::vector<Real>& notionals) {
175
0
        notionals_ = notionals;
176
0
        return *this;
177
0
    }
178
179
    AverageBMALeg& AverageBMALeg::withPaymentDayCounter(
180
0
                                               const DayCounter& dayCounter) {
181
0
        paymentDayCounter_ = dayCounter;
182
0
        return *this;
183
0
    }
184
185
    AverageBMALeg& AverageBMALeg::withPaymentAdjustment(
186
0
                                           BusinessDayConvention convention) {
187
0
        paymentAdjustment_ = convention;
188
0
        return *this;
189
0
    }
190
191
0
    AverageBMALeg& AverageBMALeg::withGearings(Real gearing) {
192
0
        gearings_ = std::vector<Real>(1,gearing);
193
0
        return *this;
194
0
    }
195
196
    AverageBMALeg& AverageBMALeg::withGearings(
197
0
                                          const std::vector<Real>& gearings) {
198
0
        gearings_ = gearings;
199
0
        return *this;
200
0
    }
201
202
0
    AverageBMALeg& AverageBMALeg::withSpreads(Spread spread) {
203
0
        spreads_ = std::vector<Spread>(1,spread);
204
0
        return *this;
205
0
    }
206
207
    AverageBMALeg& AverageBMALeg::withSpreads(
208
0
                                         const std::vector<Spread>& spreads) {
209
0
        spreads_ = spreads;
210
0
        return *this;
211
0
    }
212
213
0
    AverageBMALeg::operator Leg() const {
214
215
0
        QL_REQUIRE(!notionals_.empty(), "no notional given");
216
217
0
        Leg cashflows;
218
219
        // the following is not always correct
220
0
        Calendar calendar = schedule_.calendar();
221
222
0
        Date refStart, start, refEnd, end;
223
0
        Date paymentDate;
224
225
0
        Size n = schedule_.size()-1;
226
0
        for (Size i=0; i<n; ++i) {
227
0
            refStart = start = schedule_.date(i);
228
0
            refEnd   =   end = schedule_.date(i+1);
229
0
            paymentDate = calendar.adjust(end, paymentAdjustment_);
230
0
            if (i == 0 && schedule_.hasIsRegular() && !schedule_.isRegular(i+1)
231
0
                && schedule_.hasTenor())
232
0
                refStart = calendar.adjust(end - schedule_.tenor(),
233
0
                                           paymentAdjustment_);
234
0
            if (i == n-1 && schedule_.hasIsRegular() && !schedule_.isRegular(i+1)
235
0
                && schedule_.hasTenor())
236
0
                refEnd = calendar.adjust(start + schedule_.tenor(),
237
0
                                         paymentAdjustment_);
238
239
0
            cashflows.push_back(ext::shared_ptr<CashFlow>(new
240
0
                AverageBMACoupon(paymentDate,
241
0
                                 detail::get(notionals_, i, notionals_.back()),
242
0
                                 start, end,
243
0
                                 index_,
244
0
                                 detail::get(gearings_, i, 1.0),
245
0
                                 detail::get(spreads_, i, 0.0),
246
0
                                 refStart, refEnd,
247
0
                                 paymentDayCounter_)));
248
0
        }
249
250
0
        return cashflows;
251
0
    }
252
253
}
254