1

I have my Black-Scholes function and my bisection model for call options with data from a CSV. It appears to be getting stuck in the inner loop because it stays above the tolerance. My Black-Scholes does calculate accurately and I am using the average of bid and ask for the market price instead of the actual price of the option. After working on this for hours, maybe I am just missing something obvious.

The link to the CSV is here: http://s000.tinyupload.com/?file_id=06213890949979926112

########################################################################
#Black-Scholes-Merton Call
bsmCall <- function(S, K, M, sig, r) {
  yrTime=(M/252)
  d1 <- (log(S/K)+(r+(sig^2/2))*(yrTime))/(sig*(sqrt(yrTime)))
  d2 <- d1-sig*(sqrt(yrTime))
  C <- (S*(pnorm(d1)))-((pnorm(d2))*K*(exp(-r*yrTime)))
  return(C)
}
########################################################################

myData = read.csv("09-26-16.csv", stringsAsFactors=FALSE)    #DATA
myData <- myData[,2:24]   #omit first column

####### start bisection method of CALLS and put IV in database #######
i <- 1    # reset counter
tol <- 0.000001   #tolerance

while(i <= nrow(myData)) {
  if((myData[i,5] != 0) & (myData[i,6] != 0)) {
    volLower <- .0001    #will need to reset with each iteration
    volUpper <- 1         #will need to reset with each iteration
    volMid <- (volLower + volUpper) / 2   #will need to reset with each iteration

    while(abs(bsmCall(as.numeric(as.character(myData[i,17])),as.numeric(as.character(myData[i,1])),as.numeric(as.character(myData[i,22])),volMid,as.numeric(as.character(myData[i,23])))-(as.numeric(as.character(myData[i,5])))) >= tol) {
      if((bsmCall(as.numeric(as.character(myData[i,17])),as.numeric(as.character(myData[i,1])),as.numeric(as.character(myData[i,22])),volMid,as.numeric(as.character(myData[i,23])))-(as.numeric(as.character(myData[i,5])))) < 0) {
        volLower <- volMid
        volMid <- (volUpper + volMid)/2
      } else {
        volUpper <- volMid
        volMid <- (volLower + volMid)/2
      }
    }
    myData[i,8] <- volMid
  } else { myData[i,8] <- 0 }
  i=i+1
}
  • Instead of linking to the csv (links do die, and SO is about creating a resource), please provide a minimal reproducible example http://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example . – Shape Sep 27 '16 at 03:21
  • It's unclear what your question actually is. Is there an actual R error when running this code? Or is it simply not behaving as expected? If so, how is it not behaving as expected? (what is "it" that stays above the tolerance? Is it supposed to stay above the tolerance? Is the problem that `bmsCall` is not returning the number you expect? Many of us do not know the details of the Black Scholes eqn and bisection models for call options) – mathematical.coffee Sep 27 '16 at 03:41
  • It ends in an infinite loop. Here is a dataframe recreation of the CSV. – Probability1 Sep 27 '16 at 03:47
  • myData<-data_frame(c(70,115,120),c(147.68,102.68,95.65),c(147.08,102.21,92.00),c(148.32,102.50,93.65),c(147.700,102.355,92.825),c(1,50,126),c(0,0,0),c(1,1,1),c(2,2,2),c(3,3,3),c(4,4,4),c(5,5,5),c(6,6,6),c(7,7,7),c(8,8,8),c(9,9,9),c(214.24,214.24,214.24),c(10,10,10),c(110,110,110),c(20,20,20),c(30,30,30),c(19,19,19),c(.004,.004,.004)) – Probability1 Sep 27 '16 at 03:47

2 Answers2

1

The problem is here:

while(abs(bsmCall(as.numeric(as.character(myData[i,17])),
                  as.numeric(as.character(myData[i,1])),
                  as.numeric(as.character(myData[i,22])),
                  volMid,
                  as.numeric(as.character(myData[i,23])))-(as.numeric(as.character(myData[i,5])))) >= tol)

You're using a while loop on a condition that, if true, is always true. It's an infinite loop. On your first row of data this problem is encountered.

How to fix this error is specific to your use case, but if you just change while to if you'll see the loop complete immediately.

You asked about the bisection method. There are a few in packages and here's another from here:

bisect <- function(fn, lower, upper, tol=1.e-07, ...) {
f.lo <- fn(lower, ...)
f.hi <- fn(upper, ...)
feval <- 2

if (f.lo * f.hi > 0) stop("Root is not bracketed in the specified interval
\n")
chg <- upper - lower

while (abs(chg) > tol) {
        x.new <- (lower + upper) / 2
        f.new <- fn(x.new, ...)
        if (abs(f.new) <= tol) break
        if (f.lo * f.new < 0) upper <- x.new
        if (f.hi * f.new < 0) lower <- x.new
        chg <- upper - lower
        feval <- feval + 1
}
list(x = x.new, value = f.new, fevals=feval)
}

# An example
fn1 <- function(x, a) {
exp(-x) - a*x
}

bisect(fn1, 0, 2, a=1)

bisect(fn1, 0, 2, a=2)

Recursive version:

bisectMatt <- function(fn, lo, hi, tol = 1e-7, ...) {

    flo <- fn(lo, ...)
    fhi <- fn(hi, ...)

    if(flo * fhi > 0)
        stop("root is not bracketed by lo and hi")

    mid <- (lo + hi) / 2
    fmid <- fn(mid, ...)
    if(abs(fmid) <= tol || abs(hi-lo) <= tol)
        return(mid)


    if(fmid * fhi > 0)
        return(bisectMatt(fn, lo, mid, tol, ...))

    return(bisectMatt(fn, mid, hi, tol, ...))
}
Hack-R
  • 22,422
  • 14
  • 75
  • 131
  • But I need it to loop through until the Black-Scholes prices minus the actual price is arbitrarily low. How does the bisection method work since, like you said, the first condition is always true? – Probability1 Sep 27 '16 at 03:58
  • The way the code is written it's not looping through, it's just stopping when that condition is true. I don't know exactly what you're trying to do, but I assume you'd want to take some action if that condition is true and if not then do something else or proceed to the next iteration. If you're trying to use the bisection method you just need to use `BFfzero()`. See here http://artax.karlin.mff.cuni.cz/r-help/library/NLRoot/html/BFfzero.html or https://www.rforge.net/doc/packages/animation/bisection.method.html – Hack-R Sep 27 '16 at 04:03
  • That is true. I can use one of the packages. I was trying to recreate it myself though. I'll probably end up using one of them though. – Probability1 Sep 27 '16 at 04:14
  • @Probability1 You can also check out the 2 custom bisection functions I put in my answer or check out the source of the functions in the packages to see how they work and compare it against what you were doing. You can also use debug mode to help understand further. – Hack-R Sep 27 '16 at 04:16
  • Thanks. I'm just using the BFfzero function. Do you by chance know how to save the answer in a variable? When I set it to a variable it just saves "finding root is successful". – Probability1 Sep 27 '16 at 04:55
0

Jeez, this is my 3rd edit so far...

Lets reconstruct the while loop when i=1 and print the volMid - the of the only part of the while condition that is updating after each iteration

i <- 1
volLower <- .0001    #will need to reset with each iteration
volUpper <- 1         #will need to reset with each iteration
volMid <- (volLower + volUpper) / 2   #will need to reset with each iteration

j <- 1
while(abs(bsmCall(myData[i,17], myData[i,1], myData[i,22],volMid,myData[i,23])-myData[i,5]) >= tol & j < 30) {
  if(bsmCall(myData[i,17], myData[i,1], myData[i,22],volMid,myData[i,23])-myData[i,5] < 0) {
volLower <- volMid
volMid <- (volUpper + volMid)/2
  } else {
    print("pos")
    volUpper <- volMid
    volMid <- (volLower + volMid)/2
  }
  j <- j + 1
  print(volMid)
}

Result:

#[1] 0.750025
#[1] 0.8750125
#[1] 0.9375062
#[1] 0.9687531
#[1] 0.9843766
#[1] 0.9921883
#[1] 0.9960941
#[1] 0.9980471
#[1] 0.9990235
#[1] 0.9995118
#[1] 0.9997559
#[1] 0.9998779
#[1] 0.999939
#[1] 0.9999695
#[1] 0.9999847
#[1] 0.9999924
#[1] 0.9999962
#[1] 0.9999981
#[1] 0.999999
#[1] 0.9999995
#[1] 0.9999998
#[1] 0.9999999
#[1] 0.9999999
#[1] 1
#[1] 1
#[1] 1
#[1] 1
#[1] 1
#[1] 1

volMid converges to 1 after less than 30 iterations, and from there on out, it's stuck.

Chrisss
  • 3,211
  • 1
  • 16
  • 13
  • I'll have to look into this. I'm new to R so the $ sign I just haven't gotten used to yet. I'm going to try using that instead and maybe it will work out better because easier to read. – Probability1 Sep 27 '16 at 04:02
  • @Probability1 I made some edits, to my answer, there were some errors in my last one. – Chrisss Sep 27 '16 at 04:03
  • That's true. It does keep going to 1 and then getting stuck. I'm still just confused about how to fix all this. I'm trying to copy this basically: https://www.r-bloggers.com/the-only-thing-smiling-today-is-volatility/ – Probability1 Sep 27 '16 at 04:13
  • I think I need to redo everything. I get why it converges to 1, but just don't know how to recreate the bisection method as of now. Thanks for all the help! – Probability1 Sep 27 '16 at 04:57