tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / scaddins / source / pricing / black_scholes.cxx
blobaa417f21af2d5f06a358122f58e475966d60b83e
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 case types::Theta:
563 val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
564 - pow(B/S,a)*
565 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
566 break;
567 case types::Delta:
568 val = sc*vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
569 + pow(B/S,a) * (
570 a/S*
571 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
572 + sqr(B/S)*sc*
573 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
575 break;
576 case types::Gamma:
577 val = sc*sc*vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
578 - pow(B/S,a) * (
579 a*(a+1.0)/(S*S)*
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)
586 break;
587 case types::Vega:
588 val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
589 - pow(B/S,a) * (
590 - b*log(B/S)*
591 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
592 + 1.0*
593 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
595 break;
596 case types::Volga:
597 val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
598 - pow(B/S,a) * (
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)
601 - 2.0*b*log(B/S)*
602 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Vega)
603 + 1.0*
604 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Volga)
606 break;
607 case types::Vanna:
608 val = sc*vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
609 - pow(B/S,a) * (
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)
614 - a/S*
615 vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Vega)
616 - sqr(B/S)*sc*
617 vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Vanna)
619 break;
620 case types::Rho_d:
621 val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
622 - pow(B/S,a) * (
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)
625 + 1.0*
626 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
628 break;
629 case types::Rho_f:
630 val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
631 - pow(B/S,a) * (
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)
634 + 1.0*
635 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
637 break;
638 default:
639 printf("barrier_term: greek %d not implemented\n", greek );
640 abort();
642 return val;
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) {
651 double val = 0.0;
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
654 switch(greek) {
655 case types::Value:
656 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek);
657 break;
658 case types::Delta:
659 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek);
660 break;
661 case types::Gamma:
662 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek);
663 break;
664 case types::Theta:
665 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek);
666 break;
667 case types::Vega:
668 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek)
669 - b*log(B2/B1)*fac *
670 barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Value);
671 break;
672 case types::Volga:
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);
678 break;
679 case types::Vanna:
680 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek)
681 - b*log(B2/B1)*fac *
682 barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Delta);
683 break;
684 case types::Rho_d:
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);
688 break;
689 case types::Rho_f:
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);
693 break;
694 default:
695 printf("barrier_double_term: greek %d not implemented\n", greek );
696 abort();
698 return val;
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) {
709 assert(tau>=0.0);
710 assert(S>0.0);
711 assert(vol>0.0);
713 double val = 0.0;
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) {
719 // lower barrier
720 if(S<=B1) {
721 val = 0.0; // knocked out
722 } else {
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) {
726 // upper barrier
727 if(S>=B2) {
728 val = 0.0; // knocked out
729 } else {
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) {
733 // double barrier
734 if(S<=B1 || S>=B2) {
735 val = 0.0; // knocked out (always true if wrong input B1>B2)
736 } else {
737 // more complex calculation as we have to evaluate an infinite
738 // sum
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;
745 double fac=1.0;
746 double facinv=1.0;
747 double sc=1.0;
748 double scinv=1.0;
750 // initial term i=0
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++) {
754 fac*=BBa;
755 facinv*=BBainv;
756 sc*=BB2;
757 scinv*=BB2inv;
758 double add =
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);
761 val += add;
762 //printf("%i: val=%e (add=%e)\n",i,val,add);
763 if(fabs(add) <= 1e-12*fabs(val)) {
764 break;
767 // not knocked-out double barrier end
769 // double barrier end
770 } else {
771 // no such barrier combination exists
772 assert(false);
775 return val;
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);
787 // general barrier
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) {
794 double val = 0.0;
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);
808 } else {
809 // never get here
810 assert(false);
812 return val;
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
826 double val = 0.0;
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);
841 } else {
842 // never get here
843 assert(false);
845 return val;
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,
851 double rebate,
852 types::PutCall pc, types::BarrierKIO kio,
853 types::BarrierActive bcont,
854 types::Greeks greek) {
855 assert(tau>=0.0);
856 assert(S>0.0);
857 assert(vol>0.0);
858 assert(K>=0.0);
859 types::ForDom fd = types::Domestic;
860 double val=internal::barrier(S,vol,rd,rf,tau,K,B1,B2,pc,fd,kio,bcont,greek);
861 if(rebate!=0.0) {
862 // opposite of barrier knock-in/out type
863 types::BarrierKIO kio2 = (kio==types::KnockIn) ? types::KnockOut
864 : types::KnockIn;
865 val += rebate*touch(S,vol,rd,rf,tau,B1,B2,fd,kio2,bcont,greek);
867 return val;
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) {
876 double const rd=0.0;
877 double rf=-mu;
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) {
888 assert(S>0.0);
889 assert(vol>0.0);
890 assert(tau>=0.0);
891 double val = 0.0;
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);
895 return val;
897 double prob_in_money(double S, double vol, double mu,
898 double tau, double K, double B1, double B2,
899 types::PutCall pc) {
900 assert(S>0.0);
901 assert(vol>0.0);
902 assert(tau>=0.0);
904 // if K<0 we assume a binary option is given
905 if(K<0.0) {
906 return prob_in_money(S,vol,mu,tau,B1,B2);
909 double val = 0.0;
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) ) {
915 val = 0.0;
916 // need to figure out between what barriers payoff is greater 0
917 } else if(pc==types::Call) {
918 BM1=std::max(B1, K);
919 BM2=B2;
920 val = prob_in_money(S,vol,mu,tau,BM1,BM2);
921 } else if (pc==types::Put) {
922 BM1=B1;
923 BM2= (B2>0.0) ? std::min(B2,K) : K;
924 val = prob_in_money(S,vol,mu,tau,BM1,BM2);
925 } else {
926 // don't get here
927 assert(false);
929 return val;
932 } // namespace sca
935 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */