Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
commodityapo.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2019 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/*
20 References:
21 - Iain Clark, Commodity Option Pricing, section 2.7
22 - Pal Nicolai Henriksen: LOGNORMAL MOMENT MATCHING AND PRICING OF BASKET OPTIONS
23 https://pdfs.semanticscholar.org/16ed/c0e804379e22ff36dcbab7e9bb06519faa43.pdf
24 This paper shows that the moment matching works well when forward correlation
25 is high and forward vols have similar levels, but even then distributions
26 deviate in the tails causing discrepancies in options that are deep in or
27 out of the money.
28*/
29
30#include <oret/toplevelfixture.hpp>
31#include <boost/test/unit_test.hpp>
32#include <boost/timer/timer.hpp>
33#include <ql/currencies/america.hpp>
34#include <ql/exercise.hpp>
35#include <ql/math/interpolations/linearinterpolation.hpp>
36#include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
37#include <ql/termstructures/yield/flatforward.hpp>
38#include <ql/time/calendars/unitedstates.hpp>
42
43using namespace QuantLib;
44using namespace QuantExt;
45
46using boost::unit_test_framework::test_suite;
47
48typedef struct {
49 Real strikePrice;
50 QuantLib::Option::Type optionType;
51} ApoTestCase;
52
53BOOST_FIXTURE_TEST_SUITE(OREPlusCommodityTestSuite, ore::test::TopLevelFixture)
54
55BOOST_AUTO_TEST_SUITE(CommodityApoTest)
56
57BOOST_AUTO_TEST_CASE(testCommodityAPO) {
58
59 BOOST_TEST_MESSAGE("Testing Commodity APO Analytical Approximation vs MC Pricing");
60
61 SavedSettings backup;
62
63 Date today(5, Feb, 2019);
64 Settings::instance().evaluationDate() = today;
65 Calendar cal = UnitedStates(UnitedStates::Settlement);
66
67 // Market - flat price curve
68 std::vector<Date> dates = {today + 1 * Years, today + 5 * Years, today + 10 * Years};
69 std::vector<Real> prices = {100.0, 100.0, 100.0};
70 DayCounter dc = Actual365Fixed();
71 Handle<QuantExt::PriceTermStructure> priceCurve(
72 QuantLib::ext::make_shared<InterpolatedPriceCurve<Linear>>(today, dates, prices, dc, USDCurrency()));
73 priceCurve->enableExtrapolation();
74
75 // Market - flat discount curve
76 Handle<Quote> rateQuote(QuantLib::ext::make_shared<SimpleQuote>(0.01));
77 Handle<YieldTermStructure> discountCurve(QuantLib::ext::make_shared<FlatForward>(today, rateQuote, dc, Compounded, Annual));
78
79 // Market - flat volatility structure
80 Handle<QuantLib::BlackVolTermStructure> vol(QuantLib::ext::make_shared<QuantLib::BlackConstantVol>(today, cal, 0.3, dc));
81
82 // Analytical engine
83 Real beta = 0.0;
84 QuantLib::ext::shared_ptr<PricingEngine> analyticalEngine =
85 QuantLib::ext::make_shared<CommodityAveragePriceOptionAnalyticalEngine>(discountCurve, vol, beta);
86
87 // Monte Carlo engine
88 Size samples = 10000;
89 QuantLib::ext::shared_ptr<PricingEngine> mcEngine =
90 QuantLib::ext::make_shared<CommodityAveragePriceOptionMonteCarloEngine>(discountCurve, vol, samples, beta);
91
92 // Instrument
93 Real quantity = 1.0;
94 std::string name = "CL";
95 Period term = 1 * Months;
96
97 // std::vector<ApoTestCase> cases = {{100.0, Option::Call}, {100.0, Option::Put}};
98 std::vector<ApoTestCase> cases = {{100.0, Option::Call}, {120.0, Option::Call}, {80.0, Option::Call},
99 {100.0, Option::Put}, {120.0, Option::Put}, {80.0, Option::Put}};
100
101 for (Size k = 0; k < cases.size(); ++k) {
102
103 Real strikePrice = cases[k].strikePrice;
104 QuantLib::Option::Type optionType = cases[k].optionType;
105 std::string optionTypeString = (optionType == Option::Call ? "Call" : "Put");
106
107 // Vary APO start dates
108 for (Size i = 1; i <= 10; ++i) {
109
110 // Instrument
111 Period startTerm = i * Years;
112 Date startDate = today + startTerm;
113 Date endDate = startDate + term;
114 Date payDate = endDate;
115 QuantLib::ext::shared_ptr<CommoditySpotIndex> index = QuantLib::ext::make_shared<CommoditySpotIndex>(name, cal, priceCurve);
116 QuantLib::ext::shared_ptr<CommodityIndexedAverageCashFlow> flow =
117 QuantLib::ext::make_shared<CommodityIndexedAverageCashFlow>(quantity, startDate, endDate, payDate, index);
118 QuantLib::ext::shared_ptr<Exercise> exercise = QuantLib::ext::make_shared<EuropeanExercise>(endDate);
119
120 QuantLib::ext::shared_ptr<CommodityAveragePriceOption> apo =
121 QuantLib::ext::make_shared<CommodityAveragePriceOption>(flow, exercise, quantity, strikePrice, optionType);
122
123 boost::timer::cpu_timer tan;
124 apo->setPricingEngine(analyticalEngine);
125 Real anPrice = apo->NPV();
126 tan.stop();
127 Real anTime = tan.elapsed().wall * 1e-6;
128
129 boost::timer::cpu_timer tmc;
130 apo->setPricingEngine(mcEngine);
131 Real mcPrice = apo->NPV();
132 Real mcTime = tmc.elapsed().wall * 1e-6;
133
134 Real error1 = 100.0 * fabs(mcPrice - anPrice) / mcPrice;
135 Real error2 = 100.0 * fabs(mcPrice - anPrice) / anPrice;
136
137 BOOST_TEST_MESSAGE(optionTypeString << " " << std::fixed << std::setprecision(2) << strikePrice << " "
138 << startTerm << " Analytical vs MC price: " << std::fixed
139 << std::setprecision(2) << anPrice << " vs " << mcPrice << " diff "
140 << anPrice - mcPrice << " error " << std::max(error1, error2) << "% ("
141 << anTime << " ms, " << mcTime << " ms)");
142 BOOST_CHECK_CLOSE(anPrice, mcPrice, 1.0); // accept 1% relative error
143 }
144 }
145}
146
147BOOST_AUTO_TEST_SUITE_END()
148BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(testCommodityAPO)
string name