Skip to content

Commit

Permalink
Support custom pillar dates in DatedOISRateHelper
Browse files Browse the repository at this point in the history
Also, factor out common code into a helper base class to avoid adding
more copy-paste.
  • Loading branch information
eltoder committed Oct 23, 2024
1 parent 5ee8f89 commit b2654b6
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 152 deletions.
179 changes: 67 additions & 112 deletions ql/termstructures/yield/oisratehelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,64 @@
#include <ql/cashflows/couponpricer.hpp>
#include <ql/pricingengines/swap/discountingswapengine.hpp>
#include <ql/termstructures/yield/oisratehelper.hpp>
#include <ql/utilities/null_deleter.hpp>
#include <utility>

namespace QuantLib {

namespace detail {
template <class Base>
OISRateHelperBase<Base>::OISRateHelperBase(
const Handle<Quote>& fixedRate,
const ext::shared_ptr<OvernightIndex>& overnightIndex,
Handle<YieldTermStructure> discountingCurve,
Pillar::Choice pillar,
Date customPillarDate)
: Base(fixedRate), discountHandle_(std::move(discountingCurve)), pillarChoice_(pillar) {
overnightIndex_ = ext::dynamic_pointer_cast<OvernightIndex>(
overnightIndex->clone(termStructureHandle_));
// We want to be notified of changes of fixings, but we don't
// want notifications from termStructureHandle_ (they would
// interfere with bootstrapping.)
overnightIndex_->unregisterWith(termStructureHandle_);

this->registerWith(overnightIndex_);
this->registerWith(discountHandle_);

pillarDate_ = customPillarDate;
}

template <class Base>
void OISRateHelperBase<Base>::setPillarDate() {
earliestDate_ = swap_->startDate();
maturityDate_ = swap_->maturityDate();
Date lastPaymentDate = std::max(swap_->overnightLeg().back()->date(),
swap_->fixedLeg().back()->date());
latestRelevantDate_ = latestDate_ = std::max(maturityDate_, lastPaymentDate);

switch (pillarChoice_) {
case Pillar::MaturityDate:
pillarDate_ = maturityDate_;
break;
case Pillar::LastRelevantDate:
pillarDate_ = latestRelevantDate_;
break;
case Pillar::CustomDate:
// pillarDate_ already assigned at construction time
QL_REQUIRE(pillarDate_ >= earliestDate_,
"pillar date (" << pillarDate_ << ") must be later "
"than or equal to the instrument's earliest date (" <<
earliestDate_ << ")");
QL_REQUIRE(pillarDate_ <= latestRelevantDate_,
"pillar date (" << pillarDate_ << ") must be before "
"or equal to the instrument's latest relevant date (" <<
latestRelevantDate_ << ")");
break;
default:
QL_FAIL("unknown Pillar::Choice(" << Integer(pillarChoice_) << ")");
}
}
}

OISRateHelper::OISRateHelper(Natural settlementDays,
const Period& tenor, // swap maturity
const Handle<Quote>& fixedRate,
Expand All @@ -50,32 +103,19 @@ namespace QuantLib {
Natural lockoutDays,
bool applyObservationShift,
ext::shared_ptr<FloatingRateCouponPricer> pricer)
: RelativeDateRateHelper(fixedRate), pillarChoice_(pillar), settlementDays_(settlementDays), tenor_(tenor),
discountHandle_(std::move(discount)), telescopicValueDates_(telescopicValueDates),
: OISRateHelperBase(fixedRate, overnightIndex, std::move(discount), pillar, customPillarDate),
settlementDays_(settlementDays), tenor_(tenor), telescopicValueDates_(telescopicValueDates),
paymentLag_(paymentLag), paymentConvention_(paymentConvention),
paymentFrequency_(paymentFrequency), paymentCalendar_(std::move(paymentCalendar)),
forwardStart_(forwardStart), overnightSpread_(overnightSpread),
averagingMethod_(averagingMethod), endOfMonth_(endOfMonth),
fixedPaymentFrequency_(fixedPaymentFrequency), fixedCalendar_(std::move(fixedCalendar)),
lookbackDays_(lookbackDays), lockoutDays_(lockoutDays), applyObservationShift_(applyObservationShift),
pricer_(std::move(pricer)) {

overnightIndex_ =
ext::dynamic_pointer_cast<OvernightIndex>(overnightIndex->clone(termStructureHandle_));
// We want to be notified of changes of fixings, but we don't
// want notifications from termStructureHandle_ (they would
// interfere with bootstrapping.)
overnightIndex_->unregisterWith(termStructureHandle_);

registerWith(overnightIndex_);
registerWith(discountHandle_);

pillarDate_ = customPillarDate;
OISRateHelper::initializeDates();
}

void OISRateHelper::initializeDates() {

// input discount curve Handle might be empty now but it could
// be assigned a curve later; use a RelinkableHandle here
MakeOIS tmp = MakeOIS(tenor_, overnightIndex_, 0.0, forwardStart_)
Expand Down Expand Up @@ -107,57 +147,7 @@ namespace QuantLib {

simplifyNotificationGraph(*swap_, true);

earliestDate_ = swap_->startDate();
maturityDate_ = swap_->maturityDate();

Date lastPaymentDate = std::max(swap_->overnightLeg().back()->date(),
swap_->fixedLeg().back()->date());
latestRelevantDate_ = latestDate_ = std::max(maturityDate_, lastPaymentDate);

switch (pillarChoice_) {
case Pillar::MaturityDate:
pillarDate_ = maturityDate_;
break;
case Pillar::LastRelevantDate:
pillarDate_ = latestRelevantDate_;
break;
case Pillar::CustomDate:
// pillarDate_ already assigned at construction time
QL_REQUIRE(pillarDate_ >= earliestDate_,
"pillar date (" << pillarDate_ << ") must be later "
"than or equal to the instrument's earliest date (" <<
earliestDate_ << ")");
QL_REQUIRE(pillarDate_ <= latestRelevantDate_,
"pillar date (" << pillarDate_ << ") must be before "
"or equal to the instrument's latest relevant date (" <<
latestRelevantDate_ << ")");
break;
default:
QL_FAIL("unknown Pillar::Choice(" << Integer(pillarChoice_) << ")");
}
}

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

ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
termStructureHandle_.linkTo(temp, observer);

if (discountHandle_.empty())
discountRelinkableHandle_.linkTo(temp, observer);
else
discountRelinkableHandle_.linkTo(*discountHandle_, observer);

RelativeDateRateHelper::setTermStructure(t);
}

Real OISRateHelper::impliedQuote() const {
QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
// we didn't register as observers - force calculation
swap_->deepUpdate();
return swap_->fairRate();
setPillarDate();
}

void OISRateHelper::accept(AcyclicVisitor& v) {
Expand Down Expand Up @@ -186,33 +176,23 @@ namespace QuantLib {
Natural lookbackDays,
Natural lockoutDays,
bool applyObservationShift,
const ext::shared_ptr<FloatingRateCouponPricer>& pricer)
: RateHelper(fixedRate), discountHandle_(std::move(discount)),
telescopicValueDates_(telescopicValueDates), averagingMethod_(averagingMethod) {

auto clonedOvernightIndex =
ext::dynamic_pointer_cast<OvernightIndex>(overnightIndex->clone(termStructureHandle_));
// We want to be notified of changes of fixings, but we don't
// want notifications from termStructureHandle_ (they would
// interfere with bootstrapping.)
clonedOvernightIndex->unregisterWith(termStructureHandle_);

registerWith(clonedOvernightIndex);
registerWith(discountHandle_);

const ext::shared_ptr<FloatingRateCouponPricer>& pricer,
Pillar::Choice pillar,
Date customPillarDate)
: OISRateHelperBase(fixedRate, overnightIndex, std::move(discount), pillar, customPillarDate) {
// input discount curve Handle might be empty now but it could
// be assigned a curve later; use a RelinkableHandle here
auto tmp = MakeOIS(Period(), clonedOvernightIndex, 0.0)
auto tmp = MakeOIS(Period(), overnightIndex_, 0.0)
.withDiscountingTermStructure(discountRelinkableHandle_)
.withEffectiveDate(startDate)
.withTerminationDate(endDate)
.withTelescopicValueDates(telescopicValueDates_)
.withTelescopicValueDates(telescopicValueDates)
.withPaymentLag(paymentLag)
.withPaymentAdjustment(paymentConvention)
.withPaymentFrequency(paymentFrequency)
.withPaymentCalendar(paymentCalendar)
.withOvernightLegSpread(overnightSpread)
.withAveragingMethod(averagingMethod_)
.withAveragingMethod(averagingMethod)
.withLookbackDays(lookbackDays)
.withLockoutDays(lockoutDays)
.withObservationShift(applyObservationShift);
Expand All @@ -230,11 +210,9 @@ namespace QuantLib {
if (pricer)
setCouponPricer(swap_->overnightLeg(), pricer);

earliestDate_ = swap_->startDate();
maturityDate_ = swap_->maturityDate();
Date lastPaymentDate = std::max(swap_->overnightLeg().back()->date(),
swap_->fixedLeg().back()->date());
latestRelevantDate_ = latestDate_ = std::max(maturityDate_, lastPaymentDate);
simplifyNotificationGraph(*swap_, true);

setPillarDate();
}

DatedOISRateHelper::DatedOISRateHelper(const Date& startDate,
Expand All @@ -257,29 +235,6 @@ namespace QuantLib {
averagingMethod, paymentLag, paymentConvention, paymentFrequency, paymentCalendar,
overnightSpread, endOfMonth, fixedPaymentFrequency, fixedCalendar) {}

void DatedOISRateHelper::setTermStructure(YieldTermStructure* t) {
// do not set the relinkable handle as an observer -
// force recalculation when needed
bool observer = false;

ext::shared_ptr<YieldTermStructure> temp(t, null_deleter());
termStructureHandle_.linkTo(temp, observer);

if (discountHandle_.empty())
discountRelinkableHandle_.linkTo(temp, observer);
else
discountRelinkableHandle_.linkTo(*discountHandle_, observer);

RateHelper::setTermStructure(t);
}

Real DatedOISRateHelper::impliedQuote() const {
QL_REQUIRE(termStructure_ != nullptr, "term structure not set");
// we didn't register as observers - force calculation
swap_->deepUpdate();
return swap_->fairRate();
}

void DatedOISRateHelper::accept(AcyclicVisitor& v) {
auto* v1 = dynamic_cast<Visitor<DatedOISRateHelper>*>(&v);
if (v1 != nullptr)
Expand Down
Loading

0 comments on commit b2654b6

Please sign in to comment.