利用Numpy求解投资内部收益率IRR

一、 内部收益率和净现值

内部收益率(Internal Rate of Return, IRR)其实要和净现值(Net Present Value, NPV)结合起来讲。净现值指的是某个投资项目给公司或企业带来的价值增值,可以简单地用以下公式去计算。

1.净现值:

NPV = CF0 + CF1/(1+r1) + … + CFt/(1+rt)^t

其中,CF0是初始投资额,是一个负值,代表现金的流出;t表示时间,指第t期;后面的CF1,CF2,…,CFt这些是每期的回报金额,为正值,表示投资所得的收益。r1,r2,…、rt是指每期的折现率。

举个比较通俗的例子,我们花100万元投资了一个一年期理财产品,预期收益率为10%,一年期国债利率为5%,投资是否合算?

这里涉及到折现率的相关知识,简单介绍一下:我们都知道银行存款是有利率的,我们把10000块钱存到银行,一年后,连本带息就会超过10000元。如果我们不把钱存银行,一年后还是10000元钱,不存钱相当于亏掉了那么多利息的钱。也就是说,在存入银行后,一年后的10000元的拿到现在来看,实际是不到10000元的,我可能存9500,一年后就能拿到10000。这就是一个折现的概念,用时间来换利息。

我们再看这个问题,
1年后,预期的现金流就是:
100×(1+10%)=110(万元),
然后,我们按照国债的利率5%来对其折现(假设该产品与国债信用风险相当),即:一年后的110万元,如果我们按信用等级较高的国债去投资,现在价值多少?
答案是:110/(1+5%)。
计算净现值:
NPV = -100 + 110/(1+5%)= 4.7619(万元)
净现值是正的,说明这笔投资合算。当然,这里其实只看年利率就可以做出简单的判断,实际情况下,不论是理财产品还是国债利率都是浮动的。

2.内部收益率

前面我们取的折现率是国债的利率,是一个与投资决策之外的值,也可以理解是外部的收益率。这里内部收益率,指的是使净现值等于0时的折现率,就称为内部收益率(IRR)。我们用一个固定的折现率r去计算,令NPV=0,则
0 = CF0 + CF1/(1+r) + … + CFt/(1+r)^t
那么,此时的r在数值上与IRR相等。因此,我们只要知道每年的现金流量情况,就能计算出该笔投资的IRR,将IRR与r相比,若IRR>r,则该项目值得投资;若IRR<r,则该项目不值得投资,不如去买同期的稳定国债之类的产品。

3.内部收益率的简单计算

如下表所示,第0年也就是现在,产生了-500万的现金流量,即用500万元去做一个投资,1年、2年、3年后的现金流量均为正的100万元、200万元和300万元。假设还是以5%的国债利率去做折现,这笔投资是否值得?

年份 现金流 (万元)
0 -500
1 100
2 200
3 300

计算内部收益率,将其与国债利率比较,
-500 + 100/(1+IRR) + 200/(1+IRR)^2 + 300/(1+IRR)^3 vs 5%
计算得到
IRR约为8% >5%,该笔投资值得。
从另一个角度看,这笔投资3年后累计现金流为+100万元,如果买3年期利率为5%的国债,则累计现金流为
500×(1+5%)^3-500=78.8125(万元) < 100(万元)

二、Numpy计算内部收益率IRR

直接上源码:

def irr(values):
    """
    Return the Internal Rate of Return (IRR).
    .. deprecated:: 1.18
       `irr` is deprecated; for details, see NEP 32 [1]_.
       Use the corresponding function in the numpy-financial library,
       //pypi.org/project/numpy-financial.
    This is the "average" periodically compounded rate of return
    that gives a net present value of 0.0; for a more complete explanation,
    see Notes below.
    :class:`decimal.Decimal` type is not supported.
    Parameters
    ----------
    values : array_like, shape(N,)
        Input cash flows per time period.  By convention, net "deposits"
        are negative and net "withdrawals" are positive.  Thus, for
        example, at least the first element of `values`, which represents
        the initial investment, will typically be negative.
    Returns
    -------
    out : float
        Internal Rate of Return for periodic input values.
    Notes
    -----
    The IRR is perhaps best understood through an example (illustrated
    using np.irr in the Examples section below).  Suppose one invests 100
    units and then makes the following withdrawals at regular (fixed)
    intervals: 39, 59, 55, 20.  Assuming the ending value is 0, one's 100
    unit investment yields 173 units; however, due to the combination of
    compounding and the periodic withdrawals, the "average" rate of return
    is neither simply 0.73/4 nor (1.73)^0.25-1.  Rather, it is the solution
    (for :math:`r`) of the equation:
    .. math:: -100 + \\frac{39}{1+r} + \\frac{59}{(1+r)^2}
     + \\frac{55}{(1+r)^3} + \\frac{20}{(1+r)^4} = 0
    In general, for `values` :math:`= [v_0, v_1, ... v_M]`,
    irr is the solution of the equation: [2]_
    .. math:: \\sum_{t=0}^M{\\frac{v_t}{(1+irr)^{t}}} = 0
    References
    ----------
    .. [1] NumPy Enhancement Proposal (NEP) 32,
       //numpy.org/neps/nep-0032-remove-financial-functions.html
    .. [2] L. J. Gitman, "Principles of Managerial Finance, Brief," 3rd ed.,
       Addison-Wesley, 2003, pg. 348.
    Examples
    --------
    >>> round(np.irr([-100, 39, 59, 55, 20]), 5)
    0.28095
    >>> round(np.irr([-100, 0, 0, 74]), 5)
    -0.0955
    >>> round(np.irr([-100, 100, 0, -7]), 5)
    -0.0833
    >>> round(np.irr([-100, 100, 0, 7]), 5)
    0.06206
    >>> round(np.irr([-5, 10.5, 1, -8, 1]), 5)
    0.0886
    """
    # `np.roots` call is why this function does not support Decimal type.
    #
    # Ultimately Decimal support needs to be added to np.roots, which has
    # greater implications on the entire linear algebra module and how it does
    # eigenvalue computations.
    res = np.roots(values[::-1])  # 求根
    mask = (res.imag == 0) & (res.real > 0)  # 虚部为0,实部为大于0
    if not mask.any():  # 判断是否有满足条件的实根
        return np.nan  # 不满足,返回Not A Number
    res = res[mask].real
    # NPV(rate) = 0 can have more than one solution so we return
    # only the solution closest to zero.
    rate = 1/res - 1
    rate = rate.item(np.argmin(np.abs(rate)))  # 
    return rate

我们直接调用numpy.irr()进行计算,参数为一个数组,每年的现金流量。然后用round()函数将其约到小数点后四位。
计算结果为:0.0821。我们发现这里还报了一个DeprecationWarning,在NumPy 1.20版本,irr()函数将被移除,有一个专门计算金融的Python包从Numpy中独立出来了,叫做numpy_financial,大致看了一下,可以计算fv、pmt、nper、impt、ppmt、pv、rate、irr、npv、mirr等,感兴趣的可以点击进去看一看。

>>> import numpy as np
>>> print(round(np.irr([-500, 100, 200, 300]),4))

Warning (from warnings module):
  File "C:\Users\Administrator\Desktop\func1.py", line 2
    print(round(np.irr([-100, 39, 59, 55, 20]),4))
DeprecationWarning: numpy.irr is deprecated and will be removed from NumPy 1.20. Use numpy_financial.irr instead (//pypi.org/project/numpy-financial/).
0.0821
Tags: