204 {
205
206 BOOST_TEST_MESSAGE("Testing market simulation, measure " << measureName << ", horizon " << shiftHorizon
207 << ", discretization " << discName);
208
209 TestData d(measureName, shiftHorizon);
210
211
212 Date today = d.referenceDate;
213 std::vector<Period> tenorGrid;
214 if (discName == "exact")
215 tenorGrid = {1 * Years, 2 * Years, 3 * Years, 5 * Years, 7 * Years, 10 * Years};
216 else {
217 for (Size i = 1; i <= 60; ++i)
218 tenorGrid.push_back(i * 2 * Months);
219 }
220 QuantLib::ext::shared_ptr<DateGrid> grid = QuantLib::ext::make_shared<DateGrid>(tenorGrid);
221
222
223 QuantLib::ext::shared_ptr<QuantExt::CrossAssetModel> model = discName == "exact" ? d.ccLgmExact : d.ccLgmEuler;
224
225
227 simMarketConfig->setYieldCurveTenors("", {3 * Months, 6 * Months, 1 * Years, 2 * Years, 3 * Years, 4 * Years,
228 5 * Years, 7 * Years, 10 * Years, 12 * Years});
229 simMarketConfig->setSimulateFXVols(false);
230 simMarketConfig->setSimulateEquityVols(false);
231
232 simMarketConfig->baseCcy() = "EUR";
233 simMarketConfig->setDiscountCurveNames({"EUR", "USD", "GBP"});
234 simMarketConfig->setIndices({"EUR-EURIBOR-6M", "USD-LIBOR-3M", "GBP-LIBOR-6M"});
235 simMarketConfig->interpolation() = "LogLinear";
236 simMarketConfig->setSwapVolExpiries("", {6 * Months, 1 * Years, 2 * Years, 3 * Years, 5 * Years, 10 * Years});
237 simMarketConfig->setSwapVolTerms("", {1 * Years, 2 * Years, 3 * Years, 5 * Years, 7 * Years, 10 * Years});
238 simMarketConfig->setFxCcyPairs({"USDEUR", "GBPEUR"});
239
241 sgd->sequenceType() =
Sobol;
242 sgd->seed() = 42;
243 sgd->setGrid(grid);
244
246 QuantLib::ext::shared_ptr<ScenarioFactory> sf = QuantLib::ext::make_shared<SimpleScenarioFactory>(true);
247 QuantLib::ext::shared_ptr<ScenarioGenerator> sg = sgb.build(model, sf, simMarketConfig, today, d.market);
248
250 auto simMarket = QuantLib::ext::make_shared<ScenarioSimMarket>(d.market, simMarketConfig);
251 simMarket->scenarioGenerator() = sg;
252
253
254 Size samples = 5000;
255 Real eur = 0.0, usd = 0.0, gbp = 0.0, eur2 = 0.0, usd2 = 0.0, gbp2 = 0.0;
256 Real eur3 = 0.0, usd3 = 0.0, gbp3 = 0.0;
257 int horizon = 10;
258
259 Date d1 = grid->dates().back();
260 Date d2 = d1 + horizon * Years;
261 Real relTolerance = 0.01;
262 Real eurExpected = d.market->discountCurve("EUR")->discount(d2);
263 Real eurExpected2 = d.market->discountCurve("EUR")->discount(d1);
264 Real gbpExpected = d.market->fxRate("GBPEUR")->value() * d.market->discountCurve("GBP")->discount(d2);
265 Real gbpExpected2 = d.market->fxRate("GBPEUR")->value() * d.market->discountCurve("GBP")->discount(d1);
266 Real usdExpected = d.market->fxRate("USDEUR")->value() * d.market->discountCurve("USD")->discount(d2);
267 Real usdExpected2 = d.market->fxRate("USDEUR")->value() * d.market->discountCurve("USD")->discount(d1);
268
269 cpu_timer timer, timer2;
270 BOOST_TEST_MESSAGE("running " << samples << " samples simulation over " << grid->dates().size() << " time steps");
271 for (Size i = 0; i < samples; i++) {
272 for (Date d : grid->dates()) {
273 timer.resume();
274 simMarket->update(d);
275 timer.stop();
276 if (d == grid->dates().back()) {
277 Real numeraire = simMarket->numeraire();
278 Real usdeurFX = simMarket->fxRate("USDEUR")->value();
279 Real gbpeurFX = simMarket->fxRate("GBPEUR")->value();
280 Real eurDiscount = simMarket->discountCurve("EUR")->discount(1.0 * horizon);
281 Real gbpDiscount = simMarket->discountCurve("GBP")->discount(1.0 * horizon);
282 ;
283 Real usdDiscount = simMarket->discountCurve("USD")->discount(1.0 * horizon);
284 ;
285 Real eurIndex =
286 simMarket->iborIndex("EUR-EURIBOR-6M")->forwardingTermStructure()->discount(1.0 * horizon);
287 Real gbpIndex =
288 simMarket->iborIndex("GBP-LIBOR-6M")->forwardingTermStructure()->discount(1.0 * horizon);
289 ;
290 Real usdIndex =
291 simMarket->iborIndex("USD-LIBOR-3M")->forwardingTermStructure()->discount(1.0 * horizon);
292 ;
293 eur += eurDiscount / numeraire;
294 gbp += gbpDiscount * gbpeurFX / numeraire;
295 usd += usdDiscount * usdeurFX / numeraire;
296 eur2 += 1.0 / numeraire;
297 gbp2 += gbpeurFX / numeraire;
298 usd2 += usdeurFX / numeraire;
299 eur3 += eurIndex / numeraire;
300 gbp3 += gbpIndex * gbpeurFX / numeraire;
301 usd3 += usdIndex * usdeurFX / numeraire;
302 }
303 }
304 }
305
306 timer2.stop();
307
308 eur /= samples;
309 gbp /= samples;
310 usd /= samples;
311 eur2 /= samples;
312 gbp2 /= samples;
313 usd2 /= samples;
314 eur3 /= samples;
315 gbp3 /= samples;
316 usd3 /= samples;
317
318 Real eurDiff = fabs(eur - eurExpected) / eurExpected;
319 BOOST_CHECK_MESSAGE(eurDiff < relTolerance, "EUR 20Y Discount mismatch: " << eur << " vs " << eurExpected);
320
321 Real gbpDiff = fabs(gbp - gbpExpected) / gbpExpected;
322 BOOST_CHECK_MESSAGE(gbpDiff < relTolerance,
323 "GBP 20Y Discount mismatch: " << gbp << " vs " << gbpExpected << " (" << gbpDiff << ")");
324
325 Real usdDiff = fabs(usd - usdExpected) / usdExpected;
326 BOOST_CHECK_MESSAGE(usdDiff < relTolerance, "USD 20Y Discount mismatch: " << usd << " vs " << usdExpected);
327
328 Real eur3Diff = fabs(eur3 - eurExpected) / eurExpected;
329 BOOST_CHECK_MESSAGE(eur3Diff < relTolerance, "EUR 20Y Index Discount mismatch: " << eur3 << " vs " << eurExpected);
330
331 Real gbp3Diff = fabs(gbp3 - gbpExpected) / gbpExpected;
332 BOOST_CHECK_MESSAGE(gbp3Diff < relTolerance, "GBP 20Y Index Discount mismatch: " << gbp3 << " vs " << gbpExpected);
333
334 Real usd3Diff = fabs(usd3 - usdExpected) / usdExpected;
335 BOOST_CHECK_MESSAGE(usd3Diff < relTolerance, "USD 20Y Index Discount mismatch: " << usd3 << " vs " << usdExpected);
336
337 Real eur2Diff = fabs(eur2 - eurExpected2) / eurExpected2;
338 BOOST_CHECK_MESSAGE(eur2Diff < relTolerance, "EUR 10Y Discount mismatch: " << eur2 << " vs " << eurExpected2);
339
340 Real gbp2Diff = fabs(gbp2 - gbpExpected2) / gbpExpected2;
341 BOOST_CHECK_MESSAGE(gbp2Diff < relTolerance, "GBP 10Y Discount mismatch: " << gbp2 << " vs " << gbpExpected2);
342
343 Real usd2Diff = fabs(usd2 - usdExpected2) / usdExpected2;
344 BOOST_CHECK_MESSAGE(usd2Diff < relTolerance, "USD 10Y Discount mismatch: " << usd2 << " vs " << usdExpected2);
345
346 BOOST_TEST_MESSAGE("CrossAssetModel via ScenarioSimMarket");
347 BOOST_TEST_MESSAGE("EUR " << QuantLib::io::iso_date(d2) << " Discount: " << eur << " vs " << eurExpected
348 << " (" << eurDiff << ")");
349 BOOST_TEST_MESSAGE("GBP " << QuantLib::io::iso_date(d2) << " Discount in EUR: " << gbp << " vs " << gbpExpected
350 << " (" << gbpDiff << ")");
351 BOOST_TEST_MESSAGE("USD " << QuantLib::io::iso_date(d2) << " Discount in EUR: " << usd << " vs " << usdExpected
352 << " (" << usdDiff << ")");
353 BOOST_TEST_MESSAGE("EUR " << QuantLib::io::iso_date(d1) << " Discount: " << eur2 << " vs " << eurExpected2
354 << " (" << eur2Diff << ")");
355 BOOST_TEST_MESSAGE("GBP " << QuantLib::io::iso_date(d1) << " Discount in EUR: " << gbp2 << " vs " << gbpExpected2
356 << " (" << gbp2Diff << ")");
357 BOOST_TEST_MESSAGE("USD " << QuantLib::io::iso_date(d1) << " Discount in EUR: " << usd2 << " vs " << usdExpected2
358 << " (" << usd2Diff << ")");
359 BOOST_TEST_MESSAGE("Simulation time " << timer.format(default_places, "%w") << ", total "
360 << timer2.format(default_places, "%w"));
361}
Build a ScenarioGenerator.
Scenario Generator description.
ScenarioSimMarket description.
QuantLib::ext::shared_ptr< data::Conventions > convs()