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
BlackAverageONIndexedCouponPricer Class Reference

Black averaged overnight coupon pricer. More...

#include <qle/cashflows/blackovernightindexedcouponpricer.hpp>

+ Inheritance diagram for BlackAverageONIndexedCouponPricer:
+ Collaboration diagram for BlackAverageONIndexedCouponPricer:

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
 
 CapFlooredAverageONIndexedCouponPricer (const Handle< OptionletVolatilityStructure > &v, const bool effectiveVolatilityInput=false)
 
- Public Member Functions inherited from CapFlooredAverageONIndexedCouponPricer
 CapFlooredAverageONIndexedCouponPricer (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 swapletRate_
 
Real forwardRate_
 
const CappedFlooredAverageONIndexedCouponcoupon_
 

Additional Inherited Members

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

Detailed Description

Black averaged overnight coupon pricer.

Definition at line 63 of file blackovernightindexedcouponpricer.hpp.

Member Function Documentation

◆ initialize()

void initialize ( const FloatingRateCoupon coupon)
override

Definition at line 272 of file blackovernightindexedcouponpricer.cpp.

272 {
273 coupon_ = dynamic_cast<const CappedFlooredAverageONIndexedCoupon*>(&coupon);
274 QL_REQUIRE(coupon_, "BlackAverageONIndexedCouponPricer: CappedFlooredAverageONIndexedCoupon required");
275 gearing_ = coupon.gearing();
276 index_ = ext::dynamic_pointer_cast<OvernightIndex>(coupon.index());
277 if (!index_) {
278 // check if the coupon was right
279 const CappedFlooredAverageONIndexedCoupon* c =
280 dynamic_cast<const CappedFlooredAverageONIndexedCoupon*>(&coupon);
281 QL_REQUIRE(c, "BlackAverageONIndexedCouponPricer: CappedFlooredAverageONIndexedCoupon required");
282 // coupon was right, index is not
283 QL_FAIL("BlackAverageONIndexedCouponPricer: CappedFlooredAverageONIndexedCoupon required");
284 }
285 swapletRate_ = coupon_->underlying()->rate();
286 forwardRate_ = (swapletRate_ - coupon_->underlying()->spread()) / coupon_->underlying()->gearing();
288}
const CappedFlooredAverageONIndexedCoupon * coupon_
ext::shared_ptr< AverageONIndexedCoupon > underlying() const
+ Here is the call graph for this function:

◆ swapletPrice()

Real swapletPrice ( ) const
override

Definition at line 498 of file blackovernightindexedcouponpricer.cpp.

498 {
499 QL_FAIL("BlackAverageONIndexedCouponPricer::swapletPrice() not provided");
500}

◆ swapletRate()

Rate swapletRate ( ) const
override

Definition at line 486 of file blackovernightindexedcouponpricer.cpp.

486{ return swapletRate_; }

◆ capletPrice()

Real capletPrice ( Rate  effectiveCap) const
override

Definition at line 501 of file blackovernightindexedcouponpricer.cpp.

501 {
502 QL_FAIL("BlackAverageONIndexedCouponPricer::capletPrice() not provided");
503}

◆ capletRate()

Rate capletRate ( Rate  effectiveCap) const
override

Definition at line 488 of file blackovernightindexedcouponpricer.cpp.

488 {
489 return coupon_->localCapFloor() ? optionletRateLocal(Option::Call, effectiveCap)
490 : optionletRateGlobal(Option::Call, effectiveCap);
491}
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 504 of file blackovernightindexedcouponpricer.cpp.

504 {
505 QL_FAIL("BlackAverageONIndexedCouponPricer::floorletPrice() not provided");
506}

◆ floorletRate()

Rate floorletRate ( Rate  effectiveFloor) const
override

Definition at line 493 of file blackovernightindexedcouponpricer.cpp.

493 {
494 return coupon_->localCapFloor() ? optionletRateLocal(Option::Put, effectiveFloor)
495 : optionletRateGlobal(Option::Put, effectiveFloor);
496}
+ Here is the call graph for this function:

◆ optionletRateGlobal()

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

Definition at line 290 of file blackovernightindexedcouponpricer.cpp.

290 {
291 Date lastRelevantFixingDate = coupon_->underlying()->fixingDate();
292 if (lastRelevantFixingDate <= Settings::instance().evaluationDate()) {
293 // the amount is determined
294 Real a, b;
295 if (optionType == Option::Call) {
296 a = forwardRate_;
297 b = effStrike;
298 } else {
299 a = effStrike;
300 b = forwardRate_;
301 }
302 return gearing_ * std::max(a - b, 0.0);
303 } else {
304 // not yet determined, use Black model
305 QL_REQUIRE(!capletVolatility().empty(), "BlackAverageONIndexedCouponPricer: missing optionlet volatility");
306 std::vector<Date> fixingDates = coupon_->underlying()->fixingDates();
307 QL_REQUIRE(!fixingDates.empty(), "BlackAverageONIndexedCouponPricer: empty fixing dates");
308 bool shiftedLn = capletVolatility()->volatilityType() == ShiftedLognormal;
309 Real shift = capletVolatility()->displacement();
310 Real stdDev;
311 Real effectiveTime = capletVolatility()->timeFromReference(fixingDates.back());
313 // vol input is effective, i.e. we use a plain black model
314 stdDev = capletVolatility()->volatility(fixingDates.back(), effStrike) * std::sqrt(effectiveTime);
315 } else {
316 // vol input is not effective:
317 // for the standard deviation see Lyashenko, Mercurio, Looking forward to backward looking rates,
318 // section 6.3. the idea is to dampen the average volatility sigma between the fixing start and fixing end
319 // date by a linear function going from (fixing start, 1) to (fixing end, 0)
320 Real fixingStartTime = capletVolatility()->timeFromReference(fixingDates.front());
321 Real fixingEndTime = capletVolatility()->timeFromReference(fixingDates.back());
322 Real sigma = capletVolatility()->volatility(
323 std::max(fixingDates.front(), capletVolatility()->referenceDate() + 1), effStrike);
324 Real T = std::max(fixingStartTime, 0.0);
325 if (!close_enough(fixingEndTime, T))
326 T += std::pow(fixingEndTime - T, 3.0) / std::pow(fixingEndTime - fixingStartTime, 2.0) / 3.0;
327 stdDev = sigma * std::sqrt(T);
328 }
329 if (optionType == Option::Type::Call)
330 effectiveCapletVolatility_ = stdDev / std::sqrt(effectiveTime);
331 else
332 effectiveFloorletVolatility_ = stdDev / std::sqrt(effectiveTime);
333 Real fixing = shiftedLn ? blackFormula(optionType, effStrike, forwardRate_, stdDev, 1.0, shift)
334 : bachelierBlackFormula(optionType, effStrike, forwardRate_, stdDev, 1.0);
335 return gearing_ * fixing;
336 }
337}
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 339 of file blackovernightindexedcouponpricer.cpp.

339 {
340
341 QL_REQUIRE(!effectiveVolatilityInput(),
342 "BlackAverageONIndexedCouponPricer::optionletRateLocal() does not support effective volatility input.");
343
344 // We compute a rate and a rawRate such that
345 // rate * tau * nominal is the amount of the coupon with locally (i.e. daily) capped / floored rates
346 // rawRate * tau * nominal is the amount of the coupon without capping / flooring the rate
347 // We will then return the difference between rate and rawRate (with the correct sign, see below)
348 // as the option component of the coupon.
349
350 // See CappedFlooredOvernightIndexedCoupon::effectiveCap(), Floor() for what is passed in as effStrike.
351 // From this we back out the absolute strike at which the
352 // - daily rate + spread (spread included) or the
353 // - daily rate (spread excluded)
354 // is capped / floored.
355
356 Real absStrike = coupon_->includeSpread() ? effStrike + coupon_->underlying()->spread() : effStrike;
357
358 // This following code is inevitably quite similar to the plain ON coupon pricer code, possibly we can refactor
359 // this, but as a first step it seems safer to add the full modified code explicitly here and leave the original
360 // code alone.
361
362 ext::shared_ptr<OvernightIndex> index = ext::dynamic_pointer_cast<OvernightIndex>(coupon_->index());
363
364 const std::vector<Date>& fixingDates = coupon_->underlying()->fixingDates();
365 const std::vector<Time>& dt = coupon_->underlying()->dt();
366
367 Size n = dt.size();
368 Size i = 0;
369 QL_REQUIRE(coupon_->underlying()->rateCutoff() < n,
370 "rate cutoff (" << coupon_->underlying()->rateCutoff()
371 << ") must be less than number of fixings in period (" << n << ")");
372 Size nCutoff = n - coupon_->underlying()->rateCutoff();
373
374 Real accumulatedRate = 0.0, accumulatedRateRaw = 0.0;
375
376 // already fixed part
377 Date today = Settings::instance().evaluationDate();
378 while (i < n && fixingDates[std::min(i, nCutoff)] < today) {
379 // rate must have been fixed
380 Rate pastFixing = index->pastFixing(fixingDates[std::min(i, nCutoff)]);
381 QL_REQUIRE(pastFixing != Null<Real>(),
382 "Missing " << index->name() << " fixing for " << fixingDates[std::min(i, nCutoff)]);
383 if (coupon_->includeSpread()) {
384 pastFixing += coupon_->spread();
385 }
386 accumulatedRate += cappedFlooredRate(pastFixing, optionType, absStrike) * dt[i];
387 accumulatedRateRaw += pastFixing * dt[i];
388 ++i;
389 }
390
391 // today is a border case
392 if (i < n && fixingDates[std::min(i, nCutoff)] == today) {
393 // might have been fixed
394 try {
395 Rate pastFixing = index->pastFixing(today);
396 if (pastFixing != Null<Real>()) {
397 if (coupon_->includeSpread()) {
398 pastFixing += coupon_->spread();
399 }
400 accumulatedRate += cappedFlooredRate(pastFixing, optionType, absStrike) * dt[i];
401 accumulatedRateRaw += pastFixing * dt[i];
402 ++i;
403 } else {
404 ; // fall through and forecast
405 }
406 } catch (Error&) {
407 ; // fall through and forecast
408 }
409 }
410
411 // forward part, approximation by pricing a cap / floor in the middle of the future period
412 const std::vector<Date>& dates = coupon_->underlying()->valueDates();
413 if (i < n) {
414 Handle<YieldTermStructure> curve = index->forwardingTermStructure();
415 QL_REQUIRE(!curve.empty(), "null term structure set to this instance of " << index->name());
416
417 DiscountFactor startDiscount = curve->discount(dates[i]);
418 DiscountFactor endDiscount = curve->discount(dates[std::max(nCutoff, i)]);
419
420 // handle the rate cutoff period (if there is any, i.e. if nCutoff < n)
421 if (nCutoff < n) {
422 // forward discount factor for one calendar day on the cutoff date
423 DiscountFactor discountCutoffDate = curve->discount(dates[nCutoff] + 1) / curve->discount(dates[nCutoff]);
424 // keep the above forward discount factor constant during the cutoff period
425 endDiscount *= std::pow(discountCutoffDate, dates[n] - dates[nCutoff]);
426 }
427
428 // estimate the average daily rate over the future period (approximate the continuously compounded rate)
429 Real tau = coupon_->dayCounter().yearFraction(dates[i], dates.back());
430 Real averageRate = -std::log(endDiscount / startDiscount) / tau;
431
432 // compute the value of a cap or floor with fixing in the middle of the future period
433 // (but accounting for the rate cutoff here)
434 Time midPoint =
435 (capletVolatility()->timeFromReference(dates[i]) + capletVolatility()->timeFromReference(dates[nCutoff])) /
436 2.0;
437 Real stdDev = capletVolatility()->volatility(midPoint, effStrike) * std::sqrt(midPoint);
438 Real shift = capletVolatility()->displacement();
439 bool shiftedLn = capletVolatility()->volatilityType() == ShiftedLognormal;
440 Rate cfValue = shiftedLn ? blackFormula(optionType, effStrike, averageRate, stdDev, 1.0, shift)
441 : bachelierBlackFormula(optionType, effStrike, averageRate, stdDev, 1.0);
442
443 Real effectiveTime = capletVolatility()->timeFromReference(fixingDates.back());
444 if (optionType == Option::Type::Call)
445 effectiveCapletVolatility_ = stdDev / std::sqrt(effectiveTime);
446 else
447 effectiveFloorletVolatility_ = stdDev / std::sqrt(effectiveTime);
448
449 // add spread to average rate
450 if (coupon_->includeSpread()) {
451 averageRate += coupon_->underlying()->spread();
452 }
453
454 // incorporate cap/floor into average rate
455 Real averageRateRaw = averageRate;
456 averageRate += optionType == Option::Call ? (-cfValue) : cfValue;
457
458 // now assume the averageRate is the effective rate over the future period and update the average rate
459 // this is an approximation, see "Ester / Daily Spread Curve Setup in ORE": set tau to avg value
460 Real dailyTau =
461 coupon_->underlying()->dayCounter().yearFraction(dates[i], dates.back()) / (dates.back() - dates[i]);
462 accumulatedRate += dailyTau * averageRate * static_cast<Real>(dates.back() - dates[i]);
463 accumulatedRateRaw += dailyTau * averageRateRaw * static_cast<Real>(dates.back() - dates[i]);
464 }
465
466 Rate tau = coupon_->underlying()->lookback() == 0 * Days
467 ? coupon_->accrualPeriod()
468 : coupon_->dayCounter().yearFraction(dates.front(), dates.back());
469 Rate rate = accumulatedRate / tau;
470 Rate rawRate = accumulatedRateRaw / tau;
471
472 rate *= coupon_->underlying()->gearing();
473 rawRate *= coupon_->underlying()->gearing();
474
475 if (!coupon_->includeSpread()) {
476 rate += coupon_->underlying()->spread();
477 rawRate += coupon_->underlying()->spread();
478 }
479
480 // return optionletRate := r - rawRate, i.e. the option component only
481 // (see CappedFlooredAverageONIndexedCoupon::rate() for the signs of the capletRate / flooredRate)
482
483 return (optionType == Option::Call ? -1.0 : 1.0) * (rate - rawRate);
484}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ CapFlooredAverageONIndexedCouponPricer()

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

Member Data Documentation

◆ gearing_

Real gearing_
private

Definition at line 78 of file blackovernightindexedcouponpricer.hpp.

◆ index_

ext::shared_ptr<IborIndex> index_
private

Definition at line 79 of file blackovernightindexedcouponpricer.hpp.

◆ swapletRate_

Real swapletRate_
private

Definition at line 80 of file blackovernightindexedcouponpricer.hpp.

◆ forwardRate_

Real forwardRate_
private

Definition at line 80 of file blackovernightindexedcouponpricer.hpp.

◆ coupon_

const CappedFlooredAverageONIndexedCoupon* coupon_
private

Definition at line 82 of file blackovernightindexedcouponpricer.hpp.