From 2b16fb854ebe8c75cdb781af63ed1110797cd7e7 Mon Sep 17 00:00:00 2001 From: Luigi Ballabio Date: Thu, 16 Mar 2023 12:29:57 +0100 Subject: [PATCH 1/6] Add fixed vs floating swap; inherit VanillaSwap --- QuantLib.vcxproj | 4 +- QuantLib.vcxproj.filters | 8 +- ql/CMakeLists.txt | 2 + ql/instruments/Makefile.am | 2 + ql/instruments/all.hpp | 1 + ql/instruments/fixedvsfloatingswap.cpp | 204 +++++++++++++++++++++++ ql/instruments/fixedvsfloatingswap.hpp | 217 +++++++++++++++++++++++++ ql/instruments/vanillaswap.cpp | 211 +++--------------------- ql/instruments/vanillaswap.hpp | 140 +--------------- 9 files changed, 466 insertions(+), 323 deletions(-) create mode 100644 ql/instruments/fixedvsfloatingswap.cpp create mode 100644 ql/instruments/fixedvsfloatingswap.hpp diff --git a/QuantLib.vcxproj b/QuantLib.vcxproj index 343f19a709f..1b63f42ef97 100644 --- a/QuantLib.vcxproj +++ b/QuantLib.vcxproj @@ -918,6 +918,7 @@ + @@ -2182,6 +2183,7 @@ + @@ -2830,4 +2832,4 @@ - \ No newline at end of file + diff --git a/QuantLib.vcxproj.filters b/QuantLib.vcxproj.filters index 08e4a18bec3..aae49b12f6f 100644 --- a/QuantLib.vcxproj.filters +++ b/QuantLib.vcxproj.filters @@ -822,6 +822,9 @@ instruments + + instruments + instruments @@ -4677,6 +4680,9 @@ instruments + + instruments + instruments @@ -7184,4 +7190,4 @@ time\daycounters - \ No newline at end of file + diff --git a/ql/CMakeLists.txt b/ql/CMakeLists.txt index 98383124e05..585b69f5400 100644 --- a/ql/CMakeLists.txt +++ b/ql/CMakeLists.txt @@ -290,6 +290,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 @@ -1331,6 +1332,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 diff --git a/ql/instruments/Makefile.am b/ql/instruments/Makefile.am index cc6280ec23e..57ce05a18ec 100644 --- a/ql/instruments/Makefile.am +++ b/ql/instruments/Makefile.am @@ -31,6 +31,7 @@ this_include_HEADERS = \ equitytotalreturnswap.hpp \ europeanoption.hpp \ fixedratebondforward.hpp \ + fixedvsfloatingswap.hpp \ floatfloatswap.hpp \ floatfloatswaption.hpp \ forward.hpp \ @@ -93,6 +94,7 @@ cpp_files = \ doublebarriertype.cpp \ equitytotalreturnswap.cpp \ europeanoption.cpp \ + fixedvsfloatingswap.cpp \ floatfloatswap.cpp \ floatfloatswaption.cpp \ forward.cpp \ diff --git a/ql/instruments/all.hpp b/ql/instruments/all.hpp index 08db8903bed..53726bd8f52 100644 --- a/ql/instruments/all.hpp +++ b/ql/instruments/all.hpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/ql/instruments/fixedvsfloatingswap.cpp b/ql/instruments/fixedvsfloatingswap.cpp new file mode 100644 index 00000000000..620e07bc4f6 --- /dev/null +++ b/ql/instruments/fixedvsfloatingswap.cpp @@ -0,0 +1,204 @@ +/* -*- 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 + . The license is also available online at + . + + 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 +#include +#include +#include +#include +#include +#include +#include + +namespace QuantLib { + + FixedVsFloatingSwap::FixedVsFloatingSwap(Type type, + Real nominal, + Schedule fixedSchedule, + Rate fixedRate, + DayCounter fixedDayCount, + Schedule floatSchedule, + ext::shared_ptr iborIndex, + Spread spread, + DayCounter floatingDayCount, + ext::optional paymentConvention) + : Swap(2), type_(type), nominal_(nominal), fixedSchedule_(std::move(fixedSchedule)), + fixedRate_(fixedRate), fixedDayCount_(std::move(fixedDayCount)), + floatingSchedule_(std::move(floatSchedule)), iborIndex_(std::move(iborIndex)), + spread_(spread), floatingDayCount_(std::move(floatingDayCount)) { + + if (paymentConvention) // NOLINT(readability-implicit-bool-conversion) + paymentConvention_ = *paymentConvention; + else + paymentConvention_ = floatingSchedule_.businessDayConvention(); + + legs_[0] = FixedRateLeg(fixedSchedule_) + .withNotionals(nominal_) + .withCouponRates(fixedRate_, fixedDayCount_) + .withPaymentAdjustment(paymentConvention_); + + // 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"); + } + } + + void FixedVsFloatingSwap::setupArguments(PricingEngine::arguments* args) const { + + Swap::setupArguments(args); + + auto* arguments = dynamic_cast(args); + + if (arguments == nullptr) // it's a swap engine... + return; + + arguments->type = type_; + arguments->nominal = nominal_; + + const Leg& fixedCoupons = fixedLeg(); + Size n = fixedCoupons.size(); + + arguments->fixedResetDates = arguments->fixedPayDates = std::vector(n); + arguments->fixedCoupons = std::vector(n); + + for (Size i=0; i(fixedCoupons[i]); + + arguments->fixedPayDates[i] = coupon->date(); + arguments->fixedResetDates[i] = coupon->accrualStartDate(); + arguments->fixedCoupons[i] = coupon->amount(); + } + + setupFloatingArguments(arguments); + } + + Rate FixedVsFloatingSwap::fairRate() const { + calculate(); + QL_REQUIRE(fairRate_ != Null(), "result not available"); + return fairRate_; + } + + Spread FixedVsFloatingSwap::fairSpread() const { + calculate(); + QL_REQUIRE(fairSpread_ != Null(), "result not available"); + return fairSpread_; + } + + Real FixedVsFloatingSwap::fixedLegBPS() const { + calculate(); + QL_REQUIRE(legBPS_[0] != Null(), "result not available"); + return legBPS_[0]; + } + + Real FixedVsFloatingSwap::floatingLegBPS() const { + calculate(); + QL_REQUIRE(legBPS_[1] != Null(), "result not available"); + return legBPS_[1]; + } + + Real FixedVsFloatingSwap::fixedLegNPV() const { + calculate(); + QL_REQUIRE(legNPV_[0] != Null(), "result not available"); + return legNPV_[0]; + } + + Real FixedVsFloatingSwap::floatingLegNPV() const { + calculate(); + QL_REQUIRE(legNPV_[1] != Null(), "result not available"); + return legNPV_[1]; + } + + void FixedVsFloatingSwap::setupExpired() const { + Swap::setupExpired(); + legBPS_[0] = legBPS_[1] = 0.0; + fairRate_ = Null(); + fairSpread_ = Null(); + } + + void FixedVsFloatingSwap::fetchResults(const PricingEngine::results* r) const { + static const Spread basisPoint = 1.0e-4; + + Swap::fetchResults(r); + + const auto* results = dynamic_cast(r); + if (results != nullptr) { // might be a swap engine, so no error is thrown + fairRate_ = results->fairRate; + fairSpread_ = results->fairSpread; + } else { + fairRate_ = Null(); + fairSpread_ = Null(); + } + + if (fairRate_ == Null()) { + // calculate it from other results + if (legBPS_[0] != Null()) + fairRate_ = fixedRate_ - NPV_/(legBPS_[0]/basisPoint); + } + if (fairSpread_ == Null()) { + // ditto + if (legBPS_[1] != Null()) + fairSpread_ = spread_ - NPV_/(legBPS_[1]/basisPoint); + } + } + + void FixedVsFloatingSwap::arguments::validate() const { + Swap::arguments::validate(); + QL_REQUIRE(nominal != Null(), "nominal null or not set"); + 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(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(); + fairSpread = Null(); + } + +} diff --git a/ql/instruments/fixedvsfloatingswap.hpp b/ql/instruments/fixedvsfloatingswap.hpp new file mode 100644 index 00000000000..be795857850 --- /dev/null +++ b/ql/instruments/fixedvsfloatingswap.hpp @@ -0,0 +1,217 @@ +/* -*- 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) 2006, 2008 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 + . The license is also available online at + . + + 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. +*/ + +/*! \file fixedvsfloatingswap.hpp + \brief Fixed-rate vs floating-rate swap +*/ + +#ifndef quantlib_fixed_vs_floating_swap_hpp +#define quantlib_fixed_vs_floating_swap_hpp + +#include +#include +#include +#include + +namespace QuantLib { + + class IborIndex; + + //! Fixed vs floating swap + /*! \ingroup instruments + + If no payment convention is passed, the convention of the + floating-rate schedule is used. + + \warning if Settings::includeReferenceDateCashFlows() + is set to true, payments occurring at the + settlement date of the swap might be included in the + NPV and therefore affect the fair-rate and + fair-spread calculation. This might not be what you + want. + + \test + - the correctness of the returned value is tested by checking + that the price of a swap paying the fair fixed rate is null. + - the correctness of the returned value is tested by checking + that the price of a swap receiving the fair floating-rate + spread is null. + - the correctness of the returned value is tested by checking + that the price of a swap decreases with the paid fixed rate. + - the correctness of the returned value is tested by checking + that the price of a swap increases with the received + floating-rate spread. + - the correctness of the returned value is tested by checking + it against a known good value. + */ + class FixedVsFloatingSwap : public Swap { + public: + class arguments; + class results; + class engine; + FixedVsFloatingSwap(Type type, + Real nominal, + Schedule fixedSchedule, + Rate fixedRate, + DayCounter fixedDayCount, + Schedule floatSchedule, + ext::shared_ptr iborIndex, + Spread spread, + DayCounter floatingDayCount, + ext::optional paymentConvention = ext::nullopt); + //! \name Inspectors + //@{ + Type type() const; + Real nominal() const; + + const Schedule& fixedSchedule() const; + Rate fixedRate() const; + const DayCounter& fixedDayCount() const; + + const Schedule& floatingSchedule() const; + const ext::shared_ptr& iborIndex() const; + Spread spread() const; + const DayCounter& floatingDayCount() const; + + BusinessDayConvention paymentConvention() const; + + const Leg& fixedLeg() const; + const Leg& floatingLeg() const; + //@} + + //! \name Results + //@{ + Real fixedLegBPS() const; + Real fixedLegNPV() const; + Rate fairRate() const; + + Real floatingLegBPS() const; + Real floatingLegNPV() const; + Spread fairSpread() const; + //@} + // other + void setupArguments(PricingEngine::arguments* args) const override; + void fetchResults(const PricingEngine::results*) const override; + + private: + void setupExpired() const override; + virtual void setupFloatingArguments(arguments* args) const = 0; + Type type_; + Real nominal_; + Schedule fixedSchedule_; + Rate fixedRate_; + DayCounter fixedDayCount_; + Schedule floatingSchedule_; + ext::shared_ptr iborIndex_; + Spread spread_; + DayCounter floatingDayCount_; + BusinessDayConvention paymentConvention_; + // results + mutable Rate fairRate_; + mutable Spread fairSpread_; + }; + + + //! %Arguments for simple swap calculation + class FixedVsFloatingSwap::arguments : public Swap::arguments { + public: + arguments() : nominal(Null()) {} + Type type = Receiver; + Real nominal; + + std::vector fixedResetDates; + std::vector fixedPayDates; + std::vector