/src/quantlib/ql/pricingengines/barrier/analyticdoublebarrierbinaryengine.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
24 | | using std::fabs; |
25 | | |
26 | | namespace 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 | 0 | process_(process), |
41 | 0 | payoff_(payoff), |
42 | 0 | arguments_(arguments) |
43 | 0 | { |
44 | 0 | } |
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 | 0 | { |
68 | 0 | QL_REQUIRE(spot>0.0, |
69 | 0 | "positive spot value required"); |
70 | | |
71 | 0 | QL_REQUIRE(variance>=0.0, |
72 | 0 | "negative variance not allowed"); |
73 | | |
74 | 0 | Time residualTime = process_->time(arguments_.exercise->lastDate()); |
75 | 0 | QL_REQUIRE(residualTime>0.0, |
76 | 0 | "expiration time must be > 0"); |
77 | | |
78 | | // Option::Type type = payoff_->optionType(); // this is not used ? |
79 | 0 | Real cash = payoff_->cashPayoff(); |
80 | 0 | Real barrier_lo = arguments_.barrier_lo; |
81 | 0 | Real barrier_hi = arguments_.barrier_hi; |
82 | |
|
83 | 0 | Real sigmaq = variance/residualTime; |
84 | 0 | Real r = process_->riskFreeRate()->zeroRate(residualTime, Continuous, |
85 | 0 | NoFrequency); |
86 | 0 | Real q = process_->dividendYield()->zeroRate(residualTime, |
87 | 0 | Continuous, NoFrequency); |
88 | 0 | Real b = r - q; |
89 | |
|
90 | 0 | Real alpha = -0.5 * ( 2*b/sigmaq - 1); |
91 | 0 | Real beta = -0.25 * std::pow(( 2*b/sigmaq - 1), 2) - 2 * r/sigmaq; |
92 | 0 | Real Z = std::log(barrier_hi / barrier_lo); |
93 | 0 | Real factor = ((2*PI*cash)/std::pow(Z,2)); // common factor |
94 | 0 | Real lo_alpha = std::pow(spot/barrier_lo, alpha); |
95 | 0 | Real hi_alpha = std::pow(spot/barrier_hi, alpha); |
96 | |
|
97 | 0 | Real tot = 0, term = 0; |
98 | 0 | for (Size i = 1 ; i < maxIteration ; ++i) |
99 | 0 | { |
100 | 0 | Real term1 = (lo_alpha-std::pow(-1.0, (int)i)*hi_alpha) / |
101 | 0 | (std::pow(alpha,2)+std::pow(i*PI/Z, 2)); |
102 | 0 | Real term2 = std::sin(i*PI/Z * std::log(spot/barrier_lo)); |
103 | 0 | Real term3 = std::exp(-0.5*(std::pow(i*PI/Z,2)-beta)*variance); |
104 | 0 | term = factor * i * term1 * term2 * term3; |
105 | 0 | tot += term; |
106 | 0 | } |
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 | 0 | QL_REQUIRE(std::fabs(term) < requiredConvergence, "serie did not converge sufficiently fast"); |
111 | | |
112 | 0 | if (barrierType == DoubleBarrier::KnockOut) |
113 | 0 | return std::max(tot, 0.0); // KO |
114 | 0 | else { |
115 | 0 | Rate discount = process_->riskFreeRate()->discount( |
116 | 0 | arguments_.exercise->lastDate()); |
117 | 0 | QL_REQUIRE(discount>0.0, |
118 | 0 | "positive discount required"); |
119 | 0 | return std::max(cash * discount - tot, 0.0); // KI |
120 | 0 | } |
121 | 0 | } |
122 | | |
123 | | // helper object methods |
124 | | Real AnalyticDoubleBarrierBinaryEngine_helper::payoffKIKO( |
125 | | Real spot, Real variance, DoubleBarrier::Type barrierType, |
126 | | Size maxIteration, Real requiredConvergence) |
127 | 0 | { |
128 | 0 | QL_REQUIRE(spot>0.0, |
129 | 0 | "positive spot value required"); |
130 | | |
131 | 0 | QL_REQUIRE(variance>=0.0, |
132 | 0 | "negative variance not allowed"); |
133 | | |
134 | 0 | Time residualTime = process_->time(arguments_.exercise->lastDate()); |
135 | 0 | QL_REQUIRE(residualTime>0.0, |
136 | 0 | "expiration time must be > 0"); |
137 | | |
138 | 0 | Real cash = payoff_->cashPayoff(); |
139 | 0 | Real barrier_lo = arguments_.barrier_lo; |
140 | 0 | Real barrier_hi = arguments_.barrier_hi; |
141 | 0 | if (barrierType == DoubleBarrier::KOKI) |
142 | 0 | std::swap(barrier_lo, barrier_hi); |
143 | |
|
144 | 0 | Real sigmaq = variance/residualTime; |
145 | 0 | Real r = process_->riskFreeRate()->zeroRate(residualTime, Continuous, |
146 | 0 | NoFrequency); |
147 | 0 | Real q = process_->dividendYield()->zeroRate(residualTime, |
148 | 0 | Continuous, NoFrequency); |
149 | 0 | Real b = r - q; |
150 | |
|
151 | 0 | Real alpha = -0.5 * ( 2*b/sigmaq - 1); |
152 | 0 | Real beta = -0.25 * std::pow(( 2*b/sigmaq - 1), 2) - 2 * r/sigmaq; |
153 | 0 | Real Z = std::log(barrier_hi / barrier_lo); |
154 | 0 | Real log_S_L = std::log(spot / barrier_lo); |
155 | |
|
156 | 0 | Real tot = 0, term = 0; |
157 | 0 | for (Size i = 1 ; i < maxIteration ; ++i) |
158 | 0 | { |
159 | 0 | Real factor = std::pow(i*PI/Z,2)-beta; |
160 | 0 | Real term1 = (beta - std::pow(i*PI/Z,2) * std::exp(-0.5*factor*variance)) / factor; |
161 | 0 | Real term2 = std::sin(i * PI/Z * log_S_L); |
162 | 0 | term = (2.0/(i*PI)) * term1 * term2; |
163 | 0 | tot += term; |
164 | 0 | } |
165 | 0 | tot += 1 - log_S_L / Z; |
166 | 0 | tot *= cash*std::pow(spot/barrier_lo, alpha); |
167 | | |
168 | | // Check if convergence is sufficiently fast |
169 | 0 | QL_REQUIRE(fabs(term) < requiredConvergence, "serie did not converge sufficiently fast"); |
170 | | |
171 | 0 | return std::max(tot, 0.0); |
172 | 0 | } |
173 | | |
174 | | AnalyticDoubleBarrierBinaryEngine::AnalyticDoubleBarrierBinaryEngine( |
175 | | ext::shared_ptr<GeneralizedBlackScholesProcess> process) |
176 | 0 | : process_(std::move(process)) { |
177 | 0 | registerWith(process_); |
178 | 0 | } |
179 | | |
180 | 0 | void AnalyticDoubleBarrierBinaryEngine::calculate() const { |
181 | |
|
182 | 0 | if (arguments_.barrierType == DoubleBarrier::KIKO || |
183 | 0 | arguments_.barrierType == DoubleBarrier::KOKI) { |
184 | 0 | ext::shared_ptr<AmericanExercise> ex = |
185 | 0 | ext::dynamic_pointer_cast<AmericanExercise>( |
186 | 0 | arguments_.exercise); |
187 | 0 | QL_REQUIRE(ex, "KIKO/KOKI options must have American exercise"); |
188 | 0 | QL_REQUIRE(ex->dates()[0] <= |
189 | 0 | process_->blackVolatility()->referenceDate(), |
190 | 0 | "American option with window exercise not handled yet"); |
191 | 0 | } else { |
192 | 0 | ext::shared_ptr<EuropeanExercise> ex = |
193 | 0 | ext::dynamic_pointer_cast<EuropeanExercise>( |
194 | 0 | arguments_.exercise); |
195 | 0 | QL_REQUIRE(ex, "non-European exercise given"); |
196 | 0 | } |
197 | 0 | ext::shared_ptr<CashOrNothingPayoff> payoff = |
198 | 0 | ext::dynamic_pointer_cast<CashOrNothingPayoff>(arguments_.payoff); |
199 | 0 | QL_REQUIRE(payoff, "a cash-or-nothing payoff must be given"); |
200 | | |
201 | 0 | Real spot = process_->stateVariable()->value(); |
202 | 0 | QL_REQUIRE(spot > 0.0, "negative or null underlying given"); |
203 | | |
204 | 0 | Real variance = |
205 | 0 | process_->blackVolatility()->blackVariance( |
206 | 0 | arguments_.exercise->lastDate(), |
207 | 0 | payoff->strike()); |
208 | 0 | Real barrier_lo = arguments_.barrier_lo; |
209 | 0 | Real barrier_hi = arguments_.barrier_hi; |
210 | 0 | DoubleBarrier::Type barrierType = arguments_.barrierType; |
211 | 0 | QL_REQUIRE(barrier_lo>0.0, |
212 | 0 | "positive low barrier value required"); |
213 | 0 | QL_REQUIRE(barrier_hi>0.0, |
214 | 0 | "positive high barrier value required"); |
215 | 0 | QL_REQUIRE(barrier_lo < barrier_hi, |
216 | 0 | "barrier_lo must be < barrier_hi"); |
217 | 0 | QL_REQUIRE(barrierType == DoubleBarrier::KnockIn || |
218 | 0 | barrierType == DoubleBarrier::KnockOut || |
219 | 0 | barrierType == DoubleBarrier::KIKO || |
220 | 0 | barrierType == DoubleBarrier::KOKI, |
221 | 0 | "Unsupported barrier type"); |
222 | | |
223 | | // degenerate cases |
224 | 0 | switch (barrierType) { |
225 | 0 | case DoubleBarrier::KnockOut: |
226 | 0 | if (spot <= barrier_lo || spot >= barrier_hi) { |
227 | | // knocked out, no value |
228 | 0 | results_.value = 0; |
229 | 0 | results_.delta = 0; |
230 | 0 | results_.gamma = 0; |
231 | 0 | results_.vega = 0; |
232 | 0 | results_.rho = 0; |
233 | 0 | return; |
234 | 0 | } |
235 | 0 | break; |
236 | | |
237 | 0 | case DoubleBarrier::KnockIn: |
238 | 0 | if (spot <= barrier_lo || spot >= barrier_hi) { |
239 | | // knocked in - pays |
240 | 0 | results_.value = payoff->cashPayoff(); |
241 | 0 | results_.delta = 0; |
242 | 0 | results_.gamma = 0; |
243 | 0 | results_.vega = 0; |
244 | 0 | results_.rho = 0; |
245 | 0 | return; |
246 | 0 | } |
247 | 0 | break; |
248 | | |
249 | 0 | case DoubleBarrier::KIKO: |
250 | 0 | if (spot >= barrier_hi) { |
251 | | // knocked out, no value |
252 | 0 | results_.value = 0; |
253 | 0 | results_.delta = 0; |
254 | 0 | results_.gamma = 0; |
255 | 0 | results_.vega = 0; |
256 | 0 | results_.rho = 0; |
257 | 0 | return; |
258 | 0 | } else if (spot <= barrier_lo) { |
259 | | // knocked in, pays |
260 | 0 | results_.value = payoff->cashPayoff(); |
261 | 0 | results_.delta = 0; |
262 | 0 | results_.gamma = 0; |
263 | 0 | results_.vega = 0; |
264 | 0 | results_.rho = 0; |
265 | 0 | return; |
266 | 0 | } |
267 | 0 | break; |
268 | | |
269 | 0 | case DoubleBarrier::KOKI: |
270 | 0 | if (spot <= barrier_lo) { |
271 | | // knocked out, no value |
272 | 0 | results_.value = 0; |
273 | 0 | results_.delta = 0; |
274 | 0 | results_.gamma = 0; |
275 | 0 | results_.vega = 0; |
276 | 0 | results_.rho = 0; |
277 | 0 | return; |
278 | 0 | } else if (spot >= barrier_hi) { |
279 | | // knocked in, pays |
280 | 0 | results_.value = payoff->cashPayoff(); |
281 | 0 | results_.delta = 0; |
282 | 0 | results_.gamma = 0; |
283 | 0 | results_.vega = 0; |
284 | 0 | results_.rho = 0; |
285 | 0 | return; |
286 | 0 | } |
287 | 0 | break; |
288 | 0 | } |
289 | | |
290 | 0 | AnalyticDoubleBarrierBinaryEngine_helper helper(process_, |
291 | 0 | payoff, arguments_); |
292 | 0 | switch (barrierType) |
293 | 0 | { |
294 | 0 | case DoubleBarrier::KnockOut: |
295 | 0 | case DoubleBarrier::KnockIn: |
296 | 0 | results_.value = helper.payoffAtExpiry(spot, variance, barrierType); |
297 | 0 | break; |
298 | | |
299 | 0 | case DoubleBarrier::KIKO: |
300 | 0 | case DoubleBarrier::KOKI: |
301 | 0 | results_.value = helper.payoffKIKO(spot, variance, barrierType); |
302 | 0 | break; |
303 | 0 | } |
304 | 0 | } |
305 | | |
306 | | } |
307 | | |