Skip to content

Commit

Permalink
Add common fixed-vs-floater base class for vanilla swap and OIS (#1789)
Browse files Browse the repository at this point in the history
  • Loading branch information
lballabio authored Sep 19, 2023
2 parents cd33939 + 4d4d314 commit 6400e4d
Show file tree
Hide file tree
Showing 20 changed files with 608 additions and 490 deletions.
2 changes: 2 additions & 0 deletions QuantLib.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,7 @@
<ClInclude Include="ql\instruments\equitytotalreturnswap.hpp" />
<ClInclude Include="ql\instruments\europeanoption.hpp" />
<ClInclude Include="ql\instruments\fixedratebondforward.hpp" />
<ClInclude Include="ql\instruments\fixedvsfloatingswap.hpp" />
<ClInclude Include="ql\instruments\floatfloatswap.hpp" />
<ClInclude Include="ql\instruments\floatfloatswaption.hpp" />
<ClInclude Include="ql\instruments\forward.hpp" />
Expand Down Expand Up @@ -2183,6 +2184,7 @@
<ClCompile Include="ql\instruments\doublebarriertype.cpp" />
<ClCompile Include="ql\instruments\equitytotalreturnswap.cpp" />
<ClCompile Include="ql\instruments\europeanoption.cpp" />
<ClCompile Include="ql\instruments\fixedvsfloatingswap.cpp" />
<ClCompile Include="ql\instruments\floatfloatswap.cpp" />
<ClCompile Include="ql\instruments\floatfloatswaption.cpp" />
<ClCompile Include="ql\instruments\forward.cpp" />
Expand Down
6 changes: 6 additions & 0 deletions QuantLib.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,9 @@
<ClInclude Include="ql\instruments\fixedratebondforward.hpp">
<Filter>instruments</Filter>
</ClInclude>
<ClInclude Include="ql\instruments\fixedvsfloatingswap.hpp">
<Filter>instruments</Filter>
</ClInclude>
<ClInclude Include="ql\instruments\forward.hpp">
<Filter>instruments</Filter>
</ClInclude>
Expand Down Expand Up @@ -4712,6 +4715,9 @@
<ClCompile Include="ql\instruments\europeanoption.cpp">
<Filter>instruments</Filter>
</ClCompile>
<ClCompile Include="ql\instruments\fixedvsfloatingswap.cpp">
<Filter>instruments</Filter>
</ClCompile>
<ClCompile Include="ql\instruments\forward.cpp">
<Filter>instruments</Filter>
</ClCompile>
Expand Down
2 changes: 2 additions & 0 deletions ql/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ set(QL_SOURCES
instruments/doublebarriertype.cpp
instruments/equitytotalreturnswap.cpp
instruments/europeanoption.cpp
instruments/fixedvsfloatingswap.cpp
instruments/floatfloatswap.cpp
instruments/floatfloatswaption.cpp
instruments/forward.cpp
Expand Down Expand Up @@ -1338,6 +1339,7 @@ set(QL_HEADERS
instruments/equitytotalreturnswap.hpp
instruments/europeanoption.hpp
instruments/fixedratebondforward.hpp
instruments/fixedvsfloatingswap.hpp
instruments/floatfloatswap.hpp
instruments/floatfloatswaption.hpp
instruments/forward.hpp
Expand Down
2 changes: 2 additions & 0 deletions ql/instruments/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ this_include_HEADERS = \
equitytotalreturnswap.hpp \
europeanoption.hpp \
fixedratebondforward.hpp \
fixedvsfloatingswap.hpp \
floatfloatswap.hpp \
floatfloatswaption.hpp \
forward.hpp \
Expand Down Expand Up @@ -100,6 +101,7 @@ cpp_files = \
doublebarriertype.cpp \
equitytotalreturnswap.cpp \
europeanoption.cpp \
fixedvsfloatingswap.cpp \
floatfloatswap.cpp \
floatfloatswaption.cpp \
forward.cpp \
Expand Down
1 change: 1 addition & 0 deletions ql/instruments/all.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <ql/instruments/equitytotalreturnswap.hpp>
#include <ql/instruments/europeanoption.hpp>
#include <ql/instruments/fixedratebondforward.hpp>
#include <ql/instruments/fixedvsfloatingswap.hpp>
#include <ql/instruments/floatfloatswap.hpp>
#include <ql/instruments/floatfloatswaption.hpp>
#include <ql/instruments/forward.hpp>
Expand Down
246 changes: 246 additions & 0 deletions ql/instruments/fixedvsfloatingswap.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*
Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl
Copyright (C) 2003, 2004, 2005, 2006, 2007 StatPro Italia srl
Copyright (C) 2007 Ferdinando Ametrano
This file is part of QuantLib, a free-software/open-source library
for financial quantitative analysts and developers - http://quantlib.org/
QuantLib is free software: you can redistribute it and/or modify it
under the terms of the QuantLib license. You should have received a
copy of the license along with this program; if not, please email
<[email protected]>. The license is also available online at
<http://quantlib.org/license.shtml>.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the license for more details.
*/

#include <ql/cashflows/cashflows.hpp>
#include <ql/cashflows/cashflowvectors.hpp>
#include <ql/cashflows/couponpricer.hpp>
#include <ql/cashflows/fixedratecoupon.hpp>
#include <ql/indexes/iborindex.hpp>
#include <ql/instruments/fixedvsfloatingswap.hpp>
#include <ql/termstructures/yieldtermstructure.hpp>
#include <utility>

namespace QuantLib {

FixedVsFloatingSwap::FixedVsFloatingSwap(Type type,
std::vector<Real> fixedNominals,
Schedule fixedSchedule,
Rate fixedRate,
DayCounter fixedDayCount,
std::vector<Real> floatingNominals,
Schedule floatingSchedule,
ext::shared_ptr<IborIndex> iborIndex,
Spread spread,
DayCounter floatingDayCount,
ext::optional<BusinessDayConvention> paymentConvention,
Natural paymentLag,
const Calendar& paymentCalendar)
: Swap(2), type_(type), fixedNominals_(std::move(fixedNominals)), fixedSchedule_(std::move(fixedSchedule)),
fixedRate_(fixedRate), fixedDayCount_(std::move(fixedDayCount)),
floatingNominals_(std::move(floatingNominals)), floatingSchedule_(std::move(floatingSchedule)),
iborIndex_(std::move(iborIndex)), spread_(spread), floatingDayCount_(std::move(floatingDayCount)) {

QL_REQUIRE(iborIndex_, "null floating index provided");

if (fixedDayCount_ == DayCounter())
fixedDayCount_ = iborIndex_->dayCounter();

if (paymentConvention) // NOLINT(readability-implicit-bool-conversion)
paymentConvention_ = *paymentConvention;
else
paymentConvention_ = floatingSchedule_.businessDayConvention();

legs_[0] = FixedRateLeg(fixedSchedule_)
.withNotionals(fixedNominals_)
.withCouponRates(fixedRate_, fixedDayCount_)
.withPaymentAdjustment(paymentConvention_)
.withPaymentLag(paymentLag)
.withPaymentCalendar(paymentCalendar.empty() ?
fixedSchedule_.calendar() :
paymentCalendar);

// legs_[1] to be built by derived class constructor

switch (type_) {
case Payer:
payer_[0] = -1.0;
payer_[1] = +1.0;
break;
case Receiver:
payer_[0] = +1.0;
payer_[1] = -1.0;
break;
default:
QL_FAIL("Unknown vanilla-swap type");
}


// These bools tell us if we can support the old methods nominal() and nominals().
// There might be false negatives (i.e., if we pass constant vectors of different lengths
// as fixedNominals and floatingNominals) but we're going to assume that whoever uses the
// constructor with two vectors is mostly going to use the new methods instead.
sameNominals_ = std::equal(fixedNominals_.begin(), fixedNominals_.end(),
floatingNominals_.begin(), floatingNominals_.end());
if (!sameNominals_) {
constantNominals_ = false;
} else {
constantNominals_ = true;
Real front = fixedNominals_[0];
for (auto x : fixedNominals_) {
if (x != front) {
constantNominals_ = false;
break;
}
}
}
}

void FixedVsFloatingSwap::setupArguments(PricingEngine::arguments* args) const {

Swap::setupArguments(args);

auto* arguments = dynamic_cast<FixedVsFloatingSwap::arguments*>(args);

if (arguments == nullptr) // it's a swap engine...
return;

arguments->type = type_;

if (constantNominals_)
arguments->nominal = nominal();
else
arguments->nominal = Null<Real>();

const Leg& fixedCoupons = fixedLeg();
Size n = fixedCoupons.size();

arguments->fixedResetDates = arguments->fixedPayDates = std::vector<Date>(n);
arguments->fixedNominals = arguments->fixedCoupons = std::vector<Real>(n);

for (Size i=0; i<n; ++i) {
auto coupon = ext::dynamic_pointer_cast<FixedRateCoupon>(fixedCoupons[i]);

arguments->fixedPayDates[i] = coupon->date();
arguments->fixedResetDates[i] = coupon->accrualStartDate();
arguments->fixedCoupons[i] = coupon->amount();
arguments->fixedNominals[i] = coupon->nominal();
}

setupFloatingArguments(arguments);
}

Rate FixedVsFloatingSwap::fairRate() const {
calculate();
QL_REQUIRE(fairRate_ != Null<Rate>(), "result not available");
return fairRate_;
}

Spread FixedVsFloatingSwap::fairSpread() const {
calculate();
QL_REQUIRE(fairSpread_ != Null<Spread>(), "result not available");
return fairSpread_;
}

Real FixedVsFloatingSwap::fixedLegBPS() const {
calculate();
QL_REQUIRE(legBPS_[0] != Null<Real>(), "result not available");
return legBPS_[0];
}

Real FixedVsFloatingSwap::floatingLegBPS() const {
calculate();
QL_REQUIRE(legBPS_[1] != Null<Real>(), "result not available");
return legBPS_[1];
}

Real FixedVsFloatingSwap::fixedLegNPV() const {
calculate();
QL_REQUIRE(legNPV_[0] != Null<Real>(), "result not available");
return legNPV_[0];
}

Real FixedVsFloatingSwap::floatingLegNPV() const {
calculate();
QL_REQUIRE(legNPV_[1] != Null<Real>(), "result not available");
return legNPV_[1];
}

void FixedVsFloatingSwap::setupExpired() const {
Swap::setupExpired();
legBPS_[0] = legBPS_[1] = 0.0;
fairRate_ = Null<Rate>();
fairSpread_ = Null<Spread>();
}

void FixedVsFloatingSwap::fetchResults(const PricingEngine::results* r) const {
static const Spread basisPoint = 1.0e-4;

Swap::fetchResults(r);

const auto* results = dynamic_cast<const FixedVsFloatingSwap::results*>(r);
if (results != nullptr) { // might be a swap engine, so no error is thrown
fairRate_ = results->fairRate;
fairSpread_ = results->fairSpread;
} else {
fairRate_ = Null<Rate>();
fairSpread_ = Null<Spread>();
}

if (fairRate_ == Null<Rate>()) {
// calculate it from other results
if (legBPS_[0] != Null<Real>())
fairRate_ = fixedRate_ - NPV_/(legBPS_[0]/basisPoint);
}
if (fairSpread_ == Null<Spread>()) {
// ditto
if (legBPS_[1] != Null<Real>())
fairSpread_ = spread_ - NPV_/(legBPS_[1]/basisPoint);
}
}

void FixedVsFloatingSwap::arguments::validate() const {
Swap::arguments::validate();
QL_REQUIRE(fixedNominals.size() == fixedPayDates.size(),
"number of fixed nominals different from "
"number of fixed payment dates");
QL_REQUIRE(fixedResetDates.size() == fixedPayDates.size(),
"number of fixed start dates different from "
"number of fixed payment dates");
QL_REQUIRE(fixedPayDates.size() == fixedCoupons.size(),
"number of fixed payment dates different from "
"number of fixed coupon amounts");
QL_REQUIRE(floatingNominals.size() == floatingPayDates.size(),
"number of floating nominals different from "
"number of floating payment dates");
QL_REQUIRE(floatingResetDates.size() == floatingPayDates.size(),
"number of floating start dates different from "
"number of floating payment dates");
QL_REQUIRE(floatingFixingDates.size() == floatingPayDates.size(),
"number of floating fixing dates different from "
"number of floating payment dates");
QL_REQUIRE(floatingAccrualTimes.size() == floatingPayDates.size(),
"number of floating accrual Times different from "
"number of floating payment dates");
QL_REQUIRE(floatingSpreads.size() == floatingPayDates.size(),
"number of floating spreads different from "
"number of floating payment dates");
QL_REQUIRE(floatingPayDates.size() == floatingCoupons.size(),
"number of floating payment dates different from "
"number of floating coupon amounts");
}

void FixedVsFloatingSwap::results::reset() {
Swap::results::reset();
fairRate = Null<Rate>();
fairSpread = Null<Spread>();
}

}
Loading

0 comments on commit 6400e4d

Please sign in to comment.