[Numpy-discussion] mirr test correctly fails for given input.

josef.pktd at gmail.com josef.pktd at gmail.com
Wed Aug 26 11:25:35 EDT 2009


On Wed, Aug 26, 2009 at 10:08 AM, Skipper Seabold<jsseabold at gmail.com> wrote:
> On Wed, Aug 26, 2009 at 1:45 AM, <josef.pktd at gmail.com> wrote:
>> On Tue, Aug 25, 2009 at 11:38 PM, Charles R
>> Harris<charlesr.harris at gmail.com> wrote:
>>> So is it a bug in the test or a bug in the implementation? The problem is
>>> that the slice values[1:] when
>>> values =  [-120000,39000,30000,21000,37000,46000] contains no negative
>>> number and a nan is returned. This looks like a bug in the test. The
>>> documentation also probably needs fixing.
>>>
>>> Chuck
>>
>> There is a bug in the code, the nan is incorrectly raised. After
>> correcting the nan (checking on the original, instead of shortened
>> values), I got one failing test, that I corrected with the matching
>> number from Openoffice.
>>
>> (The main problem that the function is more complicated than
>> necessary, is because np.npv doesn't allow the inclusion of the
>> investment in the initial period)
>>
>> This needs reviewing, since it's late here.
>>
>> Josef
>>
>>
>> import numpy as np
>> from numpy.testing import assert_almost_equal, assert_
>>
>> from numpy import npv
>>
>> def mirr(values, finance_rate, reinvest_rate):
>>    """
>>    Modified internal rate of return.
>>
>>    Parameters
>>    ----------
>>    values : array_like
>>        Cash flows (must contain at least one positive and one negative value)
>>        or nan is returned.
>>    finance_rate : scalar
>>        Interest rate paid on the cash flows
>>    reinvest_rate : scalar
>>        Interest rate received on the cash flows upon reinvestment
>>
>>    Returns
>>    -------
>>    out : float
>>        Modified internal rate of return
>>
>>    """
>>
>>    values = np.asarray(values, dtype=np.double)
>>    initial = values[0]
>>    values1 = values[1:]
>>    n = values1.size
>>    pos = values1 > 0
>>    neg = values1 < 0
>>    if not (np.sum(values[values>0]) > 0 and np.sum(values[values<0]) < 0):
>>        return np.nan
>>    numer = np.abs(npv(reinvest_rate, values1*pos))
>>    denom = np.abs(npv(finance_rate, values1*neg))
>>    if initial > 0:
>>        return ((initial + numer) / denom)**(1.0/n)*(1 + reinvest_rate) - 1
>>    else:
>>        return ((numer / (-initial + denom)))**(1.0/n)*(1 + reinvest_rate) - 1
>>
>>
>>
>>
>>
>> #tests from testsuite and Skipper plus isnan test
>>
>> v1 = [-4500,-800,800,800,600,600,800,800,700,3000]
>> print mirr(v1,0.08,0.055)
>> assert_almost_equal(mirr(v1,0.08,0.055),
>>                    0.0666, 4)
>>
>> #incorrect test ? corrected
>> v2 = [-120000,39000,30000,21000,37000,46000]
>> print mirr(v2,0.10,0.12)
>> assert_almost_equal(mirr(v2,0.10,0.12), 0.126094, 6)  # corrected from OO
>>
>
> Yes, the value in the tests that this v2 tests against is wrong.  It
> was the value returned by the old mirr but not excel or oocalc.  This
> is the correct one.  I noted it in my patch, but it was hard to catch
> since I didn't supply a diff.  Now, I know...
>
>>
>> v2 = [39000,30000,21000,37000,46000]
>> assert_(np.isnan(mirr(v2,0.10,0.12)))
>>
>>
>> v3 = [100,200,-50,300,-200]
>> print mirr(v3,0.05,0.06)
>> assert_almost_equal(mirr(v3,0.05,0.06), 0.3428, 4)
>>
>>
>> #--------------
>> print mirr([100, 200, -50, 300, -200], .05, .06)
>> assert_almost_equal(mirr((100, 200,-50, 300,-200), .05, .06),
>>                    0.342823387842, 4)
>>
>> V2 = [-4500,-800,800,800,600,600,800,800,700,3000]
>> print mirr(V2, 0.08, 0.055)
>> assert_almost_equal(mirr(V2, 0.08, 0.055), 0.06659718, 4)
>
> Skipper
> _______________________________________________
> NumPy-Discussion mailing list
> NumPy-Discussion at scipy.org
> http://mail.scipy.org/mailman/listinfo/numpy-discussion
>

Here is a shortened version, that uses Skippers corrections, but
avoids splitting the values array, by working around npv not starting
with the initial investment. It passes the same tests as the corrected
version.

Josef

def mirr(values, finance_rate, reinvest_rate):
    values = np.asarray(values, dtype=np.double)
    n = values.size
    pos = values > 0
    neg = values < 0
    if not (pos.any() and neg.any()):
        return np.nan

    numer = np.abs(npv(reinvest_rate, values*pos)) * (1 + reinvest_rate)
    denom = np.abs(npv(finance_rate, values*neg)) * (1 + finance_rate)
    return (numer / denom)**(1.0/(n-1)) * (1 + reinvest_rate) - 1



More information about the NumPy-Discussion mailing list