Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
equitytrades.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>
29#include <oret/toplevelfixture.hpp>
30#include <ql/math/distributions/normaldistribution.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
42namespace {
43
44class TestMarket : public MarketImpl {
45public:
46 TestMarket() : MarketImpl(false) {
47 asof_ = Date(3, Feb, 2016);
48
49 // build discount
50 yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::Discount, "EUR")] = flatRateYts(0.1);
51 yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::Discount, "USD")] = flatRateYts(0.075);
52
53 // add fx rates
54 std::map<std::string, QuantLib::Handle<QuantLib::Quote>> quotes;
55 quotes["EURUSD"] = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(1.2));
56 fx_ = QuantLib::ext::make_shared<FXTriangulation>(quotes);
57
58 // build fx vols
59 fxVols_[make_pair(Market::defaultConfiguration, "EURUSD")] = flatRateFxv(0.10);
60
61 // add equity spots
62 equitySpots_[make_pair(Market::defaultConfiguration, "zzzCorp")] =
63 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(100));
64
65 // add dividend yield
66 yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::EquityDividend, "zzzCorp")] =
67 flatRateYts(0.05);
68
69 // add equity curve
70 equityCurves_[make_pair(Market::defaultConfiguration, "zzzCorp")] =
71 Handle<EquityIndex2>(QuantLib::ext::make_shared<EquityIndex2>(
72 "zzzCorp", TARGET(), parseCurrency("EUR"), equitySpot("zzzCorp"),
73 yieldCurve(YieldCurveType::Discount, "EUR"), yieldCurve(YieldCurveType::EquityDividend, "zzzCorp")));
74
75 // build equity vols
76 equityVols_[make_pair(Market::defaultConfiguration, "zzzCorp")] = flatRateFxv(0.20);
77 }
78
79private:
80 Handle<YieldTermStructure> flatRateYts(Real forward) {
81 QuantLib::ext::shared_ptr<YieldTermStructure> yts(new FlatForward(0, NullCalendar(), forward, ActualActual(ActualActual::ISDA)));
82 return Handle<YieldTermStructure>(yts);
83 }
84 Handle<BlackVolTermStructure> flatRateFxv(Volatility forward) {
85 QuantLib::ext::shared_ptr<BlackVolTermStructure> fxv(new BlackConstantVol(0, NullCalendar(), forward, ActualActual(ActualActual::ISDA)));
86 return Handle<BlackVolTermStructure>(fxv);
87 }
88};
89} // namespace
90
91BOOST_FIXTURE_TEST_SUITE(OREDataTestSuite, ore::test::TopLevelFixture)
92
93BOOST_AUTO_TEST_SUITE(EquityTradesTests)
94
95// Equity option/forward test, example from Haug, Chapter 1
96BOOST_AUTO_TEST_CASE(testEquityTradePrices) {
97
98 BOOST_TEST_MESSAGE("Testing EquityOption Price...");
99
100 Date today = Settings::instance().evaluationDate();
101
102 // build market
103 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>();
104 Settings::instance().evaluationDate() = market->asofDate();
105 Date expiry = market->asofDate() + 6 * Months + 1 * Days;
106 ostringstream o;
107 o << QuantLib::io::iso_date(expiry);
108 string exp_str = o.str();
109
110 // build EquityOption - expiry in 1 Year
111 OptionData callData("Long", "Call", "European", true, vector<string>(1, exp_str));
112 OptionData callDataPremium("Long", "Call", "European", true, vector<string>(1, exp_str), "Cash", "",
113 PremiumData{1.0, "EUR", expiry});
114 OptionData putData("Short", "Put", "European", true, vector<string>(1, exp_str));
115 OptionData putDataPremium("Short", "Put", "European", true, vector<string>(1, exp_str), "Cash", "",
116 PremiumData{1.0, "EUR", expiry});
117 Envelope env("CP1");
118 TradeStrike tradeStrike(95.0, "EUR");
119 EquityOption eqCall(env, callData, EquityUnderlying("zzzCorp"), "EUR", 1.0, tradeStrike);
120 EquityOption eqCallPremium(env, callDataPremium, EquityUnderlying("zzzCorp"), "EUR", 1.0, tradeStrike);
121 EquityOption eqPut(env, putData, EquityUnderlying("zzzCorp"), "EUR", 1.0, tradeStrike);
122 EquityOption eqPutPremium(env, putDataPremium, EquityUnderlying("zzzCorp"), "EUR", 1.0, tradeStrike);
123 ore::data::EquityForward eqFwd(env, "Long", EquityUnderlying("zzzCorp"), "EUR", 1.0, exp_str, 95.0);
124
125 Real expectedNPV_Put = -2.4648; // negative for sold option
126 Real expectedNPV_Put_Premium = -1.513558; // less negative due to received premium of 1 EUR at expiry
127
128 // Build and price
129 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
130 engineData->model("EquityOption") = "BlackScholesMerton";
131 engineData->engine("EquityOption") = "AnalyticEuropeanEngine";
132 engineData->model("EquityForward") = "DiscountedCashflows";
133 engineData->engine("EquityForward") = "DiscountingEquityForwardEngine";
134 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
135
136 eqCall.build(engineFactory);
137 eqCallPremium.build(engineFactory);
138 eqPut.build(engineFactory);
139 eqPutPremium.build(engineFactory);
140 eqFwd.build(engineFactory);
141
142 Real npv_call = eqCall.instrument()->NPV();
143 Real npv_call_premium = eqCallPremium.instrument()->NPV();
144 Real npv_put = eqPut.instrument()->NPV();
145 Real npv_put_premium = eqPutPremium.instrument()->NPV();
146 Real npv_fwd = eqFwd.instrument()->NPV();
147
148 Real put_call_sum = npv_call + npv_put;
149 Real put_call_premium_sum = npv_call_premium + npv_put_premium;
150
151 BOOST_CHECK_CLOSE(expectedNPV_Put, npv_put, 0.001);
152 BOOST_CHECK_CLOSE(expectedNPV_Put_Premium, npv_put_premium, 0.001);
153 BOOST_CHECK_CLOSE(npv_fwd, put_call_sum, 0.001); // put-call parity check
154 BOOST_CHECK_CLOSE(npv_fwd, put_call_premium_sum, 0.001); // put-call parity check
155
156 Settings::instance().evaluationDate() = today; // reset
157}
158
159// when futureExpiryDate == optionExpiryDate then the trade should behave like an EquityOption
160BOOST_AUTO_TEST_CASE(testEquityFutureOptionPrices) {
161
162 BOOST_TEST_MESSAGE("Testing EquityFutureOption Price...");
163
164 Date today = Settings::instance().evaluationDate();
165
166 // build market
167 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>();
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 QuantLib::ext::shared_ptr<ore::data::Underlying> underlying = QuantLib::ext::make_shared<ore::data::EquityUnderlying>("zzzCorp");
175 // build EquityOption - expiry in 1 Year
176 OptionData callData("Long", "Call", "European", true, vector<string>(1, exp_str));
177 OptionData callDataPremium("Long", "Call", "European", true, vector<string>(1, exp_str), "Physical", "",
178 PremiumData(1.0, "EUR", expiry));
179 OptionData putData("Short", "Put", "European", true, vector<string>(1, exp_str));
180 OptionData putDataPremium("Short", "Put", "European", true, vector<string>(1, exp_str), "Physical", "",
181 PremiumData(1.0, "EUR", expiry));
182 Envelope env("CP1");
183 TradeStrike strike(TradeStrike::Type::Price, 95.0);
184 EquityFutureOption eqFwdCall(env, callData, "EUR", 1.0, underlying, strike, expiry);
185 EquityFutureOption eqFwdCallPremium(env, callDataPremium, "EUR", 1.0, underlying, strike, expiry);
186 EquityFutureOption eqFwdPut(env, putData, "EUR", 1.0, underlying, strike, expiry);
187 EquityFutureOption eqFwdPutPremium(env, putDataPremium, "EUR", 1.0, underlying, strike, expiry);
188
189 TradeStrike tradeStrike(95.0, "EUR");
190 EquityOption eqCall(env, callData, EquityUnderlying("zzzCorp"), "EUR", 1.0, tradeStrike);
191 EquityOption eqCallPremium(env, callDataPremium, EquityUnderlying("zzzCorp"), "EUR", 1.0, tradeStrike);
192 EquityOption eqPut(env, putData, EquityUnderlying("zzzCorp"), "EUR", 1.0, tradeStrike);
193 EquityOption eqPutPremium(env, putDataPremium, EquityUnderlying("zzzCorp"), "EUR", 1.0, tradeStrike);
194
195 // Build and price
196 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
197 engineData->model("EquityOption") = "BlackScholesMerton";
198 engineData->engine("EquityOption") = "AnalyticEuropeanEngine";
199 engineData->model("EquityFutureOption") = "BlackScholes";
200 engineData->engine("EquityFutureOption") = "AnalyticEuropeanForwardEngine";
201 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
202
203 eqFwdCall.build(engineFactory);
204 eqFwdCallPremium.build(engineFactory);
205 eqFwdPut.build(engineFactory);
206 eqFwdPutPremium.build(engineFactory);
207
208 eqCall.build(engineFactory);
209 eqCallPremium.build(engineFactory);
210 eqPut.build(engineFactory);
211 eqPutPremium.build(engineFactory);
212
213 BOOST_CHECK_CLOSE(eqCall.instrument()->NPV(), eqFwdCall.instrument()->NPV(), 0.001);
214 BOOST_CHECK_CLOSE(eqCallPremium.instrument()->NPV(), eqFwdCallPremium.instrument()->NPV(), 0.001);
215 BOOST_CHECK_CLOSE(eqPut.instrument()->NPV(), eqFwdPut.instrument()->NPV(), 0.001);
216 BOOST_CHECK_CLOSE(eqPutPremium.instrument()->NPV(), eqFwdPutPremium.instrument()->NPV(), 0.001);
217
218 Settings::instance().evaluationDate() = today; // reset
219}
220
221BOOST_AUTO_TEST_CASE(testEquityFutureParity) {
222
223 BOOST_TEST_MESSAGE("Testing EquityFutureOption Put-Call parity...");
224
225 Date today = Settings::instance().evaluationDate();
226
227 // build market
228 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>();
229 Settings::instance().evaluationDate() = market->asofDate();
230 Date expiry = market->asofDate() + 6 * Months + 1 * Days;
231 ostringstream o;
232 o << QuantLib::io::iso_date(expiry);
233 string exp_str = o.str();
234
235 Date futureExpiry = market->asofDate() + 12 * Months + 1 * Days;
236 ostringstream o_2;
237 o_2 << QuantLib::io::iso_date(futureExpiry);
238 string f_exp_str = o_2.str();
239
240 QuantLib::ext::shared_ptr<ore::data::Underlying> underlying = QuantLib::ext::make_shared<ore::data::EquityUnderlying>("zzzCorp");
241 double spot = 100;
242 // build EquityOption - expiry in 1 Year
243 OptionData callData("Long", "Call", "European", true, vector<string>(1, exp_str));
244 OptionData callDataPremium("Long", "Call", "European", true, vector<string>(1, exp_str), "Physical", "",
245 PremiumData(1.0, "EUR", expiry));
246 OptionData putData("Long", "Put", "European", true, vector<string>(1, exp_str));
247 OptionData putDataPremium("Long", "Put", "European", true, vector<string>(1, exp_str), "Physical", "",
248 PremiumData(1.0, "EUR", expiry));
249 Envelope env("CP1");
250 TradeStrike strike(TradeStrike::Type::Price, 95.0);
251 EquityFutureOption eqCall(env, callData, "EUR", 1.0, underlying, strike, futureExpiry);
252 EquityFutureOption eqCallPremium(env, callDataPremium, "EUR", 1.0, underlying, strike, futureExpiry);
253 EquityFutureOption eqPut(env, putData, "EUR", 1.0, underlying, strike, futureExpiry);
254 EquityFutureOption eqPutPremium(env, putDataPremium, "EUR", 1.0, underlying, strike, futureExpiry);
255 ore::data::EquityForward eqFwd(env, "Long", EquityUnderlying("zzzCorp"), "EUR", 1.0, f_exp_str, 0);
256
257 // Build and price
258 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
259 engineData->model("EquityFutureOption") = "BlackScholes";
260 engineData->engine("EquityFutureOption") = "AnalyticEuropeanForwardEngine";
261 engineData->model("EquityOption") = "BlackScholesMerton";
262 engineData->engine("EquityOption") = "AnalyticEuropeanEngine";
263 engineData->model("EquityForward") = "DiscountedCashflows";
264 engineData->engine("EquityForward") = "DiscountingEquityForwardEngine";
265 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
266
267 eqCall.build(engineFactory);
268 eqCallPremium.build(engineFactory);
269 eqPut.build(engineFactory);
270 eqPutPremium.build(engineFactory);
271 eqFwd.build(engineFactory);
272
273 Handle<YieldTermStructure> discountCurve =
274 market->discountCurve("EUR", Market::defaultConfiguration);
275
276 Handle<YieldTermStructure> dividend = market->equityDividendCurve("zzzCorp", Market::defaultConfiguration);
277 Handle<YieldTermStructure> forecast = market->equityForecastCurve("zzzCorp", Market::defaultConfiguration);
278
279 Real npv_call = eqCall.instrument()->NPV();
280 Real npv_call_premium = eqCallPremium.instrument()->NPV();
281 Real npv_put = eqPut.instrument()->NPV();
282 Real npv_put_premium = eqPutPremium.instrument()->NPV();
283
284 Real npv_fwd = spot * dividend->discount(futureExpiry) / forecast->discount(futureExpiry);;
285
286 Real put_sum = npv_put + ( npv_fwd - strike.value() ) * discountCurve->discount(expiry);
287 Real put_premium_sum = npv_put_premium + ( npv_fwd - strike.value() ) * discountCurve->discount(expiry);
288
289 BOOST_CHECK_CLOSE(npv_call, put_sum, 0.001); // put-call parity check
290 BOOST_CHECK_CLOSE(npv_call_premium, put_premium_sum, 0.001); // put-call parity check
291
292 Settings::instance().evaluationDate() = today; // reset
293}
294BOOST_AUTO_TEST_SUITE_END()
295
296BOOST_AUTO_TEST_SUITE_END()
Builder that returns an engine to price an equity forward.
Engine builder for equity futures options.
Engine builder for equity options.
Serializable object holding generic trade data, reporting dimensions.
Definition: envelope.hpp:51
Serializable EQ Futures Option.
void build(const QuantLib::ext::shared_ptr< EngineFactory > &) override
Build QuantLib/QuantExt instrument, link pricing engine.
Serializable Equity Option.
void build(const QuantLib::ext::shared_ptr< EngineFactory > &) override
Build QuantLib/QuantExt instrument, link pricing engine.
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
Serializable object holding premium data.
Definition: premiumdata.hpp:37
const QuantLib::ext::shared_ptr< InstrumentWrapper > & instrument() const
Definition: trade.hpp:141
QuantLib::Real value() const
A class to hold pricing engine parameters.
Equity Forward data model and serialization.
EQ Futures Option data model and serialization.
Equity Option data model and serialization.
BOOST_AUTO_TEST_CASE(testEquityTradePrices)
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.