Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
blackaveragebmacouponpricer.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2023 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
20
21#include <ql/pricingengines/blackformula.hpp>
22#include <ql/termstructures/volatility/optionlet/optionletvolatilitystructure.hpp>
23
24namespace QuantExt {
25
27 coupon_ = dynamic_cast<const CappedFlooredAverageBMACoupon*>(&coupon);
28 QL_REQUIRE(coupon_, "BlackAverageBMACouponPricer: CappedFlooredAverageBMACoupon required");
29 gearing_ = coupon.gearing();
30 index_ = ext::dynamic_pointer_cast<BMAIndex>(coupon.index());
31 if (!index_) {
32 // check if the coupon was right
33 const CappedFlooredAverageBMACoupon* c = dynamic_cast<const CappedFlooredAverageBMACoupon*>(&coupon);
34 QL_REQUIRE(c, "BlackAverageBMACouponPricer: CappedFlooredAverageBMACoupon required");
35 // coupon was right, index is not
36 QL_FAIL("BlackAverageBMACouponPricer: CappedFlooredAverageBMACoupon required");
37 }
38 swapletRate_ = coupon_->underlying()->rate();
39 forwardRate_ = (swapletRate_ - coupon_->underlying()->spread()) / coupon_->underlying()->gearing();
41}
42
43Real BlackAverageBMACouponPricer::optionletRate(Option::Type optionType, Real effStrike) const {
44 Date lastRelevantFixingDate = coupon_->underlying()->fixingDate();
45 if (lastRelevantFixingDate <= Settings::instance().evaluationDate()) {
46 // the amount is determined
47 Real a, b;
48 if (optionType == Option::Call) {
49 a = forwardRate_;
50 b = effStrike;
51 } else {
52 a = effStrike;
53 b = forwardRate_;
54 }
55 return gearing_ * std::max(a - b, 0.0);
56 } else {
57 // not yet determined, use Black model
58 QL_REQUIRE(!capletVolatility().empty(), "BlackAverageBMACouponPricer: missing optionlet volatility");
59 std::vector<Date> fixingDates = coupon_->underlying()->fixingDates();
60 QL_REQUIRE(!fixingDates.empty(),
61 "BlackAverageBMACouponPricer: internal error, got empty fixingDates, contact dev.");
62 fixingDates.erase(std::next(fixingDates.end(), -1)); // there is one additional date returned!
63 QL_REQUIRE(!fixingDates.empty(), "BlackAverageBMACouponPricer: empty fixing dates");
64 bool shiftedLn = capletVolatility()->volatilityType() == ShiftedLognormal;
65 Real shift = capletVolatility()->displacement();
66 Real stdDev;
67 Real effectiveTime = capletVolatility()->timeFromReference(fixingDates.back());
69 // vol input is effective, i.e. we use a plain black model
70 stdDev = capletVolatility()->volatility(fixingDates.back(), effStrike) * std::sqrt(effectiveTime);
71 } else {
72 // vol input is not effective: we proceed similarly to average on coupon pricing:
73 // for the standard deviation see Lyashenko, Mercurio, Looking forward to backward looking rates,
74 // section 6.3. the idea is to dampen the average volatility sigma between the fixing start and fixing end
75 // date by a linear function going from (fixing start, 1) to (fixing end, 0)
76 Real fixingStartTime = capletVolatility()->timeFromReference(fixingDates.front());
77 Real fixingEndTime = capletVolatility()->timeFromReference(fixingDates.back());
78 Real sigma = capletVolatility()->volatility(
79 std::max(fixingDates.front(), capletVolatility()->referenceDate() + 1), effStrike);
80 Real T = std::max(fixingStartTime, 0.0);
81 if (!close_enough(fixingEndTime, T))
82 T += std::pow(fixingEndTime - T, 3.0) / std::pow(fixingEndTime - fixingStartTime, 2.0) / 3.0;
83 stdDev = sigma * std::sqrt(T);
84 }
85 if (optionType == Option::Type::Call)
86 effectiveCapletVolatility_ = stdDev / std::sqrt(effectiveTime);
87 else
88 effectiveFloorletVolatility_ = stdDev / std::sqrt(effectiveTime);
89 Real fixing = shiftedLn ? blackFormula(optionType, effStrike, forwardRate_, stdDev, 1.0, shift)
90 : bachelierBlackFormula(optionType, effStrike, forwardRate_, stdDev, 1.0);
91 return gearing_ * fixing;
92 }
93}
94
96
97Rate BlackAverageBMACouponPricer::capletRate(Rate effectiveCap) const {
98 return optionletRate(Option::Call, effectiveCap);
99}
100
101Rate BlackAverageBMACouponPricer::floorletRate(Rate effectiveFloor) const {
102 return optionletRate(Option::Put, effectiveFloor);
103}
104
106 QL_FAIL("BlackAverageBMACouponPricer::swapletPrice() not provided");
107}
108Real BlackAverageBMACouponPricer::capletPrice(Rate effectiveCap) const {
109 QL_FAIL("BlackAverageBMACouponPricer::capletPrice() not provided");
110}
111Real BlackAverageBMACouponPricer::floorletPrice(Rate effectiveFloor) const {
112 QL_FAIL("BlackAverageBMACouponPricer::floorletPrice() not provided");
113}
114
115} // namespace QuantExt
black average bma coupon pricer for capped / floored BMA coupons
Real capletPrice(Rate effectiveCap) const override
Rate floorletRate(Rate effectiveFloor) const override
const CappedFlooredAverageBMACoupon * coupon_
void initialize(const FloatingRateCoupon &coupon) override
Real optionletRate(Option::Type optionType, Real effStrike) const
Real floorletPrice(Rate effectiveFloor) const override
Rate capletRate(Rate effectiveCap) const override
Handle< OptionletVolatilityStructure > capletVolatility() const
ext::shared_ptr< AverageBMACoupon > underlying() const
Filter close_enough(const RandomVariable &x, const RandomVariable &y)