Skip to content

Commit d3def14

Browse files
zippygclaude
andcommitted
Add MultipleResetsSwap instrument and rate helper
Implements issue #2138: a fixed-vs-floating swap whose floating leg coupons are determined by compounding or averaging multiple consecutive Ibor fixings within each accrual period. Adds MultipleResetsSwap (inheriting FixedVsFloatingSwap, giving fairRate(), fairSpread(), and BPS methods for free), a MakeMultipleResetsSwap fluent builder, and MultipleResetsSwapRateHelper for bootstrapping yield curves from multiple-resets swap quotes. The floating leg is built via the existing MultipleResetsLeg/coupon infrastructure. Tests cover fair-rate pricing, payer/receiver leg NPV consistency, averaging vs. compounding differentiation, and end-to-end bootstrapping at 1Y/2Y/3Y. Relates to #2138. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 81d819d commit d3def14

18 files changed

+820
-0
lines changed

QuantLib.vcxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,12 +933,14 @@
933933
<ClInclude Include="ql\instruments\makecapfloor.hpp" />
934934
<ClInclude Include="ql\instruments\makecds.hpp" />
935935
<ClInclude Include="ql\instruments\makecms.hpp" />
936+
<ClInclude Include="ql\instruments\makemultipleresetsswap.hpp" />
936937
<ClInclude Include="ql\instruments\makeois.hpp" />
937938
<ClInclude Include="ql\instruments\makeswaption.hpp" />
938939
<ClInclude Include="ql\instruments\makevanillaswap.hpp" />
939940
<ClInclude Include="ql\instruments\makeyoyinflationcapfloor.hpp" />
940941
<ClInclude Include="ql\instruments\margrabeoption.hpp" />
941942
<ClInclude Include="ql\instruments\multiassetoption.hpp" />
943+
<ClInclude Include="ql\instruments\multipleresetsswap.hpp" />
942944
<ClInclude Include="ql\instruments\nonstandardswap.hpp" />
943945
<ClInclude Include="ql\instruments\nonstandardswaption.hpp" />
944946
<ClInclude Include="ql\instruments\oneassetoption.hpp" />
@@ -1790,6 +1792,7 @@
17901792
<ClInclude Include="ql\termstructures\yield\forwardstructure.hpp" />
17911793
<ClInclude Include="ql\termstructures\yield\impliedtermstructure.hpp" />
17921794
<ClInclude Include="ql\termstructures\yield\interpolatedsimplezerocurve.hpp" />
1795+
<ClInclude Include="ql\termstructures\yield\multipleresetsswaphelper.hpp" />
17931796
<ClInclude Include="ql\termstructures\yield\nonlinearfittingmethods.hpp" />
17941797
<ClInclude Include="ql\termstructures\yield\oisratehelper.hpp" />
17951798
<ClInclude Include="ql\termstructures\yield\overnightindexfutureratehelper.hpp" />
@@ -2212,12 +2215,14 @@
22122215
<ClCompile Include="ql\instruments\makecapfloor.cpp" />
22132216
<ClCompile Include="ql\instruments\makecds.cpp" />
22142217
<ClCompile Include="ql\instruments\makecms.cpp" />
2218+
<ClCompile Include="ql\instruments\makemultipleresetsswap.cpp" />
22152219
<ClCompile Include="ql\instruments\makeois.cpp" />
22162220
<ClCompile Include="ql\instruments\makeswaption.cpp" />
22172221
<ClCompile Include="ql\instruments\makevanillaswap.cpp" />
22182222
<ClCompile Include="ql\instruments\makeyoyinflationcapfloor.cpp" />
22192223
<ClCompile Include="ql\instruments\margrabeoption.cpp" />
22202224
<ClCompile Include="ql\instruments\multiassetoption.cpp" />
2225+
<ClCompile Include="ql\instruments\multipleresetsswap.cpp" />
22212226
<ClCompile Include="ql\instruments\nonstandardswap.cpp" />
22222227
<ClCompile Include="ql\instruments\nonstandardswaption.cpp" />
22232228
<ClCompile Include="ql\instruments\oneassetoption.cpp" />
@@ -2796,6 +2801,7 @@
27962801
<ClCompile Include="ql\termstructures\yield\fittedbonddiscountcurve.cpp" />
27972802
<ClCompile Include="ql\termstructures\yield\flatforward.cpp" />
27982803
<ClCompile Include="ql\termstructures\yield\forwardstructure.cpp" />
2804+
<ClCompile Include="ql\termstructures\yield\multipleresetsswaphelper.cpp" />
27992805
<ClCompile Include="ql\termstructures\yield\nonlinearfittingmethods.cpp" />
28002806
<ClCompile Include="ql\termstructures\yield\oisratehelper.cpp" />
28012807
<ClCompile Include="ql\termstructures\yield\overnightindexfutureratehelper.cpp" />

QuantLib.vcxproj.filters

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,9 @@
861861
<ClInclude Include="ql\instruments\makecms.hpp">
862862
<Filter>instruments</Filter>
863863
</ClInclude>
864+
<ClInclude Include="ql\instruments\makemultipleresetsswap.hpp">
865+
<Filter>instruments</Filter>
866+
</ClInclude>
864867
<ClInclude Include="ql\instruments\makeois.hpp">
865868
<Filter>instruments</Filter>
866869
</ClInclude>
@@ -879,6 +882,9 @@
879882
<ClInclude Include="ql\instruments\multiassetoption.hpp">
880883
<Filter>instruments</Filter>
881884
</ClInclude>
885+
<ClInclude Include="ql\instruments\multipleresetsswap.hpp">
886+
<Filter>instruments</Filter>
887+
</ClInclude>
882888
<ClInclude Include="ql\instruments\oneassetoption.hpp">
883889
<Filter>instruments</Filter>
884890
</ClInclude>
@@ -2187,6 +2193,9 @@
21872193
<ClInclude Include="ql\termstructures\yield\interpolatedsimplezerocurve.hpp">
21882194
<Filter>termstructures\yield</Filter>
21892195
</ClInclude>
2196+
<ClInclude Include="ql\termstructures\yield\multipleresetsswaphelper.hpp">
2197+
<Filter>termstructures\yield</Filter>
2198+
</ClInclude>
21902199
<ClInclude Include="ql\termstructures\yield\nonlinearfittingmethods.hpp">
21912200
<Filter>termstructures\yield</Filter>
21922201
</ClInclude>
@@ -4826,6 +4835,9 @@
48264835
<ClCompile Include="ql\instruments\makecms.cpp">
48274836
<Filter>instruments</Filter>
48284837
</ClCompile>
4838+
<ClCompile Include="ql\instruments\makemultipleresetsswap.cpp">
4839+
<Filter>instruments</Filter>
4840+
</ClCompile>
48294841
<ClCompile Include="ql\instruments\makeois.cpp">
48304842
<Filter>instruments</Filter>
48314843
</ClCompile>
@@ -4844,6 +4856,9 @@
48444856
<ClCompile Include="ql\instruments\multiassetoption.cpp">
48454857
<Filter>instruments</Filter>
48464858
</ClCompile>
4859+
<ClCompile Include="ql\instruments\multipleresetsswap.cpp">
4860+
<Filter>instruments</Filter>
4861+
</ClCompile>
48474862
<ClCompile Include="ql\instruments\oneassetoption.cpp">
48484863
<Filter>instruments</Filter>
48494864
</ClCompile>
@@ -5702,6 +5717,9 @@
57025717
<ClCompile Include="ql\termstructures\yield\forwardstructure.cpp">
57035718
<Filter>termstructures\yield</Filter>
57045719
</ClCompile>
5720+
<ClCompile Include="ql\termstructures\yield\multipleresetsswaphelper.cpp">
5721+
<Filter>termstructures\yield</Filter>
5722+
</ClCompile>
57055723
<ClCompile Include="ql\termstructures\yield\nonlinearfittingmethods.cpp">
57065724
<Filter>termstructures\yield</Filter>
57075725
</ClCompile>

