Pricing the Double-no-touch option

A Double-no-touch (DNT) option is a binary option that pays a fixed amount of cash at expiry. Unfortunately, the fExoticOptions package does not contain a formula for this option at present. We will show two different ways to price DNTs that incorporate two different pricing approaches. In this section, we will call the function dnt1, and for the second approach, we will use dnt2 as the name for the function.

Hui (1996) showed how a one-touch double barrier binary option can be priced. In his terminology, "one-touch" means that a single trade is enough to trigger the knock-out event, and "double barrier" binary means that there are two barriers and this is a binary option. We call this DNT as it is commonly used on the FX markets. This is a good example for the fact that many popular exotic options are running under more than one name. In Haug (2007a), the Hui-formula is already translated into the generalized framework. S, r, b, σ, and T have the same meaning as in Chapter 5, FX Derivatives. K means the payout (dollar amount) while L and U are the lower and upper barriers.

Pricing the Double-no-touch option

Where, Pricing the Double-no-touch option, Pricing the Double-no-touch option, Pricing the Double-no-touch option.

Implementing the Hui (1996) function to R starts with a big question mark: what should we do with an infinite sum? How high a number should we substitute as infinity? Interestingly, for practical purposes, small number like 5 or 10 could often play the role of infinity rather well. Hui (1996) states that convergence is fast most of the time. We are a bit skeptical about this since α will be used as an exponent. If b is negative and sigma is small enough, the (S/L)α part in the formula could turn out to be a problem.

First, we will try with normal parameters and see how quick the convergence is:

dnt1 <- function(S, K, U, L, sigma, T, r, b, N = 20, ploterror = FALSE){
    if ( L > S | S > U) return(0)
    Z <- log(U/L)
    alpha <- -1/2*(2*b/sigma^2 - 1)
    beta <- -1/4*(2*b/sigma^2 - 1)^2 - 2*r/sigma^2
    v <- rep(0, N)
    for (i in 1:N)
        v[i] <- 2*pi*i*K/(Z^2) * (((S/L)^alpha - (-1)^i*(S/U)^alpha ) /
            (alpha^2+(i*pi/Z)^2)) * sin(i*pi/Z*log(S/L)) *
             exp(-1/2 * ((i*pi/Z)^2-beta) * sigma^2*T)
    if (ploterror) barplot(v, main = "Formula Error");
    sum(v)
}
print(dnt1(100, 10, 120, 80, 0.1, 0.25, 0.05, 0.03, 20, TRUE))

The following screenshot shows the result of the preceding code:

Pricing the Double-no-touch option

The Formula Error chart shows that after the seventh step, additional steps were not influencing the result. This means that for practical purposes, the infinite sum can be quickly estimated by calculating only the first seven steps. This looks like a very quick convergence indeed. However, this could be pure luck or coincidence.

What about decreasing the volatility down to 3 percent? We have to set N as 50 to see the convergence:

print(dnt1(100, 10, 120, 80, 0.03, 0.25, 0.05, 0.03, 50, TRUE))

The preceding command gives the following output:

Pricing the Double-no-touch option

Not so impressive? 50 steps are still not that bad. What about decreasing the volatility even lower? At 1 percent, the formula with these parameters simply blows up. First, this looks catastrophic; however, the price of a DNT was already 98.75 percent of the payout when we used 3 percent volatility. Logic says that the DNT price should be a monotone-decreasing function of volatility, so we already know that the price of the DNT should be worth at least 98.75 percent if volatility is below 3 percent.

Another issue is that if we choose an extreme high U or extreme low L, calculation errors emerge. However, similar to the problem with volatility, common sense helps here too; the price of a DNT should increase if we make U higher or L lower.

There is still another trick. Since all the problem comes from the α parameter, we can try setting b as 0, which will make α equal to 0.5. If we also set r to 0, the price of a DNT converges into 100 percent as the volatility drops.

Anyway, whenever we substitute an infinite sum by a finite sum, it is always good to know when it will work and when it will not. We made a new code that takes into consideration that convergence is not always quick. The trick is that the function calculates the next step as long as the last step made any significant change. This is still not good for all the parameters as there is no cure for very low volatility, except that we accept the fact that if implied volatilities are below 1 percent, than this is an extreme market situation in which case DNT options should not be priced by this formula:

dnt1 <- function(S, K, U, L, sigma, Time, r, b) {
  if ( L > S | S > U) return(0)
  Z <- log(U/L)
  alpha <- -1/2*(2*b/sigma^2 - 1)
  beta <- -1/4*(2*b/sigma^2 - 1)^2 - 2*r/sigma^2
  p <- 0
  i <- a <- 1
  while (abs(a) > 0.0001){
    a <- 2*pi*i*K/(Z^2) * (((S/L)^alpha - (-1)^i*(S/U)^alpha ) / (alpha^2 + (i *pi / Z)^2) ) * sin(i * pi / Z * log(S/L)) * exp(-1/2*((i*pi/Z)^2-beta) * sigma^2 * Time)
    p <- p + a
    i <- i + 1
  }
  p
}

Now that we have a nice formula, it is possible to draw some DNT-related charts to get more familiar with this option. Later, we will use a particular AUDUSD DNT option with the following parameters: L equal to 0.9200, U equal to 0.9600, K (payout) equal to USD 1 million, T equal to 0.25 years, volatility equal to 6 percent, r_AUD equal to 2.75 percent, r_USD equal to 0.25 percent, and b equal to -2.5 percent. We will calculate and plot all the possible values of this DNT from 0.9200 to 0.9600; each step will be one pip (0.0001), so we will use 2,000 steps.

The following code plots a graph of price of underlying:

x <- seq(0.92, 0.96, length = 2000)
y <- z <- rep(0, 2000)
for (i in 1:2000){
    y[i] <- dnt1(x[i], 1e6, 0.96, 0.92, 0.06, 0.25, 0.0025, -0.0250)
    z[i] <- dnt1(x[i], 1e6, 0.96, 0.92, 0.065, 0.25, 0.0025, -0.0250)
}
matplot(x, cbind(y,z), type = "l", lwd = 2, lty = 1,
    main = "Price of a DNT with volatility 6% and 6.5%
", cex.main = 0.8, xlab = "Price of underlying" )

The following output is the result of the preceding code:

Pricing the Double-no-touch option

It can be clearly seen that even a small change in volatility can have a huge impact on the price of a DNT. Looking at this chart is an intuitive way to find that vega must be negative. Interestingly enough even just taking a quick look at this chart can convince us that the absolute value of vega is decreasing if we are getting closer to the barriers.

Most end users think that the biggest risk is when the spot is getting close to the trigger. This is because end users really think about binary options in a binary way. As long as the DNT is alive, they focus on the positive outcome. However, for a dynamic hedger, the risk of a DNT is not that interesting when the value of the DNT is already small.

It is also very interesting that since the T-Bill price is independent of the volatility and since the DNT + DOT = T-Bill equation holds, an increasing volatility will decrease the price of the DNT by the exact same amount just like it will increase the price of the DOT. It is not surprising that the vega of the DOT should be the exact mirror of the DNT.

We can use the GetGreeks function to estimate vega, gamma, delta, and theta. For gamma we can use the GetGreeks function in the following way:

GetGreeks <- function(FUN, arg, epsilon,...) {
    all_args1 <- all_args2 <- list(...)
    all_args1[[arg]] <- as.numeric(all_args1[[arg]] + epsilon)
    all_args2[[arg]] <- as.numeric(all_args2[[arg]] - epsilon)
    (do.call(FUN, all_args1) -
        do.call(FUN, all_args2)) / (2 * epsilon)
}
Gamma <- function(FUN, epsilon, S, ...) {
    arg1 <- list(S, ...)
    arg2 <- list(S + 2 * epsilon, ...)
    arg3 <- list(S - 2 * epsilon, ...)
    y1 <- (do.call(FUN, arg2) - do.call(FUN, arg1)) / (2 * epsilon)
    y2 <- (do.call(FUN, arg1) - do.call(FUN, arg3)) / (2 * epsilon)
    (y1 - y2) / (2 * epsilon)
}
x = seq(0.9202, 0.9598, length = 200)
delta <- vega <- theta <- gamma <- rep(0, 200)

for(i in 1:200){
  delta[i] <- GetGreeks(FUN = dnt1, arg = 1, epsilon = 0.0001, 
    x[i], 1000000, 0.96, 0.92, 0.06, 0.5, 0.02, -0.02)
  vega[i]  <-   GetGreeks(FUN = dnt1, arg = 5, epsilon = 0.0005, 
    x[i], 1000000, 0.96, 0.92, 0.06, 0.5, 0.0025, -0.025)
  theta[i] <- - GetGreeks(FUN = dnt1, arg = 6, epsilon = 1/365, 
    x[i], 1000000, 0.96, 0.92, 0.06, 0.5, 0.0025, -0.025)
  gamma[i] <- Gamma(FUN = dnt1, epsilon = 0.0001, S = x[i], K = 
    1e6, U = 0.96, L = 0.92, sigma = 0.06, Time = 0.5, r = 0.02, b = -0.02)
}

windows()
plot(x, vega, type = "l", xlab = "S",ylab = "", main = "Vega")

The following chart is the result of the preceding code:

Pricing the Double-no-touch option

After having a look at the value chart, the delta of a DNT is also very close to intuitions; if we are coming close to the higher barrier, our delta gets negative, and if we are coming closer to the lower barrier, the delta gets positive as follows:

windows()
plot(x, delta, type = "l", xlab = "S",ylab = "", main = "Delta")
Pricing the Double-no-touch option

This is really a non-convex situation; if we would like to do a dynamic delta hedge, we will lose money for sure. If the spot price goes up, the delta of the DNT decreases, so we should buy some AUDUSD as a hedge. However, if the spot price goes down, we should sell some AUDUSD. Imagine a scenario where AUDUSD goes up 20 pips in the morning and then goes down 20 pips in the afternoon. For a dynamic hedger, this means buying some AUDUSD after the price moved up and selling this very same amount after the price comes down.

The changing of the delta can be described by the gamma as follows:

windows()
plot(x, gamma, type = "l", xlab = "S",ylab = "", main = "Gamma")
Pricing the Double-no-touch option

Negative gamma means that if the spot goes up, our delta is decreasing, but if the spot goes down, our delta is increasing. This doesn't sound great. For this inconvenient non-convex situation, there is some compensation, that is, the value of theta is positive. If nothing happens, but one day passes, the DNT will automatically worth more.

Here, we use theta as minus 1 times the partial derivative, since if (T-t) is the time left, we check how the value changes as t increases by one day:

windows()
plot(x, theta, type = "l", xlab = "S",ylab = "", main = "Theta")
Pricing the Double-no-touch option

The more negative the gamma, the more positive our theta. This is how time compensates for the potential losses generated by the negative gamma.

Risk-neutral pricing also implicates that negative gamma should be compensated by a positive theta. This is the main message of the Black-Scholes framework for vanilla options, but this is also true for exotics. See Taleb (1997) and Wilmott (2006).

We already introduced the Black-Scholes surface before; now, we can go into more detail. This surface is also a nice interpretation of how theta and delta work. It shows the price of an option for different spot prices and times to maturity, so the slope of this surface is the theta for one direction and delta for the other. The code for this is as follows:

BS_surf <- function(S, Time, FUN, ...) {
  n <- length(S)
  k <- length(Time) 
  m <- matrix(0, n, k)
  for (i in 1:n) {
    for (j in 1:k) {
      l <- list(S = S[i], Time = Time[j], ...)
      m[i,j] <- do.call(FUN, l)
      }
  }
  persp3D(z = m, xlab = "underlying", ylab = "Time",zlab = "option price", phi = 30, theta = 30, bty = "b2")
}
BS_surf(seq(0.92,0.96,length = 200), seq(1/365, 1/48, length = 200),dnt1, K = 1000000, U = 0.96, L = 0.92, r = 0.0025, b = -0.0250,sigma = 0.2)

The preceding code gives the following output:

Pricing the Double-no-touch option

We can see what was already suspected; DNT likes when time is passing and the spot is moving to the middle of the (L,U) interval.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.192.59