Bump version to 5.0-14
[LibreOffice.git] / scaddins / source / pricing / black_scholes.cxx
blob95fa888a87ffa75cd7c45626105d5ef7d133cdf8
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 pricers
35 // - this is so we don't duplicate code when pricing barriers
36 // as this is derived from vanillas
38 // (4) single barrier options (knock-out), priced based on truncated vanillas
39 // - it follows from the reflection principle that the price W(S) of a
40 // single barrier option is given by
41 // W(S) = V(S) - (B/S)^a V(B^2/S), a = 2(rd-rf)/vol^2 - 1
42 // where V(S) is the price of the corresponding truncated vanilla
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 {
82 namespace pricing {
84 namespace bs {
87 // helper functions
89 inline double sqr(double x) {
90 return x*x;
92 // normal density (see also ScInterpreter::phi)
93 inline double dnorm(double x) {
94 //return (1.0/sqrt(2.0*M_PI))*exp(-0.5*x*x); // windows may not have M_PI
95 return 0.39894228040143268*exp(-0.5*x*x);
97 // cumulative normal distribution (see also ScInterpreter::integralPhi)
98 inline double pnorm(double x) {
99 //return 0.5*(erf(sqrt(0.5)*x)+1.0); // windows may not have erf
100 return 0.5 * ::rtl::math::erfc(-x * 0.7071067811865475);
103 // binary option cash (domestic)
104 // call - pays 1 if S_T is above strike K
105 // put - pays 1 if S_T is below strike K
106 double bincash(double S, double vol, double rd, double rf,
107 double tau, double K,
108 types::PutCall pc, types::Greeks greeks) {
109 assert(tau>=0.0);
110 assert(S>0.0);
111 assert(vol>0.0);
112 assert(K>=0.0);
114 double val=0.0;
116 if(tau<=0.0) {
117 // special case tau=0 (expiry)
118 switch(greeks) {
119 case types::Value:
120 if( (pc==types::Call && S>=K) || (pc==types::Put && S<=K) ) {
121 val = 1.0;
122 } else {
123 val = 0.0;
125 break;
126 default:
127 val = 0.0;
129 } else if(K==0.0) {
130 // special case with zero strike
131 if(pc==types::Put) {
132 // up-and-out (put) with K=0
133 val=0.0;
134 } else {
135 // down-and-out (call) with K=0 (zero coupon bond)
136 switch(greeks) {
137 case types::Value:
138 val = 1.0;
139 break;
140 case types::Theta:
141 val = rd;
142 break;
143 case types::Rho_d:
144 val = -tau;
145 break;
146 default:
147 val = 0.0;
150 } else {
151 // standard case with K>0, tau>0
152 double d1 = ( log(S/K)+(rd-rf+0.5*vol*vol)*tau ) / (vol*sqrt(tau));
153 double d2 = d1 - vol*sqrt(tau);
154 int pm = (pc==types::Call) ? 1 : -1;
156 switch(greeks) {
157 case types::Value:
158 val = pnorm(pm*d2);
159 break;
160 case types::Delta:
161 val = pm*dnorm(d2)/(S*vol*sqrt(tau));
162 break;
163 case types::Gamma:
164 val = -pm*dnorm(d2)*d1/(sqr(S*vol)*tau);
165 break;
166 case types::Theta:
167 val = rd*pnorm(pm*d2)
168 + pm*dnorm(d2)*(log(S/K)/(vol*sqrt(tau))-0.5*d2)/tau;
169 break;
170 case types::Vega:
171 val = -pm*dnorm(d2)*d1/vol;
172 break;
173 case types::Volga:
174 val = pm*dnorm(d2)/(vol*vol)*(-d1*d1*d2+d1+d2);
175 break;
176 case types::Vanna:
177 val = pm*dnorm(d2)/(S*vol*vol*sqrt(tau))*(d1*d2-1.0);
178 break;
179 case types::Rho_d:
180 val = -tau*pnorm(pm*d2) + pm*dnorm(d2)*sqrt(tau)/vol;
181 break;
182 case types::Rho_f:
183 val = -pm*dnorm(d2)*sqrt(tau)/vol;
184 break;
185 default:
186 printf("bincash: greek %d not implemented\n", greeks );
187 abort();
190 return exp(-rd*tau)*val;
193 // binary option asset (foreign)
194 // call - pays S_T if S_T is above strike K
195 // put - pays S_T if S_T is below strike K
196 double binasset(double S, double vol, double rd, double rf,
197 double tau, double K,
198 types::PutCall pc, types::Greeks greeks) {
199 assert(tau>=0.0);
200 assert(S>0.0);
201 assert(vol>0.0);
202 assert(K>=0.0);
204 double val=0.0;
205 if(tau<=0.0) {
206 // special case tau=0 (expiry)
207 switch(greeks) {
208 case types::Value:
209 if( (pc==types::Call && S>=K) || (pc==types::Put && S<=K) ) {
210 val = S;
211 } else {
212 val = 0.0;
214 break;
215 case types::Delta:
216 if( (pc==types::Call && S>=K) || (pc==types::Put && S<=K) ) {
217 val = 1.0;
218 } else {
219 val = 0.0;
221 break;
222 default:
223 val = 0.0;
225 } else if(K==0.0) {
226 // special case with zero strike (forward with zero strike)
227 if(pc==types::Put) {
228 // up-and-out (put) with K=0
229 val = 0.0;
230 } else {
231 // down-and-out (call) with K=0 (type of forward)
232 switch(greeks) {
233 case types::Value:
234 val = S;
235 break;
236 case types::Delta:
237 val = 1.0;
238 break;
239 case types::Theta:
240 val = rf*S;
241 break;
242 case types::Rho_f:
243 val = -tau*S;
244 break;
245 default:
246 val = 0.0;
249 } else {
250 // normal case
251 double d1 = ( log(S/K)+(rd-rf+0.5*vol*vol)*tau ) / (vol*sqrt(tau));
252 double d2 = d1 - vol*sqrt(tau);
253 int pm = (pc==types::Call) ? 1 : -1;
255 switch(greeks) {
256 case types::Value:
257 val = S*pnorm(pm*d1);
258 break;
259 case types::Delta:
260 val = pnorm(pm*d1) + pm*dnorm(d1)/(vol*sqrt(tau));
261 break;
262 case types::Gamma:
263 val = -pm*dnorm(d1)*d2/(S*sqr(vol)*tau);
264 break;
265 case types::Theta:
266 val = rf*S*pnorm(pm*d1)
267 + pm*S*dnorm(d1)*(log(S/K)/(vol*sqrt(tau))-0.5*d1)/tau;
268 break;
269 case types::Vega:
270 val = -pm*S*dnorm(d1)*d2/vol;
271 break;
272 case types::Volga:
273 val = pm*S*dnorm(d1)/(vol*vol)*(-d1*d2*d2+d1+d2);
274 break;
275 case types::Vanna:
276 val = pm*dnorm(d1)/(vol*vol*sqrt(tau))*(d2*d2-1.0);
277 break;
278 case types::Rho_d:
279 val = pm*S*dnorm(d1)*sqrt(tau)/vol;
280 break;
281 case types::Rho_f:
282 val = -tau*S*pnorm(pm*d1) - pm*S*dnorm(d1)*sqrt(tau)/vol;
283 break;
284 default:
285 printf("binasset: greek %d not implemented\n", greeks );
286 abort();
289 return exp(-rf*tau)*val;
292 // just for convenience we can combine bincash and binasset into
293 // one function binary
294 // using bincash() if fd==types::Domestic
295 // using binasset() if fd==types::Foreign
296 double binary(double S, double vol, double rd, double rf,
297 double tau, double K,
298 types::PutCall pc, types::ForDom fd,
299 types::Greeks greek) {
300 double val=0.0;
301 switch(fd) {
302 case types::Domestic:
303 val = bincash(S,vol,rd,rf,tau,K,pc,greek);
304 break;
305 case types::Foreign:
306 val = binasset(S,vol,rd,rf,tau,K,pc,greek);
307 break;
308 default:
309 // never get here
310 assert(false);
312 return val;
315 // further wrapper to combine single/double barrier binary options
316 // into one function
317 // B1<=0 - it is assumed lower barrier not set
318 // B2<=0 - it is assumed upper barrier not set
319 double binary(double S, double vol, double rd, double rf,
320 double tau, double B1, double B2,
321 types::ForDom fd, types::Greeks greek) {
322 assert(tau>=0.0);
323 assert(S>0.0);
324 assert(vol>0.0);
326 double val=0.0;
328 if(B1<=0.0 && B2<=0.0) {
329 // no barriers set, payoff 1.0 (domestic) or S_T (foreign)
330 val = binary(S,vol,rd,rf,tau,0.0,types::Call,fd,greek);
331 } else if(B1<=0.0 && B2>0.0) {
332 // upper barrier (put)
333 val = binary(S,vol,rd,rf,tau,B2,types::Put,fd,greek);
334 } else if(B1>0.0 && B2<=0.0) {
335 // lower barrier (call)
336 val = binary(S,vol,rd,rf,tau,B1,types::Call,fd,greek);
337 } else if(B1>0.0 && B2>0.0) {
338 // double barrier
339 if(B2<=B1) {
340 val = 0.0;
341 } else {
342 val = binary(S,vol,rd,rf,tau,B2,types::Put,fd,greek)
343 - binary(S,vol,rd,rf,tau,B1,types::Put,fd,greek);
345 } else {
346 // never get here
347 assert(false);
350 return val;
353 // vanilla put/call option
354 // call pays (S_T-K)^+
355 // put pays (K-S_T)^+
356 // this is the same as: +/- (binasset - K*bincash)
357 double putcall(double S, double vol, double rd, double rf,
358 double tau, double K,
359 types::PutCall putcall, types::Greeks greeks) {
361 assert(tau>=0.0);
362 assert(S>0.0);
363 assert(vol>0.0);
364 assert(K>=0.0);
366 double val = 0.0;
367 int pm = (putcall==types::Call) ? 1 : -1;
369 if(K==0 || tau==0.0) {
370 // special cases, simply refer to binasset() and bincash()
371 val = pm * ( binasset(S,vol,rd,rf,tau,K,putcall,greeks)
372 - K*bincash(S,vol,rd,rf,tau,K,putcall,greeks) );
373 } else {
374 // general case
375 // we could just use pm*(binasset-K*bincash), however
376 // since the formula for delta and gamma simplify we write them
377 // down here
378 double d1 = ( log(S/K)+(rd-rf+0.5*vol*vol)*tau ) / (vol*sqrt(tau));
379 double d2 = d1 - vol*sqrt(tau);
381 switch(greeks) {
382 case types::Value:
383 val = pm * ( exp(-rf*tau)*S*pnorm(pm*d1)-exp(-rd*tau)*K*pnorm(pm*d2) );
384 break;
385 case types::Delta:
386 val = pm*exp(-rf*tau)*pnorm(pm*d1);
387 break;
388 case types::Gamma:
389 val = exp(-rf*tau)*dnorm(d1)/(S*vol*sqrt(tau));
390 break;
391 default:
392 // too lazy for the other greeks, so simply refer to binasset/bincash
393 val = pm * ( binasset(S,vol,rd,rf,tau,K,putcall,greeks)
394 - K*bincash(S,vol,rd,rf,tau,K,putcall,greeks) );
397 return val;
400 // truncated put/call option, single barrier
401 // need to specify whether it's down-and-out or up-and-out
402 // regular (keeps monotonicity): down-and-out for call, up-and-out for put
403 // reverse (destroys monoton): up-and-out for call, down-and-out for put
404 // call pays (S_T-K)^+
405 // put pays (K-S_T)^+
406 double putcalltrunc(double S, double vol, double rd, double rf,
407 double tau, double K, double B,
408 types::PutCall pc, types::KOType kotype,
409 types::Greeks greeks) {
411 assert(tau>=0.0);
412 assert(S>0.0);
413 assert(vol>0.0);
414 assert(K>=0.0);
415 assert(B>=0.0);
417 int pm = (pc==types::Call) ? 1 : -1;
418 double val = 0.0;
420 switch(kotype) {
421 case types::Regular:
422 if( (pc==types::Call && B<=K) || (pc==types::Put && B>=K) ) {
423 // option degenerates to standard plain vanilla call/put
424 val = putcall(S,vol,rd,rf,tau,K,pc,greeks);
425 } else {
426 // normal case with truncation
427 val = pm * ( binasset(S,vol,rd,rf,tau,B,pc,greeks)
428 - K*bincash(S,vol,rd,rf,tau,B,pc,greeks) );
430 break;
431 case types::Reverse:
432 if( (pc==types::Call && B<=K) || (pc==types::Put && B>=K) ) {
433 // option degenerates to zero payoff
434 val = 0.0;
435 } else {
436 // normal case with truncation
437 val = binasset(S,vol,rd,rf,tau,K,types::Call,greeks)
438 - binasset(S,vol,rd,rf,tau,B,types::Call,greeks)
439 - K * ( bincash(S,vol,rd,rf,tau,K,types::Call,greeks)
440 - bincash(S,vol,rd,rf,tau,B,types::Call,greeks) );
442 break;
443 default:
444 assert(false);
446 return val;
449 // wrapper function for put/call option which combines
450 // double/single/no truncation barrier
451 // B1<=0 - assume no lower barrier
452 // B2<=0 - assume no upper barrier
453 double putcalltrunc(double S, double vol, double rd, double rf,
454 double tau, double K, double B1, double B2,
455 types::PutCall pc, types::Greeks greek) {
457 assert(tau>=0.0);
458 assert(S>0.0);
459 assert(vol>0.0);
460 assert(K>=0.0);
462 double val=0.0;
464 if(B1<=0.0 && B2<=0.0) {
465 // no barriers set, plain vanilla
466 val = putcall(S,vol,rd,rf,tau,K,pc,greek);
467 } else if(B1<=0.0 && B2>0.0) {
468 // upper barrier: reverse barrier for call, regular barrier for put
469 if(pc==types::Call) {
470 val = putcalltrunc(S,vol,rd,rf,tau,K,B2,pc,types::Reverse,greek);
471 } else {
472 val = putcalltrunc(S,vol,rd,rf,tau,K,B2,pc,types::Regular,greek);
474 } else if(B1>0.0 && B2<=0.0) {
475 // lower barrier: regular barrier for call, reverse barrier for put
476 if(pc==types::Call) {
477 val = putcalltrunc(S,vol,rd,rf,tau,K,B1,pc,types::Regular,greek);
478 } else {
479 val = putcalltrunc(S,vol,rd,rf,tau,K,B1,pc,types::Reverse,greek);
481 } else if(B1>0.0 && B2>0.0) {
482 // double barrier
483 if(B2<=B1) {
484 val = 0.0;
485 } else {
486 int pm = (pc==types::Call) ? 1 : -1;
487 val = pm * (
488 putcalltrunc(S,vol,rd,rf,tau,K,B1,pc,types::Regular,greek)
489 - putcalltrunc(S,vol,rd,rf,tau,K,B2,pc,types::Regular,greek)
492 } else {
493 // never get here
494 assert(false);
496 return val;
499 namespace internal {
501 // wrapper function for all non-path dependent options
502 // this is only an internal function, used to avoid code duplication when
503 // going to path-dependent barrier options,
504 // K<0 - assume binary option
505 // K>=0 - assume put/call option
506 double vanilla(double S, double vol, double rd, double rf,
507 double tau, double K, double B1, double B2,
508 types::PutCall pc, types::ForDom fd,
509 types::Greeks greek) {
510 double val = 0.0;
511 if(K<0.0) {
512 // binary option if K<0
513 val = binary(S,vol,rd,rf,tau,B1,B2,fd,greek);
514 } else {
515 val = putcall(S,vol,rd,rf,tau,K,pc,greek);
517 return val;
519 double vanilla_trunc(double S, double vol, double rd, double rf,
520 double tau, double K, double B1, double B2,
521 types::PutCall pc, types::ForDom fd,
522 types::Greeks greek) {
523 double val = 0.0;
524 if(K<0.0) {
525 // binary option if K<0
526 // truncated is actually the same as the vanilla binary
527 val = binary(S,vol,rd,rf,tau,B1,B2,fd,greek);
528 } else {
529 val = putcalltrunc(S,vol,rd,rf,tau,K,B1,B2,pc,greek);
531 return val;
534 } // namespace internal
536 // path dependent options
539 namespace internal {
541 // helper term for any type of options with continuously monitored barriers,
542 // internal, should not be called from outside
543 // calculates value and greeks based on
544 // V(S) = V1(sc*S) - (B/S)^a V1(sc*B^2/S)
545 // (a=2 mu/vol^2, mu drift in logspace, ie. mu=(rd-rf-1/2vol^2))
546 // with sc=1 and V1() being the price of the respective truncated
547 // vanilla option, V() would be the price of the respective barrier
548 // option if only one barrier is present
549 double barrier_term(double S, double vol, double rd, double rf,
550 double tau, double K, double B1, double B2, double sc,
551 types::PutCall pc, types::ForDom fd,
552 types::Greeks greek) {
554 assert(tau>=0.0);
555 assert(S>0.0);
556 assert(vol>0.0);
558 // V(S) = V1(sc*S) - (B/S)^a V1(sc*B^2/S)
559 double val = 0.0;
560 double B = (B1>0.0) ? B1 : B2;
561 double a = 2.0*(rd-rf)/(vol*vol)-1.0; // helper variable
562 double b = 4.0*(rd-rf)/(vol*vol*vol); // helper variable -da/dvol
563 double c = 12.0*(rd-rf)/(vol*vol*vol*vol); // helper -db/dvol
564 switch(greek) {
565 case types::Value:
566 val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
567 - pow(B/S,a)*
568 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
569 break;
570 case types::Delta:
571 val = sc*vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
572 + pow(B/S,a) * (
573 a/S*
574 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
575 + sqr(B/S)*sc*
576 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
578 break;
579 case types::Gamma:
580 val = sc*sc*vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
581 - pow(B/S,a) * (
582 a*(a+1.0)/(S*S)*
583 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
584 + (2.0*a+2.0)*B*B/(S*S*S)*sc*
585 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Delta)
586 + sqr(sqr(B/S))*sc*sc*
587 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Gamma)
589 break;
590 case types::Theta:
591 val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
592 - pow(B/S,a)*
593 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
594 break;
595 case types::Vega:
596 val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
597 - pow(B/S,a) * (
598 - b*log(B/S)*
599 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
600 + 1.0*
601 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
603 break;
604 case types::Volga:
605 val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
606 - pow(B/S,a) * (
607 log(B/S)*(b*b*log(B/S)+c)*
608 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
609 - 2.0*b*log(B/S)*
610 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Vega)
611 + 1.0*
612 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Volga)
614 break;
615 case types::Vanna:
616 val = sc*vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
617 - pow(B/S,a) * (
618 b/S*(log(B/S)*a+1.0)*
619 vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
620 + b*log(B/S)*sqr(B/S)*sc*
621 vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Delta)
622 - a/S*
623 vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Vega)
624 - sqr(B/S)*sc*
625 vanilla_trunc(B*B/S*sc,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Vanna)
627 break;
628 case types::Rho_d:
629 val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
630 - pow(B/S,a) * (
631 2.0*log(B/S)/(vol*vol)*
632 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
633 + 1.0*
634 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
636 break;
637 case types::Rho_f:
638 val = vanilla_trunc(sc*S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
639 - pow(B/S,a) * (
640 - 2.0*log(B/S)/(vol*vol)*
641 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,types::Value)
642 + 1.0*
643 vanilla_trunc(sc*B*B/S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
645 break;
646 default:
647 printf("barrier_term: greek %d not implemented\n", greek );
648 abort();
650 return val;
653 // one term of the infinite sum for the valuation of double barriers
654 double barrier_double_term( double S, double vol, double rd, double rf,
655 double tau, double K, double B1, double B2,
656 double fac, double sc, int i,
657 types::PutCall pc, types::ForDom fd, types::Greeks greek) {
659 double val = 0.0;
660 double b = 4.0*i*(rd-rf)/(vol*vol*vol); // helper variable -da/dvol
661 double c = 12.0*i*(rd-rf)/(vol*vol*vol*vol); // helper -db/dvol
662 switch(greek) {
663 case types::Value:
664 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek);
665 break;
666 case types::Delta:
667 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek);
668 break;
669 case types::Gamma:
670 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek);
671 break;
672 case types::Theta:
673 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek);
674 break;
675 case types::Vega:
676 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek)
677 - b*log(B2/B1)*fac *
678 barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Value);
679 break;
680 case types::Volga:
681 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek)
682 - 2.0*b*log(B2/B1)*fac *
683 barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Vega)
684 + log(B2/B1)*fac*(c+b*b*log(B2/B1)) *
685 barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Value);
686 break;
687 case types::Vanna:
688 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek)
689 - b*log(B2/B1)*fac *
690 barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Delta);
691 break;
692 case types::Rho_d:
693 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek)
694 + 2.0*i/(vol*vol)*log(B2/B1)*fac *
695 barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Value);
696 break;
697 case types::Rho_f:
698 val = fac*barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,greek)
699 - 2.0*i/(vol*vol)*log(B2/B1)*fac *
700 barrier_term(S,vol,rd,rf,tau,K,B1,B2,sc,pc,fd,types::Value);
701 break;
702 default:
703 printf("barrier_double_term: greek %d not implemented\n", greek );
704 abort();
706 return val;
709 // general pricer for any type of options with continuously monitored barriers
710 // allows two, one or zero barriers, only knock-out style
711 // payoff profiles allowed based on vanilla_trunc()
712 double barrier_ko(double S, double vol, double rd, double rf,
713 double tau, double K, double B1, double B2,
714 types::PutCall pc, types::ForDom fd,
715 types::Greeks greek) {
717 assert(tau>=0.0);
718 assert(S>0.0);
719 assert(vol>0.0);
721 double val = 0.0;
723 if(B1<=0.0 && B2<=0.0) {
724 // no barriers --> vanilla case
725 val = vanilla(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
726 } else if(B1>0.0 && B2<=0.0) {
727 // lower barrier
728 if(S<=B1) {
729 val = 0.0; // knocked out
730 } else {
731 val = barrier_term(S,vol,rd,rf,tau,K,B1,B2,1.0,pc,fd,greek);
733 } else if(B1<=0.0 && B2>0.0) {
734 // upper barrier
735 if(S>=B2) {
736 val = 0.0; // knocked out
737 } else {
738 val = barrier_term(S,vol,rd,rf,tau,K,B1,B2,1.0,pc,fd,greek);
740 } else if(B1>0.0 && B2>0.0) {
741 // double barrier
742 if(S<=B1 || S>=B2) {
743 val = 0.0; // knocked out (always true if wrong input B1>B2)
744 } else {
745 // more complex calculation as we have to evaluate an infinite
746 // sum
747 // to reduce very costly pow() calls we define some variables
748 double a = 2.0*(rd-rf)/(vol*vol)-1.0; // 2 (mu-1/2vol^2)/sigma^2
749 double BB2=sqr(B2/B1);
750 double BBa=pow(B2/B1,a);
751 double BB2inv=1.0/BB2;
752 double BBainv=1.0/BBa;
753 double fac=1.0;
754 double facinv=1.0;
755 double sc=1.0;
756 double scinv=1.0;
758 // initial term i=0
759 val=barrier_double_term(S,vol,rd,rf,tau,K,B1,B2,fac,sc,0,pc,fd,greek);
760 // infinite loop, 10 should be plenty, normal would be 2
761 for(int i=1; i<10; i++) {
762 fac*=BBa;
763 facinv*=BBainv;
764 sc*=BB2;
765 scinv*=BB2inv;
766 double add =
767 barrier_double_term(S,vol,rd,rf,tau,K,B1,B2,fac,sc,i,pc,fd,greek) +
768 barrier_double_term(S,vol,rd,rf,tau,K,B1,B2,facinv,scinv,-i,pc,fd,greek);
769 val += add;
770 //printf("%i: val=%e (add=%e)\n",i,val,add);
771 if(fabs(add) <= 1e-12*fabs(val)) {
772 break;
775 // not knocked-out double barrier end
777 // double barrier end
778 } else {
779 // no such barrier combination exists
780 assert(false);
783 return val;
786 // knock-in style barrier
787 double barrier_ki(double S, double vol, double rd, double rf,
788 double tau, double K, double B1, double B2,
789 types::PutCall pc, types::ForDom fd,
790 types::Greeks greek) {
791 return vanilla(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
792 -barrier_ko(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
795 // general barrier
796 double barrier(double S, double vol, double rd, double rf,
797 double tau, double K, double B1, double B2,
798 types::PutCall pc, types::ForDom fd,
799 types::BarrierKIO kio, types::BarrierActive bcont,
800 types::Greeks greek) {
802 double val = 0.0;
803 if( kio==types::KnockOut && bcont==types::Maturity ) {
804 // truncated vanilla option
805 val = vanilla_trunc(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
806 } else if ( kio==types::KnockOut && bcont==types::Continuous ) {
807 // standard knock-out barrier
808 val = barrier_ko(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
809 } else if ( kio==types::KnockIn && bcont==types::Maturity ) {
810 // inverse truncated vanilla
811 val = vanilla(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek)
812 - vanilla_trunc(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
813 } else if ( kio==types::KnockIn && bcont==types::Continuous ) {
814 // standard knock-in barrier
815 val = barrier_ki(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
816 } else {
817 // never get here
818 assert(false);
820 return val;
823 } // namespace internal
826 // touch/no-touch options (cash/asset or nothing payoff profile)
827 double touch(double S, double vol, double rd, double rf,
828 double tau, double B1, double B2, types::ForDom fd,
829 types::BarrierKIO kio, types::BarrierActive bcont,
830 types::Greeks greek) {
832 double K=-1.0; // dummy
833 types::PutCall pc = types::Call; // dummy
834 double val = 0.0;
835 if( kio==types::KnockOut && bcont==types::Maturity ) {
836 // truncated vanilla option
837 val = internal::vanilla_trunc(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
838 } else if ( kio==types::KnockOut && bcont==types::Continuous ) {
839 // standard knock-out barrier
840 val = internal::barrier_ko(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
841 } else if ( kio==types::KnockIn && bcont==types::Maturity ) {
842 // inverse truncated vanilla
843 val = internal::vanilla(S,vol,rd,rf,tau,K,-1.0,-1.0,pc,fd,greek)
844 - internal::vanilla_trunc(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
845 } else if ( kio==types::KnockIn && bcont==types::Continuous ) {
846 // standard knock-in barrier
847 val = internal::vanilla(S,vol,rd,rf,tau,K,-1.0,-1.0,pc,fd,greek)
848 - internal::barrier_ko(S,vol,rd,rf,tau,K,B1,B2,pc,fd,greek);
849 } else {
850 // never get here
851 assert(false);
853 return val;
856 // barrier option (put/call payoff profile)
857 double barrier(double S, double vol, double rd, double rf,
858 double tau, double K, double B1, double B2,
859 double rebate,
860 types::PutCall pc, types::BarrierKIO kio,
861 types::BarrierActive bcont,
862 types::Greeks greek) {
863 assert(tau>=0.0);
864 assert(S>0.0);
865 assert(vol>0.0);
866 assert(K>=0.0);
867 types::ForDom fd = types::Domestic;
868 double val=internal::barrier(S,vol,rd,rf,tau,K,B1,B2,pc,fd,kio,bcont,greek);
869 if(rebate!=0.0) {
870 // opposite of barrier knock-in/out type
871 types::BarrierKIO kio2 = (kio==types::KnockIn) ? types::KnockOut
872 : types::KnockIn;
873 val += rebate*touch(S,vol,rd,rf,tau,B1,B2,fd,kio2,bcont,greek);
875 return val;
878 // probability of hitting a barrier
879 // this is almost the same as the price of a touch option (domestic)
880 // as it pays one if a barrier is hit; we only have to offset the
881 // discounting and we get the probability
882 double prob_hit(double S, double vol, double mu,
883 double tau, double B1, double B2) {
884 double rd=0.0;
885 double rf=-mu;
886 return 1.0 - touch(S,vol,rd,rf,tau,B1,B2,types::Domestic,types::KnockOut,
887 types::Continuous, types::Value);
890 // probability of being in-the-money, ie payoff is greater zero,
891 // assuming payoff(S_T) > 0 iff S_T in [B1, B2]
892 // this the same as the price of a cash or nothing option
893 // with no discounting
894 double prob_in_money(double S, double vol, double mu,
895 double tau, double B1, double B2) {
896 assert(S>0.0);
897 assert(vol>0.0);
898 assert(tau>=0.0);
899 double val = 0.0;
900 if( B1<B2 || B1<=0.0 || B2<=0.0 ) {
901 val = binary(S,vol,0.0,-mu,tau,B1,B2,types::Domestic,types::Value);
903 return val;
905 double prob_in_money(double S, double vol, double mu,
906 double tau, double K, double B1, double B2,
907 types::PutCall pc) {
908 assert(S>0.0);
909 assert(vol>0.0);
910 assert(tau>=0.0);
912 // if K<0 we assume a binary option is given
913 if(K<0.0) {
914 return prob_in_money(S,vol,mu,tau,B1,B2);
917 double val = 0.0;
918 double BM1, BM2; // range of in the money [BM1, BM2]
919 // non-sense parameters with no positive payoff
920 if( (B1>B2 && B1>0.0 && B2>0.0) ||
921 (K>=B2 && B2>0.0 && pc==types::Call) ||
922 (K<=B1 && pc==types::Put) ) {
923 val = 0.0;
924 // need to figure out between what barriers payoff is greater 0
925 } else if(pc==types::Call) {
926 BM1=std::max(B1, K);
927 BM2=B2;
928 val = prob_in_money(S,vol,mu,tau,BM1,BM2);
929 } else if (pc==types::Put) {
930 BM1=B1;
931 BM2= (B2>0.0) ? std::min(B2,K) : K;
932 val = prob_in_money(S,vol,mu,tau,BM1,BM2);
933 } else {
934 // don't get here
935 assert(false);
937 return val;
940 } // namespace bs
942 } // namespace pricing
943 } // namespace sca
946 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */