From: avinash@felix.contex.com (Avinash Chopde)
Newsgroups: misc.invest
Subject: Summary: Computing ROR for investments with monthly inflows.
Date: 7 Apr 92 21:47:47 GMT


[sorry, no reference threads, original article has expired at my site ... ]
[Is a repost, if you saw a similar article without this line, it contains
 a program bug....]

I had posted a query regarding rate of return computation for investments
involving monthly inflows, and, as one responder mentioned, each method
gives me different results .. the idea is to stick to one when
making comparisons between different invevestment options.
Of course, that is not always possible ...
Anyway, here follow two methods (a third one, a C program, was posted to this
newsgroup a few days ago).

(As to the variance between the methods, I never got a difference of more
than 0.09 %, which is probably too small for most to worry about...)

---
In general, computing rate of return for montly investments is done using
an iterative method.
---
Reply from (Jonathan Edwards):
>
>>This problem is confusing me, is related to computing
>>return on investment:
>
>>Assume $ X is invested at the beginning of the year into some
>>mutual fund or like account, with $ Y added to the account every month.
>
>>Now, down the road,
>>if the value at any given month "i" is Vi,
>>what conclusions can be drawn from it ?
>
The relevant formula is F = P(1+i)**n - p((1+i)**n - 1)/i
where F is the future value of your investment (i.e., the value after
n periods), P is the present value of your investment (i.e., the
amount of money you invest initially), p is the payment each period
(p is negative if you are adding money to your account and positive
if you are taking money out of your account), n is the number of 
periods you are interested in, and i is the interest rate per
period.  You cannot manipulate this formula to get a formula for i;
you have to use some sort of iterative method (or buy a financial
calculator - the TI version is extremely cheap, although you get what
you pay for...).

One thing to keep in mind is that i is the interest rate *per period*.
You may need to compound the rate to obtain a number you can compare
apples-to-apples with other rates.  For instance, a 1 year CD paying
12% interest is not as good an investment as an investment paying 1%
per month for a year.  If you put $1000 into each, you'll have $1120
in the CD at the end of the year but $1000*(1.01)**12 = $1126.82 in
the other investment due to compounding.  I always convert interest
rates of any kind into a "simple 1-year CD equivalent" for the purposes
of comparison.

--------------------------------------------------------------------------
Reply from (Larry Martell):

[abc: I prefer this program to the one that was posted earlier. 
It is easier to use, and looking over the code, it *seems*
easier to prove that this program is correct.....
Also, it makes it easy to maintain investment for different years in
different files.
Then, to obtain the return for any year, just pipe the year's data file to the
program.
To obtain annualized returns for a period of more than one year, just
cat all the yearly data files into this program...
Sorry for the UNIX terms, but I use the same method on my MSDOS machine,
so, there! :-)]

On to the main story [my changes to the program are annotated with [abc]],
reply from (Larry Martell):

I asked the same question of this group back in March 1991. I got lots of 
responses, but here are the best two:

From: bstrand@cray.com (Brad Strand)

The formula gets very messy as more and more investments are made. For example
if you invest $1000 on Jan 1 of 1989, 1990, and 1991, and if your investment 
on Jan 1, 1992 is worth $5000, then here's the equation you need to solve:

	5000 = 1000(1+r)^3 + 1000(1+r)^2 + 1000(1+r)

	solve for r

In general, the exponents are what fractions of a year have passed between the 
time of that investment and now.  

/*************************************************************************
From: wendt@arizona.edu (Alan Lee Wendt)

Here's a little program I cobbled up to calculate rate of return on
a portfolio.  Its input is lines, each containing a date, a tab
character, and an amount of money.  Investments should be positive
numbers and withdrawls negative.  If you still have money invested,
fake a withdrawl at the end of the file, with todays date and
the current value.  For example, in my magellan IRA fund, I deposited
$1000 last year on 7/3/86 and yesterday its value was 1305.12:

07/03/1986	1000.00
12/31/1986	10
07/30/1987	-1305.12

* abc: use 4 digit years....this prog assumes year < 99  implies 19year!
* abc: added # as a comment indicator (at the beginning of the line)
*      (anything after the two columns above is ignored, anyway)

The $10 represents the IRA servicing fee which gets charged annually.

You can cat together several files and pipe them in.  The rate of return
is gotten by binary search.  The binary search is kind of interesting
because it runs out to the full precision of the machine, without any
machine-dependent code.  It stops when the floating point arithmetic
begins to break down.

I use this program all the time; it's pretty cool the way it works by guessing
and working backwards.
 *************************************************************************/
/*  rate of return of a portfolio	*/

/* abc: added USE_POW - use it if you have math.h, and the -lm library */

#define USE_POW 1

#include <stdio.h>
#ifdef USE_POW
# include <math.h>
#endif

#define		ARR_MAX	500
double		deposits[ARR_MAX];
long		dates[ARR_MAX];

/* strtoday string into day number */

#define month (n[0])
#define day (n[1])
#define year (n[2])

static unsigned maxd[] = {
    0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
};

static double dexp(d, exp)
double d;
register long exp;
{
    static double result;

    result = 1.0;	    /* init multiplicative acc */

    if (exp < 0)	    /* exponent negative? */
    {
	d = 1/d;
	exp = -exp;
    }

    while (exp != 0) {
	if (exp & 1) result *= d;   /* fold this factor */
	exp >>= 1;	    /* shift exponent */
	d *= d;     /* square running multiplier */
    }

    return result;
}

/*  convert "MM/DD/YY" or "MMDDYY" into # of days using Jan 1, 1601 = day 1
 *  ref: CACM 11, 657 (1968) - valid back to 24 Nov -4713
 */
long strtoday(dd)
char *dd;
{
    long		strtol();
    char		*ddend;
    int			c = 0, slash = 0;
    long		jdn;
    int			n[3];
    static int		m;

    jdn = strtol(dd, &ddend, 10);		/* convert mmddyy */
    month = jdn;

    if (*ddend == '/') {
	slash = 1;				/* got mm/dd/yy   */
	goto middle;
    }

    else if (*ddend) return -1;

    for (; c <= 2; c++) {
	if (slash) {
	    n[c] = strtol(dd, &ddend, 10);	/* convert a number */
	    if (*ddend != "//"[c])		/* check proper terminator */
baddy:		
		return -1;
middle:	    
	    dd = ddend + 1;
	}
	else {
	    n[2 - c] = jdn % 100;
	    jdn /= 100;
	}
    }

    if (year <= 99) year += 1900;	/* correct for c */
    maxd[2] = 28;
    if ((!(year & 3) && (year % 100)) || !(year % 400))
	maxd[2] = 29;
    if (month < 1 || month > 12 || day < 1 || day > maxd[month]) goto baddy;

    m = (month <= 2) ? -1 : 0;
    year += m;
    jdn = (long)day - 2337888L;
    jdn += 1461L * ((long)year + 4800L) / 4L;
    jdn += 367L * (month - 2L - (long)m * 12L) / 12L;
    jdn -= 3L * (((long)year + 4900L) / 100L) / 4L;

    return (double)jdn;
}

main()
{
    long	maxday = 0;
    int		ndep = 0;
    char	bf[200];
    double	limit[2], outr, rate, testtot, atof();
    int		which, i;
    char 	in[1024];

    while (fgets(in, 1024, stdin)) {
	if (sscanf(in, "%s%lf\n", bf, &deposits[ndep]) == 2) {

	    if (bf[0] == '#') { /* [abc] */
	        fprintf(stderr,"ignoring comment line: %s", in);/* \n in "in" */
		continue;
	    }

	    dates[ndep] = strtoday(bf);
	    if (maxday < dates[ndep]) maxday = dates[ndep];
	    ndep++;
	    if (ndep >= ARR_MAX) {
		fprintf(stderr, "Error: can only handle %d lines input\n", ARR_MAX);
		exit(1);
	    }
	} else {
	    fprintf(stderr, "ignoring comment line: %s", in); /* \n in "in" */
	}
    }

    /*  Calculate daily rate of return by binary search	*/
    limit[1] = 1.1;		/* 10 percent every day	*/
    limit[0] = 0;		/* throw it all away	*/
    for (;;) {
	rate = (limit[1] + limit[0]) / 2;
	testtot = 0;
	for (i = 0; i < ndep; i++) {
#ifdef USE_POW
	    testtot += (deposits[i] * pow(rate, (double)(maxday - dates[i])));
#else
	    testtot += (deposits[i] * dexp(rate, (long)(maxday - dates[i])));
#endif
	}

	/* which = (0 < testtot);*/
	which = (0 < testtot) ? 1 : 0; /* [abc] */

	if (limit[0] >= limit[1] || limit[which] == rate) {

#ifdef USE_POW
	    outr = 100.0 * (pow(rate, (double)365) - 1.0);
#else
	    outr = 100.0 * (dexp(rate, (long)365) - 1.0);
#endif
	    printf("%.6f %% (annualized)", outr);
#ifdef USE_POW
	    outr = 100.0 * (pow(rate, (double)365.0/12.0) - 1.0);
	    printf(" %.6f %% (monthly)", outr);
#endif

	    printf("\n");
	    exit(0);
	}

	limit[which] = rate;
    }
}
-- 
---------------------------
Avinash Chopde      home :   508 663 0471     office : 617 246 1776x5582
avinash@contex.com     (...!uunet!contex!avinash)