20#include <boost/test/unit_test.hpp>
24#include <ql/exercise.hpp>
25#include <ql/pricingengines/vanilla/analyticeuropeanengine.hpp>
26#include <ql/quotes/simplequote.hpp>
27#include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
28#include <ql/termstructures/yield/flatforward.hpp>
29#include <ql/termstructures/yield/piecewisezerospreadedtermstructure.hpp>
30#include <ql/time/calendars/nullcalendar.hpp>
32#include <boost/make_shared.hpp>
37using namespace boost::unit_test_framework;
42 TestData() : refDate(Date(22, Aug, 2016)) {
43 Settings::instance().evaluationDate() = refDate;
44 rateDiscount = Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(
45 0, NullCalendar(), Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.02)), Actual365Fixed()));
46 divDiscount = Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(
47 0, NullCalendar(), Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.03)), Actual365Fixed()));
48 pillarDates.push_back(refDate + 1 * Years);
49 pillarDates.push_back(refDate + 2 * Years);
50 pillarDates.push_back(refDate + 3 * Years);
51 pillarDates.push_back(refDate + 4 * Years);
52 pillarDates.push_back(refDate + 5 * Years);
53 pillarDates.push_back(refDate + 7 * Years);
54 pillarDates.push_back(refDate + 10 * Years);
55 pillarDates.push_back(refDate + 15 * Years);
56 pillarDates.push_back(refDate + 20 * Years);
57 std::vector<Handle<Quote>> tmpRateSpr, tmpDivSpr;
58 for (Size i = 0; i < pillarDates.size(); ++i) {
59 QuantLib::ext::shared_ptr<SimpleQuote> qr = QuantLib::ext::make_shared<SimpleQuote>(0.0);
60 QuantLib::ext::shared_ptr<SimpleQuote> qd = QuantLib::ext::make_shared<SimpleQuote>(0.0);
61 rateSpreads.push_back(qr);
62 divSpreads.push_back(qd);
63 tmpRateSpr.push_back(Handle<Quote>(qr));
64 tmpDivSpr.push_back(Handle<Quote>(qd));
65 pillarTimes.push_back(rateDiscount->timeFromReference(pillarDates[i]));
68 Handle<YieldTermStructure>(QuantLib::ext::make_shared<InterpolatedPiecewiseZeroSpreadedTermStructure<Linear>>(
69 rateDiscount, tmpRateSpr, pillarDates));
71 Handle<YieldTermStructure>(QuantLib::ext::make_shared<InterpolatedPiecewiseZeroSpreadedTermStructure<Linear>>(
72 divDiscount, tmpDivSpr, pillarDates));
73 rateCurve->enableExtrapolation();
74 divCurve->enableExtrapolation();
75 vol = QuantLib::ext::make_shared<SimpleQuote>(0.20);
76 volTs = Handle<BlackVolTermStructure>(
77 QuantLib::ext::make_shared<BlackConstantVol>(0, NullCalendar(), Handle<Quote>(vol), Actual365Fixed()));
78 spot = QuantLib::ext::make_shared<SimpleQuote>(100.0);
79 process = QuantLib::ext::make_shared<GeneralizedBlackScholesProcess>(Handle<Quote>(spot), divCurve, rateCurve, volTs);
82 Handle<YieldTermStructure> rateDiscount, divDiscount, rateCurve, divCurve;
83 std::vector<Date> pillarDates;
84 std::vector<QuantLib::ext::shared_ptr<SimpleQuote>> rateSpreads, divSpreads;
85 std::vector<Real> pillarTimes;
86 QuantLib::ext::shared_ptr<SimpleQuote> vol, spot;
87 Handle<BlackVolTermStructure> volTs;
88 QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess> process;
91bool check(
const Real reference,
const Real value) {
92 if (std::fabs(reference) >= 1E-4) {
93 return std::fabs((reference - value) / reference) < 1E-3;
95 return std::fabs(reference - value) < 1E-5;
103BOOST_AUTO_TEST_SUITE(AnalyticEuropeanEngineDeltaGammaTest)
107 BOOST_TEST_MESSAGE(
"Testing npv calculation in AnalyticEuropeanEngineDeltaGamma against QuantLib engine...");
111 Size n = d.pillarTimes.size();
113 Real strike = d.spot->value();
114 Date expiryDate = d.refDate + 6 * Years;
115 QuantLib::ext::shared_ptr<StrikedTypePayoff> payoff = QuantLib::ext::make_shared<PlainVanillaPayoff>(Option::Put, strike);
116 QuantLib::ext::shared_ptr<Exercise> exercise = QuantLib::ext::make_shared<EuropeanExercise>(expiryDate);
117 QuantLib::ext::shared_ptr<VanillaOption> option = QuantLib::ext::make_shared<VanillaOption>(payoff, exercise);
119 QuantLib::ext::shared_ptr<PricingEngine> engine0 = QuantLib::ext::make_shared<AnalyticEuropeanEngine>(d.process);
120 QuantLib::ext::shared_ptr<PricingEngine> engine1 =
121 QuantLib::ext::make_shared<AnalyticEuropeanEngineDeltaGamma>(d.process, d.pillarTimes, d.pillarTimes,
true,
true);
123 option->setPricingEngine(engine0);
124 Real npv0 = option->NPV();
126 option->setPricingEngine(engine1);
127 Real npv = option->NPV();
130 if (std::fabs(npv0 - npv) > tol)
131 BOOST_ERROR(
"npv (" << npv <<
") can not be verified, expected " << npv0);
134 Real deltaSpot = option->result<Real>(
"deltaSpot");
135 Real gammaSpot = option->result<Real>(
"gammaSpot");
137 std::vector<Real> vega = option->result<std::vector<Real>>(
"vega");
138 std::vector<Real> deltaRate = option->result<std::vector<Real>>(
"deltaRate");
139 std::vector<Real> deltaDividend = option->result<std::vector<Real>>(
"deltaDividend");
140 Matrix gamma = option->result<Matrix>(
"gamma");
141 std::vector<Real> gammaSpotRate = option->result<std::vector<Real>>(
"gammaSpotRate");
142 std::vector<Real> gammaSpotDiv = option->result<std::vector<Real>>(
"gammaSpotDiv");
145 option->setPricingEngine(engine0);
149 BOOST_TEST_MESSAGE(
"Checking additional results for correct dimensions in AnalyticEuropeanEngineDeltaGamma...");
151 if (vega.size() != n)
152 BOOST_ERROR(
"vega size (" << vega.size() <<
") mismatch, expected " << n);
153 if (deltaRate.size() != n)
154 BOOST_ERROR(
"delta rate size (" << deltaRate.size() <<
") mismatch, expected " << n);
155 if (deltaDividend.size() != n)
156 BOOST_ERROR(
"delta dividend size (" << deltaDividend.size() <<
") mismatch, expected " << n);
157 if (gamma.rows() != 2 * n || gamma.columns() != 2 * n)
158 BOOST_ERROR(
"gamma size (" << gamma.rows() <<
"x" << gamma.columns() <<
") mismatch, expected " << 2 * n <<
"x"
160 if (gammaSpotRate.size() != n)
161 BOOST_ERROR(
"gamma spot rate size (" << gammaSpotRate.size() <<
") mismatch, expected " << n);
162 if (gammaSpotDiv.size() != n)
163 BOOST_ERROR(
"gamma spot div size (" << gammaSpotDiv.size() <<
") mismatch, expected " << n);
168 "Checking additional results against bump and revalue results in AnalyticEuropeanEngineDeltaGamma...");
173 d.spot->setValue(d.spot->value() + h1);
174 Real npvp = option->NPV();
175 d.spot->setValue(d.spot->value() - 2.0 * h1);
176 Real npvm = option->NPV();
177 d.spot->setValue(d.spot->value() + h1);
179 Real refDelta = (npvp - npvm) / (2.0 * h1);
180 Real refGamma = (npvp - 2.0 * npv + npvm) / (h1 * h1);
182 if (!
check(refDelta, deltaSpot))
183 BOOST_ERROR(
"could not verify delta (reference value=" << refDelta <<
", result=" << deltaSpot
184 <<
", difference=" << deltaSpot - refDelta <<
")");
185 if (!
check(refGamma, gammaSpot))
186 BOOST_ERROR(
"could not verify gamma (reference value=" << refGamma <<
", result=" << gammaSpot
187 <<
", difference=" << gammaSpot - refGamma <<
")");
197 for (Size i = 0; i < vega.size(); ++i) {
201 d.vol->setValue(d.vol->value() + h2);
202 Real npvvp = option->NPV();
203 d.vol->setValue(d.vol->value() - h2);
204 Real refVega = (npvvp - npv) / h2;
206 if (!
check(refVega, vegaSum))
207 BOOST_ERROR(
"could not verify vega (reference value=" << refVega <<
", result=" << vegaSum
208 <<
", difference=" << vegaSum - refVega <<
")");
210 for (Size i = 0; i < n; ++i) {
211 d.rateSpreads[i]->setValue(h2);
212 Real refDeltaRate = (option->NPV() - npv) / h2;
213 d.rateSpreads[i]->setValue(0.0);
214 d.divSpreads[i]->setValue(h2);
215 Real refDeltaDiv = (option->NPV() - npv) / h2;
216 d.divSpreads[i]->setValue(0.0);
217 if (!
check(refDeltaRate, deltaRate[i]))
218 BOOST_ERROR(
"delta on pillar " << d.pillarTimes[i] <<
" (rate curve) could not be verified, analytical: "
219 << deltaRate[i] <<
", bump and revalue: " << refDeltaRate);
220 if (!
check(refDeltaDiv, deltaDividend[i]))
221 BOOST_ERROR(
"delta on pillar " << d.pillarTimes[i]
222 <<
" (dividend curve) could not be verified, analytical: "
223 << deltaDividend[i] <<
", bump and revalue: " << refDeltaDiv);
226 Matrix refGammaRateDiv(n * 2, n * 2, 0.0);
229 for (Size i = 0; i < n; ++i) {
231 for (Size j = 0; j < i; ++j) {
232 d.rateSpreads[i]->setValue(h1);
233 d.rateSpreads[j]->setValue(h1);
234 Real npvpp = option->NPV();
235 d.rateSpreads[j]->setValue(0.0);
236 Real npvp0 = option->NPV();
237 d.rateSpreads[i]->setValue(0.0);
238 d.rateSpreads[j]->setValue(h1);
239 Real npv0p = option->NPV();
240 d.rateSpreads[j]->setValue(0.0);
241 Real gamma = (npvpp - npvp0 - npv0p + npv) / (h1 * h1);
242 refGammaRateDiv[i][j] = refGammaRateDiv[j][i] = gamma;
245 d.rateSpreads[i]->setValue(2.0 * h1);
246 Real npvpp = option->NPV();
247 d.rateSpreads[i]->setValue(h1);
248 Real npvp = option->NPV();
249 d.rateSpreads[i]->setValue(0.0);
250 Real gamma = (npvpp - 2.0 * npvp + npv) / (h1 * h1);
251 refGammaRateDiv[i][i] = gamma;
255 for (Size i = 0; i < n; ++i) {
257 for (Size j = 0; j < n; ++j) {
258 d.rateSpreads[i]->setValue(h1);
259 d.divSpreads[j]->setValue(h1);
260 Real npvpp = option->NPV();
261 d.divSpreads[j]->setValue(0.0);
262 Real npvp0 = option->NPV();
263 d.rateSpreads[i]->setValue(0.0);
264 d.divSpreads[j]->setValue(h1);
265 Real npv0p = option->NPV();
266 d.divSpreads[j]->setValue(0.0);
267 Real gamma = (npvpp - npvp0 - npv0p + npv) / (h1 * h1);
268 refGammaRateDiv[i][n + j] = refGammaRateDiv[n + j][i] = gamma;
273 for (Size i = 0; i < n; ++i) {
275 for (Size j = 0; j < i; ++j) {
276 d.divSpreads[i]->setValue(h1);
277 d.divSpreads[j]->setValue(h1);
278 Real npvpp = option->NPV();
279 d.divSpreads[j]->setValue(0.0);
280 Real npvp0 = option->NPV();
281 d.divSpreads[i]->setValue(0.0);
282 d.divSpreads[j]->setValue(h1);
283 Real npv0p = option->NPV();
284 d.divSpreads[j]->setValue(0.0);
285 Real gamma = (npvpp - npvp0 - npv0p + npv) / (h1 * h1);
286 refGammaRateDiv[n + i][n + j] = refGammaRateDiv[n + j][n + i] = gamma;
289 d.divSpreads[i]->setValue(2.0 * h1);
290 Real npvpp = option->NPV();
291 d.divSpreads[i]->setValue(h1);
292 Real npvp = option->NPV();
293 d.divSpreads[i]->setValue(0.0);
294 Real gamma = (npvpp - 2.0 * npvp + npv0) / (h1 * h1);
295 refGammaRateDiv[n + i][n + i] = gamma;
298 for (Size i = 0; i < 2 * n; ++i) {
299 for (Size j = 0; j < 2 * n; ++j) {
300 if (!
check(refGammaRateDiv[i][j], gamma[i][j]))
301 BOOST_ERROR(
"gamma entry (" << i <<
"," << j <<
") is " << gamma[i][j]
302 <<
", bump and revalue result is " << refGammaRateDiv[i][j]);
307 for (Size i = 0; i < n; ++i) {
308 d.spot->setValue(d.spot->value() + h1);
309 d.rateSpreads[i]->setValue(h1);
310 Real npvpp = option->NPV();
311 d.rateSpreads[i]->setValue(0.0);
312 Real npvp0 = option->NPV();
313 d.spot->setValue(d.spot->value() - h1);
314 d.rateSpreads[i]->setValue(h1);
315 Real npv0p = option->NPV();
316 d.rateSpreads[i]->setValue(0.0);
317 Real refGamma = (npvpp - npvp0 - npv0p + npv) / (h1 * h1);
318 if (!
check(refGamma, gammaSpotRate[i]))
319 BOOST_ERROR(
"spot-rate gamma pillar " << i <<
" can not be verified, result is " << gammaSpotRate[i]
320 <<
", bump and revalue is " << refGamma);
324 for (Size i = 0; i < n; ++i) {
325 d.spot->setValue(d.spot->value() + h1);
326 d.divSpreads[i]->setValue(h1);
327 Real npvpp = option->NPV();
328 d.divSpreads[i]->setValue(0.0);
329 Real npvp0 = option->NPV();
330 d.spot->setValue(d.spot->value() - h1);
331 d.divSpreads[i]->setValue(h1);
332 Real npv0p = option->NPV();
333 d.divSpreads[i]->setValue(0.0);
334 Real refGamma = (npvpp - npvp0 - npv0p + npv) / (h1 * h1);
335 if (!
check(refGamma, gammaSpotDiv[i]))
336 BOOST_ERROR(
"spot-div gamma pillar " << i <<
" can not be verified, result is " << gammaSpotDiv[i]
337 <<
", bump and revalue is " << refGamma);
344BOOST_AUTO_TEST_SUITE_END()
346BOOST_AUTO_TEST_SUITE_END()
Analytic European engine providing sensitivities.
BOOST_AUTO_TEST_CASE(testNpvDeltasGammaVegas)
Fixture that can be used at top level.