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

Commodity Swaption Analytical Engine. More...

#include <qle/pricingengines/commodityswaptionengine.hpp>

+ Inheritance diagram for CommoditySwaptionEngine:
+ Collaboration diagram for CommoditySwaptionEngine:

Public Member Functions

 CommoditySwaptionEngine (const Handle< YieldTermStructure > &discountCurve, const Handle< QuantLib::BlackVolTermStructure > &vol, Real beta=0.0)
 
void calculate () const override
 
- Public Member Functions inherited from CommoditySwaptionBaseEngine
 CommoditySwaptionBaseEngine (const Handle< YieldTermStructure > &discountCurve, const Handle< QuantLib::BlackVolTermStructure > &vol, Real beta=0.0)
 

Private Member Functions

QuantLib::Real expA (QuantLib::Size floatLegIndex, QuantLib::Real normFactor) const
 
QuantLib::Real expASquared (QuantLib::Size floatLegIndex, QuantLib::Real strike, QuantLib::Real normFactor) const
 
QuantLib::Real crossTerms (const QuantLib::ext::shared_ptr< QuantLib::CashFlow > &cf_1, const QuantLib::ext::shared_ptr< QuantLib::CashFlow > &cf_2, bool isAveraging, QuantLib::Real strike, QuantLib::Real normFactor) const
 
QuantLib::Real maxQuantity (QuantLib::Size floatLegIndex) const
 

Additional Inherited Members

- Protected Member Functions inherited from CommoditySwaptionBaseEngine
QuantLib::Size fixedLegIndex () const
 
QuantLib::Real fixedLegValue (QuantLib::Size fixedLegIndex) const
 Give back the fixed leg price at the swaption expiry time. More...
 
QuantLib::Real strike (QuantLib::Size fixedLegIndex) const
 
QuantLib::Real rho (const QuantLib::Date &ed_1, const QuantLib::Date &ed_2) const
 
bool averaging (QuantLib::Size floatLegIndex) const
 
- Protected Attributes inherited from CommoditySwaptionBaseEngine
Handle< YieldTermStructure > discountCurve_
 
Handle< QuantLib::BlackVolTermStructure > volStructure_
 
Real beta_
 

Detailed Description

Commodity Swaption Analytical Engine.

Analytical pricing based on the two-moment Turnbull-Wakeman approximation similar to APO pricing. Reference: Iain Clark, Commodity Option Pricing, Wiley, section 2.8 See also the documentation in the ORE+ product catalogue.

Definition at line 79 of file commodityswaptionengine.hpp.

Constructor & Destructor Documentation

◆ CommoditySwaptionEngine()

CommoditySwaptionEngine ( const Handle< YieldTermStructure > &  discountCurve,
const Handle< QuantLib::BlackVolTermStructure > &  vol,
Real  beta = 0.0 
)

Definition at line 81 of file commodityswaptionengine.hpp.

83 : CommoditySwaptionBaseEngine(discountCurve, vol, beta) {}
CommoditySwaptionBaseEngine(const Handle< YieldTermStructure > &discountCurve, const Handle< QuantLib::BlackVolTermStructure > &vol, Real beta=0.0)

Member Function Documentation

◆ calculate()

void calculate ( ) const
override

Definition at line 167 of file commodityswaptionengine.cpp.

167 {
168
169 // Get the index of the fixed and floating leg
170 Size idxFixed = fixedLegIndex();
171 Size idxFloat = idxFixed == 0 ? 1 : 0;
172
173 // Fixed leg value is the strike
174 Real kStar = fixedLegValue(idxFixed);
175
176 // Max quantity is used as a normalisation factor
177 Real normFactor = maxQuantity(idxFloat);
178
179 // E[A(t_e)] from the ORE+ Product Catalogue
180 Real EA = expA(idxFloat, normFactor);
181
182 // Fixed leg strike price. This determines the strike at which we query the volatility surface in the
183 // calculations. The implementation here just looks at the fixed price in the first period of the fixed leg. If
184 // we have an underlying swap where the fixed price varies a lot over different calculation periods, this may
185 // lead to a mispricing.
186 Real strikePrice = strike(idxFixed);
187
188 // E[A^2(t_e)] from the ORE+ Product Catalogue
189 Real EAA = expASquared(idxFloat, strikePrice, normFactor);
190
191 // Discount factor to the exercise date, P(0, t_e) from the ORE+ Product Catalogue
192 Date exercise = arguments_.exercise->dateAt(0);
193 Real discountExercise = discountCurve_->discount(exercise);
194
195 // Time to swaption expiry, t_e from the ORE+ Product Catalogue
196 Time t_e = volStructure_->timeFromReference(exercise);
197
198 // sigma_X from the ORE+ Product Catalogue
199 Real sigmaX = sqrt(log(EAA / (EA * EA)) / t_e);
200
201 // We have used EA to get sigmaX so we can modify EA again now to remove the normalisation factor.
202 EA *= normFactor;
203
204 // If fixed leg payer flag value is -1 => payer swaption. In this case, we want \omega = 1 in black formula
205 // so we need type = Call.
206 Option::Type type = arguments_.payer[idxFixed] < 0 ? Option::Call : Option::Put;
207
208 // Populate the value using Black-76 formula as documented in ORE+ Product Catalogue
209 results_.value = blackFormula(type, kStar, EA, sigmaX * sqrt(t_e), discountExercise);
210
211 // Populate some additional results
212 results_.additionalResults["Sigma"] = sigmaX;
213 results_.additionalResults["Forward"] = EA;
214 results_.additionalResults["Strike"] = kStar;
215 results_.additionalResults["StrikePrice"] = strikePrice;
216 results_.additionalResults["Expiry"] = t_e;
217}
const Instrument::results * results_
Definition: cdsoption.cpp:81
Handle< QuantLib::BlackVolTermStructure > volStructure_
QuantLib::Real fixedLegValue(QuantLib::Size fixedLegIndex) const
Give back the fixed leg price at the swaption expiry time.
QuantLib::Real strike(QuantLib::Size fixedLegIndex) const
QuantLib::Real expASquared(QuantLib::Size floatLegIndex, QuantLib::Real strike, QuantLib::Real normFactor) const
QuantLib::Real expA(QuantLib::Size floatLegIndex, QuantLib::Real normFactor) const
QuantLib::Real maxQuantity(QuantLib::Size floatLegIndex) const
RandomVariable sqrt(RandomVariable x)
CompiledFormula log(CompiledFormula x)
Swap::arguments * arguments_
+ Here is the call graph for this function:

◆ expA()

Real expA ( QuantLib::Size  floatLegIndex,
QuantLib::Real  normFactor 
) const
private

Calculate the expected value of the floating leg at the swaption expiry date i.e. the expected value of the quantity A(t_e) from the ORE+ Product Catalogue. Quantities in the calculation are divided by the normFactor to guard against numerical blow up.

Definition at line 219 of file commodityswaptionengine.cpp.

219 {
220
221 // This is the quantity E[A(t_e)] in the ORE+ Product Catalogue.
222 Real value = 0.0;
223
224 for (const QuantLib::ext::shared_ptr<CashFlow>& cf : arguments_.legs[floatLegIndex]) {
225 value += cf->amount() * discountCurve_->discount(cf->date()) / normFactor;
226 }
227
228 Date exercise = arguments_.exercise->dateAt(0);
229 Real discountExercise = discountCurve_->discount(exercise);
230 value /= discountExercise;
231
232 return value;
233}
+ Here is the caller graph for this function:

◆ expASquared()

Real expASquared ( QuantLib::Size  floatLegIndex,
QuantLib::Real  strike,
QuantLib::Real  normFactor 
) const
private

Calculate the expected value of the floating leg squared at the swaption expiry date i.e. the expected value of the quantity A^2(t_e) from the ORE+ Product Catalogue. Quantities in the calculation are divided by the normFactor to guard against numerical blow up.

Definition at line 235 of file commodityswaptionengine.cpp.

