I was just sketching this. The following looks complete to me:
using Optim
# This is, btw not efficient given that you only use consecutive periods.
# Instead of the `^period[i]` it would be more efficient to loop as in
# pv = 0.0
# R = 1 + irr
# cumR = R
# for j = 1 : length(cf)
# cumR /= R
# pv += cf[j] / cumR
# end
function npv(irr,cf,period)
sum(cf[i]/(1+irr)^period[i] for i in 1:length(cf))
end
function irr(cf)
# Get non-zero indices
idx = findall(x -> x != 0.0, cf);
# Make sure solution may exist
if length(idx) < 2
return NaN
elseif !(any(x -> x > 0.0, cf) && any(x -> x < 0.0, cf))
return NaN
end
lb= [0.0,1.0,-1.0,-1.0e8]
up=[1.0,1.0e8,0.0,0.0]
done = false;
j = 1;
result = 0.0;
f(x) = npv(x, cf[idx], 0 : length(idx) - 1)
while !done
result = optimize(x -> f(x)^2, lb[j], up[j], Brent());
if Optim.minimum(result) <= 1e-9
done = true
elseif j == length(lb)
# No more bounds to try. Solution does not exist.
done = true
result = NaN
else
# Next set of bounds to try
j += 1;
end
end
return result
end
irr([-100,10,110])
Instead of checking whether the first entry is non-zero etc, I am simply checking whether there are any positive and negative entries.