19#include <boost/filesystem.hpp>
20#include <boost/test/unit_test.hpp>
50#include <ql/math/randomnumbers/mt19937uniformrng.hpp>
51#include <ql/time/calendars/target.hpp>
52#include <ql/time/date.hpp>
53#include <ql/time/daycounters/actualactual.hpp>
54#include <oret/toplevelfixture.hpp>
60using namespace boost::unit_test_framework;
68 for (Size i = 0; i < cube.
numIds(); ++i) {
69 for (Size j = 0; j < cube.
numDates(); ++j) {
70 for (Size k = 0; k < cube.
samples(); ++k) {
71 for (Size d = 0; d < cube.
depth(); ++d) {
72 cube.
set(i * 1000000.0 + j + k / 1000000.0 + d * 3, i, j, k, d);
79void checkCube(
NPVCube& cube, Real tolerance) {
81 for (Size i = 0; i < cube.
numIds(); ++i) {
82 for (Size j = 0; j < cube.
numDates(); ++j) {
83 for (Size k = 0; k < cube.
samples(); ++k) {
84 for (Size d = 0; d < cube.
depth(); ++d) {
85 Real expected = i * 1000000.0 + j + k / 1000000.0 + d * 3;
86 Real actual = cube.
get(i, j, k, d);
87 BOOST_CHECK_CLOSE(expected, actual, tolerance);
94void testCube(
NPVCube& cube,
const std::string& cubeName, Real tolerance) {
95 BOOST_TEST_MESSAGE(
"Testing cube " << cubeName);
100 BOOST_CHECK_THROW(cube.
set(1.0, cube.
numIds(), 0, 0), std::exception);
101 BOOST_CHECK_THROW(cube.
set(1.0, 0, cube.
numDates(), 0), std::exception);
102 BOOST_CHECK_THROW(cube.
set(1.0, 0, 0, cube.
samples()), std::exception);
103 BOOST_CHECK_THROW(cube.
set(1.0,
"test_id", Date::todaysDate(), 0), std::exception);
106 BOOST_CHECK_THROW(cube.
get(cube.
numIds(), 0, 0), std::exception);
107 BOOST_CHECK_THROW(cube.
get(0, cube.
numDates(), 0), std::exception);
108 BOOST_CHECK_THROW(cube.
get(0, 0, cube.
samples()), std::exception);
109 BOOST_CHECK_THROW(cube.
get(
"test_id", Date::todaysDate(), 0), std::exception);
111 checkCube(cube, tolerance);
116void testCubeFileIO(QuantLib::ext::shared_ptr<NPVCube> cube,
const std::string& cubeName, Real tolerance,
117 bool doublePrecision) {
122 string filename = boost::filesystem::unique_path().string();
123 BOOST_TEST_MESSAGE(
"Saving cube " << cubeName <<
" to file " << filename);
127 BOOST_TEST_MESSAGE(
"Loading from file " << filename);
129 BOOST_TEST_MESSAGE(
"Cube " << cubeName <<
" loaded from file.");
132 boost::filesystem::remove(filename);
135 BOOST_CHECK_EQUAL(cube->numIds(), cube->numIds());
136 BOOST_CHECK_EQUAL(cube->numDates(), cube->numDates());
137 BOOST_CHECK_EQUAL(cube->samples(), cube->samples());
138 BOOST_CHECK_EQUAL(cube->depth(), cube->depth());
141 checkCube(*cube2, tolerance);
144void testCubeGetSetbyDateID(
NPVCube& cube, Real tolerance) {
146 vector<Date> dates = cube.
dates();
149 for (
auto [
id, pos] : ids) {
150 for (
auto dt : dates) {
151 cube.
set(i,
id, dt, 0);
157 for (
const auto& [
id, pos]: ids) {
158 for (
auto dt : dates) {
159 Real actual = cube.
get(
id, dt, 0);
160 BOOST_CHECK_CLOSE(j, actual, tolerance);
166void initCube(
NPVCube& cube, QuantLib::ext::shared_ptr<Portfolio>& portfolio, QuantLib::ext::shared_ptr<DateGrid>& dg) {
170 Date tradeMaturity = portfolio->get(
id)->maturity();
171 while (dateLen < dg->dates().
size() && dg->dates()[dateLen] < tradeMaturity) {
174 for (Size j = 0; j < dateLen; ++j) {
175 for (Size k = 0; k < cube.
samples(); ++k) {
176 for (Size d = 0; d < cube.
depth(); ++d) {
177 cube.
set(i * 1000000.0 + j + k / 1000000.0 + d * 3, i, j, d, k);
184void checkCube(
NPVCube& cube, Real tolerance, QuantLib::ext::shared_ptr<Portfolio>& portfolio,
185 QuantLib::ext::shared_ptr<DateGrid>& dg) {
189 Date tradeMaturity = portfolio->get(
id)->maturity();
190 while (dateLen < dg->dates().
size() && dg->dates()[dateLen] < tradeMaturity) {
193 for (Size j = 0; j < dateLen; ++j) {
194 for (Size k = 0; k < cube.
samples(); ++k) {
195 for (Size d = 0; d < cube.
depth(); ++d) {
196 Real expected = i * 1000000.0 + j + k / 1000000.0 + d * 3;
197 Real actual = cube.
get(i, j, d, k);
198 BOOST_CHECK_CLOSE(expected, actual, tolerance);
205void testCube(
NPVCube& cube,
const std::string& cubeName, Real tolerance, QuantLib::ext::shared_ptr<Portfolio>& portfolio,
206 QuantLib::ext::shared_ptr<DateGrid>& d) {
207 BOOST_TEST_MESSAGE(
"Testing cube " << cubeName);
208 initCube(cube, portfolio, d);
210 BOOST_CHECK_THROW(cube.
set(1.0, cube.
numIds(), 0, 0), std::exception);
211 BOOST_CHECK_THROW(cube.
set(1.0, 0, cube.
numDates(), 0), std::exception);
212 BOOST_CHECK_THROW(cube.
set(1.0, 0, 0, cube.
samples()), std::exception);
215 BOOST_CHECK_THROW(cube.
get(cube.
numIds(), 0, 0), std::exception);
216 BOOST_CHECK_THROW(cube.
get(0, cube.
numDates(), 0), std::exception);
217 BOOST_CHECK_THROW(cube.
get(0, 0, cube.
samples()), std::exception);
218 checkCube(cube, tolerance, portfolio, d);
223void testCubeFileIO(QuantLib::ext::shared_ptr<NPVCube> cube,
const std::string& cubeName, Real tolerance,
224 QuantLib::ext::shared_ptr<Portfolio>& portfolio, QuantLib::ext::shared_ptr<DateGrid>& d,
bool doublePrecision) {
226 initCube(*cube, portfolio, d);
229 string filename = boost::filesystem::unique_path().string();
230 BOOST_TEST_MESSAGE(
"Saving cube " << cubeName <<
" to file " << filename);
234 auto cube2 = QuantLib::ext::make_shared<T>();
235 BOOST_TEST_MESSAGE(
"Loading from file " << filename);
237 BOOST_TEST_MESSAGE(
"Cube " << cubeName <<
" loaded from file.");
240 boost::filesystem::remove(filename);
243 BOOST_CHECK_EQUAL(cube->numIds(), cube2->numIds());
244 BOOST_CHECK_EQUAL(cube->numDates(), cube2->numDates());
245 BOOST_CHECK_EQUAL(cube->samples(), cube2->samples());
246 BOOST_CHECK_EQUAL(cube->depth(), cube2->depth());
249 checkCube(*cube2, tolerance, portfolio, d);
255inline unsigned long randInt(MersenneTwisterUniformRng& rng, Size min, Size max) {
256 return min + (rng.nextInt32() % (
max + 1 -
min));
259inline const string&
randString(MersenneTwisterUniformRng& rng,
const vector<string>& strs) {
260 return strs[
randInt(rng, 0, strs.size() - 1)];
264QuantLib::ext::shared_ptr<Portfolio>
buildPortfolio(Size portfolioSize, QuantLib::ext::shared_ptr<EngineFactory>& factory) {
266 QuantLib::ext::shared_ptr<Portfolio> portfolio(
new Portfolio());
268 vector<string> ccys = {
"EUR",
"USD",
"GBP",
"JPY",
"CHF"};
270 map<string, vector<string>> indices = {{
"EUR", {
"EUR-EURIBOR-6M"}},
271 {
"USD", {
"USD-LIBOR-3M"}},
272 {
"GBP", {
"GBP-LIBOR-6M"}},
273 {
"CHF", {
"CHF-LIBOR-6M"}},
274 {
"JPY", {
"JPY-LIBOR-6M"}}};
276 vector<string> fixedTenors = {
"6M",
"1Y"};
281 Size minFixedBps = 10;
282 Size maxFixedBps = 400;
285 MersenneTwisterUniformRng rng(seed);
287 Date today = Settings::instance().evaluationDate();
288 Calendar cal = TARGET();
289 string calStr =
"TARGET";
291 string rule =
"Forward";
293 string fixDC =
"30/360";
294 string floatDC =
"ACT/365";
296 vector<double> notional(1, 1000000);
297 vector<double> spread(1, 0);
299 for (Size i = 0; i < portfolioSize; i++) {
300 Size term = portfolioSize == 1 ? 20 :
randInt(rng, minTerm, maxTerm);
303 Date startDate = portfolioSize == 1 ? cal.adjust(today) : cal.adjust(today - 365 +
randInt(rng, 0, 730));
304 Date endDate = cal.adjust(startDate + term * Years);
307 std::ostringstream oss;
308 oss << io::iso_date(startDate);
309 string start(oss.str());
312 oss << io::iso_date(endDate);
313 string end(oss.str());
316 string ccy = portfolioSize == 1 ?
"EUR" :
randString(rng, ccys);
317 string index = portfolioSize == 1 ?
"EUR-EURIBOR-6M" :
randString(rng, indices[ccy]);
318 string floatFreq = portfolioSize == 1 ?
"6M" : index.substr(index.find(
'-', 4) + 1);
321 Real fixedRate = portfolioSize == 1 ? 0.02 :
randInt(rng, minFixedBps, maxFixedBps) / 100.0;
322 string fixFreq = portfolioSize == 1 ?
"1Y" :
randString(rng, fixedTenors);
334 LegData fixedLeg(QuantLib::ext::make_shared<FixedLegData>(vector<double>(1, fixedRate)), isPayer, ccy, fixedSchedule,
338 vector<double> spreads(1, 0);
339 LegData floatingLeg(QuantLib::ext::make_shared<FloatingLegData>(index, days,
false, spread), !isPayer, ccy,
340 floatSchedule, floatDC, notional);
342 QuantLib::ext::shared_ptr<Trade> swap(
new ore::data::Swap(env, floatingLeg, fixedLeg));
347 oss <<
"Trade_" << i + 1;
348 swap->id() = oss.str();
350 portfolio->add(swap);
354 portfolio->build(factory);
356 if (portfolio->size() != portfolioSize)
357 BOOST_ERROR(
"Failed to build portfolio (got " << portfolio->size() <<
" expected " << portfolioSize <<
")");
361 DayCounter dc = ActualActual(ActualActual::ISDA);
362 map<string, Size> fixedFreqs;
363 map<string, Size> floatFreqs;
364 for (
const auto& [tradeId, trade] : portfolio->trades()) {
365 maturity += dc.yearFraction(today, trade->maturity());
368 QuantLib::ext::shared_ptr<ore::data::Swap> swap = QuantLib::ext::dynamic_pointer_cast<ore::data::Swap>(trade);
369 string floatFreq = swap->legData()[0].schedule().rules().front().tenor();
370 string fixFreq = swap->legData()[1].schedule().rules().front().tenor();
371 QL_REQUIRE(swap->legData()[0].legType() ==
"Floating" && swap->legData()[1].legType() ==
"Fixed",
"Leg mixup");
372 if (fixedFreqs.find(fixFreq) == fixedFreqs.end())
373 fixedFreqs[fixFreq] = 1;
375 fixedFreqs[fixFreq]++;
376 if (floatFreqs.find(floatFreq) == floatFreqs.end())
377 floatFreqs[floatFreq] = 1;
379 floatFreqs[floatFreq]++;
382 BOOST_TEST_MESSAGE(
"Portfolio Size : " << portfolioSize);
383 BOOST_TEST_MESSAGE(
"Average Maturity : " <<
maturity);
384 std::ostringstream oss;
385 for (Size i = 0; i < ccys.size(); i++)
386 oss << ccys[i] <<
" ";
387 BOOST_TEST_MESSAGE(
"Currencies : " << oss.str());
389 map<string, Size>::iterator it;
390 BOOST_TEST_MESSAGE(
"Fixed Tenors : ");
391 for (it = fixedFreqs.begin(); it != fixedFreqs.end(); ++it) {
392 Real perc = 100 * it->second / (Real)portfolioSize;
393 BOOST_TEST_MESSAGE(
" " << it->first <<
" " << perc <<
" %");
395 BOOST_TEST_MESSAGE(
"Floating Tenors : ");
396 for (it = floatFreqs.begin(); it != floatFreqs.end(); ++it) {
397 Real perc = 100 * it->second / (Real)portfolioSize;
398 BOOST_TEST_MESSAGE(
" " << it->first <<
" " << perc <<
" %");
406BOOST_AUTO_TEST_SUITE(CubeTest)
410 std::set<string> ids {string(
"id")};
411 vector<Date> dates(100, Date());
414 testCube(c,
"SinglePrecisionInMemoryCube", 1e-5);
418 std::set<string> ids{string(
"id")};
419 vector<Date> dates(100, Date());
422 testCube(c,
"DoublePrecisionInMemoryCube", 1e-14);
426 std::set<string> ids{string(
"id")};
427 vector<Date> dates(50, Date());
431 testCube(c,
"SinglePrecisionInMemoryCubeN", 1e-5);
435 std::set<string> ids{string(
"id")};
436 vector<Date> dates(50, Date());
440 testCube(c,
"DoublePrecisionInMemoryCubeN", 1e-14);
444 std::set<string> ids{string(
"id")};
445 Date d(1, QuantLib::Jan, 2016);
446 vector<Date> dates(100, d);
448 auto c = QuantLib::ext::make_shared<DoublePrecisionInMemoryCube>(d, ids, dates, samples);
449 testCubeFileIO<DoublePrecisionInMemoryCube>(c,
"DoublePrecisionInMemoryCube", 1e-14,
true);
453 std::set<string> ids{string(
"id")};
454 Date d(1, QuantLib::Jan, 2016);
455 vector<Date> dates(50, d);
458 auto c = QuantLib::ext::make_shared<DoublePrecisionInMemoryCubeN>(d, ids, dates, samples, depth);
459 testCubeFileIO<DoublePrecisionInMemoryCubeN>(c,
"DoublePrecisionInMemoryCubeN", 1e-14,
true);
463 std::set<string> ids = {
"id1",
"id2",
"id3"};
464 Date today = Date::todaysDate();
465 vector<Date> dates = {today + QuantLib::Period(1, QuantLib::Days), today + QuantLib::Period(2, QuantLib::Days),
466 today + QuantLib::Period(3, QuantLib::Days)};
469 testCubeGetSetbyDateID(cube, 1e-14);
474 SavedSettings backup;
476 Size portfolioSize = 100;
479 Date today = Date(15, December, 2016);
480 Settings::instance().evaluationDate() = today;
481 string dateGridStr =
"270,2W";
482 QuantLib::ext::shared_ptr<DateGrid> d = QuantLib::ext::make_shared<DateGrid>(dateGridStr);
484 QuantLib::ext::shared_ptr<EngineData>
data = QuantLib::ext::make_shared<EngineData>();
487 QuantLib::ext::shared_ptr<Market> initMarket = QuantLib::ext::make_shared<testsuite::TestMarket>(today);
489 data->model(
"EuropeanSwaption") =
"BlackBachelier";
490 data->engine(
"EuropeanSwaption") =
"BlackBachelierSwaptionEngine";
491 data->model(
"Swap") =
"DiscountedCashflows";
492 data->engine(
"Swap") =
"DiscountingSwapEngine";
493 data->model(
"FxOption") =
"GarmanKohlhagen";
494 data->engine(
"FxOption") =
"AnalyticEuropeanEngine";
496 QuantLib::ext::shared_ptr<EngineFactory> factory = QuantLib::ext::make_shared<EngineFactory>(
data, initMarket);
498 QuantLib::ext::shared_ptr<Portfolio> portfolio =
buildPortfolio(portfolioSize, factory);
502 testCube(jaggedCube,
"SinglePrecisionJaggedCube", 1e-5, portfolio, d);
503 IndexManager::instance().clearHistories();
508 SavedSettings backup;
510 Size portfolioSize = 100;
513 Date today = Date(15, December, 2016);
514 Settings::instance().evaluationDate() = today;
515 string dateGridStr =
"270,2W";
516 QuantLib::ext::shared_ptr<DateGrid> d = QuantLib::ext::make_shared<DateGrid>(dateGridStr);
518 QuantLib::ext::shared_ptr<EngineData>
data = QuantLib::ext::make_shared<EngineData>();
521 QuantLib::ext::shared_ptr<Market> initMarket = QuantLib::ext::make_shared<testsuite::TestMarket>(today);
523 data->model(
"EuropeanSwaption") =
"BlackBachelier";
524 data->engine(
"EuropeanSwaption") =
"BlackBachelierSwaptionEngine";
525 data->model(
"Swap") =
"DiscountedCashflows";
526 data->engine(
"Swap") =
"DiscountingSwapEngine";
527 data->model(
"FxOption") =
"GarmanKohlhagen";
528 data->engine(
"FxOption") =
"AnalyticEuropeanEngine";
530 QuantLib::ext::shared_ptr<EngineFactory> factory = QuantLib::ext::make_shared<EngineFactory>(
data, initMarket);
532 QuantLib::ext::shared_ptr<Portfolio> portfolio =
buildPortfolio(portfolioSize, factory);
536 testCube(jaggedCube,
"DoublePrecisionJaggedCube", 1e-5, portfolio, d);
537 IndexManager::instance().clearHistories();
540BOOST_AUTO_TEST_SUITE_END()
542BOOST_AUTO_TEST_SUITE_END()
InMemoryCube of fixed depth 1.
InMemoryCube of variable depth.
JaggedCube stores the cube in memory using a vector of trade specific blocks.
NPV Cube class stores both future and current NPV values.
virtual Size numDates() const =0
virtual Size samples() const =0
virtual Real get(Size id, Size date, Size sample, Size depth=0) const =0
Get a value from the cube using index.
virtual void set(Real value, Size id, Size date, Size sample, Size depth=0)=0
Set a value in the cube using index.
virtual Size numIds() const =0
Return the length of each dimension.
virtual Size depth() const =0
virtual const std::vector< QuantLib::Date > & dates() const =0
Get the vector of dates for this cube.
virtual const std::map< std::string, Size > & idsAndIndexes() const =0
Get a map of id and their index position in this cube.
OREAnalytics Top level fixture.
Scenario generation using cross asset model paths.
unsigned long randInt(MersenneTwisterUniformRng &rng, Size min, Size max)
bool randBoolean(MersenneTwisterUniformRng &rng)
const string & randString(MersenneTwisterUniformRng &rng, const vector< string > &strs)
QuantLib::ext::shared_ptr< Portfolio > buildPortfolio(Size portfolioSize, QuantLib::ext::shared_ptr< EngineFactory > &factory)
BOOST_AUTO_TEST_CASE(testSinglePrecisionInMemoryCube)
load / save cubes and agg scen data from / to disk
Class that wraps a sensitivity stream and filters out negligible records.
A cube implementation that stores the cube in memory.
A cube implementation that stores the cube in memory.
RandomVariable max(RandomVariable x, const RandomVariable &y)
RandomVariable min(RandomVariable x, const RandomVariable &y)
void saveCube(const std::string &filename, const NPVCubeWithMetaData &cube, const bool doublePrecision)
NPVCubeWithMetaData loadCube(const std::string &filename, const bool doublePrecision)
Size size(const ValueType &v)
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.