24#include <oret/toplevelfixture.hpp>
26#include <boost/test/unit_test.hpp>
78#include <ql/pricingengines/credit/midpointcdsengine.hpp>
79#include <ql/pricingengines/forward/replicatingvarianceswapengine.hpp>
80#include <ql/processes/blackscholesprocess.hpp>
81#include <ql/termstructures/volatility/equityfx/blackvariancesurface.hpp>
83#include <ql/math/array.hpp>
84#include <ql/math/comparison.hpp>
85#include <ql/quotes/simplequote.hpp>
86#include <ql/termstructures/yield/flatforward.hpp>
87#include <ql/time/calendars/nullcalendar.hpp>
88#include <ql/time/calendars/target.hpp>
89#include <ql/time/daycounters/actualactual.hpp>
91#include <boost/make_shared.hpp>
93#define LENGTH(a) (sizeof(a) / sizeof(a[0]))
98using boost::unit_test_framework::test_suite;
102struct ReplicatingVarianceSwapData {
123BOOST_FIXTURE_TEST_SUITE(OREPlusEquityFXTestSuite, ore::test::TopLevelFixture)
125BOOST_AUTO_TEST_SUITE(GeneralisedReplicatingVarianceSwapEngineTest)
129 SavedSettings backup;
130 Date today = Date(3, Oct, 2019);
131 Settings::instance().evaluationDate() = today;
132 Calendar cal = TARGET();
133 DayCounter dc = Actual365Fixed();
134 Date exDate = today + Integer(0.246575 * 365 + 0.5);
135 std::vector<Date> dates(1, exDate);
136 Real volatilityStrike = 0.2;
137 Real varianceStrike = volatilityStrike * volatilityStrike;
138 Real vegaNotional = 50000.0;
139 Real varianceNotional = vegaNotional / (2.0 * 100.0 * volatilityStrike);
142 Real arrStrikes[] = {50.0, 55.0, 60.0, 65.0, 70.0, 75.0, 80.0, 85.0, 90.0, 95.0, 100.0,
143 105.0, 110.0, 115.0, 120.0, 125.0, 130.0, 135.0};
144 std::vector<Real>
strikes(arrStrikes, arrStrikes +
sizeof(arrStrikes) /
sizeof(Real));
146 Real arrVols[] = {0.3, 0.29, 0.28, 0.27, 0.26, 0.25, 0.24, 0.23, 0.22, 0.21, 0.2,
147 0.19, 0.18, 0.17, 0.16, 0.15, 0.14, 0.13};
148 std::vector<Real> volsVector(arrVols, arrVols +
sizeof(arrVols) /
sizeof(Real));
149 Matrix vols(18, 1, volsVector.begin(), volsVector.end());
151 BOOST_TEST_MESSAGE(
"Testing t0 pricing of the QuantExt VarSwap engine, as per Demeterfi et. al (1999).");
152 std::string equityName =
"STE";
153 QuantLib::ext::shared_ptr<SimpleQuote> spot(
new SimpleQuote(0.0));
154 Handle<Quote> equityPrice = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(100.0));
155 Handle<YieldTermStructure> yieldTS =
156 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.05, dc));
157 Handle<YieldTermStructure> dividendTS =
158 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.0, dc));
159 Handle<BlackVolTermStructure> volTS = Handle<BlackVolTermStructure>(
160 QuantLib::ext::make_shared<BlackVarianceSurface>(today, NullCalendar(), dates,
strikes, vols, dc));
161 Handle<YieldTermStructure> discountingTS =
162 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.05, dc));
163 QuantLib::ext::shared_ptr<Index> eqIndex =
164 QuantLib::ext::make_shared<EquityIndex2>(equityName, cal, EURCurrency(), equityPrice, yieldTS, dividendTS);
166 QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess> stochProcess(
167 new BlackScholesMertonProcess(equityPrice, dividendTS, discountingTS, volTS));
172 QuantExt::VarianceSwap2 varianceSwap(Position::Long, varianceStrike, varianceNotional, today, exDate, cal,
false);
173 varianceSwap.setPricingEngine(engine);
175 Real result = varianceSwap.variance();
176 Real expected = 0.040203605175062058;
178 BOOST_CHECK_CLOSE(result, expected, tol);
179 result = varianceSwap.NPV();
180 expected = 2513.8798089810457;
181 BOOST_CHECK_CLOSE(result, expected, tol);
186 SavedSettings backup;
187 Date today = Date(30, April, 2018);
188 Settings::instance().evaluationDate() = today;
189 DayCounter dc = Actual365Fixed();
190 Date startDate = today - Integer(0.019178 * 365 + 0.5);
191 Date exDate = today + Integer(0.246575 * 365 + 0.5);
192 std::string equityName =
"STE";
193 Calendar cal = TARGET();
194 std::vector<Date> pastDates;
195 std::vector<Date> dates(1, exDate);
196 Real volatilityStrike = 0.2;
197 Real varianceStrike = volatilityStrike * volatilityStrike;
198 Real vegaNotional = 50000.0;
199 Real varianceNotional = vegaNotional / (2.0 * 100.0 * volatilityStrike);
201 for (Date day = cal.adjust(cal.advance(startDate, -1, Days)); day < today; day = cal.advance(day, 1, Days)) {
202 pastDates.push_back(day);
206 Real arrFixings[] = {98.5, 98.0, 99.0, 100.2, 99.4, 98.2};
207 std::vector<Real> fixings(arrFixings, arrFixings +
sizeof(arrFixings) /
sizeof(Real));
208 TimeSeries<Real> fixingHistory(pastDates.begin(), pastDates.end(), fixings.begin());
209 IndexManager::instance().setHistory(equityName, fixingHistory);
212 Real arrStrikes[] = {50.0, 55.0, 60.0, 65.0, 70.0, 75.0, 80.0, 85.0, 90.0, 95.0, 100.0,
213 105.0, 110.0, 115.0, 120.0, 125.0, 130.0, 135.0};
214 std::vector<Real>
strikes(arrStrikes, arrStrikes +
sizeof(arrStrikes) /
sizeof(Real));
216 Real arrVols[] = {0.3, 0.29, 0.28, 0.27, 0.26, 0.25, 0.24, 0.23, 0.22, 0.21, 0.2,
217 0.19, 0.18, 0.17, 0.16, 0.15, 0.14, 0.13};
218 std::vector<Real> volsVector(arrVols, arrVols +
sizeof(arrVols) /
sizeof(Real));
219 Matrix vols(18, 1, volsVector.begin(), volsVector.end());
221 BOOST_TEST_MESSAGE(
"Testing seasoned swap pricing of the QuantExt VarSwap engine.");
222 QuantLib::ext::shared_ptr<SimpleQuote> spot(
new SimpleQuote(0.0));
223 Handle<Quote> equityPrice = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(100.0));
224 Handle<YieldTermStructure> yieldTS = Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, cal, 0.05, dc));
225 Handle<YieldTermStructure> dividendTS =
226 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, cal, 0.0, dc));
227 Handle<BlackVolTermStructure> volTS =
228 Handle<BlackVolTermStructure>(QuantLib::ext::make_shared<BlackVarianceSurface>(today, cal, dates,
strikes, vols, dc));
229 Handle<YieldTermStructure> discountingTS =
230 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, cal, 0.05, dc));
231 QuantLib::ext::shared_ptr<Index> eqIndex =
232 QuantLib::ext::make_shared<EquityIndex2>(equityName, cal, EURCurrency(), equityPrice, yieldTS, dividendTS);
234 QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess> stochProcess(
235 new BlackScholesMertonProcess(equityPrice, dividendTS, yieldTS, volTS));
242 varianceSwap.setPricingEngine(engine);
244 Real result = varianceSwap.variance();
245 Real expected = 0.040169651620750264;
247 BOOST_CHECK_CLOSE(result, expected, tol);
248 result = varianceSwap.NPV();
249 expected = 2094.6608249765977;
250 BOOST_CHECK_CLOSE(result, expected, tol);
253 IndexManager::instance().clearHistory(
"EQ/" + equityName);
258 SavedSettings backup;
259 Date today(2, Jul, 2018);
260 Settings::instance().evaluationDate() = today;
261 Calendar cal = TARGET();
262 DayCounter dc = Actual365Fixed();
263 Date exDate = today + Integer(0.246575 * 365 + 0.5);
264 std::vector<Date> dates(1, exDate);
265 Real volatilityStrike = 0.2;
266 Real varianceStrike = volatilityStrike * volatilityStrike;
267 Real vegaNotional = 50000.0;
268 Real varianceNotional = vegaNotional / (2.0 * 100.0 * volatilityStrike);
271 Real arrStrikes[] = {50.0, 55.0, 60.0, 65.0, 70.0, 75.0, 80.0, 85.0, 90.0, 95.0, 100.0,
272 105.0, 110.0, 115.0, 120.0, 125.0, 130.0, 135.0};
273 std::vector<Real>
strikes(arrStrikes, arrStrikes +
sizeof(arrStrikes) /
sizeof(Real));
275 Real arrVols[] = {0.3, 0.29, 0.28, 0.27, 0.26, 0.25, 0.24, 0.23, 0.22, 0.21, 0.2,
276 0.19, 0.18, 0.17, 0.16, 0.15, 0.14, 0.13};
277 std::vector<Real> volsVector(arrVols, arrVols +
sizeof(arrVols) /
sizeof(Real));
278 Matrix vols(18, 1, volsVector.begin(), volsVector.end());
281 "Testing future starting pricing of the QuantExt VarSwap engine, as per Demeterfi et. al (1999).");
282 std::string equityName =
"STE";
283 QuantLib::ext::shared_ptr<SimpleQuote> spot(
new SimpleQuote(0.0));
284 Handle<Quote> equityPrice = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(100.0));
285 Handle<YieldTermStructure> yieldTS =
286 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.05, dc));
287 Handle<YieldTermStructure> dividendTS =
288 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.0, dc));
289 Handle<BlackVolTermStructure> volTS = Handle<BlackVolTermStructure>(
290 QuantLib::ext::make_shared<BlackVarianceSurface>(today, NullCalendar(), dates,
strikes, vols, dc));
291 Handle<YieldTermStructure> discountingTS =
292 Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(0, NullCalendar(), 0.05, dc));
293 QuantLib::ext::shared_ptr<Index> eqIndex =
294 QuantLib::ext::make_shared<EquityIndex2>(equityName, cal, EURCurrency(), equityPrice, yieldTS, dividendTS);
296 QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess> stochProcess(
297 new BlackScholesMertonProcess(equityPrice, dividendTS, discountingTS, volTS));
304 varianceSwap.setPricingEngine(engine);
306 Real result = varianceSwap.variance();
307 Real expected = 0.038880652347511133;
309 BOOST_CHECK_CLOSE(result, expected, tol);
310 result = varianceSwap.NPV();
311 expected = -13820.40246258254;
312 BOOST_CHECK_CLOSE(result, expected, tol);
317 BOOST_TEST_MESSAGE(
"Testing variance swap with replicating cost engine...");
319 ReplicatingVarianceSwapData values[] = {
327 {Position::Long, 0.04, 50000, 100.0, 0.00, 0.05, 0.246575, 0.20, 0.041888574, 1.0e-4}
331 Datum replicatingOptionData[] = {
337 {Option::Put, 50, 0.30}, {Option::Put, 55, 0.29}, {Option::Put, 60, 0.28}, {Option::Put, 65, 0.27},
338 {Option::Put, 70, 0.26}, {Option::Put, 75, 0.25}, {Option::Put, 80, 0.24}, {Option::Put, 85, 0.23},
339 {Option::Put, 90, 0.22}, {Option::Put, 95, 0.21}, {Option::Put, 100, 0.20}, {Option::Call, 100, 0.20},
340 {Option::Call, 105, 0.19}, {Option::Call, 110, 0.18}, {Option::Call, 115, 0.17}, {Option::Call, 120, 0.16},
341 {Option::Call, 125, 0.15}, {Option::Call, 130, 0.14}, {Option::Call, 135, 0.13}};
343 SavedSettings backup;
344 DayCounter dc = Actual365Fixed();
345 Date today = Date::todaysDate();
346 Settings::instance().evaluationDate() = today;
348 QuantLib::ext::shared_ptr<SimpleQuote> spot(
new SimpleQuote(0.0));
349 QuantLib::ext::shared_ptr<SimpleQuote> qRate(
new SimpleQuote(0.0));
350 QuantLib::ext::shared_ptr<YieldTermStructure> qTS =
351 QuantLib::ext::shared_ptr<YieldTermStructure>(
new FlatForward(today, Handle<Quote>(qRate), dc));
352 QuantLib::ext::shared_ptr<SimpleQuote> rRate(
new SimpleQuote(0.0));
353 QuantLib::ext::shared_ptr<YieldTermStructure> rTS =
354 QuantLib::ext::shared_ptr<YieldTermStructure>(
new FlatForward(today, Handle<Quote>(rRate), dc));
356 for (Size i = 0; i <
LENGTH(values); i++) {
357 Date exDate = today + Integer(values[i].t * 365 + 0.5);
358 std::vector<Date> dates(1);
361 spot->setValue(values[i].s);
362 qRate->setValue(values[i].q);
363 rRate->setValue(values[i].r);
365 Size options =
LENGTH(replicatingOptionData);
366 std::vector<Real> callStrikes, putStrikes, callVols, putVols;
370 for (j = 0; j < options; j++) {
371 if (replicatingOptionData[j].type == Option::Call) {
372 callStrikes.push_back(replicatingOptionData[j].strike);
373 callVols.push_back(replicatingOptionData[j].v);
374 }
else if (replicatingOptionData[j].type == Option::Put) {
375 putStrikes.push_back(replicatingOptionData[j].strike);
376 putVols.push_back(replicatingOptionData[j].v);
378 QL_FAIL(
"unknown option type");
382 Matrix vols(options - 1, 1);
384 for (j = 0; j < putVols.size(); j++) {
385 vols[j][0] = putVols[j];
386 strikes.push_back(putStrikes[j]);
389 for (Size k = 1; k < callVols.size(); k++) {
390 Size j = putVols.size() - 1;
391 vols[j + k][0] = callVols[k];
392 strikes.push_back(callStrikes[k]);
395 QuantLib::ext::shared_ptr<BlackVolTermStructure> volTS(
396 new BlackVarianceSurface(today, NullCalendar(), dates,
strikes, vols, dc));
398 QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess> stochProcess(
399 new BlackScholesMertonProcess(Handle<Quote>(spot), Handle<YieldTermStructure>(qTS),
400 Handle<YieldTermStructure>(rTS), Handle<BlackVolTermStructure>(volTS)));
402 QuantLib::ext::shared_ptr<PricingEngine> engine(
403 new ReplicatingVarianceSwapEngine(stochProcess, 5.0, callStrikes, putStrikes));
405 QuantLib::VarianceSwap varianceSwap(values[i].type, values[i].varStrike, values[i].nominal, today, exDate);
406 varianceSwap.setPricingEngine(engine);
408 Real calculated = varianceSwap.variance();
409 Real expected = values[i].result;
411 BOOST_CHECK_CLOSE(calculated, expected, tol);
415BOOST_AUTO_TEST_SUITE_END()
417BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(testT0Pricing)