Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
Public Member Functions | Private Member Functions | Private Attributes | List of all members
BlackOvernightIndexedCouponPricer Class Reference

Black compounded overnight coupon pricer. More...

#include <qle/cashflows/blackovernightindexedcouponpricer.hpp>

+ Inheritance diagram for BlackOvernightIndexedCouponPricer:
+ Collaboration diagram for BlackOvernightIndexedCouponPricer:

Public Member Functions

void initialize (const FloatingRateCoupon &coupon) override
 
Real swapletPrice () const override
 
Rate swapletRate () const override
 
Real capletPrice (Rate effectiveCap) const override
 
Rate capletRate (Rate effectiveCap) const override
 
Real floorletPrice (Rate effectiveFloor) const override
 
Rate floorletRate (Rate effectiveFloor) const override
 
 CappedFlooredOvernightIndexedCouponPricer (const Handle< OptionletVolatilityStructure > &v, const bool effectiveVolatilityInput=false)
 
- Public Member Functions inherited from CappedFlooredOvernightIndexedCouponPricer
 CappedFlooredOvernightIndexedCouponPricer (const Handle< OptionletVolatilityStructure > &v, const bool effectiveVolatilityInput=false)
 
Handle< OptionletVolatilityStructure > capletVolatility () const
 
bool effectiveVolatilityInput () const
 
Real effectiveCapletVolatility () const
 
Real effectiveFloorletVolatility () const
 

Private Member Functions

Real optionletRateGlobal (Option::Type optionType, Real effStrike) const
 
Real optionletRateLocal (Option::Type optionType, Real effStrike) const
 

Private Attributes

Real gearing_
 
ext::shared_ptr< IborIndex > index_
 
Real effectiveIndexFixing_
 
Real swapletRate_
 
const CappedFlooredOvernightIndexedCouponcoupon_
 

Additional Inherited Members

- Protected Attributes inherited from CappedFlooredOvernightIndexedCouponPricer
Handle< OptionletVolatilityStructure > capletVol_
 
bool effectiveVolatilityInput_
 
Real effectiveCapletVolatility_ = Null<Real>()
 
Real effectiveFloorletVolatility_ = Null<Real>()
 

Detailed Description

Black compounded overnight coupon pricer.

Definition at line 37 of file blackovernightindexedcouponpricer.hpp.

Member Function Documentation

◆ initialize()

void initialize ( const FloatingRateCoupon coupon)
override

Definition at line 26 of file blackovernightindexedcouponpricer.cpp.

26 {
27 coupon_ = dynamic_cast<const CappedFlooredOvernightIndexedCoupon*>(&coupon);
28 QL_REQUIRE(coupon_, "BlackOvernightIndexedCouponPricer: CappedFlooredOvernightIndexedCoupon required");
29 gearing_ = coupon.gearing();
30 index_ = ext::dynamic_pointer_cast<OvernightIndex>(coupon.index());
31 if (!index_) {
32 // check if the coupon was right
33 const CappedFlooredOvernightIndexedCoupon* c =
34 dynamic_cast<const CappedFlooredOvernightIndexedCoupon*>(&coupon);
35 QL_REQUIRE(c, "BlackOvernightIndexedCouponPricer: CappedFlooredOvernightIndexedCoupon required");
36 // coupon was right, index is not
37 QL_FAIL("BlackOvernightIndexedCouponPricer: CappedFlooredOvernightIndexedCoupon required");
38 }
39 swapletRate_ = coupon_->underlying()->rate();
40 effectiveIndexFixing_ = coupon_->underlying()->effectiveIndexFixing();
42}
const CappedFlooredOvernightIndexedCoupon * coupon_
ext::shared_ptr< OvernightIndexedCoupon > underlying() const
+ Here is the call graph for this function:

◆ swapletPrice()

Real swapletPrice ( ) const
override

Definition at line 262 of file blackovernightindexedcouponpricer.cpp.

262 {
263 QL_FAIL("BlackOvernightIndexedCouponPricer::swapletPrice() not provided");
264}

◆ swapletRate()

Rate swapletRate ( ) const
override

Definition at line 250 of file blackovernightindexedcouponpricer.cpp.

250{ return swapletRate_; }

◆ capletPrice()

Real capletPrice ( Rate  effectiveCap) const
override

Definition at line 265 of file blackovernightindexedcouponpricer.cpp.

