Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
observationmode.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 "testmarket.hpp"
20#include <boost/test/unit_test.hpp>
21#include <boost/timer/timer.hpp>
23#include <orea/cube/npvcube.hpp>
50#include <oret/toplevelfixture.hpp>
51#include <ql/math/randomnumbers/mt19937uniformrng.hpp>
52#include <ql/time/calendars/target.hpp>
53#include <ql/time/date.hpp>
54#include <ql/time/daycounters/actualactual.hpp>
57
58using namespace std;
59using namespace QuantLib;
60using namespace QuantExt;
61using namespace boost::unit_test_framework;
62using namespace ore;
63using namespace ore::data;
64using namespace ore::analytics;
65using boost::timer::cpu_timer;
66using boost::timer::default_places;
67
69
71 QuantLib::ext::shared_ptr<data::Conventions> conventions(new data::Conventions());
72
73 QuantLib::ext::shared_ptr<data::Convention> swapIndexConv(
74 new data::SwapIndexConvention("EUR-CMS-2Y", "EUR-6M-SWAP-CONVENTIONS"));
75 conventions->add(swapIndexConv);
76
77 QuantLib::ext::shared_ptr<data::Convention> swapConv(
78 new data::IRSwapConvention("EUR-6M-SWAP-CONVENTIONS", "TARGET", "Annual", "MF", "30/360", "EUR-EURIBOR-6M"));
79 conventions->add(swapConv);
80
81 InstrumentConventions::instance().setConventions(conventions);
82}
83
84QuantLib::ext::shared_ptr<Portfolio> buildPortfolio(QuantLib::ext::shared_ptr<EngineFactory>& factory) {
85
86 QuantLib::ext::shared_ptr<Portfolio> portfolio(new Portfolio());
87
88 string ccy = "EUR";
89 string index = "EUR-EURIBOR-6M";
90 string floatFreq = "6M";
91 Real fixedRate = 0.02;
92 string fixFreq = "1Y";
93 Size term = 10;
94 bool isPayer = true;
95
96 Date today = Settings::instance().evaluationDate();
97 Calendar cal = TARGET();
98 string calStr = "TARGET";
99 string conv = "MF";
100 string rule = "Forward";
101 Natural days = 2;
102 string fixDC = "30/360";
103 string floatDC = "ACT/360";
104
105 vector<double> notional(1, 1000000);
106 vector<double> spread(1, 0);
107
108 Date startDate = cal.adjust(today + 1 * Months);
109 Date endDate = cal.adjust(startDate + term * Years);
110
111 // date 2 string
112 ostringstream oss;
113 oss << io::iso_date(startDate);
114 string start(oss.str());
115 oss.str("");
116 oss.clear();
117 oss << io::iso_date(endDate);
118 string end(oss.str());
119
120 // envelope
121 Envelope env("CP");
122
123 // Schedules
124 ScheduleData floatSchedule(ScheduleRules(start, end, floatFreq, calStr, conv, conv, rule));
125 ScheduleData fixedSchedule(ScheduleRules(start, end, fixFreq, calStr, conv, conv, rule));
126
127 // fixed Leg - with dummy rate
128 LegData fixedLeg(QuantLib::ext::make_shared<FixedLegData>(vector<double>(1, fixedRate)), isPayer, ccy, fixedSchedule, fixDC,
129 notional);
130
131 // float Leg
132 vector<double> spreads(1, 0);
133 LegData floatingLeg(QuantLib::ext::make_shared<FloatingLegData>(index, days, false, spread), !isPayer, ccy, floatSchedule,
134 floatDC, notional);
135
136 QuantLib::ext::shared_ptr<Trade> swap(new data::Swap(env, floatingLeg, fixedLeg));
137
138 swap->id() = "SWAP";
139
140 portfolio->add(swap);
141
142 portfolio->build(factory);
143
144 return portfolio;
145}
146
147void simulation(string dateGridString, bool checkFixings) {
148 SavedSettings backup;
149
150 // Log::instance().registerLogger(QuantLib::ext::make_shared<StderrLogger>());
151 // Log::instance().switchOn();
152
153 Date today = Date(14, April, 2016); // Settings::instance().evaluationDate();
154 Settings::instance().evaluationDate() = today;
155
156 // string dateGridStr = "80,3M"; // 20 years
157 QuantLib::ext::shared_ptr<DateGrid> dg = QuantLib::ext::make_shared<DateGrid>(dateGridString);
158 Size samples = 100;
159
160 BOOST_TEST_MESSAGE("Date Grid : " << dateGridString);
161
162 // build model
163 string baseCcy = "EUR";
164 vector<string> ccys;
165 ccys.push_back(baseCcy);
166 ccys.push_back("GBP");
167 ccys.push_back("CHF");
168 ccys.push_back("USD");
169 ccys.push_back("JPY");
170
171 // Init market
172 QuantLib::ext::shared_ptr<Market> initMarket = QuantLib::ext::make_shared<TestMarket>(today);
173
174 // build scenario sim market parameters
175 QuantLib::ext::shared_ptr<analytics::ScenarioSimMarketParameters> parameters(new analytics::ScenarioSimMarketParameters());
176 parameters->baseCcy() = "EUR";
177 parameters->setDiscountCurveNames({"EUR", "GBP", "USD", "CHF", "JPY"});
178 parameters->setYieldCurveTenors("",
179 {1 * Months, 6 * Months, 1 * Years, 2 * Years, 5 * Years, 10 * Years, 20 * Years});
180 parameters->setIndices({"EUR-EURIBOR-6M", "USD-LIBOR-3M", "GBP-LIBOR-6M", "CHF-LIBOR-6M", "JPY-LIBOR-6M"});
181 parameters->interpolation() = "LogLinear";
182
183 parameters->setSwapVolTerms("", {6 * Months, 1 * Years});
184 parameters->setSwapVolExpiries("", {1 * Years, 2 * Years});
185 parameters->setSwapVolKeys(ccys);
186 parameters->swapVolDecayMode() = "ForwardVariance";
187 parameters->setSimulateSwapVols(false);
188
189 parameters->setFxVolExpiries("",
190 vector<Period>{1 * Months, 3 * Months, 6 * Months, 2 * Years, 3 * Years, 4 * Years, 5 * Years});
191 parameters->setFxVolDecayMode(string("ConstantVariance"));
192 parameters->setSimulateFXVols(false);
193
194 parameters->setFxVolCcyPairs({"USDEUR", "GBPEUR", "CHFEUR", "JPYEUR"});
195
196 parameters->setFxCcyPairs({"USDEUR", "GBPEUR", "CHFEUR", "JPYEUR"});
197
198 parameters->additionalScenarioDataIndices() = {"EUR-EURIBOR-6M", "USD-LIBOR-3M", "GBP-LIBOR-6M", "CHF-LIBOR-6M",
199 "JPY-LIBOR-6M"};
200 parameters->additionalScenarioDataCcys() = {"EUR", "GBP", "USD", "CHF", "JPY"};
201
202 // Config
203
204 // Build IR configurations
205 CalibrationType calibrationType = CalibrationType::Bootstrap;
206 LgmData::ReversionType revType = LgmData::ReversionType::HullWhite;
207 LgmData::VolatilityType volType = LgmData::VolatilityType::Hagan;
208 vector<string> swaptionExpiries = {"1Y", "2Y", "3Y", "5Y", "7Y", "10Y", "15Y", "20Y", "30Y"};
209 vector<string> swaptionTerms = {"5Y", "5Y", "5Y", "5Y", "5Y", "5Y", "5Y", "5Y", "5Y"};
210 vector<string> swaptionStrikes(swaptionExpiries.size(), "ATM");
211 vector<Time> hTimes = {};
212 vector<Time> aTimes = {};
213
214 std::vector<QuantLib::ext::shared_ptr<IrModelData>> irConfigs;
215
216 vector<Real> hValues = {0.02};
217 vector<Real> aValues = {0.008};
218 irConfigs.push_back(QuantLib::ext::make_shared<IrLgmData>(
219 "EUR", calibrationType, revType, volType, false, ParamType::Constant, hTimes, hValues, true,
220 ParamType::Piecewise, aTimes, aValues, 0.0, 1.0, swaptionExpiries, swaptionTerms, swaptionStrikes));
221
222 hValues = {0.03};
223 aValues = {0.009};
224 irConfigs.push_back(QuantLib::ext::make_shared<IrLgmData>(
225 "USD", calibrationType, revType, volType, false, ParamType::Constant, hTimes, hValues, true,
226 ParamType::Piecewise, aTimes, aValues, 0.0, 1.0, swaptionExpiries, swaptionTerms, swaptionStrikes));
227
228 hValues = {0.04};
229 aValues = {0.01};
230 irConfigs.push_back(QuantLib::ext::make_shared<IrLgmData>(
231 "GBP", calibrationType, revType, volType, false, ParamType::Constant, hTimes, hValues, true,
232 ParamType::Piecewise, aTimes, aValues, 0.0, 1.0, swaptionExpiries, swaptionTerms, swaptionStrikes));
233
234 hValues = {0.04};
235 aValues = {0.01};
236 irConfigs.push_back(QuantLib::ext::make_shared<IrLgmData>(
237 "CHF", calibrationType, revType, volType, false, ParamType::Constant, hTimes, hValues, true,
238 ParamType::Piecewise, aTimes, aValues, 0.0, 1.0, swaptionExpiries, swaptionTerms, swaptionStrikes));
239
240 hValues = {0.04};
241 aValues = {0.01};
242 irConfigs.push_back(QuantLib::ext::make_shared<IrLgmData>(
243 "JPY", calibrationType, revType, volType, false, ParamType::Constant, hTimes, hValues, true,
244 ParamType::Piecewise, aTimes, aValues, 0.0, 1.0, swaptionExpiries, swaptionTerms, swaptionStrikes));
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 vector<Real> sigmaValues = {0.15};
253 fxConfigs.push_back(QuantLib::ext::make_shared<FxBsData>("USD", "EUR", calibrationType, true, ParamType::Piecewise,
254 sigmaTimes, sigmaValues, optionExpiries, optionStrikes));
255
256 sigmaValues = {0.20};
257 fxConfigs.push_back(QuantLib::ext::make_shared<FxBsData>("GBP", "EUR", calibrationType, true, ParamType::Piecewise,
258 sigmaTimes, sigmaValues, optionExpiries, optionStrikes));
259
260 sigmaValues = {0.20};
261 fxConfigs.push_back(QuantLib::ext::make_shared<FxBsData>("CHF", "EUR", calibrationType, true, ParamType::Piecewise,
262 sigmaTimes, sigmaValues, optionExpiries, optionStrikes));
263
264 sigmaValues = {0.20};
265 fxConfigs.push_back(QuantLib::ext::make_shared<FxBsData>("JPY", "EUR", calibrationType, true, ParamType::Piecewise,
266 sigmaTimes, sigmaValues, optionExpiries, optionStrikes));
267
268 map<CorrelationKey, Handle<Quote>> corr;
269 CorrelationFactor f_1{CrossAssetModel::AssetType::IR, "EUR", 0};
270 CorrelationFactor f_2{CrossAssetModel::AssetType::IR, "USD", 0};
271 corr[make_pair(f_1, f_2)] = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.6));
272
273 QuantLib::ext::shared_ptr<CrossAssetModelData> config(QuantLib::ext::make_shared<CrossAssetModelData>(irConfigs, fxConfigs, corr));
274
275 // Model Builder & Model
276 // model builder
277 QuantLib::ext::shared_ptr<CrossAssetModelBuilder> modelBuilder(new CrossAssetModelBuilder(initMarket, config));
278 QuantLib::ext::shared_ptr<QuantExt::CrossAssetModel> model = *modelBuilder->model();
279 modelBuilder = NULL;
280
281 // Path generator
282 BigNatural seed = 5;
283 bool antithetic = false;
284 if (auto tmp = QuantLib::ext::dynamic_pointer_cast<CrossAssetStateProcess>(model->stateProcess())) {
285 tmp->resetCache(dg->timeGrid().size() - 1);
286 }
287 QuantLib::ext::shared_ptr<QuantExt::MultiPathGeneratorBase> pathGen =
288 QuantLib::ext::make_shared<MultiPathGeneratorMersenneTwister>(model->stateProcess(), dg->timeGrid(), seed, antithetic);
289
290 // build scenario sim market
291 QuantLib::ext::shared_ptr<analytics::ScenarioSimMarket> simMarket =
292 QuantLib::ext::make_shared<analytics::ScenarioSimMarket>(initMarket, parameters);
293
294 // build scenario generator
295 QuantLib::ext::shared_ptr<ScenarioFactory> scenarioFactory =
296 QuantLib::ext::make_shared<SimpleScenarioFactory>(true);
297 QuantLib::ext::shared_ptr<ScenarioGenerator> scenarioGenerator = QuantLib::ext::make_shared<CrossAssetModelScenarioGenerator>(
298 model, pathGen, scenarioFactory, parameters, today, dg, initMarket);
299 simMarket->scenarioGenerator() = scenarioGenerator;
300
301 // Build Portfolio
302 QuantLib::ext::shared_ptr<EngineData> data = QuantLib::ext::make_shared<EngineData>();
303 data->model("Swap") = "DiscountedCashflows";
304 data->engine("Swap") = "DiscountingSwapEngine";
305 QuantLib::ext::shared_ptr<EngineFactory> factory = QuantLib::ext::make_shared<EngineFactory>(data, simMarket);
306
307 QuantLib::ext::shared_ptr<Portfolio> portfolio = buildPortfolio(factory);
308
309 // Storage for selected scenario data (index fixings, FX rates, ..)
310 if (checkFixings) {
311 QuantLib::ext::shared_ptr<InMemoryAggregationScenarioData> inMemoryScenarioData =
312 QuantLib::ext::make_shared<InMemoryAggregationScenarioData>(dg->size(), samples);
313 // Set AggregationScenarioData
314 simMarket->aggregationScenarioData() = inMemoryScenarioData;
315 }
316
317 // Now calculate exposure
318 ValuationEngine valEngine(today, dg, simMarket);
319
320 // Calculate Cube
321 cpu_timer t;
322 QuantLib::ext::shared_ptr<NPVCube> cube =
323 QuantLib::ext::make_shared<DoublePrecisionInMemoryCube>(today, portfolio->ids(), dg->dates(), samples);
324 vector<QuantLib::ext::shared_ptr<ValuationCalculator>> calculators;
325 calculators.push_back(QuantLib::ext::make_shared<NPVCalculator>(baseCcy));
326 valEngine.buildCube(portfolio, cube, calculators);
327 t.stop();
328
329 BOOST_TEST_MESSAGE("Cube generated in " << t.format(default_places, "%w") << " seconds");
330
331 map<string, vector<Real>> referenceFixings;
332 // First 10 EUR-EURIBOR-6M fixings at dateIndex 5, date grid 11,1Y
333 referenceFixings["11,1Y"] = {0.00739033, 0.0281673, 0.0344399, 0.03362, 0.0325276, 0.030573,
334 0.00895957, 0.0165584, 0.0194418, 0.0112834, 0.0239227};
335
336 // First 10 EUR-EURIBOR-6M fixings at dateIndex 5, date grid 10,1Y
337 referenceFixings["10,1Y"] = {0.00739033, 0.0296998, 0.0339535, 0.012449, 0.0134939, 0.0148095,
338 0.0188541, 0.0277254, 0.035063, 0.0105228, 0.0103237};
339
340 if (simMarket->aggregationScenarioData()) {
341 QL_REQUIRE(dateGridString == "10,1Y" || dateGridString == "11,1Y",
342 "date grid string " << dateGridString << " unexpected");
343 // Reference scenario data:
344 Size dateIndex = 5;
345 Size maxSample = 10;
346 string qualifier = "EUR-EURIBOR-6M";
347 Real tolerance = 1.0e-6;
348 for (Size sampleIndex = 0; sampleIndex <= maxSample; ++sampleIndex) {
349 Real fix = simMarket->aggregationScenarioData()->get(dateIndex, sampleIndex,
350 AggregationScenarioDataType::IndexFixing, qualifier);
351 Real ref = referenceFixings[dateGridString][sampleIndex];
352 if (fabs(fix - ref) > tolerance)
353 BOOST_FAIL("Stored fixing differs from reference value, found " << fix << ", expected " << ref);
354 }
355 }
356}
357
358BOOST_FIXTURE_TEST_SUITE(OREAnalyticsTestSuite, ore::test::OreaTopLevelFixture)
359
360BOOST_AUTO_TEST_SUITE(ObservationModeTest)
361
362BOOST_AUTO_TEST_CASE(testDisableShort) {
363 ObservationMode::instance().setMode(ObservationMode::Mode::Disable);
365
366 BOOST_TEST_MESSAGE("Testing Observation Mode Disable, Short Grid, No Fixing Checks");
367 simulation("10,1Y", false);
368
369 BOOST_TEST_MESSAGE("Testing Observation Mode Disable, Short Grid, With Fixing Checks");
370 simulation("10,1Y", true);
371}
372
373BOOST_AUTO_TEST_CASE(testDisableLong) {
374 ObservationMode::instance().setMode(ObservationMode::Mode::Disable);
376
377 BOOST_TEST_MESSAGE("Testing Observation Mode Disable, Long Grid, No Fixing Checks");
378 simulation("11,1Y", false);
379
380 BOOST_TEST_MESSAGE("Testing Observation Mode Disable, Long Grid, With Fixing Checks");
381 simulation("11,1Y", true);
382}
383
385 ObservationMode::instance().setMode(ObservationMode::Mode::None);
387
388 BOOST_TEST_MESSAGE("Testing Observation Mode None, Short Grid, No Fixing Checks");
389 simulation("10,1Y", false);
390
391 BOOST_TEST_MESSAGE("Testing Observation Mode None, Short Grid, With Fixing Checks");
392 simulation("10,1Y", true);
393
394 BOOST_TEST_MESSAGE("Testing Observation Mode None, Long Grid, No Fixing Checks");
395 simulation("11,1Y", false);
396
397 BOOST_TEST_MESSAGE("Testing Observation Mode None, Long Grid, With Fixing Checks");
398 simulation("11,1Y", true);
399}
400
401BOOST_AUTO_TEST_CASE(testUnregister) {
402 ObservationMode::instance().setMode(ObservationMode::Mode::Unregister);
404
405 BOOST_TEST_MESSAGE("Testing Observation Mode Unregister, Long Grid, No Fixing Checks");
406 simulation("11,1Y", false);
407
408 BOOST_TEST_MESSAGE("Testing Observation Mode Unregister, Long Grid, With Fixing Checks");
409 simulation("11,1Y", true);
410
411 BOOST_TEST_MESSAGE("Testing Observation Mode Unregister, Short Grid, No Fixing Checks");
412 simulation("10,1Y", false);
413
414 BOOST_TEST_MESSAGE("Testing Observation Mode Unregister, Short Grid, With Fixing Checks");
415 simulation("10,1Y", true);
416}
417
419 ObservationMode::instance().setMode(ObservationMode::Mode::Defer);
421
422 BOOST_TEST_MESSAGE("Testing Observation Mode Defer, Long Grid, No Fixing Checks");
423 simulation("11,1Y", false);
424
425 BOOST_TEST_MESSAGE("Testing Observation Mode Defer, Long Grid, With Fixing Checks");
426 simulation("11,1Y", true);
427
428 BOOST_TEST_MESSAGE("Testing Observation Mode Defer, Short Grid, No Fixing Checks");
429 simulation("10,1Y", false);
430
431 BOOST_TEST_MESSAGE("Testing Observation Mode Defer, Short Grid, With Fixing Checks");
432 simulation("10,1Y", true);
433}
434
435BOOST_AUTO_TEST_SUITE_END()
436
437BOOST_AUTO_TEST_SUITE_END()
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
Scenario generation using cross asset model paths.
Class that wraps a sensitivity stream and filters out negligible records.
A cube implementation that stores the cube in memory.
data
CalibrationType
The base NPV cube class.
BOOST_AUTO_TEST_CASE(testDisableShort)
void simulation(string dateGridString, bool checkFixings)
QuantLib::ext::shared_ptr< Portfolio > buildPortfolio(QuantLib::ext::shared_ptr< EngineFactory > &factory)
void setConventions()
Singleton class to hold global Observation Mode.
Fixture that can be used at top level of OREAnalytics test suites.
Perform parametric var calculation for a given portfolio.
risk class and type filter
A Market class that can be updated by Scenarios.
A class to hold Scenario parameters for scenarioSimMarket.
Class for aggregating SensitivityRecords.
Perform sensitivity analysis for a given portfolio.
Class for streaming SensitivityRecords from a SensitivityCube.
Class for streaming SensitivityRecords from file.
Class for streaming SensitivityRecords from in-memory container.
Struct for holding a sensitivity record.
Base class for sensitivity record streamer.
factory classes for simple scenarios
perform a stress testing analysis for a given portfolio.
The counterparty cube calculator interface.
The cube valuation core.