Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
nettedexpsoure.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2016 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 <boost/test/unit_test.hpp>
20#include <boost/timer/timer.hpp>
22#include <orea/cube/npvcube.hpp>
23
38#include <oret/toplevelfixture.hpp>
39#include <ql/math/randomnumbers/mt19937uniformrng.hpp>
40#include <ql/time/calendars/target.hpp>
41#include <ql/time/date.hpp>
42#include <ql/time/daycounters/actualactual.hpp>
45
46#include "testmarket.hpp"
47
57#include <orea/cube/cube_io.hpp>
59#include <ql/time/date.hpp>
60#include <ql/utilities/dataparsers.hpp>
61#include <cmath>
62
63using namespace std;
64using namespace QuantLib;
65using namespace QuantExt;
66using namespace boost::unit_test_framework;
67using namespace ore;
68using namespace ore::data;
69using namespace ore::analytics;
70
72
73BOOST_FIXTURE_TEST_SUITE(OREAnalyticsTestSuite, ore::test::OreaTopLevelFixture)
74
75BOOST_AUTO_TEST_SUITE(CollateralisedExposureTest)
76
77QuantLib::ext::shared_ptr<data::Conventions> convs() {
78 QuantLib::ext::shared_ptr<data::Conventions> conventions(new data::Conventions());
79
80 QuantLib::ext::shared_ptr<data::Convention> swapIndexConv(
81 new data::SwapIndexConvention("EUR-CMS-2Y", "EUR-6M-SWAP-CONVENTIONS"));
82 conventions->add(swapIndexConv);
83
84 QuantLib::ext::shared_ptr<data::Convention> swapConv(
85 new data::IRSwapConvention("EUR-6M-SWAP-CONVENTIONS", "TARGET", "Annual", "MF", "30/360", "EUR-EURIBOR-6M"));
86 conventions->add(swapConv);
87
88 InstrumentConventions::instance().setConventions(conventions);
89
90 return conventions;
91}
92
93QuantLib::ext::shared_ptr<Portfolio> buildPortfolio(Size portfolioSize, QuantLib::ext::shared_ptr<EngineFactory>& factory) {
94
95 QuantLib::ext::shared_ptr<Portfolio> portfolio(new Portfolio());
96
97 vector<string> ccys = {"EUR"};
98
99 map<string, vector<string>> indices = {{"EUR", {"EUR-EURIBOR-6M"}}};
100
101 vector<string> fixedTenors = {"1Y"};
102
103 Size seed = 5; // keep this constant to ensure portfolio doesn't change
104 MersenneTwisterUniformRng rng(seed);
105
106 Date today = Settings::instance().evaluationDate();
107
108 Calendar cal = TARGET();
109 string calStr = "TARGET";
110 string conv = "MF";
111 string rule = "Forward";
112 Size days = 2;
113 string fixDC = "30/360";
114 string floatDC = "ACT/365";
115
116 vector<double> notional(1, 1000000);
117 vector<double> spread(1, 0);
118
119 for (Size i = 0; i < portfolioSize; i++) {
120 Size term = 1;
121 // Start today +/- 1 Year
122 Date startDate = cal.adjust(today);
123 Date endDate = cal.adjust(startDate + term * Years);
124
125 // date 2 string
126 ostringstream oss;
127 oss << io::iso_date(startDate);
128 string start(oss.str());
129 oss.str("");
130 oss.clear();
131 oss << io::iso_date(endDate);
132 string end(oss.str());
133
134 // ccy + index
135 string ccy = "EUR";
136 string index = "EUR-EURIBOR-6M";
137 string floatFreq = "6M";
138
139 // This variable is not used and only here to ensure that the random numbers generated in
140 // subsequent blocks produce a swap portfolio, which is compatible with the archived values.
141 string fixedTenor = "1Y";
142 fixedTenor = fixedTenor + "_";
143
144 // fixed details
145 Real fixedRate = 0.02 ;
146 string fixFreq = "1Y";
147
148 // envelope
149 Envelope env("CP", "NettingSet1");
150
151 // Schedules
152 ScheduleData floatSchedule(ScheduleRules(start, end, floatFreq, calStr, conv, conv, rule));
153 ScheduleData fixedSchedule(ScheduleRules(start, end, fixFreq, calStr, conv, conv, rule));
154
155 bool isPayer = true;
156
157 // fixed Leg - with dummy rate
158 LegData fixedLeg(QuantLib::ext::make_shared<FixedLegData>(vector<double>(1, fixedRate)), isPayer, ccy, fixedSchedule,
159 fixDC, notional);
160
161 // float Leg
162 vector<double> spreads(1, 0);
163 LegData floatingLeg(QuantLib::ext::make_shared<FloatingLegData>(index, days, false, spread), !isPayer, ccy,
164 floatSchedule, floatDC, notional);
165
166 QuantLib::ext::shared_ptr<Trade> swap(new data::Swap(env, floatingLeg, fixedLeg));
167
168 // id
169 oss.clear();
170 oss.str("");
171 oss << "Trade_" << i + 1;
172 swap->id() = oss.str();
173
174 portfolio->add(swap);
175 }
176 // portfolio->save("port.xml");
177
178 portfolio->build(factory);
179
180 if (portfolio->size() != portfolioSize)
181 BOOST_ERROR("Failed to build portfolio (got " << portfolio->size() << " expected " << portfolioSize << ")");
182
183 // Dump stats about portfolio
184 Time maturity = 0;
185 DayCounter dc = ActualActual(ActualActual::ISDA);
186 map<string, Size> fixedFreqs;
187 map<string, Size> floatFreqs;
188 for (const auto& [tradeId, trade] : portfolio->trades()) {
189 maturity += dc.yearFraction(today, trade->maturity());
190
191 // fixed Freq
192 QuantLib::ext::shared_ptr<data::Swap> swap = QuantLib::ext::dynamic_pointer_cast<data::Swap>(trade);
193 string floatFreq = swap->legData()[0].schedule().rules().front().tenor();
194 string fixFreq = swap->legData()[1].schedule().rules().front().tenor();
195 QL_REQUIRE(swap->legData()[0].legType() == "Floating" && swap->legData()[1].legType() == "Fixed", "Leg mixup");
196 if (fixedFreqs.find(fixFreq) == fixedFreqs.end())
197 fixedFreqs[fixFreq] = 1;
198 else
199 fixedFreqs[fixFreq]++;
200 if (floatFreqs.find(floatFreq) == floatFreqs.end())
201 floatFreqs[floatFreq] = 1;
202 else
203 floatFreqs[floatFreq]++;
204 }
205 maturity /= portfolioSize;
206 BOOST_TEST_MESSAGE("Portfolio Size : " << portfolioSize);
207 BOOST_TEST_MESSAGE("Maturity : " << maturity);
208 ostringstream oss;
209 for (Size i = 0; i < ccys.size(); i++)
210 oss << ccys[i] << " ";
211 BOOST_TEST_MESSAGE("Currencies : " << oss.str());
212 // dump % breakdown of tenors
213 map<string, Size>::iterator it;
214 BOOST_TEST_MESSAGE("Fixed Tenors : "<< fixedFreqs.begin()->first);
215 BOOST_TEST_MESSAGE("Floating Tenors : "<< floatFreqs.begin()->first);
216 return portfolio;
217}
218
219QuantLib::ext::shared_ptr<CrossAssetModel> buildCrossAssetModel(QuantLib::ext::shared_ptr<Market>& initMarket){
220 // Config
221 // Build IR configurations
222 CalibrationType calibrationType = CalibrationType::Bootstrap;
223 LgmData::ReversionType revType = LgmData::ReversionType::HullWhite;
224 LgmData::VolatilityType volType = LgmData::VolatilityType::Hagan;
225 vector<string> swaptionExpiries = {"1Y", "2Y", "3Y", "5Y", "7Y", "10Y", "15Y", "20Y", "30Y"};
226 vector<string> swaptionTerms = {"5Y", "5Y", "5Y", "5Y", "5Y", "5Y", "5Y", "5Y", "5Y"};
227 vector<string> swaptionStrikes(swaptionExpiries.size(), "ATM");
228 vector<Time> hTimes = {};
229 vector<Time> aTimes = {};
230
231 std::vector<QuantLib::ext::shared_ptr<IrModelData>> irConfigs;
232
233 vector<Real> hValues = {0.02};
234 vector<Real> aValues = {0.008};
235 irConfigs.push_back(QuantLib::ext::make_shared<IrLgmData>(
236 "EUR", calibrationType, revType, volType, false, ParamType::Constant, hTimes, hValues, true,
237 ParamType::Piecewise, aTimes, aValues, 0.0, 1.0, swaptionExpiries, swaptionTerms, swaptionStrikes));
238
239 hValues = {0.03};
240 aValues = {0.009};
241 irConfigs.push_back(QuantLib::ext::make_shared<IrLgmData>(
242 "USD", calibrationType, revType, volType, false, ParamType::Constant, hTimes, hValues, true,
243 ParamType::Piecewise, aTimes, aValues, 0.0, 1.0, swaptionExpiries, swaptionTerms, swaptionStrikes));
244
245
246 // Compile FX configurations
247 vector<string> optionExpiries = {"1Y", "2Y", "3Y", "5Y", "7Y", "10Y"};
248 vector<string> optionStrikes(optionExpiries.size(), "ATMF");
249 vector<Time> sigmaTimes = {};
250
251 std::vector<QuantLib::ext::shared_ptr<FxBsData>> fxConfigs;
252
253 vector<Real> sigmaValues = {0.15};
254 fxConfigs.push_back(QuantLib::ext::make_shared<FxBsData>("USD", "EUR", calibrationType, true, ParamType::Piecewise,
255 sigmaTimes, sigmaValues, optionExpiries, optionStrikes));
256
257 map<CorrelationKey, Handle<Quote>> corr;
258 CorrelationFactor f_1{ CrossAssetModel::AssetType::IR, "EUR", 0 };
259 CorrelationFactor f_2{ CrossAssetModel::AssetType::IR, "USD", 0 };
260 corr[make_pair(f_1, f_2)] = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.6));
261
262 QuantLib::ext::shared_ptr<CrossAssetModelData> config(QuantLib::ext::make_shared<CrossAssetModelData>(irConfigs, fxConfigs, corr));
263
264 // Model Builder & Model
265 // model builder
266 QuantLib::ext::shared_ptr<CrossAssetModelBuilder> modelBuilder(new CrossAssetModelBuilder(initMarket, config));
267 return *modelBuilder->model();
268
269}
270
271QuantLib::ext::shared_ptr<analytics::ScenarioSimMarket> buildScenarioSimMarket(QuantLib::ext::shared_ptr<DateGrid> dateGrid,
272 QuantLib::ext::shared_ptr<Market>& initMarket,
273 QuantLib::ext::shared_ptr<CrossAssetModel>& model,
274 Size samples=1,
275 Size seed=5,
276 bool antithetic=false){
277 // build scenario sim market parameters
278 Date today = initMarket->asofDate();
279
280 string baseCcy = "EUR";
281 vector<string> ccys;
282 ccys.push_back(baseCcy);
283 ccys.push_back("USD");
284
285 QuantLib::ext::shared_ptr<analytics::ScenarioSimMarketParameters> parameters(new analytics::ScenarioSimMarketParameters());
286 parameters->baseCcy() = baseCcy;
287 parameters->setDiscountCurveNames(ccys);
288 parameters->setYieldCurveTenors("",
289 {1 * Months, 6 * Months, 1 * Years, 2 * Years, 5 * Years, 10 * Years, 20 * Years});
290 parameters->setIndices({"EUR-EONIA", "EUR-EURIBOR-6M", "USD-LIBOR-3M"});
291
292 parameters->interpolation() = "LogLinear";
293
294 parameters->setSimulateSwapVols(false);
295 parameters->setSwapVolTerms("", {6 * Months, 1 * Years});
296 parameters->setSwapVolExpiries("", {1 * Years, 2 * Years});
297 parameters->swapVolKeys() = ccys;
298 parameters->swapVolDecayMode() = "ForwardVariance";
299
300 parameters->setFxVolExpiries("",
301 vector<Period>{1 * Months, 3 * Months, 6 * Months, 2 * Years, 3 * Years, 4 * Years, 5 * Years});
302 parameters->setFxVolDecayMode(string("ConstantVariance"));
303 parameters->setSimulateFXVols(false);
304
305 parameters->setFxVolCcyPairs({"USDEUR"});
306 parameters->setFxCcyPairs({"USDEUR"});
307
308 parameters->setAdditionalScenarioDataIndices({"EUR-EONIA"});
309 parameters->setAdditionalScenarioDataCcys({"EUR"});
310
311 // Path generator
312 if (auto tmp = QuantLib::ext::dynamic_pointer_cast<CrossAssetStateProcess>(model->stateProcess())) {
313 tmp->resetCache(dateGrid->timeGrid().size() - 1);
314 }
315 QuantLib::ext::shared_ptr<QuantExt::MultiPathGeneratorBase> pathGen =
316 QuantLib::ext::make_shared<MultiPathGeneratorMersenneTwister>(model->stateProcess(), dateGrid->timeGrid(), seed, antithetic);
317
318 // build scenario generator
319 QuantLib::ext::shared_ptr<ScenarioFactory> scenarioFactory = QuantLib::ext::make_shared<SimpleScenarioFactory>();
320 QuantLib::ext::shared_ptr<ScenarioGenerator> scenarioGenerator = QuantLib::ext::make_shared<CrossAssetModelScenarioGenerator>(model,
321 pathGen,
322 scenarioFactory,
323 parameters,
324 today,
325 dateGrid,
326 initMarket);
327
328 // build scenario sim market
329 convs();
330 auto simMarket = QuantLib::ext::make_shared<analytics::ScenarioSimMarket>(initMarket, parameters);
331 simMarket->scenarioGenerator() = scenarioGenerator;
332
333 QuantLib::ext::shared_ptr<AggregationScenarioData> scenarioData = QuantLib::ext::make_shared<InMemoryAggregationScenarioData>(dateGrid->timeGrid().size(), samples);
334 simMarket->aggregationScenarioData() = scenarioData;
335
336 return simMarket;
337}
338
339QuantLib::ext::shared_ptr<NPVCube> buildNPVCube(QuantLib::ext::shared_ptr<DateGrid> dateGrid,
340 bool withCloseOutGrid,
341 QuantLib::ext::shared_ptr<analytics::ScenarioSimMarket>& simMarket,
342 QuantLib::ext::shared_ptr<Portfolio>& portfolio,
343 bool mporStickyDate=false,
344 Size samples=1,
345 Size seed=5){
346
347 Date today = Settings::instance().evaluationDate();
348 // Now calculate exposure
349 ValuationEngine valEngine(today, dateGrid, simMarket);
350
351 Size depth = 1;
352 if (withCloseOutGrid)
353 depth = 2;
354
355 // Calculate Cube
356 boost::timer::cpu_timer t;
357 //BOOST_TEST_MESSAGE("dg->numDates() " << dateGrid->valuationDates().size()<<", " <<"dg->dates() "<<dateGrid->dates().size());
358 QuantLib::ext::shared_ptr<NPVCube> cube =
359 QuantLib::ext::make_shared<DoublePrecisionInMemoryCubeN>(today, portfolio->ids(), dateGrid->valuationDates(), samples, depth);
360
361 vector<QuantLib::ext::shared_ptr<ValuationCalculator>> calculators;
362 QuantLib::ext::shared_ptr<NPVCalculator> npvCalc = QuantLib::ext::make_shared<NPVCalculator>("EUR");
363 calculators.push_back(npvCalc);
364 if (withCloseOutGrid)
365 calculators.push_back(QuantLib::ext::make_shared<MPORCalculator>(npvCalc));
366 BOOST_TEST_MESSAGE("mporStickyDate "<<mporStickyDate);
367 valEngine.buildCube(portfolio, cube, calculators, mporStickyDate);
368 t.stop();
369 double elapsed = t.elapsed().wall * 1e-9;
370
371 if (withCloseOutGrid){
372 std::string fileName = "scenarioData_closeout.csv";
373 saveAggregationScenarioData(fileName, *simMarket->aggregationScenarioData());
374 fileName = "cube_closeout.csv";
375 saveCube(fileName, NPVCubeWithMetaData{cube, nullptr, boost::none, boost::none});
376 }else{
377 std::string fileName = "scenarioData.csv";
378 saveAggregationScenarioData(fileName, *simMarket->aggregationScenarioData());
379 fileName = "cube.csv";
380 saveCube(fileName, NPVCubeWithMetaData{cube, nullptr, boost::none, boost::none});
381 }
382
383 BOOST_TEST_MESSAGE("Cube generated in " << elapsed << " seconds");
384 return cube;
385}
386
387struct TestData : ore::test::OreaTopLevelFixture {
388
389 TestData(Date referenceDate, QuantLib::ext::shared_ptr<DateGrid> dateGrid, bool withCloseOutGrid = false, bool mporStickyDate = false, Size samples=1, Size seed=5){
390 // Init market
391 BOOST_TEST_MESSAGE("Setting initial market ...");
392 this->initMarket_ = QuantLib::ext::make_shared<TestMarket>(referenceDate);
393 BOOST_TEST_MESSAGE("Setting initial market done!");
394
395 BOOST_TEST_MESSAGE("Building CAM ...");
396 this->model_ = buildCrossAssetModel(this->initMarket_);
397 BOOST_TEST_MESSAGE("Building CAM done!");
398
399 BOOST_TEST_MESSAGE("Building SimMarket ...");
400 this->simMarket_ = buildScenarioSimMarket(dateGrid, this->initMarket_, this->model_, samples, seed);
401 BOOST_TEST_MESSAGE("Building SimMarket done!");
402
403 // Build Portfolio
404 BOOST_TEST_MESSAGE("Building Portfolio ...");
405 QuantLib::ext::shared_ptr<EngineData> data = QuantLib::ext::make_shared<EngineData>();
406 data->model("Swap") = "DiscountedCashflows";
407 data->engine("Swap") = "DiscountingSwapEngine";
408 QuantLib::ext::shared_ptr<EngineFactory> factory = QuantLib::ext::make_shared<EngineFactory>(data, this->simMarket_);
409 //factory->registerBuilder(QuantLib::ext::make_shared<SwapEngineBuilder>());
410 Size portfolioSize = 1;
411 this->portfolio_ = buildPortfolio(portfolioSize, factory);
412 BOOST_TEST_MESSAGE("Building Portfolio done!");
413 BOOST_TEST_MESSAGE("Portfolio size after build: " << this->portfolio_->size());
414
415 BOOST_TEST_MESSAGE("Building NPV cube ...");
416 this->cube_ = buildNPVCube(dateGrid, withCloseOutGrid, this->simMarket_, this->portfolio_, mporStickyDate, samples, seed);
417 BOOST_TEST_MESSAGE("Building NPV done!");
418 }
419
420 QuantLib::ext::shared_ptr<Market> initMarket_;
421 QuantLib::ext::shared_ptr<analytics::ScenarioSimMarket> simMarket_;
422 QuantLib::ext::shared_ptr<QuantExt::CrossAssetModel> model_;
423 QuantLib::ext::shared_ptr<NPVCube> cube_;
424 QuantLib::ext::shared_ptr<Portfolio> portfolio_;
425};
426
427struct CachedResultsData : ore::test::OreaTopLevelFixture {
428
429CachedResultsData(){
430
431 tuple<string, string, string, string, string, string> key;
432 key = make_tuple("13,1W", "1W", "woCloseOutGrid", "ActualDate", "Symmetric", "woCompounding");
433 vector<Date> defaultDate = {Date(42481),Date(42488),Date(42495),Date(42502),Date(42509),Date(42516),Date(42523),Date(42530),Date(42537),Date(42544),Date(42551),Date(42558),Date(42565)};
434 vector<Real> defaultValue = {-5187.5422,-4905.1896,-4546.209,-4934.3538,-4719.8216,-4726.7086,-4942.2042,-4829.1002,-4871.8577,-4660.3374,-4835.9162,-5210.7846,-5112.2817};
435 vector<Date> closeOutDate = {};
436 vector<Real> closeOutValue = {-4964.2459,-5187.5422,-4905.1896,-4546.209,-4934.3538,-4719.8216,-4726.7086,-4942.2042,-4829.1002,-4871.8577,-4660.3374,-4835.9162,-5210.7846};
437 defaultDates[key] = defaultDate;
438 defaultValues[key] = defaultValue;
439 closeOutDates[key] = closeOutDate;
440 closeOutValues[key] = closeOutValue;
441
442 key = make_tuple("13,1W", "1W", "woCloseOutGrid", "ActualDate", "AsymmetricCVA", "woCompounding");
443 defaultDate = {Date(42481),Date(42488),Date(42495),Date(42502),Date(42509),Date(42516),Date(42523),Date(42530),Date(42537),Date(42544),Date(42551),Date(42558),Date(42565)};
444 defaultValue = {-5187.5422,-4905.1896,-4546.209,-4934.3538,-4719.8216,-4726.7086,-4942.2042,-4829.1002,-4871.8577,-4660.3374,-4835.9162,-5210.7846,-5112.2817};
445 closeOutValue = {-5187.5422,-5187.5422,-4905.1896,-4934.3538,-4934.3538,-4726.7086,-4942.2042,-4942.2042,-4871.8577,-4871.8577,-4835.9162,-5210.7846,-5210.7846};
446 defaultDates[key] = defaultDate;
447 defaultValues[key] = defaultValue;
448 closeOutDates[key] = closeOutDate;
449 closeOutValues[key] = closeOutValue;
450
451
452 key = make_tuple("13,1W", "1W", "woCloseOutGrid", "ActualDate", "AsymmetricDVA", "woCompounding");
453 defaultDate = {Date(42481),Date(42488),Date(42495),Date(42502),Date(42509),Date(42516),Date(42523),Date(42530),Date(42537),Date(42544),Date(42551),Date(42558),Date(42565)};
454 defaultValue = {-5187.5422,-4905.1896,-4546.209,-4934.3538,-4719.8216,-4726.7086,-4942.2042,-4829.1002,-4871.8577,-4660.3374,-4835.9162,-5210.7846,-5112.2817};
455 closeOutValue = {-4964.2459,-4905.1896,-4546.209,-4546.209,-4719.8216,-4719.8216,-4726.7086,-4829.1002,-4829.1002,-4660.3374,-4660.3374,-4835.9162,-5112.2817};
456 defaultDates[key] = defaultDate;
457 defaultValues[key] = defaultValue;
458 closeOutDates[key] = closeOutDate;
459 closeOutValues[key] = closeOutValue;
460
461 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
462 key = make_tuple("13,1M", "1W", "withCloseOutGrid", "ActualDate", "NoLag", "woCompounding");
463 defaultDate = {Date(42506),Date(42535),Date(42565),Date(42597),Date(42627),Date(42657),Date(42688),Date(42718),Date(42751),Date(42780),Date(42808),Date(42843),Date(42870)};
464 defaultValue = {-5202.1081,-4824.2195,-4475.0983,-4839.8679,-4781.7627,-10033.828,-10034.132,-10042.506,-10051.002,-10029.219,-10053.942,0,0};
465 closeOutValue = {-5482.992,-4430.4454,-4466.1567,-4952.8999,-4984.8645,-10026.681,-10030.382,-10036.444,-10049.344,-10024.172,-10050.758,0,0};
466 defaultDates[key] = defaultDate;
467 defaultValues[key] = defaultValue;
468 closeOutDates[key] = closeOutDate;
469 closeOutValues[key] = closeOutValue;
470
471 key = make_tuple("13,1M", "1W", "withCloseOutGrid", "ActualDate", "NoLag", "withCompounding");
472 defaultDate = {Date(42506),Date(42535),Date(42565),Date(42597),Date(42627),Date(42657),Date(42688),Date(42718),Date(42751),Date(42780),Date(42808),Date(42843),Date(42870)};
473 defaultValue = {-5201.05244612274,-4824.95292840006,-4477.84062127441,-4840.59273964169,-4783.19175595342,-10033.5472051449,-10033.8518374855,-10042.17634737,-10050.6267511171,-10028.9670934353,-10053.5482862015,0,0};
474 closeOutValue = {-5480.43076439459,-4433.281369965,-4468.94866890169,-4953.02218430623,-4985.17359043462,-10026.4183374549,-10030.1017618035,-10036.1263742983,-10048.9573202704,-10023.9263145357,-10050.3607175246,0,0};
475 defaultDates[key] = defaultDate;
476 defaultValues[key] = defaultValue;
477 closeOutDates[key] = closeOutDate;
478 closeOutValues[key] = closeOutValue;
479 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
480}
481
482
483struct Results {
484 string dateGridStr;
485 string nettingSetMporStr;
486 string closeOutGridStr;
487 string mporModeStr;
488 string calcType;
489 string compoundingStr;
490 vector<Date> defaultDate;
491 vector<Real> defaultValue;
492 vector<Date> closeOutDate;
493 vector<Real> closeOutValue;
494};
495
496std::map<tuple<string, string, string, string, string, string>, vector<Date>> defaultDates;
497std::map<tuple<string, string, string, string, string, string>, vector<Real>> defaultValues;
498std::map<tuple<string, string, string, string, string, string>, vector<Date>> closeOutDates;
499std::map<tuple<string, string, string, string, string, string>, vector<Real>> closeOutValues;
500
501};
502
503
504BOOST_AUTO_TEST_CASE(NettedExposureCalculatorTest) {
505
506
507
508 CachedResultsData cachedResults;
509 std::map<tuple<string, string, string, string, string, string>, vector<Date>> cachedDefaultDates = cachedResults.defaultDates;
510 std::map<tuple<string, string, string, string, string, string>, vector<Real>> cachedDefaultValues = cachedResults.defaultValues;
511 std::map<tuple<string, string, string, string, string, string>, vector<Date>> cachedCloseOutDates = cachedResults.closeOutDates;
512 std::map<tuple<string, string, string, string, string, string>, vector<Real>> cachedCloseOutValues = cachedResults.closeOutValues;
513
514
515 BOOST_TEST_MESSAGE("Running NettedExposureCalculatorTestWithCloseOutGrid...");
516 Date referenceDate = Date(14, April, 2016);
517 BOOST_TEST_MESSAGE("Reference Date is "<<QuantLib::io::iso_date(referenceDate));
518 Settings::instance().evaluationDate() = referenceDate;
519
520 string dateGridStr;
521 QuantLib::ext::shared_ptr<DateGrid> dateGrid;
522 std::string nettingSetMpor = "1W";
523 BOOST_TEST_MESSAGE("Neting-set mpor is "<< nettingSetMpor);
524 vector<bool> withCloseOutGrid = {false, true};
525 bool mporStickyDate = false;
526 string mporModeStr = mporStickyDate ? "StickyDate": "ActualDate";
527 bool withCompounding = false;
528 string compoundingStr = withCompounding ? "withCompounding" : "woCompounding";
529 string calcTypeStr;
530
531 for(Size k = 0; k<withCloseOutGrid.size(); k++){
532
533 string closeOutGridStr = withCloseOutGrid[k] ? "withCloseOutGrid": "woCloseOutGrid";
534 std::vector<CollateralExposureHelper::CalculationType> calcTypes;
535 if (withCloseOutGrid[k]){
536 dateGridStr = "13,1M";
537 dateGrid = QuantLib::ext::make_shared<DateGrid>(dateGridStr);
538 calcTypes= {CollateralExposureHelper::CalculationType::NoLag};
539 }
540 else{
541 dateGridStr = "13,1W";
542 dateGrid = QuantLib::ext::make_shared<DateGrid>(dateGridStr);
543 calcTypes= {CollateralExposureHelper::CalculationType::Symmetric,
544 CollateralExposureHelper::CalculationType::AsymmetricCVA,
545 CollateralExposureHelper::CalculationType::AsymmetricDVA
546 };
547 }
548 Period Mpor(1, Weeks);
549 if (withCloseOutGrid[k]){
550 BOOST_TEST_MESSAGE("With close-out grid!");
551 BOOST_TEST_MESSAGE("MPOR in close-out grid= "<< Mpor);
552 dateGrid->addCloseOutDates(Mpor);
553 if (mporStickyDate){
554 BOOST_TEST_MESSAGE("With mpor mode sticky date!");
555 }else
556 BOOST_TEST_MESSAGE("With mpor mode actual date!");
557 }else
558 BOOST_TEST_MESSAGE("Without close-out grid!");
559
560 TestData td(referenceDate, dateGrid, withCloseOutGrid[k], mporStickyDate);
561
562 BOOST_TEST_MESSAGE("DateGrid: ");
563 BOOST_TEST_MESSAGE("t_0, "<<io::iso_date(referenceDate));
564 for(Size i=0; i<dateGrid->times().size(); i++)
565 BOOST_TEST_MESSAGE("t_"<<i+1<<", "<<QuantLib::io::iso_date(dateGrid->dates()[i]));
566
567 QuantLib::ext::shared_ptr<Market> initMarket = td.initMarket_;
568 QuantLib::ext::shared_ptr<NPVCube> cube = td.cube_;
569 QuantLib::ext::shared_ptr<Portfolio> portfolio = td.portfolio_;
570
571 std::string nettingSetId = portfolio->trades().begin()->second->envelope().nettingSetId();
572 NettingSetDetails nettingSetDetails(nettingSetId);
573
574 if (withCloseOutGrid[k]){
575 Period nettingSetMporPeriod = PeriodParser::parse(nettingSetMpor);
576 QL_REQUIRE(nettingSetMporPeriod == Mpor, "Netting-set mpor is not consistent with the closeout grid!");
577 }
578
579 std::vector<std::string> elgColls = {"EUR"};
580 QuantLib::ext::shared_ptr<NettingSetDefinition> nettingSetDefinition = QuantLib::ext::make_shared<NettingSetDefinition>(nettingSetDetails,
581 "Bilateral", "EUR", "EUR-EONIA",
582 0.0, 0.0, 0.0, 0.0, 0.0,
583 "FIXED", "1D", "1D",
584 nettingSetMpor, 0.0, 0.0, elgColls);
585 QuantLib::ext::shared_ptr<NettingSetManager> nettingSetManager = QuantLib::ext::make_shared<NettingSetManager>();
586 nettingSetManager->add(nettingSetDefinition);
587
588 // Empty balances for now
589 QuantLib::ext::shared_ptr<CollateralBalances> collateralBalances = QuantLib::ext::make_shared<CollateralBalances>();
590
591 map<string, vector<vector<Real>>> nettingSetDefaultValue;
592 map<string, vector<vector<Real>>> nettingSetCloseOutValue;
593 map<string, vector<vector<Real>>> nettingSetMporPositiveFlow;
594 map<string, vector<vector<Real>>> nettingSetMporNegativeFlow;
595 map<string, vector<vector<Real>>> nettingSetValue;
596 vector<Real> collateralBalance;
597 vector<vector<Real>> defaultValue;
598
599 std::string fileName;
600 Handle<AggregationScenarioData> asd;
601 QuantLib::ext::shared_ptr<CubeInterpretation> cubeInterpreter;
602 if (withCloseOutGrid[k]) {
603 fileName = "scenarioData_closeout.csv";
604 asd = Handle<AggregationScenarioData>(loadAggregationScenarioData(fileName));
605 cubeInterpreter = QuantLib::ext::make_shared<CubeInterpretation>(true, true, asd, dateGrid);
606 } else {
607 fileName = "scenarioData.csv";
608 asd = Handle<AggregationScenarioData>(loadAggregationScenarioData(fileName));
609 cubeInterpreter = QuantLib::ext::make_shared<CubeInterpretation>(true, false, asd);
610 }
611
612 if (!withCompounding){
613 compoundingStr = "woCompounding";
614 for (Size i =0; i<cube->dates().size(); i++)
615 asd->set(i, 0, 0, AggregationScenarioDataType::IndexFixing, "EUR-EONIA");
616 }
617
618 vector<string> regressors = {"EUR-EURIBOR-6M"};
619 QuantLib::ext::shared_ptr<InputParameters> inputs = QuantLib::ext::make_shared<InputParameters>();
620 QuantLib::ext::shared_ptr<RegressionDynamicInitialMarginCalculator> dimCalculator =
621 QuantLib::ext::make_shared<RegressionDynamicInitialMarginCalculator>(inputs, portfolio, cube, cubeInterpreter, *asd,
622 0.99, 14, 2, regressors);
623
624 BOOST_TEST_MESSAGE("initial NPV at "<< QuantLib::io::iso_date(referenceDate)<<": "<<cube->getT0(0));
625 for (Size i = 0; i<cube->dates().size(); i++)
626 BOOST_TEST_MESSAGE("defaultValue at "<< QuantLib::io::iso_date(dateGrid->valuationDates()[i])<<": "<<cubeInterpreter->getDefaultNpv(cube, 0, i, 0));
627
628 for (Size i = 0; i<cube->dates().size(); i++){
629 if (withCloseOutGrid[k]){
630 if (i != cube->dates().size()-1)
631 BOOST_TEST_MESSAGE("closeOutValue at "<< QuantLib::io::iso_date(dateGrid->closeOutDates()[i])<<": "<<cubeInterpreter->getCloseOutNpv(cube, 0, i, 0));
632 }
633 else{
634 if (i != cube->dates().size()-1)
635 BOOST_TEST_MESSAGE("closeOutValue at "<< QuantLib::io::iso_date(dateGrid->valuationDates()[i])<<": "<<cubeInterpreter->getCloseOutNpv(cube, 0, i, 0));
636 }
637
638 }
639
640 for(auto calcType : calcTypes){
642 calcTypeStr = "Symmetric";
643 else if (calcType == CollateralExposureHelper::AsymmetricCVA)
644 calcTypeStr = "AsymmetricCVA";
645 else if (calcType == CollateralExposureHelper::AsymmetricDVA)
646 calcTypeStr = "AsymmetricDVA";
647 else if (calcType == CollateralExposureHelper::NoLag)
648 calcTypeStr = "NoLag";
649 else
650 QL_FAIL("Collateral calculation type not covered");
651 BOOST_TEST_MESSAGE("Calculation type: "<< calcTypeStr);
652
653 QuantLib::ext::shared_ptr<ExposureCalculator> exposureCalculator = QuantLib::ext::make_shared<ExposureCalculator>(portfolio, cube, cubeInterpreter,
654 initMarket, false, "EUR", "Market",
655 0.99, calcType, false, false);
656 exposureCalculator->build();
657 nettingSetDefaultValue = exposureCalculator->nettingSetDefaultValue();
658 nettingSetCloseOutValue = exposureCalculator->nettingSetCloseOutValue();
659 nettingSetMporPositiveFlow = exposureCalculator->nettingSetMporPositiveFlow();
660 nettingSetMporNegativeFlow = exposureCalculator->nettingSetMporNegativeFlow();
661 QuantLib::ext::shared_ptr<NettedExposureCalculator> nettedExposureCalculator =
662 QuantLib::ext::make_shared<NettedExposureCalculator>(
663 portfolio, initMarket, cube, "EUR", "Market", 0.99, calcType, false, nettingSetManager, collateralBalances,
664 nettingSetDefaultValue, nettingSetCloseOutValue, nettingSetMporPositiveFlow,
665 nettingSetMporNegativeFlow, *asd, cubeInterpreter, false, dimCalculator, false, false, 0.1,
666 exposureCalculator->exposureCube(), 0, 0, false, mporStickyDate, MporCashFlowMode::Unspecified);
667 nettedExposureCalculator->build();
668 nettingSetValue = (calcType == CollateralExposureHelper::CalculationType::NoLag
669 ? nettedExposureCalculator->nettingSetCloseOutValue()
670 : nettedExposureCalculator->nettingSetDefaultValue());
671 collateralBalance = nettedExposureCalculator->expectedCollateral(nettingSetId);
672 BOOST_TEST_MESSAGE("defaultDate, defaultValue, closeOutDate, collateralBalance");
673 auto key = make_tuple(dateGridStr, nettingSetMpor, closeOutGridStr, mporModeStr, calcTypeStr, compoundingStr);
674
675 vector<Date> cdd = cachedDefaultDates[key];
676 vector<Real> cdv = cachedDefaultValues[key];
677 vector<Date> ccd = cachedCloseOutDates[key];
678 vector<Real> ccv = cachedCloseOutValues[key];
679 BOOST_TEST_MESSAGE("cdd "<<cdd.size());
680 BOOST_TEST_MESSAGE("cdv "<<cdv.size());
681 BOOST_TEST_MESSAGE("ccd "<<ccd.size());
682 BOOST_TEST_MESSAGE("ccv "<<ccv.size());
683 Real tolerance = 1E-2;
684 for (auto n : nettingSetValue){
685 vector<vector<Real>> defaultValue = n.second;
686 for (Size j = 0; j < cube->dates().size(); j++){
687 BOOST_TEST_MESSAGE(io::iso_date(dateGrid->valuationDates()[j])<<", "<<defaultValue[j][0] <<", "<< collateralBalance[j+1]);
688
689 BOOST_CHECK_MESSAGE(dateGrid->valuationDates()[j] == cdd[j],
690 "default date "<< dateGrid->valuationDates()[j] << " does not match with cashed default date "<< cdd[j]);
691 BOOST_CHECK_MESSAGE(fabs(defaultValue[j][0] - cdv[j]) < tolerance,
692 "default value "<< defaultValue[j][0] << " does not match with cashed default value "<< cdv[j]);
693 BOOST_CHECK_MESSAGE(fabs(collateralBalance[j+1] - ccv[j]) < tolerance,
694 "collateral balance "<< collateralBalance[j+1] << " does not match with cashed collateral balance "<< ccv[j]);
695 }
696 }
697 }
698 }
699}
700
701BOOST_AUTO_TEST_SUITE_END()
702BOOST_AUTO_TEST_SUITE_END()
this class holds data associated to scenarios
void buildCube(const QuantLib::ext::shared_ptr< data::Portfolio > &portfolio, QuantLib::ext::shared_ptr< analytics::NPVCube > outputCube, std::vector< QuantLib::ext::shared_ptr< ValuationCalculator > > calculators, bool mporStickyDate=true, QuantLib::ext::shared_ptr< analytics::NPVCube > outputCubeNettingSet=nullptr, QuantLib::ext::shared_ptr< analytics::NPVCube > outputCptyCube=nullptr, std::vector< QuantLib::ext::shared_ptr< CounterpartyCalculator > > cptyCalculators={}, bool dryRun=false)
Build NPV cube.
OREAnalytics Top level fixture.
Simple flat market setup to be used in the test suite.
Definition: testmarket.hpp:64
const QuantLib::ext::shared_ptr< ModelCG > model_
Scenario generation using cross asset model paths.
QuantLib::ext::shared_ptr< Portfolio > buildPortfolio(Size portfolioSize, QuantLib::ext::shared_ptr< EngineFactory > &factory)
Definition: cube.cpp:264
load / save cubes and agg scen data from / to disk
Dynamic Initial Margin calculator base class.
Dynamic Initial Margin calculator by regression.
Exposure calculator.
A cube implementation that stores the cube in memory.
data
Time maturity
Date referenceDate
The cube valuation calculator interface.
QuantLib::ext::shared_ptr< AggregationScenarioData > loadAggregationScenarioData(const std::string &filename)
Definition: cube_io.cpp:234
void saveCube(const std::string &filename, const NPVCubeWithMetaData &cube, const bool doublePrecision)
Definition: cube_io.cpp:167
void saveAggregationScenarioData(const std::string &filename, const AggregationScenarioData &cube)
Definition: cube_io.cpp:302
CalibrationType
Size size(const ValueType &v)
Exposure calculator.
QuantLib::ext::shared_ptr< CrossAssetModel > buildCrossAssetModel(QuantLib::ext::shared_ptr< Market > &initMarket)
QuantLib::ext::shared_ptr< Portfolio > buildPortfolio(Size portfolioSize, QuantLib::ext::shared_ptr< EngineFactory > &factory)
BOOST_AUTO_TEST_CASE(NettedExposureCalculatorTest)
QuantLib::ext::shared_ptr< NPVCube > buildNPVCube(QuantLib::ext::shared_ptr< DateGrid > dateGrid, bool withCloseOutGrid, QuantLib::ext::shared_ptr< analytics::ScenarioSimMarket > &simMarket, QuantLib::ext::shared_ptr< Portfolio > &portfolio, bool mporStickyDate=false, Size samples=1, Size seed=5)
QuantLib::ext::shared_ptr< analytics::ScenarioSimMarket > buildScenarioSimMarket(QuantLib::ext::shared_ptr< DateGrid > dateGrid, QuantLib::ext::shared_ptr< Market > &initMarket, QuantLib::ext::shared_ptr< CrossAssetModel > &model, Size samples=1, Size seed=5, bool antithetic=false)
QuantLib::ext::shared_ptr< data::Conventions > convs()
The base NPV cube class.
Fixture that can be used at top level of OREAnalytics test suites.
Scenario generator configuration.
A Market class that can be updated by Scenarios.
A class to hold Scenario parameters for scenarioSimMarket.
factory classes for simple scenarios
The counterparty cube calculator interface.
The cube valuation core.