Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
ccyswapwithresets.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2016 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/make_shared.hpp>
20#include <boost/test/unit_test.hpp>
28#include <oret/toplevelfixture.hpp>
29#include <ql/termstructures/yield/discountcurve.hpp>
30#include <ql/time/calendars/target.hpp>
31#include <ql/time/calendars/unitedstates.hpp>
32#include <ql/time/daycounters/actual360.hpp>
33
34using namespace QuantLib;
35using namespace boost::unit_test_framework;
36using namespace ore::data;
37using std::vector;
38
39namespace {
40
41class TestMarket : public MarketImpl {
42public:
43 TestMarket() : MarketImpl(false) {
44 // valuation date
45 asof_ = Date(22, Aug, 2016);
46
47 QuantLib::ext::shared_ptr<ore::data::Conventions> conventions = QuantLib::ext::make_shared<Conventions>();
48 conventions->add(QuantLib::ext::make_shared<ore::data::FXConvention>("EUR-USD-FX", "0", "EUR", "USD", "10000",
49 "USD,EUR", "true"));
50 InstrumentConventions::instance().setConventions(conventions);
51
52 // build vectors with dates and discount factors
53 vector<Date> datesEUR = {asof_,
54 asof_ + 6 * Months,
55 asof_ + 7 * Months,
56 asof_ + 8 * Months,
57 asof_ + 9 * Months,
58 asof_ + 10 * Months,
59 asof_ + 11 * Months,
60 asof_ + 12 * Months,
61 asof_ + 13 * Months,
62 asof_ + 14 * Months,
63 asof_ + 15 * Months,
64 asof_ + 16 * Months,
65 asof_ + 17 * Months,
66 asof_ + 18 * Months,
67 asof_ + 2 * Years,
68 asof_ + 3 * Years,
69 asof_ + 4 * Years,
70 asof_ + 5 * Years,
71 asof_ + 6 * Years};
72 vector<Date> datesUSD = {asof_,
73 asof_ + 3 * Months,
74 asof_ + 4 * Months,
75 asof_ + 7 * Months,
76 asof_ + 10 * Months,
77 asof_ + 13 * Months,
78 asof_ + 16 * Months,
79 asof_ + 19 * Months,
80 asof_ + 2 * Years,
81 asof_ + 3 * Years,
82 asof_ + 4 * Years,
83 asof_ + 5 * Years,
84 asof_ + 6 * Years};
85 vector<DiscountFactor> dfsEUR = {1.0, 1.000972, 1.001138, 1.001309, 1.001452, 1.001663, 1.001826,
86 1.002005, 1.002196, 1.002369, 1.002554, 1.00275, 1.002918, 1.003114,
87 1.004134, 1.006005, 1.007114, 1.006773, 1.004282};
88 vector<DiscountFactor> dfsUSD = {1.0, 0.997872, 0.997147, 0.99499, 0.992416, 0.989948, 0.987405,
89 0.984774, 0.980358, 0.96908, 0.95704, 0.944041, 0.93004};
90
91 // build discount
92 yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::Discount, "EUR")] =
93 intDiscCurve(datesEUR, dfsEUR, Actual360(), TARGET());
94 yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::Discount, "USD")] =
95 intDiscCurve(datesUSD, dfsUSD, Actual360(), UnitedStates(UnitedStates::Settlement));
96
97 // build ibor index
98 Handle<IborIndex> hEUR(parseIborIndex("EUR-EURIBOR-6M", intDiscCurve(datesEUR, dfsEUR, Actual360(), TARGET())));
99 iborIndices_[make_pair(Market::defaultConfiguration, "EUR-EURIBOR-6M")] = hEUR;
100
101 // add Eurib 6M fixing
102 hEUR->addFixing(Date(18, Aug, 2016), -0.00191);
103 Handle<IborIndex> hUSD(
104 parseIborIndex("USD-LIBOR-3M", intDiscCurve(datesUSD, dfsUSD, Actual360(), UnitedStates(UnitedStates::Settlement))));
105 iborIndices_[make_pair(Market::defaultConfiguration, "USD-LIBOR-3M")] = hUSD;
106
107 // add Libor 3M fixing
108 hUSD->addFixing(Date(18, Aug, 2016), 0.00811);
109
110 // add fx rates
111 std::map<std::string, Handle<Quote>> quotes;
112 quotes["EURUSD"] = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(1.1306));
113 fx_ = QuantLib::ext::make_shared<FXTriangulation>(quotes);
114 }
115
116private:
117 Handle<YieldTermStructure> intDiscCurve(vector<Date> dates, vector<DiscountFactor> dfs, DayCounter dc,
118 Calendar cal) {
119 QuantLib::ext::shared_ptr<YieldTermStructure> idc(
120 new QuantLib::InterpolatedDiscountCurve<LogLinear>(dates, dfs, dc, cal));
121 return Handle<YieldTermStructure>(idc);
122 }
123};
124} // namespace
125
126BOOST_FIXTURE_TEST_SUITE(OREDataTestSuite, ore::test::TopLevelFixture)
127
128BOOST_AUTO_TEST_SUITE(CcySwapWithResetsTest)
129
130// Ccy Swap with notional resets test, example from Bloomberg
131BOOST_AUTO_TEST_CASE(testCcySwapWithResetsPrice) {
132
133 BOOST_TEST_MESSAGE("Testing CcySwapWithResets Price...");
134
135 // Bloomberg CCYswap and CCYswapReset prices (in USD)
136 Real npvCCYswap = -349.69;
137 Real npvCCYswapReset = 0;
138
139 // build market
140 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>();
141 Settings::instance().evaluationDate() = market->asofDate();
142
143 // test asof date
144 Date today = market->asofDate();
145 BOOST_CHECK_EQUAL(today, Date(22, Aug, 2016));
146
147 // Test if EUR discount curve is empty
148 Handle<YieldTermStructure> dts = market->discountCurve("EUR");
149 QL_REQUIRE(!dts.empty(), "EUR discount curve not found");
150
151 // Test EUR discount(today+3*Years)) against base value
152 BOOST_CHECK_CLOSE(market->discountCurve("EUR")->discount(today + 3 * Years), 1.006005, 0.0001);
153
154 // Test USD discount(today+3*Years)) against base value
155 BOOST_CHECK_CLOSE(market->discountCurve("USD")->discount(today + 3 * Years), 0.96908, 0.0001);
156
157 // Test EURUSD fx spot object against base value
158 BOOST_CHECK_EQUAL(market->fxSpot("EURUSD")->value(), 1.1306);
159
160 // envelope
161 Envelope env("CP");
162
163 // Start/End date
164 Date startDate = today;
165 Date endDate = today + 5 * Years;
166
167 // date 2 string
168 std::ostringstream oss;
169 oss << io::iso_date(startDate);
170 string start(oss.str());
171 oss.str("");
172 oss.clear();
173 oss << io::iso_date(endDate);
174 string end(oss.str());
175
176 // Schedules
177 string conv = "MF";
178 string rule = "Forward";
179 ScheduleData scheduleEUR(ScheduleRules(start, end, "6M", "TARGET", conv, conv, rule));
180 ScheduleData scheduleUSD(ScheduleRules(start, end, "3M", "US", conv, conv, rule));
181
182 // EUR Leg without and with Notional resets
183 bool isPayerEUR = true;
184 string indexEUR = "EUR-EURIBOR-6M";
185 bool isInArrears = false;
186 int days = 2;
187 vector<Real> spreadEUR(1, 0.000261);
188 string dc = "ACT/360";
189 vector<Real> notionalEUR(1, 8833141.95);
190 string paymentConvention = "F";
191 bool notionalInitialXNL = true;
192 bool notionalFinalXNL = true;
193 bool notionalAmortizingXNL = false;
194 string foreignCCY = "USD";
195 Real foreignAmount = 10000000;
196 string fxIndex = "FX-ECB-EUR-USD";
197 auto legdataEUR = QuantLib::ext::make_shared<FloatingLegData>(indexEUR, days, isInArrears, spreadEUR);
198 LegData legEUR1(legdataEUR, isPayerEUR, "EUR", scheduleEUR, dc, notionalEUR, vector<string>(), paymentConvention,
199 notionalInitialXNL, notionalFinalXNL, notionalAmortizingXNL, notionalFinalXNL, foreignCCY,
200 foreignAmount, fxIndex);
201 LegData legEUR2(legdataEUR, isPayerEUR, "EUR", scheduleEUR, dc, notionalEUR, vector<string>(), paymentConvention,
202 notionalInitialXNL, notionalFinalXNL, notionalAmortizingXNL, false, foreignCCY, foreignAmount,
203 fxIndex);
204
205 // USD Leg without notional resets
206 bool isPayerUSD = false;
207 string indexUSD = "USD-LIBOR-3M";
208 vector<Real> spreadUSD(1, 0);
209 vector<Real> notionalUSD(1, 10000000);
210 auto legdataUSD = QuantLib::ext::make_shared<FloatingLegData>(indexUSD, days, isInArrears, spreadUSD);
211 LegData legUSD(legdataUSD, isPayerUSD, "USD", scheduleUSD, dc, notionalUSD, vector<string>(), paymentConvention,
212 notionalInitialXNL, notionalFinalXNL, notionalAmortizingXNL);
213
214 // Build swap trades
215 QuantLib::ext::shared_ptr<Trade> swap1(new ore::data::Swap(env, legUSD, legEUR1));
216 QuantLib::ext::shared_ptr<Trade> swap2(new ore::data::Swap(env, legUSD, legEUR2));
217
218 // engine data and factory
219 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
220 engineData->model("CrossCurrencySwap") = "DiscountedCashflows";
221 engineData->engine("CrossCurrencySwap") = "DiscountingCrossCurrencySwapEngine";
222 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
223
224 // build swaps and portfolio
225 QuantLib::ext::shared_ptr<Portfolio> portfolio(new Portfolio());
226 swap1->id() = "XCCY_Swap_EUR_USD";
227 swap2->id() = "XCCY_Swap_EUR_USD_RESET";
228
229 portfolio->add(swap1);
230 portfolio->add(swap2);
231 portfolio->build(engineFactory);
232
233 // check CCYswap NPV
234 BOOST_TEST_MESSAGE("CcySwap Price = " << swap1->instrument()->NPV() << " " << swap1->npvCurrency()
235 << ". BBG Price = " << npvCCYswap << " USD");
236 // check if difference between ore and bbg NPV does not exceed 250 USD (on 10M notional)
237 BOOST_CHECK_CLOSE(std::abs(swap1->instrument()->NPV() - npvCCYswap), 250, 5);
238
239 // check CCYswapReset NPV
240 BOOST_TEST_MESSAGE("CcySwapReset Price = " << swap2->instrument()->NPV() << " " << swap2->npvCurrency()
241 << ". BBG Price = " << npvCCYswapReset << " USD");
242 // check if difference between ore and bbg NPV does not exceed 250 USD (on 10M notional)
243 BOOST_CHECK_CLOSE(std::abs(swap2->instrument()->NPV() - npvCCYswapReset), 250, 5);
244
245 // check if sum(XNL) on resetting leg is zero
246 Real sumXNL = 0;
247 const vector<Leg>& legs = swap2->legs();
248 for (Size i = 3; i < legs.size(); i++) {
249 const QuantLib::Leg& leg = legs[i];
250 for (Size j = 0; j < leg.size(); j++) {
251 QuantLib::ext::shared_ptr<QuantLib::CashFlow> ptrFlow = leg[j];
252 sumXNL += ptrFlow->amount();
253 }
254 }
255 BOOST_CHECK_EQUAL(sumXNL, 0);
256}
257
258BOOST_AUTO_TEST_SUITE_END()
259
260BOOST_AUTO_TEST_SUITE_END()
Engine builder for Swaps.
BOOST_AUTO_TEST_CASE(testCcySwapWithResetsPrice)
Serializable object holding generic trade data, reporting dimensions.
Definition: envelope.hpp:51
Serializable object holding leg data.
Definition: legdata.hpp:844
static const string defaultConfiguration
Default configuration label.
Definition: market.hpp:296
Market Implementation.
Definition: marketimpl.hpp:53
QuantLib::ext::shared_ptr< FXTriangulation > fx_
Definition: marketimpl.hpp:206
map< tuple< string, YieldCurveType, string >, Handle< YieldTermStructure > > yieldCurves_
Definition: marketimpl.hpp:208
map< pair< string, string >, Handle< IborIndex > > iborIndices_
Definition: marketimpl.hpp:209
Serializable portfolio.
Definition: portfolio.hpp:43
Serializable schedule data.
Definition: schedule.hpp:202
Serializable object holding schedule Rules data.
Definition: schedule.hpp:37
Serializable Swap, Single and Cross Currency.
Definition: swap.hpp:36
A class to hold pricing engine parameters.
QuantLib::ext::shared_ptr< IborIndex > parseIborIndex(const string &s, const Handle< YieldTermStructure > &h)
Convert std::string to QuantLib::IborIndex.
Map text representations to QuantLib/QuantExt types.
Classes and functions for log message handling.
An implementation of the Market class that stores the required objects in maps.
Portfolio class.
Swap trade data model and serialization.