LCOV - code coverage report
Current view: top level - libreoffice/scaddins/source/pricing - black_scholes.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 0 402 0.0 %
Date: 2012-12-27 Functions: 0 22 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10