[go: up one dir, main page]

1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2011 Master IMAFA - Polytech'Nice Sophia - Université de Nice Sophia Antipolis
5
6 This file is part of QuantLib, a free-software/open-source library
7 for financial quantitative analysts and developers - http://quantlib.org/
8
9 QuantLib is free software: you can redistribute it and/or modify it
10 under the terms of the QuantLib license. You should have received a
11 copy of the license along with this program; if not, please email
12 <quantlib-dev@lists.sf.net>. The license is also available online at
13 <http://quantlib.org/license.shtml>.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the license for more details.
18*/
19
20#include "spreadoption.hpp"
21#include "utilities.hpp"
22#include <ql/experimental/exoticoptions/kirkspreadoptionengine.hpp>
23#include <ql/exercise.hpp>
24#include <ql/quotes/simplequote.hpp>
25#include <ql/time/daycounters/actual360.hpp>
26#include <ql/utilities/dataformatters.hpp>
27
28using namespace QuantLib;
29using namespace boost::unit_test_framework;
30
31#undef REPORT_FAILURE
32#define REPORT_FAILURE( \
33 greekName, \
34 payoff, exercise, \
35 expected, calculated, tolerance) \
36 BOOST_ERROR( \
37 exerciseTypeToString(exercise) \
38 << " Spread option with " \
39 << payoffTypeToString(payoff) << " payoff:\n" \
40 << " strike: " << payoff->strike() << "\n" \
41 << " reference date: " << today << "\n" \
42 << " maturity: " << exercise->lastDate() << "\n" \
43 << " expected " << greekName << ": " << expected << "\n" \
44 << " calculated " << greekName << ": " << calculated << "\n" \
45 << " error: " << std::fabs(expected-calculated) \
46 << "\n" \
47 << " tolerance: " << tolerance);
48
49
50struct Case {
51 Real F1;
52 Real F2;
53 Real X;
54 Rate r;
55 Volatility sigma1;
56 Volatility sigma2;
57 Real rho;
58 Natural length;
59 Real value;
60 Real theta;
61};
62
63void SpreadOptionTest::testKirkEngine() {
64 BOOST_TEST_MESSAGE("Testing Kirk approximation for spread options...");
65
66 /* The example data below are from "complete guide to option
67 pricing formulas", Espen Gaarder Haug, p 60
68
69 Expected values of option theta were calculated using automatic
70 differentiation of the pricing function. The engine uses closed-form
71 formula */
72
73 Case cases[] = {
74 { .F1: 28.0, .F2: 20.0, .X: 7.0, .r: 0.05, .sigma1: 0.29, .sigma2: 0.36, .rho: 0.42, .length: 90, .value: 2.1670, .theta: -3.0431 },
75 { .F1: 122.0, .F2: 120.0, .X: 3.0, .r: 0.10, .sigma1: 0.20, .sigma2: 0.20, .rho: -0.5, .length: 36, .value: 4.7530, .theta: -25.5905 },
76 { .F1: 122.0, .F2: 120.0, .X: 3.0, .r: 0.10, .sigma1: 0.20, .sigma2: 0.20, .rho: 0.0, .length: 36, .value: 3.7970, .theta: -20.8841 },
77 { .F1: 122.0, .F2: 120.0, .X: 3.0, .r: 0.10, .sigma1: 0.20, .sigma2: 0.20, .rho: 0.5, .length: 36, .value: 2.5537, .theta: -14.7260 },
78 { .F1: 122.0, .F2: 120.0, .X: 3.0, .r: 0.10, .sigma1: 0.20, .sigma2: 0.20, .rho: -0.5, .length: 180, .value: 10.7517, .theta: -10.0847 },
79 { .F1: 122.0, .F2: 120.0, .X: 3.0, .r: 0.10, .sigma1: 0.20, .sigma2: 0.20, .rho: 0.0, .length: 180, .value: 8.7020, .theta: -8.2619 },
80 { .F1: 122.0, .F2: 120.0, .X: 3.0, .r: 0.10, .sigma1: 0.20, .sigma2: 0.20, .rho: 0.5, .length: 180, .value: 6.0257, .theta: -5.8661 },
81 { .F1: 122.0, .F2: 120.0, .X: 3.0, .r: 0.10, .sigma1: 0.25, .sigma2: 0.20, .rho: -0.5, .length: 36, .value: 5.4275, .theta: -28.9013 },
82 { .F1: 122.0, .F2: 120.0, .X: 3.0, .r: 0.10, .sigma1: 0.25, .sigma2: 0.20, .rho: 0.0, .length: 36, .value: 4.3712, .theta: -23.7133 },
83 { .F1: 122.0, .F2: 120.0, .X: 3.0, .r: 0.10, .sigma1: 0.25, .sigma2: 0.20, .rho: 0.5, .length: 36, .value: 3.0086, .theta: -16.9864 },
84 { .F1: 122.0, .F2: 120.0, .X: 3.0, .r: 0.10, .sigma1: 0.25, .sigma2: 0.20, .rho: -0.5, .length: 180, .value: 12.1941, .theta: -11.3603 },
85 { .F1: 122.0, .F2: 120.0, .X: 3.0, .r: 0.10, .sigma1: 0.25, .sigma2: 0.20, .rho: 0.0, .length: 180, .value: 9.9340, .theta: -9.3589 },
86 { .F1: 122.0, .F2: 120.0, .X: 3.0, .r: 0.10, .sigma1: 0.25, .sigma2: 0.20, .rho: 0.5, .length: 180, .value: 7.0067, .theta: -6.7463 },
87 { .F1: 122.0, .F2: 120.0, .X: 3.0, .r: 0.10, .sigma1: 0.20, .sigma2: 0.25, .rho: -0.5, .length: 36, .value: 5.4061, .theta: -28.7963 },
88 { .F1: 122.0, .F2: 120.0, .X: 3.0, .r: 0.10, .sigma1: 0.20, .sigma2: 0.25, .rho: 0.0, .length: 36, .value: 4.3451, .theta: -23.5848 },
89 { .F1: 122.0, .F2: 120.0, .X: 3.0, .r: 0.10, .sigma1: 0.20, .sigma2: 0.25, .rho: 0.5, .length: 36, .value: 2.9723, .theta: -16.8060 },
90 { .F1: 122.0, .F2: 120.0, .X: 3.0, .r: 0.10, .sigma1: 0.20, .sigma2: 0.25, .rho: -0.5, .length: 180, .value: 12.1483, .theta: -11.3200 },
91 { .F1: 122.0, .F2: 120.0, .X: 3.0, .r: 0.10, .sigma1: 0.20, .sigma2: 0.25, .rho: 0.0, .length: 180, .value: 9.8780, .theta: -9.3091 },
92 { .F1: 122.0, .F2: 120.0, .X: 3.0, .r: 0.10, .sigma1: 0.20, .sigma2: 0.25, .rho: 0.5, .length: 180, .value: 6.9284, .theta: -6.6761 }
93 };
94
95 for (auto& i : cases) {
96
97 // First step: preparing the test values
98 // Useful dates
99 DayCounter dc = Actual360();
100 Date today = Date::todaysDate();
101 Date exerciseDate = today + i.length;
102
103 // Futures values
104 ext::shared_ptr<SimpleQuote> F1(new SimpleQuote(i.F1));
105 ext::shared_ptr<SimpleQuote> F2(new SimpleQuote(i.F2));
106
107 // Risk-free interest rate
108 Rate riskFreeRate = i.r;
109 ext::shared_ptr<YieldTermStructure> forwardRate =
110 flatRate(today,forward: riskFreeRate,dc);
111
112 // Correlation
113 ext::shared_ptr<Quote> rho(new SimpleQuote(i.rho));
114
115 // Volatilities
116 Volatility vol1 = i.sigma1;
117 Volatility vol2 = i.sigma2;
118 ext::shared_ptr<BlackVolTermStructure> volTS1 =
119 flatVol(today,volatility: vol1,dc);
120 ext::shared_ptr<BlackVolTermStructure> volTS2 =
121 flatVol(today,volatility: vol2,dc);
122
123 // Black-Scholes Processes
124 // The BlackProcess is the relevant class for futures contracts
125 ext::shared_ptr<BlackProcess> stochProcess1(
126 new BlackProcess(Handle<Quote>(F1),
127 Handle<YieldTermStructure>(forwardRate),
128 Handle<BlackVolTermStructure>(volTS1)));
129
130 ext::shared_ptr<BlackProcess> stochProcess2(
131 new BlackProcess(Handle<Quote>(F2),
132 Handle<YieldTermStructure>(forwardRate),
133 Handle<BlackVolTermStructure>(volTS2)));
134
135 // Creating the pricing engine
136 ext::shared_ptr<PricingEngine> engine(
137 new KirkSpreadOptionEngine(stochProcess1, stochProcess2,
138 Handle<Quote>(rho)));
139
140 // Finally, create the option:
141 Option::Type type = Option::Call;
142 Real strike = i.X;
143 ext::shared_ptr<PlainVanillaPayoff> payoff(
144 new PlainVanillaPayoff(type, strike));
145 ext::shared_ptr<Exercise> exercise(
146 new EuropeanExercise(exerciseDate));
147
148 SpreadOption option(payoff, exercise);
149 option.setPricingEngine(engine);
150
151 // And test the data
152 Real value = option.NPV();
153 Real theta = option.theta();
154 Real tolerance = 1e-4;
155
156 if (std::fabs(x: value - i.value) > tolerance) {
157 REPORT_FAILURE("value", payoff, exercise, i.value, value, tolerance);
158 }
159
160 if (std::fabs(x: theta - i.theta) > tolerance) {
161 REPORT_FAILURE("theta", payoff, exercise, i.theta, theta, tolerance);
162 }
163 }
164}
165
166test_suite* SpreadOptionTest::suite() {
167 auto* suite = BOOST_TEST_SUITE("Spread option tests");
168
169 suite->add(QUANTLIB_TEST_CASE(&SpreadOptionTest::testKirkEngine));
170
171 return suite;
172}
173
174

source code of quantlib/test-suite/spreadoption.cpp