DCF法の計算式をPHPで書いてみる

※以前別の場所で書いた文章を備忘的に書き記しておきます。

【投稿年月日】2008-04-23 【ジャンル】PHP/MySQL | 提供サービス

 「DCF法の計算式」というツールを作成したのですが、肝心のDCF法に対する理解が不足していたこともあり、PHPでプログラムを組む前にお勉強をしました。と言うわけで、復習を兼ねて(具体的な思考の道筋を)備忘的に書き残しておくことにします。

 まずはDCF法について確認。
 DCF法(ディスカウント・キャッシュフロー法)。
 収益還元法の一つで、資産価値を収益面から見る手法。将来見込まれる純収益(キャッシュフロー・現金収支)の合計額を、現在の値打ちに引き戻した(=還元した)もの。
 投資期間内に得られる純収益の合計額と、投資期間終了時における見込み売却価格を基にして算出する。
 これだけでは分かりにくいので、DCF法の基本式を見ることにします。

DCF法の計算式

 次に基本式を以下の2つに分けて考えます。なお、両者共に式の分母に投資収益率r%を持ってきて、現在の値打ちに換算しています。
  1. n年間のCf(キャッシュフロー)の合計額 … *純収益の年額の合計
  2. Rv(残存価値) … *投資期間終了時の見込み売却価格
DCF法の計算式2
 上記1.と2.の合計がPv(現在価値)、すなわちDCF法における資産価値となります。

 具体例に即して単純化すると、例えば年間100万円の純収益が生じるマンションを5年後に1,000万円で売却するケースでは、以下の2つの合計額が資産価値算出のベースとなります。
  1. 5年間の純収益の合計額 … 100万円×5年=500万円
  2. 投資期間終了時の見込み売却価格 … 1,000万円
 上記1.と2.を投資収益率を使って現在の値打ちに修正すれば、DCF法における資産価値を算出することができます。

 DCF法について理解が深まったので、最後にプログラムの方向性を決めます。
  1. (r+1)のn乗を計算するためにpow()を使う。
  2. ①については、for()で1年目からn年目までループ処理をする。
  3. ②については、n年目だけ計算処理する。
 以下、DCF法でPv(現在価値)を求めるPHPのコード。とてもシンプルです。
<?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;
 }
?>

 これだけというのもなんなので、せっかくだからIRR(*内部収益率)も求めることにします。
 IRR(Internal Rate of Return:内部収益率)。
 資産価値と投資額が等しくなるような投資収益率。
 要するに投資における損益分岐点的な指標かと。
 どうやって計算するのか見当がつかなかったので調べました。エクセルにIRR関数があったのでヘルプ等を確認したところ、再帰的に計算していることが分かりました。なるほど、なるほど。まさにプログラム向けの計算です。

 計算の道筋が分かれば後は一本道。プログラムの方向性を決めます。
  1. 上記PHPのコードをベースにして再帰的に処理する。
  2. 再帰処理は負担がかかるので、1.のループは30回以内とする。
  3. 近似値を求めるために、資産価値と投資額の比率を可能な限り1に近づける。
  4. 精度の高さよりも処理速度を優先して、3.の誤差率を0.001に設定する。
 以下、DCF法でIRR(*内部収益率)を求めるPHPのコード。結構シンプルです。
<?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の算出は難しいかなと思っていましたが、プログラムの方向性さえ決まればすぐできました。ちなみに実際のコードは、ループ回数を減らすために、より効率的にプログラムを組んでいます。

 ところで、「DCF法の計算式」で苦労したのが、桁数が大きい数値の扱いです。9999億まではOKなのですが、1兆を超えると「1E+012」のようになってしまい、計算がうまくできなくなります。
 いろいろ試してみましたが、結局うまいやり方は見つかりませんでした。仕方がないので、数値は全てnumber_format()を通すことで無理やり対応しましたが、釈然としないものが残りました。まあ、いいんですけど。。

EDIUNET | PHP/MySQL | 独り言 | 提供サービス | JavaScript