Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
compositetrade.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2017 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
20
21#include <boost/make_shared.hpp>
22#include <boost/test/unit_test.hpp>
23#include <boost/test/data/test_case.hpp>
24#include <oret/datapaths.hpp>
30#include <oret/toplevelfixture.hpp>
31#include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
32#include <ql/termstructures/yield/flatforward.hpp>
33#include <ql/time/daycounters/actualactual.hpp>
35
36using namespace QuantLib;
37using namespace QuantExt;
38using namespace boost::unit_test_framework;
39using namespace std;
40using namespace ore::data;
41
42BOOST_FIXTURE_TEST_SUITE(OREDataPlusTestSuite, ore::test::TopLevelFixture)
43
44BOOST_AUTO_TEST_SUITE(CompositeTradeTest)
45
46namespace {
47
48class TestMarket : public MarketImpl {
49public:
50 TestMarket(map<string, Handle<Quote>> fxRates = {{"EURUSD", Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(1.2))}})
51 : MarketImpl(false) {
52 asof_ = Date(3, Feb, 2016);
53
54 // build discount
55 yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::Discount, "EUR")] = flatRateYts(0.075);
56 yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::Discount, "USD")] = flatRateYts(0.1);
57
58 // add fx rates
59 std::map<std::string, QuantLib::Handle<QuantLib::Quote>> quotes;
60 for (auto& fxRate : fxRates) {
61 quotes[fxRate.first] = fxRate.second;
62 }
63 fx_ = QuantLib::ext::make_shared<FXTriangulation>(quotes);
64
65 // build fx vols
66 fxVols_[make_pair(Market::defaultConfiguration, "EURUSD")] = flatRateFxv(0.10);
67
68 // add equity spots
69 equitySpots_[make_pair(Market::defaultConfiguration, "eurCorp")] =
70 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(100));
71 equitySpots_[make_pair(Market::defaultConfiguration, "usdCorp")] =
72 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(100));
73
74 // add dividend yield
75 yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::EquityDividend, "eurCorp")] =
76 flatRateYts(0.05);
77 yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::EquityDividend, "usdCorp")] =
78 flatRateYts(0.05);
79
80 // add equity curve
81 equityCurves_[make_pair(Market::defaultConfiguration, "eurCorp")] =
82 Handle<EquityIndex2>(QuantLib::ext::make_shared<EquityIndex2>(
83 "eurCorp", TARGET(), parseCurrency("EUR"), equitySpot("eurCorp"),
84 yieldCurve(YieldCurveType::Discount, "EUR"), yieldCurve(YieldCurveType::EquityDividend, "eurCorp")));
85 equityCurves_[make_pair(Market::defaultConfiguration, "usdCorp")] =
86 Handle<EquityIndex2>(QuantLib::ext::make_shared<EquityIndex2>(
87 "usdCorp", TARGET(), parseCurrency("USD"), equitySpot("usdCorp"),
88 yieldCurve(YieldCurveType::Discount, "USD"), yieldCurve(YieldCurveType::EquityDividend, "usdCorp")));
89
90 // build equity vols
91 equityVols_[make_pair(Market::defaultConfiguration, "eurCorp")] = flatRateFxv(0.20);
92 equityVols_[make_pair(Market::defaultConfiguration, "usdCorp")] = flatRateFxv(0.20);
93 }
94
95private:
96 Handle<YieldTermStructure> flatRateYts(Real forward) {
97 QuantLib::ext::shared_ptr<YieldTermStructure> yts(new FlatForward(0, NullCalendar(), forward, ActualActual(ActualActual::ISDA)));
98 return Handle<YieldTermStructure>(yts);
99 }
100 Handle<BlackVolTermStructure> flatRateFxv(Volatility forward) {
101 QuantLib::ext::shared_ptr<BlackVolTermStructure> fxv(new BlackConstantVol(0, NullCalendar(), forward, ActualActual(ActualActual::ISDA)));
102 return Handle<BlackVolTermStructure>(fxv);
103 }
104};
105} // namespace
106
107// Synthetic Forward Test.. Per put-call parity a call - a put = a forward
108BOOST_AUTO_TEST_CASE(testSyntheticForward) {
109 BOOST_TEST_MESSAGE("Testing SyntheticForwardTrade...");
110
111 SavedSettings backup;
112
113 InstrumentConventions::instance().setConventions(QuantLib::ext::make_shared<Conventions>());
114
115 // build market
116 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>();
117 Settings::instance().evaluationDate() = market->asofDate();
118 Date expiry = market->asofDate() + 6 * Months + 1 * Days;
119 ostringstream o;
120 o << QuantLib::io::iso_date(expiry);
121 string exp_str = o.str();
122
123 // build EquityOption - expiry in 1 Year
124 OptionData callData("Long", "Call", "European", true, vector<string>(1, exp_str));
125 OptionData putData("Short", "Put", "European", true, vector<string>(1, exp_str));
126 Envelope env("CP1");
127 TradeStrike tradeStrike(95.0, "EUR");
128 QuantLib::ext::shared_ptr<Trade> eqCall =
129 QuantLib::ext::make_shared<EquityOption>(env, callData, EquityUnderlying("eurCorp"), "EUR", 1.0, tradeStrike);
130 eqCall->id() = "Long Call";
131 QuantLib::ext::shared_ptr<Trade> eqPut =
132 QuantLib::ext::make_shared<EquityOption>(env, putData, EquityUnderlying("eurCorp"), "EUR", 1.0, tradeStrike);
133 eqPut->id() = "Short Put";
134 CompositeTrade syntheticForward("EUR", {eqCall, eqPut}, "Mean", 0.0, env);
135 syntheticForward.id() = "Synthetic Forward Test";
136 ore::data::EquityForward eqFwd(env, "Long", EquityUnderlying("eurCorp"), "EUR", 1.0, exp_str, 95.0);
137
138 // Build and price
139 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
140 engineData->model("EquityOption") = "BlackScholesMerton";
141 engineData->engine("EquityOption") = "AnalyticEuropeanEngine";
142 engineData->model("EquityForward") = "DiscountedCashflows";
143 engineData->engine("EquityForward") = "DiscountingEquityForwardEngine";
144 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
145
146 syntheticForward.build(engineFactory);
147 eqFwd.build(engineFactory);
148
149 Real npv_composite = syntheticForward.instrument()->NPV();
150 Real npv_fwd = eqFwd.instrument()->NPV();
151
152 BOOST_CHECK_CLOSE(npv_composite, npv_fwd, 0.01);
153 BOOST_CHECK_CLOSE(syntheticForward.notional(), eqFwd.notional(), 0.01);
154}
155
156// Simple combination of 2 options in different currencies
157BOOST_AUTO_TEST_CASE(testMultiCcyComposite) {
158 BOOST_TEST_MESSAGE("Testing SyntheticForwardTrade...");
159
160 SavedSettings backup;
161
162 InstrumentConventions::instance().setConventions(QuantLib::ext::make_shared<Conventions>());
163
164 // build market
165 QuantLib::ext::shared_ptr<SimpleQuote> eurusdRate(QuantLib::ext::make_shared<SimpleQuote>(1.2));
166 map<string, Handle<Quote>> fxRates = {{"EURUSD", Handle<Quote>(eurusdRate)}};
167 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>(fxRates);
168 Settings::instance().evaluationDate() = market->asofDate();
169 Date expiry = market->asofDate() + 6 * Months + 1 * Days;
170 ostringstream o;
171 o << QuantLib::io::iso_date(expiry);
172 string exp_str = o.str();
173
174 // build EquityOption - expiry in 1 Year
175 OptionData callData("Long", "Call", "European", true, vector<string>(1, exp_str));
176 Envelope env("CP1");
177 TradeStrike tradeStrike_EUR(95.0, "EUR");
178 QuantLib::ext::shared_ptr<Trade> eurCall =
179 QuantLib::ext::make_shared<EquityOption>(env, callData, EquityUnderlying("eurCorp"), "EUR", 1.0, tradeStrike_EUR);
180 eurCall->id() = "EUR Call";
181 TradeStrike tradeStrike_USD(95.0, "USD");
182 QuantLib::ext::shared_ptr<Trade> usdCall =
183 QuantLib::ext::make_shared<EquityOption>(env, callData, EquityUnderlying("usdCorp"), "USD", 1.0, tradeStrike_USD);
184 usdCall->id() = "USD Call";
185 CompositeTrade eurComp("EUR", {eurCall, usdCall}, "Sum", 0.0, env);
186 CompositeTrade usdComp("USD", {eurCall, usdCall}, "Sum", 0.0, env);
187 eurComp.id() = "EUR Combo Call Test";
188 usdComp.id() = "USD Combo Call Test";
189
190 // Build and price
191 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
192 engineData->model("EquityOption") = "BlackScholesMerton";
193 engineData->engine("EquityOption") = "AnalyticEuropeanEngine";
194 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
195
196 eurComp.build(engineFactory);
197 usdComp.build(engineFactory);
198
199 Real npv_eur_composite = eurComp.instrument()->NPV();
200 Real npv_usd_composite = usdComp.instrument()->NPV();
201 Real npv_eurCall = eurCall->instrument()->NPV();
202 Real npv_usdCall = usdCall->instrument()->NPV();
203
204 BOOST_CHECK_CLOSE(npv_eur_composite, npv_eurCall * 1.0 + npv_usdCall / 1.2, 0.01);
205 BOOST_CHECK_CLOSE(npv_usd_composite, npv_eurCall * 1.2 + npv_usdCall / 1.0, 0.01);
206 // Check that the notional is calulated correctly.
207 BOOST_CHECK_CLOSE(usdComp.notional(), eurCall->notional() * 2.2, 0.01);
208
209 // let's bump the fx to check that observation is working
210 auto testMarket = QuantLib::ext::dynamic_pointer_cast<TestMarket>(market);
211 eurusdRate->setValue(1.25);
212 npv_usd_composite = usdComp.instrument()->NPV();
213 BOOST_CHECK_CLOSE(npv_usd_composite, npv_eurCall * 1.25 + npv_usdCall / 1.0, 0.01);
214}
215
216BOOST_AUTO_TEST_CASE(testCompositeReferenceData) {
217 BOOST_TEST_MESSAGE("Testing Composite Trade with and w/o reference data...");
218
219 SavedSettings backup;
220
221 InstrumentConventions::instance().setConventions(QuantLib::ext::make_shared<Conventions>());
222
223 // build CompositeTrade with referencedata
224 auto rdm = QuantLib::ext::make_shared<BasicReferenceDataManager>(TEST_INPUT_FILE("reference_data.xml"));
225 auto ptfReferenceDatum = QuantLib::ext::dynamic_pointer_cast<PortfolioBasketReferenceDatum>(rdm->getData("PortfolioBasket", "MSFDSJP"));
226 auto refData = ptfReferenceDatum->getTrades();
227 QuantLib::ext::shared_ptr<Trade> eqRefCall = refData[0];
228 QuantLib::ext::shared_ptr<Trade> eqRefPut = refData[1];
229
230 Envelope env("CP1");
231 CompositeTrade RefData("EUR", {eqRefCall, eqRefPut}, "Mean", 0.0, env);
232 RefData.id() = "Reference Data Test";
233
234 // build market
235 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>();
236 Settings::instance().evaluationDate() = market->asofDate();
237 Date expiry = market->asofDate() + 6 * Months + 1 * Days;
238 ostringstream o;
239 o << QuantLib::io::iso_date(expiry);
240 string exp_str = o.str();
241
242 // build CompositeTrade without referencedata
243 OptionData callData("Long", "Call", "European", true, vector<string>(1, exp_str));
244 OptionData putData("Short", "Put", "European", true, vector<string>(1, exp_str));
245 //Envelope env("CP1");
246 TradeStrike tradeStrike(95.0, "EUR");
247 QuantLib::ext::shared_ptr<Trade> eqCall =
248 QuantLib::ext::make_shared<EquityOption>(env, callData, EquityUnderlying("eurCorp"), "EUR", 1.0, tradeStrike);
249 eqCall->id() = "Long Call";
250 QuantLib::ext::shared_ptr<Trade> eqPut =
251 QuantLib::ext::make_shared<EquityOption>(env, putData, EquityUnderlying("eurCorp"), "EUR", 1.0, tradeStrike);
252 eqPut->id() = "Short Put";
253 CompositeTrade noRefData("EUR", {eqCall, eqPut}, "Mean", 0.0, env);
254 noRefData.id() = "No Reference Data Test";
255
256 // Build and price
257 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
258 engineData->model("EquityOption") = "BlackScholesMerton";
259 engineData->engine("EquityOption") = "AnalyticEuropeanEngine";
260 QuantLib::ext::shared_ptr<EngineFactory> engineFactory =
261 QuantLib::ext::make_shared<EngineFactory>(engineData, market);
262
263 noRefData.build(engineFactory);
264 RefData.build(engineFactory);
265
266 Real npv_composite_NoRefData = noRefData.instrument()->NPV();
267 Real npv_composite_RefData = RefData.instrument()->NPV();
268
269 BOOST_CHECK_CLOSE(npv_composite_NoRefData, npv_composite_RefData, 0.01);
270 BOOST_CHECK_CLOSE(noRefData.notional(), RefData.notional(), 0.01);
271}
272
273BOOST_AUTO_TEST_CASE(testConstructionWithCompositeTradeReferenceData) {
274
275 // CompoiteTrade with Reference Data
276 auto rdm = QuantLib::ext::make_shared<BasicReferenceDataManager>(TEST_INPUT_FILE("reference_data.xml"));
277 auto ptfReferenceDatum =
278 QuantLib::ext::dynamic_pointer_cast<PortfolioBasketReferenceDatum>(rdm->getData("PortfolioBasket", "MSFDSJP"));
279
280 string xmlRefData = ptfReferenceDatum->toXMLString();
281 PortfolioBasketReferenceDatum xmlPortfolioBasket("MSFDSJP");
282 xmlPortfolioBasket.fromXMLString(xmlRefData);
283
284 BOOST_CHECK_EQUAL(ptfReferenceDatum->id(), xmlPortfolioBasket.id());
285 BOOST_CHECK_EQUAL(ptfReferenceDatum->getTrades()[0]->notional(), xmlPortfolioBasket.getTrades()[0]->notional());
286 BOOST_CHECK_EQUAL(ptfReferenceDatum->getTrades()[1]->notional(), xmlPortfolioBasket.getTrades()[1]->notional());
287 BOOST_CHECK_EQUAL(ptfReferenceDatum->getTrades()[0]->id(), xmlPortfolioBasket.getTrades()[0]->id());
288 BOOST_CHECK_EQUAL(ptfReferenceDatum->getTrades()[1]->id(), xmlPortfolioBasket.getTrades()[1]->id());
289
290 auto refData = ptfReferenceDatum->getTrades();
291 QuantLib::ext::shared_ptr<Trade> eqRefCall = refData[0];
292 QuantLib::ext::shared_ptr<Trade> eqRefPut = refData[1];
293
294 Envelope env("CP1");
295 CompositeTrade compRefData("EUR", {eqRefCall, eqRefPut}, "Mean", 0.0, env);
296
297 // Use toXml to serialise to string
298 string xmlStr = compRefData.toXMLString();
299 CompositeTrade xmlComposite;
300 xmlComposite.fromXMLString(xmlStr);
301
302 BOOST_CHECK_EQUAL(compRefData.id(), xmlComposite.id());
303 BOOST_CHECK_EQUAL(compRefData.currency(), xmlComposite.currency());
304 BOOST_CHECK_EQUAL(compRefData.notionalCalculation(), xmlComposite.notionalCalculation());
305 BOOST_CHECK_EQUAL(compRefData.trades()[0]->tradeType(), xmlComposite.trades()[0]->tradeType());
306 BOOST_CHECK_EQUAL(compRefData.trades()[0]->notional(), xmlComposite.trades()[0]->notional());
307 BOOST_CHECK_EQUAL(compRefData.trades()[1]->tradeType(), xmlComposite.trades()[1]->tradeType());
308 BOOST_CHECK_EQUAL(compRefData.trades()[1]->notional(), xmlComposite.trades()[1]->notional());
309
310
311}
312
313BOOST_AUTO_TEST_SUITE_END()
314
315BOOST_AUTO_TEST_SUITE_END()
Builder that returns an engine to price an equity forward.
Engine builder for equity options.
Composite Trade class.
const string & currency() const
const vector< QuantLib::ext::shared_ptr< Trade > > & trades() const
const string & notionalCalculation() const
Serializable object holding generic trade data, reporting dimensions.
Definition: envelope.hpp:51
Handle< Quote > fxRate(const string &ccypair, const string &configuration=Market::defaultConfiguration) const
Definition: market.cpp:216
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< pair< string, string >, Handle< BlackVolTermStructure > > fxVols_
Definition: marketimpl.hpp:214
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< Quote > > equitySpots_
Definition: marketimpl.hpp:225
Handle< Quote > equitySpot(const string &eqName, const string &configuration=Market::defaultConfiguration) const override
Equity curves.
Definition: marketimpl.cpp:359
map< pair< string, string >, Handle< BlackVolTermStructure > > equityVols_
Definition: marketimpl.hpp:226
Handle< YieldTermStructure > yieldCurve(const YieldCurveType &type, const string &ccy, const string &configuration=Market::defaultConfiguration) const override
Yield Curves.
Definition: marketimpl.cpp:74
Serializable object holding option data.
Definition: optiondata.hpp:42
const vector< QuantLib::ext::shared_ptr< Trade > > & getTrades() const
const std::string & id() const
string & id()
Set the trade id.
Definition: trade.hpp:118
std::string toXMLString() const
Parse from XML string.
Definition: xmlutils.cpp:168
void fromXMLString(const std::string &xml)
Parse from XML string.
Definition: xmlutils.cpp:162
Composite trades operate as a mini portfolio. Their intended use is for strategies like straddles.
Equity Forward data model and serialization.
Equity Option data model and serialization.
Currency parseCurrency(const string &s)
Convert text to QuantLib::Currency.
Definition: parsers.cpp:290
An implementation of the Market class that stores the required objects in maps.
BOOST_AUTO_TEST_CASE(testSyntheticForward)