I wrote my own modified dietz return function to calculate the return on a portfolio with external cash movements. It seems to work correctly when compared to an excel solution but I have come accross a case where the result is wrong. My function returns 0.23% when the correct result is 0.30%.
Here is the function:
//Calculates the Modified-Dietz return given arrays of MIMO.
//MIMO is an associative array of dates to values
function modDietz($startDate, $endDate, $BMV, $EMV, $MIMO){
if( strtotime($startDate) !== false ){
//Date was passed as a string; convert it to php date
$startDate = strtotime($startDate);
}
if( strtotime($endDate) !== false ){
//Date was passed as a string; convert it to php date
$endDate = strtotime($endDate);
}
//Convert seconds to days
$CD = ($startDate - $endDate)/(60*60*24);
$i = 0;
$SumWiFi = 0;
$F = 0;
foreach ($MIMO as $date=>$Fi){
if( strtotime($date) !== false ){
//Date was passed as a string; convert it to php date
$date = strtotime($date);
}
//Only take into account the MIMO if it falls between the start and end dates.
if ($date >= $startDate && $date <= $endDate){
$Di = ($date - $startDate) / (60*60*24);
$Wi = ($CD - $Di) / $CD;
$SumWiFi += $Wi * $Fi;
$F += $Fi;
}
}
if ($BMV + $SumWiFi != 0) {
return ($EMV - $BMV - $F)/($BMV + $SumWiFi);
} else {
return 0;
}
}
And here is the data I fed it which is giving the wrong result:
Dates:
Array
(
[0] => 2013-04-30
[1] => 2013-03-31
)
Values:
Array
(
[2013-03-31] => 4990430.0
[2013-04-30] => 5991710.1
)
MIMO:
Array
(
[2013-04-19] => -600.0
[2013-04-23] => 1000000.0
[2013-04-29] => -13750
)
This is the line that produces the result:
$mdReturn = modDietz($dates[1], $dates[0], $values[$dates[1]], $values[$dates[0]], $mimo);
I have a feeling the Money-Out on the 29th might be the cause - I'm not too sure of the best way to handle the dates in this function. At the moment I just use string-to-date on strings passed as format yyyy-mm-dd
as shown in the above arrays.
Any idea where I'm going wrong here?
I think Mark Baker is right, try this:
function modDietz(DateTime $startDate, DateTime $endDate, $BMV, $EMV, $MIMO){
$CD = $startDate->diff($endDate);
$SumWiFi = 0;
$F = 0;
foreach ($MIMO as $date=>$Fi){
$date = new DateTime($date);
//Only take into account the MIMO if it falls between the start and end dates.
if ($date >= $startDate && $date <= $endDate){
$Di = $date->diff($startDate);
$Wi = ($CD->d - $Di->d) / $CD->d;
$SumWiFi += $Wi * $Fi;
$F += $Fi;
}
}
if ($BMV + $SumWiFi != 0) {
return ($EMV - $BMV - $F)/($BMV + $SumWiFi);
} else {
return 0;
}
}
$mimo = array(
'2013-04-19' => -600.0,
'2013-04-23' => 1000000.0,
'2013-04-29' => -13750,
);
$startDate = new DateTime('2013-03-31');
$endDate = new DateTime('2013-04-30');
$result = modDietz($startDate, $endDate, 4990430.0, 5991710.1, $mimo);
// 0.0029925034725645