Skip to content

Commit

Permalink
Inherit OIS from fixed-vs-floating swap
Browse files Browse the repository at this point in the history
  • Loading branch information
lballabio committed Sep 18, 2023
1 parent 31252ee commit 4e0942a
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 146 deletions.
15 changes: 13 additions & 2 deletions ql/instruments/fixedvsfloatingswap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,19 @@ namespace QuantLib {
ext::shared_ptr<IborIndex> iborIndex,
Spread spread,
DayCounter floatingDayCount,
ext::optional<BusinessDayConvention> paymentConvention)
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
Expand All @@ -54,7 +61,11 @@ namespace QuantLib {
legs_[0] = FixedRateLeg(fixedSchedule_)
.withNotionals(fixedNominals_)
.withCouponRates(fixedRate_, fixedDayCount_)
.withPaymentAdjustment(paymentConvention_);
.withPaymentAdjustment(paymentConvention_)
.withPaymentLag(paymentLag)
.withPaymentCalendar(paymentCalendar.empty() ?
fixedSchedule_.calendar() :
paymentCalendar);

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

Expand Down
4 changes: 3 additions & 1 deletion ql/instruments/fixedvsfloatingswap.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ namespace QuantLib {
ext::shared_ptr<IborIndex> iborIndex,
Spread spread,
DayCounter floatingDayCount,
ext::optional<BusinessDayConvention> paymentConvention = ext::nullopt);
ext::optional<BusinessDayConvention> paymentConvention = ext::nullopt,
Natural paymentLag = 0,
const Calendar& paymentCalendar = Calendar());
//! \name Inspectors
//@{
Type type() const;
Expand Down
134 changes: 35 additions & 99 deletions ql/instruments/overnightindexedswap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,121 +113,57 @@ namespace QuantLib {
Rate fixedRate,
DayCounter fixedDC,
std::vector<Real> overnightNominals,
Schedule overnightSchedule,
const Schedule& overnightSchedule,
ext::shared_ptr<OvernightIndex> overnightIndex,
Spread spread,
Natural paymentLag,
BusinessDayConvention paymentAdjustment,
const Calendar& paymentCalendar,
bool telescopicValueDates,
RateAveraging::Type averagingMethod)
: Swap(2), type_(type), fixedNominals_(std::move(fixedNominals)),
fixedSchedule_(std::move(fixedSchedule)), fixedRate_(fixedRate), fixedDC_(std::move(fixedDC)),
overnightNominals_(std::move(overnightNominals)), overnightSchedule_(std::move(overnightSchedule)),
overnightIndex_(std::move(overnightIndex)), spread_(spread), averagingMethod_(averagingMethod) {
if (fixedDC_ == DayCounter())
fixedDC_ = overnightIndex_->dayCounter();
legs_[0] = FixedRateLeg(fixedSchedule_)
.withNotionals(fixedNominals_)
.withCouponRates(fixedRate_, fixedDC_)
.withPaymentLag(paymentLag)
.withPaymentAdjustment(paymentAdjustment)
.withPaymentCalendar(paymentCalendar.empty() ? fixedSchedule_.calendar() :
paymentCalendar);
: FixedVsFloatingSwap(type, fixedNominals, fixedSchedule, fixedRate, fixedDC,
overnightNominals, overnightSchedule, overnightIndex,
spread, DayCounter(), ext::nullopt),
overnightIndex_(overnightIndex), averagingMethod_(averagingMethod) {

legs_[1] =
OvernightLeg(overnightSchedule_, overnightIndex_)
.withNotionals(overnightNominals_)
.withSpreads(spread_)
OvernightLeg(overnightSchedule, overnightIndex_)
.withNotionals(overnightNominals)
.withSpreads(spread)
.withTelescopicValueDates(telescopicValueDates)
.withPaymentLag(paymentLag)
.withPaymentAdjustment(paymentAdjustment)
.withPaymentCalendar(paymentCalendar.empty() ? overnightSchedule_.calendar() :
paymentCalendar)
.withPaymentCalendar(paymentCalendar.empty() ?
overnightSchedule.calendar() :
paymentCalendar)
.withAveragingMethod(averagingMethod_);
}

for (Size j = 0; j < 2; ++j) {
for (auto& i : legs_[j])
registerWith(i);
}

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 overnight-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(),
overnightNominals_.begin(), overnightNominals_.end());
if (!sameNominals_) {
constantNominals_ = false;
} else {
constantNominals_ = true;
Real front = fixedNominals_[0];
for (auto x : fixedNominals_) {
if (x != front) {
constantNominals_ = false;
break;
}
void OvernightIndexedSwap::setupFloatingArguments(arguments* args) const {
const Leg& floatingCoupons = floatingLeg();
Size n = floatingCoupons.size();

args->floatingResetDates = args->floatingPayDates = args->floatingFixingDates = std::vector<Date>(n);
args->floatingAccrualTimes = std::vector<Time>(n);
args->floatingSpreads = std::vector<Spread>(n);
args->floatingCoupons = args->floatingNominals = std::vector<Real>(n);

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

args->floatingResetDates[i] = coupon->accrualStartDate();
args->floatingPayDates[i] = coupon->date();
args->floatingNominals[i] = coupon->nominal();

args->floatingFixingDates[i] = coupon->fixingDate();
args->floatingAccrualTimes[i] = coupon->accrualPeriod();
args->floatingSpreads[i] = coupon->spread();
try {
args->floatingCoupons[i] = coupon->amount();
} catch (Error&) {
args->floatingCoupons[i] = Null<Real>();
}
}
}

Real OvernightIndexedSwap::fairRate() const {
static Spread basisPoint = 1.0e-4;
calculate();
return fixedRate_ - NPV_ / (fixedLegBPS() / basisPoint);
}

Spread OvernightIndexedSwap::fairSpread() const {
static Spread basisPoint = 1.0e-4;
calculate();
return spread_ - NPV_ / (overnightLegBPS() / basisPoint);
}

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

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

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

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

Real OvernightIndexedSwap::nominal() const {
QL_REQUIRE(constantNominals_, "varying nominals");
return fixedNominals_[0];
}

const std::vector<Real>& OvernightIndexedSwap::nominals() const {
QL_REQUIRE(sameNominals_, "different nominals on fixed and floating leg");
return fixedNominals_;
}

}
53 changes: 11 additions & 42 deletions ql/instruments/overnightindexedswap.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,18 @@
#define quantlib_overnight_indexed_swap_hpp

#include <ql/cashflows/rateaveraging.hpp>
#include <ql/instruments/swap.hpp>
#include <ql/instruments/fixedvsfloatingswap.hpp>
#include <ql/time/businessdayconvention.hpp>
#include <ql/time/calendar.hpp>
#include <ql/time/daycounter.hpp>
#include <ql/time/schedule.hpp>

namespace QuantLib {

class Schedule;
class OvernightIndex;

//! Overnight indexed swap: fix vs compounded overnight rate
class OvernightIndexedSwap : public Swap {
class OvernightIndexedSwap : public FixedVsFloatingSwap {
public:
OvernightIndexedSwap(Type type,
Real nominal,
Expand Down Expand Up @@ -88,7 +87,7 @@ namespace QuantLib {
Rate fixedRate,
DayCounter fixedDC,
std::vector<Real> overnightNominals,
Schedule overnightSchedule,
const Schedule& overnightSchedule,
ext::shared_ptr<OvernightIndex> overnightIndex,
Spread spread = 0.0,
Natural paymentLag = 0,
Expand All @@ -99,59 +98,29 @@ namespace QuantLib {

//! \name Inspectors
//@{
Type type() const { return type_; }

/*! This throws if the nominal is not constant across coupons. */
Real nominal() const;
/*! This throws if the nominals are not the same for the two legs. */
const std::vector<Real>& nominals() const;

Frequency paymentFrequency() const {
return std::max(fixedSchedule_.tenor().frequency(),
overnightSchedule_.tenor().frequency());
return std::max(fixedSchedule().tenor().frequency(),
floatingSchedule().tenor().frequency());
}

const std::vector<Real>& fixedNominals() const { return fixedNominals_; }
const Schedule& fixedSchedule() const { return fixedSchedule_; }
Rate fixedRate() const { return fixedRate_; }
const DayCounter& fixedDayCount() const { return fixedDC_; }

const std::vector<Real>& overnightNominals() const { return overnightNominals_; }
const Schedule& overnightSchedule() const { return overnightSchedule_; }
const std::vector<Real>& overnightNominals() const { return floatingNominals(); }
const Schedule& overnightSchedule() const { return floatingSchedule(); }
const ext::shared_ptr<OvernightIndex>& overnightIndex() const { return overnightIndex_; }
Spread spread() const { return spread_; }

const Leg& fixedLeg() const { return legs_[0]; }
const Leg& overnightLeg() const { return legs_[1]; }
const Leg& overnightLeg() const { return floatingLeg(); }

RateAveraging::Type averagingMethod() const { return averagingMethod_; }
//@}

//! \name Results
//@{
Real fixedLegBPS() const;
Real fixedLegNPV() const;
Real fairRate() const;

Real overnightLegBPS() const;
Real overnightLegNPV() const;
Spread fairSpread() const;
Real overnightLegBPS() const { return floatingLegBPS(); }
Real overnightLegNPV() const { return floatingLegNPV(); }
//@}
private:
Type type_;
void setupFloatingArguments(arguments* args) const override;

std::vector<Real> fixedNominals_;
Schedule fixedSchedule_;
Rate fixedRate_;
DayCounter fixedDC_;

std::vector<Real> overnightNominals_;
Schedule overnightSchedule_;
ext::shared_ptr<OvernightIndex> overnightIndex_;
Spread spread_;
RateAveraging::Type averagingMethod_;

bool constantNominals_, sameNominals_;
};

}
Expand Down
4 changes: 2 additions & 2 deletions test-suite/overnightindexedswap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ void OvernightIndexedSwapTest::testConstructorsAndNominals() {
BOOST_CHECK_EQUAL(ois_2.paymentFrequency(), Annual);

BOOST_CHECK_EXCEPTION(ois_2.nominal(), Error,
ExpectedErrorMessage("varying nominal"));
ExpectedErrorMessage("nominal is not constant"));

BOOST_CHECK_EQUAL(ois_2.nominals().size(), Size(2));
BOOST_CHECK_EQUAL(ois_2.nominals()[0], nominal);
Expand Down Expand Up @@ -632,7 +632,7 @@ void OvernightIndexedSwapTest::testConstructorsAndNominals() {
BOOST_CHECK_EQUAL(ois_4.paymentFrequency(), Semiannual);

BOOST_CHECK_EXCEPTION(ois_4.nominal(), Error,
ExpectedErrorMessage("varying nominals"));
ExpectedErrorMessage("nominal is not constant"));

BOOST_CHECK_EXCEPTION(ois_4.nominals(), Error,
ExpectedErrorMessage("different nominals"));
Expand Down

0 comments on commit 4e0942a

Please sign in to comment.