LibreOffice Module scaddins (master) 1
black_scholes.cxx
Go to the documentation of this file.
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
82
83
84// helper functions
85
86static double sqr(double x) {
87 return x*x;
88}
89// normal density (see also ScInterpreter::phi)
90static 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 return 0.39894228040143268*exp(-0.5*x*x);
93}
94// cumulative normal distribution (see also ScInterpreter::integralPhi)
95static double pnorm(double x) {
96 return 0.5 * std::erfc(-x * M_SQRT1_2);
97}
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
102double bincash(double S, double vol, double rd, double rf,
103 double tau, double K,
104 types::PutCall pc, types::Greeks greeks) {
105 assert(tau>=0.0);
106 assert(S>0.0);
107 assert(vol>0.0);
108 assert(K>=0.0);
109
110 double val=0.0;
111
112 if(tau<=0.0) {
113 // special case tau=0 (expiry)
114 switch(greeks) {
115 case types::Value:
116 if( (pc==types::Call && S>=K) || (pc==types::Put && S<=K) ) {
117 val = 1.0;
118 } else {
119 val = 0.0;
120 }
121 break;
122 default:
123 val = 0.0;
124 }
125 } else if(K==0.0) {
126 // special case with zero strike
127 if(pc==types::Put) {
128 // up-and-out (put) with K=0
129 val=0.0;
130 } else {
131 // down-and-out (call) with K=0 (zero coupon bond)
132 switch(greeks) {
133 case types::Value:
134 val = 1.0;
135 break;
136 case types::Theta:
137 val = rd;
138 break;
139 case types::Rho_d:
140 val = -tau;
141 break;
142 default:
143 val = 0.0;
144 }
145 }
146 } else {
147 // standard case with K>0, tau>0
148 double d1 = ( log(S/K)+(rd-rf+0.5*vol*vol)*tau ) / (vol*sqrt(tau));
149 double d2 = d1 - vol*sqrt(tau);
150 int pm = (pc==types::Call) ? 1 : -1;
151
152 switch(greeks) {
153 case types::Value:
154 val = pnorm(pm*d2);
155 break;
156 case types::Delta:
157 val = pm*dnorm(d2)/(S*vol*sqrt(tau));
158 break;
159 case types::Gamma:
160 val = -pm*dnorm(d2)*d1/(sqr(S*vol)*tau);
161 break;
162 case types::Theta:
163 val = rd*pnorm(pm*d2)
164 + pm*dnorm(d2)*(log(S/K)/(vol*sqrt(tau))-0.5*d2)/tau;
165 break;
166 case types::Vega:
167 val = -pm*dnorm(d2)*d1/vol;
168 break;
169 case types::Volga:
170 val = pm*dnorm(d2)/(vol*vol)*(-d1*d1*d2+d1+d2);
171 break;
172 case types::Vanna:
173 val = pm*dnorm(d2)/(S*vol*vol*sqrt(tau))*(d1*d2-1.0);
174 break;
175 case types::Rho_d:
176 val = -tau*pnorm(pm*d2) + pm*dnorm(d2)*sqrt(tau)/vol;
177 break;
178 case types::Rho_f:
179 val = -pm*dnorm(d2)*sqrt(tau)/vol;
180 break;
181 default:
182 printf("bincash: greek %d not implemented\n", greeks );
183 abort();
184 }
185 }
186 return exp(-rd*tau)*val;
187}
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
192double binasset(double S, double vol, double rd, double rf,
193 double tau, double K,
194 types::PutCall pc, types::Greeks greeks) {
195 assert(tau>=0.0);
196 assert(S>0.0);
197 assert(vol>0.0);
198 assert(K>=0.0);
199
200 double val=0.0;
201 if(tau<=0.0) {
202 // special case tau=0 (expiry)
203 switch(greeks) {
204 case types::Value:
205 if( (pc==types::Call && S>=K) || (pc==types::Put && S<=K) ) {
206 val = S;
207 } else {
208 val = 0.0;
209 }
210 break;
211 case types::Delta:
212 if( (pc==types::Call && S>=K) || (pc==types::Put && S<=K) ) {
213 val = 1.0;
214 } else {
215 val = 0.0;
216 }
217 break;
218 default:
219 val = 0.0;
220 }
221 } else if(K==0.0) {
222 // special case with zero strike (forward with zero strike)
223 if(pc==types::Put) {
224 // up-and-out (put) with K=0
225 val = 0.0;
226 } else {
227 // down-and-out (call) with K=0 (type of forward)
228 switch(greeks) {
229 case types::Value:
230 val = S;
231 break;
232 case types::Delta:
233 val = 1.0;
234 break;
235 case types::Theta:
236 val = rf*S;
237 break;
238 case types::Rho_f:
239 val = -tau*S;
240 break;
241 default:
242 val = 0.0;
243 }
244 }
245 } else {
246 // normal case
247 double d1 = ( log(S/K)+(rd-rf+0.5*vol*vol)*tau ) / (vol*sqrt(tau));
248 double d2 = d1 - vol*sqrt(tau);
249 int pm = (pc==types::Call) ? 1 : -1;
250
251 switch(greeks) {
252 case types::Value:
253 val = S*pnorm(pm*d1);
254 break;
255 case types::Delta:
256 val = pnorm(pm*d1) + pm*dnorm(d1)/(vol*sqrt(tau));
257 break;
258 case types::Gamma:
259 val = -pm*dnorm(d1)*d2/(S*sqr(vol)*tau);
260 break;
261 case types::Theta:
262 val = rf*S*pnorm(pm*d1)
263 + pm*S*dnorm(d1)*(log(S/K)/(vol*sqrt(tau))-0.5*d1)/tau;
264 break;
265 case types::Vega:
266 val = -pm*S*dnorm(d1)*d2/vol;
267 break;
268 case types::Volga:
269 val = pm*S*dnorm(d1)/(vol*vol)*(-d1*d2*d2+d1+d2);
270 break;
271 case types::Vanna:
272 val = pm*dnorm(d1)/(vol*vol*sqrt(tau))*(d2*d2-1.0);
273 break;
274 case types::Rho_d:
275 val = pm*S*dnorm(d1)*sqrt(tau)/vol;
276 break;
277 case types::Rho_f:
278 val = -tau*S*pnorm(pm*d1) - pm*S*dnorm(d1)*sqrt(tau)/vol;
279 break;
280 default:
281 printf("binasset: greek %d not implemented\n", greeks );
282 abort();
283 }
284 }
285 return exp(-rf*tau)*val;
286}
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
292static double binary(double S, double vol, double rd, double rf,
293 double tau, double K,
295 types::Greeks greek) {
296 double val=0.0;
297 switch(fd) {
298 case types::Domestic:
299 val = bincash(S,vol,rd,rf,tau,K,pc,greek);
300 break;
301 case types::Foreign:
302 val = binasset(S,vol,rd,rf,tau,K,pc,greek);
303 break;
304 default:
305 // never get here
306 assert(false);
307 }
308 return val;
309}
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
315static double binary(double S, double vol, double rd, double rf,
316 double tau, double B1, double B2,
317 types::ForDom fd, types::Greeks greek) {
318 assert(tau>=0.0);
319 assert(S>0.0);
320 assert(vol>0.0);
321
322 double val=0.0;
323
324 if(B1<=0.0 && B2<=0.0) {
325 // no barriers set, payoff 1.0 (domestic) or S_T (foreign)
326 val = binary(S,vol,rd,rf,tau,0.0,types::Call,fd,greek);
327 } else if(B1<=0.0 && B2>0.0) {
328 // upper barrier (put)
329 val = binary(S,vol,rd,rf,tau,B2,types::Put,fd,greek);
330 } else if(B1>0.0 && B2<=0.0) {
331 // lower barrier (call)
332 val = binary(S,vol,rd,rf,tau,B1,types::Call,fd,greek);
333 } else if(B1>0.0 && B2>0.0) {
334 // double barrier
335 if(B2<=B1) {
336 val = 0.0;
337 } else {
338 val = binary(S,vol,rd,rf,tau,B2,types::Put,fd,greek)
339 - binary(S,vol,rd,rf,tau,B1,types::Put,fd,greek);
340 }
341 } else {
342 // never get here
343 assert(false);
344 }
345
346 return val;
347}
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)
353double putcall(double S, double vol, double rd, double rf,
354 double tau, double K,
356
357 assert(tau>=0.0);
358 assert(S>0.0);
359 assert(vol>0.0);
360 assert(K>=0.0);
361
362 double val = 0.0;
363 int pm = (putcall==types::Call) ? 1 : -1;
364
365 if(K==0 || tau==0.0) {
366 // special cases, simply refer to binasset() and bincash()
367 val = pm * ( binasset(S,vol,rd,rf,tau,K,putcall,greeks)
368 - K*bincash(S,vol,rd,rf,tau,K,putcall,greeks) );
369 } 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 double d1 = ( log(S/K)+(rd-rf+0.5*vol*vol)*tau ) / (vol*sqrt(tau));
375 double d2 = d1 - vol*sqrt(tau);
376
377 switch(greeks) {
378 case types::Value:
379 val = pm * ( exp(-rf*tau)*S*pnorm(pm*d1)-exp(-rd*tau)*K*pnorm(pm*d2) );
380 break;
381 case types::Delta:
382 val = pm*exp(-rf*tau)*pnorm(pm*d1);
383 break;
384 case types::Gamma:
385 val = exp(-rf*tau)*dnorm(d1)/(S*vol*sqrt(tau));
386 break;
387 default:
388 // too lazy for the other greeks, so simply refer to binasset/bincash
389 val = pm * ( binasset(S,vol,rd,rf,tau,K,putcall,greeks)
390 - K*bincash(S,vol,rd,rf,tau,K,putcall,greeks) );
391 }
392 }
393 return val;
394}
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)^+
402double putcalltrunc(double S, double vol, double rd, double rf,
403 double tau, double K, double B,
404 types::PutCall pc, types::KOType kotype,
405 types::Greeks greeks) {
406
407 assert(tau>=0.0);
408 assert(S>0.0);
409 assert(vol>0.0);
410 assert(K>=0.0);
411 assert(B>=0.0);
412
413 int pm = (pc==types::Call) ? 1 : -1;
414 double val = 0.0;
415
416 switch(kotype) {
417 case types::Regular:
418 if( (pc==types::Call && B<=K) || (pc==types::Put && B>=K) ) {
419 // option degenerates to standard plain vanilla call/put
420 val = putcall(S,vol,rd,rf,tau,K,pc,greeks);
421 } else {
422 // normal case with truncation
423 val = pm * ( binasset(S,vol,rd,rf,tau,B,pc,greeks)
424 - K*bincash(S,vol,rd,rf,tau,B,pc,greeks) );
425 }
426 break;
427 case types::Reverse:
428 if( (pc==types::Call && B<=K) || (pc==types::Put && B>=K) ) {
429 // option degenerates to zero payoff
430 val = 0.0;
431 } else {
432 // normal case with truncation
433 val = binasset(S,vol,rd,rf,tau,K,types::Call,greeks)
434 - binasset(S,vol,rd,rf,tau,B,types::Call,greeks)
435 - K * ( bincash(S,vol,rd,rf,tau,K,types::Call,greeks)
436 - bincash(S,vol,rd,rf,tau,B,types::Call,greeks) );
437 }
438 break;
439 default:
440 assert(false);
441 }
442 return val;
443}
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
449double putcalltrunc(double S, double vol, double rd, double rf,
450 double tau, double K, double B1, double B2,
451 types::PutCall pc, types::Greeks greek) {
452
453 assert(tau>=0.0);
454 assert(S>0.0);
455 assert(vol>0.0);
456 assert(K>=0.0);
457
458 double val=0.0;
459
460 if(B1<=0.0 && B2<=0.0) {
461 // no barriers set, plain vanilla
462 val = putcall(S,vol,rd,rf,tau,K,pc,greek);
463 } else if(B1<=0.0 && B2>0.0) {
464 // upper barrier: reverse barrier for call, regular barrier for put
465 if(pc==types::Call) {
466 val = putcalltrunc(S,vol,rd,rf,tau,K,B2,pc,types::Reverse,greek);
467 } else {
468 val = putcalltrunc(S,vol,rd,rf,tau,K,B2,pc,types::Regular,greek);
469 }
470 } else if(B1>0.0 && B2<=0.0) {
471 // lower barrier: regular barrier for call, reverse barrier for put
472 if(pc==types::Call) {
473 val = putcalltrunc(S,vol,rd,rf,tau,K,B1,pc,types::Regular,greek);
474 } else {
475 val = putcalltrunc(S,vol,rd,rf,tau,K,B1,pc,types::Reverse,greek);
476 }
477 } else if(B1>0.0 && B2>0.0) {
478 // double barrier
479 if(B2<=B1) {
480 val = 0.0;
481 } else {
482 int pm = (pc==types::Call) ? 1 : -1;
483 val = pm * (
484 putcalltrunc(S,vol,rd,rf,tau,K,B1,pc,types::Regular,greek)
485 - putcalltrunc(S,vol,rd,rf,tau,K,B2,pc,types::Regular,greek)
486 );
487 }
488 } else {
489 // never get here
490 assert(false);
491 }
492 return val;
493}
494
495namespace 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
502static double vanilla(double S, double vol, double rd, double rf,
503 double tau, double K, double B1, double B2,
505 types::Greeks greek) {
506 double val = 0.0;
507 if(K<0.0) {
508 // binary option if K<0
509 val = binary(S,vol,rd,rf,tau,B1,B2,fd,greek);
510 } else {
511 val = putcall(S,vol,rd,rf,tau,K,pc,greek);
512 }
513 return val;
514}
515static double vanilla_trunc(double S, double vol, double rd, double rf,
516 double tau, double K, double B1, double B2,
518 types::Greeks greek) {
519 double val = 0.0;
520 if(K<0.0) {
521 // binary option if K<0
522 // truncated is actually the same as the vanilla binary
523 val = binary(S,vol,rd,rf,tau,B1,B2,fd,greek);
524 } else {
525 val = putcalltrunc(S,vol,rd,rf,tau,K,B1,B2,pc,greek);
526 }
527 return val;
528}
529
530} // namespace internal
531
532// path dependent options
533
534
535namespace 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
545static double barrier_term(double S, double vol, double rd, double rf,
546 double tau, double K, double B1, double B2, double sc,
548 types::Greeks greek) {
549
550 assert(tau>=0.0);
551 assert(S>0.0);
552 assert(vol>0.0);
553
554 // V(S) = V1(sc*S) - (B/S)^a V1(sc*B^2/S)
555 double val = 0.0;
556 double B = (B1>0.0) ? B1 : B2;
557 double a = 2.0*(rd-rf)/(vol*vol)-1.0; // helper variable
558 double b = 4.0*(rd-rf)/(vol*vol*vol); // helper variable -da/dvol
559 double c = 12.0*(rd-rf)/(vol*vol*vol*vol); // helper -db/dvol
560 switch(greek) {
561 case types::Value:
562 val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
563 - pow(B/S,a)*
564 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
565 break;
566 case types::Delta:
567 val = sc*vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
568 + pow(B/S,a) * (
569 a/S*
570 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
571 + sqr(B/S)*sc*
572 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
573 );
574 break;
575 case types::Gamma:
576 val = sc*sc*vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
577 - pow(B/S,a) * (
578 a*(a+1.0)/(S*S)*
579 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
580 + (2.0*a+2.0)*B*B/(S*S*S)*sc*
581 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Delta)
582 + sqr(sqr(B/S))*sc*sc*
583 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Gamma)
584 );
585 break;
586 case types::Theta:
587 val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
588 - pow(B/S,a)*
589 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
590 break;
591 case types::Vega:
592 val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
593 - pow(B/S,a) * (
594 - b*log(B/S)*
595 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
596 + 1.0*
597 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
598 );
599 break;
600 case types::Volga:
601 val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
602 - pow(B/S,a) * (
603 log(B/S)*(b*b*log(B/S)+c)*
604 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
605 - 2.0*b*log(B/S)*
606 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Vega)
607 + 1.0*
608 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Volga)
609 );
610 break;
611 case types::Vanna:
612 val = sc*vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
613 - pow(B/S,a) * (
614 b/S*(log(B/S)*a+1.0)*
615 vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
616 + b*log(B/S)*sqr(B/S)*sc*
617 vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Delta)
618 - a/S*
619 vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Vega)
620 - sqr(B/S)*sc*
621 vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Vanna)
622 );
623 break;
624 case types::Rho_d:
625 val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
626 - pow(B/S,a) * (
627 2.0*log(B/S)/(vol*vol)*
628 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
629 + 1.0*
630 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
631 );
632 break;
633 case types::Rho_f:
634 val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
635 - pow(B/S,a) * (
636 - 2.0*log(B/S)/(vol*vol)*
637 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
638 + 1.0*
639 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
640 );
641 break;
642 default:
643 printf("barrier_term: greek %d not implemented\n", greek );
644 abort();
645 }
646 return val;
647}
648
649// one term of the infinite sum for the valuation of double barriers
650static double barrier_double_term( double S, double vol, double rd, double rf,
651 double tau, double K, double B1, double B2,
652 double fac, double sc, int i,
654
655 double val = 0.0;
656 double b = 4.0*i*(rd-rf)/(vol*vol*vol); // helper variable -da/dvol
657 double c = 12.0*i*(rd-rf)/(vol*vol*vol*vol); // helper -db/dvol
658 switch(greek) {
659 case types::Value:
660 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek);
661 break;
662 case types::Delta:
663 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek);
664 break;
665 case types::Gamma:
666 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek);
667 break;
668 case types::Theta:
669 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek);
670 break;
671 case types::Vega:
672 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek)
673 - b*log(B2/B1)*fac *
674 barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Value);
675 break;
676 case types::Volga:
677 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek)
678 - 2.0*b*log(B2/B1)*fac *
679 barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Vega)
680 + log(B2/B1)*fac*(c+b*b*log(B2/B1)) *
681 barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Value);
682 break;
683 case types::Vanna:
684 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek)
685 - b*log(B2/B1)*fac *
686 barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Delta);
687 break;
688 case types::Rho_d:
689 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek)
690 + 2.0*i/(vol*vol)*log(B2/B1)*fac *
691 barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Value);
692 break;
693 case types::Rho_f:
694 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek)
695 - 2.0*i/(vol*vol)*log(B2/B1)*fac *
696 barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Value);
697 break;
698 default:
699 printf("barrier_double_term: greek %d not implemented\n", greek );
700 abort();
701 }
702 return val;
703}
704
705// general pricer for any type of options with continuously monitored barriers
706// allows two, one or zero barriers, only knock-out style
707// payoff profiles allowed based on vanilla_trunc()
708static double barrier_ko(double S, double vol, double rd, double rf,
709 double tau, double K, double B1, double B2,
711 types::Greeks greek) {
712
713 assert(tau>=0.0);
714 assert(S>0.0);
715 assert(vol>0.0);
716
717 double val = 0.0;
718
719 if(B1<=0.0 && B2<=0.0) {
720 // no barriers --> vanilla case
721 val = vanilla(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
722 } else if(B1>0.0 && B2<=0.0) {
723 // lower barrier
724 if(S<=B1) {
725 val = 0.0; // knocked out
726 } else {
727 val = barrier_term(S,vol,rd,rf,tau,K,B1,B2,1.0,pc,fd,greek);
728 }
729 } else if(B1<=0.0 && B2>0.0) {
730 // upper barrier
731 if(S>=B2) {
732 val = 0.0; // knocked out
733 } else {
734 val = barrier_term(S,vol,rd,rf,tau,K,B1,B2,1.0,pc,fd,greek);
735 }
736 } else if(B1>0.0 && B2>0.0) {
737 // double barrier
738 if(S<=B1 || S>=B2) {
739 val = 0.0; // knocked out (always true if wrong input B1>B2)
740 } else {
741 // more complex calculation as we have to evaluate an infinite
742 // sum
743 // to reduce very costly pow() calls we define some variables
744 double a = 2.0*(rd-rf)/(vol*vol)-1.0; // 2 (mu-1/2vol^2)/sigma^2
745 double BB2=sqr(B2/B1);
746 double BBa=pow(B2/B1,a);
747 double BB2inv=1.0/BB2;
748 double BBainv=1.0/BBa;
749 double fac=1.0;
750 double facinv=1.0;
751 double sc=1.0;
752 double scinv=1.0;
753
754 // initial term i=0
755 val=barrier_double_term(S,vol,rd,rf,tau,K,B1,B2,fac,sc,0,pc,fd,greek);
756 // infinite loop, 10 should be plenty, normal would be 2
757 for(int i=1; i<10; i++) {
758 fac*=BBa;
759 facinv*=BBainv;
760 sc*=BB2;
761 scinv*=BB2inv;
762 double add =
763 barrier_double_term(S,vol,rd,rf,tau,K,B1,B2,fac,sc,i,pc,fd,greek) +
764 barrier_double_term(S,vol,rd,rf,tau,K,B1,B2,facinv,scinv,-i,pc,fd,greek);
765 val += add;
766 //printf("%i: val=%e (add=%e)\n",i,val,add);
767 if(fabs(add) <= 1e-12*fabs(val)) {
768 break;
769 }
770 }
771 // not knocked-out double barrier end
772 }
773 // double barrier end
774 } else {
775 // no such barrier combination exists
776 assert(false);
777 }
778
779 return val;
780}
781
782// knock-in style barrier
783static double barrier_ki(double S, double vol, double rd, double rf,
784 double tau, double K, double B1, double B2,
786 types::Greeks greek) {
787 return vanilla(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
788 -barrier_ko(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
789}
790
791// general barrier
792static double barrier(double S, double vol, double rd, double rf,
793 double tau, double K, double B1, double B2,
796 types::Greeks greek) {
797
798 double val = 0.0;
799 if( kio==types::KnockOut && bcont==types::Maturity ) {
800 // truncated vanilla option
801 val = vanilla_trunc(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
802 } else if ( kio==types::KnockOut && bcont==types::Continuous ) {
803 // standard knock-out barrier
804 val = barrier_ko(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
805 } else if ( kio==types::KnockIn && bcont==types::Maturity ) {
806 // inverse truncated vanilla
807 val = vanilla(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
808 - vanilla_trunc(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
809 } else if ( kio==types::KnockIn && bcont==types::Continuous ) {
810 // standard knock-in barrier
811 val = barrier_ki(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
812 } else {
813 // never get here
814 assert(false);
815 }
816 return val;
817}
818
819} // namespace internal
820
821
822// touch/no-touch options (cash/asset or nothing payoff profile)
823double touch(double S, double vol, double rd, double rf,
824 double tau, double B1, double B2, types::ForDom fd,
826 types::Greeks greek) {
827
828 double K=-1.0; // dummy
829 types::PutCall pc = types::Call; // dummy
830 double val = 0.0;
831 if( kio==types::KnockOut && bcont==types::Maturity ) {
832 // truncated vanilla option
833 val = internal::vanilla_trunc(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
834 } else if ( kio==types::KnockOut && bcont==types::Continuous ) {
835 // standard knock-out barrier
836 val = internal::barrier_ko(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
837 } else if ( kio==types::KnockIn && bcont==types::Maturity ) {
838 // inverse truncated vanilla
839 val = internal::vanilla(S,vol,rd,rf,tau,K,-1.0,-1.0,pc,fd,greek)
840 - internal::vanilla_trunc(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
841 } else if ( kio==types::KnockIn && bcont==types::Continuous ) {
842 // standard knock-in barrier
843 val = internal::vanilla(S,vol,rd,rf,tau,K,-1.0,-1.0,pc,fd,greek)
844 - internal::barrier_ko(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
845 } else {
846 // never get here
847 assert(false);
848 }
849 return val;
850}
851
852// barrier option (put/call payoff profile)
853double barrier(double S, double vol, double rd, double rf,
854 double tau, double K, double B1, double B2,
855 double rebate,
858 types::Greeks greek) {
859 assert(tau>=0.0);
860 assert(S>0.0);
861 assert(vol>0.0);
862 assert(K>=0.0);
864 double val=internal::barrier(S,vol,rd,rf,tau,K,B1,B2,pc,fd,kio,bcont,greek);
865 if(rebate!=0.0) {
866 // opposite of barrier knock-in/out type
869 val += rebate*touch(S,vol,rd,rf,tau,B1,B2,fd,kio2,bcont,greek);
870 }
871 return val;
872}
873
874// probability of hitting a barrier
875// this is almost the same as the price of a touch option (domestic)
876// as it pays one if a barrier is hit; we only have to offset the
877// discounting and we get the probability
878double prob_hit(double S, double vol, double mu,
879 double tau, double B1, double B2) {
880 double const rd=0.0;
881 double rf=-mu;
882 return 1.0 - touch(S,vol,rd,rf,tau,B1,B2,types::Domestic,types::KnockOut,
884}
885
886// probability of being in-the-money, ie payoff is greater zero,
887// assuming payoff(S_T) > 0 iff S_T in [B1, B2]
888// this the same as the price of a cash or nothing option
889// with no discounting
890double prob_in_money(double S, double vol, double mu,
891 double tau, double B1, double B2) {
892 assert(S>0.0);
893 assert(vol>0.0);
894 assert(tau>=0.0);
895 double val = 0.0;
896 if( B1<B2 || B1<=0.0 || B2<=0.0 ) {
897 val = binary(S,vol,0.0,-mu,tau,B1,B2,types::Domestic,types::Value);
898 }
899 return val;
900}
901double prob_in_money(double S, double vol, double mu,
902 double tau, double K, double B1, double B2,
903 types::PutCall pc) {
904 assert(S>0.0);
905 assert(vol>0.0);
906 assert(tau>=0.0);
907
908 // if K<0 we assume a binary option is given
909 if(K<0.0) {
910 return prob_in_money(S,vol,mu,tau,B1,B2);
911 }
912
913 double val = 0.0;
914 double BM1, BM2; // range of in the money [BM1, BM2]
915 // non-sense parameters with no positive payoff
916 if( (B1>B2 && B1>0.0 && B2>0.0) ||
917 (K>=B2 && B2>0.0 && pc==types::Call) ||
918 (K<=B1 && pc==types::Put) ) {
919 val = 0.0;
920 // need to figure out between what barriers payoff is greater 0
921 } else if(pc==types::Call) {
922 BM1=std::max(B1, K);
923 BM2=B2;
924 val = prob_in_money(S,vol,mu,tau,BM1,BM2);
925 } else if (pc==types::Put) {
926 BM1=B1;
927 BM2= (B2>0.0) ? std::min(B2,K) : K;
928 val = prob_in_money(S,vol,mu,tau,BM1,BM2);
929 } else {
930 // don't get here
931 assert(false);
932 }
933 return val;
934}
935
936} // namespace sca
937
938
939/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
int fac(int n)
float x
uno_Any a
int i
log
static double barrier_ko(double S, double vol, double rd, double rf, double tau, double K, double B1, double B2, types::PutCall pc, types::ForDom fd, types::Greeks greek)
static double barrier_double_term(double S, double vol, double rd, double rf, double tau, double K, double B1, double B2, double fac, double sc, int i, types::PutCall pc, types::ForDom fd, types::Greeks greek)
static double barrier(double S, double vol, double rd, double rf, double tau, double K, double B1, double B2, types::PutCall pc, types::ForDom fd, types::BarrierKIO kio, types::BarrierActive bcont, types::Greeks greek)
static double vanilla(double S, double vol, double rd, double rf, double tau, double K, double B1, double B2, types::PutCall pc, types::ForDom fd, types::Greeks greek)
static double vanilla_trunc(double S, double vol, double rd, double rf, double tau, double K, double B1, double B2, types::PutCall pc, types::ForDom fd, types::Greeks greek)
static double barrier_ki(double S, double vol, double rd, double rf, double tau, double K, double B1, double B2, types::PutCall pc, types::ForDom fd, types::Greeks greek)
static double barrier_term(double S, double vol, double rd, double rf, double tau, double K, double B1, double B2, double sc, types::PutCall pc, types::ForDom fd, types::Greeks greek)
static double sqr(double x)
static double binary(double S, double vol, double rd, double rf, double tau, double K, types::PutCall pc, types::ForDom fd, types::Greeks greek)
double touch(double S, double vol, double rd, double rf, double tau, double B1, double B2, types::ForDom fd, types::BarrierKIO kio, types::BarrierActive bcont, types::Greeks greek)
double putcalltrunc(double S, double vol, double rd, double rf, double tau, double K, double B, types::PutCall pc, types::KOType kotype, types::Greeks greeks)
static double pnorm(double x)
double prob_in_money(double S, double vol, double mu, double tau, double B1, double B2)
double binasset(double S, double vol, double rd, double rf, double tau, double K, types::PutCall pc, types::Greeks greeks)
double barrier(double S, double vol, double rd, double rf, double tau, double K, double B1, double B2, double rebate, types::PutCall pc, types::BarrierKIO kio, types::BarrierActive bcont, types::Greeks greek)
double putcall(double S, double vol, double rd, double rf, double tau, double K, types::PutCall putcall, types::Greeks greeks)
static double dnorm(double x)
double prob_hit(double S, double vol, double mu, double tau, double B1, double B2)
double bincash(double S, double vol, double rd, double rf, double tau, double K, types::PutCall pc, types::Greeks greeks)