[Numpy-discussion] mirr test correctly fails for given input.
josef.pktd@gmai...
josef.pktd@gmai...
Wed Aug 26 10:44:41 CDT 2009
On Wed, Aug 26, 2009 at 11:25 AM, <josef.pktd@gmail.com> wrote:
> On Wed, Aug 26, 2009 at 10:08 AM, Skipper Seabold<jsseabold@gmail.com> wrote:
>> On Wed, Aug 26, 2009 at 1:45 AM, <josef.pktd@gmail.com> wrote:
>>> On Tue, Aug 25, 2009 at 11:38 PM, Charles R
>>> Harris<charlesr.harris@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@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
>
a comment on the function
>From a theoretical perspective returning nan wouldn't be necessary.
The rate of return would be well defined:
-1: you only pay and get nothing back (you lose 100%)
inf: you only receive and have nothing to pay
0/0 = nan : you don't pay and you get nothing back
for practical purposes, the nan might signal better that the user
might have made a mistake
Josef
More information about the NumPy-Discussion
mailing list