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

Commodity Swaption Monte Carlo Engine. More...

#include <qle/pricingengines/commodityswaptionengine.hpp>

+ Inheritance diagram for CommoditySwaptionMonteCarloEngine:
+ Collaboration diagram for CommoditySwaptionMonteCarloEngine:

Public Member Functions

 CommoditySwaptionMonteCarloEngine (const Handle< YieldTermStructure > &discountCurve, const Handle< QuantLib::BlackVolTermStructure > &vol, Size samples, Real beta=0.0, const Size seed=42)
 
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

void calculateSpot (QuantLib::Size idxFixed, QuantLib::Size idxFloat, QuantLib::Real strike) const
 Calculations when underlying swap references a commodity spot price. More...
 
void calculateFuture (QuantLib::Size idxFixed, QuantLib::Size idxFloat, QuantLib::Real strike) const
 Calculations when underlying swap references a commodity spot price. More...
 
QuantLib::Real spotFloatLegFactor (QuantLib::Size idxFloat, QuantLib::Real discountExercise) const
 
void futureFloatLegFactors (QuantLib::Size idxFloat, QuantLib::Real discountExercise, const std::vector< QuantLib::Date > &expiries, QuantLib::Matrix &floatLegFactors, QuantLib::Array &discounts, QuantLib::Array &amounts) const
 
std::map< QuantLib::Date, QuantLib::Real > futureExpiries (QuantLib::Size idxFloat, QuantLib::Matrix &outSqrtCorr, QuantLib::Real strike) const
 

Private Attributes

Size samples_
 
long seed_
 

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 Monte Carlo Engine.

Monte Carlo implementation of the Swaption payoff for as documented in ORE+ Product Catalogue. Reference: Iain Clark, Commodity Option Pricing, Wiley, section 2.8

Definition at line 116 of file commodityswaptionengine.hpp.

Constructor & Destructor Documentation

◆ CommoditySwaptionMonteCarloEngine()

CommoditySwaptionMonteCarloEngine ( const Handle< YieldTermStructure > &  discountCurve,
const Handle< QuantLib::BlackVolTermStructure > &  vol,
Size  samples,
Real  beta = 0.0,
const Size  seed = 42 
)

Definition at line 118 of file commodityswaptionengine.hpp.

121 : CommoditySwaptionBaseEngine(discountCurve, vol, beta), samples_(samples), seed_(seed) {}
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 424 of file commodityswaptionengine.cpp.

424 {
425
426 // Get the index of the fixed and floating leg
427 Size idxFixed = fixedLegIndex();
428 Size idxFloat = idxFixed == 0 ? 1 : 0;
429
430 // Determines the strike at which we read values from the vol surface.
431 Real strikePrice = strike(idxFixed);
432
433 // Switch implementation depending on whether underlying swap references a spot or future price
434 if (referencesFuturePrice(arguments_.legs[idxFloat])) {
435 calculateFuture(idxFixed, idxFloat, strikePrice);
436 } else {
437 calculateSpot(idxFixed, idxFloat, strikePrice);
438 }
439}
QuantLib::Real strike(QuantLib::Size fixedLegIndex) const
void calculateSpot(QuantLib::Size idxFixed, QuantLib::Size idxFloat, QuantLib::Real strike) const
Calculations when underlying swap references a commodity spot price.
void calculateFuture(QuantLib::Size idxFixed, QuantLib::Size idxFloat, QuantLib::Real strike) const
Calculations when underlying swap references a commodity spot price.
Swap::arguments * arguments_
+ Here is the call graph for this function:

◆ calculateSpot()

void calculateSpot ( QuantLib::Size  idxFixed,
QuantLib::Size  idxFloat,
QuantLib::Real  strike 
) const
private

Calculations when underlying swap references a commodity spot price.

Definition at line 441 of file commodityswaptionengine.cpp.

441 {
442
443 // Fixed leg value
444 Real valueFixedLeg = fixedLegValue(idxFixed);
445
446 // If float leg payer flag is 1 (-1) => rec (pay) float and pay (rec) fixed => omega = 1 (-1) for payer (receiver).
447 Real omega = arguments_.payer[idxFloat];
448
449 // Time to swaption expiry
450 Date exercise = arguments_.exercise->dateAt(0);
451 Time t_e = volStructure_->timeFromReference(exercise);
452
453 // Discount to exercise
454 Real discountExercise = discountCurve_->discount(exercise);
455
456 // Variance of spot process to expiry
457 Real var = volStructure_->blackVariance(t_e, strike);
458 Real stdDev = sqrt(var);
459
460 // Sample spot is S_i(t_e) = F(0, t_e) exp(-var / 2) exp(stdDev z_i). factor is second two term here.
461 Real factor = exp(-var / 2.0);
462
463 // Populate these values below
464 Real optionValue = 0.0;
465 Real swapValue = 0.0;
466 Real floatLegValue = 0.0;
467
468 // Create the standard normal RNG
469 LowDiscrepancy::rsg_type rsg = LowDiscrepancy::make_sequence_generator(1, seed_);
470
471 // Get the value that when multiplied by the sample value gives the float leg value
472 Real floatFactor = spotFloatLegFactor(idxFloat, discountExercise);
473
474 // Value swap at swaption exercise date over a number, i = 1, ..., N of samples i.e. \hat{V}_i(t_e) from ORE+
475 // Product Catalogue. Value of the swaption at time 0 is then P(0, t_e) \sum_{i=1}^{N} \hat{V}^{+}_i(t_e) / N
476 for (Size i = 0; i < samples_; i++) {
477
478 // Sample value is S_i(t_e) / F(0, t_e) = exp(-var / 2) exp(stdDev z_i).
479 Real sample = factor * exp(stdDev * rsg.nextSequence().value[0]);
480
481 // Calculate float leg value and swap value given sample
482 Real sampleFloatLegValue = floatFactor * sample;
483 Real sampleSwapValue = omega * (sampleFloatLegValue - valueFixedLeg);
484
485 // Update values dividing by samples each time to guard against blow up.
486 optionValue += max(sampleSwapValue, 0.0) / samples_;
487 swapValue += sampleSwapValue / samples_;
488 floatLegValue += sampleFloatLegValue / samples_;
489 }
490
491 // Populate the results remembering to multiply by P(0, t_e)
492 results_.value = discountExercise * optionValue;
493 results_.additionalResults["SwapNPV"] = discountExercise * swapValue;
494 results_.additionalResults["FixedLegNPV"] = discountExercise * valueFixedLeg;
495 results_.additionalResults["FloatingLegNPV"] = discountExercise * floatLegValue;
496}
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 spotFloatLegFactor(QuantLib::Size idxFloat, QuantLib::Real discountExercise) const
RandomVariable sqrt(RandomVariable x)
CompiledFormula exp(CompiledFormula x)
CompiledFormula max(CompiledFormula x, const CompiledFormula &y)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ calculateFuture()

void calculateFuture ( QuantLib::Size  idxFixed,
QuantLib::Size  idxFloat,
QuantLib::Real  strike 
) const
private

Calculations when underlying swap references a commodity spot price.

Definition at line 498 of file commodityswaptionengine.cpp.

498 {
499
500 // Fixed leg value
501 Real valueFixedLeg = fixedLegValue(idxFixed);
502
503 // Unique expiry dates
504 Matrix sqrtCorr;
505 map<Date, Real> expiries = futureExpiries(idxFloat, sqrtCorr, strike);
506
507 // Generate n _independent_ standard normal variables {Z_{i,1}, ..., Z_{i,n}} where n is the number of future
508 // contracts that we are modelling and i = 1, ..., N is the number of samples. It is a speed-up to set n = 1 if
509 // correlation between all the future contracts is 1.0 i.e. beta_ = 0.0. We don't do this and prefer code clarity.
510 LowDiscrepancy::rsg_type rsg = LowDiscrepancy::make_sequence_generator(expiries.size(), seed_);
511
512 // If float leg payer flag is 1 (-1) => rec (pay) float and pay (rec) fixed => omega = 1 (-1) for payer (receiver).
513 Real omega = arguments_.payer[idxFloat];
514
515 // Time to swaption expiry
516 Date exercise = arguments_.exercise->dateAt(0);
517 Time t_e = volStructure_->timeFromReference(exercise);
518
519 // Discount to exercise
520 Real discountExercise = discountCurve_->discount(exercise);
521
522 // Precalculate exp{-var_j / 2} and stdDev_j mentioned in comment above
523 vector<Real> expVar;
524 vector<Real> stdDev;
525 vector<Date> expiryDates;
526 for (const auto& p : expiries) {
527 Real var = p.second * p.second * t_e;
528 expVar.push_back(exp(-var / 2.0));
529 stdDev.push_back(sqrt(var));
530 expiryDates.push_back(p.first);
531 }
532
533 // Populate these values below
534 Real optionValue = 0.0;
535 Real swapValue = 0.0;
536 Real floatLegValue = 0.0;
537
538 // Values that will be used to calculate floating leg value on each iteration. We precalculate as much as
539 // possible here to avoid calculation on each iteration.
540 Size numCfs = arguments_.legs[idxFloat].size();
541 Matrix factors(numCfs, expiries.size(), 0.0);
542 Array discounts(numCfs, 0.0);
543 Array amounts(numCfs, 0.0);
544 futureFloatLegFactors(idxFloat, discountExercise, expiryDates, factors, discounts, amounts);
545 Array factor = (discounts * amounts) * factors;
546
547 // Value swap at swaption exercise date over a number, i = 1, ..., N of samples i.e. \hat{V}_i(t_e) from ORE+
548 // Product Catalogue. Value of the swaption at time 0 is then P(0, t_e) \sum_{i=1}^{N} \hat{V}^{+}_i(t_e) / N
549 for (Size i = 0; i < samples_; i++) {
550
551 // sequence is n independent standard normal random variables
552 vector<Real> sequence = rsg.nextSequence().value;
553
554 // w holds n correlated standard normal variables
555 Array w = sqrtCorr * Array(sequence.begin(), sequence.end());
556
557 // Update w to hold the sample value that we want i.e.
558 // F_{i,j}(t_e) / F_{i,j}(0) = exp{-var_j / 2} exp{stdDev_j w_{i,j}}
559 // where j = 1,..., n indexes the futures (i.e. date keys in the map)
560 // and i = 1,..., N indexes the number of Monte Carlo samples.
561 for (Size i = 0; i < w.size(); i++) {
562 w[i] = exp(stdDev[i] * w[i]) * expVar[i];
563 }
564
565 // Calculate float leg value on this iteration
566 Real sampleFloatLegValue = DotProduct(factor, w);
567
568 // Calculate the swap leg value on this iteration
569 Real sampleSwapValue = omega * (sampleFloatLegValue - valueFixedLeg);
570
571 // Update values dividing by samples each time to guard against blow up.
572 optionValue += max(sampleSwapValue, 0.0) / samples_;
573 swapValue += sampleSwapValue / samples_;
574 floatLegValue += sampleFloatLegValue / samples_;
575 }
576
577 // Populate the results remembering to multiply by P(0, t_e)
578 results_.value = discountExercise * optionValue;
579 results_.additionalResults["SwapNPV"] = discountExercise * swapValue;
580 results_.additionalResults["FixedLegNPV"] = discountExercise * valueFixedLeg;
581 results_.additionalResults["FloatingLegNPV"] = discountExercise * floatLegValue;
582}
void futureFloatLegFactors(QuantLib::Size idxFloat, QuantLib::Real discountExercise, const std::vector< QuantLib::Date > &expiries, QuantLib::Matrix &floatLegFactors, QuantLib::Array &discounts, QuantLib::Array &amounts) const
std::map< QuantLib::Date, QuantLib::Real > futureExpiries(QuantLib::Size idxFloat, QuantLib::Matrix &outSqrtCorr, QuantLib::Real strike) const
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ spotFloatLegFactor()

Real spotFloatLegFactor ( QuantLib::Size  idxFloat,
QuantLib::Real  discountExercise 
) const
private

Calculate the underlying spot float leg factor value at expiry time. This quantity will be multiplied by a sample value on each Monte Carlo iteration to give the swap float leg value.

Definition at line 584 of file commodityswaptionengine.cpp.

584 {
585
586 // Different float leg valuation depending on whether leg is averaging or not.
587 Real floatLegValue = 0.0;
588 if (averaging(idxFloat)) {
589 for (const auto& cf : arguments_.legs[idxFloat]) {
590 auto ccf = QuantLib::ext::dynamic_pointer_cast<CommodityIndexedAverageCashFlow>(cf);
591 QL_REQUIRE(ccf, "spotSwapValue: expected a CommodityIndexedAverageCashFlow");
592 Real disc = discountCurve_->discount(ccf->date());
593 floatLegValue += disc * ccf->amount();
594 }
595 } else {
596 for (const auto& cf : arguments_.legs[idxFloat]) {
597 auto ccf = QuantLib::ext::dynamic_pointer_cast<CommodityIndexedCashFlow>(cf);
598 QL_REQUIRE(ccf, "spotSwapValue: expected a CommodityIndexedCashFlow");
599 Real disc = discountCurve_->discount(ccf->date());
600 floatLegValue += disc * ccf->amount();
601 }
602 }
603
604 // Divide floating leg value by discount to swaption expiry
605 floatLegValue /= discountExercise;
606
607 // Return the swap value at the expiry date t_e
608 return floatLegValue;
609}
bool averaging(QuantLib::Size floatLegIndex) const
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ futureFloatLegFactors()

void futureFloatLegFactors ( QuantLib::Size  idxFloat,
QuantLib::Real  discountExercise,
const std::vector< QuantLib::Date > &  expiries,
QuantLib::Matrix &  floatLegFactors,
QuantLib::Array &  discounts,
QuantLib::Array &  amounts 
) const
private

Populate the factors that we need to value the floating leg of a swap referencing a future contract given a Monte Carlo sample. This is to avoid recalculation of these quantities on each iteration.

Definition at line 611 of file commodityswaptionengine.cpp.

613 {
614
615 Size numCfs = arguments_.legs[idxFloat].size();
616
617 QL_REQUIRE(numCfs == floatLegFactors.rows(),
618 "Expected number of rows in floatLegFactors to equal number of cashflows");
619 QL_REQUIRE(numCfs == discounts.size(), "Expected size of discounts to equal number of cashflows");
620 QL_REQUIRE(numCfs == amounts.size(), "Expected size of amounts to equal number of cashflows");
621 QL_REQUIRE(expiries.size() == floatLegFactors.columns(),
622 "Expected number of columns in floatLegFactors to equal number of expiries");
623
624 // Different float leg valuation depending on whether leg is averaging or not.
625 if (averaging(idxFloat)) {
626 for (Size i = 0; i < numCfs; i++) {
627 auto ccf = QuantLib::ext::dynamic_pointer_cast<CommodityIndexedAverageCashFlow>(arguments_.legs[idxFloat][i]);
628 QL_REQUIRE(ccf, "futureFloatLegFactors: expected a CommodityIndexedAverageCashFlow");
629 Size numObs = ccf->indices().size();
630 for (const auto& p : ccf->indices()) {
631 Date expiry = p.second->expiryDate();
632 auto it = find(expiries.begin(), expiries.end(), expiry);
633 QL_REQUIRE(it != expiries.end(), "futureFloatLegFactors: expected to find expiry in expiries vector");
634 auto idx = distance(expiries.begin(), it);
635 Real fxRate{1.};
636 if (ccf->fxIndex())
637 fxRate = ccf->fxIndex()->fixing(expiry);
638 floatLegFactors[i][idx] += fxRate * p.second->fixing(expiry) / numObs;
639 }
640 discounts[i] = discountCurve_->discount(ccf->date());
641 amounts[i] = ccf->periodQuantity();
642 }
643 } else {
644 for (Size i = 0; i < numCfs; i++) {
645 auto ccf = QuantLib::ext::dynamic_pointer_cast<CommodityIndexedCashFlow>(arguments_.legs[idxFloat][i]);
646 QL_REQUIRE(ccf, "futureFloatLegFactors: expected a CommodityIndexedCashFlow");
647
648 auto it = find(expiries.begin(), expiries.end(), ccf->index()->expiryDate());
649 QL_REQUIRE(it != expiries.end(), "futureFloatLegFactors: expected to find expiry in expiries vector");
650 auto idx = distance(expiries.begin(), it);
651 floatLegFactors[i][idx] = 1.0;
652 discounts[i] = discountCurve_->discount(ccf->date());
653 amounts[i] = ccf->amount();
654 }
655 }
656
657 // Divide discounts by discount to swaption expiry
658 for (Size i = 0; i < discounts.size(); i++) {
659 discounts[i] /= discountExercise;
660 }
661}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ futureExpiries()

map< Date, Real > futureExpiries ( QuantLib::Size  idxFloat,
QuantLib::Matrix &  outSqrtCorr,
QuantLib::Real  strike 
) const
private

Given the index of the floating leg idxFloat, populate a map where the keys are the unique expiry dates of the future contracts referenced in the floating leg and the values are the volatilities associated with the future contract. Also, the matrix outSqrtCorr is poulated with the square root of the correlation matrix between the expiries.

An exception is thrown if the floating leg is not referencing a commodity future price.

Definition at line 663 of file commodityswaptionengine.cpp.

664 {
665
666 // Will hold the result
667 map<Date, Real> result;
668
669 // Populate the unique future expiry dates referenced on the floating leg
670 if (averaging(idxFloat)) {
671 for (const auto& cf : arguments_.legs[idxFloat]) {
672 auto ccf = QuantLib::ext::dynamic_pointer_cast<CommodityIndexedAverageCashFlow>(cf);
673 QL_REQUIRE(ccf, "futureExpiries: expected a CommodityIndexedAverageCashFlow");
674 QL_REQUIRE(ccf->useFuturePrice(), "futureExpiries: expected the cashflow to reference a future price");
675 for (const auto& p : ccf->indices()) {
676 result[p.second->expiryDate()] = 0.0;
677 }
678 }
679 } else {
680 for (const auto& cf : arguments_.legs[idxFloat]) {
681 auto ccf = QuantLib::ext::dynamic_pointer_cast<CommodityIndexedCashFlow>(cf);
682 QL_REQUIRE(ccf, "futureExpiries: expected a CommodityIndexedCashFlow");
683 QL_REQUIRE(ccf->useFuturePrice(), "futureExpiries: expected the cashflow to reference a future price");
684 result[ccf->index()->expiryDate()] = 0.0;
685 }
686 }
687
688 // Populate the map values i.e. the instantaneous volatility associated with the future contract whose expiry date
689 // is the map key. Here, we make the simplifying assumption that the volatility can be read from the volatility
690 // term structure at the future contract's expiry date. In most cases, if the volatility term structure is built
691 // from options on futures, the option contract expiry will be a number of days before the future contract expiry
692 // and we should really read off the term structure at that date. Also populate a temp vector containing the key
693 // dates for use in the loop below where we populate the sqrt correlation matrix.
694 vector<Date> expiryDates;
695 for (auto& p : result) {
696 p.second = volStructure_->blackVol(p.first, strike);
697 expiryDates.push_back(p.first);
698 }
699
700 // Populate the outSqrtCorr matrix
701 outSqrtCorr = Matrix(expiryDates.size(), expiryDates.size(), 1.0);
702 for (Size i = 0; i < expiryDates.size(); i++) {
703 for (Size j = 0; j < i; j++) {
704 outSqrtCorr[i][j] = outSqrtCorr[j][i] = rho(expiryDates[i], expiryDates[j]);
705 }
706 }
707 outSqrtCorr = pseudoSqrt(outSqrtCorr, SalvagingAlgorithm::None);
708
709 return result;
710}
QuantLib::Real rho(const QuantLib::Date &ed_1, const QuantLib::Date &ed_2) const
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Member Data Documentation

◆ samples_

Size samples_
private

Definition at line 153 of file commodityswaptionengine.hpp.

◆ seed_

long seed_
private

Definition at line 154 of file commodityswaptionengine.hpp.