21#include <ql/exercise.hpp>
22#include <ql/math/matrixutilities/choleskydecomposition.hpp>
23#include <ql/math/matrixutilities/pseudosqrt.hpp>
24#include <ql/math/randomnumbers/inversecumulativerng.hpp>
25#include <ql/math/randomnumbers/mt19937uniformrng.hpp>
26#include <ql/pricingengines/blackformula.hpp>
27#include <ql/processes/ornsteinuhlenbeckprocess.hpp>
40using QuantLib::CashFlow;
47template <
typename CommCashflow>
void checkCashflows(
const Leg& leg) {
48 for (
const QuantLib::ext::shared_ptr<CashFlow>& cf : leg) {
49 auto ccf = QuantLib::ext::dynamic_pointer_cast<CommCashflow>(cf);
50 QL_REQUIRE(ccf,
"checkCashflows: not all of the "
51 <<
"cashflows on the commodity floating leg are of the same type");
52 QL_REQUIRE(close(ccf->spread(), 0.0),
"checkCashflows: a non-zero spread on a commodity swap "
53 <<
"underlying a commodity swaption is not supported");
54 QL_REQUIRE(close(ccf->gearing(), 1.0),
"checkCashflows: a gearing different from 1.0 on a commodity swap "
55 <<
"underlying a commodity swaption is not supported");
61bool referencesFuturePrice(
const Leg& leg) {
62 QuantLib::ext::shared_ptr<CashFlow> cf = leg.front();
63 if (QuantLib::ext::shared_ptr<CommodityIndexedCashFlow> ccf = QuantLib::ext::dynamic_pointer_cast<CommodityIndexedCashFlow>(cf)) {
64 return ccf->useFuturePrice();
65 }
else if (QuantLib::ext::shared_ptr<CommodityIndexedAverageCashFlow> ccf =
66 QuantLib::ext::dynamic_pointer_cast<CommodityIndexedAverageCashFlow>(cf)) {
67 return ccf->useFuturePrice();
69 QL_FAIL(
"referencesFuturePrice: expected leg to be a commodity leg");
78 const Handle<BlackVolTermStructure>& vol, Real beta)
79 : discountCurve_(discountCurve), volStructure_(vol), beta_(beta) {
80 QL_REQUIRE(
beta_ >= 0.0,
"beta >= 0 required, found " <<
beta_);
88 bool haveFloatingLeg =
false;
90 QL_REQUIRE(
arguments_.legs.size() == 2,
"Two legs expected but found " <<
arguments_.legs.size());
93 for (Size i = 0; i < 2; i++) {
94 QuantLib::ext::shared_ptr<CashFlow> cf =
arguments_.legs[i].front();
95 if (QuantLib::ext::shared_ptr<CommodityIndexedAverageCashFlow> flow =
96 QuantLib::ext::dynamic_pointer_cast<CommodityIndexedAverageCashFlow>(cf)) {
97 haveFloatingLeg =
true;
98 checkCashflows<CommodityIndexedAverageCashFlow>(
arguments_.legs[i]);
99 }
else if (QuantLib::ext::shared_ptr<CommodityIndexedCashFlow> flow =
100 QuantLib::ext::dynamic_pointer_cast<CommodityIndexedCashFlow>(cf)) {
101 haveFloatingLeg =
true;
102 checkCashflows<CommodityIndexedCashFlow>(
arguments_.legs[i]);
108 QL_REQUIRE(haveFloatingLeg,
"CommoditySwaptionBaseEngine: expected the swap to have a commodity floating leg");
109 QL_REQUIRE(
fixedLegIndex != Null<Size>(),
"CommoditySwaptionBaseEngine: expected the swap to have a fixed leg");
124 Date exercise =
arguments_.exercise->dateAt(0);
126 value /= discountExercise;
138 QuantLib::ext::shared_ptr<CashFlow> cf =
arguments_.legs[idxFloat].front();
139 if (QuantLib::ext::shared_ptr<CommodityIndexedCashFlow> ccf = QuantLib::ext::dynamic_pointer_cast<CommodityIndexedCashFlow>(cf)) {
140 return amount / ccf->periodQuantity();
141 }
else if (QuantLib::ext::shared_ptr<CommodityIndexedAverageCashFlow> ccf =
142 QuantLib::ext::dynamic_pointer_cast<CommodityIndexedAverageCashFlow>(cf)) {
143 return amount / ccf->periodQuantity();
145 QL_FAIL(
"Expected a CommodityIndexedCashFlow or CommodityIndexedAverageCashFlow");
151 if (
beta_ == 0.0 || ed_1 == ed_2) {
156 return exp(-
beta_ * fabs(t_2 - t_1));
163 return static_cast<bool>(
164 QuantLib::ext::dynamic_pointer_cast<CommodityIndexedAverageCashFlow>(
arguments_.legs[floatLegIndex].front()));
171 Size idxFloat = idxFixed == 0 ? 1 : 0;
180 Real EA =
expA(idxFloat, normFactor);
186 Real strikePrice =
strike(idxFixed);
189 Real EAA =
expASquared(idxFloat, strikePrice, normFactor);
192 Date exercise =
arguments_.exercise->dateAt(0);
199 Real sigmaX =
sqrt(
log(EAA / (EA * EA)) / t_e);
206 Option::Type type =
arguments_.payer[idxFixed] < 0 ? Option::Call : Option::Put;
209 results_.value = blackFormula(type, kStar, EA, sigmaX *
sqrt(t_e), discountExercise);
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;
224 for (
const QuantLib::ext::shared_ptr<CashFlow>& cf :
arguments_.legs[floatLegIndex]) {
225 value += cf->amount() *
discountCurve_->discount(cf->date()) / normFactor;
228 Date exercise =
arguments_.exercise->dateAt(0);
230 value /= discountExercise;
241 bool isAveraging =
averaging(floatLegIndex);
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;
249 isAveraging,
strike, normFactor);
254 Date exercise =
arguments_.exercise->dateAt(0);
256 value /= (discountExercise * discountExercise);
262 const QuantLib::ext::shared_ptr<CashFlow>& cf_2,
bool isAveraging, Real strike,
263 Real normFactor)
const {
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);
278 Real result = ccf_1->periodQuantity() / normFactor;
279 result *= ccf_2->periodQuantity() / normFactor;
286 result /= ccf_1->indices().size();
287 result /= ccf_2->indices().size();
292 if (ccf_1->useFuturePrice()) {
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());
299 for (
const auto& [d, p] : ccf_1->indices()) {
300 expiries1[i] = p->expiryDate();
302 if (ccf_1->fxIndex())
303 fxRate = ccf_1->fxIndex()->fixing(expiries1[i]);
304 prices1[i] = fxRate * p->fixing(expiries1[i]);
310 for (
const auto& [d, p] : ccf_2->indices()) {
311 expiries2[i] = p->expiryDate();
313 if (ccf_2->fxIndex())
314 fxRate = ccf_2->fxIndex()->fixing(expiries2[i]);
315 prices2[i] = fxRate * p->fixing(expiries2[i]);
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);
328 std::vector<Real> prices1(ccf_1->indices().size()), prices2(ccf_2->indices().size());
331 for (
const auto& [d, p] : ccf_1->indices()) {
333 if (ccf_1->fxIndex())
334 fxRate = ccf_1->fxIndex()->fixing(d);
335 prices1[i] = fxRate * p->fixing(d);
340 for (
const auto& [d, p] : ccf_2->indices()) {
342 if (ccf_2->fxIndex())
343 fxRate = ccf_2->fxIndex()->fixing(d);
344 prices2[i] = fxRate * p->fixing(d);
348 for (Size i = 0; i < prices1.size(); ++i) {
349 for (Size j = 0; j < prices2.size(); ++j) {
350 cross += prices1[i] * prices2[j];
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);
368 Real result = ccf_1->amount() / normFactor;
369 result *= ccf_2->amount() / normFactor;
377 if (ccf_1->useFuturePrice()) {
379 Date e_1 = ccf_1->index()->expiryDate();
382 Date e_2 = ccf_2->index()->expiryDate();
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());
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());
419 QL_REQUIRE(result > 0.0,
"maxQuantity: quantities should be greater than 0.0");
428 Size idxFloat = idxFixed == 0 ? 1 : 0;
431 Real strikePrice =
strike(idxFixed);
434 if (referencesFuturePrice(
arguments_.legs[idxFloat])) {
450 Date exercise =
arguments_.exercise->dateAt(0);
458 Real stdDev =
sqrt(var);
461 Real factor =
exp(-var / 2.0);
464 Real optionValue = 0.0;
465 Real swapValue = 0.0;
466 Real floatLegValue = 0.0;
469 LowDiscrepancy::rsg_type rsg = LowDiscrepancy::make_sequence_generator(1,
seed_);
476 for (Size i = 0; i <
samples_; i++) {
479 Real sample = factor *
exp(stdDev * rsg.nextSequence().value[0]);
482 Real sampleFloatLegValue = floatFactor * sample;
483 Real sampleSwapValue = omega * (sampleFloatLegValue - valueFixedLeg);
486 optionValue +=
max(sampleSwapValue, 0.0) /
samples_;
487 swapValue += sampleSwapValue /
samples_;
488 floatLegValue += sampleFloatLegValue /
samples_;
492 results_.value = discountExercise * optionValue;
493 results_.additionalResults[
"SwapNPV"] = discountExercise * swapValue;
494 results_.additionalResults[
"FixedLegNPV"] = discountExercise * valueFixedLeg;
495 results_.additionalResults[
"FloatingLegNPV"] = discountExercise * floatLegValue;
510 LowDiscrepancy::rsg_type rsg = LowDiscrepancy::make_sequence_generator(expiries.size(),
seed_);
516 Date exercise =
arguments_.exercise->dateAt(0);
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);
534 Real optionValue = 0.0;
535 Real swapValue = 0.0;
536 Real floatLegValue = 0.0;
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);
545 Array factor = (discounts * amounts) * factors;
549 for (Size i = 0; i <
samples_; i++) {
552 vector<Real> sequence = rsg.nextSequence().value;
555 Array w = sqrtCorr * Array(sequence.begin(), sequence.end());
561 for (Size i = 0; i < w.size(); i++) {
562 w[i] =
exp(stdDev[i] * w[i]) * expVar[i];
566 Real sampleFloatLegValue = DotProduct(factor, w);
569 Real sampleSwapValue = omega * (sampleFloatLegValue - valueFixedLeg);
572 optionValue +=
max(sampleSwapValue, 0.0) /
samples_;
573 swapValue += sampleSwapValue /
samples_;
574 floatLegValue += sampleFloatLegValue /
samples_;
578 results_.value = discountExercise * optionValue;
579 results_.additionalResults[
"SwapNPV"] = discountExercise * swapValue;
580 results_.additionalResults[
"FixedLegNPV"] = discountExercise * valueFixedLeg;
581 results_.additionalResults[
"FloatingLegNPV"] = discountExercise * floatLegValue;
587 Real floatLegValue = 0.0;
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");
593 floatLegValue += disc * ccf->amount();
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");
600 floatLegValue += disc * ccf->amount();
605 floatLegValue /= discountExercise;
608 return floatLegValue;
612 const vector<Date>& expiries, Matrix& floatLegFactors,
613 Array& discounts, Array& amounts)
const {
615 Size numCfs =
arguments_.legs[idxFloat].size();
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");
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);
637 fxRate = ccf->fxIndex()->fixing(expiry);
638 floatLegFactors[i][idx] += fxRate * p.second->fixing(expiry) / numObs;
641 amounts[i] = ccf->periodQuantity();
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");
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;
653 amounts[i] = ccf->amount();
658 for (Size i = 0; i < discounts.size(); i++) {
659 discounts[i] /= discountExercise;
667 map<Date, Real> result;
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;
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;
694 vector<Date> expiryDates;
695 for (
auto& p : result) {
697 expiryDates.push_back(p.first);
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]);
707 outSqrtCorr = pseudoSqrt(outSqrtCorr, SalvagingAlgorithm::None);
const Instrument::results * results_
Cash flow dependent on a single commodity spot price or futures settlement price on a given pricing d...
Handle< QuantLib::BlackVolTermStructure > volStructure_
QuantLib::Real rho(const QuantLib::Date &ed_1, const QuantLib::Date &ed_2) const
Handle< YieldTermStructure > discountCurve_
CommoditySwaptionBaseEngine(const Handle< YieldTermStructure > &discountCurve, const Handle< QuantLib::BlackVolTermStructure > &vol, Real beta=0.0)
QuantLib::Real fixedLegValue(QuantLib::Size fixedLegIndex) const
Give back the fixed leg price at the swaption expiry time.
bool averaging(QuantLib::Size floatLegIndex) const
QuantLib::Real strike(QuantLib::Size fixedLegIndex) const
QuantLib::Size fixedLegIndex() const
QuantLib::Real expASquared(QuantLib::Size floatLegIndex, QuantLib::Real strike, QuantLib::Real normFactor) const
void calculate() const override
QuantLib::Real expA(QuantLib::Size floatLegIndex, 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
void calculateSpot(QuantLib::Size idxFixed, QuantLib::Size idxFloat, QuantLib::Real strike) const
Calculations when underlying swap references a commodity spot price.
void calculate() const override
void futureFloatLegFactors(QuantLib::Size idxFloat, QuantLib::Real discountExercise, const std::vector< QuantLib::Date > &expiries, QuantLib::Matrix &floatLegFactors, QuantLib::Array &discounts, QuantLib::Array &amounts) const
void calculateFuture(QuantLib::Size idxFixed, QuantLib::Size idxFloat, QuantLib::Real strike) const
Calculations when underlying swap references a commodity spot price.
std::map< QuantLib::Date, QuantLib::Real > futureExpiries(QuantLib::Size idxFloat, QuantLib::Matrix &outSqrtCorr, QuantLib::Real strike) const
QuantLib::Real spotFloatLegFactor(QuantLib::Size idxFloat, QuantLib::Real discountExercise) const
Cash flow dependent on the average commodity spot price or future's settlement price over a period....
Cash flow dependent on a single commodity spot price or future's settlement price.
commodity swaption engine
RandomVariable sqrt(RandomVariable x)
CompiledFormula exp(CompiledFormula x)
CompiledFormula max(CompiledFormula x, const CompiledFormula &y)
CompiledFormula log(CompiledFormula x)
Swap::arguments * arguments_