265 {
266 QL_FAIL("BlackOvernightIndexedCouponPricer::capletPrice() not provided");
267}

◆ capletRate()

Rate capletRate ( Rate  effectiveCap) const
override

Definition at line 252 of file blackovernightindexedcouponpricer.cpp.

252 {
253 return coupon_->localCapFloor() ? optionletRateLocal(Option::Call, effectiveCap)
254 : optionletRateGlobal(Option::Call, effectiveCap);
255}
Real optionletRateGlobal(Option::Type optionType, Real effStrike) const
Real optionletRateLocal(Option::Type optionType, Real effStrike) const
+ Here is the call graph for this function:

◆ floorletPrice()

Real floorletPrice ( Rate  effectiveFloor) const
override

Definition at line 268 of file blackovernightindexedcouponpricer.cpp.

268 {
269 QL_FAIL("BlackOvernightIndexedCouponPricer::floorletPrice() not provided");
270}

◆ floorletRate()

Rate floorletRate ( Rate  effectiveFloor) const
override

Definition at line 257 of file blackovernightindexedcouponpricer.cpp.

257 {
258 return coupon_->localCapFloor() ? optionletRateLocal(Option::Put, effectiveFloor)
259 : optionletRateGlobal(Option::Put, effectiveFloor);
260}
+ Here is the call graph for this function:

◆ optionletRateGlobal()

Real optionletRateGlobal ( Option::Type  optionType,
Real  effStrike 
) const
private

Definition at line 44 of file blackovernightindexedcouponpricer.cpp.

44 {
45 Date lastRelevantFixingDate = coupon_->underlying()->fixingDate();
46 if (lastRelevantFixingDate <= Settings::instance().evaluationDate()) {
47 // the amount is determined
48 Real a, b;
49 if (optionType == Option::Call) {
51 b = effStrike;
52 } else {
53 a = effStrike;
55 }
56 return gearing_ * std::max(a - b, 0.0);
57 } else {
58 // not yet determined, use Black model
59 QL_REQUIRE(!capletVolatility().empty(), "BlackOvernightIndexedCouponPricer: missing optionlet volatility");
60 std::vector<Date> fixingDates = coupon_->underlying()->fixingDates();
61 QL_REQUIRE(!fixingDates.empty(), "BlackOvernightIndexedCouponPricer: empty fixing dates");
62 bool shiftedLn = capletVolatility()->volatilityType() == ShiftedLognormal;
63 Real shift = capletVolatility()->displacement();
64 Real stdDev;
65 Real effectiveTime = capletVolatility()->timeFromReference(fixingDates.back());
67 // vol input is effective, i.e. we use a plain black model
68 stdDev = capletVolatility()->volatility(fixingDates.back(), effStrike) * std::sqrt(effectiveTime);
69 } else {
70 // vol input is not effective:
71 // for the standard deviation see Lyashenko, Mercurio, Looking forward to backward looking rates,
72 // section 6.3. the idea is to dampen the average volatility sigma between the fixing start and fixing end
73 // date by a linear function going from (fixing start, 1) to (fixing end, 0)
74 Real fixingStartTime = capletVolatility()->timeFromReference(fixingDates.front());
75 Real fixingEndTime = capletVolatility()->timeFromReference(fixingDates.back());
76 Real sigma = capletVolatility()->volatility(
77 std::max(fixingDates.front(), capletVolatility()->referenceDate() + 1), effStrike);
78 Real T = std::max(fixingStartTime, 0.0);
79 if (!close_enough(fixingEndTime, T))
80 T += std::pow(fixingEndTime - T, 3.0) / std::pow(fixingEndTime - fixingStartTime, 2.0) / 3.0;
81 stdDev = sigma * std::sqrt(T);
82 }
83 if (optionType == Option::Type::Call)
84 effectiveCapletVolatility_ = stdDev / std::sqrt(effectiveTime);
85 else
86 effectiveFloorletVolatility_ = stdDev / std::sqrt(effectiveTime);
87 Real fixing = shiftedLn ? blackFormula(optionType, effStrike, effectiveIndexFixing_, stdDev, 1.0, shift)
88 : bachelierBlackFormula(optionType, effStrike, effectiveIndexFixing_, stdDev, 1.0);
89 return gearing_ * fixing;
90 }
91}
Handle< OptionletVolatilityStructure > capletVolatility() const
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ optionletRateLocal()

Real optionletRateLocal ( Option::Type  optionType,
Real  effStrike 
) const
private

Definition at line 103 of file blackovernightindexedcouponpricer.cpp.

103 {
104
105 QL_REQUIRE(!effectiveVolatilityInput(),
106 "BlackAverageONIndexedCouponPricer::optionletRateLocal() does not support effective volatility input.");
107
108 // We compute a rate and a rawRate such that
109 // rate * tau * nominal is the amount of the coupon with locally (i.e. daily) capped / floored rates
110 // rawRate * tau * nominal is the amount of the coupon without capping / flooring the rate
111 // We will then return the difference between rate and rawRate (with the correct sign, see below)
112 // as the option component of the coupon.
113
114 // See CappedFlooredOvernightIndexedCoupon::effectiveCap(), Floor() for what is passed in as effStrike.
115 // From this we back out the absolute strike at which the
116 // - daily rate + spread (spread included) or the
117 // - daily rate (spread excluded)
118 // is capped / floored.
119
120 Real absStrike = coupon_->underlying()->includeSpread() ? effStrike + coupon_->underlying()->spread() : effStrike;
121
122 // This following code is inevitably quite similar to the plain ON coupon pricer code, possibly we can refactor
123 // this, but as a first step it seems safer to add the full modified code explicitly here and leave the original
124 // code alone.
125
126 ext::shared_ptr<OvernightIndex> index = ext::dynamic_pointer_cast<OvernightIndex>(coupon_->index());
127
128 const std::vector<Date>& fixingDates = coupon_->underlying()->fixingDates();
129 const std::vector<Time>& dt = coupon_->underlying()->dt();
130
131 Size n = dt.size();
132 Size i = 0;
133 QL_REQUIRE(coupon_->underlying()->rateCutoff() < n,
134 "rate cutoff (" << coupon_->underlying()->rateCutoff()
135 << ") must be less than number of fixings in period (" << n << ")");
136 Size nCutoff = n - coupon_->underlying()->rateCutoff();
137
138 Real compoundFactor = 1.0, compoundFactorRaw = 1.0;
139
140 // already fixed part
141 Date today = Settings::instance().evaluationDate();
142 while (i < n && fixingDates[std::min(i, nCutoff)] < today) {
143 // rate must have been fixed
144 Rate pastFixing = index->pastFixing(fixingDates[std::min(i, nCutoff)]);
145 QL_REQUIRE(pastFixing != Null<Real>(),
146 "Missing " << index->name() << " fixing for " << fixingDates[std::min(i, nCutoff)]);
147 if (coupon_->underlying()->includeSpread()) {
148 pastFixing += coupon_->spread();
149 }
150 compoundFactor *= 1.0 + cappedFlooredRate(pastFixing, optionType, absStrike) * dt[i];
151 compoundFactorRaw *= 1.0 + pastFixing * dt[i];
152 ++i;
153 }
154
155 // today is a border case
156 if (i < n && fixingDates[std::min(i, nCutoff)] == today) {
157 // might have been fixed
158 try {
159 Rate pastFixing = index->pastFixing(today);
160 if (pastFixing != Null<Real>()) {
161 if (coupon_->underlying()->includeSpread()) {
162 pastFixing += coupon_->spread();
163 }
164 compoundFactor *= 1.0 + cappedFlooredRate(pastFixing, optionType, absStrike) * dt[i];
165 compoundFactorRaw *= 1.0 + pastFixing * dt[i];
166 ++i;
167 } else {
168 ; // fall through and forecast
169 }
170 } catch (Error&) {
171 ; // fall through and forecast
172 }
173 }
174
175 // forward part, approximation by pricing a cap / floor in the middle of the future period
176 const std::vector<Date>& dates = coupon_->underlying()->valueDates();
177 if (i < n) {
178 Handle<YieldTermStructure> curve = index->forwardingTermStructure();
179 QL_REQUIRE(!curve.empty(), "null term structure set to this instance of " << index->name());
180
181 DiscountFactor startDiscount = curve->discount(dates[i]);
182 DiscountFactor endDiscount = curve->discount(dates[std::max(nCutoff, i)]);
183
184 // handle the rate cutoff period (if there is any, i.e. if nCutoff < n)
185 if (nCutoff < n) {
186 // forward discount factor for one calendar day on the cutoff date
187 DiscountFactor discountCutoffDate = curve->discount(dates[nCutoff] + 1) / curve->discount(dates[nCutoff]);
188 // keep the above forward discount factor constant during the cutoff period
189 endDiscount *= std::pow(discountCutoffDate, dates[n] - dates[nCutoff]);
190 }
191
192 // estimate the average daily rate over the future period (approximate the continuously compounded rate)
193 Real tau = coupon_->dayCounter().yearFraction(dates[i], dates.back());
194 Real averageRate = -std::log(endDiscount / startDiscount) / tau;
195
196 // compute the value of a cap or floor with fixing in the middle of the future period
197 // (but accounting for the rate cutoff here)
198 Time midPoint =
199 (capletVolatility()->timeFromReference(dates[i]) + capletVolatility()->timeFromReference(dates[nCutoff])) /
200 2.0;
201 Real stdDev = capletVolatility()->volatility(midPoint, effStrike) * std::sqrt(midPoint);
202 Real shift = capletVolatility()->displacement();
203 bool shiftedLn = capletVolatility()->volatilityType() == ShiftedLognormal;
204 Rate cfValue = shiftedLn ? blackFormula(optionType, effStrike, averageRate, stdDev, 1.0, shift)
205 : bachelierBlackFormula(optionType, effStrike, averageRate, stdDev, 1.0);
206 Real effectiveTime = capletVolatility()->timeFromReference(fixingDates.back());
207 if (optionType == Option::Type::Call)
208 effectiveCapletVolatility_ = stdDev / std::sqrt(effectiveTime);
209 else
210 effectiveFloorletVolatility_ = stdDev / std::sqrt(effectiveTime);
211
212 // add spread to average rate
213 if (coupon_->underlying()->includeSpread()) {
214 averageRate += coupon_->underlying()->spread();
215 }
216
217 // incorporate cap/floor into average rate
218 Real averageRateRaw = averageRate;
219 averageRate += optionType == Option::Call ? (-cfValue) : cfValue;
220
221 // now assume the averageRate is the effective rate over the future period and update the compoundFactor
222 // this is an approximation, see "Ester / Daily Spread Curve Setup in ORE": set tau to avg value
223 Real dailyTau =
224 coupon_->underlying()->dayCounter().yearFraction(dates[i], dates.back()) / (dates.back() - dates[i]);
225 // now use formula (4) from the paper
226 compoundFactor *= std::pow(1.0 + dailyTau * averageRate, static_cast<int>(dates.back() - dates[i]));
227 compoundFactorRaw *= std::pow(1.0 + dailyTau * averageRateRaw, static_cast<int>(dates.back() - dates[i]));
228 }
229
230 Rate tau = coupon_->underlying()->lookback() == 0 * Days
231 ? coupon_->accrualPeriod()
232 : coupon_->dayCounter().yearFraction(dates.front(), dates.back());
233 Rate rate = (compoundFactor - 1.0) / tau;
234 Rate rawRate = (compoundFactorRaw - 1.0) / tau;
235
236 rate *= coupon_->underlying()->gearing();
237 rawRate *= coupon_->underlying()->gearing();
238
239 if (!coupon_->underlying()->includeSpread()) {
240 rate += coupon_->underlying()->spread();
241 rawRate += coupon_->underlying()->spread();
242 }
243
244 // return optionletRate := r - rawRate, i.e. the option component only
245 // (see CappedFlooredOvernightIndexedCoupon::rate() for the signs of the capletRate / flooredRate)
246
247 return (optionType == Option::Call ? -1.0 : 1.0) * (rate - rawRate);
248}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ CappedFlooredOvernightIndexedCouponPricer()

CappedFlooredOvernightIndexedCouponPricer ( const Handle< OptionletVolatilityStructure > &  v,
const bool  effectiveVolatilityInput = false 
)

Member Data Documentation

◆ gearing_

Real gearing_
private

Definition at line 52 of file blackovernightindexedcouponpricer.hpp.

◆ index_

ext::shared_ptr<IborIndex> index_
private

Definition at line 53 of file blackovernightindexedcouponpricer.hpp.

◆ effectiveIndexFixing_

Real effectiveIndexFixing_
private

Definition at line 54 of file blackovernightindexedcouponpricer.hpp.

◆ swapletRate_

Real swapletRate_
private

Definition at line 54 of file blackovernightindexedcouponpricer.hpp.

◆ coupon_

const CappedFlooredOvernightIndexedCoupon* coupon_
private

Definition at line 56 of file blackovernightindexedcouponpricer.hpp.