QuantLib: a free/open-source library for quantitative finance
Fully annotated sources - version 1.32
Loading...
Searching...
No Matches
analyticbinarybarrierengine.cpp
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2014 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/instruments/vanillaoption.hpp>
22#include <ql/math/distributions/normaldistribution.hpp>
23#include <ql/pricingengines/barrier/analyticbinarybarrierengine.hpp>
24#include <ql/pricingengines/vanilla/analyticeuropeanengine.hpp>
25#include <utility>
26
27namespace QuantLib {
28
29 // calc helper object
30 class AnalyticBinaryBarrierEngine_helper
31 {
32
33 public:
34 AnalyticBinaryBarrierEngine_helper(
35 const ext::shared_ptr<GeneralizedBlackScholesProcess>& process,
36 const ext::shared_ptr<StrikedTypePayoff> &payoff,
37 const ext::shared_ptr<AmericanExercise> &exercise,
38 const BarrierOption::arguments &arguments):
39 process_(process),
40 payoff_(payoff),
41 exercise_(exercise),
42 arguments_(arguments)
43 {
44 }
45
46 Real payoffAtExpiry(Real spot, Real variance, Real discount);
47 private:
48 const ext::shared_ptr<GeneralizedBlackScholesProcess>& process_;
49 const ext::shared_ptr<StrikedTypePayoff> &payoff_;
50 const ext::shared_ptr<AmericanExercise> &exercise_;
51 const BarrierOption::arguments &arguments_;
52 };
53
54
56 ext::shared_ptr<GeneralizedBlackScholesProcess> process)
57 : process_(std::move(process)) {
59 }
60
62
63 ext::shared_ptr<AmericanExercise> ex =
64 ext::dynamic_pointer_cast<AmericanExercise>(arguments_.exercise);
65 QL_REQUIRE(ex, "non-American exercise given");
66 QL_REQUIRE(ex->payoffAtExpiry(), "payoff must be at expiry");
67 QL_REQUIRE(ex->dates()[0] <=
68 process_->blackVolatility()->referenceDate(),
69 "American option with window exercise not handled yet");
70
71 ext::shared_ptr<StrikedTypePayoff> payoff =
72 ext::dynamic_pointer_cast<StrikedTypePayoff>(arguments_.payoff);
73 QL_REQUIRE(payoff, "non-striked payoff given");
74
75 Real spot = process_->stateVariable()->value();
76 QL_REQUIRE(spot > 0.0, "negative or null underlying given");
77
78 Real variance =
79 process_->blackVolatility()->blackVariance(ex->lastDate(),
80 payoff->strike());
81 Real barrier = arguments_.barrier;
82 QL_REQUIRE(barrier>0.0,
83 "positive barrier value required");
85
86 // KO degenerate cases
87 if ( (barrierType == Barrier::DownOut && spot <= barrier) ||
88 (barrierType == Barrier::UpOut && spot >= barrier))
89 {
90 // knocked out, no value
91 results_.value = 0;
92 results_.delta = 0;
93 results_.gamma = 0;
94 results_.vega = 0;
95 results_.theta = 0;
96 results_.rho = 0;
97 results_.dividendRho = 0;
98 return;
99 }
100
101 // KI degenerate cases
102 if ((barrierType == Barrier::DownIn && spot <= barrier) ||
103 (barrierType == Barrier::UpIn && spot >= barrier)) {
104 // knocked in - is a digital european
105 ext::shared_ptr<Exercise> exercise(new EuropeanExercise(
106 arguments_.exercise->lastDate()));
107
108 ext::shared_ptr<PricingEngine> engine(
110
111 VanillaOption opt(payoff, exercise);
112 opt.setPricingEngine(engine);
113 results_.value = opt.NPV();
114 results_.delta = opt.delta();
115 results_.gamma = opt.gamma();
116 results_.vega = opt.vega();
117 results_.theta = opt.theta();
118 results_.rho = opt.rho();
119 results_.dividendRho = opt.dividendRho();
120 return;
121 }
122
123 Rate riskFreeDiscount =
124 process_->riskFreeRate()->discount(ex->lastDate());
125
126 AnalyticBinaryBarrierEngine_helper helper(process_,
127 payoff, ex, arguments_);
128 results_.value = helper.payoffAtExpiry(spot, variance, riskFreeDiscount);
129 }
130
131 Real AnalyticBinaryBarrierEngine_helper::payoffAtExpiry(
132 Real spot, Real variance, Real discount)
133 {
134 Rate dividendDiscount =
135 process_->dividendYield()->discount(exercise_->lastDate());
136
137 QL_REQUIRE(spot>0.0,
138 "positive spot value required");
139
140 QL_REQUIRE(discount>0.0,
141 "positive discount required");
142
143 QL_REQUIRE(dividendDiscount>0.0,
144 "positive dividend discount required");
145
146 QL_REQUIRE(variance>=0.0,
147 "negative variance not allowed");
148
149 Option::Type type = payoff_->optionType();
150 Real strike = payoff_->strike();
151 Real barrier = arguments_.barrier;
152 QL_REQUIRE(barrier>0.0,
153 "positive barrier value required");
154 Barrier::Type barrierType = arguments_.barrierType;
155
156 Real stdDev = std::sqrt(variance);
157 Real mu = std::log(dividendDiscount/discount)/variance - 0.5;
158 Real K = 0;
159
160 // binary cash-or-nothing payoff?
161 ext::shared_ptr<CashOrNothingPayoff> coo =
162 ext::dynamic_pointer_cast<CashOrNothingPayoff>(payoff_);
163 if (coo != nullptr) {
164 K = coo->cashPayoff();
165 }
166
167 // binary asset-or-nothing payoff?
168 ext::shared_ptr<AssetOrNothingPayoff> aoo =
169 ext::dynamic_pointer_cast<AssetOrNothingPayoff>(payoff_);
170 if (aoo != nullptr) {
171 mu += 1.0;
172 K = spot * dividendDiscount / discount; // forward
173 }
174
175 Real log_S_X = std::log(spot/strike);
176 Real log_S_H = std::log(spot/barrier);
177 Real log_H_S = std::log(barrier/spot);
178 Real log_H2_SX = std::log(barrier*barrier/(spot*strike));
179 Real H_S_2mu = std::pow(barrier/spot, 2*mu);
180
181 Real eta = (barrierType == Barrier::DownIn ||
182 barrierType == Barrier::DownOut ? 1.0 : -1.0);
183 Real phi = (type == Option::Call ? 1.0 : -1.0);
184
185 Real x1, x2, y1, y2;
186 Real cum_x1, cum_x2, cum_y1, cum_y2;
187 if (variance>=QL_EPSILON) {
188
189 // we calculate using mu*stddev instead of (mu+1)*stddev
190 // because cash-or-nothing don't need it. asset-or-nothing
191 // mu is really mu+1
192 x1 = phi*(log_S_X/stdDev + mu*stdDev);
193 x2 = phi*(log_S_H/stdDev + mu*stdDev);
194 y1 = eta*(log_H2_SX/stdDev + mu*stdDev);
195 y2 = eta*(log_H_S/stdDev + mu*stdDev);
196
197 CumulativeNormalDistribution f;
198 cum_x1 = f(x1);
199 cum_x2 = f(x2);
200 cum_y1 = f(y1);
201 cum_y2 = f(y2);
202 } else {
203 if (log_S_X>0)
204 cum_x1= 1.0;
205 else
206 cum_x1= 0.0;
207 if (log_S_H>0)
208 cum_x2= 1.0;
209 else
210 cum_x2= 0.0;
211 if (log_H2_SX>0)
212 cum_y1= 1.0;
213 else
214 cum_y1= 0.0;
215 if (log_H_S>0)
216 cum_y2= 1.0;
217 else
218 cum_y2= 0.0;
219 }
220
221 Real alpha = 0;
222
223 switch (barrierType) {
224 case Barrier::DownIn:
225 if (type == Option::Call) {
226 // down-in and call
227 if (strike >= barrier) {
228 // B3 (eta=1, phi=1)
229 alpha = H_S_2mu * cum_y1;
230 } else {
231 // B1-B2+B4 (eta=1, phi=1)
232 alpha = cum_x1 - cum_x2 + H_S_2mu * cum_y2;
233 }
234 }
235 else {
236 // down-in and put
237 if (strike >= barrier) {
238 // B2-B3+B4 (eta=1, phi=-1)
239 alpha = cum_x2 + H_S_2mu*(-cum_y1 + cum_y2);
240 } else {
241 // B1 (eta=1, phi=-1)
242 alpha = cum_x1;
243 }
244 }
245 break;
246
247 case Barrier::UpIn:
248 if (type == Option::Call) {
249 // up-in and call
250 if (strike >= barrier) {
251 // B1 (eta=-1, phi=1)
252 alpha = cum_x1;
253 } else {
254 // B2-B3+B4 (eta=-1, phi=1)
255 alpha = cum_x2 + H_S_2mu * (-cum_y1 + cum_y2);
256 }
257 }
258 else {
259 // up-in and put
260 if (strike >= barrier) {
261 // B1-B2+B4 (eta=-1, phi=-1)
262 alpha = cum_x1 - cum_x2 + H_S_2mu * cum_y2;
263 } else {
264 // B3 (eta=-1, phi=-1)
265 alpha = H_S_2mu * cum_y1;
266 }
267 }
268 break;
269
270 case Barrier::DownOut:
271 if (type == Option::Call) {
272 // down-out and call
273 if (strike >= barrier) {
274 // B1-B3 (eta=1, phi=1)
275 alpha = cum_x1 - H_S_2mu * cum_y1;
276 } else {
277 // B2-B4 (eta=1, phi=1)
278 alpha = cum_x2 - H_S_2mu * cum_y2;
279 }
280 }
281 else {
282 // down-out and put
283 if (strike >= barrier) {
284 // B1-B2+B3-B4 (eta=1, phi=-1)
285 alpha = cum_x1 - cum_x2 + H_S_2mu * (cum_y1-cum_y2);
286 } else {
287 // always 0
288 alpha = 0;
289 }
290 }
291 break;
292 case Barrier::UpOut:
293 if (type == Option::Call) {
294 // up-out and call
295 if (strike >= barrier) {
296 // always 0
297 alpha = 0;
298 } else {
299 // B1-B2+B3-B4 (eta=-1, phi=1)
300 alpha = cum_x1 - cum_x2 + H_S_2mu * (cum_y1-cum_y2);
301 }
302 }
303 else {
304 // up-out and put
305 if (strike >= barrier) {
306 // B2-B4 (eta=-1, phi=-1)
307 alpha = cum_x2 - H_S_2mu * cum_y2;
308 } else {
309 // B1-B3 (eta=-1, phi=-1)
310 alpha = cum_x1 - H_S_2mu * cum_y1;
311 }
312 }
313 break;
314 default:
315 QL_FAIL("invalid barrier type");
316 }
317
318 return discount * K * alpha;
319 }
320
321
322
323}
324
AnalyticBinaryBarrierEngine(ext::shared_ptr< GeneralizedBlackScholesProcess >)
ext::shared_ptr< GeneralizedBlackScholesProcess > process_
Pricing engine for European vanilla options using analytical formulae.
European exercise.
Definition: exercise.hpp:96
Real NPV() const
returns the net present value of the instrument.
Definition: instrument.hpp:167
void setPricingEngine(const ext::shared_ptr< PricingEngine > &)
set the pricing engine to be used.
Definition: instrument.cpp:35
std::pair< iterator, bool > registerWith(const ext::shared_ptr< Observable > &)
Definition: observable.hpp:228
Vanilla option (no discrete dividends, no barriers) on a single asset.
#define QL_EPSILON
Definition: qldefines.hpp:178
QL_REAL Real
real number
Definition: types.hpp:50
Real Rate
interest rates
Definition: types.hpp:70
Definition: any.hpp:35
STL namespace.