39#include <ql/instruments/compositeinstrument.hpp>
43#include <boost/timer/timer.hpp>
55Real fx(
const std::vector<std::vector<std::vector<Real>>>& fxBuffer,
const Size ccyIndex,
const Size timeIndex,
59 return fxBuffer[ccyIndex - 1][timeIndex][sample];
62Real state(
const std::vector<std::vector<std::vector<Real>>>& irStateBuffer,
const Size ccyIndex,
const Size timeIndex,
64 return irStateBuffer[ccyIndex][timeIndex][sample];
67Real numRatio(
const QuantLib::ext::shared_ptr<CrossAssetModel>& model,
68 const std::vector<std::vector<std::vector<Real>>>& irStateBuffer,
const Size ccyIndex,
69 const Size timeIndex,
const Real time,
const Size sample) {
72 Real state_base = state(irStateBuffer, 0, timeIndex, sample);
73 Real state_curr = state(irStateBuffer, ccyIndex, timeIndex, sample);
74 return model->numeraire(ccyIndex, time, state_curr) / model->numeraire(0, time, state_base);
77Real num(
const QuantLib::ext::shared_ptr<CrossAssetModel>& model,
78 const std::vector<std::vector<std::vector<Real>>>& irStateBuffer,
const Size ccyIndex,
const Size timeIndex,
79 const Real time,
const Size sample) {
80 Real state_curr = state(irStateBuffer, ccyIndex, timeIndex, sample);
81 return model->numeraire(ccyIndex, time, state_curr);
84Real discount(
const QuantLib::ext::shared_ptr<CrossAssetModel>& model,
85 const std::vector<std::vector<std::vector<Real>>>& irStateBuffer,
const Size ccyIndex,
86 const Size timeIndex,
const Real t,
const Real T,
const Size sample) {
87 Real state_curr = state(irStateBuffer, ccyIndex, timeIndex, sample);
88 return model->discountBond(ccyIndex, t, T, state_curr);
91std::vector<QuantExt::RandomVariable>
92simulatePathInterface2(
const QuantLib::ext::shared_ptr<AmcCalculator>& amcCalc,
const std::vector<Real>& pathTimes,
93 std::vector<std::vector<RandomVariable>>& paths,
const std::vector<size_t>& pathIdx,
94 const std::vector<size_t>& timeIdx,
95 const std::string& tradeLabel,
96 const std::string& tradeType) {
97 QL_REQUIRE(pathIdx.size() == timeIdx.size(),
98 "internal error, mismatch between relevant path idx and timegrid idx, please contact dev");
100 return amcCalc->simulatePath(pathTimes, paths, pathIdx, timeIdx);
101 }
catch (
const std::exception& e) {
104 return std::vector<QuantExt::RandomVariable>(pathIdx.size() + 1,
109std::vector<QuantExt::RandomVariable>
110feeContributions(
const Size j,
const QuantLib::ext::shared_ptr<ScenarioGeneratorData>& sgd,
const Date&
asof,
111 const Size samples,
const std::vector<std::vector<std::tuple<Size, Real, QuantLib::Date>>>& tradeFees,
112 const QuantLib::ext::shared_ptr<CrossAssetModel>& model,
113 const std::vector<std::vector<std::vector<Real>>>& fxBuffer,
114 const std::vector<std::vector<std::vector<Real>>>& irStateBuffer) {
115 std::vector<QuantExt::RandomVariable> result;
116 for (Size k = 0; k < sgd->getGrid()->timeGrid().
size(); ++k) {
117 Date simDate = k == 0 ?
asof : sgd->getGrid()->dates()[k - 1];
120 if (k == 0 || !sgd->withCloseOutLag() || !sgd->withMporStickyDate() ||
121 sgd->getGrid()->isValuationDate()[k - 1]) {
123 if (tradeFees[j].empty())
125 for (Size i = 0; i < samples; ++i) {
127 for (Size f = 0; f < tradeFees[j].size(); ++f) {
128 if (std::get<2>(tradeFees[j][f]) > simDate) {
129 Real t = sgd->getGrid()->timeGrid()[k];
130 Real T = model->irModel(0)->termStructure()->timeFromReference(std::get<2>(tradeFees[j][f]));
131 tmp += std::get<1>(tradeFees[j][f]) * fx(fxBuffer, std::get<0>(tradeFees[j][f]), k, i) *
132 discount(model, irStateBuffer, std::get<0>(tradeFees[j][f]), k, t, T, i) *
133 num(model, irStateBuffer, 0, k, t, i);
136 result.back().set(i, tmp);
143void runCoreEngine(
const QuantLib::ext::shared_ptr<ore::data::Portfolio>& portfolio,
144 const QuantLib::ext::shared_ptr<QuantExt::CrossAssetModel>& model,
145 const QuantLib::ext::shared_ptr<ore::data::Market>& market,
146 const QuantLib::ext::shared_ptr<ore::analytics::ScenarioGeneratorData>& sgd,
147 const std::vector<string>& aggDataIndices,
const std::vector<string>& aggDataCurrencies,
148 const Size aggDataNumberCreditStates, QuantLib::ext::shared_ptr<ore::analytics::AggregationScenarioData> asd,
149 QuantLib::ext::shared_ptr<NPVCube> outputCube, QuantLib::ext::shared_ptr<ProgressIndicator> progressIndicator) {
151 std::ostringstream detail;
152 detail << portfolio->size() <<
" trade" << (portfolio->size() == 1 ?
"" :
"s");
153 progressIndicator->updateProgress(0, portfolio->size(), detail.str());
157 Currency baseCurrency = model->irlgm1f(0)->currency();
161 boost::timer::cpu_timer timer, timerTotal;
162 Real calibrationTime = 0.0, valuationTime = 0.0, asdTime = 0.0, bufferTime = 0.0, pathGenTime = 0.0, residualTime,
168 std::vector<Size> asdCurrencyIndex;
169 std::vector<string> asdCurrencyCode;
170 std::vector<QuantLib::ext::shared_ptr<LgmImpliedYtsFwdFwdCorrected>> asdIndexCurve;
171 std::vector<QuantLib::ext::shared_ptr<Index>> asdIndex;
172 std::vector<Size> asdIndexIndex;
173 std::vector<string> asdIndexName;
174 if (asd !=
nullptr) {
175 LOG(
"Collect information for aggregation scenario data...");
177 for (
auto const& c : aggDataCurrencies) {
179 if (cur == baseCurrency)
181 Size ccyIndex = model->ccyIndex(cur);
182 asdCurrencyIndex.push_back(ccyIndex);
183 asdCurrencyCode.push_back(c);
186 for (
auto const& i : aggDataIndices) {
187 QuantLib::ext::shared_ptr<IborIndex> tmp;
189 tmp = *market->iborIndex(i);
190 }
catch (
const std::exception& e) {
191 ALOG(
"index \"" << i <<
"\" not found in market, skipping. (" << e.what() <<
")");
193 Size ccyIndex = model->ccyIndex(tmp->currency());
194 asdIndexCurve.push_back(
195 QuantLib::ext::make_shared<LgmImpliedYtsFwdFwdCorrected>(model->lgm(ccyIndex), tmp->forwardingTermStructure()));
196 asdIndex.push_back(tmp->clone(Handle<YieldTermStructure>(asdIndexCurve.back())));
197 asdIndexIndex.push_back(ccyIndex);
198 asdIndexName.push_back(i);
201 LOG(
"No asd object set, won't write aggregation scenario data...");
206 LOG(
"Extract AMC Calculators...");
207 std::vector<QuantLib::ext::shared_ptr<AmcCalculator>> amcCalculators;
208 std::vector<Size> tradeId;
209 std::vector<std::string> tradeLabel, tradeType;
210 std::vector<Real> effectiveMultiplier;
211 std::vector<Size> currencyIndex;
212 std::vector<std::vector<std::tuple<Size, Real, QuantLib::Date>>> tradeFees;
214 Size progressCounter = 0;
217 RandomVariableStats::instance().enabled =
true;
218 RandomVariableStats::instance().data_ops = 0;
219 RandomVariableStats::instance().calc_ops = 0;
220 RandomVariableStats::instance().data_timer.start();
221 RandomVariableStats::instance().data_timer.stop();
222 RandomVariableStats::instance().calc_timer.start();
223 RandomVariableStats::instance().calc_timer.stop();
224 McEngineStats::instance().other_timer.start();
225 McEngineStats::instance().other_timer.stop();
226 McEngineStats::instance().path_timer.start();
227 McEngineStats::instance().path_timer.stop();
228 McEngineStats::instance().calc_timer.start();
229 McEngineStats::instance().calc_timer.stop();
231 auto extractAmcCalculator = [&amcCalculators, &tradeId, &tradeLabel, &tradeType, &effectiveMultiplier,
232 ¤cyIndex, &tradeFees, &model,
233 &outputCube](
const std::pair<std::string, QuantLib::ext::shared_ptr<Trade>>& trade,
234 QuantLib::ext::shared_ptr<AmcCalculator> amcCalc, Real multiplier,
bool addFees) {
235 LOG(
"AMCCalculator extracted for \"" << trade.first <<
"\"");
236 amcCalculators.push_back(amcCalc);
237 effectiveMultiplier.push_back(multiplier);
238 currencyIndex.push_back(model->ccyIndex(amcCalc->npvCurrency()));
239 if (
auto id = outputCube->idsAndIndexes().find(trade.first);
id != outputCube->idsAndIndexes().end()) {
240 tradeId.push_back(id->second);
242 QL_FAIL(
"AMCValuationEngine: trade id '" << trade.first
243 <<
"' is not present in output cube - internal error.");
245 tradeLabel.push_back(trade.first);
246 tradeType.push_back(trade.second->tradeType());
247 tradeFees.push_back({});
249 for (Size i = 0; i < trade.second->instrument()->additionalInstruments().
size(); ++i) {
250 if (
auto p = QuantLib::ext::dynamic_pointer_cast<QuantExt::Payment>(
251 trade.second->instrument()->additionalInstruments()[i])) {
252 tradeFees.back().push_back(std::make_tuple(model->ccyIndex(p->currency()), p->cashFlow()->amount(),
253 p->cashFlow()->date()));
256 "only QuantExt::Payment is handled as additional instrument.")
263 for (
auto const& trade : portfolio->trades()) {
264 QuantLib::ext::shared_ptr<AmcCalculator> amcCalc;
266 auto inst = trade.second->instrument()->qlInstrument(
true);
267 QL_REQUIRE(inst !=
nullptr,
268 "instrument has no ql instrument, this is not supported by the amc valuation engine.");
269 Real multiplier = trade.second->instrument()->multiplier() * trade.second->instrument()->multiplier2();
272 if (
auto cInst = QuantLib::ext::dynamic_pointer_cast<CompositeInstrument>(inst)) {
273 auto addResults = cInst->additionalResults();
274 std::vector<Real> multipliers;
276 std::stringstream ss;
277 ss << multipliers.size() + 1 <<
"_multiplier";
278 if (addResults.find(ss.str()) == addResults.end())
280 multipliers.push_back(inst->result<Real>(ss.str()));
282 std::vector<QuantLib::ext::shared_ptr<AmcCalculator>> amcCalcs;
283 for (Size cmpIdx = 0; cmpIdx < multipliers.size(); ++cmpIdx) {
284 std::stringstream ss;
285 ss << cmpIdx + 1 <<
"_amcCalculator";
286 if (addResults.find(ss.str()) != addResults.end()) {
287 amcCalcs.push_back(inst->result<QuantLib::ext::shared_ptr<AmcCalculator>>(ss.str()));
290 QL_REQUIRE(amcCalcs.size() == multipliers.size(),
291 "Did not find amc calculators for all components of composite trade.");
292 for (Size cmpIdx = 0; cmpIdx < multipliers.size(); ++cmpIdx) {
293 extractAmcCalculator(trade, amcCalc, multiplier * multipliers[cmpIdx], cmpIdx == 0);
299 amcCalc = inst->result<QuantLib::ext::shared_ptr<AmcCalculator>>(
"amcCalculator");
300 extractAmcCalculator(trade, amcCalc, multiplier,
true);
302 }
catch (
const std::exception& e) {
308 calibrationTime += timer.elapsed().wall * 1e-9;
309 LOG(
"Extracted " << amcCalculators.size() <<
" AMCCalculators for " << portfolio->size() <<
" source trades");
314 std::vector<std::vector<std::vector<Real>>> fxBuffer(
315 model->components(CrossAssetModel::AssetType::FX),
316 std::vector<std::vector<Real>>(sgd->getGrid()->dates().size() + 1, std::vector<Real>(outputCube->samples())));
317 std::vector<std::vector<std::vector<Real>>> irStateBuffer(
318 model->components(CrossAssetModel::AssetType::IR),
319 std::vector<std::vector<Real>>(sgd->getGrid()->dates().size() + 1, std::vector<Real>(outputCube->samples())));
323 auto process = model->stateProcess();
324 if (
auto tmp = QuantLib::ext::dynamic_pointer_cast<CrossAssetStateProcess>(process)) {
325 tmp->resetCache(sgd->getGrid()->timeGrid().size() - 1);
327 Size nStates = process->size();
328 QL_REQUIRE(sgd->getGrid()->timeGrid().size() > 0,
"AMCValuationEngine: empty time grid given");
329 std::vector<Real> pathTimes(std::next(sgd->getGrid()->timeGrid().begin(), 1), sgd->getGrid()->timeGrid().end());
330 std::vector<std::vector<RandomVariable>> paths(
331 pathTimes.size(), std::vector<RandomVariable>(nStates,
RandomVariable(outputCube->samples())));
335 auto pathGenerator =
makeMultiPathGenerator(sgd->sequenceType(), process, sgd->getGrid()->timeGrid(), sgd->seed(),
336 sgd->ordering(), sgd->directionIntegers());
338 LOG(
"Write ASD, fill internal fx and irState buffers...");
340 for (Size i = 0; i < outputCube->samples(); ++i) {
342 const auto& path = pathGenerator->next().value;
344 pathGenTime += timer.elapsed().wall * 1e-9;
349 for (Size k = 0; k < fxBuffer.size(); ++k) {
350 for (Size j = 0; j < sgd->getGrid()->timeGrid().
size(); ++j) {
351 fxBuffer[k][j][i] = std::exp(path[model->pIdx(CrossAssetModel::AssetType::FX, k)][j]);
354 for (Size k = 0; k < irStateBuffer.size(); ++k) {
355 for (Size j = 0; j < sgd->getGrid()->timeGrid().
size(); ++j) {
356 irStateBuffer[k][j][i] = path[model->pIdx(CrossAssetModel::AssetType::IR, k)][j];
360 for (Size k = 0; k < nStates; ++k) {
361 for (Size j = 0; j < pathTimes.size(); ++j) {
362 paths[j][k].set(i, path[k][j + 1]);
366 bufferTime += timer.elapsed().wall * 1e-9;
370 if (asd !=
nullptr) {
373 for (Size k = 1; k < sgd->getGrid()->timeGrid().
size(); ++k) {
375 if (!sgd->getGrid()->isValuationDate()[k - 1])
378 asd->set(dateIndex, i, model->numeraire(0, path[0].time(k), path[0][k]),
381 for (Size j = 0; j < asdCurrencyIndex.size(); ++j) {
386 Date d = sgd->getGrid()->dates()[k - 1];
387 for (Size j = 0; j < asdIndex.size(); ++j) {
388 asdIndexCurve[j]->move(d, state(irStateBuffer, asdIndexIndex[j], k, i));
389 auto index = asdIndex[j];
390 if (
auto fb = QuantLib::ext::dynamic_pointer_cast<FallbackIborIndex>(asdIndex[j])) {
392 index = fb->rfrIndex();
394 asd->set(dateIndex, i, index->fixing(index->fixingCalendar().adjust(d)),
398 for (Size j = 0; j < aggDataNumberCreditStates; ++j) {
399 asd->set(dateIndex, i, path[model->pIdx(CrossAssetModel::AssetType::CrState, j)][k],
405 asdTime += timer.elapsed().wall * 1e-9;
411 LOG(
"Run simulation...");
414 std::vector<size_t> allTimes;
415 std::vector<size_t> valuationTimeIdx, closeOutTimeIdx;
416 const auto& grid = sgd->getGrid();
417 const auto& dates = grid->dates();
419 for (
size_t i = 0; i < pathTimes.size(); ++i) {
420 allTimes.push_back(i);
421 if (sgd->withCloseOutLag()) {
423 if (grid->isValuationDate()[i]) {
424 valuationTimeIdx.push_back(i);
425 auto closeOutDate = grid->closeOutDateFromValuationDate(d);
426 while (j < pathTimes.size() && dates[j] != closeOutDate) {
429 QL_REQUIRE(j < pathTimes.size(),
430 "AmcValuationEngine:: couldnt find close out date" <<
to_string(closeOutDate));
431 closeOutTimeIdx.push_back(j);
438 for (Size j = 0; j < amcCalculators.size(); ++j) {
439 auto resFee = feeContributions(j, sgd, model->irModel(0)->termStructure()->referenceDate(),
440 outputCube->samples(), tradeFees, model, fxBuffer, irStateBuffer);
442 if (!sgd->withCloseOutLag()) {
444 auto res = simulatePathInterface2(amcCalculators[j], pathTimes, paths, allTimes, allTimes, tradeLabel[j],
446 Real v = outputCube->getT0(tradeId[j], 0);
447 outputCube->setT0(v +
448 res[0].at(0) * fx(fxBuffer, currencyIndex[j], 0, 0) *
449 numRatio(model, irStateBuffer, currencyIndex[j], 0, 0.0, 0) *
450 effectiveMultiplier[j] +
453 for (Size k = 1; k < res.size(); ++k) {
454 Real t = sgd->getGrid()->timeGrid()[k];
455 for (Size i = 0; i < outputCube->samples(); ++i) {
456 Real v = outputCube->get(tradeId[j], k - 1, i, 0);
458 res[k][i] * fx(fxBuffer, currencyIndex[j], k, i) *
459 numRatio(model, irStateBuffer, currencyIndex[j], k, t, i) *
460 effectiveMultiplier[j] +
462 tradeId[j], k - 1, i, 0);
467 if (sgd->withMporStickyDate()) {
469 auto res = simulatePathInterface2(amcCalculators[j], pathTimes, paths, valuationTimeIdx,
470 valuationTimeIdx, tradeLabel[j], tradeType[j]);
472 auto resLag = simulatePathInterface2(amcCalculators[j], pathTimes, paths, closeOutTimeIdx,
473 valuationTimeIdx, tradeLabel[j], tradeType[j]);
474 Real v = outputCube->getT0(tradeId[j], 0);
475 outputCube->setT0(v +
476 res[0].at(0) * fx(fxBuffer, currencyIndex[j], 0, 0) *
477 numRatio(model, irStateBuffer, currencyIndex[j], 0, 0.0, 0) *
478 effectiveMultiplier[j] +
482 std::map<QuantLib::Date, std::vector<std::tuple<QuantLib::Date, double, size_t>>> closeOutDateToValuationDate;
483 for (Size k = 0; k < sgd->getGrid()->dates().
size(); ++k) {
485 Real t = sgd->getGrid()->timeGrid()[k + 1];
486 if (sgd->getGrid()->isCloseOutDate()[k]) {
487 Date closeOutDate = sgd->getGrid()->dates()[k];
488 auto dateIndexIt = closeOutDateToValuationDate.find(closeOutDate);
489 QL_REQUIRE(dateIndexIt != closeOutDateToValuationDate.end() && !dateIndexIt->second.empty(),
490 "The valuation date needs to before the corresponding close out date");
491 for (
const auto& [valuationDate, valuationTime, valuationIndex] : dateIndexIt->second){
492 for (Size i = 0; i < outputCube->samples(); ++i) {
493 Real v = outputCube->get(tradeId[j], valuationIndex, i, 1);
496 resLag[valuationIndex + 1][i] * fx(fxBuffer, currencyIndex[j], k + 1, i) *
497 num(model, irStateBuffer, currencyIndex[j], k + 1, valuationTime, i) *
498 effectiveMultiplier[j] +
499 resFee[valuationIndex + 1][i],
500 tradeId[j], valuationIndex, i, 1);
505 if (sgd->getGrid()->isValuationDate()[k]) {
506 Date valuationDate = sgd->getGrid()->dates()[k];
507 Date closeOutDate = sgd->getGrid()->closeOutDateFromValuationDate(valuationDate);
508 closeOutDateToValuationDate[closeOutDate].push_back(std::make_tuple(valuationDate, t, ++dateIndex));
509 for (Size i = 0; i < outputCube->samples(); ++i) {
510 Real v = outputCube->get(tradeId[j], dateIndex, i, 0);
512 res[dateIndex + 1][i] * fx(fxBuffer, currencyIndex[j], k + 1, i) *
513 numRatio(model, irStateBuffer, currencyIndex[j], k + 1, t, i) *
514 effectiveMultiplier[j] +
515 resFee[dateIndex + 1][i],
516 tradeId[j], dateIndex, i, 0);
522 auto res = simulatePathInterface2(amcCalculators[j], pathTimes, paths, allTimes, allTimes,
523 tradeLabel[j], tradeType[j]);
524 Real v = outputCube->getT0(tradeId[j], 0);
525 outputCube->setT0(v + res[0].at(0) * fx(fxBuffer, currencyIndex[j], 0, 0) *
526 numRatio(model, irStateBuffer, currencyIndex[j], 0, 0.0, 0) *
527 effectiveMultiplier[j],
529 std::map<QuantLib::Date, std::vector<std::tuple<QuantLib::Date, double, size_t>>>
530 closeOutDateToValuationDate;
532 for (Size k = 1; k < res.size(); ++k) {
533 Real t = sgd->getGrid()->timeGrid()[k];
534 if (sgd->getGrid()->isCloseOutDate()[k - 1]) {
535 Date closeOutDate = sgd->getGrid()->dates()[k - 1];
536 auto dateIndexIt = closeOutDateToValuationDate.find(closeOutDate);
537 QL_REQUIRE(dateIndexIt != closeOutDateToValuationDate.end() && !dateIndexIt->second.empty(),
538 "The valuation date needs to before the corresponding close out date");
539 for (
const auto& [valuationDate, valuationTime, valuationIndex] : dateIndexIt->second) {
540 for (Size i = 0; i < outputCube->samples(); ++i) {
541 Real v = outputCube->get(tradeId[j], valuationIndex, i, 1);
543 res[k][i] * fx(fxBuffer, currencyIndex[j], k, i) *
544 num(model, irStateBuffer, currencyIndex[j], k, t, i) *
545 effectiveMultiplier[j] +
547 tradeId[j], valuationIndex, i, 1);
551 if (sgd->getGrid()->isValuationDate()[k - 1]) {
552 Date valuationDate = sgd->getGrid()->dates()[k - 1];
553 Date closeOutDate = sgd->getGrid()->closeOutDateFromValuationDate(valuationDate);
554 closeOutDateToValuationDate[closeOutDate].push_back(std::make_tuple(valuationDate, t, ++dateIndex));
555 for (Size i = 0; i < outputCube->samples(); ++i) {
556 Real v = outputCube->get(tradeId[j], dateIndex, i, 0);
558 res[k][i] * fx(fxBuffer, currencyIndex[j], k, i) *
559 numRatio(model, irStateBuffer, currencyIndex[j], k, t, i) *
560 effectiveMultiplier[j] +
562 tradeId[j], dateIndex, i, 0);
568 std::ostringstream detail;
569 detail << portfolio->size() <<
" trade" << (portfolio->size() == 1 ?
"" :
"s");
570 progressIndicator->updateProgress(++progressCounter, portfolio->size(), detail.str());
573 valuationTime += timer.elapsed().wall * 1e-9;
575 totalTime = timerTotal.elapsed().wall * 1e-9;
576 residualTime = totalTime - (calibrationTime + pathGenTime + valuationTime + asdTime + bufferTime);
577 LOG(
"calibration time : " << calibrationTime <<
" sec");
578 LOG(
"asd time : " << asdTime <<
" sec");
579 LOG(
"buffer time : " << bufferTime <<
" sec");
580 LOG(
"path generation time : " << pathGenTime <<
" sec");
581 LOG(
"valuation time : " << valuationTime <<
" sec");
582 LOG(
"residual time : " << residualTime <<
" sec");
583 LOG(
"total time : " << totalTime <<
" sec");
584 LOG(
"AMCValuationEngine finished for one of possibly multiple threads.");
585 LOG(
"RandomVariableStats : ");
586 LOG(
"Data Ops : " << RandomVariableStats::instance().data_ops / 1E6 <<
" MOPS");
587 LOG(
"Calc Ops : " << RandomVariableStats::instance().calc_ops / 1E6 <<
" MOPS");
588 LOG(
"Data Timer : " << RandomVariableStats::instance().data_timer.elapsed().wall / 1E9 <<
" sec");
589 LOG(
"Calc Timer : " << RandomVariableStats::instance().calc_timer.elapsed().wall / 1E9 <<
" sec");
590 LOG(
"Data Performace : " << RandomVariableStats::instance().data_ops * 1E3 /
591 RandomVariableStats::instance().data_timer.elapsed().wall
593 LOG(
"Calc Performace : " << RandomVariableStats::instance().calc_ops * 1E3 /
594 RandomVariableStats::instance().calc_timer.elapsed().wall
596 LOG(
"MC Other Timer : " << McEngineStats::instance().other_timer.elapsed().wall / 1E9 <<
" sec");
597 LOG(
"MC Path Timer : " << McEngineStats::instance().path_timer.elapsed().wall / 1E9 <<
" sec");
598 LOG(
"MC Calc Timer : " << McEngineStats::instance().calc_timer.elapsed().wall / 1E9 <<
" sec");
605 const QuantLib::Size nThreads,
const QuantLib::Date& today,
const QuantLib::Size nSamples,
606 const QuantLib::ext::shared_ptr<ore::data::Loader>& loader,
607 const QuantLib::ext::shared_ptr<ScenarioGeneratorData>& scenarioGeneratorData,
608 const std::vector<string>& aggDataIndices,
const std::vector<string>& aggDataCurrencies,
609 const Size aggDataNumberCreditStates,
const QuantLib::ext::shared_ptr<CrossAssetModelData>& crossAssetModelData,
610 const QuantLib::ext::shared_ptr<ore::data::EngineData>& engineData,
611 const QuantLib::ext::shared_ptr<ore::data::CurveConfigurations>& curveConfigs,
612 const QuantLib::ext::shared_ptr<ore::data::TodaysMarketParameters>& todaysMarketParams,
613 const std::string& configurationLgmCalibration,
const std::string& configurationFxCalibration,
614 const std::string& configurationEqCalibration,
const std::string& configurationInfCalibration,
615 const std::string& configurationCrCalibration,
const std::string& configurationFinalModel,
616 const QuantLib::ext::shared_ptr<ore::data::ReferenceDataManager>& referenceData,
618 const std::function<QuantLib::ext::shared_ptr<ore::analytics::NPVCube>(
619 const QuantLib::Date&,
const std::set<std::string>&,
const std::vector<QuantLib::Date>&,
const QuantLib::Size)>&
621 const QuantLib::ext::shared_ptr<Scenario>& offSetScenario,
622 const QuantLib::ext::shared_ptr<ore::analytics::ScenarioSimMarketParameters>& simMarketParams)
623 : useMultithreading_(true), aggDataIndices_(aggDataIndices), aggDataCurrencies_(aggDataCurrencies),
624 aggDataNumberCreditStates_(aggDataNumberCreditStates), scenarioGeneratorData_(scenarioGeneratorData),
625 nThreads_(nThreads), today_(today), nSamples_(nSamples), loader_(loader),
626 crossAssetModelData_(crossAssetModelData), engineData_(engineData), curveConfigs_(
curveConfigs),
627 todaysMarketParams_(todaysMarketParams), configurationLgmCalibration_(configurationLgmCalibration),
628 configurationFxCalibration_(configurationFxCalibration), configurationEqCalibration_(configurationEqCalibration),
629 configurationInfCalibration_(configurationInfCalibration),
630 configurationCrCalibration_(configurationCrCalibration), configurationFinalModel_(configurationFinalModel),
631 referenceData_(referenceData), iborFallbackConfig_(iborFallbackConfig),
632 handlePseudoCurrenciesTodaysMarket_(handlePseudoCurrenciesTodaysMarket), cubeFactory_(cubeFactory),
633 offsetScenario_(offSetScenario), simMarketParams_(simMarketParams) {
634#ifndef QL_ENABLE_SESSIONS
636 "AMCValuationEngine requires a build with QL_ENABLE_SESSIONS = ON when ctor multi-threaded runs is called.");
639 "AMCValuationEngine: path generation uses seed 0 - this might lead to inconsistent results to a classic "
640 "simulation run, if both are combined. Consider using a non-zero seed.");
642 cubeFactory_ = [](
const QuantLib::Date&
asof,
const std::set<std::string>& ids,
643 const std::vector<QuantLib::Date>& dates,
const Size samples) {
644 return QuantLib::ext::make_shared<ore::analytics::DoublePrecisionInMemoryCube>(
asof, ids, dates, samples);
649 const QuantLib::ext::shared_ptr<ScenarioGeneratorData>& scenarioGeneratorData,
650 const QuantLib::ext::shared_ptr<Market>& market,
651 const std::vector<string>& aggDataIndices,
652 const std::vector<string>& aggDataCurrencies,
653 const Size aggDataNumberCreditStates)
654 : useMultithreading_(false), aggDataIndices_(aggDataIndices), aggDataCurrencies_(aggDataCurrencies),
655 aggDataNumberCreditStates_(aggDataNumberCreditStates), scenarioGeneratorData_(scenarioGeneratorData),
656 model_(model), market_(market) {
658 QL_REQUIRE((aggDataIndices.empty() && aggDataCurrencies.empty()) || market !=
nullptr,
659 "AMCValuationEngine: market is required for asd generation");
661 "AMCValuationEngine: path generation uses seed 0 - this might lead to inconsistent results to a classic "
662 "simulation run, if both are combined. Consider using a non-zero seed.");
665 "AMCValuationEngine: day counter in simulation parameters ("
667 << model->irlgm1f(0)->termStructure()->dayCounter()
668 <<
"), align these e.g. by setting the day counter in the simulation parameters to the model day counter");
672 QuantLib::ext::shared_ptr<NPVCube>& outputCube) {
674 LOG(
"Starting single-threaded AMCValuationEngine for " << portfolio->size() <<
" trades, " << outputCube->samples()
678 QL_REQUIRE(!
useMultithreading_,
"AMCValuationEngine::buildCube() method was called with signature for "
679 "single-threaded run, but engine was constructed for multi-threaded runs");
681 QL_REQUIRE(portfolio->size() > 0,
"AMCValuationEngine::buildCube: empty portfolio");
683 QL_REQUIRE(outputCube->numIds() == portfolio->trades().size(),
684 "cube x dimension (" << outputCube->numIds() <<
") "
685 <<
"different from portfolio size (" << portfolio->trades().size() <<
")");
688 "cube y dimension (" << outputCube->numDates() <<
") "
689 <<
"different from number of valuation dates ("
696 QuantLib::ext::make_shared<ore::analytics::MultiThreadedProgressIndicator>(this->
progressIndicators()));
697 }
catch (
const std::exception& e) {
698 QL_FAIL(
"Error during amc val engine run: " << e.what());
701 LOG(
"Finished single-threaded AMCValuationEngine run.");
705 LOG(
"Starting multi-threaded AMCValuationEngine for "
709 QL_REQUIRE(
useMultithreading_,
"AMCValuationEngine::buildCube() method was called with signature for "
710 "multi-threaded run, but engine was constructed for single-threaded runs");
712 QL_REQUIRE(portfolio->size() > 0,
"AMCValuationEngine::buildCube: empty portfolio");
716 LOG(
"Splitting portfolio.");
718 Size eff_nThreads = std::min(portfolio->size(),
nThreads_);
720 LOG(
"portfolio size = " << portfolio->size());
722 LOG(
"eff nThreads = " << eff_nThreads);
724 QL_REQUIRE(eff_nThreads > 0,
"effective threads are zero, this is not allowed.");
726 std::vector<QuantLib::ext::shared_ptr<ore::data::Portfolio>> portfolios;
727 for (Size i = 0; i < eff_nThreads; ++i)
728 portfolios.push_back(QuantLib::ext::make_shared<ore::data::Portfolio>());
730 Size portfolioIndex = 0;
731 for (
auto const& t : portfolio->trades()) {
732 portfolios[portfolioIndex]->add(t.second);
733 if (++portfolioIndex >= eff_nThreads)
739 std::vector<std::string> portfoliosAsString;
740 for (
auto const& p : portfolios) {
741 portfoliosAsString.emplace_back(p->toXMLString());
746 for (Size i = 0; i < eff_nThreads; ++i) {
747 LOG(
"Portfolio #" << i <<
" number of trades : " << portfolios[i]->
size());
752 LOG(
"Cloning loaders for " << eff_nThreads <<
" threads...");
753 std::vector<QuantLib::ext::shared_ptr<ore::data::ClonedLoader>> loaders;
754 for (Size i = 0; i < eff_nThreads; ++i)
755 loaders.push_back(QuantLib::ext::make_shared<ore::data::ClonedLoader>(
today_,
loader_));
759 LOG(
"Build " << eff_nThreads <<
" mini result cubes...");
761 for (Size i = 0; i < eff_nThreads; ++i) {
768 std::vector<Date> simDates =
775 auto progressIndicator =
776 QuantLib::ext::make_shared<ore::analytics::MultiThreadedProgressIndicator>(this->
progressIndicators());
785 using resultType = int;
786 std::vector<std::future<resultType>> results(eff_nThreads);
788 std::vector<std::thread> jobs;
794 for (Size i = 0; i < eff_nThreads; ++i) {
796 auto job = [
this, obsMode, &portfoliosAsString, &loaders, &simDates, &progressIndicator](
int id) -> resultType {
799 QuantLib::Settings::instance().evaluationDate() =
today_;
800 ore::analytics::ObservationMode::instance().setMode(obsMode);
802 LOG(
"Start thread " <<
id);
810 QuantLib::ext::shared_ptr<ore::data::Market> initMarket = QuantLib::ext::make_shared<ore::data::TodaysMarket>(
814 QuantLib::ext::shared_ptr<ore::data::Market> market = initMarket;
818 "AMC Valuation Engine can not build simMarket without simMarketParam");
819 bool continueOnError =
true;
821 market = QuantLib::ext::make_shared<ScenarioSimMarket>(
833 auto cam = *modelBuilder.
model();
837 auto portfolio = QuantLib::ext::make_shared<ore::data::Portfolio>();
838 portfolio->fromXMLString(portfoliosAsString[
id]);
840 QuantLib::ext::shared_ptr<EngineData> edCopy = QuantLib::ext::make_shared<EngineData>(*
engineData_);
841 edCopy->globalParameters()[
"GenerateAdditionalResults"] =
"false";
842 edCopy->globalParameters()[
"RunType"] =
"NPV";
847 auto engineFactory = QuantLib::ext::make_shared<EngineFactory>(
849 EngineBuilderFactory::instance().generateAmcEngineBuilders(cam, simDates),
true);
851 portfolio->build(engineFactory,
"amc-val-engine",
true);
860 LOG(
"Thread " <<
id <<
" successfully finished.");
864 }
catch (
const std::exception& e) {
882 std::packaged_task<resultType(
int)> task(job);
883 results[i] = task.get_future();
884 std::thread thread(std::move(task), i);
885 jobs.emplace_back(std::move(thread));
892 for (Size i = 0; i < results.size(); ++i) {
896 for (Size i = 0; i < results.size(); ++i) {
897 QL_REQUIRE(results[i].valid(),
"internal error: did not get a valid result");
898 int rc = results[i].get();
899 QL_REQUIRE(rc == 0,
"error: thread " << i <<
" exited with return code " << rc
900 <<
". Check for structured errors from 'AMCValuationEngine'.");
908 LOG(
"Finished multi-threaded AMCValuationEngine run.");
QuantLib::ext::shared_ptr< ore::data::CurveConfigurations > curveConfigs_
std::string configurationEqCalibration_
QuantLib::ext::shared_ptr< ore::data::TodaysMarketParameters > todaysMarketParams_
const QuantLib::ext::shared_ptr< QuantExt::CrossAssetModel > model_
QuantLib::ext::shared_ptr< ore::analytics::ScenarioSimMarketParameters > simMarketParams_
std::vector< QuantLib::ext::shared_ptr< ore::analytics::NPVCube > > miniCubes_
std::string configurationCrCalibration_
const Size aggDataNumberCreditStates_
void buildCube(const QuantLib::ext::shared_ptr< ore::data::Portfolio > &portfolio, QuantLib::ext::shared_ptr< ore::analytics::NPVCube > &outputCube)
build cube in single threaded run
QuantLib::ext::shared_ptr< ore::analytics::AggregationScenarioData > asd_
QuantLib::ext::shared_ptr< ScenarioGeneratorData > scenarioGeneratorData_
const QuantLib::ext::shared_ptr< ore::data::Market > market_
QuantLib::ext::shared_ptr< ore::data::ReferenceDataManager > referenceData_
std::string configurationInfCalibration_
bool handlePseudoCurrenciesTodaysMarket_
QuantLib::ext::shared_ptr< Scenario > offsetScenario_
ore::data::IborFallbackConfig iborFallbackConfig_
AMCValuationEngine(const QuantLib::ext::shared_ptr< QuantExt::CrossAssetModel > &model, const QuantLib::ext::shared_ptr< ore::analytics::ScenarioGeneratorData > &scenarioGeneratorData, const QuantLib::ext::shared_ptr< ore::data::Market > &market, const std::vector< string > &aggDataIndices, const std::vector< string > &aggDataCurrencies, const Size aggDataNumberCreditStates)
Constructor for single-threaded runs.
std::string configurationFxCalibration_
std::string configurationLgmCalibration_
QuantLib::ext::shared_ptr< CrossAssetModelData > crossAssetModelData_
std::string configurationFinalModel_
const std::vector< string > aggDataCurrencies_
QuantLib::ext::shared_ptr< ore::data::Loader > loader_
const std::vector< string > aggDataIndices_
std::function< QuantLib::ext::shared_ptr< ore::analytics::NPVCube >(const QuantLib::Date &, const std::set< std::string > &, const std::vector< QuantLib::Date > &, const QuantLib::Size)> cubeFactory_
QuantLib::ext::shared_ptr< ore::data::EngineData > engineData_
Handle< QuantExt::CrossAssetModel > model() const
const std::set< QuantLib::ext::shared_ptr< ProgressIndicator > > & progressIndicators() const
const QuantLib::ext::shared_ptr< ModelCG > model_
Currency parseCurrency(const string &s)
A cube implementation that stores the cube in memory.
boost::shared_ptr< MultiPathGeneratorBase > makeMultiPathGenerator(const SequenceType s, const boost::shared_ptr< StochasticProcess > &process, const TimeGrid &timeGrid, const BigNatural seed, const SobolBrownianGenerator::Ordering ordering=SobolBrownianGenerator::Steps, const SobolRsg::DirectionIntegers directionIntegers=SobolRsg::JoeKuoD7)
Size size(const ValueType &v)
std::string to_string(const LocationInfo &l)
Singleton class to hold global Observation Mode.
Structured analytics error.
vector< string > curveConfigs