Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
equityswap.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/make_shared.hpp>
20#include <boost/test/unit_test.hpp>
27#include <oret/toplevelfixture.hpp>
28#include <ql/cashflows/iborcoupon.hpp>
29#include <ql/termstructures/yield/discountcurve.hpp>
30#include <ql/termstructures/yield/flatforward.hpp>
31#include <ql/time/calendars/unitedstates.hpp>
32#include <ql/time/daycounters/actual365fixed.hpp>
33#include <ql/time/daycounters/actualactual.hpp>
36
37using namespace QuantLib;
38using namespace QuantExt;
39using namespace boost::unit_test_framework;
40using namespace ore::data;
41using std::vector;
42
43namespace {
44
45class TestMarket : public MarketImpl {
46public:
47 TestMarket() : MarketImpl(false) {
48 // valuation date
49 asof_ = Date(18, July, 2016);
50
51 spotSP5 = Handle<Quote>(QuantLib::ext::shared_ptr<SimpleQuote>(new SimpleQuote(2100)));
52 forecastSP5 = flatRateYts(0.03);
53 dividendSP5 = flatRateYts(0.01);
54
55 // build vectors with dates and discount factors for GBP
56 yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::Discount, "USD")] = forecastSP5;
57 yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::Yield, "SP5")] = forecastSP5;
58 yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::EquityDividend, "SP5")] = dividendSP5;
59
60 // build USD Libor index
61 hUSD = Handle<IborIndex>(parseIborIndex("USD-LIBOR-3M", flatRateYts(0.035)));
62 iborIndices_[make_pair(Market::defaultConfiguration, "USD-LIBOR-3M")] = hUSD;
63
64 // build SP5 Equity Curve
65 hSP5 = Handle<EquityIndex2>(QuantLib::ext::shared_ptr<EquityIndex2>(
66 new EquityIndex2("SP5", UnitedStates(UnitedStates::Settlement), parseCurrency("USD"), spotSP5, forecastSP5, dividendSP5)));
67 equityCurves_[make_pair(Market::defaultConfiguration, "SP5")] = hSP5;
68 IndexNameTranslator::instance().add(hSP5->name(), "EQ-" + hSP5->name());
69
70 // add fixings
71 hUSD->addFixing(Date(14, July, 2016), 0.035);
72 hUSD->addFixing(Date(18, October, 2016), 0.037);
73 hSP5->addFixing(Date(18, October, 2016), 2100.0);
74 }
75
76 Handle<IborIndex> hUSD;
77 Handle<EquityIndex2> hSP5;
78 Handle<Quote> spotSP5;
79 Handle<YieldTermStructure> forecastSP5;
80 Handle<YieldTermStructure> dividendSP5;
81
82private:
83 Handle<YieldTermStructure> flatRateYts(Real forward) {
84 QuantLib::ext::shared_ptr<YieldTermStructure> yts(new FlatForward(0, UnitedStates(UnitedStates::Settlement), forward, ActualActual(ActualActual::ISDA)));
85 return Handle<YieldTermStructure>(yts);
86 }
87};
88
89struct CommonVars {
90 // global data
91 string ccy;
92 bool isPayer;
93 string start;
94 string end;
95 string floattenor;
96 string eqtenor;
97 string calStr;
98 string conv;
99 string rule;
100 Natural days;
101 string fixDC;
102 string index;
103 string eqName;
104 Real dividendFactor;
105 Real initialPrice;
106 Integer settlementDays;
107 bool isinarrears;
108 vector<double> notionals;
109 vector<double> spread;
110
111 // utilities
112 QuantLib::ext::shared_ptr<ore::data::Swap> makeEquitySwap(EquityReturnType returnType, bool notionalReset = false) {
113 ScheduleData floatSchedule(ScheduleRules(start, end, floattenor, calStr, conv, conv, rule));
114 ScheduleData eqSchedule(ScheduleRules(start, end, eqtenor, calStr, conv, conv, rule));
115
116 // build EquitySwap
117 LegData floatLegData(QuantLib::ext::make_shared<FloatingLegData>(index, days, isinarrears, spread), !isPayer, ccy,
118 floatSchedule, fixDC, notionals);
119 LegData eqLegData(QuantLib::ext::make_shared<EquityLegData>(returnType, dividendFactor, EquityUnderlying(eqName),
120 initialPrice, notionalReset, settlementDays),
121 isPayer, ccy, eqSchedule, fixDC, notionals);
122
123 Envelope env("CP1");
124 QuantLib::ext::shared_ptr<ore::data::Swap> swap(new ore::data::Swap(env, eqLegData, floatLegData));
125 return swap;
126 }
127
128 QuantLib::ext::shared_ptr<QuantLib::Swap> qlEquitySwap(EquityReturnType returnType, bool notionalReset = false) {
129
130 QuantLib::ext::shared_ptr<TestMarket> market = QuantLib::ext::make_shared<TestMarket>();
131 // check Equity swap NPV against pure QL pricing
132 Schedule floatSchedule(parseDate(start), parseDate(end), parsePeriod(floattenor), parseCalendar(calStr),
134 parseDateGenerationRule(rule), false);
135 Schedule eqSchedule(parseDate(start), parseDate(end), parsePeriod(eqtenor), parseCalendar(calStr),
137 parseDateGenerationRule(rule), false);
138 Leg floatLeg = IborLeg(floatSchedule, *market->hUSD)
139 .withNotionals(notionals)
140 .withFixingDays(days)
141 .withSpreads(spread)
142 .withPaymentDayCounter(parseDayCounter(fixDC))
143 .withPaymentAdjustment(parseBusinessDayConvention(conv));
144 Leg eqLeg = EquityLeg(eqSchedule, *market->equityCurve("SP5"))
145 .withNotionals(notionals)
148 .withReturnType(returnType)
149 .withInitialPrice(initialPrice)
150 .withNotionalReset(notionalReset);
151
152 QuantLib::ext::shared_ptr<QuantLib::Swap> swap(new QuantLib::Swap(floatLeg, eqLeg));
153 return swap;
154 }
155
156 CommonVars() {
157 ccy = "USD";
158 isPayer = false;
159 start = "20160718";
160 end = "20210718";
161 floattenor = "3M";
162 eqtenor = "3M";
163 calStr = "USD";
164 conv = "MF";
165 rule = "Forward";
166 fixDC = "ACT/ACT";
167 index = "USD-LIBOR-3M";
168 eqName = "SP5";
169 dividendFactor = 1.0;
170 initialPrice = 2100.0;
171 settlementDays = 0;
172 days = 0;
173 isinarrears = false;
174 notionals.push_back(10000000);
175 spread.push_back(0.0);
176 }
177};
178
179} // namespace
180
181BOOST_FIXTURE_TEST_SUITE(OREDataTestSuite, ore::test::TopLevelFixture)
182
183BOOST_AUTO_TEST_SUITE(EquitySwapTests)
184
185BOOST_AUTO_TEST_CASE(testEquitySwapPriceReturn) {
186
187 BOOST_TEST_MESSAGE("Testing Equity Swap Price Return...");
188
189 // build market
190 QuantLib::ext::shared_ptr<TestMarket> market = QuantLib::ext::make_shared<TestMarket>();
191 Date today = market->asofDate();
192 Settings::instance().evaluationDate() = today;
193
194 CommonVars vars;
195 QuantLib::ext::shared_ptr<ore::data::Swap> eqSwap = vars.makeEquitySwap(EquityReturnType::Price);
196
197 // engine data and factory
198 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
199 engineData->model("Swap") = "DiscountedCashflows";
200 engineData->engine("Swap") = "DiscountingSwapEngine";
201 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
202
203 // build swaps and portfolio
204 QuantLib::ext::shared_ptr<Portfolio> portfolio(new Portfolio());
205 eqSwap->id() = "EQ_Swap";
206
207 portfolio->add(eqSwap);
208 portfolio->build(engineFactory);
209
210 QuantLib::ext::shared_ptr<QuantLib::Swap> qlSwap = vars.qlEquitySwap(EquityReturnType::Price);
211
212 auto dscEngine = QuantLib::ext::make_shared<DiscountingSwapEngine>(market->discountCurve("USD"));
213 qlSwap->setPricingEngine(dscEngine);
214 BOOST_TEST_MESSAGE("Leg 1 NPV: ORE = "
215 << QuantLib::ext::static_pointer_cast<QuantLib::Swap>(eqSwap->instrument()->qlInstrument())->legNPV(0)
216 << " QL = " << qlSwap->legNPV(0));
217 BOOST_TEST_MESSAGE("Leg 2 NPV: ORE = "
218 << QuantLib::ext::static_pointer_cast<QuantLib::Swap>(eqSwap->instrument()->qlInstrument())->legNPV(1)
219 << " QL = " << qlSwap->legNPV(1));
220 BOOST_CHECK_CLOSE(eqSwap->instrument()->NPV(), qlSwap->NPV(), 1E-8); // this is 1E-10 rel diff
221}
222
223BOOST_AUTO_TEST_CASE(testEquitySwapTotalReturn) {
224
225 BOOST_TEST_MESSAGE("Testing Equity Swap Total Return...");
226
227 // build market
228 QuantLib::ext::shared_ptr<TestMarket> market = QuantLib::ext::make_shared<TestMarket>();
229 Date today = market->asofDate();
230 Settings::instance().evaluationDate() = today;
231
232 CommonVars vars;
233 QuantLib::ext::shared_ptr<ore::data::Swap> eqSwap = vars.makeEquitySwap(EquityReturnType::Total);
234
235 // engine data and factory
236 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
237 engineData->model("Swap") = "DiscountedCashflows";
238 engineData->engine("Swap") = "DiscountingSwapEngine";
239 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
240
241 // build swaps and portfolio
242 QuantLib::ext::shared_ptr<Portfolio> portfolio(new Portfolio());
243 eqSwap->id() = "EQ_Swap";
244
245 portfolio->add(eqSwap);
246 portfolio->build(engineFactory);
247
248 QuantLib::ext::shared_ptr<QuantLib::Swap> qlSwap = vars.qlEquitySwap(EquityReturnType::Total);
249
250 auto dscEngine = QuantLib::ext::make_shared<DiscountingSwapEngine>(market->discountCurve("USD"));
251 qlSwap->setPricingEngine(dscEngine);
252 BOOST_TEST_MESSAGE("Leg 1 NPV: ORE = "
253 << QuantLib::ext::static_pointer_cast<QuantLib::Swap>(eqSwap->instrument()->qlInstrument())->legNPV(0)
254 << " QL = " << qlSwap->legNPV(0));
255 BOOST_TEST_MESSAGE("Leg 2 NPV: ORE = "
256 << QuantLib::ext::static_pointer_cast<QuantLib::Swap>(eqSwap->instrument()->qlInstrument())->legNPV(1)
257 << " QL = " << qlSwap->legNPV(1));
258 BOOST_CHECK_CLOSE(eqSwap->instrument()->NPV(), qlSwap->NPV(), 1E-8);
259}
260
261BOOST_AUTO_TEST_CASE(testEquitySwapNotionalReset) {
262
263 BOOST_TEST_MESSAGE("Testing Equity Swap Notional Reset...");
264
265 // build market
266 QuantLib::ext::shared_ptr<TestMarket> market = QuantLib::ext::make_shared<TestMarket>();
267 Date today = market->asofDate();
268 // Move on 4 months so we during next period, check we can get a notional
269 Settings::instance().evaluationDate() = today + Period(4, Months);
270
271 CommonVars vars;
272 QuantLib::ext::shared_ptr<ore::data::Swap> eqSwap = vars.makeEquitySwap(EquityReturnType::Total, true);
273
274 // engine data and factory
275 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
276 engineData->model("Swap") = "DiscountedCashflows";
277 engineData->engine("Swap") = "DiscountingSwapEngine";
278 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
279
280 // build swaps and portfolio
281 QuantLib::ext::shared_ptr<Portfolio> portfolio(new Portfolio());
282 eqSwap->id() = "EQ_Swap";
283
284 portfolio->add(eqSwap);
285 portfolio->build(engineFactory);
286
287 QuantLib::ext::shared_ptr<QuantLib::Swap> qlSwap = vars.qlEquitySwap(EquityReturnType::Total, true);
288
289 BOOST_TEST_MESSAGE("Initial notional = " << eqSwap->notional());
290
291 // Add equity fixing after portfolio build, this allows us to test notional without fixing
292 market->equityCurve("SP5")->addFixing(Date(18, October, 2016), 2100);
293
294 auto dscEngine = QuantLib::ext::make_shared<DiscountingSwapEngine>(market->discountCurve("USD"));
295 qlSwap->setPricingEngine(dscEngine);
296 BOOST_TEST_MESSAGE("Leg 1 NPV: ORE = "
297 << QuantLib::ext::static_pointer_cast<QuantLib::Swap>(eqSwap->instrument()->qlInstrument())->legNPV(0)
298 << " QL = " << qlSwap->legNPV(0));
299 BOOST_TEST_MESSAGE("Leg 2 NPV: ORE = "
300 << QuantLib::ext::static_pointer_cast<QuantLib::Swap>(eqSwap->instrument()->qlInstrument())->legNPV(1)
301 << " QL = " << qlSwap->legNPV(1));
302 BOOST_CHECK_CLOSE(eqSwap->instrument()->NPV(), qlSwap->NPV(), 1E-8);
303}
304
305BOOST_AUTO_TEST_SUITE_END()
306
307BOOST_AUTO_TEST_SUITE_END()
Engine builder for Swaps.
EquityLeg & withNotionalReset(bool)
EquityLeg & withNotionals(const std::vector< Real > &notionals)
EquityLeg & withInitialPrice(Real)
EquityLeg & withPaymentDayCounter(const DayCounter &dayCounter)
EquityLeg & withPaymentAdjustment(BusinessDayConvention convention)
EquityLeg & withReturnType(EquityReturnType)
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
map< pair< string, string >, QuantLib::Handle< QuantExt::EquityIndex2 > > equityCurves_
Definition: marketimpl.hpp:232
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.
DateGeneration::Rule parseDateGenerationRule(const string &s)
Convert text to QuantLib::DateGeneration::Rule.
Definition: parsers.cpp:328
Calendar parseCalendar(const string &s)
Convert text to QuantLib::Calendar.
Definition: parsers.cpp:157
QuantLib::ext::shared_ptr< IborIndex > parseIborIndex(const string &s, const Handle< YieldTermStructure > &h)
Convert std::string to QuantLib::IborIndex.
Date parseDate(const string &s)
Convert std::string to QuantLib::Date.
Definition: parsers.cpp:51
Currency parseCurrency(const string &s)
Convert text to QuantLib::Currency.
Definition: parsers.cpp:290
BusinessDayConvention parseBusinessDayConvention(const string &s)
Convert text to QuantLib::BusinessDayConvention.
Definition: parsers.cpp:173
Period parsePeriod(const string &s)
Convert text to QuantLib::Period.
Definition: parsers.cpp:171
DayCounter parseDayCounter(const string &s)
Convert text to QuantLib::DayCounter.
Definition: parsers.cpp:209
Map text representations to QuantLib/QuantExt types.
An implementation of the Market class that stores the required objects in maps.
EquityReturnType
Portfolio class.
Swap trade data model and serialization.
BOOST_AUTO_TEST_CASE(testEquitySwapPriceReturn)
Definition: equityswap.cpp:185