ql/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,12 +283,14 @@ set(QL_SOURCES
283283
instruments/makecapfloor.cpp
284284
instruments/makecds.cpp
285285
instruments/makecms.cpp
286+
instruments/makemultipleresetsswap.cpp
286287
instruments/makeois.cpp
287288
instruments/makeswaption.cpp
288289
instruments/makevanillaswap.cpp
289290
instruments/makeyoyinflationcapfloor.cpp
290291
instruments/margrabeoption.cpp
291292
instruments/multiassetoption.cpp
293+
instruments/multipleresetsswap.cpp
292294
instruments/nonstandardswap.cpp
293295
instruments/nonstandardswaption.cpp
294296
instruments/oneassetoption.cpp
@@ -876,6 +878,7 @@ set(QL_SOURCES
876878
termstructures/yield/fittedbonddiscountcurve.cpp
877879
termstructures/yield/flatforward.cpp
878880
termstructures/yield/forwardstructure.cpp
881+
termstructures/yield/multipleresetsswaphelper.cpp
879882
termstructures/yield/nonlinearfittingmethods.cpp
880883
termstructures/yield/oisratehelper.cpp
881884
termstructures/yield/overnightindexfutureratehelper.cpp
@@ -1363,12 +1366,14 @@ set(QL_HEADERS
13631366
instruments/makecapfloor.hpp
13641367
instruments/makecds.hpp
13651368
instruments/makecms.hpp
1369+
instruments/makemultipleresetsswap.hpp
13661370
instruments/makeois.hpp
13671371
instruments/makeswaption.hpp
13681372
instruments/makevanillaswap.hpp
13691373
instruments/makeyoyinflationcapfloor.hpp
13701374
instruments/margrabeoption.hpp
13711375
instruments/multiassetoption.hpp
1376+
instruments/multipleresetsswap.hpp
13721377
instruments/nonstandardswap.hpp
13731378
instruments/nonstandardswaption.hpp
13741379
instruments/oneassetoption.hpp
@@ -2162,6 +2167,7 @@ set(QL_HEADERS
21622167
termstructures/yield/forwardstructure.hpp
21632168
termstructures/yield/impliedtermstructure.hpp
21642169
termstructures/yield/interpolatedsimplezerocurve.hpp
2170+
termstructures/yield/multipleresetsswaphelper.hpp
21652171
termstructures/yield/nonlinearfittingmethods.hpp
21662172
termstructures/yield/oisratehelper.hpp
21672173
termstructures/yield/overnightindexfutureratehelper.hpp

ql/instruments/Makefile.am

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,14 @@ this_include_HEADERS = \
4545
makecapfloor.hpp \
4646
makecds.hpp \
4747
makecms.hpp \
48+
makemultipleresetsswap.hpp \
4849
makeois.hpp \
4950
makeswaption.hpp \
5051
makevanillaswap.hpp \
5152
makeyoyinflationcapfloor.hpp \
5253
margrabeoption.hpp \
5354
multiassetoption.hpp \
55+
multipleresetsswap.hpp \
5456
nonstandardswap.hpp \
5557
nonstandardswaption.hpp \
5658
oneassetoption.hpp \
@@ -119,12 +121,14 @@ cpp_files = \
119121
makecapfloor.cpp \
120122
makecds.cpp \
121123
makecms.cpp \
124+
makemultipleresetsswap.cpp \
122125
makeois.cpp \
123126
makeswaption.cpp \
124127
makevanillaswap.cpp \
125128
makeyoyinflationcapfloor.cpp \
126129
margrabeoption.cpp \
127130
multiassetoption.cpp \
131+
multipleresetsswap.cpp \
128132
nonstandardswap.cpp \
129133
nonstandardswaption.cpp \
130134
oneassetoption.cpp \

ql/instruments/all.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,14 @@
4040
#include <ql/instruments/makecapfloor.hpp>
4141
#include <ql/instruments/makecds.hpp>
4242
#include <ql/instruments/makecms.hpp>
43+
#include <ql/instruments/makemultipleresetsswap.hpp>
4344
#include <ql/instruments/makeois.hpp>
4445
#include <ql/instruments/makeswaption.hpp>
4546
#include <ql/instruments/makevanillaswap.hpp>
4647
#include <ql/instruments/makeyoyinflationcapfloor.hpp>
4748
#include <ql/instruments/margrabeoption.hpp>
4849
#include <ql/instruments/multiassetoption.hpp>
50+
#include <ql/instruments/multipleresetsswap.hpp>
4951
#include <ql/instruments/nonstandardswap.hpp>
5052
#include <ql/instruments/nonstandardswaption.hpp>
5153
#include <ql/instruments/oneassetoption.hpp>
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2+
3+
/*
4+
Copyright (C) 2026 Zain Mughal
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/indexes/iborindex.hpp>
21+
#include <ql/instruments/makemultipleresetsswap.hpp>
22+
#include <ql/pricingengines/swap/discountingswapengine.hpp>
23+
#include <ql/settings.hpp>
24+
#include <ql/time/schedule.hpp>
25+
26+
namespace QuantLib {
27+
28+
MakeMultipleResetsSwap::MakeMultipleResetsSwap(
29+
const Period& tenor,
30+
const ext::shared_ptr<IborIndex>& iborIndex,
31+
Size resetsPerCoupon,
32+
Rate fixedRate,
33+
const Period& fwdStart)
34+
: tenor_(tenor), iborIndex_(iborIndex), resetsPerCoupon_(resetsPerCoupon),
35+
fixedRate_(fixedRate), forwardStart_(fwdStart),
36+
fixedDayCount_(iborIndex->dayCounter()) {}
37+
38+
MakeMultipleResetsSwap::operator MultipleResetsSwap() const {
39+
ext::shared_ptr<MultipleResetsSwap> swap = *this;
40+
return *swap;
41+
}
42+
43+
MakeMultipleResetsSwap::operator ext::shared_ptr<MultipleResetsSwap>() const {
44+
Calendar cal = iborIndex_->fixingCalendar();
45+
BusinessDayConvention bdc = iborIndex_->businessDayConvention();
46+
47+
Date startDate;
48+
if (effectiveDate_ != Date()) {
49+
startDate = effectiveDate_;
50+
} else {
51+
Natural settlDays = settlementDays_ != Null<Natural>() ?
52+
settlementDays_ : iborIndex_->fixingDays();
53+
Date refDate = Settings::instance().evaluationDate();
54+
startDate = cal.advance(cal.adjust(refDate), settlDays * Days);
55+
startDate = cal.advance(startDate, forwardStart_,
56+
forwardStart_.length() < 0 ? Preceding : Following);
57+
}
58+
59+
Date endDate = terminationDate_ != Date() ?
60+
terminationDate_ :
61+
cal.advance(startDate, tenor_, bdc);
62+
63+
Period resetTenor = iborIndex_->tenor();
64+
// Fixed coupon period: resetsPerCoupon consecutive reset periods.
65+
// If not overridden, derive the fixed frequency from the coupon period.
66+
Frequency fixedFreq = fixedFrequency_;
67+
if (fixedFreq == NoFrequency) {
68+
Period couponTenor(resetsPerCoupon_ * resetTenor.length(), resetTenor.units());
69+
fixedFreq = couponTenor.frequency();
70+
}
71+
72+
Schedule fixedSchedule(startDate, endDate, Period(fixedFreq),
73+
cal, fixedConvention_, fixedConvention_,
74+
DateGeneration::Backward, false);
75+
76+
Schedule fullResetSchedule(startDate, endDate, resetTenor,
77+
cal, bdc, bdc,
78+
DateGeneration::Backward, false);
79+
80+
Rate usedFixedRate = fixedRate_;
81+
if (fixedRate_ == Null<Rate>()) {
82+
MultipleResetsSwap temp(type_, nominal_,
83+
fixedSchedule, 0.0, fixedDayCount_,
84+
fullResetSchedule, iborIndex_, resetsPerCoupon_,
85+
spread_, averagingMethod_);
86+
if (engine_ == nullptr) {
87+
Handle<YieldTermStructure> disc =
88+
iborIndex_->forwardingTermStructure();
89+
QL_REQUIRE(!disc.empty(),
90+
"null term structure set to this instance of "
91+
<< iborIndex_->name());
92+
temp.setPricingEngine(ext::make_shared<DiscountingSwapEngine>(disc, false));
93+
} else {
94+
temp.setPricingEngine(engine_);
95+
}
96+
usedFixedRate = temp.fairRate();
97+
}
98+
99+
auto swap = ext::make_shared<MultipleResetsSwap>(
100+
type_, nominal_,
101+
fixedSchedule, usedFixedRate, fixedDayCount_,
102+
fullResetSchedule, iborIndex_, resetsPerCoupon_,
103+
spread_, averagingMethod_);
104+
105+
if (engine_ == nullptr) {
106+
Handle<YieldTermStructure> disc = iborIndex_->forwardingTermStructure();
107+
if (!disc.empty())
108+
swap->setPricingEngine(ext::make_shared<DiscountingSwapEngine>(disc, false));
109+
} else {
110+
swap->setPricingEngine(engine_);
111+
}
112+
113+
return swap;
114+
}
115+
116+
MakeMultipleResetsSwap& MakeMultipleResetsSwap::receiveFixed(bool flag) {
117+
type_ = flag ? Swap::Receiver : Swap::Payer;
118+
return *this;
119+
}
120+
121+
MakeMultipleResetsSwap& MakeMultipleResetsSwap::withType(Swap::Type type) {
122+
type_ = type;
123+
return *this;
124+
}
125+
126+
MakeMultipleResetsSwap& MakeMultipleResetsSwap::withNominal(Real n) {
127+
nominal_ = n;
128+
return *this;
129+
}
130+
131+
MakeMultipleResetsSwap& MakeMultipleResetsSwap::withSettlementDays(Natural settlementDays) {
132+
settlementDays_ = settlementDays;
133+
effectiveDate_ = Date();
134+
return *this;
135+
}
136+
137+
MakeMultipleResetsSwap& MakeMultipleResetsSwap::withEffectiveDate(const Date& d) {
138+
effectiveDate_ = d;
139+
return *this;
140+
}
141+
142+
MakeMultipleResetsSwap& MakeMultipleResetsSwap::withTerminationDate(const Date& d) {
143+
terminationDate_ = d;
144+
if (d != Date())
145+
tenor_ = Period();
146+
return *this;
147+
}
148+
149+
MakeMultipleResetsSwap& MakeMultipleResetsSwap::withFixedLegFrequency(Frequency f) {
150+
fixedFrequency_ = f;
151+
return *this;
152+
}
153+
154+
MakeMultipleResetsSwap& MakeMultipleResetsSwap::withFixedLegDayCount(const DayCounter& dc) {
155+
fixedDayCount_ = dc;
156+
return *this;
157+
}
158+
159+
MakeMultipleResetsSwap& MakeMultipleResetsSwap::withFixedLegConvention(BusinessDayConvention bdc) {
160+
fixedConvention_ = bdc;
161+
return *this;
162+
}
163+
164+
MakeMultipleResetsSwap& MakeMultipleResetsSwap::withFloatingLegSpread(Spread sp) {
165+
spread_ = sp;
166+
return *this;
167+
}
168+
169+
MakeMultipleResetsSwap& MakeMultipleResetsSwap::withAveragingMethod(RateAveraging::Type m) {
170+
averagingMethod_ = m;
171+
return *this;
172+
}
173+
174+
MakeMultipleResetsSwap& MakeMultipleResetsSwap::withDiscountingTermStructure(
175+
const Handle<YieldTermStructure>& d) {
176+
engine_ = ext::make_shared<DiscountingSwapEngine>(d, false);
177+
return *this;
178+
}
179+
180+
MakeMultipleResetsSwap& MakeMultipleResetsSwap::withPricingEngine(
181+
const ext::shared_ptr<PricingEngine>& engine) {
182+
engine_ = engine;
183+
return *this;
184+
}
185+
186+
}

0 commit comments

Comments
 (0)