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 prices
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
81 namespace sca::pricing::bs
{
86 static double sqr(double x
) {
89 // normal density (see also ScInterpreter::phi)
90 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 return 0.39894228040143268*exp(-0.5*x
*x
);
94 // cumulative normal distribution (see also ScInterpreter::integralPhi)
95 static double pnorm(double x
) {
96 return 0.5 * std::erfc(-x
* M_SQRT1_2
);
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 types::PutCall pc
, types::Greeks greeks
) {
113 // special case tau=0 (expiry)
116 if( (pc
==types::Call
&& S
>=K
) || (pc
==types::Put
&& S
<=K
) ) {
126 // special case with zero strike
128 // up-and-out (put) with K=0
131 // down-and-out (call) with K=0 (zero coupon bond)
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;
157 val
= pm
*dnorm(d2
)/(S
*vol
*sqrt(tau
));
160 val
= -pm
*dnorm(d2
)*d1
/(sqr(S
*vol
)*tau
);
163 val
= rd
*pnorm(pm
*d2
)
164 + pm
*dnorm(d2
)*(log(S
/K
)/(vol
*sqrt(tau
))-0.5*d2
)/tau
;
167 val
= -pm
*dnorm(d2
)*d1
/vol
;
170 val
= pm
*dnorm(d2
)/(vol
*vol
)*(-d1
*d1
*d2
+d1
+d2
);
173 val
= pm
*dnorm(d2
)/(S
*vol
*vol
*sqrt(tau
))*(d1
*d2
-1.0);
176 val
= -tau
*pnorm(pm
*d2
) + pm
*dnorm(d2
)*sqrt(tau
)/vol
;
179 val
= -pm
*dnorm(d2
)*sqrt(tau
)/vol
;
182 printf("bincash: greek %d not implemented\n", greeks
);
186 return exp(-rd
*tau
)*val
;
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 types::PutCall pc
, types::Greeks greeks
) {
202 // special case tau=0 (expiry)
205 if( (pc
==types::Call
&& S
>=K
) || (pc
==types::Put
&& S
<=K
) ) {
212 if( (pc
==types::Call
&& S
>=K
) || (pc
==types::Put
&& S
<=K
) ) {
222 // special case with zero strike (forward with zero strike)
224 // up-and-out (put) with K=0
227 // down-and-out (call) with K=0 (type of forward)
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;
253 val
= S
*pnorm(pm
*d1
);
256 val
= pnorm(pm
*d1
) + pm
*dnorm(d1
)/(vol
*sqrt(tau
));
259 val
= -pm
*dnorm(d1
)*d2
/(S
*sqr(vol
)*tau
);
262 val
= rf
*S
*pnorm(pm
*d1
)
263 + pm
*S
*dnorm(d1
)*(log(S
/K
)/(vol
*sqrt(tau
))-0.5*d1
)/tau
;
266 val
= -pm
*S
*dnorm(d1
)*d2
/vol
;
269 val
= pm
*S
*dnorm(d1
)/(vol
*vol
)*(-d1
*d2
*d2
+d1
+d2
);
272 val
= pm
*dnorm(d1
)/(vol
*vol
*sqrt(tau
))*(d2
*d2
-1.0);
275 val
= pm
*S
*dnorm(d1
)*sqrt(tau
)/vol
;
278 val
= -tau
*S
*pnorm(pm
*d1
) - pm
*S
*dnorm(d1
)*sqrt(tau
)/vol
;
281 printf("binasset: greek %d not implemented\n", greeks
);
285 return exp(-rf
*tau
)*val
;
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 types::Greeks greek
) {
298 case types::Domestic
:
299 val
= bincash(S
,vol
,rd
,rf
,tau
,K
,pc
,greek
);
302 val
= binasset(S
,vol
,rd
,rf
,tau
,K
,pc
,greek
);
311 // further wrapper to combine single/double barrier binary options
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 types::ForDom fd
, types::Greeks greek
) {
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) {
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
);
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 types::PutCall putcall
, types::Greeks greeks
) {
363 int pm
= (putcall
==types::Call
) ? 1 : -1;
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
) );
371 // we could just use pm*(binasset-K*bincash), however
372 // since the formula for delta and gamma simplify we write them
374 double d1
= ( log(S
/K
)+(rd
-rf
+0.5*vol
*vol
)*tau
) / (vol
*sqrt(tau
));
375 double d2
= d1
- vol
*sqrt(tau
);
379 val
= pm
* ( exp(-rf
*tau
)*S
*pnorm(pm
*d1
)-exp(-rd
*tau
)*K
*pnorm(pm
*d2
) );
382 val
= pm
*exp(-rf
*tau
)*pnorm(pm
*d1
);
385 val
= exp(-rf
*tau
)*dnorm(d1
)/(S
*vol
*sqrt(tau
));
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
) );
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 types::Greeks greeks
) {
413 int pm
= (pc
==types::Call
) ? 1 : -1;
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
);
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
) );
428 if( (pc
==types::Call
&& B
<=K
) || (pc
==types::Put
&& B
>=K
) ) {
429 // option degenerates to zero payoff
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
) );
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 types::PutCall pc
, types::Greeks greek
) {
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
);
468 val
= putcalltrunc(S
,vol
,rd
,rf
,tau
,K
,B2
,pc
,types::Regular
,greek
);
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
);
475 val
= putcalltrunc(S
,vol
,rd
,rf
,tau
,K
,B1
,pc
,types::Reverse
,greek
);
477 } else if(B1
>0.0 && B2
>0.0) {
482 int pm
= (pc
==types::Call
) ? 1 : -1;
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
)
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 types::Greeks greek
) {
508 // binary option if K<0
509 val
= binary(S
,vol
,rd
,rf
,tau
,B1
,B2
,fd
,greek
);
511 val
= putcall(S
,vol
,rd
,rf
,tau
,K
,pc
,greek
);
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 types::Greeks greek
) {
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
);
525 val
= putcalltrunc(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,greek
);
530 } // namespace internal
532 // path dependent options
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 types::Greeks greek
) {
554 // V(S) = V1(sc*S) - (B/S)^a V1(sc*B^2/S)
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
563 val
= vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
565 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
568 val
= sc
*vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
571 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Value
)
573 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
577 val
= sc
*sc
*vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
580 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Value
)
581 + (2.0*a
+2.0)*B
*B
/(S
*S
*S
)*sc
*
582 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Delta
)
583 + sqr(sqr(B
/S
))*sc
*sc
*
584 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Gamma
)
588 val
= vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
591 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Value
)
593 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
597 val
= vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
599 log(B
/S
)*(b
*b
*log(B
/S
)+c
)*
600 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Value
)
602 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Vega
)
604 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Volga
)
608 val
= sc
*vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
610 b
/S
*(log(B
/S
)*a
+1.0)*
611 vanilla_trunc(B
*B
/S
*sc
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Value
)
612 + b
*log(B
/S
)*sqr(B
/S
)*sc
*
613 vanilla_trunc(B
*B
/S
*sc
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Delta
)
615 vanilla_trunc(B
*B
/S
*sc
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Vega
)
617 vanilla_trunc(B
*B
/S
*sc
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Vanna
)
621 val
= vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
623 2.0*log(B
/S
)/(vol
*vol
)*
624 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Value
)
626 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
630 val
= vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
632 - 2.0*log(B
/S
)/(vol
*vol
)*
633 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Value
)
635 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
639 printf("barrier_term: greek %d not implemented\n", greek
);
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 types::PutCall pc
, types::ForDom fd
, types::Greeks greek
) {
652 double b
= 4.0*i
*(rd
-rf
)/(vol
*vol
*vol
); // helper variable -da/dvol
653 double c
= 12.0*i
*(rd
-rf
)/(vol
*vol
*vol
*vol
); // helper -db/dvol
656 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
);
659 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
);
662 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
);
665 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
);
668 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
)
670 barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,types::Value
);
673 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
)
674 - 2.0*b
*log(B2
/B1
)*fac
*
675 barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,types::Vega
)
676 + log(B2
/B1
)*fac
*(c
+b
*b
*log(B2
/B1
)) *
677 barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,types::Value
);
680 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
)
682 barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,types::Delta
);
685 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
)
686 + 2.0*i
/(vol
*vol
)*log(B2
/B1
)*fac
*
687 barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,types::Value
);
690 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
)
691 - 2.0*i
/(vol
*vol
)*log(B2
/B1
)*fac
*
692 barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,types::Value
);
695 printf("barrier_double_term: greek %d not implemented\n", greek
);
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 types::Greeks greek
) {
715 if(B1
<=0.0 && B2
<=0.0) {
716 // no barriers --> vanilla case
717 val
= vanilla(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
718 } else if(B1
>0.0 && B2
<=0.0) {
721 val
= 0.0; // knocked out
723 val
= barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,1.0,pc
,fd
,greek
);
725 } else if(B1
<=0.0 && B2
>0.0) {
728 val
= 0.0; // knocked out
730 val
= barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,1.0,pc
,fd
,greek
);
732 } else if(B1
>0.0 && B2
>0.0) {
735 val
= 0.0; // knocked out (always true if wrong input B1>B2)
737 // more complex calculation as we have to evaluate an infinite
739 // to reduce very costly pow() calls we define some variables
740 double a
= 2.0*(rd
-rf
)/(vol
*vol
)-1.0; // 2 (mu-1/2vol^2)/sigma^2
741 double BB2
=sqr(B2
/B1
);
742 double BBa
=pow(B2
/B1
,a
);
743 double BB2inv
=1.0/BB2
;
744 double BBainv
=1.0/BBa
;
751 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 for(int i
=1; i
<10; i
++) {
759 barrier_double_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,fac
,sc
,i
,pc
,fd
,greek
) +
760 barrier_double_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,facinv
,scinv
,-i
,pc
,fd
,greek
);
762 //printf("%i: val=%e (add=%e)\n",i,val,add);
763 if(fabs(add
) <= 1e-12*fabs(val
)) {
767 // not knocked-out double barrier end
769 // double barrier end
771 // no such barrier combination exists
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 types::Greeks greek
) {
783 return vanilla(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
784 -barrier_ko(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
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 types::Greeks greek
) {
795 if( kio
==types::KnockOut
&& bcont
==types::Maturity
) {
796 // truncated vanilla option
797 val
= vanilla_trunc(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
798 } else if ( kio
==types::KnockOut
&& bcont
==types::Continuous
) {
799 // standard knock-out barrier
800 val
= barrier_ko(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
801 } else if ( kio
==types::KnockIn
&& bcont
==types::Maturity
) {
802 // inverse truncated vanilla
803 val
= vanilla(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
804 - vanilla_trunc(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
805 } else if ( kio
==types::KnockIn
&& bcont
==types::Continuous
) {
806 // standard knock-in barrier
807 val
= barrier_ki(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
815 } // namespace internal
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 types::Greeks greek
) {
824 double K
=-1.0; // dummy
825 types::PutCall pc
= types::Call
; // dummy
827 if( kio
==types::KnockOut
&& bcont
==types::Maturity
) {
828 // truncated vanilla option
829 val
= internal::vanilla_trunc(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
830 } else if ( kio
==types::KnockOut
&& bcont
==types::Continuous
) {
831 // standard knock-out barrier
832 val
= internal::barrier_ko(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
833 } else if ( kio
==types::KnockIn
&& bcont
==types::Maturity
) {
834 // inverse truncated vanilla
835 val
= internal::vanilla(S
,vol
,rd
,rf
,tau
,K
,-1.0,-1.0,pc
,fd
,greek
)
836 - internal::vanilla_trunc(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
837 } else if ( kio
==types::KnockIn
&& bcont
==types::Continuous
) {
838 // standard knock-in barrier
839 val
= internal::vanilla(S
,vol
,rd
,rf
,tau
,K
,-1.0,-1.0,pc
,fd
,greek
)
840 - internal::barrier_ko(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
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
,
852 types::PutCall pc
, types::BarrierKIO kio
,
853 types::BarrierActive bcont
,
854 types::Greeks greek
) {
859 types::ForDom fd
= types::Domestic
;
860 double val
=internal::barrier(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,kio
,bcont
,greek
);
862 // opposite of barrier knock-in/out type
863 types::BarrierKIO kio2
= (kio
==types::KnockIn
) ? types::KnockOut
865 val
+= rebate
*touch(S
,vol
,rd
,rf
,tau
,B1
,B2
,fd
,kio2
,bcont
,greek
);
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 double tau
, double B1
, double B2
) {
878 return 1.0 - touch(S
,vol
,rd
,rf
,tau
,B1
,B2
,types::Domestic
,types::KnockOut
,
879 types::Continuous
, types::Value
);
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 double tau
, double B1
, double B2
) {
892 if( B1
<B2
|| B1
<=0.0 || B2
<=0.0 ) {
893 val
= binary(S
,vol
,0.0,-mu
,tau
,B1
,B2
,types::Domestic
,types::Value
);
897 double prob_in_money(double S
, double vol
, double mu
,
898 double tau
, double K
, double B1
, double B2
,
904 // if K<0 we assume a binary option is given
906 return prob_in_money(S
,vol
,mu
,tau
,B1
,B2
);
910 double BM1
, BM2
; // range of in the money [BM1, BM2]
911 // non-sense parameters with no positive payoff
912 if( (B1
>B2
&& B1
>0.0 && B2
>0.0) ||
913 (K
>=B2
&& B2
>0.0 && pc
==types::Call
) ||
914 (K
<=B1
&& pc
==types::Put
) ) {
916 // need to figure out between what barriers payoff is greater 0
917 } else if(pc
==types::Call
) {
920 val
= prob_in_money(S
,vol
,mu
,tau
,BM1
,BM2
);
921 } else if (pc
==types::Put
) {
923 BM2
= (B2
>0.0) ? std::min(B2
,K
) : K
;
924 val
= prob_in_money(S
,vol
,mu
,tau
,BM1
,BM2
);
935 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */