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

#include <qle/pricingengines/blackmultilegoptionengine.hpp>

+ Inheritance diagram for BlackMultiLegOptionEngineBase:
+ Collaboration diagram for BlackMultiLegOptionEngineBase:

Public Member Functions

 BlackMultiLegOptionEngineBase (const Handle< YieldTermStructure > &discountCurve, const Handle< SwaptionVolatilityStructure > &volatility)
 

Static Public Member Functions

static bool instrumentIsHandled (const MultiLegOption &m, std::vector< std::string > &messages)
 

Protected Member Functions

void calculate () const
 

Static Protected Member Functions

static bool instrumentIsHandled (const std::vector< Leg > &legs, const std::vector< bool > &payer, const std::vector< Currency > &currency, const QuantLib::ext::shared_ptr< Exercise > &exercise, const Settlement::Type &settlementType, const Settlement::Method &settlementMethod, std::vector< std::string > &messages)
 

Protected Attributes

Handle< YieldTermStructure > discountCurve_
 
Handle< SwaptionVolatilityStructurevolatility_
 
std::vector< Leg > legs_
 
std::vector< boolpayer_
 
std::vector< Currency > currency_
 
QuantLib::ext::shared_ptr< Exercise > exercise_
 
Settlement::Type settlementType_
 
Settlement::Method settlementMethod_
 
Real npv_
 
Real underlyingNpv_
 
std::map< std::string, boost::any > additionalResults_
 

Detailed Description

Definition at line 34 of file blackmultilegoptionengine.hpp.

Constructor & Destructor Documentation

◆ BlackMultiLegOptionEngineBase()

BlackMultiLegOptionEngineBase ( const Handle< YieldTermStructure > &  discountCurve,
const Handle< SwaptionVolatilityStructure > &  volatility 
)

Definition at line 35 of file blackmultilegoptionengine.cpp.

37 : discountCurve_(discountCurve), volatility_(volatility) {}
Handle< SwaptionVolatilityStructure > volatility_

Member Function Documentation

◆ instrumentIsHandled() [1/2]

bool instrumentIsHandled ( const MultiLegOption m,
std::vector< std::string > &  messages 
)
static

Definition at line 39 of file blackmultilegoptionengine.cpp.

39 {
40 return instrumentIsHandled(m.legs(), m.payer(), m.currency(), m.exercise(), m.settlementType(),
41 m.settlementMethod(), messages);
42}
static bool instrumentIsHandled(const MultiLegOption &m, std::vector< std::string > &messages)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ instrumentIsHandled() [2/2]

bool instrumentIsHandled ( const std::vector< Leg > &  legs,
const std::vector< bool > &  payer,
const std::vector< Currency > &  currency,
const QuantLib::ext::shared_ptr< Exercise > &  exercise,
const Settlement::Type &  settlementType,
const Settlement::Method &  settlementMethod,
std::vector< std::string > &  messages 
)
staticprotected

Definition at line 44 of file blackmultilegoptionengine.cpp.

49 {
50
51 bool isHandled = true;
52
53 // is there a unique pay currency and all interest rate indices are in this same currency?
54
55 for (Size i = 1; i < currency.size(); ++i) {
56 if (currency[0] != currency[i]) {
57 messages.push_back("NumericLgmMultilegOptionEngine: can only handle single currency underlyings, got " +
58 currency[0].code() + " on leg #1 and " + currency[i].code() + " on leg #" +
59 std::to_string(i + 1));
60 isHandled = false;
61 }
62 }
63
64 for (Size i = 0; i < legs.size(); ++i) {
65 for (Size j = 0; j < legs[i].size(); ++j) {
66 if (auto cpn = QuantLib::ext::dynamic_pointer_cast<FloatingRateCoupon>(legs[i][j])) {
67 if (cpn->index()->currency() != currency[0]) {
68 messages.push_back("NumericLgmMultilegOptionEngine: can only handle indices (" +
69 cpn->index()->name() + ") with same currency as unqiue pay currency (" +
70 currency[0].code());
71 }
72 }
73 }
74 }
75
76 // check coupon types
77
78 for (Size i = 0; i < legs.size(); ++i) {
79 for (Size j = 0; j < legs[i].size(); ++j) {
80 if (auto c = QuantLib::ext::dynamic_pointer_cast<Coupon>(legs[i][j])) {
81 if (!(QuantLib::ext::dynamic_pointer_cast<IborCoupon>(c) || QuantLib::ext::dynamic_pointer_cast<FixedRateCoupon>(c) ||
82 QuantLib::ext::dynamic_pointer_cast<QuantExt::OvernightIndexedCoupon>(c) ||
83 QuantLib::ext::dynamic_pointer_cast<QuantExt::AverageONIndexedCoupon>(c) ||
84 QuantLib::ext::dynamic_pointer_cast<QuantLib::AverageBMACoupon>(c) ||
85 QuantLib::ext::dynamic_pointer_cast<QuantExt::SubPeriodsCoupon1>(c))) {
86 messages.push_back(
87 "BlackMultilegOptionEngine: coupon type not handled, supported coupon types: Fix, "
88 "Ibor, ON comp, ON avg, BMA/SIFMA, subperiod. leg = " +
89 std::to_string(i) + " cf = " + std::to_string(j));
90 isHandled = false;
91 }
92 }
93 }
94 }
95
96 // check exercise type
97
98 isHandled = isHandled && (exercise == nullptr || exercise->type() == Exercise::European);
99
100 return isHandled;
101}

◆ calculate()

void calculate ( ) const
protected

Definition at line 103 of file blackmultilegoptionengine.cpp.

103 {
104
105 std::vector<std::string> messages;
107 "BlackMultiLegOptionEngineBase::calculate(): instrument is not handled: "
108 << boost::algorithm::join(messages, ", "));
109
110 // handle empty exercise
111
112 if (exercise_ == nullptr) {
113 npv_ = 0.0;
114 for (Size i = 0; i < legs_.size(); ++i) {
115 for (Size j = 0; j < legs_[i].size(); ++j) {
116 npv_ += legs_[i][j]->amount() * discountCurve_->discount(legs_[i][j]->date());
117 }
118 }
120 return;
121 }
122
123 // set exercise date and calculate exercise time on vol day counter
124
125 QL_REQUIRE(exercise_->dates().size() == 1,
126 "BlackMultiLegOptionEngineBase: expected 1 exercise dates, got " << exercise_->dates().size());
127 Date exerciseDate = exercise_->date(0);
128 Real exerciseTime = volatility_->timeFromReference(exerciseDate);
129
130 // filter on future coupons and store the necessary data,
131 // also determine fixed day count (one of the fixed cpn), earliest and latest accrual dates
132
133 struct CfInfo {
134 Real fixedRate = Null<Real>(); // for fixed cpn
135 Real floatingSpread = Null<Real>(); // for float cpn
136 Real floatingGearing = Null<Real>(); // for float cpn
137 Real nominal = Null<Real>(); // for all cpn
138 Real accrual = Null<Real>(); // for all cpn
139 Real amount = Null<Real>(); // for all cf
140 Real discountFactor = Null<Real>(); // for all cf
141 Date payDate = Null<Date>(); // for all cf
142 };
143
144 std::vector<CfInfo> cfInfo;
145
146 DayCounter fixedDayCount;
147 Date earliestAccrualStartDate = Date::maxDate();
148 Date latestAccrualEndDate = Date::minDate();
149
150 Size numFixed = 0, numFloat = 0;
151
152 for (Size i = 0; i < legs_.size(); ++i) {
153 for (Size j = 0; j < legs_[i].size(); ++j) {
154 Real payer = payer_[i] ? -1.0 : 1.0;
155 if (auto c = QuantLib::ext::dynamic_pointer_cast<Coupon>(legs_[i][j])) {
156 auto fixedCoupon = QuantLib::ext::dynamic_pointer_cast<FixedRateCoupon>(c);
157 if (fixedCoupon)
158 fixedDayCount = c->dayCounter();
159 if (c->accrualStartDate() >= exerciseDate) {
160 cfInfo.push_back(CfInfo());
161 earliestAccrualStartDate = std::min(earliestAccrualStartDate, c->accrualStartDate());
162 latestAccrualEndDate = std::max(latestAccrualEndDate, c->accrualEndDate());
163 if (fixedCoupon) {
164 cfInfo.back().fixedRate = fixedCoupon->rate();
165 cfInfo.back().nominal = c->nominal() * payer;
166 ++numFixed;
167 } else if (auto f = QuantLib::ext::dynamic_pointer_cast<FloatingRateCoupon>(c)) {
168 cfInfo.back().floatingSpread = f->spread();
169 cfInfo.back().floatingGearing = f->gearing();
170 cfInfo.back().nominal = f->nominal() * payer;
171 ++numFloat;
172 }
173 cfInfo.back().accrual = c->accrualPeriod();
174 cfInfo.back().discountFactor = discountCurve_->discount(c->date());
175 cfInfo.back().amount = c->amount() * payer;
176 cfInfo.back().payDate = c->date();
177 }
178 } else if (legs_[i][j]->date() > exerciseDate) {
179 cfInfo.push_back(CfInfo());
180 cfInfo.back().discountFactor = discountCurve_->discount(legs_[i][j]->date());
181 cfInfo.back().amount = legs_[i][j]->amount() * payer;
182 cfInfo.back().payDate = legs_[i][j]->date();
183 }
184 }
185 }
186
187 // if there are future floating coupons, but no fixed, we add a dummy fixed with 0 fixed rate to
188 // ensure we can calculate the ATM rate
189 if (numFloat > 0 && numFixed == 0) {
190 for (auto const& c : cfInfo) {
191 if (c.floatingSpread != Null<Real>()) {
192 cfInfo.push_back(CfInfo());
193 cfInfo.back().fixedRate = 0.0;
194 cfInfo.back().nominal = c.nominal * -1; // assume opposite direction
195 cfInfo.back().accrual = c.accrual;
196 cfInfo.back().discountFactor = c.discountFactor;
197 cfInfo.back().amount = 0.0;
198 cfInfo.back().payDate = c.payDate;
199 }
200 }
201 }
202
203 QL_REQUIRE(!fixedDayCount.empty(), "BlackMultiLegOptionEngineBase::calculate(): could not determine fixed day "
204 "counter - it appears no fixed coupons are present in the underlying.");
205 QL_REQUIRE(earliestAccrualStartDate != Date::maxDate() && latestAccrualEndDate != Date::minDate(),
206 "BlackMultiLegOptionEngineBase::calculate(): could not determine earliest / latest accrual start / end "
207 "dates. No coupons seems to be present in the underlying.");
208
209 // add the rebate (if any) as a fixed cashflow to the exercise-into underlying
210
211 if (auto rebatedExercise = QuantLib::ext::dynamic_pointer_cast<QuantExt::RebatedExercise>(exercise_)) {
212 cfInfo.push_back(CfInfo());
213 cfInfo.back().amount = rebatedExercise->rebate(0);
214 cfInfo.back().discountFactor = discountCurve_->discount(rebatedExercise->rebatePaymentDate(0));
215 }
216
217 // determine the pv-weighted fixed rate, the fixed bps, the fixed npv
218
219 Real weightedFixedRateNom = 0.0, weightedFixedRateDenom = 0.0;
220 Real fixedBps = 0.0, fixedNpv = 0.0;
221 for (auto const& c : cfInfo) {
222 if (c.fixedRate != Null<Real>()) {
223 Real w = c.nominal * c.accrual * c.discountFactor;
224 weightedFixedRateNom += c.fixedRate * w;
225 weightedFixedRateDenom += w;
226 fixedBps += w;
227 fixedNpv += c.amount * c.discountFactor;
228 }
229 }
230 Real weightedFixedRate = weightedFixedRateNom / weightedFixedRateDenom;
231
232 // determine the pv-weighted floating margin, the floating bps, the floating npv
233
234 Real weightedFloatingSpreadNom = 0.0, weightedFloatingSpreadDenom = 0.0;
235 Real floatingBps = 0.0, floatingNpv = 0.0;
236 for (auto const& c : cfInfo) {
237 if (c.floatingSpread != Null<Real>()) {
238 Real w = c.nominal * c.accrual * c.discountFactor;
239 weightedFloatingSpreadNom += c.floatingSpread * w;
240 weightedFloatingSpreadDenom += w;
241 floatingBps += w;
242 floatingNpv += c.amount * c.discountFactor;
243 }
244 }
245 Real weightedFloatingSpread = weightedFloatingSpreadNom / weightedFloatingSpreadDenom;
246
247 // determine the simple cf npv
248
249 Real simpleCfNpv = 0.0;
250 for (auto const& c : cfInfo) {
251 if (c.fixedRate == Null<Real>() && c.floatingSpread == Null<Real>()) {
252 simpleCfNpv += c.amount * c.discountFactor;
253 }
254 }
255
256 // determine the fair swap rate
257
258 Real atmForward = -floatingNpv / fixedBps;
259
260 // determine the spread correction
261
262 Real spreadCorrection = weightedFloatingSpread * std::abs(floatingBps / fixedBps);
263
264 Real effectiveAtmForward = atmForward - spreadCorrection;
265 Real effectiveFixedRate = weightedFixedRate - spreadCorrection;
266
267 // incorporate the simple cashflows (including the rebate)
268
269 Real simpleCfCorrection = 0.0;
270 for (auto const& c : cfInfo) {
271 if (c.fixedRate == Null<Real>() && c.floatingSpread == Null<Real>()) {
272 simpleCfCorrection += c.amount * c.discountFactor / fixedBps;
273 }
274 }
275
276 effectiveFixedRate += simpleCfCorrection;
277
278 // determine the annuity
279
280 Real annuity = 0.0;
281 if (settlementType_ == Settlement::Physical ||
282 (settlementType_ == Settlement::Cash && settlementMethod_ == Settlement::CollateralizedCashPrice)) {
283 annuity = std::abs(fixedBps);
284 } else if (settlementType_ == Settlement::Cash && settlementMethod_ == Settlement::ParYieldCurve) {
285 // we assume that the cash settlement date is equal to the swap start date
286 InterestRate sr(effectiveAtmForward, fixedDayCount, Compounded, Annual);
287 for (auto const& c : cfInfo) {
288 if (c.fixedRate != Null<Real>()) {
289 annuity += std::abs(c.nominal) * c.accrual *
290 sr.discountFactor(std::min(earliestAccrualStartDate, c.payDate), c.payDate);
291 }
292 }
293 annuity *= discountCurve_->discount(earliestAccrualStartDate);
294 } else {
295 QL_FAIL("BlackMultiLegOptionEngine: invalid (settlementType, settlementMethod) pair");
296 }
297
298 // determine the swap length
299
300 Real swapLength = volatility_->swapLength(earliestAccrualStartDate, latestAccrualEndDate);
301
302 // swapLength is rounded to whole months. To ensure we can read a variance
303 // and a shift from volatility_ we floor swapLength at 1/12 here therefore.
304 swapLength = std::max(swapLength, 1.0 / 12.0);
305
306 Real variance = volatility_->blackVariance(exerciseDate, swapLength, effectiveFixedRate);
307 Real displacement =
308 volatility_->volatilityType() == ShiftedLognormal ? volatility_->shift(exerciseDate, swapLength) : 0.0;
309
310 Real stdDev = std::sqrt(variance);
311 Real delta = 0.0, vega = 0.0;
312 Option::Type optionType = fixedBps > 0 ? Option::Put : Option::Call;
313
314 if (close_enough(annuity, 0.0)) {
315 npv_ = 0.0;
316 } else if (volatility_->volatilityType() == ShiftedLognormal) {
317 npv_ = blackFormula(optionType, effectiveFixedRate, effectiveAtmForward, stdDev, annuity, displacement);
318 vega = std::sqrt(exerciseTime) *
319 blackFormulaStdDevDerivative(effectiveFixedRate, effectiveAtmForward, stdDev, annuity, displacement);
320 delta = blackFormulaForwardDerivative(optionType, effectiveFixedRate, effectiveAtmForward, stdDev, annuity,
321 displacement);
322 } else {
323 npv_ = bachelierBlackFormula(optionType, effectiveFixedRate, effectiveAtmForward, stdDev, annuity);
324 vega = std::sqrt(exerciseTime) *
325 bachelierBlackFormulaStdDevDerivative(effectiveFixedRate, effectiveAtmForward, stdDev, annuity);
326 delta = bachelierBlackFormulaForwardDerivative(optionType, effectiveFixedRate, effectiveAtmForward, stdDev,
327 annuity);
328 }
329
330 underlyingNpv_ = fixedNpv + floatingNpv + simpleCfNpv;
331
332 additionalResults_["spreadCorrection"] = spreadCorrection;
333 additionalResults_["simpleCashflowCorrection"] = simpleCfCorrection;
334 additionalResults_["strike"] = effectiveFixedRate;
335 additionalResults_["atmForward"] = effectiveAtmForward;
336 additionalResults_["underlyingNPV"] = underlyingNpv_;
337 additionalResults_["annuity"] = annuity;
338 additionalResults_["timeToExpiry"] = exerciseTime;
339 additionalResults_["impliedVolatility"] = Real(stdDev / std::sqrt(exerciseTime));
340 additionalResults_["swapLength"] = swapLength;
341 additionalResults_["stdDev"] = stdDev;
342 additionalResults_["delta"] = delta;
343 additionalResults_["vega"] = vega;
344 additionalResults_["fixedNpv"] = fixedNpv;
345 additionalResults_["fixedBps"] = fixedBps;
346 additionalResults_["weightedFixedRate"] = weightedFixedRate;
347 additionalResults_["floatingNpv"] = floatingNpv;
348 additionalResults_["floatingBps"] = floatingBps;
349 additionalResults_["weightedFloatingSpread"] = weightedFloatingSpread;
350 additionalResults_["simpleCfNpv"] = simpleCfNpv;
351}
QuantLib::ext::shared_ptr< Exercise > exercise_
std::map< std::string, boost::any > additionalResults_
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
RandomVariable variance(const RandomVariable &r)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Member Data Documentation

◆ discountCurve_

Handle<YieldTermStructure> discountCurve_
protected

Definition at line 50 of file blackmultilegoptionengine.hpp.

◆ volatility_

Handle<SwaptionVolatilityStructure> volatility_
protected

Definition at line 51 of file blackmultilegoptionengine.hpp.

◆ legs_

std::vector<Leg> legs_
mutableprotected

Definition at line 54 of file blackmultilegoptionengine.hpp.

◆ payer_

std::vector<bool> payer_
mutableprotected

Definition at line 55 of file blackmultilegoptionengine.hpp.

◆ currency_

std::vector<Currency> currency_
mutableprotected

Definition at line 56 of file blackmultilegoptionengine.hpp.

◆ exercise_

QuantLib::ext::shared_ptr<Exercise> exercise_
mutableprotected

Definition at line 57 of file blackmultilegoptionengine.hpp.

◆ settlementType_

Settlement::Type settlementType_
mutableprotected

Definition at line 58 of file blackmultilegoptionengine.hpp.

◆ settlementMethod_

Settlement::Method settlementMethod_
mutableprotected

Definition at line 59 of file blackmultilegoptionengine.hpp.

◆ npv_

Real npv_
mutableprotected

Definition at line 62 of file blackmultilegoptionengine.hpp.

◆ underlyingNpv_

Real underlyingNpv_
protected

Definition at line 62 of file blackmultilegoptionengine.hpp.

◆ additionalResults_

std::map<std::string, boost::any> additionalResults_
mutableprotected

Definition at line 63 of file blackmultilegoptionengine.hpp.