Pricing continuous and discrete barrier options. The payoff of the discrete down-and-out call, with m discrete monitoring points at

\[\begin{align*} 0, \frac{T}{m}, \frac{2T}{m}, ... , \frac{(m-1)T}{m}, T \end{align*}\]

is given by

\[\begin{align*} (S(T) - K)^+ I\{{\tau}^{'} > m\} , {\tau}^{'}= min\{i : S(\frac{iT}{m}) \leq H\}, H < S(0)\\ \end{align*}\]

The payoff of a continuous (i.e. m = \(\infty\)) down-and-out call option is given by

\[\begin{align*} (S(T) - K)^+ I\{{\tau} > T\} , {\tau}= inf\{t \geq 0 : S(t) \leq H\}, H < S(0)\\ \end{align*}\]


a)

It can be shown that the price of the continuous down-and-out call (DAOC) option is given by

\[\begin{align*} DAOC = BSC(S(0)) - (\frac{H}{S(0)})^{\frac{2r}{\sigma ^ 2} - 1} * BSC(\frac{H^2}{S(0)}),\\ \end{align*}\]

where \(BSC(x)\) is the Black-Scholes call price with the initial stock price being x:

\[\begin{align*} BSC(x) = x\phi(d_+) - Ke^{-rT}\phi (d_-), d_\pm = \frac{log(x/K)+(r \pm \sigma^2 / 2)T}{\sigma \sqrt{T}}\\ \end{align*}\]

Take the parameters r = 10%, \(\sigma\) = 0.30, S(0) = 100, K = 100, H = 97, T = 0.2 year. Compute the price of the continuous down-and-out call option.

I wrote the following code using C++11 and QuantLib

#include <ql/quantlib.hpp>
#include <ql/math/distributions/normaldistribution.hpp>
#include <iostream>

using namespace QuantLib;
using namespace std;

CumulativeNormalDistribution phai;

Real dplus(Real x, Real K, Real r, Real sigma, Real T){
  return (log(x / K) + (r + sigma*sigma / 2)*T) / (sigma*sqrt(T));
}

Real dminus(Real x, Real K, Real r, Real sigma, Real T){
  return (log(x / K) + (r - sigma*sigma / 2)*T) / (sigma*sqrt(T));
}

Real BSC(Real x, Real K, Real r, Real sigma, Real T){
  return x*phai(dplus(x, K, r, sigma, T)) - K*phai(dminus(x, K, r, sigma, T))*exp(-r*T);
}

Real DAOC_Continuous(Real x, Real K, Real r, Real sigma, Real T, Real H){
  return BSC(x, K, r, sigma, T) - 
    pow(H / x, (2 * r / (sigma*sigma) - 1)) * BSC(H*H / x, K, r, sigma, T);
}

int main(int aggc, char* argv[])
{
  Real x=100, K=100, r=0.1, sigma=0.3, T=0.2, H=97;
  
  Real result= DAOC_Continuous(x, K, r, sigma, T, H);
  cout << "Continuous DAOC price:" << result << endl;
  return 0;
}

After compiling, we can run it and get the result:

Continuous DAOC price:3.05956


b)

Simulate 10,000 the geometric Brownian motion sample paths. Then estimate the prices of discrete down-and-out call options for m = 5 and m = 50. Report both estimators and their standard deviations. Remark: Many people might think that the continuous price given in part (a) may be a good approximation to the case of large m, say m = 50. However, this is wrong, as is discussed in part (c).

I wrote the following code using C++11, QuantLib and TA-Lib

#include <ql/quantlib.hpp>
#include <ql/math/distributions/normaldistribution.hpp>
#include <iostream>
#include <random>
#include <cmath>
#include <vector>
#include <ta_libc.h>

using namespace QuantLib;
using namespace std;

CumulativeNormalDistribution phai;

Real dplus(Real x, Real K, Real r, Real sigma, Real T){
  return (log(x / K) + (r + sigma*sigma / 2)*T) / (sigma*sqrt(T));
}

Real dminus(Real x, Real K, Real r, Real sigma, Real T){
  return (log(x / K) + (r - sigma*sigma / 2)*T) / (sigma*sqrt(T));
}

Real BSC(Real x, Real K, Real r, Real sigma, Real T){
  return x*phai(dplus(x, K, r, sigma, T)) - K*phai(dminus(x, K, r, sigma, T))*exp(-r*T);
}

Real DAOC_Continuous(Real x, Real K, Real r, Real sigma, Real T, Real H){
  return BSC(x, K, r, sigma, T) - 
    pow(H / x, (2 * r / (sigma*sigma) - 1)) * BSC(H*H / x, K, r, sigma, T);
}

vector<Real> simulateZI(uint16_t len){
  std::random_device rd;
  std::mt19937 gen(rd());
  std::normal_distribution<> d;
  vector<Real> zi(len);
  for (uint16_t i = 0; i < len; ++i){
    zi[i] = d(gen);
  }
  return zi;
}

vector<Real> simulateGBM(Real s0, Real mu, Real sigma, Real T, uint16_t step){
  vector<Real> zi = simulateZI(step);
  vector<Real> rs(step+1);
  Real dt = T / step;
  rs[0] = s0;
  for (size_t i = 1; i < step+1; ++i){
    rs[i] = rs[i - 1] * exp((mu-sigma*sigma/2)*dt+sigma*sqrt(dt)*zi[i-1]);
  }
  return rs;
}

pair<Real,Real> DAOC_Discrete(Real x, Real K, Real r, Real sigma, Real T, Real H, uint64_t step, uint64_t simnum){
  vector<Real> ps;
  for (uint16_t i = 0; i < simnum; ++i){
    // one sample path
    vector<Real> vr1 = simulateGBM(x, r, sigma, T, step);
    bool isknockedout = false;
    for (Real s : vr1){
      if (s < H){
        isknockedout = true;
        break;
      }
    }
    if (isknockedout){
      ps.push_back(0);
      continue;
    }
    Real tmp = vr1.back() - K;
    ps.push_back(tmp > 0 ? tmp : 0);
  }
  Real cumP = accumulate(ps.begin(), ps.end(), 0.0);
  int ob, n, sz = ps.size();
  double stddev=0.0;
  TA_STDDEV(0, sz - 1, &ps[0], sz, 1, &ob, &n, &stddev);
  return pair<Real, Real>(cumP/simnum, stddev);
}


int main(int aggc, char* argv[]){
  Real x=100, K=100, r=0.1, sigma=0.3, T=0.2, H=97;
  
  Real result= DAOC_Continuous(x, K, r, sigma, T, H);
  cout << "Continuous DAOC price:" << result << endl;

  uint16_t step = 5;
  pair<Real, Real> result2 = DAOC_Discrete(x, K, r, sigma, T, H, step, 10000);
  cout << "Discrete DAOC price[step=5]:" << result2.first 
    << "\tStandard Deviation:" << result2.second << endl;

  step = 50;
  result2 = DAOC_Discrete(x, K, r, sigma, T, H, step, 10000);
  cout << "Discrete DAOC price[step=50]:" << result2.first
    << "\tStandard Deviation:" << result2.second << endl;
  return 0;
}

The result is:

Discrete DAOC price[step=5]:5.1436 Standard Deviation:9.02163

Discrete DAOC price[step=50]:3.84976 Standard Deviation:8.49334


c)

Discuss why there is a big difference between the prices of continuous vs discrete barrier options (m = 5 and even m = 50).

The reason is there is a big difference between discrete crossing and continuous crossing. Under continuous time, if the stock price crosses the barrier, it is possible that under discrete time, the stock price doesn’t cross the barrier. So the result which we get from discrete version of DAOC will be always greater than that we get from continuous version of DAOC.