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
562 val
= vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
564 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
567 val
= sc
*vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
570 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Value
)
572 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
576 val
= sc
*sc
*vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
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
)
587 val
= vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
589 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
);
592 val
= vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
595 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Value
)
597 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
601 val
= vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
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
)
606 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Vega
)
608 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Volga
)
612 val
= sc
*vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
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
)
619 vanilla_trunc(B
*B
/S
*sc
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Vega
)
621 vanilla_trunc(B
*B
/S
*sc
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,types::Vanna
)
625 val
= vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
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
)
630 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
634 val
= vanilla_trunc(sc
*S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
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
)
639 vanilla_trunc(sc
*B
*B
/S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,greek
)
643 printf("barrier_term: greek %d not implemented\n", greek
);
649 // one term of the infinite sum for the valuation of double barriers
650 static 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
,
653 types::PutCall pc
, types::ForDom fd
, types::Greeks greek
) {
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
660 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
);
663 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
);
666 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
);
669 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
);
672 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
)
674 barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,types::Value
);
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
);
684 val
= fac
*barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,greek
)
686 barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,sc
,pc
,fd
,types::Delta
);
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
);
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
);
699 printf("barrier_double_term: greek %d not implemented\n", greek
);
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()
708 static double barrier_ko(double S
, double vol
, double rd
, double rf
,
709 double tau
, double K
, double B1
, double B2
,
710 types::PutCall pc
, types::ForDom fd
,
711 types::Greeks greek
) {
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) {
725 val
= 0.0; // knocked out
727 val
= barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,1.0,pc
,fd
,greek
);
729 } else if(B1
<=0.0 && B2
>0.0) {
732 val
= 0.0; // knocked out
734 val
= barrier_term(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,1.0,pc
,fd
,greek
);
736 } else if(B1
>0.0 && B2
>0.0) {
739 val
= 0.0; // knocked out (always true if wrong input B1>B2)
741 // more complex calculation as we have to evaluate an infinite
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
;
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
++) {
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
);
766 //printf("%i: val=%e (add=%e)\n",i,val,add);
767 if(fabs(add
) <= 1e-12*fabs(val
)) {
771 // not knocked-out double barrier end
773 // double barrier end
775 // no such barrier combination exists
782 // knock-in style barrier
783 static double barrier_ki(double S
, double vol
, double rd
, double rf
,
784 double tau
, double K
, double B1
, double B2
,
785 types::PutCall pc
, types::ForDom fd
,
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
);
792 static double barrier(double S
, double vol
, double rd
, double rf
,
793 double tau
, double K
, double B1
, double B2
,
794 types::PutCall pc
, types::ForDom fd
,
795 types::BarrierKIO kio
, types::BarrierActive bcont
,
796 types::Greeks greek
) {
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
);
819 } // namespace internal
822 // touch/no-touch options (cash/asset or nothing payoff profile)
823 double touch(double S
, double vol
, double rd
, double rf
,
824 double tau
, double B1
, double B2
, types::ForDom fd
,
825 types::BarrierKIO kio
, types::BarrierActive bcont
,
826 types::Greeks greek
) {
828 double K
=-1.0; // dummy
829 types::PutCall pc
= types::Call
; // dummy
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
);
852 // barrier option (put/call payoff profile)
853 double barrier(double S
, double vol
, double rd
, double rf
,
854 double tau
, double K
, double B1
, double B2
,
856 types::PutCall pc
, types::BarrierKIO kio
,
857 types::BarrierActive bcont
,
858 types::Greeks greek
) {
863 types::ForDom fd
= types::Domestic
;
864 double val
=internal::barrier(S
,vol
,rd
,rf
,tau
,K
,B1
,B2
,pc
,fd
,kio
,bcont
,greek
);
866 // opposite of barrier knock-in/out type
867 types::BarrierKIO kio2
= (kio
==types::KnockIn
) ? types::KnockOut
869 val
+= rebate
*touch(S
,vol
,rd
,rf
,tau
,B1
,B2
,fd
,kio2
,bcont
,greek
);
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
878 double prob_hit(double S
, double vol
, double mu
,
879 double tau
, double B1
, double B2
) {
882 return 1.0 - touch(S
,vol
,rd
,rf
,tau
,B1
,B2
,types::Domestic
,types::KnockOut
,
883 types::Continuous
, types::Value
);
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
890 double prob_in_money(double S
, double vol
, double mu
,
891 double tau
, double B1
, double B2
) {
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
);
901 double prob_in_money(double S
, double vol
, double mu
,
902 double tau
, double K
, double B1
, double B2
,
908 // if K<0 we assume a binary option is given
910 return prob_in_money(S
,vol
,mu
,tau
,B1
,B2
);
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
) ) {
920 // need to figure out between what barriers payoff is greater 0
921 } else if(pc
==types::Call
) {
924 val
= prob_in_money(S
,vol
,mu
,tau
,BM1
,BM2
);
925 } else if (pc
==types::Put
) {
927 BM2
= (B2
>0.0) ? std::min(B2
,K
) : K
;
928 val
= prob_in_money(S
,vol
,mu
,tau
,BM1
,BM2
);
939 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */