19#include <boost/test/unit_test.hpp>
35#include <oret/toplevelfixture.hpp>
49#include <ql/cashflows/simplecashflow.hpp>
50#include <ql/currencies/america.hpp>
51#include <ql/currencies/europe.hpp>
52#include <ql/indexes/ibor/all.hpp>
53#include <ql/indexes/swap/euriborswap.hpp>
54#include <ql/indexes/swap/usdliborswap.hpp>
55#include <ql/instruments/cpicapfloor.hpp>
56#include <ql/instruments/makeswaption.hpp>
57#include <ql/instruments/makevanillaswap.hpp>
58#include <ql/math/statistics/incrementalstatistics.hpp>
59#include <ql/pricingengines/vanilla/analyticeuropeanengine.hpp>
60#include <ql/quotes/simplequote.hpp>
61#include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
62#include <ql/termstructures/volatility/swaption/swaptionconstantvol.hpp>
63#include <ql/termstructures/yield/flatforward.hpp>
64#include <ql/time/calendars/target.hpp>
65#include <ql/time/daycounters/actual360.hpp>
66#include <ql/time/daycounters/thirty360.hpp>
69#include <boost/timer/timer.hpp>
73using namespace boost::unit_test_framework;
77using boost::timer::cpu_timer;
78using boost::timer::default_places;
83QuantLib::ext::shared_ptr<data::Conventions>
convs() {
84 QuantLib::ext::shared_ptr<data::Conventions> conventions(
new data::Conventions());
86 QuantLib::ext::shared_ptr<data::Convention> swapIndexConv(
88 conventions->add(swapIndexConv);
90 QuantLib::ext::shared_ptr<data::Convention> swapConv(
91 new data::IRSwapConvention(
"EUR-6M-SWAP-CONVENTIONS",
"TARGET",
"Annual",
"MF",
"30/360",
"EUR-EURIBOR-6M"));
92 conventions->add(swapConv);
94 InstrumentConventions::instance().setConventions(conventions);
100 TestData(std::string measure, Real shiftHorizon = 0.0) :
referenceDate(30, July, 2015) {
104 market = QuantLib::ext::make_shared<TestMarket>(referenceDate);
110 vector<string> swaptionExpiries = {
"1Y",
"2Y",
"3Y",
"5Y",
"7Y",
"10Y",
"15Y",
"20Y",
"30Y"};
111 vector<string> swaptionTerms = {
"5Y",
"5Y",
"5Y",
"5Y",
"5Y",
"5Y",
"5Y",
"5Y",
"5Y"};
112 vector<string> swaptionStrikes(swaptionExpiries.size(),
"ATM");
113 vector<Time> hTimes = {};
114 vector<Time> aTimes = {};
116 std::vector<QuantLib::ext::shared_ptr<IrModelData>> irConfigs;
118 vector<Real> hValues = {0.02};
119 vector<Real> aValues = {0.08};
120 irConfigs.push_back(QuantLib::ext::make_shared<IrLgmData>(
"EUR", calibrationType, revType, volType,
false,
121 ParamType::Constant, hTimes, hValues,
true,
122 ParamType::Piecewise, aTimes, aValues, shiftHorizon, 1.0,
123 swaptionExpiries, swaptionTerms, swaptionStrikes));
127 irConfigs.push_back(QuantLib::ext::make_shared<IrLgmData>(
128 "USD", calibrationType, revType, volType,
false, ParamType::Constant, hTimes, hValues,
true,
129 ParamType::Piecewise, aTimes, aValues, 0.0, 1.0, swaptionExpiries, swaptionTerms, swaptionStrikes));
133 irConfigs.push_back(QuantLib::ext::make_shared<IrLgmData>(
134 "GBP", calibrationType, revType, volType,
false, ParamType::Constant, hTimes, hValues,
true,
135 ParamType::Piecewise, aTimes, aValues, 0.0, 1.0, swaptionExpiries, swaptionTerms, swaptionStrikes));
138 vector<string> optionExpiries = {
"1Y",
"2Y",
"3Y",
"5Y",
"7Y",
"10Y"};
139 vector<string> optionStrikes(optionExpiries.size(),
"ATMF");
140 vector<Time> sigmaTimes = {};
142 std::vector<QuantLib::ext::shared_ptr<FxBsData>> fxConfigs;
144 vector<Real> sigmaValues = {0.15};
145 fxConfigs.push_back(QuantLib::ext::make_shared<FxBsData>(
"USD",
"EUR", calibrationType,
true, ParamType::Piecewise,
146 sigmaTimes, sigmaValues, optionExpiries, optionStrikes));
148 sigmaValues = {0.15};
149 fxConfigs.push_back(QuantLib::ext::make_shared<FxBsData>(
"GBP",
"EUR", calibrationType,
true, ParamType::Piecewise,
150 sigmaTimes, sigmaValues, optionExpiries, optionStrikes));
152 std::vector<QuantLib::ext::shared_ptr<EqBsData>> eqConfigs;
154 vector<QuantLib::ext::shared_ptr<InflationModelData>> infConfigs;
156 std::vector<QuantLib::ext::shared_ptr<CrLgmData>> crLgmConfigs;
157 std::vector<QuantLib::ext::shared_ptr<CrCirData>> crCirConfigs;
159 std::vector<QuantLib::ext::shared_ptr<CommoditySchwartzData>> comConfigs;
162 cmb.
addCorrelation(
"IR:EUR",
"IR:USD", Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.6)));
163 cmb.
addCorrelation(
"IR:EUR",
"IR:GBP", Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.3)));
164 cmb.
addCorrelation(
"IR:USD",
"IR:GBP", Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.1)));
165 cmb.
addCorrelation(
"FX:USDEUR",
"FX:GBPEUR", Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.3)));
166 cmb.
addCorrelation(
"IR:EUR",
"FX:USDEUR", Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.2)));
167 cmb.
addCorrelation(
"IR:EUR",
"FX:GBPEUR", Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.3)));
168 cmb.
addCorrelation(
"IR:USD",
"FX:USDEUR", Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(-0.2)));
169 cmb.
addCorrelation(
"IR:USD",
"FX:GBPEUR", Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(-0.1)));
170 cmb.
addCorrelation(
"IR:GBP",
"FX:USDEUR", Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.0)));
171 cmb.
addCorrelation(
"IR:GBP",
"FX:GBPEUR", Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.1)));
173 Real tolerance = 1e-4;
174 QuantLib::ext::shared_ptr<CrossAssetModelData> config1(QuantLib::ext::make_shared<CrossAssetModelData>(
175 irConfigs, fxConfigs, eqConfigs, infConfigs, crLgmConfigs, crCirConfigs, comConfigs, 0, cmb.
correlations(),
176 tolerance, measure, CrossAssetModel::Discretization::Exact));
177 QuantLib::ext::shared_ptr<CrossAssetModelData> config2(QuantLib::ext::make_shared<CrossAssetModelData>(
178 irConfigs, fxConfigs, eqConfigs, infConfigs, crLgmConfigs, crCirConfigs, comConfigs, 0, cmb.
correlations(),
179 tolerance, measure, CrossAssetModel::Discretization::Euler));
182 ccLgmExact = *modelBuilder1.model();
185 ccLgmEuler = *modelBuilder2.model();
187 lgm = QuantLib::ext::make_shared<QuantExt::LGM>(ccLgmExact->irlgm1f(0));
190 SavedSettings backup;
192 QuantLib::ext::shared_ptr<CrossAssetModelData> config;
193 QuantLib::ext::shared_ptr<QuantExt::CrossAssetModel> ccLgmExact, ccLgmEuler;
194 QuantLib::ext::shared_ptr<QuantExt::LGM> lgm;
195 QuantLib::ext::shared_ptr<ore::data::Market> market;
202BOOST_AUTO_TEST_SUITE(SimulationMeasuresTest)
204void
test_measure(std::
string measureName, Real shiftHorizon, std::
string discName) {
206 BOOST_TEST_MESSAGE(
"Testing market simulation, measure " << measureName <<
", horizon " << shiftHorizon
207 <<
", discretization " << discName);
209 TestData d(measureName, shiftHorizon);
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};
217 for (Size i = 1; i <= 60; ++i)
218 tenorGrid.push_back(i * 2 * Months);
220 QuantLib::ext::shared_ptr<DateGrid> grid = QuantLib::ext::make_shared<DateGrid>(tenorGrid);
223 QuantLib::ext::shared_ptr<QuantExt::CrossAssetModel> model = discName ==
"exact" ? d.ccLgmExact : d.ccLgmEuler;
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);
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"});
241 sgd->sequenceType() =
Sobol;
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);
250 auto simMarket = QuantLib::ext::make_shared<ScenarioSimMarket>(d.market, simMarketConfig);
251 simMarket->scenarioGenerator() = sg;
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;
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);
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()) {
274 simMarket->update(d);
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);
283 Real usdDiscount = simMarket->discountCurve(
"USD")->discount(1.0 * horizon);
286 simMarket->iborIndex(
"EUR-EURIBOR-6M")->forwardingTermStructure()->discount(1.0 * horizon);
288 simMarket->iborIndex(
"GBP-LIBOR-6M")->forwardingTermStructure()->discount(1.0 * horizon);
291 simMarket->iborIndex(
"USD-LIBOR-3M")->forwardingTermStructure()->discount(1.0 * horizon);
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;
318 Real eurDiff = fabs(eur - eurExpected) / eurExpected;
319 BOOST_CHECK_MESSAGE(eurDiff < relTolerance,
"EUR 20Y Discount mismatch: " << eur <<
" vs " << eurExpected);
321 Real gbpDiff = fabs(gbp - gbpExpected) / gbpExpected;
322 BOOST_CHECK_MESSAGE(gbpDiff < relTolerance,
323 "GBP 20Y Discount mismatch: " << gbp <<
" vs " << gbpExpected <<
" (" << gbpDiff <<
")");
325 Real usdDiff = fabs(usd - usdExpected) / usdExpected;
326 BOOST_CHECK_MESSAGE(usdDiff < relTolerance,
"USD 20Y Discount mismatch: " << usd <<
" vs " << usdExpected);
328 Real eur3Diff = fabs(eur3 - eurExpected) / eurExpected;
329 BOOST_CHECK_MESSAGE(eur3Diff < relTolerance,
"EUR 20Y Index Discount mismatch: " << eur3 <<
" vs " << eurExpected);
331 Real gbp3Diff = fabs(gbp3 - gbpExpected) / gbpExpected;
332 BOOST_CHECK_MESSAGE(gbp3Diff < relTolerance,
"GBP 20Y Index Discount mismatch: " << gbp3 <<
" vs " << gbpExpected);
334 Real usd3Diff = fabs(usd3 - usdExpected) / usdExpected;
335 BOOST_CHECK_MESSAGE(usd3Diff < relTolerance,
"USD 20Y Index Discount mismatch: " << usd3 <<
" vs " << usdExpected);
337 Real eur2Diff = fabs(eur2 - eurExpected2) / eurExpected2;
338 BOOST_CHECK_MESSAGE(eur2Diff < relTolerance,
"EUR 10Y Discount mismatch: " << eur2 <<
" vs " << eurExpected2);
340 Real gbp2Diff = fabs(gbp2 - gbpExpected2) / gbpExpected2;
341 BOOST_CHECK_MESSAGE(gbp2Diff < relTolerance,
"GBP 10Y Discount mismatch: " << gbp2 <<
" vs " << gbpExpected2);
343 Real usd2Diff = fabs(usd2 - usdExpected2) / usdExpected2;
344 BOOST_CHECK_MESSAGE(usd2Diff < relTolerance,
"USD 10Y Discount mismatch: " << usd2 <<
" vs " << usdExpected2);
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"));
375BOOST_AUTO_TEST_SUITE_END()
377BOOST_AUTO_TEST_SUITE_END()
Build a ScenarioGenerator.
QuantLib::ext::shared_ptr< ScenarioGenerator > build(QuantLib::ext::shared_ptr< QuantExt::CrossAssetModel > model, QuantLib::ext::shared_ptr< ScenarioFactory > sf, QuantLib::ext::shared_ptr< ScenarioSimMarketParameters > marketConfig, Date asof, QuantLib::ext::shared_ptr< ore::data::Market > initMarket, const std::string &configuration=ore::data::Market::defaultConfiguration, const QuantLib::ext::shared_ptr< PathGeneratorFactory > &pf=QuantLib::ext::make_shared< MultiPathGeneratorFactory >())
Build function.
Scenario Generator description.
ScenarioSimMarket description.
void addCorrelation(const std::string &factor1, const std::string &factor2, QuantLib::Real correlation)
const std::map< CorrelationKey, QuantLib::Handle< QuantLib::Quote > > & correlations()
OREAnalytics Top level fixture.
Simple flat market setup to be used in the test suite.
Scenario generation using cross asset model paths.
Scenario generation using LGM paths.
QuantLib::ext::shared_ptr< data::Conventions > convs()
Fixture that can be used at top level of OREAnalytics test suites.
Build a scenariogenerator.
A Market class that can be updated by Scenarios.
factory classes for simple scenarios
void test_measure(std::string measureName, Real shiftHorizon, std::string discName)
BOOST_AUTO_TEST_CASE(testLgmExact)