IRR是计算信用卡分期、小额贷款、汽车租赁实际利率的重要参考,本文主要介绍如何在R语言中编写IRR函数。
关于使用Excel内置公式计算IRR的相关文章已有很多,那么为何要使用R语言编写函数呢?原因就在于R语言可以借助向量化操作方式实现irr的批量查询。
1、自定义函数
在编写IRR之前,我们先简单回忆一下初等数学中一个最简单的函数形式:$f(x, y) = x + y$。
R语言自定义函数非常简单,其形式如下:
f <- function(x,y){
x + y
}
编写函数的语法包括:
- 函数名:可以用便于记忆的词汇定义函数名,例如上段代码中的f。
- 关键词:funciton,说明此数据对象为函数。
- 参数:供用户后续输入的变量,例如上段代码中的x、y。
- 函数主体:函数的表达式。
- 返回值:默认返回函数主体的最后一个表达式的结果,可以用returun()函数指定要返回的值。
输入参数x = 2, y = 3, 则f(2, 3) = 2 + 3 = 5,下面我们来验证一下结果。
f(2, 3)
## 5
2、编写IRR函数
内部收益率(Internal Rate of Return (IRR)),就是资金流入现值总额与资金流出现值总额相等、净现值等于零时的折现率。
根据资金的时间价值理论,我们将每一期现金流进行折现,求净现值为0时的折现率,其本质是求解一个一元多次方程的根。
设r为折现率,N为后期现金流对应的总期限,Cn为每一期的现金流,n为现金流对应的期限,则一元多次方程为:
$\begin{equation}
npv = \sum_{n = 0} ^ N\frac{C_n}{(1 + r) ^ n} = 0
\end{equation}$
根据前述函数编写说明,定义IRR函数的完整代码如下:
irr <- function(cf)
{ N <- length(cf) - 1
n <- 0 : N
f <- function(r, n) (1 + r) ^ n
r <- seq(0.0001,1,0.0001)
pv <- cf / t(outer(r, n, f))
npv <- colSums(pv)
irr <- r[abs(npv) == min(abs(npv))]
return(irr)
}
下面逐一解释此段代码。
cf
## 现金流cf可作为一个向量输入
N <- length(cf) - 1
## 求后期现金流对应的总期数
n <- 0 : N
## 生成一个长度为N + 1的序列
r <- seq(0.0001,1,0.0001)
## 定义折现率的区间范围,提高计算效率
pv <- cf / t(outer(r, n, f))
## 外积向量化操作替代for循环,生成一个length(r) x length(n)矩阵
npv <- colSums(pv)
## 矩阵的列和即为净现值
irr <- r[abs(npv) == min(abs(npv))]
## 穷举法求npv最接近于0时的r值,即irr
return(irr)
## 返回irr
3、应用
问题的提出:贷款金额10万元,24期,利率10%,费率1%(提前收取),10%保证金(放款时扣除,最后一期退还),求实际IRR?
按照前述方法,先定义一个pmt函数:
pmt <- function(R, N, PV){
(1 + R / 12) ^ N / ((1 + R / 12) ^ N - 1) * R / 12 * PV
}
S <- 100000
N <- 24
R <- 0.1
fee <- 0.01
deposit <- 0.1
cf <- c(-S + S * deposit + S * fee, rep(pmt(R, N, S), N - 1), pmt(R, N, S) - S * deposit)
## 期初现金流出为(-贷款金额 + 保证金 + 手续费),中间(N - 1)期现金流为月供款,最后一期现金流为(月供款 - 保证金)。
irr(cf)*12
## 0.1344
## 计算结果为13.44%(年化)
以上为单一现金流的IRR计算,关于多个现金流的批量IRR计算方式,后续更新。
资金时间价值理论
fv = pv* (1+r)
编写函数
2、函数基础
R中一切
下面以一个最简单的函数形式为例,说明R语言如何编写函数。
编写实例:
R语言函数的编写分为函数名、函数、参数三个部分
f(2,3)
公式编写
- 月供公式:
函数形式: $pmt = f(R, N, PV)_{1} = \frac{(1 + R / 12) ^ N R / 12 PV}{(1 + R / 12) ^ N - 1}$
r为利率,n为期限
2. 利息公式
$interest = f(R, N, PV){2} = f(R, N, PV){1} * N - PV$
interest <- function(R, N, PV){
(1 + R / 12) ^ N / ((1 + R / 12) ^ N - 1) * R / 12 * PV * N - PV
}
3. 费率公式
$ratio = f(R, N, PV){3} = \frac{f(R, N, PV){2}}{PV}$
ratio <- function(R, N, PV){
((1 + R / 12) ^ N / ((1 + R / 12) ^ N - 1) * R / 12 * PV * N - PV) / PV
}
4. irr公式
irr <- function(cf)
{ n <- 0 : (length(cf) - 1)
f <- function(r, n) (1 + r) ^ n
r <- seq(0.0001,1,0.0001)
pv <- cf / t(outer(r, n, f)) ##外积向量化操作替代for循环
npv <- colSums(pv)
irr <- r[abs(npv) == min(abs(npv))] ##穷举法替代求根公式
return(irr)
}
应用
- 费率与利率的线性拟合
R <- seq(from = 0.06, to = 0.12, by = 0.01)
ratio <- ratio(R, 24, 100000)
lm(ratio~R)
summary(ratio / R)
df <- data.frame(R,ratio)
library(ggplot2)
ggplot(df,aes(R,ratio)) + geom_point()
2.irr计算
总价10万元,10%首付,10%保证金,利率10%,费率1%,24期,求实际IRR?
cf <- c(-100000*0.9+90000*0.1+90000*0.01, rep(pmt(0.1,24,90000),23),pmt(0.1,24,90000)-0.1*90000)
sale_price <- 100000
DP <- 0.1
deposit <- 0.1
fee <- 0.01
R <- 0.1
N <- 24
cf <- c(- sale_price * (1 - DP) + sale_price * (1 - DP) * deposit +sale_price * (1 - DP) * fee, rep(pmt(R, N, sale_price * (1 - DP)), N-1), pmt(R, N, sale_price * (1 - DP)) - sale_price * (1 - DP) * deposit)
irr(cf)*12
- 保证金与irr的线性拟合
sale_price <- 100000
DP <- 0.1
deposit <- seq(0, 0.5, 0.05)
fee <- 0.01
R <- 0.1
N <- 24
cf <- c(- sale_price * (1 - DP) + sale_price * (1 - DP) * deposit +sale_price * (1 - DP) * fee, rep(pmt(R, N, sale_price * (1 - DP)), N-1), pmt(R, N, sale_price * (1 - DP)) - sale_price * (1 - DP) * deposit)
irr(cf)*12
拓展资源
FinCal包
npv <- function(r,cf)
{ n <- 0 : (length(cf) - 1)
f <- function(r, n) (1 + r) ^ n
pv <- cf / t(outer(r, n, f)) ##外积向量化操作替代for循环,且可求多个R值对应的npv,fincal包只能求单个r值的npv
npv <- colSums(pv)
return(npv)
}
npv(c(0.12,0.13),c(-5, 1.6, 2.4, 2.8))
# 等价于
sapply(c(0.12,0.13),function(r) npv(r,c(-5, 1.6, 2.4, 2.8)))
irr <- function (cf)
{
uniroot(function(r) npv(r, cf), interval = c(1e-10,
1e+10), extendInt = "yes")$root
## 求解一元多次方程
}