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 b052a36
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 151 deletions.
203 changes: 92 additions & 111 deletions ql/termstructures/yield/oisratehelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,85 @@

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>::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);

Base::setTermStructure(t);
}

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

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 +129,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 +173,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 +202,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 +236,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 +261,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
78 changes: 38 additions & 40 deletions ql/termstructures/yield/oisratehelper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,41 @@ namespace QuantLib {

class FloatingRateCouponPricer;

namespace detail {
template <class Base>
class OISRateHelperBase : public Base {
public:
OISRateHelperBase(const Handle<Quote>& fixedRate,
const ext::shared_ptr<OvernightIndex>& overnightIndex,
Handle<YieldTermStructure> discountingCurve = {},
Pillar::Choice pillar = Pillar::LastRelevantDate,
Date customPillarDate = Date());
//! \name RateHelper interface
//@{
Real impliedQuote() const override;
void setTermStructure(YieldTermStructure*) override;
//@}
//! \name OISRateHelperBase inspectors
//@{
// NOLINTNEXTLINE(cppcoreguidelines-noexcept-swap,performance-noexcept-swap)
ext::shared_ptr<OvernightIndexedSwap> swap() const { return swap_; }
//@}
protected:
void setPillarDate();

using Base::earliestDate_, Base::latestDate_;
using Base::maturityDate_, Base::latestRelevantDate_, Base::pillarDate_;
ext::shared_ptr<OvernightIndex> overnightIndex_;
ext::shared_ptr<OvernightIndexedSwap> swap_;
RelinkableHandle<YieldTermStructure> termStructureHandle_;
Handle<YieldTermStructure> discountHandle_;
RelinkableHandle<YieldTermStructure> discountRelinkableHandle_;
Pillar::Choice pillarChoice_;
};
}

//! Rate helper for bootstrapping over Overnight Indexed Swap rates
class OISRateHelper : public RelativeDateRateHelper {
class OISRateHelper : public detail::OISRateHelperBase<RelativeDateRateHelper> {
public:
OISRateHelper(Natural settlementDays,
const Period& tenor, // swap maturity
Expand All @@ -59,35 +92,16 @@ namespace QuantLib {
Natural lockoutDays = 0,
bool applyObservationShift = false,
ext::shared_ptr<FloatingRateCouponPricer> pricer = {});
//! \name RateHelper interface
//@{
Real impliedQuote() const override;
void setTermStructure(YieldTermStructure*) override;
//@}
//! \name inspectors
//@{
// NOLINTNEXTLINE(cppcoreguidelines-noexcept-swap,performance-noexcept-swap)
ext::shared_ptr<OvernightIndexedSwap> swap() const { return swap_; }
//@}
//! \name Visitability
//@{
void accept(AcyclicVisitor&) override;
//@}
protected:
void initializeDates() override;
Pillar::Choice pillarChoice_;

Natural settlementDays_;
Period tenor_;
ext::shared_ptr<OvernightIndex> overnightIndex_;

ext::shared_ptr<OvernightIndexedSwap> swap_;
RelinkableHandle<YieldTermStructure> termStructureHandle_;

Handle<YieldTermStructure> discountHandle_;
bool telescopicValueDates_;
RelinkableHandle<YieldTermStructure> discountRelinkableHandle_;

Integer paymentLag_;
BusinessDayConvention paymentConvention_;
Frequency paymentFrequency_;
Expand All @@ -105,7 +119,7 @@ namespace QuantLib {
};

//! Rate helper for bootstrapping over Overnight Indexed Swap rates
class DatedOISRateHelper : public RateHelper {
class DatedOISRateHelper : public detail::OISRateHelperBase<RateHelper> {
public:
DatedOISRateHelper(const Date& startDate,
const Date& endDate,
Expand All @@ -126,7 +140,9 @@ namespace QuantLib {
Natural lookbackDays = Null<Natural>(),
Natural lockoutDays = 0,
bool applyObservationShift = false,
const ext::shared_ptr<FloatingRateCouponPricer>& pricer = {});
const ext::shared_ptr<FloatingRateCouponPricer>& pricer = {},
Pillar::Choice pillar = Pillar::LastRelevantDate,
Date customPillarDate = Date());

/*! \deprecated Use the overload without forward start.
Deprecated in version 1.35.
Expand All @@ -150,29 +166,11 @@ namespace QuantLib {
ext::optional<Frequency> fixedPaymentFrequency = ext::nullopt,
const Calendar& fixedCalendar = Calendar());

//! \name RateHelper interface
//@{
Real impliedQuote() const override;
void setTermStructure(YieldTermStructure*) override;
//@}
//! \name inspectors
//@{
// NOLINTNEXTLINE(cppcoreguidelines-noexcept-swap,performance-noexcept-swap)
ext::shared_ptr<OvernightIndexedSwap> swap() const { return swap_; }
//@}
//@}
//! \name Visitability
//@{
void accept(AcyclicVisitor&) override;
//@}
protected:
ext::shared_ptr<OvernightIndexedSwap> swap_;
RelinkableHandle<YieldTermStructure> termStructureHandle_;

Handle<YieldTermStructure> discountHandle_;
bool telescopicValueDates_;
RelinkableHandle<YieldTermStructure> discountRelinkableHandle_;
RateAveraging::Type averagingMethod_;
};

}
Expand Down

0 comments on commit b052a36

Please sign in to comment.