improvements to this php function to calculate monthly returns from a set of price data I have made this function that should take a set of price data and calculate a set of monthly returns. The idea is that is should be able to cope with irregular time intervals.
For example I have daily data for the S&amp;P 500 but many hedge funds only release price data monthly.

Here is what I have come up with:

//Turns a price series into an array of monthly returns. 
function monthlyReturns($dates, $prices, $startDate = "", $endDate = ""){
if ($endDate == "") $endDate = time();

if (count($dates) != count($prices)){
die("The dates array must be the same length as the prices array");
}  
$i = 0; $firstPass = false;
foreach ($dates as $date){
  $d = strtotime($date);
  if ($startDate == "" || ($d &gt;= $startDate &amp;&amp; $d &lt;= $endDate )) :
    $p = $prices[$i];
    $month = date('M',$d);
    $year = date('Y',$d);

    if ($i == 0 || $firstPass == false){
      $startPrice = $p;
      $lastMonth = $month;
    }

    if ($month != $lastMonth &amp;&amp; $i != 0){
      $returnTable['date'][] = $dates[$i-1];
      $returnTable['return'][] = $p / $startPrice - 1;
      $startPrice = $p;
    }
    $lastMonth = $month;
    $firstPass = true;
  endif;
  $i++;
  } 
  return $returnTable;
}


I noticed that for a fund with only monthly data the returns are off by a month. Can anyone see any major flaws with this function that could explain this or a solution to solve it.

For info, the monthly returns for hedge funds often come near the end of the month. E.g.

date       | Price
-----------------
2013-01-29 | 25.69
2013-02-28 | 27.62
2013-03-30 | 26.53
2013-04-29 | 28.45
If there are now performance constraint I always prefere a readable solution over a compact one. So I would go with the following: (assuming $prices is sorted and there are no gaps in the months)

&lt;?
date_default_timezone_set('Europe/Berlin');

//Put the data into a assoc array. If necessary I would maybe create a separate
//method to create this input or put it also to extractMonthlyFinalPrice()
$prices=array(); 
$prices['2013-01-29']='25.69';
$prices['2013-02-28']='27.62';
$prices['2013-03-02']='13.53';
$prices['2013-03-07']='16.53';
$prices['2013-03-30']='26.53';
$prices['2013-03-30']='26.53';
$prices['2013-04-29']='28.45';

$monthlyFinalPrice=extractMonthlyFinalPrice($prices);
/*
array(4) {
  ["2013-01"]=&gt; string(5) "25.69"
  ["2013-02"]=&gt; string(5) "27.62"
  ["2013-03"]=&gt; string(5) "26.53"
  ["2013-04"]=&gt; string(5) "28.45"
}
*/

$monthlyReturns=calculateMonthlyReturn($monthlyFinalPrice,new DateTime("2013-02"));
/*
array(3) {
  ["2013-02"]=&gt;  float(1.93)  //with '2013-01' as start
  ["2013-03"]=&gt;  float(-1.09)
  ["2013-04"]=&gt;  float(1.92)
*/

function extractMonthlyFinalPrice($prices)
{
    $monthlyPrice=array();
    foreach ($prices as $date=&gt;$price)
    {
        $month=substr($date,0,7);
        $monthlyPrice[$month]=$price; // just overwrite existing value and use the last
    }
    return $monthlyPrice;
}

//Be careful with timestamps. Think about using DateTime.
function calculateMonthlyReturn($monthlyFinalPrice, DateTime $startDate=null, DateTime $endDate=null)
{
    if ($endDate==null) $endDate=new DateTime();
    $monthlyReturns=array();
    $lastMonthPrice=null;
    foreach ($monthlyFinalPrice as $month=&gt;$price)
    {
        $date=new DateTime($month);
        $inRange = $date&gt;=$startDate &amp;&amp; $date &lt;=$endDate;
        if ($lastMonthPrice!=null &amp;&amp; $inRange)
        {
            $monthlyReturns[$month]=$price-$lastMonthPrice;
        }
            //assuming that there are no gaps in the list. 
            //Otherwise some date magic is required.
        $lastMonthPrice=$price; 
    }
    return $monthlyReturns;
}


As you see, there is no much "logic" left. I get rid of a bunch of ifs and could split the bigger problem in two smaller own. Now you could write tests independently.