Skip to content

Commit 5fcdcbd

Browse files
committed
QPR-13648 update pillar only yield curve
1 parent 27db5c6 commit 5fcdcbd

1 file changed

Lines changed: 84 additions & 44 deletions

File tree

QuantExt/qle/termstructures/pillaronlyyieldcurve.hpp

Lines changed: 84 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
#include <ql/math/comparison.hpp>
2727
#include <ql/termstructures/interpolatedcurve.hpp>
28+
#include <ql/termstructures/yield/forwardstructure.hpp>
29+
#include <ql/termstructures/yield/zeroyieldstructure.hpp>
2830
#include <ql/termstructures/yieldtermstructure.hpp>
2931

3032
namespace QuantExt {
@@ -86,7 +88,7 @@ class InterpolatedPillarOnlyDiscountCurve : public YieldTermStructure, protected
8688
\ingroup termstructures
8789
*/
8890
template <class Interpolator>
89-
class InterpolatedPillarOnlyZeroCurve : public YieldTermStructure, protected InterpolatedCurve<Interpolator> {
91+
class InterpolatedPillarOnlyZeroCurve : public ZeroYieldStructure, protected InterpolatedCurve<Interpolator> {
9092
public:
9193
//! \name Constructors
9294
//@{
@@ -102,9 +104,9 @@ class InterpolatedPillarOnlyZeroCurve : public YieldTermStructure, protected Int
102104
//@}
103105

104106
protected:
105-
//! \name YieldTermStructure implementation
107+
//! \name ZeroYieldStructure implementation
106108
//@{
107-
DiscountFactor discountImpl(Time t) const override;
109+
DiscountFactor zeroYieldImpl(Time t) const override;
108110
//@}
109111

110112
private:
@@ -126,7 +128,7 @@ class InterpolatedPillarOnlyZeroCurve : public YieldTermStructure, protected Int
126128
\ingroup termstructures
127129
*/
128130
template <class Interpolator>
129-
class InterpolatedPillarOnlyForwardCurve : public YieldTermStructure, protected InterpolatedCurve<Interpolator> {
131+
class InterpolatedPillarOnlyForwardCurve : public ForwardRateStructure, protected InterpolatedCurve<Interpolator> {
130132
public:
131133
//! \name Constructors
132134
//@{
@@ -142,9 +144,10 @@ class InterpolatedPillarOnlyForwardCurve : public YieldTermStructure, protected
142144
//@}
143145

144146
protected:
145-
//! \name YieldTermStructure implementation
147+
//! \name ForwardRateStructure implementation
146148
//@{
147-
DiscountFactor discountImpl(Time t) const override;
149+
Rate forwardImpl(Time t) const override;
150+
Rate zeroYieldImpl(Time t) const override;
148151
//@}
149152

150153
private:
@@ -200,21 +203,29 @@ DiscountFactor InterpolatedPillarOnlyDiscountCurve<Interpolator>::discountImpl(T
200203

201204
// Interpolate on pillars for times within the curve range
202205
if (t <= this->times_.back()) {
203-
return this->interpolation_(t, this->allowsExtrapolation());
206+
return this->interpolation_(t, true);
204207
}
205208

206209
// Flat forward extrapolation beyond the last pillar
207210
Time tMax = this->times_.back();
208211
DiscountFactor dMax = this->data_.back();
209-
Rate instFwdMax = -this->interpolation_.derivative(tMax) / dMax;
210-
return dMax * std::exp(-instFwdMax * (t - tMax));
212+
if (extrapolation_ == YieldTermStructure::Extrapolation::ContinuousForward) {
213+
Rate instFwdMax = -this->interpolation_.derivative(tMax, true) / dMax;
214+
return dMax * std::exp(-instFwdMax * (t - tMax));
215+
} else if (extrapolation_ == YieldTermStructure::Extrapolation::DiscreteForward) {
216+
Time tMax_m = this->timeFromReference(dates_.back() - 1);
217+
DiscountFactor dMax_m = this->interpolation_(tMax_m, true);
218+
return dMax * std::pow(dMax / dMax_m, (t - tMax) / (tMax - tMax_m));
219+
} else {
220+
QL_FAIL("extrapolation method not handled.");
221+
}
211222
}
212223

213224
template <class Interpolator>
214225
InterpolatedPillarOnlyZeroCurve<Interpolator>::InterpolatedPillarOnlyZeroCurve(
215226
const Date& referenceDate, const std::vector<Date>& dates, const std::vector<Rate>& zeroRates,
216227
const DayCounter& dayCounter, const Interpolator& interpolator, const Extrapolation extrapolation)
217-
: YieldTermStructure(referenceDate, Calendar(), dayCounter), InterpolatedCurve<Interpolator>(interpolator),
228+
: ZeroYieldStructure(referenceDate, Calendar(), dayCounter), InterpolatedCurve<Interpolator>(interpolator),
218229
extrapolation_(extrapolation), dates_(dates) {
219230

220231
QL_REQUIRE(!dates.empty(), "InterpolatedPillarOnlyZeroCurve: dates cannot be empty");
@@ -237,35 +248,35 @@ template <class Interpolator> Date InterpolatedPillarOnlyZeroCurve<Interpolator>
237248
return dates_.back();
238249
}
239250

240-
template <class Interpolator> DiscountFactor InterpolatedPillarOnlyZeroCurve<Interpolator>::discountImpl(Time t) const {
241-
// At reference date (t=0), return 1.0 by definition
242-
if (t <= 0.0 || QuantLib::close_enough(t, 0.0)) {
243-
return 1.0;
244-
}
245-
246-
Rate zeroRate;
247-
// For times between t=0 and first pillar, flat extrapolation using first pillar rate
251+
template <class Interpolator> DiscountFactor InterpolatedPillarOnlyZeroCurve<Interpolator>::zeroYieldImpl(Time t) const {
248252
if (t < this->times_.front()) {
249-
zeroRate = this->data_.front();
250-
}
251-
// Interpolate on pillars for times within the curve range
252-
else if (t <= this->times_.back()) {
253-
zeroRate = this->interpolation_(t, this->allowsExtrapolation());
254-
}
255-
// Flat extrapolation beyond the last pillar
256-
else {
257-
zeroRate = this->data_.back();
253+
// For times between t=0 and first pillar, flat extrapolation using first pillar rate
254+
return this->data_.front();
255+
} else if (t <= this->times_.back()) {
256+
// Interpolate on pillars for times within the curve range
257+
return this->interpolation_(t, true);
258+
} else {
259+
// flat fwd extrapolation
260+
Time tMax = this->times_.back();
261+
Rate zMax = this->data_.back();
262+
if (extrapolation_ == YieldTermStructure::Extrapolation::ContinuousForward) {
263+
Rate instFwdMax = zMax + tMax * this->interpolation_.derivative(tMax, true);
264+
return (zMax * tMax + instFwdMax * (t - tMax)) / t;
265+
} else if (extrapolation_ == YieldTermStructure::Extrapolation::DiscreteForward) {
266+
Time tMax_m = this->timeFromReference(dates_.back() - 1);
267+
Rate dz = this->interpolation_(tMax) - this->interpolation_(tMax_m, true);
268+
return (zMax * tMax + dz * (t - tMax) / (tMax - tMax_m)) / t;
269+
} else {
270+
QL_FAIL("extrapolation method not handled.");
271+
}
258272
}
259-
260-
// Convert zero rate to discount factor: DF = exp(-r * t)
261-
return std::exp(-zeroRate * t);
262273
}
263274

264275
template <class Interpolator>
265276
InterpolatedPillarOnlyForwardCurve<Interpolator>::InterpolatedPillarOnlyForwardCurve(
266277
const Date& referenceDate, const std::vector<Date>& dates, const std::vector<Rate>& forwardRates,
267278
const DayCounter& dayCounter, const Interpolator& interpolator, const Extrapolation extrapolation)
268-
: YieldTermStructure(referenceDate, Calendar(), dayCounter), InterpolatedCurve<Interpolator>(interpolator),
279+
: ForwardRateStructure(referenceDate, Calendar(), dayCounter), InterpolatedCurve<Interpolator>(interpolator),
269280
extrapolation_(extrapolation), dates_(dates) {
270281

271282
QL_REQUIRE(!dates.empty(), "InterpolatedPillarOnlyForwardCurve: dates cannot be empty");
@@ -289,29 +300,58 @@ template <class Interpolator> Date InterpolatedPillarOnlyForwardCurve<Interpolat
289300
}
290301

291302
template <class Interpolator>
292-
DiscountFactor InterpolatedPillarOnlyForwardCurve<Interpolator>::discountImpl(Time t) const {
293-
// At reference date (t=0), return 1.0 by definition
294-
if (t <= 0.0 || QuantLib::close_enough(t, 0.0)) {
295-
return 1.0;
303+
DiscountFactor InterpolatedPillarOnlyForwardCurve<Interpolator>::forwardImpl(Time t) const {
304+
if (t < this->times_.front()) {
305+
Rate f = this->data_.front();
306+
return f;
296307
}
297308

309+
if (t <= this->times_.back()) {
310+
return this->interpolation_(t, true);
311+
} else {
312+
// flat fwd extrapolation
313+
if (extrapolation_ == YieldTermStructure::Extrapolation::ContinuousForward) {
314+
return this->data_.back();
315+
} else if (extrapolation_ == YieldTermStructure::Extrapolation::DiscreteForward) {
316+
Time tMax = this->times_.back();
317+
Time tMax_m = this->timeFromReference(dates_.back() - 1);
318+
Real iMax = this->interpolation_.primitive(tMax, true);
319+
Real iMax_m = this->interpolation_.primitive(tMax_m, true);
320+
return (iMax - iMax_m) / (tMax - tMax_m);
321+
} else {
322+
QL_FAIL("extrapolation method not handled.");
323+
}
324+
}
325+
}
326+
327+
template <class Interpolator>
328+
DiscountFactor InterpolatedPillarOnlyForwardCurve<Interpolator>::zeroYieldImpl(Time t) const {
298329
// For times between t=0 and first pillar, use flat forward from first pillar
299330
if (t < this->times_.front()) {
300331
Rate f = this->data_.front();
301-
return std::exp(-f * t);
332+
return f;
302333
}
303334

304335
// For times within the curve range, integrate forward rates
336+
Real integral;
305337
if (t <= this->times_.back()) {
306-
// Use numerical integration of forward rates
307-
return std::exp(-this->interpolation_.primitive(t, this->allowsExtrapolation()));
338+
integral = this->interpolation_.primitive(t, true);
339+
} else {
340+
// flat fwd extrapolation
341+
if (extrapolation_ == YieldTermStructure::Extrapolation::ContinuousForward) {
342+
integral = this->interpolation_.primitive(this->times_.back(), true) +
343+
this->data_.back() * (t - this->times_.back());
344+
} else if (extrapolation_ == YieldTermStructure::Extrapolation::DiscreteForward) {
345+
Time tMax = this->times_.back();
346+
Time tMax_m = this->timeFromReference(dates_.back() - 1);
347+
Real iMax = this->interpolation_.primitive(tMax, true);
348+
Real iMax_m = this->interpolation_.primitive(tMax_m, true);
349+
integral = iMax + (iMax - iMax_m) * (t - tMax) / (tMax - tMax_m);
350+
} else {
351+
QL_FAIL("extrapolation method not handled.");
352+
}
308353
}
309-
310-
// Flat extrapolation beyond the last pillar
311-
Rate fMax = this->data_.back();
312-
Time tMax = this->times_.back();
313-
DiscountFactor dMax = std::exp(-this->interpolation_.primitive(tMax, this->allowsExtrapolation()));
314-
return dMax * std::exp(-fMax * (t - tMax));
354+
return integral / t;
315355
}
316356

317357
} // namespace QuantExt

0 commit comments

Comments
 (0)