/src/libreoffice/scaddins/source/pricing/black_scholes.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | * |
9 | | * Copyright (C) 2012 Tino Kluge <tino.kluge@hrz.tu-chemnitz.de> |
10 | | * |
11 | | */ |
12 | | |
13 | | #include <cstdio> |
14 | | #include <cstdlib> |
15 | | #include <cmath> |
16 | | #include <cassert> |
17 | | #include <algorithm> |
18 | | #include <rtl/math.hxx> |
19 | | #include "black_scholes.hxx" |
20 | | |
21 | | // options prices and greeks in the Black-Scholes model |
22 | | // also known as TV (theoretical value) |
23 | | |
24 | | // the code is structured as follows: |
25 | | |
26 | | // (1) basic assets |
27 | | // - cash-or-nothing option: bincash() |
28 | | // - asset-or-nothing option: binasset() |
29 | | |
30 | | // (2) derived basic assets, can all be priced based on (1) |
31 | | // - vanilla put/call: putcall() = +/- ( binasset() - K*bincash() ) |
32 | | // - truncated put/call (barriers active at maturity only) |
33 | | |
34 | | // (3) write a wrapper function to include all vanilla prices |
35 | | // - this is so we don't duplicate code when pricing barriers |
36 | | // as this is derived from vanillas |
37 | | |
38 | | // (4) single barrier options (knock-out), priced based on truncated vanillas |
39 | | // - it follows from the reflection principle that the price W(S) of a |
40 | | // single barrier option is given by |
41 | | // W(S) = V(S) - (B/S)^a V(B^2/S), a = 2(rd-rf)/vol^2 - 1 |
42 | | // where V(S) is the price of the corresponding truncated vanilla |
43 | | // option |
44 | | // - to reduce code duplication and in anticipation of double barrier |
45 | | // options we write the following function |
46 | | // barrier_term(S,c) = V(c*S) - (B/S)^a V(c*B^2/S) |
47 | | |
48 | | // (5) double barrier options (knock-out) |
49 | | // - value is an infinite sum over option prices of the corresponding |
50 | | // truncated vanillas (truncated at both barriers): |
51 | | |
52 | | // W(S)=sum (B2/B1)^(i*a) (V(S(B2/B1)^(2i)) - (B1/S)^a V(B1^2/S (B2/B1)^(2i)) |
53 | | |
54 | | // (6) write routines for put/call barriers and touch options which |
55 | | // mainly call the general double barrier pricer |
56 | | // the main routines are touch() and barrier() |
57 | | // both can price in/out barriers, double/single barriers as well as |
58 | | // vanillas |
59 | | |
60 | | |
61 | | // the framework allows any barriers to be priced as long as we define |
62 | | // the value/greek functions for the corresponding truncated vanilla |
63 | | // and wrap them into internal::vanilla() and internal::vanilla_trunc() |
64 | | |
65 | | // disadvantage of that approach is that due to the rules of |
66 | | // differentiations the formulas for greeks become long and possible |
67 | | // simplifications in the formulas won't be made |
68 | | |
69 | | // other code inefficiency due to multiplication with pm (+/- 1) |
70 | | // cvtsi2sd: int-->double, 6/3 cycles |
71 | | // mulsd: double-double multiplication, 5/1 cycles |
72 | | // with -O3, however, it compiles 2 versions with pm=1, and pm=-1 |
73 | | // which are efficient |
74 | | // note this is tiny anyway as compared to exp/log (100 cycles), |
75 | | // pow (200 cycles), erf (70 cycles) |
76 | | |
77 | | // this code is not tested for numerical instability, ie overruns, |
78 | | // underruns, accuracy, etc |
79 | | |
80 | | |
81 | | namespace sca::pricing::bs { |
82 | | |
83 | | |
84 | | // helper functions |
85 | | |
86 | 0 | static double sqr(double x) { |
87 | 0 | return x*x; |
88 | 0 | } |
89 | | // normal density (see also ScInterpreter::phi) |
90 | 0 | static double dnorm(double x) { |
91 | | //return (1.0/sqrt(2.0*M_PI))*exp(-0.5*x*x); // windows may not have M_PI |
92 | 0 | return 0.39894228040143268*exp(-0.5*x*x); |
93 | 0 | } |
94 | | // cumulative normal distribution (see also ScInterpreter::integralPhi) |
95 | 0 | static double pnorm(double x) { |
96 | 0 | return 0.5 * std::erfc(-x * M_SQRT1_2); |
97 | 0 | } |
98 | | |
99 | | // binary option cash (domestic) |
100 | | // call - pays 1 if S_T is above strike K |
101 | | // put - pays 1 if S_T is below strike K |
102 | | double bincash(double S, double vol, double rd, double rf, |
103 | | double tau, double K, |
104 | 0 | types::PutCall pc, types::Greeks greeks) { |
105 | 0 | assert(tau>=0.0); |
106 | 0 | assert(S>0.0); |
107 | 0 | assert(vol>0.0); |
108 | 0 | assert(K>=0.0); |
109 | |
|
110 | 0 | double val=0.0; |
111 | |
|
112 | 0 | if(tau<=0.0) { |
113 | | // special case tau=0 (expiry) |
114 | 0 | switch(greeks) { |
115 | 0 | case types::Value: |
116 | 0 | if( (pc==types::Call && S>=K) || (pc==types::Put && S<=K) ) { |
117 | 0 | val = 1.0; |
118 | 0 | } else { |
119 | 0 | val = 0.0; |
120 | 0 | } |
121 | 0 | break; |
122 | 0 | default: |
123 | 0 | val = 0.0; |
124 | 0 | } |
125 | 0 | } else if(K==0.0) { |
126 | | // special case with zero strike |
127 | 0 | if(pc==types::Put) { |
128 | | // up-and-out (put) with K=0 |
129 | 0 | val=0.0; |
130 | 0 | } else { |
131 | | // down-and-out (call) with K=0 (zero coupon bond) |
132 | 0 | switch(greeks) { |
133 | 0 | case types::Value: |
134 | 0 | val = 1.0; |
135 | 0 | break; |
136 | 0 | case types::Theta: |
137 | 0 | val = rd; |
138 | 0 | break; |
139 | 0 | case types::Rho_d: |
140 | 0 | val = -tau; |
141 | 0 | break; |
142 | 0 | default: |
143 | 0 | val = 0.0; |
144 | 0 | } |
145 | 0 | } |
146 | 0 | } else { |
147 | | // standard case with K>0, tau>0 |
148 | 0 | double d1 = ( log(S/K)+(rd-rf+0.5*vol*vol)*tau ) / (vol*sqrt(tau)); |
149 | 0 | double d2 = d1 - vol*sqrt(tau); |
150 | 0 | int pm = (pc==types::Call) ? 1 : -1; |
151 | |
|
152 | 0 | switch(greeks) { |
153 | 0 | case types::Value: |
154 | 0 | val = pnorm(pm*d2); |
155 | 0 | break; |
156 | 0 | case types::Delta: |
157 | 0 | val = pm*dnorm(d2)/(S*vol*sqrt(tau)); |
158 | 0 | break; |
159 | 0 | case types::Gamma: |
160 | 0 | val = -pm*dnorm(d2)*d1/(sqr(S*vol)*tau); |
161 | 0 | break; |
162 | 0 | case types::Theta: |
163 | 0 | val = rd*pnorm(pm*d2) |
164 | 0 | + pm*dnorm(d2)*(log(S/K)/(vol*sqrt(tau))-0.5*d2)/tau; |
165 | 0 | break; |
166 | 0 | case types::Vega: |
167 | 0 | val = -pm*dnorm(d2)*d1/vol; |
168 | 0 | break; |
169 | 0 | case types::Volga: |
170 | 0 | val = pm*dnorm(d2)/(vol*vol)*(-d1*d1*d2+d1+d2); |
171 | 0 | break; |
172 | 0 | case types::Vanna: |
173 | 0 | val = pm*dnorm(d2)/(S*vol*vol*sqrt(tau))*(d1*d2-1.0); |
174 | 0 | break; |
175 | 0 | case types::Rho_d: |
176 | 0 | val = -tau*pnorm(pm*d2) + pm*dnorm(d2)*sqrt(tau)/vol; |
177 | 0 | break; |
178 | 0 | case types::Rho_f: |
179 | 0 | val = -pm*dnorm(d2)*sqrt(tau)/vol; |
180 | 0 | break; |
181 | 0 | default: |
182 | 0 | printf("bincash: greek %d not implemented\n", greeks ); |
183 | 0 | abort(); |
184 | 0 | } |
185 | 0 | } |
186 | 0 | return exp(-rd*tau)*val; |
187 | 0 | } |
188 | | |
189 | | // binary option asset (foreign) |
190 | | // call - pays S_T if S_T is above strike K |
191 | | // put - pays S_T if S_T is below strike K |
192 | | double binasset(double S, double vol, double rd, double rf, |
193 | | double tau, double K, |
194 | 0 | types::PutCall pc, types::Greeks greeks) { |
195 | 0 | assert(tau>=0.0); |
196 | 0 | assert(S>0.0); |
197 | 0 | assert(vol>0.0); |
198 | 0 | assert(K>=0.0); |
199 | |
|
200 | 0 | double val=0.0; |
201 | 0 | if(tau<=0.0) { |
202 | | // special case tau=0 (expiry) |
203 | 0 | switch(greeks) { |
204 | 0 | case types::Value: |
205 | 0 | if( (pc==types::Call && S>=K) || (pc==types::Put && S<=K) ) { |
206 | 0 | val = S; |
207 | 0 | } else { |
208 | 0 | val = 0.0; |
209 | 0 | } |
210 | 0 | break; |
211 | 0 | case types::Delta: |
212 | 0 | if( (pc==types::Call && S>=K) || (pc==types::Put && S<=K) ) { |
213 | 0 | val = 1.0; |
214 | 0 | } else { |
215 | 0 | val = 0.0; |
216 | 0 | } |
217 | 0 | break; |
218 | 0 | default: |
219 | 0 | val = 0.0; |
220 | 0 | } |
221 | 0 | } else if(K==0.0) { |
222 | | // special case with zero strike (forward with zero strike) |
223 | 0 | if(pc==types::Put) { |
224 | | // up-and-out (put) with K=0 |
225 | 0 | val = 0.0; |
226 | 0 | } else { |
227 | | // down-and-out (call) with K=0 (type of forward) |
228 | 0 | switch(greeks) { |
229 | 0 | case types::Value: |
230 | 0 | val = S; |
231 | 0 | break; |
232 | 0 | case types::Delta: |
233 | 0 | val = 1.0; |
234 | 0 | break; |
235 | 0 | case types::Theta: |
236 | 0 | val = rf*S; |
237 | 0 | break; |
238 | 0 | case types::Rho_f: |
239 | 0 | val = -tau*S; |
240 | 0 | break; |
241 | 0 | default: |
242 | 0 | val = 0.0; |
243 | 0 | } |
244 | 0 | } |
245 | 0 | } else { |
246 | | // normal case |
247 | 0 | double d1 = ( log(S/K)+(rd-rf+0.5*vol*vol)*tau ) / (vol*sqrt(tau)); |
248 | 0 | double d2 = d1 - vol*sqrt(tau); |
249 | 0 | int pm = (pc==types::Call) ? 1 : -1; |
250 | |
|
251 | 0 | switch(greeks) { |
252 | 0 | case types::Value: |
253 | 0 | val = S*pnorm(pm*d1); |
254 | 0 | break; |
255 | 0 | case types::Delta: |
256 | 0 | val = pnorm(pm*d1) + pm*dnorm(d1)/(vol*sqrt(tau)); |
257 | 0 | break; |
258 | 0 | case types::Gamma: |
259 | 0 | val = -pm*dnorm(d1)*d2/(S*sqr(vol)*tau); |
260 | 0 | break; |
261 | 0 | case types::Theta: |
262 | 0 | val = rf*S*pnorm(pm*d1) |
263 | 0 | + pm*S*dnorm(d1)*(log(S/K)/(vol*sqrt(tau))-0.5*d1)/tau; |
264 | 0 | break; |
265 | 0 | case types::Vega: |
266 | 0 | val = -pm*S*dnorm(d1)*d2/vol; |
267 | 0 | break; |
268 | 0 | case types::Volga: |
269 | 0 | val = pm*S*dnorm(d1)/(vol*vol)*(-d1*d2*d2+d1+d2); |
270 | 0 | break; |
271 | 0 | case types::Vanna: |
272 | 0 | val = pm*dnorm(d1)/(vol*vol*sqrt(tau))*(d2*d2-1.0); |
273 | 0 | break; |
274 | 0 | case types::Rho_d: |
275 | 0 | val = pm*S*dnorm(d1)*sqrt(tau)/vol; |
276 | 0 | break; |
277 | 0 | case types::Rho_f: |
278 | 0 | val = -tau*S*pnorm(pm*d1) - pm*S*dnorm(d1)*sqrt(tau)/vol; |
279 | 0 | break; |
280 | 0 | default: |
281 | 0 | printf("binasset: greek %d not implemented\n", greeks ); |
282 | 0 | abort(); |
283 | 0 | } |
284 | 0 | } |
285 | 0 | return exp(-rf*tau)*val; |
286 | 0 | } |
287 | | |
288 | | // just for convenience we can combine bincash and binasset into |
289 | | // one function binary |
290 | | // using bincash() if fd==types::Domestic |
291 | | // using binasset() if fd==types::Foreign |
292 | | static double binary(double S, double vol, double rd, double rf, |
293 | | double tau, double K, |
294 | | types::PutCall pc, types::ForDom fd, |
295 | 0 | types::Greeks greek) { |
296 | 0 | double val=0.0; |
297 | 0 | switch(fd) { |
298 | 0 | case types::Domestic: |
299 | 0 | val = bincash(S,vol,rd,rf,tau,K,pc,greek); |
300 | 0 | break; |
301 | 0 | case types::Foreign: |
302 | 0 | val = binasset(S,vol,rd,rf,tau,K,pc,greek); |
303 | 0 | break; |
304 | 0 | default: |
305 | | // never get here |
306 | 0 | assert(false); |
307 | 0 | } |
308 | 0 | return val; |
309 | 0 | } |
310 | | |
311 | | // further wrapper to combine single/double barrier binary options |
312 | | // into one function |
313 | | // B1<=0 - it is assumed lower barrier not set |
314 | | // B2<=0 - it is assumed upper barrier not set |
315 | | static double binary(double S, double vol, double rd, double rf, |
316 | | double tau, double B1, double B2, |
317 | 0 | types::ForDom fd, types::Greeks greek) { |
318 | 0 | assert(tau>=0.0); |
319 | 0 | assert(S>0.0); |
320 | 0 | assert(vol>0.0); |
321 | |
|
322 | 0 | double val=0.0; |
323 | |
|
324 | 0 | if(B1<=0.0 && B2<=0.0) { |
325 | | // no barriers set, payoff 1.0 (domestic) or S_T (foreign) |
326 | 0 | val = binary(S,vol,rd,rf,tau,0.0,types::Call,fd,greek); |
327 | 0 | } else if(B1<=0.0 && B2>0.0) { |
328 | | // upper barrier (put) |
329 | 0 | val = binary(S,vol,rd,rf,tau,B2,types::Put,fd,greek); |
330 | 0 | } else if(B1>0.0 && B2<=0.0) { |
331 | | // lower barrier (call) |
332 | 0 | val = binary(S,vol,rd,rf,tau,B1,types::Call,fd,greek); |
333 | 0 | } else if(B1>0.0 && B2>0.0) { |
334 | | // double barrier |
335 | 0 | if(B2<=B1) { |
336 | 0 | val = 0.0; |
337 | 0 | } else { |
338 | 0 | val = binary(S,vol,rd,rf,tau,B2,types::Put,fd,greek) |
339 | 0 | - binary(S,vol,rd,rf,tau,B1,types::Put,fd,greek); |
340 | 0 | } |
341 | 0 | } else { |
342 | | // never get here |
343 | 0 | assert(false); |
344 | 0 | } |
345 | |
|
346 | 0 | return val; |
347 | 0 | } |
348 | | |
349 | | // vanilla put/call option |
350 | | // call pays (S_T-K)^+ |
351 | | // put pays (K-S_T)^+ |
352 | | // this is the same as: +/- (binasset - K*bincash) |
353 | | double putcall(double S, double vol, double rd, double rf, |
354 | | double tau, double K, |
355 | 0 | types::PutCall putcall, types::Greeks greeks) { |
356 | |
|
357 | 0 | assert(tau>=0.0); |
358 | 0 | assert(S>0.0); |
359 | 0 | assert(vol>0.0); |
360 | 0 | assert(K>=0.0); |
361 | |
|
362 | 0 | double val = 0.0; |
363 | 0 | int pm = (putcall==types::Call) ? 1 : -1; |
364 | |
|
365 | 0 | if(K==0 || tau==0.0) { |
366 | | // special cases, simply refer to binasset() and bincash() |
367 | 0 | val = pm * ( binasset(S,vol,rd,rf,tau,K,putcall,greeks) |
368 | 0 | - K*bincash(S,vol,rd,rf,tau,K,putcall,greeks) ); |
369 | 0 | } else { |
370 | | // general case |
371 | | // we could just use pm*(binasset-K*bincash), however |
372 | | // since the formula for delta and gamma simplify we write them |
373 | | // down here |
374 | 0 | double d1 = ( log(S/K)+(rd-rf+0.5*vol*vol)*tau ) / (vol*sqrt(tau)); |
375 | 0 | double d2 = d1 - vol*sqrt(tau); |
376 | |
|
377 | 0 | switch(greeks) { |
378 | 0 | case types::Value: |
379 | 0 | val = pm * ( exp(-rf*tau)*S*pnorm(pm*d1)-exp(-rd*tau)*K*pnorm(pm*d2) ); |
380 | 0 | break; |
381 | 0 | case types::Delta: |
382 | 0 | val = pm*exp(-rf*tau)*pnorm(pm*d1); |
383 | 0 | break; |
384 | 0 | case types::Gamma: |
385 | 0 | val = exp(-rf*tau)*dnorm(d1)/(S*vol*sqrt(tau)); |
386 | 0 | break; |
387 | 0 | default: |
388 | | // too lazy for the other greeks, so simply refer to binasset/bincash |
389 | 0 | val = pm * ( binasset(S,vol,rd,rf,tau,K,putcall,greeks) |
390 | 0 | - K*bincash(S,vol,rd,rf,tau,K,putcall,greeks) ); |
391 | 0 | } |
392 | 0 | } |
393 | 0 | return val; |
394 | 0 | } |
395 | | |
396 | | // truncated put/call option, single barrier |
397 | | // need to specify whether it's down-and-out or up-and-out |
398 | | // regular (keeps monotonicity): down-and-out for call, up-and-out for put |
399 | | // reverse (destroys monoton): up-and-out for call, down-and-out for put |
400 | | // call pays (S_T-K)^+ |
401 | | // put pays (K-S_T)^+ |
402 | | double putcalltrunc(double S, double vol, double rd, double rf, |
403 | | double tau, double K, double B, |
404 | | types::PutCall pc, types::KOType kotype, |
405 | 0 | types::Greeks greeks) { |
406 | |
|
407 | 0 | assert(tau>=0.0); |
408 | 0 | assert(S>0.0); |
409 | 0 | assert(vol>0.0); |
410 | 0 | assert(K>=0.0); |
411 | 0 | assert(B>=0.0); |
412 | |
|
413 | 0 | int pm = (pc==types::Call) ? 1 : -1; |
414 | 0 | double val = 0.0; |
415 | |
|
416 | 0 | switch(kotype) { |
417 | 0 | case types::Regular: |
418 | 0 | if( (pc==types::Call && B<=K) || (pc==types::Put && B>=K) ) { |
419 | | // option degenerates to standard plain vanilla call/put |
420 | 0 | val = putcall(S,vol,rd,rf,tau,K,pc,greeks); |
421 | 0 | } else { |
422 | | // normal case with truncation |
423 | 0 | val = pm * ( binasset(S,vol,rd,rf,tau,B,pc,greeks) |
424 | 0 | - K*bincash(S,vol,rd,rf,tau,B,pc,greeks) ); |
425 | 0 | } |
426 | 0 | break; |
427 | 0 | case types::Reverse: |
428 | 0 | if( (pc==types::Call && B<=K) || (pc==types::Put && B>=K) ) { |
429 | | // option degenerates to zero payoff |
430 | 0 | val = 0.0; |
431 | 0 | } else { |
432 | | // normal case with truncation |
433 | 0 | val = binasset(S,vol,rd,rf,tau,K,types::Call,greeks) |
434 | 0 | - binasset(S,vol,rd,rf,tau,B,types::Call,greeks) |
435 | 0 | - K * ( bincash(S,vol,rd,rf,tau,K,types::Call,greeks) |
436 | 0 | - bincash(S,vol,rd,rf,tau,B,types::Call,greeks) ); |
437 | 0 | } |
438 | 0 | break; |
439 | 0 | default: |
440 | 0 | assert(false); |
441 | 0 | } |
442 | 0 | return val; |
443 | 0 | } |
444 | | |
445 | | // wrapper function for put/call option which combines |
446 | | // double/single/no truncation barrier |
447 | | // B1<=0 - assume no lower barrier |
448 | | // B2<=0 - assume no upper barrier |
449 | | double putcalltrunc(double S, double vol, double rd, double rf, |
450 | | double tau, double K, double B1, double B2, |
451 | 0 | types::PutCall pc, types::Greeks greek) { |
452 | |
|
453 | 0 | assert(tau>=0.0); |
454 | 0 | assert(S>0.0); |
455 | 0 | assert(vol>0.0); |
456 | 0 | assert(K>=0.0); |
457 | |
|
458 | 0 | double val=0.0; |
459 | |
|
460 | 0 | if(B1<=0.0 && B2<=0.0) { |
461 | | // no barriers set, plain vanilla |
462 | 0 | val = putcall(S,vol,rd,rf,tau,K,pc,greek); |
463 | 0 | } else if(B1<=0.0 && B2>0.0) { |
464 | | // upper barrier: reverse barrier for call, regular barrier for put |
465 | 0 | if(pc==types::Call) { |
466 | 0 | val = putcalltrunc(S,vol,rd,rf,tau,K,B2,pc,types::Reverse,greek); |
467 | 0 | } else { |
468 | 0 | val = putcalltrunc(S,vol,rd,rf,tau,K,B2,pc,types::Regular,greek); |
469 | 0 | } |
470 | 0 | } else if(B1>0.0 && B2<=0.0) { |
471 | | // lower barrier: regular barrier for call, reverse barrier for put |
472 | 0 | if(pc==types::Call) { |
473 | 0 | val = putcalltrunc(S,vol,rd,rf,tau,K,B1,pc,types::Regular,greek); |
474 | 0 | } else { |
475 | 0 | val = putcalltrunc(S,vol,rd,rf,tau,K,B1,pc,types::Reverse,greek); |
476 | 0 | } |
477 | 0 | } else if(B1>0.0 && B2>0.0) { |
478 | | // double barrier |
479 | 0 | if(B2<=B1) { |
480 | 0 | val = 0.0; |
481 | 0 | } else { |
482 | 0 | int pm = (pc==types::Call) ? 1 : -1; |
483 | 0 | val = pm * ( |
484 | 0 | putcalltrunc(S,vol,rd,rf,tau,K,B1,pc,types::Regular,greek) |
485 | 0 | - putcalltrunc(S,vol,rd,rf,tau,K,B2,pc,types::Regular,greek) |
486 | 0 | ); |
487 | 0 | } |
488 | 0 | } else { |
489 | | // never get here |
490 | 0 | assert(false); |
491 | 0 | } |
492 | 0 | return val; |
493 | 0 | } |
494 | | |
495 | | namespace internal { |
496 | | |
497 | | // wrapper function for all non-path dependent options |
498 | | // this is only an internal function, used to avoid code duplication when |
499 | | // going to path-dependent barrier options, |
500 | | // K<0 - assume binary option |
501 | | // K>=0 - assume put/call option |
502 | | static double vanilla(double S, double vol, double rd, double rf, |
503 | | double tau, double K, double B1, double B2, |
504 | | types::PutCall pc, types::ForDom fd, |
505 | 0 | types::Greeks greek) { |
506 | 0 | double val = 0.0; |
507 | 0 | if(K<0.0) { |
508 | | // binary option if K<0 |
509 | 0 | val = binary(S,vol,rd,rf,tau,B1,B2,fd,greek); |
510 | 0 | } else { |
511 | 0 | val = putcall(S,vol,rd,rf,tau,K,pc,greek); |
512 | 0 | } |
513 | 0 | return val; |
514 | 0 | } |
515 | | static double vanilla_trunc(double S, double vol, double rd, double rf, |
516 | | double tau, double K, double B1, double B2, |
517 | | types::PutCall pc, types::ForDom fd, |
518 | 0 | types::Greeks greek) { |
519 | 0 | double val = 0.0; |
520 | 0 | if(K<0.0) { |
521 | | // binary option if K<0 |
522 | | // truncated is actually the same as the vanilla binary |
523 | 0 | val = binary(S,vol,rd,rf,tau,B1,B2,fd,greek); |
524 | 0 | } else { |
525 | 0 | val = putcalltrunc(S,vol,rd,rf,tau,K,B1,B2,pc,greek); |
526 | 0 | } |
527 | 0 | return val; |
528 | 0 | } |
529 | | |
530 | | } // namespace internal |
531 | | |
532 | | // path dependent options |
533 | | |
534 | | |
535 | | namespace internal { |
536 | | |
537 | | // helper term for any type of options with continuously monitored barriers, |
538 | | // internal, should not be called from outside |
539 | | // calculates value and greeks based on |
540 | | // V(S) = V1(sc*S) - (B/S)^a V1(sc*B^2/S) |
541 | | // (a=2 mu/vol^2, mu drift in logspace, ie. mu=(rd-rf-1/2vol^2)) |
542 | | // with sc=1 and V1() being the price of the respective truncated |
543 | | // vanilla option, V() would be the price of the respective barrier |
544 | | // option if only one barrier is present |
545 | | static double barrier_term(double S, double vol, double rd, double rf, |
546 | | double tau, double K, double B1, double B2, double sc, |
547 | | types::PutCall pc, types::ForDom fd, |
548 | 0 | types::Greeks greek) { |
549 | |
|
550 | 0 | assert(tau>=0.0); |
551 | 0 | assert(S>0.0); |
552 | 0 | assert(vol>0.0); |
553 | | |
554 | | // V(S) = V1(sc*S) - (B/S)^a V1(sc*B^2/S) |
555 | 0 | double val = 0.0; |
556 | 0 | double B = (B1>0.0) ? B1 : B2; |
557 | 0 | double a = 2.0*(rd-rf)/(vol*vol)-1.0; // helper variable |
558 | 0 | double b = 4.0*(rd-rf)/(vol*vol*vol); // helper variable -da/dvol |
559 | 0 | double c = 12.0*(rd-rf)/(vol*vol*vol*vol); // helper -db/dvol |
560 | 0 | switch(greek) { |
561 | 0 | case types::Value: |
562 | 0 | case types::Theta: |
563 | 0 | val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) |
564 | 0 | - pow(B/S,a)* |
565 | 0 | vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); |
566 | 0 | break; |
567 | 0 | case types::Delta: |
568 | 0 | val = sc*vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) |
569 | 0 | + pow(B/S,a) * ( |
570 | 0 | a/S* |
571 | 0 | vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value) |
572 | 0 | + sqr(B/S)*sc* |
573 | 0 | vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) |
574 | 0 | ); |
575 | 0 | break; |
576 | 0 | case types::Gamma: |
577 | 0 | val = sc*sc*vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) |
578 | 0 | - pow(B/S,a) * ( |
579 | 0 | a*(a+1.0)/(S*S)* |
580 | 0 | vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value) |
581 | 0 | + (2.0*a+2.0)*B*B/(S*S*S)*sc* |
582 | 0 | vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Delta) |
583 | 0 | + sqr(sqr(B/S))*sc*sc* |
584 | 0 | vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Gamma) |
585 | 0 | ); |
586 | 0 | break; |
587 | 0 | case types::Vega: |
588 | 0 | val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) |
589 | 0 | - pow(B/S,a) * ( |
590 | 0 | - b*log(B/S)* |
591 | 0 | vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value) |
592 | 0 | + 1.0* |
593 | 0 | vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) |
594 | 0 | ); |
595 | 0 | break; |
596 | 0 | case types::Volga: |
597 | 0 | val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) |
598 | 0 | - pow(B/S,a) * ( |
599 | 0 | log(B/S)*(b*b*log(B/S)+c)* |
600 | 0 | vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value) |
601 | 0 | - 2.0*b*log(B/S)* |
602 | 0 | vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Vega) |
603 | 0 | + 1.0* |
604 | 0 | vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Volga) |
605 | 0 | ); |
606 | 0 | break; |
607 | 0 | case types::Vanna: |
608 | 0 | val = sc*vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) |
609 | 0 | - pow(B/S,a) * ( |
610 | 0 | b/S*(log(B/S)*a+1.0)* |
611 | 0 | vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value) |
612 | 0 | + b*log(B/S)*sqr(B/S)*sc* |
613 | 0 | vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Delta) |
614 | 0 | - a/S* |
615 | 0 | vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Vega) |
616 | 0 | - sqr(B/S)*sc* |
617 | 0 | vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Vanna) |
618 | 0 | ); |
619 | 0 | break; |
620 | 0 | case types::Rho_d: |
621 | 0 | val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) |
622 | 0 | - pow(B/S,a) * ( |
623 | 0 | 2.0*log(B/S)/(vol*vol)* |
624 | 0 | vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value) |
625 | 0 | + 1.0* |
626 | 0 | vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) |
627 | 0 | ); |
628 | 0 | break; |
629 | 0 | case types::Rho_f: |
630 | 0 | val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) |
631 | 0 | - pow(B/S,a) * ( |
632 | 0 | - 2.0*log(B/S)/(vol*vol)* |
633 | 0 | vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value) |
634 | 0 | + 1.0* |
635 | 0 | vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) |
636 | 0 | ); |
637 | 0 | break; |
638 | 0 | default: |
639 | 0 | printf("barrier_term: greek %d not implemented\n", greek ); |
640 | 0 | abort(); |
641 | 0 | } |
642 | 0 | return val; |
643 | 0 | } |
644 | | |
645 | | // one term of the infinite sum for the valuation of double barriers |
646 | | static double barrier_double_term( double S, double vol, double rd, double rf, |
647 | | double tau, double K, double B1, double B2, |
648 | | double fac, double sc, int i, |
649 | 0 | types::PutCall pc, types::ForDom fd, types::Greeks greek) { |
650 | |
|
651 | 0 | double val = 0.0; |
652 | 0 | double b = 4.0*i*(rd-rf)/(vol*vol*vol); // helper variable -da/dvol |
653 | 0 | double c = 12.0*i*(rd-rf)/(vol*vol*vol*vol); // helper -db/dvol |
654 | 0 | switch(greek) { |
655 | 0 | case types::Value: |
656 | 0 | val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek); |
657 | 0 | break; |
658 | 0 | case types::Delta: |
659 | 0 | val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek); |
660 | 0 | break; |
661 | 0 | case types::Gamma: |
662 | 0 | val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek); |
663 | 0 | break; |
664 | 0 | case types::Theta: |
665 | 0 | val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek); |
666 | 0 | break; |
667 | 0 | case types::Vega: |
668 | 0 | val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek) |
669 | 0 | - b*log(B2/B1)*fac * |
670 | 0 | barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Value); |
671 | 0 | break; |
672 | 0 | case types::Volga: |
673 | 0 | val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek) |
674 | 0 | - 2.0*b*log(B2/B1)*fac * |
675 | 0 | barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Vega) |
676 | 0 | + log(B2/B1)*fac*(c+b*b*log(B2/B1)) * |
677 | 0 | barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Value); |
678 | 0 | break; |
679 | 0 | case types::Vanna: |
680 | 0 | val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek) |
681 | 0 | - b*log(B2/B1)*fac * |
682 | 0 | barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Delta); |
683 | 0 | break; |
684 | 0 | case types::Rho_d: |
685 | 0 | val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek) |
686 | 0 | + 2.0*i/(vol*vol)*log(B2/B1)*fac * |
687 | 0 | barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Value); |
688 | 0 | break; |
689 | 0 | case types::Rho_f: |
690 | 0 | val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek) |
691 | 0 | - 2.0*i/(vol*vol)*log(B2/B1)*fac * |
692 | 0 | barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Value); |
693 | 0 | break; |
694 | 0 | default: |
695 | 0 | printf("barrier_double_term: greek %d not implemented\n", greek ); |
696 | 0 | abort(); |
697 | 0 | } |
698 | 0 | return val; |
699 | 0 | } |
700 | | |
701 | | // general pricer for any type of options with continuously monitored barriers |
702 | | // allows two, one or zero barriers, only knock-out style |
703 | | // payoff profiles allowed based on vanilla_trunc() |
704 | | static double barrier_ko(double S, double vol, double rd, double rf, |
705 | | double tau, double K, double B1, double B2, |
706 | | types::PutCall pc, types::ForDom fd, |
707 | 0 | types::Greeks greek) { |
708 | |
|
709 | 0 | assert(tau>=0.0); |
710 | 0 | assert(S>0.0); |
711 | 0 | assert(vol>0.0); |
712 | |
|
713 | 0 | double val = 0.0; |
714 | |
|
715 | 0 | if(B1<=0.0 && B2<=0.0) { |
716 | | // no barriers --> vanilla case |
717 | 0 | val = vanilla(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); |
718 | 0 | } else if(B1>0.0 && B2<=0.0) { |
719 | | // lower barrier |
720 | 0 | if(S<=B1) { |
721 | 0 | val = 0.0; // knocked out |
722 | 0 | } else { |
723 | 0 | val = barrier_term(S,vol,rd,rf,tau,K,B1,B2,1.0,pc,fd,greek); |
724 | 0 | } |
725 | 0 | } else if(B1<=0.0 && B2>0.0) { |
726 | | // upper barrier |
727 | 0 | if(S>=B2) { |
728 | 0 | val = 0.0; // knocked out |
729 | 0 | } else { |
730 | 0 | val = barrier_term(S,vol,rd,rf,tau,K,B1,B2,1.0,pc,fd,greek); |
731 | 0 | } |
732 | 0 | } else if(B1>0.0 && B2>0.0) { |
733 | | // double barrier |
734 | 0 | if(S<=B1 || S>=B2) { |
735 | 0 | val = 0.0; // knocked out (always true if wrong input B1>B2) |
736 | 0 | } else { |
737 | | // more complex calculation as we have to evaluate an infinite |
738 | | // sum |
739 | | // to reduce very costly pow() calls we define some variables |
740 | 0 | double a = 2.0*(rd-rf)/(vol*vol)-1.0; // 2 (mu-1/2vol^2)/sigma^2 |
741 | 0 | double BB2=sqr(B2/B1); |
742 | 0 | double BBa=pow(B2/B1,a); |
743 | 0 | double BB2inv=1.0/BB2; |
744 | 0 | double BBainv=1.0/BBa; |
745 | 0 | double fac=1.0; |
746 | 0 | double facinv=1.0; |
747 | 0 | double sc=1.0; |
748 | 0 | double scinv=1.0; |
749 | | |
750 | | // initial term i=0 |
751 | 0 | val=barrier_double_term(S,vol,rd,rf,tau,K,B1,B2,fac,sc,0,pc,fd,greek); |
752 | | // infinite loop, 10 should be plenty, normal would be 2 |
753 | 0 | for(int i=1; i<10; i++) { |
754 | 0 | fac*=BBa; |
755 | 0 | facinv*=BBainv; |
756 | 0 | sc*=BB2; |
757 | 0 | scinv*=BB2inv; |
758 | 0 | double add = |
759 | 0 | barrier_double_term(S,vol,rd,rf,tau,K,B1,B2,fac,sc,i,pc,fd,greek) + |
760 | 0 | barrier_double_term(S,vol,rd,rf,tau,K,B1,B2,facinv,scinv,-i,pc,fd,greek); |
761 | 0 | val += add; |
762 | | //printf("%i: val=%e (add=%e)\n",i,val,add); |
763 | 0 | if(fabs(add) <= 1e-12*fabs(val)) { |
764 | 0 | break; |
765 | 0 | } |
766 | 0 | } |
767 | | // not knocked-out double barrier end |
768 | 0 | } |
769 | | // double barrier end |
770 | 0 | } else { |
771 | | // no such barrier combination exists |
772 | 0 | assert(false); |
773 | 0 | } |
774 | |
|
775 | 0 | return val; |
776 | 0 | } |
777 | | |
778 | | // knock-in style barrier |
779 | | static double barrier_ki(double S, double vol, double rd, double rf, |
780 | | double tau, double K, double B1, double B2, |
781 | | types::PutCall pc, types::ForDom fd, |
782 | 0 | types::Greeks greek) { |
783 | 0 | return vanilla(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) |
784 | 0 | -barrier_ko(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); |
785 | 0 | } |
786 | | |
787 | | // general barrier |
788 | | static double barrier(double S, double vol, double rd, double rf, |
789 | | double tau, double K, double B1, double B2, |
790 | | types::PutCall pc, types::ForDom fd, |
791 | | types::BarrierKIO kio, types::BarrierActive bcont, |
792 | 0 | types::Greeks greek) { |
793 | |
|
794 | 0 | double val = 0.0; |
795 | 0 | if( kio==types::KnockOut && bcont==types::Maturity ) { |
796 | | // truncated vanilla option |
797 | 0 | val = vanilla_trunc(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); |
798 | 0 | } else if ( kio==types::KnockOut && bcont==types::Continuous ) { |
799 | | // standard knock-out barrier |
800 | 0 | val = barrier_ko(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); |
801 | 0 | } else if ( kio==types::KnockIn && bcont==types::Maturity ) { |
802 | | // inverse truncated vanilla |
803 | 0 | val = vanilla(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek) |
804 | 0 | - vanilla_trunc(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); |
805 | 0 | } else if ( kio==types::KnockIn && bcont==types::Continuous ) { |
806 | | // standard knock-in barrier |
807 | 0 | val = barrier_ki(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); |
808 | 0 | } else { |
809 | | // never get here |
810 | 0 | assert(false); |
811 | 0 | } |
812 | 0 | return val; |
813 | 0 | } |
814 | | |
815 | | } // namespace internal |
816 | | |
817 | | |
818 | | // touch/no-touch options (cash/asset or nothing payoff profile) |
819 | | double touch(double S, double vol, double rd, double rf, |
820 | | double tau, double B1, double B2, types::ForDom fd, |
821 | | types::BarrierKIO kio, types::BarrierActive bcont, |
822 | 0 | types::Greeks greek) { |
823 | |
|
824 | 0 | double K=-1.0; // dummy |
825 | 0 | types::PutCall pc = types::Call; // dummy |
826 | 0 | double val = 0.0; |
827 | 0 | if( kio==types::KnockOut && bcont==types::Maturity ) { |
828 | | // truncated vanilla option |
829 | 0 | val = internal::vanilla_trunc(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); |
830 | 0 | } else if ( kio==types::KnockOut && bcont==types::Continuous ) { |
831 | | // standard knock-out barrier |
832 | 0 | val = internal::barrier_ko(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); |
833 | 0 | } else if ( kio==types::KnockIn && bcont==types::Maturity ) { |
834 | | // inverse truncated vanilla |
835 | 0 | val = internal::vanilla(S,vol,rd,rf,tau,K,-1.0,-1.0,pc,fd,greek) |
836 | 0 | - internal::vanilla_trunc(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); |
837 | 0 | } else if ( kio==types::KnockIn && bcont==types::Continuous ) { |
838 | | // standard knock-in barrier |
839 | 0 | val = internal::vanilla(S,vol,rd,rf,tau,K,-1.0,-1.0,pc,fd,greek) |
840 | 0 | - internal::barrier_ko(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek); |
841 | 0 | } else { |
842 | | // never get here |
843 | 0 | assert(false); |
844 | 0 | } |
845 | 0 | return val; |
846 | 0 | } |
847 | | |
848 | | // barrier option (put/call payoff profile) |
849 | | double barrier(double S, double vol, double rd, double rf, |
850 | | double tau, double K, double B1, double B2, |
851 | | double rebate, |
852 | | types::PutCall pc, types::BarrierKIO kio, |
853 | | types::BarrierActive bcont, |
854 | 0 | types::Greeks greek) { |
855 | 0 | assert(tau>=0.0); |
856 | 0 | assert(S>0.0); |
857 | 0 | assert(vol>0.0); |
858 | 0 | assert(K>=0.0); |
859 | 0 | types::ForDom fd = types::Domestic; |
860 | 0 | double val=internal::barrier(S,vol,rd,rf,tau,K,B1,B2,pc,fd,kio,bcont,greek); |
861 | 0 | if(rebate!=0.0) { |
862 | | // opposite of barrier knock-in/out type |
863 | 0 | types::BarrierKIO kio2 = (kio==types::KnockIn) ? types::KnockOut |
864 | 0 | : types::KnockIn; |
865 | 0 | val += rebate*touch(S,vol,rd,rf,tau,B1,B2,fd,kio2,bcont,greek); |
866 | 0 | } |
867 | 0 | return val; |
868 | 0 | } |
869 | | |
870 | | // probability of hitting a barrier |
871 | | // this is almost the same as the price of a touch option (domestic) |
872 | | // as it pays one if a barrier is hit; we only have to offset the |
873 | | // discounting and we get the probability |
874 | | double prob_hit(double S, double vol, double mu, |
875 | 0 | double tau, double B1, double B2) { |
876 | 0 | double const rd=0.0; |
877 | 0 | double rf=-mu; |
878 | 0 | return 1.0 - touch(S,vol,rd,rf,tau,B1,B2,types::Domestic,types::KnockOut, |
879 | 0 | types::Continuous, types::Value); |
880 | 0 | } |
881 | | |
882 | | // probability of being in-the-money, ie payoff is greater zero, |
883 | | // assuming payoff(S_T) > 0 iff S_T in [B1, B2] |
884 | | // this the same as the price of a cash or nothing option |
885 | | // with no discounting |
886 | | double prob_in_money(double S, double vol, double mu, |
887 | 0 | double tau, double B1, double B2) { |
888 | 0 | assert(S>0.0); |
889 | 0 | assert(vol>0.0); |
890 | 0 | assert(tau>=0.0); |
891 | 0 | double val = 0.0; |
892 | 0 | if( B1<B2 || B1<=0.0 || B2<=0.0 ) { |
893 | 0 | val = binary(S,vol,0.0,-mu,tau,B1,B2,types::Domestic,types::Value); |
894 | 0 | } |
895 | 0 | return val; |
896 | 0 | } |
897 | | double prob_in_money(double S, double vol, double mu, |
898 | | double tau, double K, double B1, double B2, |
899 | 0 | types::PutCall pc) { |
900 | 0 | assert(S>0.0); |
901 | 0 | assert(vol>0.0); |
902 | 0 | assert(tau>=0.0); |
903 | | |
904 | | // if K<0 we assume a binary option is given |
905 | 0 | if(K<0.0) { |
906 | 0 | return prob_in_money(S,vol,mu,tau,B1,B2); |
907 | 0 | } |
908 | | |
909 | 0 | double val = 0.0; |
910 | 0 | double BM1, BM2; // range of in the money [BM1, BM2] |
911 | | // non-sense parameters with no positive payoff |
912 | 0 | if( (B1>B2 && B1>0.0 && B2>0.0) || |
913 | 0 | (K>=B2 && B2>0.0 && pc==types::Call) || |
914 | 0 | (K<=B1 && pc==types::Put) ) { |
915 | 0 | val = 0.0; |
916 | | // need to figure out between what barriers payoff is greater 0 |
917 | 0 | } else if(pc==types::Call) { |
918 | 0 | BM1=std::max(B1, K); |
919 | 0 | BM2=B2; |
920 | 0 | val = prob_in_money(S,vol,mu,tau,BM1,BM2); |
921 | 0 | } else if (pc==types::Put) { |
922 | 0 | BM1=B1; |
923 | 0 | BM2= (B2>0.0) ? std::min(B2,K) : K; |
924 | 0 | val = prob_in_money(S,vol,mu,tau,BM1,BM2); |
925 | 0 | } else { |
926 | | // don't get here |
927 | | assert(false); |
928 | 0 | } |
929 | 0 | return val; |
930 | 0 | } |
931 | | |
932 | | } // namespace sca |
933 | | |
934 | | |
935 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |