DCF法の計算式をPHPで書いてみる
※以前別の場所で書いた文章を備忘的に書き記しておきます。
【投稿年月日】2008-04-23 【ジャンル】PHP/MySQL | 提供サービス
「DCF法の計算式」というツールを作成したのですが、肝心のDCF法に対する理解が不足していたこともあり、PHPでプログラムを組む前にお勉強をしました。と言うわけで、復習を兼ねて(具体的な思考の道筋を)備忘的に書き残しておくことにします。まずはDCF法について確認。
DCF法(ディスカウント・キャッシュフロー法)。
収益還元法の一つで、資産価値を収益面から見る手法。将来見込まれる純収益(キャッシュフロー・現金収支)の合計額を、現在の値打ちに引き戻した(=還元した)もの。
投資期間内に得られる純収益の合計額と、投資期間終了時における見込み売却価格を基にして算出する。
これだけでは分かりにくいので、DCF法の基本式を見ることにします。収益還元法の一つで、資産価値を収益面から見る手法。将来見込まれる純収益(キャッシュフロー・現金収支)の合計額を、現在の値打ちに引き戻した(=還元した)もの。
投資期間内に得られる純収益の合計額と、投資期間終了時における見込み売却価格を基にして算出する。
- Pv … 現在価値(*資産価値)
- r … 投資収益率(*割引率)
- n … 投資期間(*年数)
- Cf … キャッシュフロー(*純収益の年額)
- Rv … 残存価値(*投資期間終了時の見込み売却価格)
次に基本式を以下の2つに分けて考えます。なお、両者共に式の分母に投資収益率r%を持ってきて、現在の値打ちに換算しています。
- n年間のCf(キャッシュフロー)の合計額 … *純収益の年額の合計
- Rv(残存価値) … *投資期間終了時の見込み売却価格
上記1.と2.の合計がPv(現在価値)、すなわちDCF法における資産価値となります。
具体例に即して単純化すると、例えば年間100万円の純収益が生じるマンションを5年後に1,000万円で売却するケースでは、以下の2つの合計額が資産価値算出のベースとなります。
- 5年間の純収益の合計額 … 100万円×5年=500万円
- 投資期間終了時の見込み売却価格 … 1,000万円
DCF法について理解が深まったので、最後にプログラムの方向性を決めます。
- (r+1)のn乗を計算するためにpow()を使う。
- ①については、for()で1年目からn年目までループ処理をする。
- ②については、n年目だけ計算処理する。
<?php
$in = 100; //年間総収入
$co = 0.2; //支出割合
$rv = 1000; //残存価値
$r = 0.05; //投資収益率(*割引率)
$n = 10; //投資期間
echo calc($in, $co, $rv, $r, $n);
function calc($in='', $co='', $rv='', $r='', $n='') {
$cf = $in*(1-$co); //キャッシュフロー(純収益)
$pv = 0; //資産価値
for($i=1; $i<=$n; $i++) {
$val = ($i!=$n) ? $cf : $cf+$rv;
$pv += $val/pow($r+1, $i);
}
return $pv;
}
?>
$in = 100; //年間総収入
$co = 0.2; //支出割合
$rv = 1000; //残存価値
$r = 0.05; //投資収益率(*割引率)
$n = 10; //投資期間
echo calc($in, $co, $rv, $r, $n);
function calc($in='', $co='', $rv='', $r='', $n='') {
$cf = $in*(1-$co); //キャッシュフロー(純収益)
$pv = 0; //資産価値
for($i=1; $i<=$n; $i++) {
$val = ($i!=$n) ? $cf : $cf+$rv;
$pv += $val/pow($r+1, $i);
}
return $pv;
}
?>
これだけというのもなんなので、せっかくだからIRR(*内部収益率)も求めることにします。
IRR(Internal Rate of Return:内部収益率)。
資産価値と投資額が等しくなるような投資収益率。
要するに投資における損益分岐点的な指標かと。資産価値と投資額が等しくなるような投資収益率。
どうやって計算するのか見当がつかなかったので調べました。エクセルにIRR関数があったのでヘルプ等を確認したところ、再帰的に計算していることが分かりました。なるほど、なるほど。まさにプログラム向けの計算です。
計算の道筋が分かれば後は一本道。プログラムの方向性を決めます。
- 上記PHPのコードをベースにして再帰的に処理する。
- 再帰処理は負担がかかるので、1.のループは30回以内とする。
- 近似値を求めるために、資産価値と投資額の比率を可能な限り1に近づける。
- 精度の高さよりも処理速度を優先して、3.の誤差率を0.001に設定する。
<?php
$in = 100; //年間総収入
$co = 0.2; //支出割合
$iv = 1500; //投資額
$rv = 1000; //残存価値
$r = 0.05; //投資収益率(*割引率)
$n = 10; //投資期間
echo irr($in, $co, $rv, $r, $n, $iv);
function irr($in='', $co='', $rv='', $r='', $n='', $iv='', $ii=0) {
$cf = $in*(1-$co); //純収支(キャッシュフロー)
$pv = 0; //資産価値
for($i=1; $i<=$n; $i++) {
$val = ($i!=$n) ? $cf : $cf+$rv;
$pv += $val/pow($r+1, $i);
}
$per = $pv/$iv; //資産価値と投資額の比率
/*
print_r($ii.' [$r]: '.$r.' [$per]: '.$per.'<br />'); //デバッグ用
*/
switch(TRUE) {
case ($per>0.999 && $per<1.001) : //誤差率0.001ならばループを脱出
return round($r, 3);
case ($ii==30) : //ループ回数が30回になったらループを脱出
return "Error";
default : //$iiをカウントアップし、$rを1に近づけた上で、ユーザー関数irr()をループ
$ii++;
return irr($in, $co, $rv, $r*$per, $n, $iv, $ii);
}
}
?>
IRRの算出は難しいかなと思っていましたが、プログラムの方向性さえ決まればすぐできました。ちなみに実際のコードは、ループ回数を減らすために、より効率的にプログラムを組んでいます。$in = 100; //年間総収入
$co = 0.2; //支出割合
$iv = 1500; //投資額
$rv = 1000; //残存価値
$r = 0.05; //投資収益率(*割引率)
$n = 10; //投資期間
echo irr($in, $co, $rv, $r, $n, $iv);
function irr($in='', $co='', $rv='', $r='', $n='', $iv='', $ii=0) {
$cf = $in*(1-$co); //純収支(キャッシュフロー)
$pv = 0; //資産価値
for($i=1; $i<=$n; $i++) {
$val = ($i!=$n) ? $cf : $cf+$rv;
$pv += $val/pow($r+1, $i);
}
$per = $pv/$iv; //資産価値と投資額の比率
/*
print_r($ii.' [$r]: '.$r.' [$per]: '.$per.'<br />'); //デバッグ用
*/
switch(TRUE) {
case ($per>0.999 && $per<1.001) : //誤差率0.001ならばループを脱出
return round($r, 3);
case ($ii==30) : //ループ回数が30回になったらループを脱出
return "Error";
default : //$iiをカウントアップし、$rを1に近づけた上で、ユーザー関数irr()をループ
$ii++;
return irr($in, $co, $rv, $r*$per, $n, $iv, $ii);
}
}
?>
ところで、「DCF法の計算式」で苦労したのが、桁数が大きい数値の扱いです。9999億まではOKなのですが、1兆を超えると「1E+012」のようになってしまい、計算がうまくできなくなります。
いろいろ試してみましたが、結局うまいやり方は見つかりませんでした。仕方がないので、数値は全てnumber_format()を通すことで無理やり対応しましたが、釈然としないものが残りました。まあ、いいんですけど。。
EDIUNET | PHP/MySQL | 独り言 | 提供サービス | JavaScript