2288 {
2289
2290 BOOST_TEST_MESSAGE("Testing martingale property in ir-fx-inf-cr-com model for Euler and exact discretizations...");
2291 BOOST_TEST_MESSAGE("EUR inflation model is: " << (infEurIsDk ? "DK" : "JY"));
2292 BOOST_TEST_MESSAGE("GBP inflation model is: " << (infGbpIsDk ? "DK" : "JY"));
2293 BOOST_TEST_MESSAGE("Using " << (flatVols ? "" : "non-") << "flat volatilities.");
2294
2295 IrFxInfCrComModelTestData d(infEurIsDk, infGbpIsDk, flatVols,
driftFreeState);
2296
2297 BOOST_TEST_MESSAGE("get exact state process");
2298 QuantLib::ext::shared_ptr<StochasticProcess> process1 = d.modelExact->stateProcess();
2299 BOOST_TEST_MESSAGE("get Euler state process");
2300 QuantLib::ext::shared_ptr<StochasticProcess> process2 = d.modelEuler->stateProcess();
2301
2302 Size n = 5000;
2303 Size seed = 18;
2304 Time T = 2.0;
2305 Time T2 = 20.0;
2306 Size
steps =
static_cast<Size
>(T * 40);
2307
2308 BOOST_TEST_MESSAGE("build sequence generators");
2309 LowDiscrepancy::rsg_type sg1 = LowDiscrepancy::make_sequence_generator(process1->factors() * 1, seed);
2310 LowDiscrepancy::rsg_type sg2 = LowDiscrepancy::make_sequence_generator(process2->factors() *
steps, seed);
2311
2312 BOOST_TEST_MESSAGE("build multi path generator");
2313 TimeGrid grid1(T, 1);
2314 if (auto tmp = QuantLib::ext::dynamic_pointer_cast<CrossAssetStateProcess>(process1)) {
2315 tmp->resetCache(grid1.size() - 1);
2316 }
2317 MultiPathGenerator<LowDiscrepancy::rsg_type> pg1(process1, grid1, sg1, false);
2318 TimeGrid grid2(T,
steps);
2319 if (auto tmp = QuantLib::ext::dynamic_pointer_cast<CrossAssetStateProcess>(process2)) {
2320 tmp->resetCache(grid2.size() - 1);
2321 }
2322 MultiPathGenerator<LowDiscrepancy::rsg_type> pg2(process2, grid2, sg2, false);
2323
2324 accumulator_set<double, stats<tag::mean, tag::error_of<tag::mean> > > eurzb1, usdzb1, gbpzb1, infeur1, infgbp1,
2325 n1eur1, commodityA_1, commodityB_1;
2326 accumulator_set<double, stats<tag::mean, tag::error_of<tag::mean> > > eurzb2, usdzb2, gbpzb2, infeur2, infgbp2,
2327 n1eur2, commodityA_2, commodityB_2;
2328
2329 for (Size j = 0; j < n; ++j) {
2330 Sample<MultiPath> path2 = pg2.next();
2331 Size l2 = path2.value[0].length() - 1;
2332 Sample<MultiPath> path1 = pg1.next();
2333 Size l1 = path1.value[0].length() - 1;
2334 Real zeur1 = path1.value[0][l1];
2335 Real zusd1 = path1.value[1][l1];
2336 Real zgbp1 = path1.value[2][l1];
2337 Real fxusd1 = std::exp(path1.value[3][l1]);
2338 Real fxgbp1 = std::exp(path1.value[4][l1]);
2339 Real infeurz1 = path1.value[5][l1];
2340 Real infeury1 = path1.value[6][l1];
2341 Real infgbpz1 = path1.value[7][l1];
2342 Real infgbpy1 = path1.value[8][l1];
2343 Real crzn11 = path1.value[9][l1];
2344 Real cryn11 = path1.value[10][l1];
2345 Real coma1 = path1.value[11][l1];
2346 Real comb1 = path1.value[12][l1];
2347 Real zeur2 = path2.value[0][l2];
2348 Real zusd2 = path2.value[1][l2];
2349 Real zgbp2 = path2.value[2][l2];
2350 Real fxusd2 = std::exp(path2.value[3][l2]);
2351 Real fxgbp2 = std::exp(path2.value[4][l2]);
2352 Real infeurz2 = path2.value[5][l2];
2353 Real infeury2 = path2.value[6][l2];
2354 Real infgbpz2 = path2.value[7][l2];
2355 Real infgbpy2 = path2.value[8][l2];
2356 Real crzn12 = path2.value[9][l2];
2357 Real cryn12 = path2.value[10][l2];
2358 Real coma2 = path2.value[11][l2];
2359 Real comb2 = path2.value[12][l2];
2360
2361 eurzb1(d.modelExact->discountBond(0, T, T2, zeur1) / d.modelExact->numeraire(0, T, zeur1));
2362
2363 usdzb1(d.modelExact->discountBond(1, T, T2, zusd1) * fxusd1 / d.modelExact->numeraire(0, T, zeur1));
2364
2365 gbpzb1(d.modelExact->discountBond(2, T, T2, zgbp1) * fxgbp1 / d.modelExact->numeraire(0, T, zeur1));
2366
2367 bool indexIsInterpolated = true;
2368 if (infEurIsDk) {
2369 std::pair<Real, Real> sinfeur1 = d.modelExact->infdkI(0, T, T2, infeurz1, infeury1);
2370 infeur1(sinfeur1.first * sinfeur1.second * d.modelExact->discountBond(0, T, T2, zeur1) /
2371 d.modelExact->numeraire(0, T, zeur1));
2372 } else {
2373 infeur1(
exp(infeury1) *
inflationGrowth(d.modelExact, 0, T, T2, zeur1, infeurz1, indexIsInterpolated) *
2374 d.modelExact->discountBond(0, T, T2, zeur1) / d.modelExact->numeraire(0, T, zeur1));
2375 }
2376
2377 if (infGbpIsDk) {
2378 std::pair<Real, Real> sinfgbp1 = d.modelExact->infdkI(1, T, T2, infgbpz1, infgbpy1);
2379 infgbp1(sinfgbp1.first * sinfgbp1.second * d.modelExact->discountBond(2, T, T2, zgbp1) * fxgbp1 /
2380 d.modelExact->numeraire(0, T, zeur1));
2381 } else {
2382 infgbp1(
exp(infgbpy1) *
inflationGrowth(d.modelExact, 1, T, T2, zgbp1, infgbpz1, indexIsInterpolated) *
2383 d.modelExact->discountBond(2, T, T2, zgbp1) * fxgbp1 / d.modelExact->numeraire(0, T, zeur1));
2384 }
2385
2386 std::pair<Real, Real> sn11 = d.modelExact->crlgm1fS(0, 0, T, T2, crzn11, cryn11);
2387 n1eur1(sn11.first * sn11.second * d.modelExact->discountBond(0, T, T2, zeur1) / d.modelExact->numeraire(0, T, zeur1));
2388
2389
2390 eurzb2(d.modelExact->discountBond(0, T, T2, zeur2) / d.modelExact->numeraire(0, T, zeur2));
2391
2392 usdzb2(d.modelExact->discountBond(1, T, T2, zusd2) * fxusd2 / d.modelExact->numeraire(0, T, zeur2));
2393
2394 gbpzb2(d.modelExact->discountBond(2, T, T2, zgbp2) * fxgbp2 / d.modelExact->numeraire(0, T, zeur2));
2395
2396 if (infEurIsDk) {
2397 std::pair<Real, Real> sinfeur2 = d.modelExact->infdkI(0, T, T2, infeurz2, infeury2);
2398 infeur2(sinfeur2.first * sinfeur2.second * d.modelExact->discountBond(0, T, T2, zeur2) /
2399 d.modelExact->numeraire(0, T, zeur2));
2400 } else {
2401 infeur2(
exp(infeury2) *
inflationGrowth(d.modelExact, 0, T, T2, zeur2, infeurz2, indexIsInterpolated) *
2402 d.modelExact->discountBond(0, T, T2, zeur2) / d.modelExact->numeraire(0, T, zeur2));
2403 }
2404
2405 if (infGbpIsDk) {
2406 std::pair<Real, Real> sinfgbp2 = d.modelExact->infdkI(1, T, T2, infgbpz2, infgbpy2);
2407 infgbp2(sinfgbp2.first * sinfgbp2.second * d.modelExact->discountBond(2, T, T2, zgbp2) * fxgbp2 /
2408 d.modelExact->numeraire(0, T, zeur2));
2409 } else {
2410 infgbp2(
exp(infgbpy2) *
inflationGrowth(d.modelExact, 1, T, T2, zgbp2, infgbpz2, indexIsInterpolated) *
2411 d.modelExact->discountBond(2, T, T2, zgbp2) * fxgbp2 / d.modelExact->numeraire(0, T, zeur2));
2412 }
2413
2414 std::pair<Real, Real> sn12 = d.modelExact->crlgm1fS(0, 0, T, T2, crzn12, cryn12);
2415 n1eur2(sn12.first * sn12.second * d.modelExact->discountBond(0, T, T2, zeur2) / d.modelExact->numeraire(0, T, zeur2));
2416
2417 commodityA_1(d.comModelA->forwardPrice(T, T2, Array(1, coma1)));
2418 commodityB_1(d.comModelB->forwardPrice(T, T2, Array(1, comb1)));
2419 commodityA_2(d.comModelA->forwardPrice(T, T2, Array(1, coma2)));
2420 commodityB_2(d.comModelB->forwardPrice(T, T2, Array(1, comb2)));
2421 }
2422
2423 BOOST_TEST_MESSAGE("EXACT:");
2424 BOOST_TEST_MESSAGE("EUR zb = " << mean(eurzb1) << " +- " << error_of<tag::mean>(eurzb1) << " vs analytical "
2425 << d.eurYts->discount(T2));
2426 BOOST_TEST_MESSAGE("USD zb = " << mean(usdzb1) << " +- " << error_of<tag::mean>(usdzb1) << " vs analytical "
2427 << d.usdYts->discount(T2) * d.fxEurUsd->value());
2428 BOOST_TEST_MESSAGE("GBP zb = " << mean(gbpzb1) << " +- " << error_of<tag::mean>(gbpzb1) << " vs analytical "
2429 << d.gbpYts->discount(T2) * d.fxEurGbp->value());
2430 BOOST_TEST_MESSAGE("IDX zb EUR = " << mean(infeur1) << " +- " << error_of<tag::mean>(infeur1) << " vs analytical "
2431 << d.eurYts->discount(T2) *
2432 std::pow(1.0 + d.infEurTs->zeroRate(T2 - d.infLag), T2));
2433 BOOST_TEST_MESSAGE("IDX zb GBP = " << mean(infgbp1) << " +- " << error_of<tag::mean>(infgbp1) << " vs analytical "
2434 << d.gbpYts->discount(T2) *
2435 std::pow(1.0 + d.infGbpTs->zeroRate(T2 - d.infLag), T2) *
2436 d.fxEurGbp->value());
2437 BOOST_TEST_MESSAGE("N1 zb EUR = " << mean(n1eur1) << " +- " << error_of<tag::mean>(n1eur1) << " vs analytical "
2438 << d.eurYts->discount(T2) * d.n1Ts->survivalProbability(T2));
2439
2440 BOOST_TEST_MESSAGE("\nEULER:");
2441 BOOST_TEST_MESSAGE("EUR zb = " << mean(eurzb2) << " +- " << error_of<tag::mean>(eurzb2) << " vs analytical "
2442 << d.eurYts->discount(T2));
2443 BOOST_TEST_MESSAGE("USD zb = " << mean(usdzb2) << " +- " << error_of<tag::mean>(usdzb2) << " vs analytical "
2444 << d.usdYts->discount(T2) * d.fxEurUsd->value());
2445 BOOST_TEST_MESSAGE("GBP zb = " << mean(gbpzb2) << " +- " << error_of<tag::mean>(gbpzb2) << " vs analytical "
2446 << d.gbpYts->discount(T2) * d.fxEurGbp->value());
2447 BOOST_TEST_MESSAGE("IDX zb EUR = " << mean(infeur2) << " +- " << error_of<tag::mean>(infeur2) << " vs analytical "
2448 << d.eurYts->discount(T2) *
2449 std::pow(1.0 + d.infEurTs->zeroRate(T2 - d.infLag), T2));
2450 BOOST_TEST_MESSAGE("IDX zb GBP = " << mean(infgbp2) << " +- " << error_of<tag::mean>(infgbp2) << " vs analytical "
2451 << d.gbpYts->discount(T2) *
2452 std::pow(1.0 + d.infGbpTs->zeroRate(T2 - d.infLag), T2) *
2453 d.fxEurGbp->value());
2454 BOOST_TEST_MESSAGE("N1 zb EUR = " << mean(n1eur2) << " +- " << error_of<tag::mean>(n1eur2) << " vs analytical "
2455 << d.eurYts->discount(T2) * d.n1Ts->survivalProbability(T2));
2456
2457
2458
2459 Real tol1 = 5.0E-4;
2460 Real tol2 = 14.0E-4;
2461
2462 Real ev = d.eurYts->discount(T2);
2463 if (std::abs(mean(eurzb1) - ev) > tol1)
2464 BOOST_TEST_ERROR("Martingale test failed for eurzb (exact discr.),"
2465 "expected "
2466 << ev << ", got " << mean(eurzb1) << ", tolerance " << tol1);
2467 ev = d.usdYts->discount(T2) * d.fxEurUsd->value();
2468 if (std::abs(mean(usdzb1) - ev) > tol1)
2469 BOOST_TEST_ERROR("Martingale test failed for eurzb (exact discr.),"
2470 "expected "
2471 << ev << ", got " << mean(usdzb1) << ", tolerance " << tol1);
2472 ev = d.gbpYts->discount(T2) * d.fxEurGbp->value();
2473 if (std::abs(mean(gbpzb1) - ev) > tol1)
2474 BOOST_TEST_ERROR("Martingale test failed for eurzb (exact discr.),"
2475 "expected "
2476 << ev << ", got " << mean(gbpzb1) << ", tolerance " << tol1);
2477 ev = d.eurYts->discount(T2) * std::pow(1.0 + d.infEurTs->zeroRate(T2 - d.infLag), T2);
2478 if (std::abs(mean(infeur1) - ev) > tol1)
2479 BOOST_TEST_ERROR("Martingale test failed for idx eurzb (exact discr.),"
2480 "expected "
2481 << ev << ", got " << mean(infeur1) << ", tolerance " << tol1);
2482 ev = d.gbpYts->discount(T2) * std::pow(1.0 + d.infGbpTs->zeroRate(T2 - d.infLag), T2) * d.fxEurGbp->value();
2483 if (std::abs(mean(infgbp1) - ev) > tol1)
2484 BOOST_TEST_ERROR("Martingale test failed for idx gbpzb (exact discr.),"
2485 "expected "
2486 << ev << ", got " << mean(infgbp1) << ", tolerance " << tol1);
2487 ev = d.eurYts->discount(T2) * d.n1Ts->survivalProbability(T2);
2488 if (std::abs(mean(n1eur1) - ev) > tol1)
2489 BOOST_TEST_ERROR("Martingale test failed for def eurzb (exact discr.),"
2490 "expected "
2491 << ev << ", got " << mean(n1eur1) << ", tolerance " << tol1);
2492
2493 ev = d.eurYts->discount(T2);
2494 if (std::abs(mean(eurzb2) - ev) > tol2)
2495 BOOST_TEST_ERROR("Martingale test failed for eurzb (Euler discr.),"
2496 "expected "
2497 << ev << ", got " << mean(eurzb2) << ", tolerance " << tol2);
2498 ev = d.usdYts->discount(T2) * d.fxEurUsd->value();
2499 if (std::abs(mean(usdzb2) - ev) > tol2)
2500 BOOST_TEST_ERROR("Martingale test failed for usdzb (Euler discr.),"
2501 "expected "
2502 << ev << ", got " << mean(usdzb2) << ", tolerance " << tol2 * error_of<tag::mean>(usdzb2));
2503 ev = d.gbpYts->discount(T2) * d.fxEurGbp->value();
2504 if (std::abs(mean(gbpzb2) - ev) > tol2)
2505 BOOST_TEST_ERROR("Martingale test failed for gbpzb (Euler discr.),"
2506 "expected "
2507 << ev << ", got " << mean(gbpzb2) << ", tolerance " << tol2);
2508 ev = d.eurYts->discount(T2) * std::pow(1.0 + d.infEurTs->zeroRate(T2 - d.infLag), T2);
2509 if (std::abs(mean(infeur2) - ev) > tol2)
2510 BOOST_TEST_ERROR("Martingale test failed for idx eurzb (Euler discr.),"
2511 "expected "
2512 << ev << ", got " << mean(infeur2) << ", tolerance " << tol1);
2513 ev = d.gbpYts->discount(T2) * std::pow(1.0 + d.infGbpTs->zeroRate(T2 - d.infLag), T2) * d.fxEurGbp->value();
2514 if (std::abs(mean(infgbp2) - ev) > tol2)
2515 BOOST_TEST_ERROR("Martingale test failed for idx gbpzb (Euler discr.),"
2516 "expected "
2517 << ev << ", got " << mean(infgbp2) << ", tolerance " << tol1);
2518 ev = d.eurYts->discount(T2) * d.n1Ts->survivalProbability(T2);
2519 if (std::abs(mean(n1eur2) - ev) > tol2)
2520 BOOST_TEST_ERROR("Martingale test failed for def eurzb (Euler discr.),"
2521 "expected "
2522 << ev << ", got " << mean(n1eur1) << ", tolerance " << tol1);
2523
2524
2525 Real tol;
2526 ev = d.comParametrizationA->priceCurve()->price(T2);
2527 tol = error_of<tag::mean>(commodityA_1);
2528 if (std::abs(mean(commodityA_1) - ev) > tol)
2529 BOOST_TEST_ERROR("Martingale test failed for commodity A (exact discr.),"
2530 "expected " << ev << ", got " << mean(commodityA_1) << " +- " << tol);
2531 tol = error_of<tag::mean>(commodityA_2);
2532 if (std::abs(mean(commodityA_2) - ev) > tol)
2533 BOOST_TEST_ERROR("Martingale test failed for commodity A (Euler discr.),"
2534 "expected " << ev << ", got " << mean(commodityA_2) << " +- " << tol);
2535
2536
2537 ev = d.comParametrizationB->priceCurve()->price(T2);
2538 tol = error_of<tag::mean>(commodityB_1);
2539 if (std::abs(mean(commodityB_1) - ev) > tol)
2540 BOOST_TEST_ERROR("Martingale test failed for commodity B (exact discr.),"
2541 "expected " << ev << ", got " << mean(commodityB_1) << " +- " << tol);
2542 tol = error_of<tag::mean>(commodityB_2);
2543 if (std::abs(mean(commodityB_2) - ev) > tol)
2544 BOOST_TEST_ERROR("Martingale test failed for commodity B (Euler discr.),"
2545 "expected " << ev << ", got " << mean(commodityB_2) << " +- " << tol);
2546
2547
2548}
Real inflationGrowth(const QuantLib::ext::shared_ptr< CrossAssetModel > &model, Size index, Time S, Time T, Real irState, Real rrState, bool indexIsInterpolated)
vector< bool > driftFreeState