1

I want to find the internal rate of return (IRR), basically the 'rate' that makes my NPV function go to zero, using the optim function.

My current code for the NPV function (which works) is:

npv <- function(rate, cf){
    r_v <- rep (rate,length (cf))
    t_v <- as.numeric (seq(1:length (cf)))
    pv <- cf * exp (-t_v*r_v)
    sum (pv)
} 

I tried using the following optim function:

InternalRateReturn <- optim(c(0,1), npv, cf = testcf2, gr = NULL, method = "L-BFGS-B", lower = -Inf, upper = Inf,control=list(), hessian = FALSE)

but it is not coming back with the correct answer for InternalRateReturn$par as opposed to using the uniroot method below.

May I ask how to modify this code (to reiterate, I just want to optimize the rate in the npv function such that the npv function equals zero)?

The IRR function using uniroot is as per below:

irr1 <- function(cf) {
    uniroot(npv, c(0, 1), cf=cf)$root
}
landroni
  • 2,902
  • 1
  • 32
  • 39
dhhs91
  • 11
  • 3
  • Well, I can see one problem, it might not be the only one: `optim` optimizes over the first argument in your function (e.g., `rate`, which is a scalar in the function `npv`). The initial value you supplied is a vector of two elements, so `optim` will throw an error. You can make your initial value a scalar to fix this, or package `rate` and `values` into a vector in `npv`. When I tried to replicate this problem, however, I got a different error than you have, so maybe try to fix and update. – tkmckenzie Apr 21 '15 at 20:06
  • @ tkmckenzie I understand what you are getting at. However, I am very new to this site (or programming in general) and is not sure how to package rate and values into a vector in npv. Is it possible to elaborate a bit more on that or direct me to a relevant link? thank you so much for your help. – dhhs91 Apr 21 '15 at 20:24
  • Ah sorry, it didn't help I was running into the character limit too (and sorry, I'd like to put this as an answer, but I'm not sure this actually fixes the problem). What you want to do is `npv <- function(x)`, then the first couple lines of your function should be something like `rate <- x[1]` and `values <- x[2]`. After that, you can have your function do whatever it was doing before. Hopefully this helps, let me know if I can clarify further. – tkmckenzie Apr 21 '15 at 21:02
  • @ tkmckenzie, I am sorry I still don't quite get you as if we embed the rate into the npv function, I cannot specify (hence optimize) the rate. I tried making everything vector using ***bold*** 'npv <- function(rate, cf){ r_v <- rep (rate,length (cf)) t_v <- as.numeric (seq(1:length (cf))) pv <- cf * exp (-t_v*r_v) sum (pv) } ' ***bold*** But this still have the same error as above. If you can post the npv function using what you meant, I will be really grateful. Thanks – dhhs91 Apr 22 '15 at 11:54
  • 1
    May I ask why not using `irr` from `FinCal` package? – mallet May 12 '15 at 14:44

2 Answers2

2

There's pretty cool example of using optim to calculate IRR by Matt Brigida

### IRR Function:  Takes a vector of payments and returns a list which includes the internal rate of return ($IRR) and possible word of warning ($beware) ----

irr <- function(x, period = 1, starting.value = .1){

### This should detect the number of sign changes.  Should correctly not warn if there are many negative cash flows (so long as there is only 1 change in sign).

    irr.func <- function(r){ ( sum(x / (1 + r)^{0:(length(x)-1)}) )^2 }
    result <- optim(par = starting.value, fn = irr.func, method = "Brent", lower = -1000000, upper = 1000000)

    ## detecting number of sign changes
    x.ge.0 <- 1 * (x >= 0)
    changes <- diff(x.ge.0)
    changes <- changes * changes
    num.changes <- sum(changes)

    if( num.changes > 1) {

        statement <- "Your cash flows change more than once -- so you may have multiple IRRs. This function will only return the first IRR it finds. To find the others, you can try different starting values.  However, note the IRR does not make sense if the signs change more than once (try Modified IRR or NPV)."
        value <- period * result$par
        return(list(beware = statement, IRR = value))

    } else {

        return(list(IRR = period * result$par))

    }
}

Matt also has a pretty useful function for more realistic modified IRR (which does not make unreasonable assumptions about the reinvestment rate)

### Modified IRR (MIRR) Function:  Takes a vector of payments and returns the MIRR by ----

mirr <- function(x, period = 1, starting.value = .1, discount.rate = 0.1, investment.rate = 0.05){

    ## move cash flows
    ## negative
    cf.neg <- (x < 0) * x
    ## discounted
    pv.cf.neg <- cf.neg / (1 + discount.rate)^{0:(length(x)-1)}
    pv <- sum(pv.cf.neg)

    ## positive
    cf.pos <- (x > 0) * x
    fv.cf.pos <- cf.pos * (1 + investment.rate)^{0:(length(x)-1)}
    fv <- sum(fv.cf.pos)

    mirr.per.period <- ( fv / abs(pv) )^{1 / (length(x))} - 1

    return( period * mirr.per.period )
} 
dmi3kno
  • 2,943
  • 17
  • 31
0

If you simply need to compute IRR or NPV (or MIRR), and since it is not clear why you would absolutely need to use optim, you may simply consider packages financial or FinCal instead of hacking your own function. Like this:

> require(financial)
> cf(c(-123400, 36200, 54800, 48100), i = 2.5)

Cash Flow Model

Flows:
      1       2       3       4 
-123400   36200   54800   48100 

 IRR%: 5.96 
 NPV Extremes at I%:  

   I%     NPV     NFV     NUS
1 2.5 8742.13 9414.32 3060.95

> require(FinCal)
> npv(c(-123400, 36200, 54800, 48100), r = 0.025)
[1] 8742.134
> irr(c(-123400, 36200, 54800, 48100))
[1] 0.05959787
landroni
  • 2,902
  • 1
  • 32
  • 39