[Numpy-discussion] add xirr to numpy financial functions?

josef.pktd@gmai... josef.pktd@gmai...
Tue May 26 10:07:21 CDT 2009


I rewrote irr to use the iterative solver instead of polynomial roots
so that it can also handle large arrays. For 3000 values, I had to
kill the current np.irr since I didn't want to wait longer than 10
minutes

When writing the test, I found that npv is missing a "when" keyword,
for the case when the first payment is immediate, i.e. in the present,
and that broadcasting has problems:

>>> np.npv(0.05, np.array([[1,1],[1,1]]))
array([ 1.9047619 ,  1.81405896])
>>> np.npv(0.05, np.array([[1,1],[1,1],[1,1]]))

Traceback (most recent call last):
  File "<pyshell#82>", line 1, in <module>
    np.npv(0.05, np.array([[1,1],[1,1],[1,1]]))
  File "C:\Programs\Python25\Lib\site-packages\numpy\lib\financial.py",
line 449, in npv
    return (values / (1+rate)**np.arange(1,len(values)+1)).sum(axis=0)
ValueError: shape mismatch: objects cannot be broadcast to a single shape


--------------------------

Here is the changed version, that only looks for one root. I added an
optional starting value as keyword argument (as in open office) but
didn't make any other changes:

def irr(values, start=None):
    """
    Return the Internal Rate of Return (IRR).

    This is the rate of return that gives a net present value of 0.0.

    Parameters
    ----------
    values : array_like, shape(N,)
        Input cash flows per time period.  At least the first value would be
        negative to represent the investment in the project.

    Returns
    -------
    out : float
        Internal Rate of Return for periodic input values.

    Examples
    --------
    >>> np.irr([-100, 39, 59, 55, 20])
    0.2809484211599611

    """
    p = np.poly1d(values[::-1])
    pd1 = np.polyder(p)
    if start is None:
        r = 0.99  # starting value, find polynomial root in neighborhood
    else:
        r = start
    # iterative solver for discount factor
    for i in range(10):
        r = r - p(r)/pd1(r)

##    #res = np.roots(values[::-1])
##    # Find the root(s) between 0 and 1
##    mask = (res.imag == 0) & (res.real > 0) & (res.real <= 1)
##    res = res[mask].real
##    if res.size == 0:
##        return np.nan
    rate = 1.0/r - 1
    if rate.size == 1:
        rate = rate.item()
    return rate

def test_irr():
    v = [-150000, 15000, 25000, 35000, 45000, 60000]
    assert_almost_equal(irr(v),
                        0.0524, 2)

    nper = 300 #Number of periods
    freq = 5  #frequency of payment
    v = np.zeros(nper)
    v[1:nper+1:freq] = 1  # periodic payment
    v[0] = -4.3995180296393199
    assert_almost_equal(irr(v), 0.05, 10)

    nper = 3000 #Number of periods
    freq = 5  #frequency of payment
    v = np.zeros(nper)
    v[1:nper+1:freq] = 1  # periodic payment
    v[0] = -4.3995199643479603
    assert_almost_equal(irr(v), 0.05, 10)


If this looks ok, I can write a proper patch.

Josef


More information about the Numpy-discussion mailing list