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

#include <qle/pricingengines/commodityapoengine.hpp>

+ Inheritance diagram for CommodityAveragePriceOptionMonteCarloEngine:
+ Collaboration diagram for CommodityAveragePriceOptionMonteCarloEngine:

Public Member Functions

 CommodityAveragePriceOptionMonteCarloEngine (const QuantLib::Handle< QuantLib::YieldTermStructure > &discountCurve, const QuantLib::Handle< QuantExt::BlackScholesModelWrapper > &model, QuantLib::Size samples, QuantLib::Real beta=0.0, const QuantLib::Size seed=42)
 
 CommodityAveragePriceOptionMonteCarloEngine (const QuantLib::Handle< QuantLib::YieldTermStructure > &discountCurve, const QuantLib::Handle< QuantLib::BlackVolTermStructure > &vol, QuantLib::Size samples, QuantLib::Real beta=0.0, const QuantLib::Size seed=42)
 
void calculate () const override
 
- Public Member Functions inherited from CommodityAveragePriceOptionBaseEngine
 CommodityAveragePriceOptionBaseEngine (const QuantLib::Handle< QuantLib::YieldTermStructure > &discountCurve, const QuantLib::Handle< QuantExt::BlackScholesModelWrapper > &model, QuantLib::Real beta=0.0)
 
 CommodityAveragePriceOptionBaseEngine (const QuantLib::Handle< QuantLib::YieldTermStructure > &discountCurve, const QuantLib::Handle< QuantLib::BlackVolTermStructure > &vol, QuantLib::Real beta=0.0)
 

Private Member Functions

void calculateSpot () const
 Calculations when underlying swap references a commodity spot price. More...
 
void calculateFuture () const
 Calculations when underlying swap references a commodity spot price. More...
 
void setupFuture (std::vector< QuantLib::Real > &outVolatilities, QuantLib::Matrix &outSqrtCorr, std::vector< QuantLib::Real > &outPrices, std::vector< QuantLib::Size > &futureIndex, QuantLib::Real strike) const
 
std::vector< QuantLib::Real > timegrid (std::vector< QuantLib::Date > &outDates) const
 

Private Attributes

QuantLib::Size samples_
 
QuantLib::Size seed_
 

Additional Inherited Members

- Protected Member Functions inherited from CommodityAveragePriceOptionBaseEngine
QuantLib::Real rho (const QuantLib::Date &ed_1, const QuantLib::Date &ed_2) const
 Return the correlation between two future expiry dates ed_1 and ed_2. More...
 
bool isModelDependent () const
 
bool barrierTriggered (const Real price, const bool logPrice) const
 
bool alive (const bool barrierTriggered) const
 
- Protected Attributes inherited from CommodityAveragePriceOptionBaseEngine
QuantLib::Handle< QuantLib::YieldTermStructure > discountCurve_
 
QuantLib::Handle< QuantLib::BlackVolTermStructure > volStructure_
 
QuantLib::Real beta_
 
QuantLib::Real logBarrier_
 

Detailed Description

Commodity APO Monte Carlo Engine Monte Carlo implementation of the APO payoff Reference: Iain Clark, Commodity Option Pricing, Wiley, section 2.7.4, equations (2.118) and (2.126)

Definition at line 124 of file commodityapoengine.hpp.

Constructor & Destructor Documentation

◆ CommodityAveragePriceOptionMonteCarloEngine() [1/2]

CommodityAveragePriceOptionMonteCarloEngine ( const QuantLib::Handle< QuantLib::YieldTermStructure > &  discountCurve,
const QuantLib::Handle< QuantExt::BlackScholesModelWrapper > &  model,
QuantLib::Size  samples,
QuantLib::Real  beta = 0.0,
const QuantLib::Size  seed = 42 
)

Definition at line 126 of file commodityapoengine.hpp.

130 : CommodityAveragePriceOptionBaseEngine(discountCurve, model, beta), samples_(samples), seed_(seed) {}
CommodityAveragePriceOptionBaseEngine(const QuantLib::Handle< QuantLib::YieldTermStructure > &discountCurve, const QuantLib::Handle< QuantExt::BlackScholesModelWrapper > &model, QuantLib::Real beta=0.0)

◆ CommodityAveragePriceOptionMonteCarloEngine() [2/2]

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

Definition at line 133 of file commodityapoengine.hpp.

137 : CommodityAveragePriceOptionBaseEngine(discountCurve, vol, beta), samples_(samples), seed_(seed) {}

Member Function Documentation

◆ calculate()

void calculate ( ) const
override

Definition at line 318 of file commodityapoengine.cpp.

318 {
319 if (isModelDependent()) {
320 // Switch implementation depending on whether underlying swap references a spot or future price
321 if (arguments_.flow->useFuturePrice()) {
323 } else {
325 }
326 }
327}
void calculateFuture() const
Calculations when underlying swap references a commodity spot price.
void calculateSpot() const
Calculations when underlying swap references a commodity spot price.
Swap::arguments * arguments_
+ Here is the call graph for this function:

◆ calculateSpot()

void calculateSpot ( ) const
private

Calculations when underlying swap references a commodity spot price.

Definition at line 329 of file commodityapoengine.cpp.

329 {
330
331 // Discount factor to the APO payment date
332 Real discount = discountCurve_->discount(arguments_.flow->date());
333
334 // Put call indicator
335 Real omega = arguments_.type == Option::Call ? 1.0 : -1.0;
336
337 // Variable to hold the payoff
338 Real payoff = 0.0;
339
340 // Vector of timesteps from today = t_0 out to last pricing date t_n
341 // i.e. {t_1 - t_0, t_2 - t_1,..., t_n - t_{n-1}}
342 vector<Date> dates;
343 vector<Real> dt = timegrid(dates);
344
345 // On each Monte Carlo sample, we must generate the spot price process path over n time steps.
346 LowDiscrepancy::rsg_type rsg = LowDiscrepancy::make_sequence_generator(dt.size(), seed_);
347
348 // We will read the volatility off the surface at the effective strike
349 // We will only call this method when the effectiveStrike > 0 but will check anyway
350 Real effectiveStrike = arguments_.effectiveStrike - arguments_.accrued;
351 QL_REQUIRE(effectiveStrike > 0.0, "calculateSpot: expected effectiveStrike to be positive");
352
353 // Precalculate:
354 // exp(-1/2 forward variance over dt): \exp \left(-\frac{1}{2} \int_{t_{i-1}}^{t_i} \sigma^2(u) du \right)
355 // forward std dev over dt: \exp \left(\sqrt{\int_{t_{i-1}}^{t_i} \sigma^2(u) du} \right)
356 // forward ratio: \exp \left(\int_{t_{i-1}}^{t_i} r(u) - r_f(u) \, du \right) = \frac{F(0, t_i)}{F(0, t_{i-1})}
357 // first period is multiplied by S(0)
358 // factors array is exp(-1/2 forward variance over dt) x forward ratio
359 Array expHalfFwdVar(dt.size(), 0.0);
360 Array fwdStdDev(dt.size(), 0.0);
361 Array fwdRatio(dt.size(), 0.0);
362 Time t = 0.0;
363 for (Size i = 0; i < dt.size(); i++) {
364 t += dt[i];
365 expHalfFwdVar[i] = volStructure_->blackForwardVariance(t - dt[i], t, effectiveStrike);
366 fwdStdDev[i] = sqrt(expHalfFwdVar[i]);
367 expHalfFwdVar[i] = exp(-expHalfFwdVar[i] / 2.0);
368 Real fxRate{1.};
369 if(arguments_.flow->fxIndex())
370 fxRate = arguments_.flow->fxIndex()->fixing(dates[i+1]);
371 fwdRatio[i] = fxRate * arguments_.flow->index()->fixing(dates[i + 1]);
372 if (i > 0) {
373 if(arguments_.flow->fxIndex())
374 fxRate = arguments_.flow->fxIndex()->fixing(dates[i]);
375 fwdRatio[i] /= (fxRate * arguments_.flow->index()->fixing(dates[i]));
376 }
377 }
378 Array factors = expHalfFwdVar * fwdRatio;
379
380 // Loop over each sample
381 auto m = arguments_.flow->indices().size();
382 for (Size k = 0; k < samples_; k++) {
383
384 // Sequence is n independent standard normal random variables
385 vector<Real> sequence = rsg.nextSequence().value;
386
387 // Calculate payoff on this sample - price holds the evolved price
388 Real price = 0.0;
389 Real samplePayoff = 0.0;
390 Array path(sequence.begin(), sequence.end());
391 path = Exp(path * fwdStdDev) * factors;
392 bool barrierTriggered = false;
393 for (Size i = 0; i < dt.size(); i++) {
394
395 // Update price
396 price = i == 0 ? path[i] : price * path[i];
397
398 // Update sum of the spot prices on the pricing dates after today
399 samplePayoff += price;
400
401 // check barrier
402 if (arguments_.barrierStyle == Exercise::American)
403 barrierTriggered = barrierTriggered || this->barrierTriggered(price, false);
404 }
405 // Average price on this sample
406 samplePayoff /= m;
407
408 // Finally, the payoff on this sample
409 samplePayoff = max(omega * (samplePayoff - effectiveStrike), 0.0);
410
411 // account for barrier
412 if (arguments_.barrierStyle == Exercise::European)
413 barrierTriggered = this->barrierTriggered(price, false);
414
416 samplePayoff = 0.0;
417
418 // Update the final average
419 // Note: payoff * k / (k + 1) - left to right precedence => double division.
420 payoff = k == 0 ? samplePayoff : payoff * k / (k + 1) + samplePayoff / (k + 1);
421 }
422
423 // Populate the result value
424 results_.value = arguments_.quantity * arguments_.flow->gearing() * payoff * discount;
425}
const Instrument::results * results_
Definition: cdsoption.cpp:81
bool barrierTriggered(const Real price, const bool logPrice) const
QuantLib::Handle< QuantLib::YieldTermStructure > discountCurve_
QuantLib::Handle< QuantLib::BlackVolTermStructure > volStructure_
bool alive(const bool barrierTriggered) const
std::vector< QuantLib::Real > timegrid(std::vector< QuantLib::Date > &outDates) 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 ( ) const
private

Calculations when underlying swap references a commodity spot price.

Definition at line 427 of file commodityapoengine.cpp.

427 {
428
429 // this method uses checkBarrier() on log prices, therefore we have to init the log barrier level
430 if (arguments_.barrierLevel != Null<Real>())
431 logBarrier_ = std::log(arguments_.barrierLevel);
432
433 // Discount factor to the APO payment date
434 Real discount = discountCurve_->discount(arguments_.flow->date());
435
436 // Put call indicator
437 Real omega = arguments_.type == Option::Call ? 1.0 : -1.0;
438
439 // Variable to hold the payoff
440 Real payoff = 0.0;
441
442 // We will read the volatility off the surface the effective strike
443 // We will only call this method when the effectiveStrike > 0 but will check anyway
444 Real effectiveStrike = arguments_.effectiveStrike - arguments_.accrued;
445 QL_REQUIRE(effectiveStrike > 0.0, "calculateFuture: expected effectiveStrike to be positive");
446
447 // Unique future expiry dates i.e. contracts, their volatilities and the correlation matrix between them
448 Matrix sqrtCorr;
449 vector<Real> vols;
450 vector<Real> prices;
451 vector<Size> futureIndex;
452 setupFuture(vols, sqrtCorr, prices, futureIndex, effectiveStrike);
453
454 // Vector of timesteps from today = t_0 out to last pricing date t_n
455 // i.e. {t_1 - t_0, t_2 - t_1,..., t_n - t_{n-1}}. Don't need the dates here.
456 vector<Date> dates;
457 vector<Real> dt = timegrid(dates);
458
459 // On each Monte Carlo sample, we must generate the paths for N (size of vols) future contracts where
460 // each path has n time steps. We will represent the paths with an N x n matrix. First step is to fill the
461 // matrix with N x n _independent_ standard normal variables. Then correlate the N variables in each column
462 // using the sqrtCorr matrix and then fill each entry F_{i, j} in the matrix with the value of the i-th
463 // future price process at timestep j. Note, we will possibly simulate contracts past their expiries but not
464 // use the price in the APO rate averaging.
465 LowDiscrepancy::rsg_type rsg = LowDiscrepancy::make_sequence_generator(vols.size() * dt.size(), seed_);
466
467 // Precalculate exp(-0.5 \sigma_i^2 \delta t_j) and std dev = sqrt(\delta t_j) \sigma_i
468 Matrix drifts(vols.size(), dt.size(), 0.0);
469 Matrix stdDev(vols.size(), dt.size(), 0.0);
470 Array logPrices(vols.size());
471 for (Size i = 0; i < drifts.rows(); i++) {
472 logPrices[i] = std::log(prices[i]);
473 for (Size j = 0; j < drifts.columns(); j++) {
474 drifts[i][j] = -vols[i] * vols[i] * dt[j] / 2.0;
475 stdDev[i][j] = vols[i] * sqrt(dt[j]);
476 }
477 }
478
479 // Loop over each sample
480 auto m = arguments_.flow->indices().size();
481 Matrix paths(vols.size(), dt.size());
482 for (Size k = 0; k < samples_; ++k) {
483
484 // Sequence is N x n independent standard normal random variables
485 const vector<Real>& sequence = rsg.nextSequence().value;
486
487 // paths initially holds independent standard normal random variables
488 std::copy(sequence.begin(), sequence.end(), paths.begin());
489
490 // Correlate the random variables in each column
491 paths = sqrtCorr * paths;
492
493 // Fill the paths
494 for (Size i = 0; i < paths.rows(); ++i) {
495 for (Size j = 0; j < paths.columns(); ++j) {
496 paths[i][j] = (j == 0 ? logPrices[i] : paths[i][j - 1]) + drifts[i][j] + stdDev[i][j] * paths[i][j];
497 }
498 }
499
500 // Calculate the sum of the commodity future prices on the pricing dates after today
501 Real samplePayoff = 0.0;
502 bool barrierTriggered = false;
503 Real price = 0.0;
504 for (Size j = 0; j < dt.size(); ++j) {
505 price = paths[futureIndex[j]][j];
506 if (arguments_.barrierStyle == Exercise::American)
507 barrierTriggered = barrierTriggered || this->barrierTriggered(price, true);
508 samplePayoff += std::exp(price);
509 }
510
511 // Average price on this sample
512 samplePayoff /= m;
513
514 // Finally, the payoff on this sample
515 samplePayoff = max(omega * (samplePayoff - effectiveStrike), 0.0);
516
517 // account for barrier
518 if (arguments_.barrierStyle == Exercise::European)
519 barrierTriggered = this->barrierTriggered(price, true);
520
522 samplePayoff = 0.0;
523
524 // Update the final average
525 // Note: payoff * k / (k + 1) - left to right precedence => double division.
526 payoff = k == 0 ? samplePayoff : payoff * k / (k + 1) + samplePayoff / (k + 1);
527 }
528
529 // Populate the result value
530 results_.value = arguments_.quantity * arguments_.flow->gearing() * payoff * discount;
531}
void setupFuture(std::vector< QuantLib::Real > &outVolatilities, QuantLib::Matrix &outSqrtCorr, std::vector< QuantLib::Real > &outPrices, std::vector< QuantLib::Size > &futureIndex, QuantLib::Real strike) const
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ setupFuture()

void setupFuture ( std::vector< QuantLib::Real > &  outVolatilities,
QuantLib::Matrix &  outSqrtCorr,
std::vector< QuantLib::Real > &  outPrices,
std::vector< QuantLib::Size > &  futureIndex,
QuantLib::Real  strike 
) const
private

Prepare data for APO calculation. The outVolatilities parameter will be populated with separate future contract volatilities taking into account the strike level. The number of elements of outVolatilities gives the number, N, of future contracts involved in the non-accrued portion of the APO. The matrix outSqrtCorr is populated with the square root of the correlation matrix between the future contracts. The outPrices vector will be populated with the current future price values. The futureIndex is populated with the index of the future to be used on each timestep in the simulation.

Definition at line 533 of file commodityapoengine.cpp.

535 {
536
537 // Ensure that vectors are empty
538 outVolatilities.clear();
539 prices.clear();
540 futureIndex.clear();
541
542 // Will hold the result
543 map<Date, Real> result;
544
545 // Note that here we make the simplifying assumption that the volatility can be read from the volatility
546 // term structure at the future contract's expiry date. In most cases, if the volatility term structure is built
547 // from options on futures, the option contract expiry will be a number of days before the future contract expiry
548 // and we should really read off the term structure at that date. Also populate a temp vector containing the key
549 // dates for use in the loop below where we populate the sqrt correlation matrix.
550
551 // Initialise result with expiry date keys that are still live in the APO
552 Date today = Settings::instance().evaluationDate();
553 set<Date> expiryDates;
554 for (const auto& p : arguments_.flow->indices()) {
555 if (p.first > today) {
556 Date expiry = p.second->expiryDate();
557 // If expiry has not been encountered yet
558 if (expiryDates.insert(expiry).second) {
559 outVolatilities.push_back(volStructure_->blackVol(expiry, strike));
560 Real fxRate{1.};
561 if(arguments_.flow->fxIndex())
562 fxRate = arguments_.flow->fxIndex()->fixing(expiry);
563 prices.push_back(fxRate*p.second->fixing(today));//check if today should not be p.first
564 }
565 futureIndex.push_back(expiryDates.size() - 1);
566 }
567 }
568
569 // Populate the outSqrtCorr matrix
570 vector<Date> vExpiryDates(expiryDates.begin(), expiryDates.end());
571 outSqrtCorr = Matrix(vExpiryDates.size(), vExpiryDates.size(), 1.0);
572 for (Size i = 0; i < vExpiryDates.size(); i++) {
573 for (Size j = 0; j < i; j++) {
574 outSqrtCorr[i][j] = outSqrtCorr[j][i] = rho(vExpiryDates[i], vExpiryDates[j]);
575 }
576 }
577 outSqrtCorr = pseudoSqrt(outSqrtCorr, SalvagingAlgorithm::None);
578}
QuantLib::Real rho(const QuantLib::Date &ed_1, const QuantLib::Date &ed_2) const
Return the correlation between two future expiry dates ed_1 and ed_2.
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ timegrid()

vector< Real > timegrid ( std::vector< QuantLib::Date > &  outDates) const
private

Return the \(n\) timesteps from today, \(t_0\), up to \(t_n\) where \(n > 0\). Note that each \(t_i\) corresponds to a pricing date \(d_i\) that is after today. The method returns the vector of time deltas \(t_i - t_{i-1}\) for \(i=1,\ldots,n\) and populates the vector outDates with the dates \(d_0, d_1,...,d_n\). Note that the size of outDates is one larger than the size of the return vector.

Definition at line 580 of file commodityapoengine.cpp.

580 {
581
582 // Initialise result with expiry date keys that are still live in the APO
583 // i.e. {t_1 - t_0, t_2 - t_0,..., t_n - t_0} where t_0 corresponds to today
584 vector<Real> times;
585 outDates.clear();
586 Date today = Settings::instance().evaluationDate();
587 outDates.push_back(today);
588 for (const auto& p : arguments_.flow->indices()) {
589 if (p.first > today) {
590 outDates.push_back(p.first);
591 times.push_back(volStructure_->timeFromReference(p.first));
592 }
593 }
594
595 // Populate time deltas i.e. {t_1 - t_0, t_2 - t_1,..., t_n - t_{n-1}}
596 vector<Real> dt(times.size());
597 adjacent_difference(times.begin(), times.end(), dt.begin());
598
599 return dt;
600}
+ Here is the caller graph for this function:

Member Data Documentation

◆ samples_

QuantLib::Size samples_
private

Definition at line 166 of file commodityapoengine.hpp.

◆ seed_

QuantLib::Size seed_
private

Definition at line 167 of file commodityapoengine.hpp.