Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
conventions.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2018 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
19#include <boost/test/unit_test.hpp>
20#include <oret/toplevelfixture.hpp>
21
22#include <boost/algorithm/string/replace.hpp>
23#include <boost/make_shared.hpp>
25#include <ql/currencies/all.hpp>
26#include <ql/time/calendars/all.hpp>
27#include <ql/time/daycounters/all.hpp>
28#include <qle/calendars/ice.hpp>
29
30using namespace std;
31using namespace boost::unit_test_framework;
32using namespace QuantExt;
33using namespace QuantLib;
34using namespace ore::data;
35using boost::algorithm::replace_all_copy;
36
37namespace {
38
39void testIborIndexConvention(const string& id, const string& fixingCalendar, const string& dayCounter,
40 const Size settlementDays, const string& businessDayConvention, const bool endOfMonth,
41 const string& internalId = "") {
42 // Check construction raises no errors
43 QuantLib::ext::shared_ptr<IborIndexConvention> convention;
44 BOOST_CHECK_NO_THROW(
45 convention = QuantLib::ext::make_shared<IborIndexConvention>(id, fixingCalendar, dayCounter,
46 settlementDays, businessDayConvention, endOfMonth));
47
48 // Check object
49 BOOST_CHECK_EQUAL(convention->id(), internalId.empty() ? id : internalId);
50 BOOST_CHECK_EQUAL(convention->fixingCalendar(), fixingCalendar);
51 BOOST_CHECK_EQUAL(convention->dayCounter(), dayCounter);
52 BOOST_CHECK_EQUAL(convention->settlementDays(), settlementDays);
53 BOOST_CHECK_EQUAL(convention->businessDayConvention(), businessDayConvention);
54 BOOST_CHECK_EQUAL(convention->endOfMonth(), endOfMonth);
55}
56
57} // namespace
58
59BOOST_FIXTURE_TEST_SUITE(OREDataTestSuite, ore::test::TopLevelFixture)
60
61BOOST_AUTO_TEST_SUITE(ConventionsTests)
62
63BOOST_AUTO_TEST_CASE(testCrossCcyFixFloatSwapConventionConstruction) {
64
65 BOOST_TEST_MESSAGE("Testing cross currency fix float convention construction");
66
67 // Check construction raises no errors
68 QuantLib::ext::shared_ptr<CrossCcyFixFloatSwapConvention> convention;
69 BOOST_CHECK_NO_THROW(
70 convention = QuantLib::ext::make_shared<CrossCcyFixFloatSwapConvention>("USD-TRY-XCCY-FIX-FLOAT", "2", "US,UK,TRY", "F",
71 "TRY", "Annual", "F", "A360", "USD-LIBOR-3M"));
72
73 // Check object
74 BOOST_CHECK_EQUAL(convention->id(), "USD-TRY-XCCY-FIX-FLOAT");
75 BOOST_CHECK_EQUAL(convention->settlementDays(), 2);
76 BOOST_CHECK_EQUAL(convention->settlementCalendar(), JointCalendar(UnitedStates(UnitedStates::Settlement), UnitedKingdom(), Turkey()));
77 BOOST_CHECK_EQUAL(convention->settlementConvention(), Following);
78 BOOST_CHECK_EQUAL(convention->fixedCurrency(), TRYCurrency());
79 BOOST_CHECK_EQUAL(convention->fixedFrequency(), Annual);
80 BOOST_CHECK_EQUAL(convention->fixedConvention(), Following);
81 BOOST_CHECK_EQUAL(convention->fixedDayCounter(), Actual360());
82 BOOST_CHECK_EQUAL(convention->index()->name(), "USDLibor3M Actual/360");
83 BOOST_CHECK(!convention->eom());
84
85 // Check end of month when not default
86 BOOST_CHECK_NO_THROW(
87 convention = QuantLib::ext::make_shared<CrossCcyFixFloatSwapConvention>(
88 "USD-TRY-XCCY-FIX-FLOAT", "2", "US,UK,TRY", "F", "TRY", "Annual", "F", "A360", "USD-LIBOR-3M", "false"));
89 BOOST_CHECK(!convention->eom());
90
91 BOOST_CHECK_NO_THROW(
92 convention = QuantLib::ext::make_shared<CrossCcyFixFloatSwapConvention>(
93 "USD-TRY-XCCY-FIX-FLOAT", "2", "US,UK,TRY", "F", "TRY", "Annual", "F", "A360", "USD-LIBOR-3M", "true"));
94 BOOST_CHECK(convention->eom());
95}
96
97BOOST_AUTO_TEST_CASE(testCrossCcyFixFloatSwapConventionFromXml) {
98
99 BOOST_TEST_MESSAGE("Testing parsing of cross currency fix float convention from XML");
100
101 // XML string convention
102 string xml;
103 xml.append("<CrossCurrencyFixFloat>");
104 xml.append(" <Id>USD-TRY-XCCY-FIX-FLOAT</Id>");
105 xml.append(" <SettlementDays>2</SettlementDays>");
106 xml.append(" <SettlementCalendar>US,UK,TRY</SettlementCalendar>");
107 xml.append(" <SettlementConvention>F</SettlementConvention>");
108 xml.append(" <FixedCurrency>TRY</FixedCurrency>");
109 xml.append(" <FixedFrequency>Annual</FixedFrequency>");
110 xml.append(" <FixedConvention>F</FixedConvention>");
111 xml.append(" <FixedDayCounter>A360</FixedDayCounter>");
112 xml.append(" <Index>USD-LIBOR-3M</Index>");
113 xml.append("</CrossCurrencyFixFloat>");
114
115 // Parse convention from XML
116 QuantLib::ext::shared_ptr<CrossCcyFixFloatSwapConvention> convention = QuantLib::ext::make_shared<CrossCcyFixFloatSwapConvention>();
117 BOOST_CHECK_NO_THROW(convention->fromXMLString(xml));
118
119 // Check parsed object
120 BOOST_CHECK_EQUAL(convention->id(), "USD-TRY-XCCY-FIX-FLOAT");
121 BOOST_CHECK_EQUAL(convention->settlementDays(), 2);
122 BOOST_CHECK_EQUAL(convention->settlementCalendar(), JointCalendar(UnitedStates(UnitedStates::Settlement), UnitedKingdom(), Turkey()));
123 BOOST_CHECK_EQUAL(convention->settlementConvention(), Following);
124 BOOST_CHECK_EQUAL(convention->fixedCurrency(), TRYCurrency());
125 BOOST_CHECK_EQUAL(convention->fixedFrequency(), Annual);
126 BOOST_CHECK_EQUAL(convention->fixedConvention(), Following);
127 BOOST_CHECK_EQUAL(convention->fixedDayCounter(), Actual360());
128 BOOST_CHECK_EQUAL(convention->index()->name(), "USDLibor3M Actual/360");
129 BOOST_CHECK(!convention->eom());
130
131 // Check end of month also
132 xml = replace_all_copy(xml, "</CrossCurrencyFixFloat>", "<EOM>false</EOM></CrossCurrencyFixFloat>");
133 BOOST_TEST_MESSAGE("xml is: " << xml);
134 convention->fromXMLString(xml);
135 BOOST_CHECK(!convention->eom());
136
137 xml = replace_all_copy(xml, "<EOM>false</EOM>", "<EOM>true</EOM>");
138 convention->fromXMLString(xml);
139 BOOST_CHECK(convention->eom());
140}
141
142BOOST_AUTO_TEST_CASE(testCrossCcyFixFloatSwapConventionToXml) {
143
144 BOOST_TEST_MESSAGE("Testing writing of cross currency fix float convention to XML");
145
146 // Construct the convention
147 QuantLib::ext::shared_ptr<CrossCcyFixFloatSwapConvention> convention;
148 BOOST_CHECK_NO_THROW(
149 convention = QuantLib::ext::make_shared<CrossCcyFixFloatSwapConvention>("USD-TRY-XCCY-FIX-FLOAT", "2", "US,UK,TRY", "F",
150 "TRY", "Annual", "F", "A360", "USD-LIBOR-3M"));
151
152 // Write the convention to a string
153 string xml = convention->toXMLString();
154
155 // Read the convention back from the string using fromXMLString
156 QuantLib::ext::shared_ptr<CrossCcyFixFloatSwapConvention> readConvention =
157 QuantLib::ext::make_shared<CrossCcyFixFloatSwapConvention>();
158 BOOST_CHECK_NO_THROW(readConvention->fromXMLString(xml));
159
160 // The read convention should equal the original convention
161 BOOST_CHECK_EQUAL(convention->id(), readConvention->id());
162 BOOST_CHECK_EQUAL(convention->settlementDays(), readConvention->settlementDays());
163 BOOST_CHECK_EQUAL(convention->settlementCalendar(), readConvention->settlementCalendar());
164 BOOST_CHECK_EQUAL(convention->settlementConvention(), readConvention->settlementConvention());
165 BOOST_CHECK_EQUAL(convention->fixedCurrency(), readConvention->fixedCurrency());
166 BOOST_CHECK_EQUAL(convention->fixedFrequency(), readConvention->fixedFrequency());
167 BOOST_CHECK_EQUAL(convention->fixedConvention(), readConvention->fixedConvention());
168 BOOST_CHECK_EQUAL(convention->fixedDayCounter(), readConvention->fixedDayCounter());
169 BOOST_CHECK_EQUAL(convention->index()->name(), readConvention->index()->name());
170 BOOST_CHECK_EQUAL(convention->eom(), readConvention->eom());
171}
172
173BOOST_AUTO_TEST_CASE(testDayOfMonthCommodityFutureConventionConstruction) {
174
175 BOOST_TEST_MESSAGE("Testing commodity future convention construction with day of month based anchor day");
176
177 // Check construction raises no errors
179 set<PE> prohibitedExpiries;
180 prohibitedExpiries.insert(PE(Date(31, Dec, 2020)));
181 prohibitedExpiries.insert(PE(Date(31, Dec, 2021)));
182 prohibitedExpiries.insert(PE(Date(30, Dec, 2022)));
183
184 QuantLib::ext::shared_ptr<CommodityFutureConvention> convention;
186 CommodityFutureConvention::CalendarDaysBefore optionExpiryBusinessDayBefore("3");
187 CommodityFutureConvention::OptionExpiryAnchorDateRule optionExpiryDateRule(optionExpiryBusinessDayBefore);
188 BOOST_CHECK_NO_THROW(convention = QuantLib::ext::make_shared<CommodityFutureConvention>(
189 "ICE:B", dayOfMonth, "Monthly", "ICE_FuturesEU", "UK", 2, "Jan", "0", "Preceding", true,
190 false, optionExpiryDateRule, prohibitedExpiries));
191
192 // Check object
193 BOOST_CHECK_EQUAL(convention->id(), "ICE:B");
194 BOOST_CHECK(convention->anchorType() == CommodityFutureConvention::AnchorType::DayOfMonth);
195 BOOST_CHECK_EQUAL(convention->dayOfMonth(), 31);
196 BOOST_CHECK_EQUAL(convention->contractFrequency(), Monthly);
197 BOOST_CHECK_EQUAL(convention->calendar(), ICE(ICE::FuturesEU));
198 BOOST_CHECK_EQUAL(convention->expiryCalendar(), UnitedKingdom());
199 BOOST_CHECK_EQUAL(convention->expiryMonthLag(), 2);
200 BOOST_CHECK_EQUAL(convention->oneContractMonth(), Jan);
201 BOOST_CHECK_EQUAL(convention->offsetDays(), 0);
202 BOOST_CHECK_EQUAL(convention->businessDayConvention(), Preceding);
203 BOOST_CHECK(convention->adjustBeforeOffset());
204 BOOST_CHECK(!convention->isAveraging());
205 BOOST_CHECK_EQUAL(convention->optionExpiryOffset(), 3);
206
207 set<Date> expExpiries{Date(31, Dec, 2020), Date(31, Dec, 2021), Date(30, Dec, 2022)};
208 const auto& proExps = convention->prohibitedExpiries();
209 BOOST_CHECK_EQUAL(proExps.size(), expExpiries.size());
210 for (const Date& expExpiry : expExpiries) {
211 auto it = proExps.find(PE(expExpiry));
212 BOOST_REQUIRE_MESSAGE(it != proExps.end(), "Expected date " << io::iso_date(expExpiry) <<
213 " not found in Prohibited Expiries");
214 BOOST_CHECK_EQUAL(it->expiry(), expExpiry);
215 BOOST_CHECK(it->forFuture());
216 BOOST_CHECK_EQUAL(it->futureBdc(), Preceding);
217 BOOST_CHECK(it->forOption());
218 BOOST_CHECK_EQUAL(it->optionBdc(), Preceding);
219 }
220}
221
222BOOST_AUTO_TEST_CASE(testDayOfMonthCommodityFutureConventionFromXml) {
223
224 BOOST_TEST_MESSAGE("Testing parsing of commodity future convention with day of month based anchor day from XML");
225
226 // XML string convention
227 string xml;
228 xml.append("<CommodityFuture>");
229 xml.append(" <Id>ICE:B</Id>");
230 xml.append(" <AnchorDay>");
231 xml.append(" <DayOfMonth>31</DayOfMonth>");
232 xml.append(" </AnchorDay>");
233 xml.append(" <ContractFrequency>Monthly</ContractFrequency>");
234 xml.append(" <Calendar>ICE_FuturesEU</Calendar>");
235 xml.append(" <ExpiryCalendar>UK</ExpiryCalendar>");
236 xml.append(" <ExpiryMonthLag>2</ExpiryMonthLag>");
237 xml.append(" <IsAveraging>false</IsAveraging>");
238 xml.append(" <OptionExpiryOffset>3</OptionExpiryOffset>");
239 xml.append(" <ProhibitedExpiries>");
240 xml.append(" <Dates>");
241 xml.append(" <Date>2020-12-31</Date>");
242 xml.append(" <Date>2021-12-31</Date>");
243 xml.append(" <Date>2022-12-30</Date>");
244 xml.append(" </Dates>");
245 xml.append(" </ProhibitedExpiries>");
246 xml.append("</CommodityFuture>");
247
248 // Parse convention from XML
249 QuantLib::ext::shared_ptr<CommodityFutureConvention> convention = QuantLib::ext::make_shared<CommodityFutureConvention>();
250 BOOST_CHECK_NO_THROW(convention->fromXMLString(xml));
251
252 // Check parsed object
253 BOOST_CHECK_EQUAL(convention->id(), "ICE:B");
254 BOOST_CHECK(convention->anchorType() == CommodityFutureConvention::AnchorType::DayOfMonth);
255 BOOST_CHECK_EQUAL(convention->dayOfMonth(), 31);
256 BOOST_CHECK_EQUAL(convention->contractFrequency(), Monthly);
257 BOOST_CHECK_EQUAL(convention->calendar(), ICE(ICE::FuturesEU));
258 BOOST_CHECK_EQUAL(convention->expiryCalendar(), UnitedKingdom());
259 BOOST_CHECK_EQUAL(convention->expiryMonthLag(), 2);
260 BOOST_CHECK_EQUAL(convention->oneContractMonth(), Jan);
261 BOOST_CHECK_EQUAL(convention->offsetDays(), 0);
262 BOOST_CHECK_EQUAL(convention->businessDayConvention(), Preceding);
263 BOOST_CHECK(convention->adjustBeforeOffset());
264 BOOST_CHECK(!convention->isAveraging());
265 BOOST_CHECK_EQUAL(convention->optionExpiryOffset(), 3);
266
268 set<Date> expExpiries{ Date(31, Dec, 2020), Date(31, Dec, 2021), Date(30, Dec, 2022) };
269 const auto& proExps = convention->prohibitedExpiries();
270 BOOST_CHECK_EQUAL(proExps.size(), expExpiries.size());
271 for (const Date& expExpiry : expExpiries) {
272 auto it = proExps.find(PE(expExpiry));
273 BOOST_REQUIRE_MESSAGE(it != proExps.end(), "Expected date " << io::iso_date(expExpiry) <<
274 " not found in Prohibited Expiries");
275 BOOST_CHECK_EQUAL(it->expiry(), expExpiry);
276 BOOST_CHECK(it->forFuture());
277 BOOST_CHECK_EQUAL(it->futureBdc(), Preceding);
278 BOOST_CHECK(it->forOption());
279 BOOST_CHECK_EQUAL(it->optionBdc(), Preceding);
280 }
281}
282
283BOOST_AUTO_TEST_CASE(testDayOfMonthCommodityFutureConventionToXml) {
284
285 BOOST_TEST_MESSAGE("Testing writing of commodity future convention with day of month based anchor day to XML");
286
287 // Construct the convention
289 set<PE> prohibitedExpiries;
290 prohibitedExpiries.insert(PE(Date(31, Dec, 2020), true, Following, false, ModifiedFollowing));
291 prohibitedExpiries.insert(PE(Date(31, Dec, 2021), false, Preceding, true, ModifiedPreceding));
292 prohibitedExpiries.insert(PE(Date(31, Dec, 2021), false, Following));
293
294 QuantLib::ext::shared_ptr<CommodityFutureConvention> convention;
296 CommodityFutureConvention::CalendarDaysBefore optionExpiryBusinessDayBefore("3");
297 CommodityFutureConvention::OptionExpiryAnchorDateRule optionExpiryDateRule(optionExpiryBusinessDayBefore);
298 BOOST_CHECK_NO_THROW(convention = QuantLib::ext::make_shared<CommodityFutureConvention>(
299 "ICE:B", dayOfMonth, "Monthly", "ICE_FuturesEU", "UK", 2, "Jan", "0", "Preceding", true,
300 false, optionExpiryDateRule, prohibitedExpiries));
301
302 // Write the convention to a string
303 string xml = convention->toXMLString();
304
305 // Read the convention back from the string using fromXMLString
306 QuantLib::ext::shared_ptr<CommodityFutureConvention> readConvention = QuantLib::ext::make_shared<CommodityFutureConvention>();
307 BOOST_CHECK_NO_THROW(readConvention->fromXMLString(xml));
308
309 // The read convention should equal the original convention
310 BOOST_CHECK_EQUAL(convention->id(), readConvention->id());
311 BOOST_CHECK(convention->anchorType() == readConvention->anchorType());
312 BOOST_CHECK_EQUAL(convention->dayOfMonth(), readConvention->dayOfMonth());
313 BOOST_CHECK_EQUAL(convention->contractFrequency(), readConvention->contractFrequency());
314 BOOST_CHECK_EQUAL(convention->calendar(), readConvention->calendar());
315 BOOST_CHECK_EQUAL(convention->expiryMonthLag(), readConvention->expiryMonthLag());
316 BOOST_CHECK_EQUAL(convention->oneContractMonth(), readConvention->oneContractMonth());
317 BOOST_CHECK_EQUAL(convention->offsetDays(), readConvention->offsetDays());
318 BOOST_CHECK_EQUAL(convention->businessDayConvention(), readConvention->businessDayConvention());
319 BOOST_CHECK_EQUAL(convention->adjustBeforeOffset(), readConvention->adjustBeforeOffset());
320 BOOST_CHECK_EQUAL(convention->isAveraging(), readConvention->isAveraging());
321
322 const auto& proExps = convention->prohibitedExpiries();
323 const auto& readProExps = readConvention->prohibitedExpiries();
324 BOOST_CHECK_EQUAL(proExps.size(), readProExps.size());
325 for (const auto& pe : proExps) {
326 auto it = readProExps.find(pe);
327 BOOST_REQUIRE_MESSAGE(it != readProExps.end(), "Expected date " << io::iso_date(pe.expiry()) <<
328 " not found in Prohibited Expiries");
329 BOOST_CHECK_EQUAL(it->expiry(), pe.expiry());
330 BOOST_CHECK_EQUAL(it->forFuture(), pe.forFuture());
331 BOOST_CHECK_EQUAL(it->futureBdc(), pe.futureBdc());
332 BOOST_CHECK_EQUAL(it->forOption(), pe.forOption());
333 BOOST_CHECK_EQUAL(it->optionBdc(), pe.optionBdc());
334 }
335}
336
337BOOST_AUTO_TEST_CASE(testIborConventionConstructionWithTenor) {
338
339 BOOST_TEST_MESSAGE("Testing Ibor Index convention construction with Tenor");
340 testIborIndexConvention("AED-EIBOR-3M", "AED", "ACT/360", 2, "MF", false);
341}
342
343BOOST_AUTO_TEST_CASE(testIborConventionConstructionWithoutTenor) {
344
345 BOOST_TEST_MESSAGE("Testing Ibor Index convention construction with Tenor");
346 testIborIndexConvention("AED-EIBOR", "AED", "ACT/360", 2, "MF", false);
347}
348
349BOOST_AUTO_TEST_CASE(testIborConventionConstruction7D) {
350 BOOST_TEST_MESSAGE("Testing Ibor Index convention construction with Tenor");
351 testIborIndexConvention("CNY-REPO-7D", "CNY", "A365F", 2, "MF", true, "CNY-REPO-1W");
352}
353
354BOOST_AUTO_TEST_SUITE_END()
355
356BOOST_AUTO_TEST_SUITE_END()
Class to hold prohibited expiry information.
Currency and instrument specific conventions/defaults.
Classes to differentiate constructors below.
BOOST_AUTO_TEST_CASE(testCrossCcyFixFloatSwapConventionConstruction)
Definition: conventions.cpp:63