235 {
236
237 // This is the quantity E[A^2(t_e)] in the ORE+ Product Catalogue.
238 Real value = 0.0;
239
240 // Is the floating leg averaging.
241 bool isAveraging = averaging(floatLegIndex);
242
243 // Calculate E[A^2(t_e)]
244 Size numPeriods = arguments_.legs[floatLegIndex].size();
245 for (Size i = 0; i < numPeriods; i++) {
246 for (Size j = 0; j <= i; j++) {
247 Real factor = i == j ? 1.0 : 2.0;
248 value += factor * crossTerms(arguments_.legs[floatLegIndex][i], arguments_.legs[floatLegIndex][j],
249 isAveraging, strike, normFactor);
250 }
251 }
252
253 // Divide through by P^2(0, t_e) to get quantity E[A^2(t_e)] in the ORE+ Product Catalogue.
254 Date exercise = arguments_.exercise->dateAt(0);
255 Real discountExercise = discountCurve_->discount(exercise);
256 value /= (discountExercise * discountExercise);
257
258 return value;
259}
bool averaging(QuantLib::Size floatLegIndex) const
QuantLib::Real crossTerms(const QuantLib::ext::shared_ptr< QuantLib::CashFlow > &cf_1, const QuantLib::ext::shared_ptr< QuantLib::CashFlow > &cf_2, bool isAveraging, QuantLib::Real strike, QuantLib::Real normFactor) const
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ crossTerms()

Real crossTerms ( const QuantLib::ext::shared_ptr< QuantLib::CashFlow > &  cf_1,
const QuantLib::ext::shared_ptr< QuantLib::CashFlow > &  cf_2,
bool  isAveraging,
QuantLib::Real  strike,
QuantLib::Real  normFactor 
) const
private

Calculate the cross terms involved in expASquared. Quantities in the calculation are divided by the normFactor to guard against numerical blow up.

Definition at line 261 of file commodityswaptionengine.cpp.

263 {
264
265 // Time to swaption expiry
266 Time t_e = volStructure_->timeFromReference(arguments_.exercise->dateAt(0));
267
268 // Switch depending on whether the two cashflows are averaging or not
269 if (isAveraging) {
270
271 // Must have CommodityIndexedAverageCashFlow if averaging
272 QuantLib::ext::shared_ptr<CommodityIndexedAverageCashFlow> ccf_1 =
273 QuantLib::ext::dynamic_pointer_cast<CommodityIndexedAverageCashFlow>(cf_1);
274 QuantLib::ext::shared_ptr<CommodityIndexedAverageCashFlow> ccf_2 =
275 QuantLib::ext::dynamic_pointer_cast<CommodityIndexedAverageCashFlow>(cf_2);
276
277 // Product of quantities
278 Real result = ccf_1->periodQuantity() / normFactor;
279 result *= ccf_2->periodQuantity() / normFactor;
280
281 // Multiply by discount factor to payment date
282 result *= discountCurve_->discount(ccf_1->date());
283 result *= discountCurve_->discount(ccf_2->date());
284
285 // Divide by number of observations
286 result /= ccf_1->indices().size();
287 result /= ccf_2->indices().size();
288
289 // The cross terms due to the averaging in each cashflow's period. The calculation here differs
290 // depending on whether cashflows reference a future price or spot.
291 Real cross = 0.0;
292 if (ccf_1->useFuturePrice()) {
293
294 std::vector<Real> prices1(ccf_1->indices().size()), prices2(ccf_2->indices().size()),
295 vol1(ccf_1->indices().size()), vol2(ccf_2->indices().size());
296 std::vector<Date> expiries1(ccf_1->indices().size()), expiries2(ccf_2->indices().size());
297
298 Size i = 0;
299 for (const auto& [d, p] : ccf_1->indices()) {
300 expiries1[i] = p->expiryDate();
301 Real fxRate{1.};
302 if (ccf_1->fxIndex())
303 fxRate = ccf_1->fxIndex()->fixing(expiries1[i]);
304 prices1[i] = fxRate * p->fixing(expiries1[i]);
305 vol1[i] = volStructure_->blackVol(expiries1[i], strike);
306 ++i;
307 }
308
309 i = 0;
310 for (const auto& [d, p] : ccf_2->indices()) {
311 expiries2[i] = p->expiryDate();
312 Real fxRate{1.};
313 if (ccf_2->fxIndex())
314 fxRate = ccf_2->fxIndex()->fixing(expiries2[i]);
315 prices2[i] = fxRate * p->fixing(expiries2[i]);
316 vol2[i] = volStructure_->blackVol(expiries2[i], strike);
317 ++i;
318 }
319
320 for (Size i = 0; i < prices1.size(); ++i) {
321 for (Size j = 0; j < prices2.size(); ++j) {
322 cross += prices1[i] * prices2[j] * exp(rho(expiries1[i], expiries2[j]) * vol1[i] * vol2[j] * t_e);
323 }
324 }
325
326 } else {
327
328 std::vector<Real> prices1(ccf_1->indices().size()), prices2(ccf_2->indices().size());
329
330 Size i = 0;
331 for (const auto& [d, p] : ccf_1->indices()) {
332 Real fxRate{1.};
333 if (ccf_1->fxIndex())
334 fxRate = ccf_1->fxIndex()->fixing(d);
335 prices1[i] = fxRate * p->fixing(d);
336 ++i;
337 }
338
339 i = 0;
340 for (const auto& [d, p] : ccf_2->indices()) {
341 Real fxRate{1.};
342 if (ccf_2->fxIndex())
343 fxRate = ccf_2->fxIndex()->fixing(d);
344 prices2[i] = fxRate * p->fixing(d);
345 ++i;
346 }
347
348 for (Size i = 0; i < prices1.size(); ++i) {
349 for (Size j = 0; j < prices2.size(); ++j) {
350 cross += prices1[i] * prices2[j];
351 }
352 }
353
354 cross *= exp(volStructure_->blackVariance(t_e, strike));
355 }
356 result *= cross;
357
358 return result;
359
360 } else {
361
362 // Must have CommodityIndexedCashFlow if not averaging
363 QuantLib::ext::shared_ptr<CommodityIndexedCashFlow> ccf_1 = QuantLib::ext::dynamic_pointer_cast<CommodityIndexedCashFlow>(cf_1);
364 QuantLib::ext::shared_ptr<CommodityIndexedCashFlow> ccf_2 = QuantLib::ext::dynamic_pointer_cast<CommodityIndexedCashFlow>(cf_2);
365
366 // Don't support non-zero spreads or gearing != 1 so amount gives forward * quantity for commodity cashflow
367 // referencing spot price or future * quantity for commodity cashflow referencing future price
368 Real result = ccf_1->amount() / normFactor;
369 result *= ccf_2->amount() / normFactor;
370
371 // Multiply by discount factor to payment date
372 result *= discountCurve_->discount(ccf_1->date());
373 result *= discountCurve_->discount(ccf_2->date());
374
375 // Multiply by exp{v} where the quantity v depends on whether commodity is spot or future
376 Real v = 0.0;
377 if (ccf_1->useFuturePrice()) {
378
379 Date e_1 = ccf_1->index()->expiryDate();
380 v = volStructure_->blackVol(e_1, strike);
381
382 Date e_2 = ccf_2->index()->expiryDate();
383 if (e_1 == e_2) {
384 v *= v;
385 } else {
386 v *= volStructure_->blackVol(e_2, strike);
387 v *= rho(e_1, e_2);
388 }
389
390 v *= t_e;
391
392 } else {
393 v = volStructure_->blackVariance(t_e, strike);
394 }
395 result *= exp(v);
396
397 return result;
398 }
399}
QuantLib::Real rho(const QuantLib::Date &ed_1, const QuantLib::Date &ed_2) const
CompiledFormula exp(CompiledFormula x)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maxQuantity()

Real maxQuantity ( QuantLib::Size  floatLegIndex) const
private

Return the maximum quantity over all cashflows on the commodity floating leg. This is used as a normalisation factor in the calculation of E[A(t_e)] and E[A^2(t_e)] to guard against blow up.

Definition at line 401 of file commodityswaptionengine.cpp.

401 {
402
403 Real result = 0.0;
404
405 if (averaging(floatLegIndex)) {
406 for (const auto& cf : arguments_.legs[floatLegIndex]) {
407 auto ccf = QuantLib::ext::dynamic_pointer_cast<CommodityIndexedAverageCashFlow>(cf);
408 QL_REQUIRE(ccf, "maxQuantity: expected a CommodityIndexedAverageCashFlow");
409 result = max(result, ccf->periodQuantity());
410 }
411 } else {
412 for (const auto& cf : arguments_.legs[floatLegIndex]) {
413 auto ccf = QuantLib::ext::dynamic_pointer_cast<CommodityIndexedCashFlow>(cf);
414 QL_REQUIRE(ccf, "maxQuantity: expected a CommodityIndexedCashFlow");
415 result = max(result, ccf->periodQuantity());
416 }
417 }
418
419 QL_REQUIRE(result > 0.0, "maxQuantity: quantities should be greater than 0.0");
420
421 return result;
422}
CompiledFormula max(CompiledFormula x, const CompiledFormula &y)
+ Here is the call graph for this function:
+ Here is the caller graph for this function: