Avoid potential negative array index access to cached text.
[LibreOffice.git] / scaddins / source / pricing / black_scholes.cxx
blob98baa307c1007e948bc77a4d1754e942303ae969
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
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"
21 // options prices and greeks in the Black-Scholes model
22 // also known as TV (theoretical value)
24 // the code is structured as follows:
26 // (1) basic assets
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
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)
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
58 // vanillas
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 {
84 // helper functions
86 static double sqr(double x) {
87 return x*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) {
105 assert(tau>=0.0);
106 assert(S>0.0);
107 assert(vol>0.0);
108 assert(K>=0.0);
110 double val=0.0;
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;
121 break;
122 default:
123 val = 0.0;
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;
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;
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();
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) {
195 assert(tau>=0.0);
196 assert(S>0.0);
197 assert(vol>0.0);
198 assert(K>=0.0);
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;
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;
217 break;
218 default:
219 val = 0.0;
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;
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;
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();
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) {
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);
308 return val;
311 // further wrapper to combine single/double barrier binary options
312 // into one function
313 // B1<=0 - it is assumed lower barrier not set
314 // B2<=0 - it is assumed upper barrier not set
315 static double binary(double S, double vol, double rd, double rf,
316 double tau, double B1, double B2,
317 types::ForDom fd, types::Greeks greek) {
318 assert(tau>=0.0);
319 assert(S>0.0);
320 assert(vol>0.0);
322 double val=0.0;
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);
341 } else {
342 // never get here
343 assert(false);
346 return val;
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) {
357 assert(tau>=0.0);
358 assert(S>0.0);
359 assert(vol>0.0);
360 assert(K>=0.0);
362 double val = 0.0;
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) );
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);
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) );
393 return val;
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) {
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);
413 int pm = (pc==types::Call) ? 1 : -1;
414 double val = 0.0;
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) );
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) );
438 break;
439 default:
440 assert(false);
442 return val;
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) {
453 assert(tau>=0.0);
454 assert(S>0.0);
455 assert(vol>0.0);
456 assert(K>=0.0);
458 double val=0.0;
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);
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);
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)
488 } else {
489 // never get here
490 assert(false);
492 return val;
495 namespace internal {
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) {
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);
513 return val;
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) {
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);
527 return val;
530 } // namespace internal
532 // path dependent options
535 namespace internal {
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) {
550 assert(tau>=0.0);
551 assert(S>0.0);
552 assert(vol>0.0);
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)
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)
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)
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)
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)
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)
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)
641 break;
642 default:
643 printf("barrier_term: greek %d not implemented\n", greek );
644 abort();
646 return val;
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) {
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();
702 return val;
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) {
713 assert(tau>=0.0);
714 assert(S>0.0);
715 assert(vol>0.0);
717 double val = 0.0;
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);
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);
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;
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;
771 // not knocked-out double barrier end
773 // double barrier end
774 } else {
775 // no such barrier combination exists
776 assert(false);
779 return val;
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);
791 // general barrier
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) {
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);
816 return val;
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
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);
849 return val;
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,
855 double rebate,
856 types::PutCall pc, types::BarrierKIO kio,
857 types::BarrierActive bcont,
858 types::Greeks greek) {
859 assert(tau>=0.0);
860 assert(S>0.0);
861 assert(vol>0.0);
862 assert(K>=0.0);
863 types::ForDom fd = types::Domestic;
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
867 types::BarrierKIO kio2 = (kio==types::KnockIn) ? types::KnockOut
868 : types::KnockIn;
869 val += rebate*touch(S,vol,rd,rf,tau,B1,B2,fd,kio2,bcont,greek);
871 return val;
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) {
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,
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) {
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);
899 return val;
901 double 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);
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);
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);
933 return val;
936 } // namespace sca
939 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */