1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * Copyright (C) 2012 Tino Kluge <tino.kluge@hrz.tu-chemnitz.de>
18 #include <rtl/math.hxx>
19 #include "black_scholes.hxx"
21 // options prices and greeks in the Black-Scholes model
22 // also known as TV (theoretical value)
24 // the code is structured as follows:
27 // - cash-or-nothing option: bincash()
28 // - asset-or-nothing option: binasset()
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)
34 // (3) write a wrapper function to include all vanilla pricers
35 // - this is so we don't duplicate code when pricing barriers
36 // as this is derived from vanillas
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
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)
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):
52 // W(S)=sum (B2/B1)^(i*a) (V(S(B2/B1)^(2i)) - (B1/S)^a V(B1^2/S (B2/B1)^(2i))
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
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()
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
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)
77 // this code is not tested for numerical instability, ie overruns,
78 // underruns, accuracy, etc
89 inline double sqr(double x
) {
92 // normal density (see also ScInterpreter::phi)
93 inline double dnorm(double x
) {
94 //return (1.0/sqrt(2.0*M_PI))*exp(-0.5*x*x); // windows may not have M_PI
95 return 0.39894228040143268*exp(-0.5*x
*x
);
97 // cumulative normal distribution (see also ScInterpreter::integralPhi)
98 inline double pnorm(double x
) {
99 //return 0.5*(erf(sqrt(0.5)*x)+1.0); // windows may not have erf
100 return 0.5 * ::rtl::math::erfc(-x
* 0.7071067811865475);
103 // binary option cash (domestic)
104 // call - pays 1 if S_T is above strike K
105 // put - pays 1 if S_T is below strike K
106 double bincash(double S
, double vol
, double rd
, double rf
,
107 double tau
, double K
,
108 types::PutCall pc
, types::Greeks greeks
) {
117 // special case tau=0 (expiry)
120 if( (pc
==types::Call
&& S
>=K
) || (pc
==types::Put
&& S
<=K
) ) {
130 // special case with zero strike
132 // up-and-out (put) with K=0
135 // down-and-out (call) with K=0 (zero coupon bond)
151 // standard case with K>0, tau>0
152 double d1
= ( log(S
/K
)+(rd
-rf
+0.5*vol
*vol
)*tau
) / (vol
*sqrt(tau
));
153 double d2
= d1
- vol
*sqrt(tau
);
154 int pm
= (pc
==types::Call
) ? 1 : -1;
161 val
= pm
*dnorm(d2
)/(S
*vol
*sqrt(tau
));
164 val
= -pm
*dnorm(d2
)*d1
/(sqr(S
*vol
)*tau
);
167 val
= rd
*pnorm(pm
*d2
)
168 + pm
*dnorm(d2
)*(log(S
/K
)/(vol
*sqrt(tau
))-0.5*d2
)/tau
;
171 val
= -pm
*dnorm(d2
)*d1
/vol
;
174 val
= pm
*dnorm(d2
)/(vol
*vol
)*(-d1
*d1
*d2
+d1
+d2
);
177 val
= pm
*dnorm(d2
)/(S
*vol
*vol
*sqrt(tau
))*(d1
*d2
-1.0);
180 val
= -tau
*pnorm(pm
*d2
) + pm
*dnorm(d2
)*sqrt(tau
)/vol
;
183 val
= -pm
*dnorm(d2
)*sqrt(tau
)/vol
;
186 printf("bincash: greek %d not implemented\n", greeks
);
190 return exp(-rd
*tau
)*val
;
193 // binary option asset (foreign)
194 // call - pays S_T if S_T is above strike K
195 // put - pays S_T if S_T is below strike K
196 double binasset(double S
, double vol
, double rd
, double rf
,
197 double tau
, double K
,
198 types::PutCall pc
, types::Greeks greeks
) {
206 // special case tau=0 (expiry)
209 if( (pc
==types::Call
&& S
>=K
) || (pc
==types::Put
&& S
<=K
) ) {
216 if( (pc
==types::Call
&& S
>=K
) || (pc
==types::Put
&& S
<=K
) ) {
226 // special case with zero strike (forward with zero strike)
228 // up-and-out (put) with K=0
231 // down-and-out (call) with K=0 (type of forward)
251 double d1
= ( log(S
/K
)+(rd
-rf
+0.5*vol
*vol
)*tau
) / (vol
*sqrt(tau
));
252 double d2
= d1
- vol
*sqrt(tau
);
253 int pm
= (pc
==types::Call
) ? 1 : -1;
257 val
= S
*pnorm(pm
*d1
);
260 val
= pnorm(pm
*d1
) + pm
*dnorm(d1
)/(vol
*sqrt(tau
));
263 val
= -pm
*dnorm(d1
)*d2
/(S
*sqr(vol
)*tau
);
266 val
= rf
*S
*pnorm(pm
*d1
)
267 + pm
*S
*dnorm(d1
)*(log(S
/K
)/(vol
*sqrt(tau
))-0.5*d1
)/tau
;
270 val
= -pm
*S
*dnorm(d1
)*d2
/vol
;
273 val
= pm
*S
*dnorm(d1
)/(vol
*vol
)*(-d1
*d2
*d2
+d1
+d2
);
276 val
= pm
*dnorm(d1
)/(vol
*vol
*sqrt(tau
))*(d2
*d2
-1.0);
279 val
= pm
*S
*dnorm(d1
)*sqrt(tau
)/vol
;
282 val
= -tau
*S
*pnorm(pm
*d1
) - pm
*S
*dnorm(d1
)*sqrt(tau
)/vol
;
285 printf("binasset: greek %d not implemented\n", greeks
);
289 return exp(-rf
*tau
)*val
;
292 // just for convenience we can combine bincash and binasset into
293 // one function binary
294 // using bincash() if fd==types::Domestic
295 // using binasset() if fd==types::Foreign
296 double binary(double S
, double vol
, double rd
, double rf
,
297 double tau
, double K
,
298 types::PutCall pc
, types::ForDom fd
,
299 types::Greeks greek
) {
302 case types::Domestic
:
303 val
= bincash(S
,vol
,rd
,rf
,tau
,K
,pc
,greek
);
306 val
= binasset(S
,vol
,rd
,rf
,tau
,K
,pc
,greek
);
315 // further wrapper to combine single/double barrier binary options
317 // B1<=0 - it is assumed lower barrier not set
318 // B2<=0 - it is assumed upper barrier not set
319 double binary(double S
, double vol
, double rd
, double rf
,
320 double tau
, double B1
, double B2
,
321 types::ForDom fd
, types::Greeks greek
) {
328 if(B1
<=0.0 && B2
<=0.0) {
329 // no barriers set, payoff 1.0 (domestic) or S_T (foreign)
330 val
= binary(S
,vol
,rd
,rf
,tau
,0.0,types::Call
,fd
,greek
);
331 } else if(B1
<=0.0 && B2
>0.0) {
332 // upper barrier (put)
333 val
= binary(S
,vol
,rd
,rf
,tau
,B2
,types::Put
,fd
,greek
);
334 } else if(B1
>0.0 && B2
<=0.0) {
335 // lower barrier (call)
336 val
= binary(S
,vol
,rd
,rf
,tau
,B1
,types::Call
,fd
,greek
);
337 } else if(B1
>0.0 && B2
>0.0) {
342 val
= binary(S
,vol
,rd
,rf
,tau
,B2
,types::Put
,fd
,greek
)
343 - binary(S
,vol
,rd
,rf
,tau
,B1
,types::Put
,fd
,greek
);
353 // vanilla put/call option
354 // call pays (S_T-K)^+
355 // put pays (K-S_T)^+
356 // this is the same as: +/- (binasset - K*bincash)
357 double putcall(double S
, double vol
, double rd
, double rf
,
358 double tau
, double K
,
359 types::PutCall putcall
, types::Greeks greeks
) {
367 int pm
= (putcall
==types::Call
) ? 1 : -1;
369 if(K
==0 || tau
==0.0) {
370 // special cases, simply refer to binasset() and bincash()
371 val
= pm
* ( binasset(S
,vol
,rd
,rf
,tau
,K
,putcall
,greeks
)
372 - K
*bincash(S
,vol
,rd
,rf
,tau
,K
,putcall
,greeks
) );
375 // we could just use pm*(binasset-K*bincash), however
376 // since the formula for delta and gamma simplify we write them
378 double d1
= ( log(S
/K
)+(rd
-rf
+0.5*vol
*vol
)*tau
) / (vol
*sqrt(tau
));
379 double d2
= d1
- vol
*sqrt(tau
);
383 val
= pm
* ( exp(-rf
*tau
)*S
*pnorm(pm
*d1
)-exp(-rd
*tau
)*K
*pnorm(pm
*d2
) );
386 val
= pm
*exp(-rf
*tau
)*pnorm(pm
*d1
);
389 val
= exp(-rf
*tau
)*dnorm(d1
)/(S
*vol
*sqrt(tau
));
392 // too lazy for the other greeks, so simply refer to binasset/bincash
393 val
= pm
* ( binasset(S
,vol
,rd
,rf
,tau
,K
,putcall
,greeks
)
394 - K
*bincash(S
,vol
,rd
,rf
,tau
,K
,putcall
,greeks
) );
400 // truncated put/call option, single barrier
401 // need to specify whether it's down-and-out or up-and-out
402 // regular (keeps monotonicity): down-and-out for call, up-and-out for put
403 // reverse (destroys monoton): up-and-out for call, down-and-out for put
404 // call pays (S_T-K)^+
405 // put pays (K-S_T)^+
406 double putcalltrunc(double S
, double vol
, double rd
, double rf
,
407 double tau
, double K
, double B
,
408 types::PutCall pc
, types::KOType kotype
,
409 types::Greeks greeks
) {
417 int pm
= (pc
==types::Call
) ? 1 : -1;
422 if( (pc
==types::Call
&& B
<=K
) || (pc
==types::Put
&& B
>=K
) ) {
423 // option degenerates to standard plain vanilla call/put
424 val
= putcall(S
,vol
,rd
,rf
,tau
,K
,pc
,greeks
);
426 // normal case with truncation
427 val
= pm
* ( binasset(S
,vol
,rd
,rf
,tau
,B
,pc
,greeks
)
428 - K
*bincash(S
,vol
,rd
,rf
,tau
,B
,pc
,greeks
) );
432 if( (pc
==types::Call
&& B
<=K
) || (pc
==types::Put
&& B
>=K
) ) {
433 // option degenerates to zero payoff
436 // normal case with truncation
437 val
= binasset(S
,vol
,rd
,rf
,tau
,K
,types::Call
,greeks
)
438 - binasset(S
,vol
,rd
,rf
,tau
,B
,types::Call
,greeks
)
439 - K
* ( bincash(S
,vol
,rd
,rf
,tau
,K
,types::Call
,greeks
)
440 - bincash(S
,vol
,rd
,rf
,tau
,B
,types::Call
,greeks
) );
449 // wrapper function for put/call option which combines
450 // double/single/no truncation barrier
451 // B1<=0 - assume no lower barrier
452 // B2<=0 - assume no upper barrier
453 double putcalltrunc(double S
, double vol
, double rd
, double rf
,
454 double tau
, double K
, double B1
, double B2
,
455 types::PutCall pc
, types::Greeks greek
) {
464 if(B1
<=0.0 && B2
<=0.0) {
465 // no barriers set, plain vanilla
466 val
= putcall(S
,vol
,rd
,rf
,tau
,K
,pc
,greek
);
467 } else if(B1
<=0.0 && B2
>0.0) {
468 // upper barrier: reverse barrier for call, regular barrier for put
469 if(pc
==types::Call
) {
470 val
= putcalltrunc(S
,vol
,rd
,rf
,tau
,K
,B2
,pc
,types::Reverse
,greek
);
472 val
= putcalltrunc(S
,vol
,rd
,rf
,tau
,K
,B2
,pc
,types::Regular
,greek
);
474 } else if(B1
>0.0 && B2
<=0.0) {
475 // lower barrier: regular barrier for call, reverse barrier for put
476 if(pc
==types::Call
) {
477 val
= putcalltrunc(S
,vol
,rd
,rf
,tau
,K
,B1
,pc
,types::Regular
,greek
);
479 val
= putcalltrunc(S
,vol
,rd
,rf
,tau
,K
,B1
,pc
,types::Reverse
,greek
);
481 } else if(B1
>0.0 && B2
>0.0) {
486 int pm
= (pc
==types::Call
) ? 1 : -1;
488 putcalltrunc(S
,vol
,rd
,rf
,tau
,K
,B1
,pc
,types::Regular
,greek
)
489 - putcalltrunc(S
,vol
,rd
,rf
,tau
,K
,B2
,pc
,types::Regular
,greek
)
501 // wrapper function for all non-path dependent options
502 // this is only an internal function, used to avoid code duplication when
503 // going to path-dependent barrier options,
504 // K<0 - assume binary option
505 // K>=0 - assume put/call option
506 double vanilla(double S
, double vol
, double rd
, double rf
,
507 double tau
, double K
, double B1
, double B2
,
508 types::PutCall pc
, types::ForDom fd
,
509 types::Greeks greek
) {
512 // binary option if K<0
513 val
= binary(S
,vol
,rd
,rf
,tau
,B1
,B2
,fd
,greek
);
515 val
= putcall(S
,vol
,rd
,rf
,tau
,K
,pc
,greek
);
519 double vanilla_trunc(double S
, double vol
, double rd
, double rf
,
520 double tau
, double K
, double B1
, double B2
,
521 types::PutCall pc
, types::ForDom fd
,
522 types::Greeks greek
) {
525 // binary option if K<0
526 // truncated is actually the same as the vanilla binary
527 val
= binary(S
,vol
,rd
,rf
,tau
,B1
,B2
,fd
,greek
);
529 val
= putcalltrunc(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,greek
);
534 } // namespace internal
536 // path dependent options
541 // helper term for any type of options with continuously monitored barriers,
542 // internal, should not be called from outside
543 // calculates value and greeks based on
544 // V(S) = V1(sc*S) - (B/S)^a V1(sc*B^2/S)
545 // (a=2 mu/vol^2, mu drift in logspace, ie. mu=(rd-rf-1/2vol^2))
546 // with sc=1 and V1() being the price of the respective truncated
547 // vanilla option, V() would be the price of the respective barrier
548 // option if only one barrier is present
549 double barrier_term(double S
, double vol
, double rd
, double rf
,
550 double tau
, double K
, double B1
, double B2
, double sc
,
551 types::PutCall pc
, types::ForDom fd
,
552 types::Greeks greek
) {
558 // V(S) = V1(sc*S) - (B/S)^a V1(sc*B^2/S)
560 double B
= (B1
>0.0) ? B1
: B2
;
561 double a
= 2.0*(rd
-rf
)/(vol
*vol
)-1.0; // helper variable
562 double b
= 4.0*(rd
-rf
)/(vol
*vol
*vol
); // helper variable -da/dvol
563 double c
= 12.0*(rd
-rf
)/(vol
*vol
*vol
*vol
); // helper -db/dvol
566 val
= vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
568 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
571 val
= sc
*vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
574 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Value
)
576 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
580 val
= sc
*sc
*vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
583 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Value
)
584 + (2.0*a
+2.0)*B
*B
/(S
*S
*S
)*sc
*
585 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Delta
)
586 + sqr(sqr(B
/S
))*sc
*sc
*
587 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Gamma
)
591 val
= vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
593 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
596 val
= vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
599 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Value
)
601 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
605 val
= vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
607 log(B
/S
)*(b
*b
*log(B
/S
)+c
)*
608 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Value
)
610 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Vega
)
612 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Volga
)
616 val
= sc
*vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
618 b
/S
*(log(B
/S
)*a
+1.0)*
619 vanilla_trunc(B
*B
/S
*sc
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Value
)
620 + b
*log(B
/S
)*sqr(B
/S
)*sc
*
621 vanilla_trunc(B
*B
/S
*sc
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Delta
)
623 vanilla_trunc(B
*B
/S
*sc
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Vega
)
625 vanilla_trunc(B
*B
/S
*sc
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Vanna
)
629 val
= vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
631 2.0*log(B
/S
)/(vol
*vol
)*
632 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Value
)
634 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
638 val
= vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
640 - 2.0*log(B
/S
)/(vol
*vol
)*
641 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Value
)
643 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
647 printf("barrier_term: greek %d not implemented\n", greek
);
653 // one term of the infinite sum for the valuation of double barriers
654 double barrier_double_term( double S
, double vol
, double rd
, double rf
,
655 double tau
, double K
, double B1
, double B2
,
656 double fac
, double sc
, int i
,
657 types::PutCall pc
, types::ForDom fd
, types::Greeks greek
) {
660 double b
= 4.0*i
*(rd
-rf
)/(vol
*vol
*vol
); // helper variable -da/dvol
661 double c
= 12.0*i
*(rd
-rf
)/(vol
*vol
*vol
*vol
); // helper -db/dvol
664 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
);
667 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
);
670 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
);
673 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
);
676 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
)
678 barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,types::Value
);
681 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
)
682 - 2.0*b
*log(B2
/B1
)*fac
*
683 barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,types::Vega
)
684 + log(B2
/B1
)*fac
*(c
+b
*b
*log(B2
/B1
)) *
685 barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,types::Value
);
688 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
)
690 barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,types::Delta
);
693 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
)
694 + 2.0*i
/(vol
*vol
)*log(B2
/B1
)*fac
*
695 barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,types::Value
);
698 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
)
699 - 2.0*i
/(vol
*vol
)*log(B2
/B1
)*fac
*
700 barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,types::Value
);
703 printf("barrier_double_term: greek %d not implemented\n", greek
);
709 // general pricer for any type of options with continuously monitored barriers
710 // allows two, one or zero barriers, only knock-out style
711 // payoff profiles allowed based on vanilla_trunc()
712 double barrier_ko(double S
, double vol
, double rd
, double rf
,
713 double tau
, double K
, double B1
, double B2
,
714 types::PutCall pc
, types::ForDom fd
,
715 types::Greeks greek
) {
723 if(B1
<=0.0 && B2
<=0.0) {
724 // no barriers --> vanilla case
725 val
= vanilla(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
726 } else if(B1
>0.0 && B2
<=0.0) {
729 val
= 0.0; // knocked out
731 val
= barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,1.0,pc
,fd
,greek
);
733 } else if(B1
<=0.0 && B2
>0.0) {
736 val
= 0.0; // knocked out
738 val
= barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,1.0,pc
,fd
,greek
);
740 } else if(B1
>0.0 && B2
>0.0) {
743 val
= 0.0; // knocked out (always true if wrong input B1>B2)
745 // more complex calculation as we have to evaluate an infinite
747 // to reduce very costly pow() calls we define some variables
748 double a
= 2.0*(rd
-rf
)/(vol
*vol
)-1.0; // 2 (mu-1/2vol^2)/sigma^2
749 double BB2
=sqr(B2
/B1
);
750 double BBa
=pow(B2
/B1
,a
);
751 double BB2inv
=1.0/BB2
;
752 double BBainv
=1.0/BBa
;
759 val
=barrier_double_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,fac
,sc
,0,pc
,fd
,greek
);
760 // infinite loop, 10 should be plenty, normal would be 2
761 for(int i
=1; i
<10; i
++) {
767 barrier_double_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,fac
,sc
,i
,pc
,fd
,greek
) +
768 barrier_double_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,facinv
,scinv
,-i
,pc
,fd
,greek
);
770 //printf("%i: val=%e (add=%e)\n",i,val,add);
771 if(fabs(add
) <= 1e-12*fabs(val
)) {
775 // not knocked-out double barrier end
777 // double barrier end
779 // no such barrier combination exists
786 // knock-in style barrier
787 double barrier_ki(double S
, double vol
, double rd
, double rf
,
788 double tau
, double K
, double B1
, double B2
,
789 types::PutCall pc
, types::ForDom fd
,
790 types::Greeks greek
) {
791 return vanilla(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
792 -barrier_ko(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
796 double barrier(double S
, double vol
, double rd
, double rf
,
797 double tau
, double K
, double B1
, double B2
,
798 types::PutCall pc
, types::ForDom fd
,
799 types::BarrierKIO kio
, types::BarrierActive bcont
,
800 types::Greeks greek
) {
803 if( kio
==types::KnockOut
&& bcont
==types::Maturity
) {
804 // truncated vanilla option
805 val
= vanilla_trunc(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
806 } else if ( kio
==types::KnockOut
&& bcont
==types::Continuous
) {
807 // standard knock-out barrier
808 val
= barrier_ko(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
809 } else if ( kio
==types::KnockIn
&& bcont
==types::Maturity
) {
810 // inverse truncated vanilla
811 val
= vanilla(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
812 - vanilla_trunc(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
813 } else if ( kio
==types::KnockIn
&& bcont
==types::Continuous
) {
814 // standard knock-in barrier
815 val
= barrier_ki(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
823 } // namespace internal
826 // touch/no-touch options (cash/asset or nothing payoff profile)
827 double touch(double S
, double vol
, double rd
, double rf
,
828 double tau
, double B1
, double B2
, types::ForDom fd
,
829 types::BarrierKIO kio
, types::BarrierActive bcont
,
830 types::Greeks greek
) {
832 double K
=-1.0; // dummy
833 types::PutCall pc
= types::Call
; // dummy
835 if( kio
==types::KnockOut
&& bcont
==types::Maturity
) {
836 // truncated vanilla option
837 val
= internal::vanilla_trunc(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
838 } else if ( kio
==types::KnockOut
&& bcont
==types::Continuous
) {
839 // standard knock-out barrier
840 val
= internal::barrier_ko(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
841 } else if ( kio
==types::KnockIn
&& bcont
==types::Maturity
) {
842 // inverse truncated vanilla
843 val
= internal::vanilla(S
,vol
,rd
,rf
,tau
,K
,-1.0,-1.0,pc
,fd
,greek
)
844 - internal::vanilla_trunc(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
845 } else if ( kio
==types::KnockIn
&& bcont
==types::Continuous
) {
846 // standard knock-in barrier
847 val
= internal::vanilla(S
,vol
,rd
,rf
,tau
,K
,-1.0,-1.0,pc
,fd
,greek
)
848 - internal::barrier_ko(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
856 // barrier option (put/call payoff profile)
857 double barrier(double S
, double vol
, double rd
, double rf
,
858 double tau
, double K
, double B1
, double B2
,
860 types::PutCall pc
, types::BarrierKIO kio
,
861 types::BarrierActive bcont
,
862 types::Greeks greek
) {
867 types::ForDom fd
= types::Domestic
;
868 double val
=internal::barrier(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,kio
,bcont
,greek
);
870 // opposite of barrier knock-in/out type
871 types::BarrierKIO kio2
= (kio
==types::KnockIn
) ? types::KnockOut
873 val
+= rebate
*touch(S
,vol
,rd
,rf
,tau
,B1
,B2
,fd
,kio2
,bcont
,greek
);
878 // probability of hitting a barrier
879 // this is almost the same as the price of a touch option (domestic)
880 // as it pays one if a barrier is hit; we only have to offset the
881 // discounting and we get the probability
882 double prob_hit(double S
, double vol
, double mu
,
883 double tau
, double B1
, double B2
) {
886 return 1.0 - touch(S
,vol
,rd
,rf
,tau
,B1
,B2
,types::Domestic
,types::KnockOut
,
887 types::Continuous
, types::Value
);
890 // probability of being in-the-money, ie payoff is greater zero,
891 // assuming payoff(S_T) > 0 iff S_T in [B1, B2]
892 // this the same as the price of a cash or nothing option
893 // with no discounting
894 double prob_in_money(double S
, double vol
, double mu
,
895 double tau
, double B1
, double B2
) {
900 if( B1
<B2
|| B1
<=0.0 || B2
<=0.0 ) {
901 val
= binary(S
,vol
,0.0,-mu
,tau
,B1
,B2
,types::Domestic
,types::Value
);
905 double prob_in_money(double S
, double vol
, double mu
,
906 double tau
, double K
, double B1
, double B2
,
912 // if K<0 we assume a binary option is given
914 return prob_in_money(S
,vol
,mu
,tau
,B1
,B2
);
918 double BM1
, BM2
; // range of in the money [BM1, BM2]
919 // non-sense parameters with no positive payoff
920 if( (B1
>B2
&& B1
>0.0 && B2
>0.0) ||
921 (K
>=B2
&& B2
>0.0 && pc
==types::Call
) ||
922 (K
<=B1
&& pc
==types::Put
) ) {
924 // need to figure out between what barriers payoff is greater 0
925 } else if(pc
==types::Call
) {
928 val
= prob_in_money(S
,vol
,mu
,tau
,BM1
,BM2
);
929 } else if (pc
==types::Put
) {
931 BM2
= (B2
>0.0) ? std::min(B2
,K
) : K
;
932 val
= prob_in_money(S
,vol
,mu
,tau
,BM1
,BM2
);
942 } // namespace pricing
946 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */