4
\$\begingroup\$

In investing, internal rate of return (IRR), the continuously compounded rate of return economically equivalent to a specified series of cashflows, is often a valuable concept.

Your challenge is to demonstrate how to compute IRR in the language of your choice in as few bytes as possible, subject to the following conditions:

  1. Your code should accept arbitrary cashflows, subject only to the condition that the initial cashflow is negative.
  2. You can assume that the cashflow has already been assigned to a variable f (use some other variable name if 'f' is a command name in your language).
  3. Assume cashflow is specified as a series of (date, datum) pairs, in whatever format is most convenient for your language. This challenge is about the IRR computation, not parsing the input.
  4. All functions built into your language are allowed except IRR() or the equivalent, if your language has it, because that's not interesting.
  5. Output should be a real number (thus, 0.14, not "14%").

The solution requires numerical root-finding, and different methods and starting conditions will find slightly different answers. For the purposes of the contest, if you're within 0.001 of the answer reached by Excel's XIRR function, you're close enough.

Example 1:

f={{{2001, 1, 1}, -1}, {{2002, 1, 1}, .3}, {{2005, 1, 1}, .4}, {{2005, 1, 10}, .5}};

0.0584316

Example 2:

f = {{{2011, 1, 1}, -1}, {{2012, 2, 1}, -.3}, {{2015, 3, 1}, .4}, {{2015,3,15},.01}, {{2015, 4, 10}, .8}};

-0.2455921

Example 3:

f = {{{2011, 1, 1}, -1}, {{2012, 2, 1}, 3.3}, {{2015, 3, 1}, 5.4}, {{2015,3,15}, 6.01}, {{2015, 4, 10}, 17.8}};

2.4823336

More information available at http://en.wikipedia.org/wiki/Internal_rate_of_return

\$\endgroup\$
  • \$\begingroup\$ What about cases where there are multiple roots? \$\endgroup\$ – Peter Taylor Jun 25 '14 at 20:31
  • \$\begingroup\$ @PeterTaylor discard any imaginary roots. In the case of multiple real roots, either report all, or any one. \$\endgroup\$ – Michael Stern Jun 25 '14 at 22:59
  • \$\begingroup\$ Minimizing the size of code is only one aspect of it. Newton-Raphson method is not the appropriate of numerical methods to find IRR. There are far better numerical algorithms as compared to NR that produce IRR values when Newton-Raphson method fails The use of 1st, 2nd, 3rd and 4th order Taylor polynomial approximations along with use of remainder term and zero, 1st, 2nd and 3rd order Taylor polynomial approximations produce multiple IRR values where the 3rd order Taylor polynomial approximation out performs any IRR algorithm that I have known and I happen to know roughly 60 different IRR find \$\endgroup\$ – user26801 Jul 2 '14 at 1:54
  • \$\begingroup\$ @Finance101 A reasonable point, but it feels like the subject of a separate contest. Perhaps one in which numerous difficult test problems are shown and the challenge is to demonstrate a method that solves all of them correctly. \$\endgroup\$ – Michael Stern Jul 2 '14 at 11:22
2
\$\begingroup\$

Maxima, 149

load(mnewton)$y(d):=?encode\-universal\-time(0,0,0,d[4],d[3],d[2])/31536000$mnewton(f[1][1]+sum(f[i][1]*(1+r)^(y(f[1])-y(f[i])),i,2,length(f)),r,.1);

This code has a bug where some payments might be off by 1/24 day (1 hour). This bug happens if your local time zone moves in or out of daylight-saving time. To fix this bug, add ,0 (2 characters) after d[2].

First set f to a list of lists in the form [[flow, year, month, day], ...]. Then paste this code at Maxima's prompt. The answer comes in the form [[r = float]]. If you want the float by itself, use part(%[1][1],2);

Example 1:

(%i150) f : [[-1, 2001, 1, 1], [.3, 2002, 1, 1], [.4, 2005, 1, 1],
             [.5, 2005, 1, 10]]$

(%i151) load(mnewton)$y(d):=?encode\-universal\-time(0,0,0,d[4],d[3],d[2])/31536000$mnewton(sum(f[i][1]*(1+r)^(y(f[1])-y(f[i])),i,1,length(f)),r,.1);
(%o153)                   [[r = .058394251994029284]]

Example 2:

(%i154) f : [[-1, 2011, 1, 1], [-.3, 2012, 2, 1], [.4, 2015, 3, 1],
             [.01, 2015, 3, 15], [.8, 2015, 4, 10]]$

(%i155) load(mnewton)$y(d):=?encode\-universal\-time(0,0,0,d[4],d[3],d[2])/31536000$mnewton(sum(f[i][1]*(1+r)^(y(f[1])-y(f[i])),i,1,length(f)),r,.1);
(%o157)                  [[r = - .017843072768412977]]

Example 3:

(%i158) f : [[-1, 2011, 1, 1], [3.3, 2012, 2, 1], [5.4, 2015, 3, 1],
             [6.01, 2015, 3, 15], [17.8, 2015, 4, 10]]$

(%i159) load(mnewton)$y(d):=?encode\-universal\-time(0,0,0,d[4],d[3],d[2])/31536000$mnewton(sum(f[i][1]*(1+r)^(y(f[1])-y(f[i])),i,1,length(f)),r,.1);
(%o161)                   [[r = 2.4823712861605407]]

My time zone is America/New_York. Because of the daylight-saving bug, other time zones might give slightly different answers.

Ungolfed

load(mnewton)$

/*
Returns the number of 365-day periods since the epoch, when called as
year([year, month, day]).
*/
year(date):=
?encode\-universal\-time(
  /* s, m, h, day, month, year, time zone */
  0, 0, 0, date[3], date[2], date[1], 0) /
31536000 /* seconds in 365 days */$

/*
Finds a numerical approximation for the internal rate of return for a
list of cash flows on a list of dates.  The dates must be a list of
lists in the form

    [[year, month, day], [year, month, day], ...]

The first date in the list is the present date for calculating the
present value of each cash flow.  Returns the rate for each 365-day
period.
*/
xirr(flows, dates):=
part(
  /* Solve for rate where net present value = 0. */
  mnewton(
    sum(
      /* present value of flows[i] on dates[i] */
      flows[i] * (1 + rate)^(year(dates[1]) - year(dates[i])),
      i, 1, length(flows)),
    rate  /* solve for rate */,
    .1    /* initial guess */)
  [1][1], /* extract 1st solution, 1st variable */
  2       /* value in variable = value */)$

Here xirr(flows, dates) is a function, but the golfed version only has an expression that looks at f. The golfed version also removes the part call and adds the daylight-saving bug.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.