QuantLib: a free/open-source library for quantitative finance
Fully annotated sources - version 1.32
Loading...
Searching...
No Matches
analyticdoublebarrierbinaryengine.cpp
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2015 Thema Consulting SA
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 <ql/exercise.hpp>
21#include <ql/pricingengines/barrier/analyticdoublebarrierbinaryengine.hpp>
22#include <utility>
23
24using std::fabs;
25
26namespace QuantLib {
27
28 // number of iterations ...
29 static Real PI= 3.14159265358979323846264338327950;
30
31 // calc helper object
32 class AnalyticDoubleBarrierBinaryEngine_helper
33 {
34
35 public:
36 AnalyticDoubleBarrierBinaryEngine_helper(
37 const ext::shared_ptr<GeneralizedBlackScholesProcess>& process,
38 const ext::shared_ptr<CashOrNothingPayoff> &payoff,
39 const DoubleBarrierOption::arguments &arguments):
40 process_(process),
41 payoff_(payoff),
42 arguments_(arguments)
43 {
44 }
45
46 Real payoffAtExpiry(Real spot, Real variance,
47 DoubleBarrier::Type barrierType,
48 Size maxIteration = 100,
49 Real requiredConvergence = 1e-8);
50 Real payoffKIKO(Real spot, Real variance,
51 DoubleBarrier::Type barrierType,
52 Size maxIteration = 1000,
53 Real requiredConvergence = 1e-8);
54
55 private:
56
57 const ext::shared_ptr<GeneralizedBlackScholesProcess>& process_;
58 const ext::shared_ptr<CashOrNothingPayoff> &payoff_;
59 const DoubleBarrierOption::arguments &arguments_;
60 };
61
62
63 // helper object methods
64 Real AnalyticDoubleBarrierBinaryEngine_helper::payoffAtExpiry(
65 Real spot, Real variance, DoubleBarrier::Type barrierType,
66 Size maxIteration, Real requiredConvergence)
67 {
68 QL_REQUIRE(spot>0.0,
69 "positive spot value required");
70
71 QL_REQUIRE(variance>=0.0,
72 "negative variance not allowed");
73
74 Time residualTime = process_->time(arguments_.exercise->lastDate());
75 QL_REQUIRE(residualTime>0.0,
76 "expiration time must be > 0");
77
78 // Option::Type type = payoff_->optionType(); // this is not used ?
79 Real cash = payoff_->cashPayoff();
80 Real barrier_lo = arguments_.barrier_lo;
81 Real barrier_hi = arguments_.barrier_hi;
82
83 Real sigmaq = variance/residualTime;
84 Real r = process_->riskFreeRate()->zeroRate(residualTime, Continuous,
86 Real q = process_->dividendYield()->zeroRate(residualTime,
88 Real b = r - q;
89
90 Real alpha = -0.5 * ( 2*b/sigmaq - 1);
91 Real beta = -0.25 * std::pow(( 2*b/sigmaq - 1), 2) - 2 * r/sigmaq;
92 Real Z = std::log(barrier_hi / barrier_lo);
93 Real factor = ((2*PI*cash)/std::pow(Z,2)); // common factor
94 Real lo_alpha = std::pow(spot/barrier_lo, alpha);
95 Real hi_alpha = std::pow(spot/barrier_hi, alpha);
96
97 Real tot = 0, term = 0;
98 for (Size i = 1 ; i < maxIteration ; ++i)
99 {
100 Real term1 = (lo_alpha-std::pow(-1.0, (int)i)*hi_alpha) /
101 (std::pow(alpha,2)+std::pow(i*PI/Z, 2));
102 Real term2 = std::sin(i*PI/Z * std::log(spot/barrier_lo));
103 Real term3 = std::exp(-0.5*(std::pow(i*PI/Z,2)-beta)*variance);
104 term = factor * i * term1 * term2 * term3;
105 tot += term;
106 }
107
108 // Check if convergence is sufficiently fast (for extreme parameters with big alpha the convergence can be very
109 // poor, see for example Hui "One-touch double barrier binary option value")
110 QL_REQUIRE(std::fabs(term) < requiredConvergence, "serie did not converge sufficiently fast");
111
112 if (barrierType == DoubleBarrier::KnockOut)
113 return std::max(tot, 0.0); // KO
114 else {
115 Rate discount = process_->riskFreeRate()->discount(
116 arguments_.exercise->lastDate());
117 QL_REQUIRE(discount>0.0,
118 "positive discount required");
119 return std::max(cash * discount - tot, 0.0); // KI
120 }
121 }
122
123 // helper object methods
124 Real AnalyticDoubleBarrierBinaryEngine_helper::payoffKIKO(
125 Real spot, Real variance, DoubleBarrier::Type barrierType,
126 Size maxIteration, Real requiredConvergence)
127 {
128 QL_REQUIRE(spot>0.0,
129 "positive spot value required");
130
131 QL_REQUIRE(variance>=0.0,
132 "negative variance not allowed");
133
134 Time residualTime = process_->time(arguments_.exercise->lastDate());
135 QL_REQUIRE(residualTime>0.0,
136 "expiration time must be > 0");
137
138 Real cash = payoff_->cashPayoff();
139 Real barrier_lo = arguments_.barrier_lo;
140 Real barrier_hi = arguments_.barrier_hi;
141 if (barrierType == DoubleBarrier::KOKI)
142 std::swap(barrier_lo, barrier_hi);
143
144 Real sigmaq = variance/residualTime;
145 Real r = process_->riskFreeRate()->zeroRate(residualTime, Continuous,
147 Real q = process_->dividendYield()->zeroRate(residualTime,
149 Real b = r - q;
150
151 Real alpha = -0.5 * ( 2*b/sigmaq - 1);
152 Real beta = -0.25 * std::pow(( 2*b/sigmaq - 1), 2) - 2 * r/sigmaq;
153 Real Z = std::log(barrier_hi / barrier_lo);
154 Real log_S_L = std::log(spot / barrier_lo);
155
156 Real tot = 0, term = 0;
157 for (Size i = 1 ; i < maxIteration ; ++i)
158 {
159 Real factor = std::pow(i*PI/Z,2)-beta;
160 Real term1 = (beta - std::pow(i*PI/Z,2) * std::exp(-0.5*factor*variance)) / factor;
161 Real term2 = std::sin(i * PI/Z * log_S_L);
162 term = (2.0/(i*PI)) * term1 * term2;
163 tot += term;
164 }
165 tot += 1 - log_S_L / Z;
166 tot *= cash*std::pow(spot/barrier_lo, alpha);
167
168 // Check if convergence is sufficiently fast
169 QL_REQUIRE(fabs(term) < requiredConvergence, "serie did not converge sufficiently fast");
170
171 return std::max(tot, 0.0);
172 }
173
175 ext::shared_ptr<GeneralizedBlackScholesProcess> process)
176 : process_(std::move(process)) {
178 }
179
181
184 ext::shared_ptr<AmericanExercise> ex =
185 ext::dynamic_pointer_cast<AmericanExercise>(
186 arguments_.exercise);
187 QL_REQUIRE(ex, "KIKO/KOKI options must have American exercise");
188 QL_REQUIRE(ex->dates()[0] <=
189 process_->blackVolatility()->referenceDate(),
190 "American option with window exercise not handled yet");
191 } else {
192 ext::shared_ptr<EuropeanExercise> ex =
193 ext::dynamic_pointer_cast<EuropeanExercise>(
194 arguments_.exercise);
195 QL_REQUIRE(ex, "non-European exercise given");
196 }
197 ext::shared_ptr<CashOrNothingPayoff> payoff =
198 ext::dynamic_pointer_cast<CashOrNothingPayoff>(arguments_.payoff);
199 QL_REQUIRE(payoff, "a cash-or-nothing payoff must be given");
200
201 Real spot = process_->stateVariable()->value();
202 QL_REQUIRE(spot > 0.0, "negative or null underlying given");
203
204 Real variance =
205 process_->blackVolatility()->blackVariance(
206 arguments_.exercise->lastDate(),
207 payoff->strike());
208 Real barrier_lo = arguments_.barrier_lo;
209 Real barrier_hi = arguments_.barrier_hi;
211 QL_REQUIRE(barrier_lo>0.0,
212 "positive low barrier value required");
213 QL_REQUIRE(barrier_hi>0.0,
214 "positive high barrier value required");
215 QL_REQUIRE(barrier_lo < barrier_hi,
216 "barrier_lo must be < barrier_hi");
217 QL_REQUIRE(barrierType == DoubleBarrier::KnockIn ||
218 barrierType == DoubleBarrier::KnockOut ||
219 barrierType == DoubleBarrier::KIKO ||
220 barrierType == DoubleBarrier::KOKI,
221 "Unsupported barrier type");
222
223 // degenerate cases
224 switch (barrierType) {
226 if (spot <= barrier_lo || spot >= barrier_hi) {
227 // knocked out, no value
228 results_.value = 0;
229 results_.delta = 0;
230 results_.gamma = 0;
231 results_.vega = 0;
232 results_.rho = 0;
233 return;
234 }
235 break;
236
238 if (spot <= barrier_lo || spot >= barrier_hi) {
239 // knocked in - pays
240 results_.value = payoff->cashPayoff();
241 results_.delta = 0;
242 results_.gamma = 0;
243 results_.vega = 0;
244 results_.rho = 0;
245 return;
246 }
247 break;
248
250 if (spot >= barrier_hi) {
251 // knocked out, no value
252 results_.value = 0;
253 results_.delta = 0;
254 results_.gamma = 0;
255 results_.vega = 0;
256 results_.rho = 0;
257 return;
258 } else if (spot <= barrier_lo) {
259 // knocked in, pays
260 results_.value = payoff->cashPayoff();
261 results_.delta = 0;
262 results_.gamma = 0;
263 results_.vega = 0;
264 results_.rho = 0;
265 return;
266 }
267 break;
268
270 if (spot <= barrier_lo) {
271 // knocked out, no value
272 results_.value = 0;
273 results_.delta = 0;
274 results_.gamma = 0;
275 results_.vega = 0;
276 results_.rho = 0;
277 return;
278 } else if (spot >= barrier_hi) {
279 // knocked in, pays
280 results_.value = payoff->cashPayoff();
281 results_.delta = 0;
282 results_.gamma = 0;
283 results_.vega = 0;
284 results_.rho = 0;
285 return;
286 }
287 break;
288 }
289
290 AnalyticDoubleBarrierBinaryEngine_helper helper(process_,
291 payoff, arguments_);
292 switch (barrierType)
293 {
296 results_.value = helper.payoffAtExpiry(spot, variance, barrierType);
297 break;
298
301 results_.value = helper.payoffKIKO(spot, variance, barrierType);
302 break;
303 }
304 }
305
306}
307
AnalyticDoubleBarrierBinaryEngine(ext::shared_ptr< GeneralizedBlackScholesProcess >)
ext::shared_ptr< GeneralizedBlackScholesProcess > process_
std::pair< iterator, bool > registerWith(const ext::shared_ptr< Observable > &)
Definition: observable.hpp:228
@ NoFrequency
null frequency
Definition: frequency.hpp:37
Real Time
continuous quantity with 1-year units
Definition: types.hpp:62
QL_REAL Real
real number
Definition: types.hpp:50
Real Rate
interest rates
Definition: types.hpp:70
std::size_t Size
size of a container
Definition: types.hpp:58
Definition: any.hpp:35
STL namespace.
@ KOKI
lower barrier KI, upper KO