• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lballabio / QuantLib / 14910176578

08 May 2025 03:28PM UTC coverage: 73.315% (+0.02%) from 73.3%
14910176578

Pull #2195

github

web-flow
Merge 3a61f499c into 5d972fb7b
Pull Request #2195: Added `Handle<Quote>` for spread in `OISRateHelper`

32 of 33 new or added lines in 2 files covered. (96.97%)

277 existing lines in 25 files now uncovered.

56277 of 76761 relevant lines covered (73.31%)

8687029.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.78
/ql/termstructures/yield/oisratehelper.cpp
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2

3
/*
4
 Copyright (C) 2009, 2012 Roland Lichters
5
 Copyright (C) 2009, 2012 Ferdinando Ametrano
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/instruments/makeois.hpp>
22
#include <ql/instruments/simplifynotificationgraph.hpp>
23
#include <ql/cashflows/couponpricer.hpp>
24
#include <ql/pricingengines/swap/discountingswapengine.hpp>
25
#include <ql/termstructures/yield/oisratehelper.hpp>
26
#include <ql/utilities/null_deleter.hpp>
27
#include <utility>
28

29
namespace QuantLib {
30

31
    namespace {
32
        Handle<Quote> normalizeSpreadType(const std::variant<Spread, Handle<Quote>>& overnightSpread) {
419✔
33
            return std::visit(
34
                [](const auto& value) -> Handle<Quote> {
35
                    using T = std::decay_t<decltype(value)>;
36
                    if constexpr (std::is_same_v<T, Spread>) {
37
                        return makeQuoteHandle(value);
53✔
38
                    } else if constexpr (std::is_same_v<T, Handle<Quote>>) {
39
                        return value;
40
                    } else {
41
                        QL_FAIL("Unexpected type in overnightSpread variant");
42
                    }
43
                },
44
                overnightSpread
45
            );
419✔
46
        }
47
    }
48

49
    OISRateHelper::OISRateHelper(Natural settlementDays,
412✔
50
        const Period& tenor, // swap maturity
51
        const Handle<Quote>& fixedRate,
52
        const ext::shared_ptr<OvernightIndex>& overnightIndex,
53
        Handle<YieldTermStructure> discount,
54
        bool telescopicValueDates,
55
        Integer paymentLag,
56
        BusinessDayConvention paymentConvention,
57
        Frequency paymentFrequency,
58
        Calendar paymentCalendar,
59
        const Period& forwardStart,
60
        const std::variant<Spread, Handle<Quote>> overnightSpread,
61
        Pillar::Choice pillar,
62
        Date customPillarDate,
63
        RateAveraging::Type averagingMethod,
64
        ext::optional<bool> endOfMonth,
65
        ext::optional<Frequency> fixedPaymentFrequency,
66
        Calendar fixedCalendar,
67
        Natural lookbackDays,
68
        Natural lockoutDays,
69
        bool applyObservationShift,
70
        ext::shared_ptr<FloatingRateCouponPricer> pricer,
71
        DateGeneration::Rule rule)
412✔
72
    : RelativeDateRateHelper(fixedRate), settlementDays_(settlementDays), tenor_(tenor),
412✔
73
    discountHandle_(std::move(discount)), telescopicValueDates_(telescopicValueDates),
412✔
74
    paymentLag_(paymentLag), paymentConvention_(paymentConvention),
412✔
75
    paymentFrequency_(paymentFrequency), paymentCalendar_(std::move(paymentCalendar)),
412✔
76
    forwardStart_(forwardStart), overnightSpread_(normalizeSpreadType(overnightSpread)), pillarChoice_(pillar),
412✔
77
    averagingMethod_(averagingMethod), endOfMonth_(endOfMonth),
412✔
78
    fixedPaymentFrequency_(fixedPaymentFrequency), fixedCalendar_(std::move(fixedCalendar)),
412✔
79
    lookbackDays_(lookbackDays), lockoutDays_(lockoutDays), applyObservationShift_(applyObservationShift),
412✔
80
    pricer_(std::move(pricer)), rule_(rule) {
412✔
81
    initialize(overnightIndex, customPillarDate);
412✔
82
    }
410✔
83

84
    OISRateHelper::OISRateHelper(const Date& startDate,
7✔
85
        const Date& endDate,
86
        const Handle<Quote>& fixedRate,
87
        const ext::shared_ptr<OvernightIndex>& overnightIndex,
88
        Handle<YieldTermStructure> discount,
89
        bool telescopicValueDates,
90
        Integer paymentLag,
91
        BusinessDayConvention paymentConvention,
92
        Frequency paymentFrequency,
93
        Calendar paymentCalendar,
94
        const std::variant<Spread, Handle<Quote>> overnightSpread,
95
        Pillar::Choice pillar,
96
        Date customPillarDate,
97
        RateAveraging::Type averagingMethod,
98
        ext::optional<bool> endOfMonth,
99
        ext::optional<Frequency> fixedPaymentFrequency,
100
        Calendar fixedCalendar,
101
        Natural lookbackDays,
102
        Natural lockoutDays,
103
        bool applyObservationShift,
104
        ext::shared_ptr<FloatingRateCouponPricer> pricer,
105
        DateGeneration::Rule rule)
7✔
106
    : RelativeDateRateHelper(fixedRate, false), startDate_(startDate), endDate_(endDate),
7✔
107
    discountHandle_(std::move(discount)), telescopicValueDates_(telescopicValueDates),
7✔
108
    paymentLag_(paymentLag), paymentConvention_(paymentConvention),
7✔
109
    paymentFrequency_(paymentFrequency), paymentCalendar_(std::move(paymentCalendar)),
7✔
110
    overnightSpread_(normalizeSpreadType(overnightSpread)), pillarChoice_(pillar),
7✔
111
    averagingMethod_(averagingMethod), endOfMonth_(endOfMonth),
7✔
112
    fixedPaymentFrequency_(fixedPaymentFrequency), fixedCalendar_(std::move(fixedCalendar)),
7✔
113
    lookbackDays_(lookbackDays), lockoutDays_(lockoutDays), applyObservationShift_(applyObservationShift),
7✔
114
    pricer_(std::move(pricer)), rule_(rule) {
7✔
115
    initialize(overnightIndex, customPillarDate);
7✔
116
    }
7✔
117

118
    void OISRateHelper::initialize(const ext::shared_ptr<OvernightIndex>& overnightIndex,
419✔
119
                                   Date customPillarDate) {
120
        overnightIndex_ =
121
            ext::dynamic_pointer_cast<OvernightIndex>(overnightIndex->clone(termStructureHandle_));
838✔
122
        // We want to be notified of changes of fixings, but we don't
123
        // want notifications from termStructureHandle_ (they would
124
        // interfere with bootstrapping.)
125
        overnightIndex_->unregisterWith(termStructureHandle_);
419✔
126

127
        registerWith(overnightIndex_);
838✔
128
        registerWith(discountHandle_);
838✔
129
        registerWith(overnightSpread_);
419✔
130

131
        pillarDate_ = customPillarDate;
419✔
132
        OISRateHelper::initializeDates();
419✔
133
    }
417✔
134

135
    void OISRateHelper::initializeDates() {
419✔
136

137
        // 1. do not pass the spread here, as it might be a Quote
138
        //    i.e. it can dynamically change
139
        // 2. input discount curve Handle might be empty now but it could
140
        //    be assigned a curve later; use a RelinkableHandle here
141
        auto tmp = MakeOIS(tenor_, overnightIndex_, 0.0, forwardStart_)
838✔
142
            .withDiscountingTermStructure(discountRelinkableHandle_)
419✔
143
            .withSettlementDays(settlementDays_)  // resets effectiveDate
419✔
144
            .withEffectiveDate(startDate_)
419✔
145
            .withTerminationDate(endDate_)
419✔
146
            .withTelescopicValueDates(telescopicValueDates_)
419✔
147
            .withPaymentLag(paymentLag_)
419✔
148
            .withPaymentAdjustment(paymentConvention_)
419✔
149
            .withPaymentFrequency(paymentFrequency_)
419✔
150
            .withPaymentCalendar(paymentCalendar_)
419✔
151
            .withAveragingMethod(averagingMethod_)
419✔
152
            .withLookbackDays(lookbackDays_)
419✔
153
            .withLockoutDays(lockoutDays_)
419✔
154
            .withRule(rule_)
419✔
155
            .withObservationShift(applyObservationShift_);
419✔
156
        if (endOfMonth_) {
419✔
157
            tmp.withEndOfMonth(*endOfMonth_);
×
158
        }
159
        if (fixedPaymentFrequency_) {
419✔
160
            tmp.withFixedLegPaymentFrequency(*fixedPaymentFrequency_);
×
161
        }
162
        if (!fixedCalendar_.empty()) {
419✔
163
            tmp.withFixedLegCalendar(fixedCalendar_);
×
164
        }
165
        swap_ = tmp;
836✔
166

167
        if (pricer_)
417✔
168
            setCouponPricer(swap_->overnightLeg(), pricer_);
33✔
169

170
        simplifyNotificationGraph(*swap_, true);
417✔
171

172
        earliestDate_ = swap_->startDate();
417✔
173
        maturityDate_ = swap_->maturityDate();
417✔
174

175
        Date lastPaymentDate = std::max(swap_->overnightLeg().back()->date(),
417✔
176
                                        swap_->fixedLeg().back()->date());
417✔
177
        latestRelevantDate_ = latestDate_ = std::max(maturityDate_, lastPaymentDate);
417✔
178

179
        switch (pillarChoice_) {
417✔
180
          case Pillar::MaturityDate:
22✔
181
            pillarDate_ = maturityDate_;
22✔
182
            break;
22✔
183
          case Pillar::LastRelevantDate:
395✔
184
            pillarDate_ = latestRelevantDate_;
395✔
185
            break;
395✔
186
          case Pillar::CustomDate:
×
187
            // pillarDate_ already assigned at construction time
188
            QL_REQUIRE(pillarDate_ >= earliestDate_,
×
189
                       "pillar date (" << pillarDate_ << ") must be later "
190
                       "than or equal to the instrument's earliest date (" <<
191
                       earliestDate_ << ")");
192
            QL_REQUIRE(pillarDate_ <= latestRelevantDate_,
×
193
                       "pillar date (" << pillarDate_ << ") must be before "
194
                       "or equal to the instrument's latest relevant date (" <<
195
                       latestRelevantDate_ << ")");
196
            break;
197
          default:
×
198
            QL_FAIL("unknown Pillar::Choice(" << Integer(pillarChoice_) << ")");
×
199
        }
200
    }
419✔
201

202
    void OISRateHelper::setTermStructure(YieldTermStructure* t) {
416✔
203
        // do not set the relinkable handle as an observer -
204
        // force recalculation when needed
205
        bool observer = false;
206

207
        ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
208
        termStructureHandle_.linkTo(temp, observer);
416✔
209

210
        if (discountHandle_.empty())
416✔
211
            discountRelinkableHandle_.linkTo(temp, observer);
416✔
212
        else
213
            discountRelinkableHandle_.linkTo(*discountHandle_, observer);
×
214

215
        RelativeDateRateHelper::setTermStructure(t);
416✔
216
    }
416✔
217

218
    Real OISRateHelper::impliedQuote() const {
4,362✔
219
        QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
4,362✔
220
        // we didn't register as observers - force calculation
221
        swap_->deepUpdate();
4,362✔
222
        // weak implementation... to be improved
223
        static const Spread basisPoint = 1.0e-4;
224
        Real floatingLegNPV = swap_->overnightLegNPV();
4,362✔
225
        Spread spread = overnightSpread_.empty() ? 0.0 : overnightSpread_->value();
4,362✔
226
        Real spreadNPV = swap_->overnightLegBPS()/basisPoint*spread;
4,362✔
227
        Real totNPV = - (floatingLegNPV+spreadNPV);
4,362✔
228
        Real result = totNPV/(swap_->fixedLegBPS()/basisPoint);
4,362✔
229
        return result;
4,362✔
230
    }
231

232
    void OISRateHelper::accept(AcyclicVisitor& v) {
×
233
        auto* v1 = dynamic_cast<Visitor<OISRateHelper>*>(&v);
×
234
        if (v1 != nullptr)
×
235
            v1->visit(*this);
×
236
        else
237
            RateHelper::accept(v);
×
238
    }
×
239

240
    DatedOISRateHelper::DatedOISRateHelper(const Date& startDate,
1✔
241
                                           const Date& endDate,
242
                                           const Handle<Quote>& fixedRate,
243
                                           const ext::shared_ptr<OvernightIndex>& overnightIndex,
244
                                           Handle<YieldTermStructure> discount,
245
                                           bool telescopicValueDates,
246
                                           RateAveraging::Type averagingMethod,
247
                                           Integer paymentLag,
248
                                           BusinessDayConvention paymentConvention,
249
                                           Frequency paymentFrequency,
250
                                           const Calendar& paymentCalendar,
251
                                           Handle<Quote> overnightSpread,
252
                                           ext::optional<bool> endOfMonth,
253
                                           ext::optional<Frequency> fixedPaymentFrequency,
254
                                           const Calendar& fixedCalendar,
255
                                           Natural lookbackDays,
256
                                           Natural lockoutDays,
257
                                           bool applyObservationShift,
258
                                           const ext::shared_ptr<FloatingRateCouponPricer>& pricer)
1✔
259
    : OISRateHelper(startDate, endDate, fixedRate, overnightIndex, std::move(discount), telescopicValueDates,
260
                    paymentLag, paymentConvention, paymentFrequency, paymentCalendar, 
261
                    std::variant<Spread, Handle<Quote>>(std::move(overnightSpread)),
1✔
262
                    Pillar::LastRelevantDate, Date(), averagingMethod, endOfMonth, fixedPaymentFrequency,
263
                    fixedCalendar, lookbackDays, lockoutDays, applyObservationShift, pricer) {}
3✔
264

265
    DatedOISRateHelper::DatedOISRateHelper(const Date& startDate,
×
266
                                           const Date& endDate,
267
                                           const Handle<Quote>& fixedRate,
268
                                           const ext::shared_ptr<OvernightIndex>& overnightIndex,
269
                                           Handle<YieldTermStructure> discount,
270
                                           bool telescopicValueDates,
271
                                           RateAveraging::Type averagingMethod,
272
                                           Integer paymentLag,
273
                                           BusinessDayConvention paymentConvention,
274
                                           Frequency paymentFrequency,
275
                                           const Calendar& paymentCalendar,
276
                                           const Period&,
277
                                           Handle<Quote> overnightSpread,
278
                                           ext::optional<bool> endOfMonth,
279
                                           ext::optional<Frequency> fixedPaymentFrequency,
280
                                           const Calendar& fixedCalendar)
×
281
    : DatedOISRateHelper(startDate, endDate, fixedRate, overnightIndex, std::move(discount), telescopicValueDates,
282
                         averagingMethod, paymentLag, paymentConvention, paymentFrequency, paymentCalendar,
NEW
283
                         std::move(overnightSpread), endOfMonth, fixedPaymentFrequency, fixedCalendar) {}
×
284

285
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc