246 {
247 BOOST_TEST_MESSAGE("Testing Swap Exposure Performance size=" << portfolioSize << "...");
248
249 SavedSettings backup;
251 ObservationMode::instance().setMode(om);
252
253
254
255
256 Date today = Date(14, April, 2016);
257 Settings::instance().evaluationDate() = today;
258
259 BOOST_TEST_MESSAGE("Today is " << today);
260
261 string dateGridStr = "80,3M";
262 QuantLib::ext::shared_ptr<DateGrid> dg = QuantLib::ext::make_shared<DateGrid>(dateGridStr);
263 Size samples = 1000;
264
265 BOOST_TEST_MESSAGE("Date Grid : " << dateGridStr);
266 BOOST_TEST_MESSAGE("Samples : " << samples);
267 BOOST_TEST_MESSAGE("Swaps : " << portfolioSize);
268
269
270 string baseCcy = "EUR";
271 vector<string> ccys;
272 ccys.push_back(baseCcy);
273 ccys.push_back("GBP");
274 ccys.push_back("CHF");
275 ccys.push_back("USD");
276 ccys.push_back("JPY");
277
278
279 QuantLib::ext::shared_ptr<Market> initMarket = QuantLib::ext::make_shared<TestMarket>(today);
280
281
283 parameters->baseCcy() = "EUR";
284 parameters->setDiscountCurveNames({"EUR", "GBP", "USD", "CHF", "JPY"});
285 parameters->setYieldCurveTenors("",
286 {1 * Months, 6 * Months, 1 * Years, 2 * Years, 5 * Years, 10 * Years, 20 * Years});
287 parameters->setIndices({"EUR-EURIBOR-6M", "USD-LIBOR-3M", "GBP-LIBOR-6M", "CHF-LIBOR-6M", "JPY-LIBOR-6M"});
288
289 parameters->interpolation() = "LogLinear";
290
291 parameters->setSimulateSwapVols(false);
292 parameters->setSwapVolTerms("", {6 * Months, 1 * Years});
293 parameters->setSwapVolExpiries("", {1 * Years, 2 * Years});
294 parameters->swapVolKeys() = ccys;
295 parameters->swapVolDecayMode() = "ForwardVariance";
296
297 parameters->setFxVolExpiries("",
298 vector<Period>{1 * Months, 3 * Months, 6 * Months, 2 * Years, 3 * Years, 4 * Years, 5 * Years});
299 parameters->setFxVolDecayMode(string("ConstantVariance"));
300 parameters->setSimulateFXVols(false);
301
302 parameters->setFxVolCcyPairs({"USDEUR", "GBPEUR", "CHFEUR", "JPYEUR"});
303 parameters->setFxCcyPairs({"USDEUR", "GBPEUR", "CHFEUR", "JPYEUR"});
304
305 parameters->setEquityVolExpiries("", {1 * Months, 3 * Months, 6 * Months, 2 * Years, 3 * Years, 4 * Years, 5 * Years});
306 parameters->setEquityVolDecayMode("ConstantVariance");
307 parameters->setSimulateEquityVols(false);
308
309
310
311
315 vector<string> swaptionExpiries = {"1Y", "2Y", "3Y", "5Y", "7Y", "10Y", "15Y", "20Y", "30Y"};
316 vector<string> swaptionTerms = {"5Y", "5Y", "5Y", "5Y", "5Y", "5Y", "5Y", "5Y", "5Y"};
317 vector<string> swaptionStrikes(swaptionExpiries.size(), "ATM");
318 vector<Time> hTimes = {};
319 vector<Time> aTimes = {};
320
321 std::vector<QuantLib::ext::shared_ptr<IrModelData>> irConfigs;
322
323 vector<Real> hValues = {0.02};
324 vector<Real> aValues = {0.008};
325 irConfigs.push_back(QuantLib::ext::make_shared<IrLgmData>(
326 "EUR", calibrationType, revType, volType, false, ParamType::Constant, hTimes, hValues, true,
327 ParamType::Piecewise, aTimes, aValues, 0.0, 1.0, swaptionExpiries, swaptionTerms, swaptionStrikes));
328
329 hValues = {0.03};
330 aValues = {0.009};
331 irConfigs.push_back(QuantLib::ext::make_shared<IrLgmData>(
332 "USD", calibrationType, revType, volType, false, ParamType::Constant, hTimes, hValues, true,
333 ParamType::Piecewise, aTimes, aValues, 0.0, 1.0, swaptionExpiries, swaptionTerms, swaptionStrikes));
334
335 hValues = {0.04};
336 aValues = {0.01};
337 irConfigs.push_back(QuantLib::ext::make_shared<IrLgmData>(
338 "GBP", calibrationType, revType, volType, false, ParamType::Constant, hTimes, hValues, true,
339 ParamType::Piecewise, aTimes, aValues, 0.0, 1.0, swaptionExpiries, swaptionTerms, swaptionStrikes));
340
341 hValues = {0.04};
342 aValues = {0.01};
343 irConfigs.push_back(QuantLib::ext::make_shared<IrLgmData>(
344 "CHF", calibrationType, revType, volType, false, ParamType::Constant, hTimes, hValues, true,
345 ParamType::Piecewise, aTimes, aValues, 0.0, 1.0, swaptionExpiries, swaptionTerms, swaptionStrikes));
346
347 hValues = {0.04};
348 aValues = {0.01};
349 irConfigs.push_back(QuantLib::ext::make_shared<IrLgmData>(
350 "JPY", calibrationType, revType, volType, false, ParamType::Constant, hTimes, hValues, true,
351 ParamType::Piecewise, aTimes, aValues, 0.0, 1.0, swaptionExpiries, swaptionTerms, swaptionStrikes));
352
353
354 vector<string> optionExpiries = {"1Y", "2Y", "3Y", "5Y", "7Y", "10Y"};
355 vector<string> optionStrikes(optionExpiries.size(), "ATMF");
356 vector<Time> sigmaTimes = {};
357
358 std::vector<QuantLib::ext::shared_ptr<FxBsData>> fxConfigs;
359
360 vector<Real> sigmaValues = {0.15};
361 fxConfigs.push_back(QuantLib::ext::make_shared<FxBsData>("USD", "EUR", calibrationType, true, ParamType::Piecewise,
362 sigmaTimes, sigmaValues, optionExpiries, optionStrikes));
363
364 sigmaValues = {0.20};
365 fxConfigs.push_back(QuantLib::ext::make_shared<FxBsData>("GBP", "EUR", calibrationType, true, ParamType::Piecewise,
366 sigmaTimes, sigmaValues, optionExpiries, optionStrikes));
367
368 sigmaValues = {0.20};
369 fxConfigs.push_back(QuantLib::ext::make_shared<FxBsData>("CHF", "EUR", calibrationType, true, ParamType::Piecewise,
370 sigmaTimes, sigmaValues, optionExpiries, optionStrikes));
371
372 sigmaValues = {0.20};
373 fxConfigs.push_back(QuantLib::ext::make_shared<FxBsData>("JPY", "EUR", calibrationType, true, ParamType::Piecewise,
374 sigmaTimes, sigmaValues, optionExpiries, optionStrikes));
375
376 map<CorrelationKey, Handle<Quote>> corr;
379 corr[make_pair(f_1, f_2)] = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.6));
380
381 QuantLib::ext::shared_ptr<CrossAssetModelData> config(QuantLib::ext::make_shared<CrossAssetModelData>(irConfigs, fxConfigs, corr));
382
383
384
385 QuantLib::ext::shared_ptr<CrossAssetModelBuilder> modelBuilder(
new CrossAssetModelBuilder(initMarket, config));
386 QuantLib::ext::shared_ptr<QuantExt::CrossAssetModel> model = *modelBuilder->model();
387 modelBuilder = NULL;
388
389
390 Size seed = 5;
391 bool antithetic = false;
392 if (auto tmp = QuantLib::ext::dynamic_pointer_cast<CrossAssetStateProcess>(model->stateProcess())) {
393 tmp->resetCache(dg->timeGrid().size() - 1);
394 }
395 QuantLib::ext::shared_ptr<QuantExt::MultiPathGeneratorBase> pathGen =
396 QuantLib::ext::make_shared<MultiPathGeneratorMersenneTwister>(model->stateProcess(), dg->timeGrid(), seed, antithetic);
397
398
399 QuantLib::ext::shared_ptr<ScenarioFactory> scenarioFactory = QuantLib::ext::make_shared<SimpleScenarioFactory>(true);
400 QuantLib::ext::shared_ptr<ScenarioGenerator> scenarioGenerator = QuantLib::ext::make_shared<CrossAssetModelScenarioGenerator>(
401 model, pathGen, scenarioFactory, parameters, today, dg, initMarket);
402
403
405 auto simMarket = QuantLib::ext::make_shared<analytics::ScenarioSimMarket>(initMarket, parameters);
406 simMarket->scenarioGenerator() = scenarioGenerator;
407
408
409 QuantLib::ext::shared_ptr<EngineData>
data = QuantLib::ext::make_shared<EngineData>();
410 data->model(
"Swap") =
"DiscountedCashflows";
411 data->engine(
"Swap") =
"DiscountingSwapEngine";
412 QuantLib::ext::shared_ptr<EngineFactory> factory = QuantLib::ext::make_shared<EngineFactory>(data, simMarket);
413
414 QuantLib::ext::shared_ptr<Portfolio> portfolio =
buildPortfolio(portfolioSize, factory);
415
416 BOOST_TEST_MESSAGE("Portfolio size after build: " << portfolio->size());
417
418
420
421
422 boost::timer::cpu_timer t;
423 QuantLib::ext::shared_ptr<NPVCube> cube =
424 QuantLib::ext::make_shared<DoublePrecisionInMemoryCube>(today, portfolio->ids(), dg->dates(), samples);
425 vector<QuantLib::ext::shared_ptr<ValuationCalculator>> calculators;
426 calculators.push_back(QuantLib::ext::make_shared<NPVCalculator>(baseCcy));
427 valEngine.buildCube(portfolio, cube, calculators);
428 t.stop();
429 double elapsed = t.elapsed().wall * 1e-9;
430
431 BOOST_TEST_MESSAGE("Cube generated in " << elapsed << " seconds");
432
433 Size dates = dg->dates().size();
434 Size numNPVs = dates * samples * portfolioSize;
435 BOOST_TEST_MESSAGE("Cube size = " << numNPVs << " elements");
436 BOOST_TEST_MESSAGE("Cube elements theoretical storage " << numNPVs * sizeof(Real) / (1024 * 1024) << " MB");
437 Real pricingTimeMicroSeconds = elapsed * 1000000 / numNPVs;
438 BOOST_TEST_MESSAGE("Avg Pricing time = " << pricingTimeMicroSeconds << " microseconds");
439
440
441
443 for (Size i = 0; i < portfolioSize; ++i) {
444 for (Size j = 0; j < dates; ++j) {
445 if (cube->get(i, j, 0) == 0 && cube->get(i, j, 1) == 0 && cube->get(i, j, 2) == 0)
447 }
448 }
449 Real nonZeroPerc = 100 * (1 - (
count / ((Real)portfolioSize * dates)));
450 BOOST_TEST_MESSAGE("Percentage of cube that is non-expired : " << nonZeroPerc << " %");
451 BOOST_TEST_MESSAGE("Avg Pricing time (for non-expired trades) = " << pricingTimeMicroSeconds * 100 / nonZeroPerc
452 << " microseconds");
453
454
455
456
457 vector<Real> eeVec, eneVec;
458 for (Size i = 0; i < dates; ++i) {
459 Real epe = 0.0, ene = 0.0;
460 for (Size j = 0; j < samples; ++j) {
461 Real npv = 0.0;
462 for (Size k = 0; k < portfolioSize; ++k)
463 npv += cube->get(k, i, j);
464 epe += std::max(npv, 0.0);
465 ene += std::max(-npv, 0.0);
466 }
467 epe /= samples;
468 ene /= samples;
469
470 eeVec.push_back(epe);
471 eneVec.push_back(ene);
472 }
473
474 ObservationMode::instance().setMode(backupOm);
475 IndexManager::instance().clearHistories();
476
477
478 BOOST_CHECK_CLOSE(nonZeroPVRatio, nonZeroPerc, 0.005);
479
480 for (Size i = 0; i < epe_archived.size(); ++i) {
481 BOOST_CHECK_CLOSE(eeVec[i], epe_archived[i], 0.01);
482 }
483
484 for (Size i = 0; i < ene_archived.size(); ++i) {
485 BOOST_CHECK_CLOSE(eneVec[i], ene_archived[i], 0.01);
486 }
487}
ScenarioSimMarket description.