Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
commodityapoengine.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2019, 2022 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
19#include <ql/math/matrixutilities/pseudosqrt.hpp>
20#include <ql/pricingengines/blackformula.hpp>
21#include <ql/processes/ornsteinuhlenbeckprocess.hpp>
25
26using std::adjacent_difference;
27using std::exp;
28using std::make_pair;
29using std::map;
30using std::max;
31using std::pair;
32using std::set;
33using std::vector;
34
35namespace QuantExt {
36
37namespace CommodityAveragePriceOptionMomementMatching {
38
39QuantLib::Real MomentMatchingResults::firstMoment() { return forward; }
40QuantLib::Real MomentMatchingResults::secondMoment() { return sigma * sigma * tn; }
41QuantLib::Real MomentMatchingResults::stdDev() { return std::sqrt(secondMoment()); }
42QuantLib::Time MomentMatchingResults::timeToExpriy() { return tn; }
43
45 const ext::shared_ptr<CommodityIndexedAverageCashFlow>& flow,
46 const ext::shared_ptr<QuantLib::BlackVolTermStructure>& vol,
47 const std::function<double(const QuantLib::Date& expiry1, const QuantLib::Date& expiry2)>& rho,
48 QuantLib::Real strike) {
49 Date today = Settings::instance().evaluationDate();
51
52 res.tn = 0.0;
53 res.accruals = 0.0;
54 double EA = 0;
55 std::vector<Date> futureExpiries;
56 std::map<Date, Real> futureVols;
57 std::vector<double> spotVariances;
58 size_t n = flow->indices().size();
59 double atmUnderlyingCcy = 0;
60
61 for (const auto& [pricingDate, index] : flow->indices()) {
62 Date fixingDate = index->fixingCalendar().adjust(pricingDate, Preceding);
63 Real fxRate = (flow->fxIndex()) ? flow->fxIndex()->fixing(fixingDate) : 1.0;
64 res.indexNames.push_back(index->name());
65 res.pricingDates.push_back(fixingDate);
66 res.indexExpiries.push_back(index->expiryDate());
67 res.fixings.push_back(index->fixing(fixingDate) * fxRate);
68 if (pricingDate <= today) {
69 res.accruals += res.fixings.back();
70 } else {
71 atmUnderlyingCcy = index->fixing(fixingDate);
72 res.forwards.push_back(res.fixings.back());
73 res.times.push_back(vol->timeFromReference(pricingDate));
74 // use ATM vol if no strike is given
75 double K = strike == Null<Real>() ? atmUnderlyingCcy : strike;
76 if (flow->useFuturePrice()) {
77 Date expiry = index->expiryDate();
78 futureExpiries.push_back(expiry);
79 if (futureVols.count(expiry) == 0) {
80 futureVols[expiry] = vol->blackVol(expiry, K);
81 }
82 } else {
83 spotVariances.push_back(
84 vol->blackVariance(res.times.back(), K));
85 res.spotVols.push_back(std::sqrt(spotVariances.back() / res.times.back()));
86 }
87 EA += res.forwards.back();
88 }
89 }
90 res.accruals /= static_cast<double>(n);
91 EA /= static_cast<double>(n);
92
93 res.forward = EA;
94
95 double EA2 = 0.0;
96
97 if (flow->useFuturePrice()) {
98 // References future prices
99 for (Size i = 0; i < res.forwards.size(); ++i) {
100 Date e_i = futureExpiries[i];
101 Volatility v_i = futureVols.at(e_i);
102 res.futureVols.push_back(v_i);
103 EA2 += res.forwards[i] * res.forwards[i] * exp(v_i * v_i * res.times[i]);
104 for (Size j = 0; j < i; ++j) {
105 Date e_j = futureExpiries[j];
106 Volatility v_j = futureVols.at(e_j);
107 EA2 += 2 * res.forwards[i] * res.forwards[j] * exp(rho(e_i, e_j) * v_i * v_j * res.times[j]);
108 }
109 }
110 } else {
111 // References spot prices
112 for (Size i = 0; i < res.forwards.size(); ++i) {
113 EA2 += res.forwards[i] * res.forwards[i] * exp(spotVariances[i]);
114 for (Size j = 0; j < i; ++j) {
115 EA2 += 2 * res.forwards[i] * res.forwards[j] * exp(spotVariances[j]);
116 }
117 }
118 }
119 EA2 /= std::pow(static_cast<double>(n), 2.0);
120 res.EA2 = EA2;
121
122 QL_REQUIRE(!std::isinf(EA2),
123 "moment matching fails (EA2 = inf) - this is possibly due to too high input volatilities.");
124
125 // Calculate value
126 if (!res.times.empty()) {
127 res.tn = res.times.back();
128 double s = EA2 / (EA * EA);
129 // if future vol = 0 for all dates, then EA2 = EA*EA, but
130 // due to numerical precision EA2 can be actually less than EA*EA
131 if (s < 1.0 || QuantLib::close_enough(s, 1)) {
132 res.sigma = 0.0;
133 } else {
134 res.sigma = std::sqrt(std::log(s) / res.tn);
135 }
136 } else {
137 res.tn = 0;
138 res.sigma = 0;
139 }
140 return res;
141}
142} // namespace CommodityAveragePriceOptionMomementMatching
143
145 const Handle<YieldTermStructure>& discountCurve, const QuantLib::Handle<QuantExt::BlackScholesModelWrapper>& model,
146 QuantLib::Real beta)
147 : discountCurve_(discountCurve), volStructure_(model->processes().front()->blackVolatility()), beta_(beta) {
148 QL_REQUIRE(beta_ >= 0.0, "beta >= 0 required, found " << beta_);
149 registerWith(model);
150}
151
153 const QuantLib::Handle<QuantLib::YieldTermStructure>& discountCurve,
154 const QuantLib::Handle<QuantLib::BlackVolTermStructure>& vol, QuantLib::Real beta)
155 : discountCurve_(discountCurve), volStructure_(vol), beta_(beta) {
156 QL_REQUIRE(beta_ >= 0.0, "beta >= 0 required, found " << beta_);
157 registerWith(discountCurve_);
158 registerWith(volStructure_);
159}
160
161Real CommodityAveragePriceOptionBaseEngine::rho(const Date& ed_1, const Date& ed_2) const {
162 if (beta_ == 0.0 || ed_1 == ed_2) {
163 return 1.0;
164 } else {
165 Time t_1 = volStructure_->timeFromReference(ed_1);
166 Time t_2 = volStructure_->timeFromReference(ed_2);
167 return exp(-beta_ * fabs(t_2 - t_1));
168 }
169}
170
172
173 // Discount factor to the APO payment date
174 Real discount = discountCurve_->discount(arguments_.flow->date());
175
176 // Valuation date
177 Date today = Settings::instance().evaluationDate();
178
179 // If all pricing dates are on or before today. This can happen when the APO payment date is a positive number
180 // of days after the final APO pricing date and today is in between.
181 if (today >= arguments_.flow->indices().rbegin()->first) {
182
183 // Put call indicator
184 Real omega = arguments_.type == Option::Call ? 1.0 : -1.0;
185
186 // Populate the result value
187 Real payoff = arguments_.flow->gearing() * max(omega * (arguments_.accrued - arguments_.effectiveStrike), 0.0);
188 results_.value = arguments_.quantity * payoff * discount;
189
190 return false;
191 }
192
193 // If a portion of the average price has already accrued, the effective strike of the APO will
194 // have changed by the accrued amount. The strike could be non-positive.
195 Real effectiveStrike = arguments_.effectiveStrike - arguments_.accrued;
196 if (effectiveStrike <= 0.0) {
197
198 // If the effective strike is <= 0, put payoff is 0.0 and call payoff is [A - K]
199 if (arguments_.type == Option::Call) {
200 results_.value = (arguments_.flow->amount() - arguments_.quantity * arguments_.strikePrice) * discount;
201 } else {
202 results_.value = 0.0;
203 }
204
205 return false;
206 }
207
208 // If we get to here, value is model dependent except the option was knocked out
209
210 bool barrierTriggered = false;
211 Real lastFixing = 0.0;
212
213 for (const auto& kv : arguments_.flow->indices()) {
214 // Break on the first pricing date that is greater than today
215 if (today < kv.first) {
216 break;
217 }
218 // Update accrued where pricing date is on or before today
219 Real fxRate{1.};
220 if(arguments_.fxIndex)
221 fxRate=arguments_.fxIndex->fixing(kv.first);
222 lastFixing = fxRate*kv.second->fixing(kv.first);
223 if (arguments_.barrierStyle == Exercise::American)
224 barrierTriggered = barrierTriggered || this->barrierTriggered(lastFixing, false);
225 }
226
227 if (arguments_.barrierStyle == Exercise::European)
228 barrierTriggered = this->barrierTriggered(lastFixing, false);
229
230 if(barrierTriggered && (arguments_.barrierType == Barrier::DownOut || arguments_.barrierType == Barrier::UpOut)) {
231 results_.value = 0.0;
232 return false;
233 }
234
235 return true;
236}
237
238bool CommodityAveragePriceOptionBaseEngine::barrierTriggered(const Real price, const bool logPrice) const {
239 if (arguments_.barrierLevel == Null<Real>())
240 return false;
241 Real tmp = logPrice ? logBarrier_ : arguments_.barrierLevel;
242 if (arguments_.barrierType == Barrier::DownIn || arguments_.barrierType == Barrier::DownOut)
243 return price <= tmp;
244 else if (arguments_.barrierType == Barrier::UpIn || arguments_.barrierType == Barrier::UpOut)
245 return price >= tmp;
246 return false;
247}
248
249bool CommodityAveragePriceOptionBaseEngine::alive(const bool barrierTriggered) const {
250 if (arguments_.barrierLevel == Null<Real>())
251 return true;
252 return ((arguments_.barrierType == Barrier::DownIn || arguments_.barrierType == Barrier::UpIn) &&
254 ((arguments_.barrierType == Barrier::DownOut || arguments_.barrierType == Barrier::UpOut) &&
256}
257
259
260 QL_REQUIRE(arguments_.barrierLevel == Null<Real>(),
261 "CommodityAveragePriceOptionAnalyticalEngine does not support barrier feature. Use MC engine instead.");
262
263 // Populate some additional results that don't change
264 auto& mp = results_.additionalResults;
265 Real discount = discountCurve_->discount(arguments_.flow->date());
266 mp["gearing"] = arguments_.flow->gearing();
267 mp["spread"] = arguments_.flow->spread();
268 mp["strike"] = arguments_.strikePrice;
269 mp["payment_date"] = arguments_.flow->date();
270 mp["accrued"] = arguments_.accrued;
271 mp["discount"] = discount;
272 if(arguments_.fxIndex)
273 mp["FXIndex"] = arguments_.fxIndex->name();
274
275 // If not model dependent, return early.
276 if (!isModelDependent()) {
277 mp["effective_strike"] = arguments_.effectiveStrike;
278 mp["npv"] = results_.value;
279 return;
280 }
281
282 // We will read the volatility off the surface at the effective strike
283 // We should only get this far when the effectiveStrike > 0 but will check anyway
284 Real effectiveStrike = arguments_.effectiveStrike - arguments_.accrued;
285 QL_REQUIRE(effectiveStrike > 0.0, "calculateSpot: expected effectiveStrike to be positive");
286
287 // Valuation date
288
291 std::bind(&CommodityAveragePriceOptionAnalyticalEngine::rho, this, std::placeholders::_1, std
292 ::placeholders::_2), effectiveStrike);
293
294 if (arguments_.flow->useFuturePrice()) {
295 mp["futureVols"] = matchedMoments.futureVols;
296 } else {
297 mp["spotVols"] = matchedMoments.spotVols;
298 }
299
300 // Populate results
301 results_.value = arguments_.quantity * arguments_.flow->gearing() *
302 blackFormula(arguments_.type, effectiveStrike, matchedMoments.firstMoment(),
303 matchedMoments.stdDev(), discount);
304
305 // Add more additional results
306 // Could be part of a strip so we add the value also.
307 mp["effective_strike"] = effectiveStrike;
308 mp["forward"] = matchedMoments.forward;
309 mp["exp_A_2"] = matchedMoments.EA2;
310 mp["tte"] = matchedMoments.timeToExpriy();
311 mp["sigma"] = matchedMoments.sigma;
312 mp["npv"] = results_.value;
313 mp["times"] = matchedMoments.times;
314 mp["forwards"] = matchedMoments.forwards;
315 mp["beta"] = beta_;
316}
317
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}
328
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}
426
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}
532
533void CommodityAveragePriceOptionMonteCarloEngine::setupFuture(vector<Real>& outVolatilities, Matrix& outSqrtCorr,
534 vector<Real>& prices, vector<Size>& futureIndex,
535 Real strike) const {
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}
579
580vector<Real> CommodityAveragePriceOptionMonteCarloEngine::timegrid(vector<Date>& outDates) const {
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}
601
602} // namespace QuantExt
const Instrument::results * results_
Definition: cdsoption.cpp:81
bool barrierTriggered(const Real price, const bool logPrice) const
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.
CommodityAveragePriceOptionBaseEngine(const QuantLib::Handle< QuantLib::YieldTermStructure > &discountCurve, const QuantLib::Handle< QuantExt::BlackScholesModelWrapper > &model, QuantLib::Real beta=0.0)
QuantLib::Handle< QuantLib::YieldTermStructure > discountCurve_
QuantLib::Handle< QuantLib::BlackVolTermStructure > volStructure_
bool alive(const bool barrierTriggered) const
void calculateFuture() const
Calculations when underlying swap references a commodity spot price.
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
void calculateSpot() const
Calculations when underlying swap references a commodity spot price.
commodity average price option engine
Cash flow dependent on a single commodity spot price or future's settlement price.
base class for multi path generators
MomentMatchingResults matchFirstTwoMomentsTurnbullWakeman(const ext::shared_ptr< CommodityIndexedAverageCashFlow > &flow, const ext::shared_ptr< QuantLib::BlackVolTermStructure > &vol, const std::function< double(const QuantLib::Date &expiry1, const QuantLib::Date &expiry2)> &rho, QuantLib::Real strike)
RandomVariable sqrt(RandomVariable x)
CompiledFormula exp(CompiledFormula x)
CompiledFormula max(CompiledFormula x, const CompiledFormula &y)
Swap::arguments * arguments_