19#include <boost/make_shared.hpp>
21#include <boost/test/unit_test.hpp>
22#include <boost/test/data/test_case.hpp>
38#include <oret/datapaths.hpp>
39#include <oret/toplevelfixture.hpp>
40#include <ql/time/calendars/weekendsonly.hpp>
45using namespace boost::unit_test_framework;
49using ore::test::TopLevelFixture;
57map<tuple<string, string, bool, bool>, map<string, set<Date>>> tradeTypeExpected() {
59 static map<tuple<string, string, bool, bool>, map<string, set<Date>>>
exp;
61 string expResultsFile = TEST_INPUT_FILE(
"test_trade_types_expected.csv");
64 while (reader.next()) {
65 string tradeType = reader.get(
"trade_type");
66 string tradeCase = reader.get(
"trade_case");
67 bool includeSettlementDateFlows =
parseBool(reader.get(
"isdf"));
68 bool enforcesTodaysHistoricFixings =
parseBool(reader.get(
"ethf"));
69 string indexName = reader.get(
"index_name");
71 string datesList = reader.get(
"dates");
72 vector<string> strDates;
73 boost::split(strDates, datesList, boost::is_any_of(
"|"));
75 for (
const auto& strDate : strDates) {
79 auto key = make_tuple(tradeType, tradeCase, includeSettlementDateFlows, enforcesTodaysHistoricFixings);
80 if (
exp.count(key) == 0) {
83 exp[key][indexName] = dates;
91map<tuple<string, Date>,
Fixing> dummyFixings() {
93 static map<tuple<string, Date>,
Fixing> result;
95 string dummyFixingsFile = TEST_INPUT_FILE(
"test_trade_types_dummy_fixings.csv");
98 while (reader.next()) {
99 string name = reader.get(
"name");
100 Date date =
parseDate(reader.get(
"date"));
101 Real fixing =
parseReal(reader.get(
"value"));
103 result.emplace(make_pair(
name, date),
Fixing(date,
name, fixing));
110void loadFixings(
const map<string, RequiredFixings::FixingDates>& requestedFixings) {
113 auto fixingValues = dummyFixings();
116 set<Fixing> relevantFixings;
117 for (
const auto& [indexName, fixingDates] : requestedFixings) {
118 for (
const auto& [d, mandatory] : fixingDates) {
119 relevantFixings.insert(fixingValues.at(make_pair(indexName, d)));
131class F :
public TopLevelFixture {
134 QuantLib::ext::shared_ptr<Conventions> conventions = QuantLib::ext::make_shared<Conventions>();
135 QuantLib::ext::shared_ptr<EngineFactory> engineFactory;
138 today = Date(12, Feb, 2019);
139 Settings::instance().evaluationDate() = today;
141 conventions->fromFile(TEST_INPUT_FILE(
"market/conventions.xml"));
142 InstrumentConventions::instance().setConventions(conventions);
144 auto todaysMarketParams = QuantLib::ext::make_shared<TodaysMarketParameters>();
145 todaysMarketParams->fromFile(TEST_INPUT_FILE(
"market/todaysmarket.xml"));
147 auto curveConfigs = QuantLib::ext::make_shared<CurveConfigurations>();
148 curveConfigs->fromFile(TEST_INPUT_FILE(
"market/curveconfig.xml"));
150 string marketFile = TEST_INPUT_FILE(
"market/market.txt");
151 string fixingsFile = TEST_INPUT_FILE(
"market/fixings_for_bootstrap.txt");
152 string dividendsFile = TEST_INPUT_FILE(
"market/dividends.txt");
153 auto loader = QuantLib::ext::make_shared<CSVLoader>(marketFile, fixingsFile, dividendsFile,
false);
155 bool continueOnError =
false;
156 QuantLib::ext::shared_ptr<TodaysMarket> market = QuantLib::ext::make_shared<TodaysMarket>(
157 today, todaysMarketParams, loader,
curveConfigs, continueOnError);
159 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
160 engineData->fromFile(TEST_INPUT_FILE(
"market/pricingengine.xml"));
162 engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
174vector<string> tradeTypes = {
"fixed_float_swap",
"in_ccy_basis_swap",
"zciis_with_interp",
175 "cpi_swap_with_interp",
"yoy_swap_without_interp",
"xccy_resetting_swap",
176 "equity_swap",
"cms_spread_swap"};
178vector<string> tradeCases = {
"simple_case",
"payment_today",
"fixing_today"};
180vector<bool> bools = {
true,
false};
182vector<bool> enforcesTodaysHistoricFixings = {
true,
false};
186BOOST_FIXTURE_TEST_SUITE(OREDataTestSuite, ore::test::TopLevelFixture)
188BOOST_AUTO_TEST_SUITE(FixingsTests)
192 tradeType, tradeCase, includeSettlementDateFlows, enforcesTodaysHistoricFixings) {
195 Settings::instance().enforcesTodaysHistoricFixings() = enforcesTodaysHistoricFixings;
198 Settings::instance().includeTodaysCashFlows() = includeSettlementDateFlows;
202 string portfolioFile =
"trades/" + tradeType +
"/" + tradeCase +
".xml";
203 p.
fromFile(TEST_INPUT_FILE(portfolioFile));
204 BOOST_REQUIRE_MESSAGE(p.
size() == 1,
"Expected portfolio to contain a single trade");
208 BOOST_CHECK_MESSAGE(m.empty(),
"Expected fixings to be empty when trades not built");
211 p.
build(engineFactory);
215 auto exp = tradeTypeExpected();
216 auto key = make_tuple(tradeType, tradeCase, includeSettlementDateFlows, enforcesTodaysHistoricFixings);
217 if (
exp.count(key) == 0) {
219 BOOST_CHECK_MESSAGE(m.empty(),
"Expected no required fixings for ["
220 << tradeType <<
", " << tradeCase <<
", "
223 <<
"] but got a map containing " << m.size() <<
" indices");
226 BOOST_CHECK_NO_THROW(p.
trades().begin()->second->instrument()->NPV());
230 auto expMap =
exp.
at(key);
231 BOOST_CHECK_EQUAL(expMap.size(), m.size());
232 for (
const auto& [indexName, expectedDates] : expMap) {
233 BOOST_CHECK_MESSAGE(m.count(indexName),
"Could not find index " <<indexName <<
" in retrieved fixings");
234 std::set<QuantLib::Date> actualDates;
235 for (
const auto& [d, _] : m.at(indexName)) {
236 actualDates.insert(d);
238 BOOST_CHECK_EQUAL_COLLECTIONS(expectedDates.begin(), expectedDates.end(), actualDates.begin(),
244 if (tradeType !=
"zciis_with_interp" && tradeType !=
"cpi_swap_with_interp") {
245 BOOST_CHECK_THROW(p.
trades().begin()->second->instrument()->NPV(), Error);
252 BOOST_CHECK_NO_THROW(p.
trades().begin()->second->instrument()->NPV());
259 map<string, RequiredFixings::FixingDates> fixings = {
260 {
"EUHICP",
RequiredFixings::FixingDates({Date(1, Jan, 2019), Date(1, Dec, 2018), Date(1, Nov, 2018)},
true)},
261 {
"USCPI",
RequiredFixings::FixingDates({Date(1, Dec, 2018), Date(1, Nov, 2018), Date(22, Oct, 2018),
262 Date(1, Feb, 2018), Date(1, Feb, 2016)},
267 map<string, RequiredFixings::FixingDates> expectedFixings = {
268 {
"EUHICP",
RequiredFixings::FixingDates({Date(31, Jan, 2019), Date(31, Dec, 2018), Date(30, Nov, 2018)},
true)},
269 {
"USCPI",
RequiredFixings::FixingDates({Date(31, Dec, 2018), Date(30, Nov, 2018), Date(22, Oct, 2018),
270 Date(28, Feb, 2018), Date(29, Feb, 2016)},
278 BOOST_CHECK_EQUAL(expectedFixings.size(), fixings.size());
279 for (
const auto& [indexname, expectedFixingDates] : expectedFixings) {
280 BOOST_CHECK_MESSAGE(fixings.count(indexname),
"Could not find index " << indexname <<
" in retrieved fixings");
281 std::set<QuantLib::Date> expectedDates;
282 for (
const auto& [d, _] : expectedFixingDates) {
283 expectedDates.insert(d);
286 std::set<QuantLib::Date> actualDates;
287 for (
const auto& [d, _] : fixings[indexname]) {
288 actualDates.insert(d);
290 BOOST_CHECK_EQUAL_COLLECTIONS(expectedDates.begin(), expectedDates.end(), actualDates.begin(),
298 Date asof(21, Feb, 2019);
299 Settings::instance().evaluationDate() = asof;
306 map<string, string> m = {{
"EUR",
"Yield/EUR/EUR-EONIA"}, {
"USD",
"Yield/USD/USD-IN-EUR"}};
310 m = {{
"EUR-EURIBOR-3M",
"Yield/EUR/EUR-EURIBOR-3M"},
311 {
"USD-FedFunds",
"Yield/USD/USD-FedFunds"},
312 {
"USD-LIBOR-3M",
"Yield/USD/USD-LIBOR-3M"}};
316 m = {{
"EUHICPXT",
"Inflation/EUHICPXT/EUHICPXT_ZC_Swaps"}, {
"USCPI",
"Inflation/USCPI/USCPI_ZC_Swaps"}};
320 m = {{
"EUHICPXT",
"Inflation/EUHICPXT/EUHICPXT_YOY_Swaps"}, {
"UKRPI",
"Inflation/UKRPI/UKRPI_YOY_Swaps"}};
324 set<Date> inflationDates = {Date(1, Feb, 2019), Date(1, Jan, 2019), Date(1, Dec, 2018), Date(1, Nov, 2018),
325 Date(1, Oct, 2018), Date(1, Sep, 2018), Date(1, Aug, 2018), Date(1, Jul, 2018),
326 Date(1, Jun, 2018), Date(1, May, 2018), Date(1, Apr, 2018), Date(1, Mar, 2018),
328 set<Date> iborDates = {Date(21, Feb, 2019), Date(20, Feb, 2019), Date(19, Feb, 2019),
329 Date(18, Feb, 2019), Date(15, Feb, 2019), Date(14, Feb, 2019)};
334 Date oisDate(22, Oct, 2018);
336 while (oisDate <= asof) {
337 oisDates.insert(oisDate);
338 oisDate = cal.advance(oisDate, 1 * Days);
341 map<string, RequiredFixings::FixingDates> expectedFixings = {
351 map<string, RequiredFixings::FixingDates> fixings;
355 BOOST_CHECK_EQUAL(expectedFixings.size(), fixings.size());
356 for (
const auto& [indexName, expectedFixingDates] : expectedFixings) {
357 BOOST_CHECK_MESSAGE(fixings.count(indexName),
"Could not find index " << indexName <<
" in retrieved fixings");
358 std::set<QuantLib::Date> expectedDates;
359 for (
const auto& [d, _] : expectedFixingDates) {
360 expectedDates.insert(d);
363 std::set<QuantLib::Date> actualDates;
364 for (
const auto& [d, _] : fixings[indexName]) {
365 actualDates.insert(d);
367 BOOST_CHECK_EQUAL_COLLECTIONS(expectedDates.begin(), expectedDates.end(), actualDates.begin(),
375 Settings::instance().enforcesTodaysHistoricFixings() =
true;
378 Settings::instance().includeTodaysCashFlows() =
true;
382 string portfolioFile =
"trades/xccy_resetting_swap/simple_case_in_first_coupon.xml";
383 p.
fromFile(TEST_INPUT_FILE(portfolioFile));
384 BOOST_REQUIRE_MESSAGE(p.
size() == 1,
"Expected portfolio to contain a single trade");
388 BOOST_CHECK_MESSAGE(m.empty(),
"Expected fixings to be empty when trades not built");
391 p.
build(engineFactory);
395 map<string, Date>
exp = {{
"USD-LIBOR-3M", Date(5, Feb, 2019)}, {
"EUR-EURIBOR-3M", Date(5, Feb, 2019)}};
398 BOOST_CHECK_EQUAL(m.size(),
exp.
size());
399 for (
const auto& kv :
exp) {
400 BOOST_CHECK_MESSAGE(m.count(kv.first) == 1,
"Could not find index " << kv.first <<
" in retrieved fixings");
401 if (m.count(kv.first) == 1) {
402 BOOST_CHECK_EQUAL(m.at(kv.first).size(), 1);
403 BOOST_CHECK_EQUAL(kv.second, m.at(kv.first).begin()->first);
408 BOOST_CHECK_THROW(p.
trades().begin()->second->instrument()->NPV(), Error);
414 BOOST_CHECK_NO_THROW(p.
trades().begin()->second->instrument()->NPV());
419 const string equityName =
"RIC:DMIWO00000GUS";
422 BOOST_REQUIRE_MESSAGE(eq,
"Could not parse equity index EQ-" + equityName);
424 BOOST_REQUIRE_MESSAGE(DividendManager::instance().hasHistory(eq->name()),
425 "Could not find index " << eq->name() <<
" in DividendManager");
426 map<Date, QuantExt::Dividend> divMap;
427 const set<QuantExt::Dividend>& dividends = eq->dividendFixings();
428 for (
const auto& d : dividends)
429 divMap[d.exDate] = d;
432 map<Date, Real>
exp = {{Date(1, Nov, 2018), 25.313}, {Date(1, Dec, 2018), 15.957}};
434 BOOST_CHECK_EQUAL(dividends.size(),
exp.
size());
435 for (
const auto& kv :
exp) {
436 BOOST_CHECK_EQUAL(divMap[kv.first].rate, kv.second);
440BOOST_AUTO_TEST_SUITE_END()
442BOOST_AUTO_TEST_SUITE_END()
static const string defaultConfiguration
Default configuration label.
const std::map< std::string, QuantLib::ext::shared_ptr< Trade > > & trades() const
Return the map tradeId -> trade.
std::map< std::string, RequiredFixings::FixingDates > fixings(const QuantLib::Date &settlementDate=QuantLib::Date()) const
QuantLib::Size size() const
Portfolio size.
void build(const QuantLib::ext::shared_ptr< EngineFactory > &, const std::string &context="unspecified", const bool emitStructuredError=true)
Call build on all trades in the portfolio, the context is included in error messages.
Today's Market Parameters.
void addMarketObject(const MarketObject o, const string &id, const map< string, string > &assignments)
void addConfiguration(const string &name, const MarketConfiguration &configuration)
void fromFile(const std::string &filename)
Currency and instrument specific conventions/defaults.
utility class to access CSV files
Market Datum Loader Implementation.
Curve configuration repository.
Logic for calculating required fixing dates on legs.
QuantLib::ext::shared_ptr< QuantExt::EquityIndex2 > parseEquityIndex(const string &s)
Convert std::string (e.g SP5) to QuantExt::EquityIndex.
Date parseDate(const string &s)
Convert std::string to QuantLib::Date.
bool parseBool(const string &s)
Convert text to bool.
Real parseReal(const string &s)
Convert text to Real.
Map text representations to QuantLib/QuantExt types.
RandomVariable exp(RandomVariable x)
void amendInflationFixingDates(std::map< std::string, RequiredFixings::FixingDates > &fixings)
void addMarketFixingDates(const Date &asof, map< string, RequiredFixings::FixingDates > &fixings, const TodaysMarketParameters &mktParams, const Period &iborLookback, const Period &oisLookback, const Period &bmaLookback, const Period &inflationLookback)
std::string to_string(const LocationInfo &l)
void applyFixings(const set< Fixing > &fixings)
Utility to write a vector of fixings in the QuantLib index manager's fixing history.
Real at(const Size i) const
vector< string > curveConfigs
BOOST_FIXTURE_TEST_CASE(testFxNotionalResettingSwapFirstCoupon, F)
BOOST_AUTO_TEST_CASE(testModifyInflationFixings)
BOOST_DATA_TEST_CASE_F(F, testTradeTypes, bdata::make(tradeTypes) *bdata::make(tradeCases) *bdata::make(bools) *bdata::make(bools), tradeType, tradeCase, includeSettlementDateFlows, enforcesTodaysHistoricFixings)
string conversion utilities
An concrete implementation of the Market class that loads todays market and builds the required curve...
A class to hold todays market configuration(s)
base trade data model and serialization