Advertisement
Guest User

Untitled

a guest
Aug 12th, 2010
236
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 58.21 KB | None | 0 0
  1. <?php
  2.     /**
  3.      * EGM Mathematical Finance class.
  4.      *
  5.      * Financial functions with the Excel function names and
  6.      * parameter order.
  7.      *
  8.      * @version   $Id: financial_class.php,v 1.0.6 2004-06-30 13:20:56-05 egarcia Exp $
  9.      * @author    Enrique García M. <[email protected]>
  10.      * @copyright (c) 2003-2004 Enrique García M.
  11.      * @since     Saturday, January 7, 2003
  12.      **/
  13.    
  14.     /***************************************************************************
  15.      *
  16.      *   This program is free software; you can redistribute it and/or modify
  17.      *   it under the terms of the GNU General Public License as published by
  18.      *   the Free Software Foundation; either version 2 of the License, or
  19.      *   (at your option) any later version.
  20.      *
  21.      ***************************************************************************/
  22.    
  23.     /**
  24.      * Constants to define the accuracy in numeric aproximations, and the max
  25.      * iterations to solve
  26.      */
  27.     define('FINANCIAL_ACCURACY', 1.0e-6);
  28.     define('FINANCIAL_MAX_ITERATIONS', 100);
  29.    
  30.     define('FINANCIAL_SECS_PER_DAY', 24 * 60 * 60);
  31.     define('FINANCIAL_HALF_SEC', 0.5 / FINANCIAL_SECS_PER_DAY);
  32.    
  33.     /**
  34.      * Constants for the day-counting
  35.      * rules are mandated by NASD Uniform Practice Code, Section 46, and MSRB Rule G-33,
  36.      * described in Mayle (1993, 17?23, A3?A15). See Public Securities Association (1990)
  37.      * for other rules applicable to state and municipal bonds.
  38.      *
  39.      * base 0: BASIS_MSRB_30_360
  40.      *         MSRB/NSAD 30/360 daycount method
  41.      *         see MSRB Rule G-33
  42.      *         http://www.msrb.org/msrb1/rules/ruleg33.htm
  43.      *         http://www.msrb.org/msrb1/rules/interpg33.htm
  44.      *         Number of days = (Y2 - Y1) 360 + (M2 - M1) 30 + (D2 - D1)
  45.      *         The variables "Yl," "M1," and "D1" are defined as the year, month, and day, respectively,
  46.      *         of the date on which the computation period begins (June 15, 1982, in your example),
  47.      *         and "Y2,a" "M2," and "D2" as the year, month, and day of the date on which the
  48.      *         computation period ends (July 1, 1982, in your example).
  49.      *         For purposes of this formula, if the symbol "D2" has a value of "31," and the symbol "D1"
  50.      *         has a value of "30" or "31," the value of the symbol "D2" shall be changed to "30."
  51.      *         If the symbol "D1" has a value of "31," the value of the symbol "D1" shall be changed to
  52.      *         "30." For purposes of this rule time periods shall be computed to include the day
  53.      *         specified in the rule for the beginning of the period but not to include the day
  54.      *         specified for the end of the period.
  55.      *
  56.      * base 1: BASIS_ACTACT
  57.      *         Actual/Actual daycount method
  58.      *         date adjustment: no change
  59.      *         date difference: serial delta (# of days)
  60.      *
  61.      * base 2: BASIS_ACT_360
  62.      *         Actual/360 daycount method
  63.      *         date adjustment: no change
  64.      *         date difference: serial delta
  65.      *         360/freq for length of coupon period
  66.      *
  67.      * base 3: BASIS_ACT_365
  68.      *         Actual/365 daycount method (short term and Canadian bonds only)
  69.      *         date adjustment: no change
  70.      *         date difference: serial delta
  71.      *         365/freq for length of coupon period, (with decimal answer)
  72.      * base 4: BASIS_30E_360
  73.      *         date adjustment: from_date is changed from 31st to 30th
  74.      *                          to_date is changed from 31st to 30th
  75.      *         date difference: each month 30 days, within a month serial delta
  76.      * base 5: BASIS_30Ep_360
  77.      *         date adjustment: from_date is changed from 31st to 30th
  78.      *                          to_date is changed from 31st to 1st of following month
  79.      *         date difference: each month 30 days, within a month serial delta
  80.      */
  81.     define('FINANCIAL_BASIS_MSRB_30_360', 0);
  82.     define('FINANCIAL_BASIS_ACT_ACT', 1);
  83.     define('FINANCIAL_BASIS_ACT_360', 2);
  84.     define('FINANCIAL_BASIS_ACT_365', 3);
  85.     define('FINANCIAL_BASIS_30E_360', 4);
  86.     define('FINANCIAL_BASIS_30Ep_360', 5);
  87.    
  88.     class Financial
  89.     {
  90.         function Financial()
  91.         {
  92.             // forces the precision for calculations
  93.             ini_set('precision', '14');
  94.         }
  95.        
  96.         /**
  97.         * DATEADD
  98.         * Returns a new Unix timestamp value based on adding an interval to the specified date.
  99.         * @param  string  $datepart is the parameter that specifies on which part of the date to return a new value.
  100.         * @param  integer $number   is the value used to increment datepart. If you specify a value that is not an integer, the fractional part of the value is discarded.
  101.         * @param  integer $date     a Unix timestamp value.    
  102.         * @return integer a Unix timestamp.
  103.         */
  104.         function DATEADD($datepart, $number, $date)
  105.         {
  106.             $number = intval($number);
  107.             switch (strtolower($datepart)) {
  108.                 case 'yy':
  109.                 case 'yyyy':
  110.                 case 'year':
  111.                     $d = getdate($date);
  112.                     $d['year'] += $number;
  113.                     if (($d['mday'] == 29) && ($d['mon'] == 2) && (date('L', mktime(0, 0, 0, 1, 1, $d['year'])) == 0)) $d['mday'] = 28;
  114.                     return mktime($d['hours'], $d['minutes'], $d['seconds'], $d['mon'], $d['mday'], $d['year']);
  115.                     break;
  116.                 case 'm':
  117.                 case 'mm':
  118.                 case 'month':
  119.                     $d = getdate($date);
  120.                     $d['mon'] += $number;
  121.                     while($d['mon'] > 12) {
  122.                         $d['mon'] -= 12;
  123.                         $d['year']++;
  124.                     }
  125.                     while($d['mon'] < 1) {
  126.                         $d['mon'] += 12;
  127.                         $d['year']--;
  128.                     }
  129.                     $l = date('t', mktime(0,0,0,$d['mon'],1,$d['year']));
  130.                     if ($d['mday'] > $l) $d['mday'] = $l;
  131.                     return mktime($d['hours'], $d['minutes'], $d['seconds'], $d['mon'], $d['mday'], $d['year']);
  132.                     break;
  133.                 case 'd':
  134.                 case 'dd':
  135.                 case 'day':
  136.                     return ($date + $number * 86400);
  137.                     break;
  138.                 default:
  139.                     die("Unsupported operation");
  140.             }
  141.         }
  142.        
  143.         /**
  144.         * DATEDIFF
  145.         * Returns the number of date and time boundaries crossed between two specified dates.
  146.         * @param  string  $datepart  is the parameter that specifies on which part of the date to calculate the difference.
  147.         * @param  integer $startdate is the beginning date (Unix timestamp) for the calculation.
  148.         * @param  integer $enddate   is the ending date (Unix timestamp) for the calculation.    
  149.         * @return integer the number between the two dates.
  150.         */
  151.         function DATEDIFF($datepart, $startdate, $enddate)
  152.         {
  153.             switch (strtolower($datepart)) {
  154.                 case 'yy':
  155.                 case 'yyyy':
  156.                 case 'year':
  157.                     $di = getdate($startdate);
  158.                     $df = getdate($enddate);
  159.                     return $df['year'] - $di['year'];
  160.                     break;
  161.                 case 'q':
  162.                 case 'qq':
  163.                 case 'quarter':
  164.                     die("Unsupported operation");
  165.                     break;
  166.                 case 'n':
  167.                 case 'mi':
  168.                 case 'minute':
  169.                     return ceil(($enddate - $startdate) / 60);
  170.                     break;
  171.                 case 'hh':
  172.                 case 'hour':
  173.                     return ceil(($enddate - $startdate) / 3600);
  174.                     break;
  175.                 case 'd':
  176.                 case 'dd':
  177.                 case 'day':
  178.                     return ceil(($enddate - $startdate) / 86400);
  179.                     break;
  180.                 case 'wk':
  181.                 case 'ww':
  182.                 case 'week':
  183.                     return ceil(($enddate - $startdate) / 604800);
  184.                     break;
  185.                 case 'm':
  186.                 case 'mm':
  187.                 case 'month':
  188.                     $di = getdate($startdate);
  189.                     $df = getdate($enddate);
  190.                     return ($df['year'] - $di['year']) * 12 + ($df['mon'] - $di['mon']);
  191.                     break;
  192.                 default:
  193.                     die("Unsupported operation");
  194.             }
  195.         }
  196.        
  197.         /**
  198.         * Determine if the basis is valid
  199.         * @param  integer $basis
  200.         * @return bool
  201.         */
  202.         function _is_valid_basis($basis)
  203.         {
  204.             return (($basis >= FINANCIAL_BASIS_MSRB_30_360) && ($basis <= FINANCIAL_BASIS_30Ep_360));
  205.         }
  206.        
  207.         /**
  208.         * Determine if the frequency is valid
  209.         * @param  integer $frequency
  210.         * @return bool
  211.         */
  212.         function _is_valid_frequency($frequency)
  213.         {
  214.             return (($frequency == 1) || ($frequency == 2) || ($frequency == 4));
  215.         }
  216.        
  217.         /**
  218.         * DAYS360
  219.         * Returns the number of days between two dates based on a 360-day year
  220.         * (twelve 30-day months), which is used in some accounting calculations.
  221.         * Use this function to help compute payments if your accounting system
  222.         * is based on twelve 30-day months.
  223.         * @param  integer $start_date is the beginning date (Unix timestamp) for the calculation.
  224.         * @param  integer $end_date   is the ending date (Unix timestamp) for the calculation.
  225.         * @param  bool    $method     is a logical value that specifies whether to use the U.S. or European method in the calculation.
  226.         * @return integer the number of days between two dates based on a 360-day year
  227.         */
  228.         function DAYS360($start_date, $end_date, $method = false)
  229.         {
  230.             if ($method) {
  231.                 return $this->Thirty360USdayCount($start_date, $end_date);
  232.             } else {
  233.                 return $this->Thirty360EUdayCount($start_date, $end_date);
  234.             }
  235.         }
  236.        
  237.         /**
  238.         * Thirty360USdayCount
  239.         * Returns the number of days between two dates based on a 360-day year
  240.         * (twelve 30-day months) using the US method.
  241.         * @param  integer $startdate is the beginning date (Unix timestamp) for the calculation.
  242.         * @param  integer $enddate   is the ending date (Unix timestamp) for the calculation.
  243.         * @return integer the number of days between two dates
  244.         */
  245.         function Thirty360USdayCount($startdate, $enddate)
  246.         {
  247.             $d1 = getdate($startdate);
  248.             $d2 = getdate($enddate);
  249.             $dd1 = $d1['mday']; $mm1 = $d1['mon']; $yy1 = $d1['year'];
  250.             $dd2 = $d2['mday']; $mm2 = $d2['mon']; $yy2 = $d2['year'];
  251.            
  252.             if ($dd2 == 31 && $dd1 < 30) { $dd2 = 1; $mm2++; }
  253.            
  254.             return 360 * ($yy2 - $yy1) + 30 * ($mm2 - $mm1 - 1) + max(0, 30 - $dd1) + min(30, $dd2);
  255.         }
  256.        
  257.         /**
  258.         * Thirty360USyearFraction
  259.         * Returns the period between two dates as a fraction of year.
  260.         * @param  integer $startdate is the beginning date (Unix timestamp) for the calculation.
  261.         * @param  integer $enddate   is the ending date (Unix timestamp) for the calculation.
  262.         * @return float   the fraction of years between two dates
  263.         */
  264.         function Thirty360USyearFraction($startdate, $enddate)
  265.         {
  266.             return $this->Thirty360USdayCount($startdate, $enddate) / 360.0;
  267.         }
  268.        
  269.         /**
  270.         * Thirty360EUdayCount
  271.         * Returns the number of days between two dates based on a 360-day year
  272.         * (twelve 30-day months) using the EUROPEAN method.
  273.         * @param  integer $startdate is the beginning date (Unix timestamp) for the calculation.
  274.         * @param  integer $enddate   is the ending date (Unix timestamp) for the calculation.
  275.         * @return integer the number of days between two dates
  276.         */
  277.         function Thirty360EUdayCount($startdate, $enddate)
  278.         {
  279.             $d1 = getdate($startdate);
  280.             $d2 = getdate($enddate);
  281.             $dd1 = $d1['mday']; $mm1 = $d1['mon']; $yy1 = $d1['year'];
  282.             $dd2 = $d2['mday']; $mm2 = $d2['mon']; $yy2 = $d2['year'];
  283.            
  284.             return 360 * ($yy2 - $yy1) + 30 * ($mm2 - $mm1 - 1) + max(0, 30 - $dd1) + min(30, $dd2);
  285.         }
  286.        
  287.         /**
  288.         * Thirty360EUyearFraction
  289.         * Returns the period between two dates as a fraction of year.
  290.         * @param  integer $startdate is the beginning date (Unix timestamp) for the calculation.
  291.         * @param  integer $enddate   is the ending date (Unix timestamp) for the calculation.
  292.         * @return float   the fraction of years between two dates
  293.         */
  294.         function Thirty360EUyearFraction($startdate, $enddate)
  295.         {
  296.             return $this->Thirty360EUdayCount($startdate, $enddate) / 360.0;
  297.         }
  298.        
  299.         /**
  300.         * ActualActualdayCount
  301.         * Returns the number of days between two dates.
  302.         * @param  integer $startdate is the beginning date (Unix timestamp) for the calculation.
  303.         * @param  integer $enddate   is the ending date (Unix timestamp) for the calculation.
  304.         * @return integer the number of days between two dates
  305.         */
  306.         function ActualActualdayCount($startdate, $enddate)
  307.         {
  308.             return $this->DATEDIFF('day', $startdate, $enddate);
  309.         }
  310.        
  311.         /**
  312.         * ActualActualyearFraction
  313.         * Returns the period between two dates as a fraction of year.
  314.         * @param  integer $startdate is the beginning date (Unix timestamp) for the calculation.
  315.         * @param  integer $enddate   is the ending date (Unix timestamp) for the calculation.
  316.         * @param  date    $refPeriodStart is the reference beginning date (Unix timestamp) for the inner calculation.
  317.         * @param  date    $refPeriodEnd   is the reference ending date (Unix timestamp) for the inner calculation.
  318.         * @return float   the fraction of years between two dates
  319.         */
  320.         function ActualActualyearFraction($startdate, $enddate, $refPeriodStart = null, $refPeriodEnd = null)
  321.         {
  322.             $t = time();
  323.             if (!isset($refPeriodStart)) $refPeriodStart = $startdate;
  324.             if (!isset($refPeriodEnd)) $refPeriodEnd = $enddate;
  325.            
  326.             /*
  327.             if ($this->DATEDIFF('day', $startdate, $enddate) == 0) return 0.0;
  328.             $d1 = getdate($startdate);
  329.             $d2 = getdate($enddate);
  330.             $dib1 = ((date('L', $startdate) == 1) ? 366.0 : 365.0);
  331.             $dib2 = ((date('L', $enddate) == 1) ? 366.0 : 365.0);
  332.            
  333.             $sum = $d2['year'] - $d1['year'] - 1;
  334.             $sum += $this->ActualActualdayCount($startdate, mktime(0,0,0,1,1,$d1['year'] + 1)) / $dib1;
  335.             $sum += $this->ActualActualdayCount(mktime(0,0,0,1,1,$d2['year']), $enddate) / $dib2;
  336.             return $sum;
  337.             */
  338.            
  339.             if ($this->DATEDIFF('day', $startdate, $enddate) == 0) return 0.0;
  340.            
  341.             if ($startdate > $enddate) die("Invalid dates");
  342.             if (!($refPeriodStart != $t) && ($refPeriodEnd != $t) &&
  343.                 ($refPeriodEnd > $refPeriodStart) && ($refPeriodEnd > $startdate)) die("Invalid reference period");
  344.                    
  345.             $months = intval(0.5 + 12 * $this->DATEDIFF('day', $refPeriodStart, $refPeriodEnd) / 365);
  346.             $period = $months / 12.0;
  347.             if($months == 0) die("number of months does not divide 12 exactly");
  348.             if ($enddate <= $refPeriodEnd) {
  349.                 // here refPeriodEnd is a future (notional?) payment date
  350.                 if ($startdate >= $refPeriodStart) {
  351.                     // here refPeriodStart is the last (maybe notional)
  352.                     // payment date.
  353.                     // refPeriodStart <= startdate <= enddate <= refPeriodEnd
  354.                     // [maybe the equality should be enforced, since
  355.                     // refPeriodStart < startdate <= enddate < refPeriodEnd
  356.                     // could give wrong results] ???
  357.                     return $period * $this->ActualActualdayCount($startdate, $enddate) /
  358.                         $this->ActualActualdayCount($refPeriodStart, $refPeriodEnd);
  359.                 } else {
  360.                     // here refPeriodStart is the next (maybe notional)
  361.                     // payment date and refPeriodEnd is the second next
  362.                     // (maybe notional) payment date.
  363.                     // startdate < refPeriodStart < refPeriodEnd
  364.                     // AND enddate <= refPeriodEnd
  365.                     // this case is long first coupon
  366.                     // the last notional payment date
  367.                     $previousRef = $this->DATEADD('month', -$months, $refPeriodStart);
  368.                     if ($enddate > $refPeriodStart)
  369.                         return $this->ActualActualyearFraction($startdate, $refPeriodStart, $previousRef,
  370.                                     $refPeriodStart) +
  371.                                 $this->ActualActualyearFraction($refPeriodStart, $enddate, $refPeriodStart,
  372.                                     $refPeriodEnd);
  373.                     else
  374.                         return $this->ActualActualyearFraction($startdate,$enddate,$previousRef,$refPeriodStart);
  375.                 }
  376.             } else {
  377.                 // here refPeriodEnd is the last (notional?) payment date
  378.                 // startdate < refPeriodEnd < enddate AND refPeriodStart < refPeriodEnd
  379.                 if ($refPeriodStart > $startdate) die("Invalid dates");
  380.                
  381.                 // now it is: refPeriodStart <= startdate < refPeriodEnd < enddate
  382.                
  383.                 // the part from startdate to refPeriodEnd
  384.                 $sum = $this->ActualActualyearFraction($startdate, $refPeriodEnd, $refPeriodStart, $refPeriodEnd);
  385.                
  386.                 // the part from refPeriodEnd to enddate
  387.                 // count how many regular periods are in [refPeriodEnd, enddate],
  388.                 // then add the remaining time
  389.                 $i = 0;
  390.                 do {
  391.                     $newRefStart = $this->DATEADD('month', $months * $i, $refPeriodEnd);
  392.                     $newRefEnd   = $this->DATEADD('month', $months * ($i + 1), $refPeriodEnd);
  393.                     if ($enddate < $newRefEnd) {
  394.                         break;
  395.                     } else {
  396.                         $sum += $period;
  397.                         $i++;
  398.                     }
  399.                 } while (true);
  400.                 $sum += $this->ActualActualyearFraction($newRefStart, $newRefEnd, $newRefStart, $newRefEnd);
  401.                 return $sum;
  402.             }
  403.         }
  404.        
  405.         /**
  406.         * Actual360dayCount
  407.         * Returns the number of days between two dates.
  408.         * @param  integer $startdate is the beginning date (Unix timestamp) for the calculation.
  409.         * @param  integer $enddate   is the ending date (Unix timestamp) for the calculation.
  410.         * @return integer the number of days between two dates
  411.         */
  412.         function Actual360dayCount($startdate, $enddate)
  413.         {
  414.             return $this->DATEDIFF('day', $startdate, $enddate);
  415.         }
  416.        
  417.         /**
  418.         * Actual360yearFraction
  419.         * Returns the period between two dates as a fraction of 360 days year.
  420.         * @param  integer $startdate is the beginning date (Unix timestamp) for the calculation.
  421.         * @param  integer $enddate   is the ending date (Unix timestamp) for the calculation.
  422.         * @return float   the fraction of years between two dates
  423.         */
  424.         function Actual360yearFraction($startdate, $enddate)
  425.         {
  426.             return $this->Actual360dayCount($startdate, $enddate) / 360.0;
  427.         }
  428.        
  429.         /**
  430.         * Actual365dayCount
  431.         * Returns the number of days between two dates.
  432.         * @param  integer $startdate is the beginning date (Unix timestamp) for the calculation.
  433.         * @param  integer $enddate   is the ending date (Unix timestamp) for the calculation.
  434.         * @return integer the number of days between two dates
  435.         */
  436.         function Actual365dayCount($startdate, $enddate)
  437.         {
  438.             return $this->DATEDIFF('day', $startdate, $enddate);
  439.         }
  440.        
  441.         /**
  442.         * Actual365yearFraction
  443.         * Returns the period between two dates as a fraction of 365 days year.
  444.         * @param  integer $startdate is the beginning date (Unix timestamp) for the calculation.
  445.         * @param  integer $enddate   is the ending date (Unix timestamp) for the calculation.
  446.         * @return float   the fraction of years between two dates
  447.         */
  448.         function Actual365yearFraction($startdate, $enddate)
  449.         {
  450.             return $this->Actual365dayCount($startdate, $enddate) / 365.0;
  451.         }
  452.        
  453.         /**
  454.         * DOLLARDE
  455.         * Converts a dollar price expressed as a fraction into a dollar
  456.         * price expressed as a decimal number. Use DOLLARDE to convert
  457.         * fractional dollar numbers, such as securities prices, to decimal
  458.         * numbers.
  459.         * @param  float   $fractional_dollar is a number expressed as a fraction.
  460.         * @param  integer $fraction          is the integer to use in the denominator of the fraction.
  461.         * @return float   dollar price expressed as a decimal number.
  462.         */
  463.         function DOLLARDE($fractional_dollar, $fraction)
  464.         {
  465.             $fraction = intval($fraction);
  466.             $integer = intval($fractional_dollar);
  467.             return $integer + 100 * ($fractional_dollar - $integer) / $fraction;
  468.         }
  469.        
  470.         /**
  471.         * DOLLARFR
  472.         * Converts a dollar price expressed as a decimal number into a
  473.         * dollar price expressed as a fraction. Use DOLLARFR to convert
  474.         * decimal numbers to fractional dollar numbers, such as securities
  475.         * prices.
  476.         * @param  float   $decimal_dollar is a decimal number.
  477.         * @param  integer $fraction       is the integer to use in the denominator of the fraction.
  478.         * @return float   dollar price expressed as a fraction.
  479.         */
  480.         function DOLLARFR($decimal_dollar, $fraction)
  481.         {
  482.             $fraction = intval($fraction);
  483.             $integer = intval($decimal_dollar);
  484.             return ($decimal_dollar - $integer) * $fraction / 100 + $integer;
  485.         }
  486.        
  487.         /**
  488.         * DDB
  489.         * Returns the depreciation of an asset for a specified period using
  490.         * the double-declining balance method or some other method you specify.
  491.         * @param  float   $cost    is the initial cost of the asset.
  492.         * @param  float   $salvage is the value at the end of the depreciation (sometimes called the salvage value of the asset).
  493.         * @param  integer $life    is the number of periods over which the asset is being depreciated (sometimes called the useful life of the asset).
  494.         * @param  integer $period  is the period for which you want to calculate the depreciation. Period must use the same units as life.
  495.         * @param  float   $factor  is the rate at which the balance declines. If factor is omitted, it is assumed to be 2 (the double-declining balance method).
  496.         * @return float   the depreciation of n periods.
  497.         */
  498.         function DDB($cost, $salvage, $life, $period, $factor = 2)
  499.         {
  500.             $x = 0;
  501.             $n = 0;
  502.             $life   = intval($life);
  503.             $period = intval($period);
  504.             while ($period > $n) {
  505.                 $x = $factor * $cost / $life;
  506.                 if (($cost - $x) < $salvage) $x = $cost- $salvage;
  507.                 if ($x < 0) $x = 0;
  508.                 $cost -= $x;
  509.                 $n++;
  510.             }
  511.             return $x;
  512.         }
  513.        
  514.         /**
  515.         * SLN
  516.         * Returns the straight-line depreciation of an asset for one period.
  517.         * @param  float   $cost    is the initial cost of the asset.
  518.         * @param  float   $salvage is the value at the end of the depreciation (sometimes called the salvage value of the asset).
  519.         * @param  integer $life    is the number of periods over which the asset is being depreciated (sometimes called the useful life of the asset).
  520.         * @return float   the depreciation allowance for each period.
  521.         */
  522.         function SLN($cost, $salvage, $life)
  523.         {
  524.             $sln = ($cost - $salvage) / $life;
  525.             return (is_finite($sln) ? $sln: null);
  526.         }
  527.        
  528.         /**
  529.         * SYD
  530.         * Returns the sum-of-years' digits depreciation of an asset for
  531.         * a specified period.
  532.         *
  533.         *        (cost - salvage) * (life - per + 1) * 2
  534.         * SYD = -----------------------------------------
  535.         *                  life * (1 + life)
  536.         *
  537.         * @param  float   $cost    is the initial cost of the asset.
  538.         * @param  float   $salvage is the value at the end of the depreciation (sometimes called the salvage value of the asset).
  539.         * @param  integer $life    is the number of periods over which the asset is depreciated (sometimes called the useful life of the asset).
  540.         * @param  integer $per     is the period and must use the same units as life.  
  541.         */
  542.         function SYD($cost, $salvage, $life, $per)
  543.         {
  544.             $life = intval($life);
  545.             $per  = intval($per);
  546.             $syd  = (($cost - $salvage) * ($life - $per + 1) * 2) / ($life * (1 + $life));
  547.             return (is_finite($syd) ? $syd: null);
  548.         }
  549.        
  550.         /**
  551.         * @param float $fWert
  552.         * @param float $fRest
  553.         * @param float $fDauer
  554.         * @param float $fPeriode
  555.         * @param float $fFaktor
  556.         * @return float
  557.         */
  558.         function _ScGetGDA($fWert, $fRest, $fDauer,$fPeriode, $fFaktor)
  559.         {
  560.             $fZins = $fFaktor / $fDauer;
  561.             if ($fZins >= 1.0) {
  562.                 $fZins = 1.0;
  563.                 if ($fPeriode == 1.0)
  564.                     $fAlterWert = $fWert;
  565.                 else
  566.                     $fAlterWert = 0.0;
  567.             } else
  568.                 $fAlterWert = $fWert * pow(1.0 - $fZins, $fPeriode - 1.0);
  569.            
  570.             $fNeuerWert = $fWert * pow(1.0 - $fZins, $fPeriode);
  571.            
  572.             if ($fNeuerWert < $fRest)
  573.                 $fGda = $fAlterWert - $fRest;
  574.             else
  575.                 $fGda = $fAlterWert - $fNeuerWert;
  576.            
  577.             if ($fGda < 0.0) $fGda = 0.0;
  578.            
  579.             return $fGda;
  580.         }
  581.        
  582.         /**
  583.         * @param  float $cost
  584.         * @param  float $salvage
  585.         * @param  float $life
  586.         * @param  float $life1
  587.         * @param  float $period
  588.         * @param  float $factor
  589.         * @return float
  590.         */
  591.         function _ScInterVDB($cost, $salvage, $life, $life1, $period, $factor)
  592.         {
  593.             $fVdb       = 0;
  594.             $fIntEnd    = ceil($period);
  595.             $nLoopEnd   = $fIntEnd;
  596.             $fRestwert  = $cost - $salvage;
  597.             $bNowLia    = false;
  598.            
  599.             $fLia = 0;
  600.             for ($i = 1; $i <= $nLoopEnd; $i++) {
  601.                 if (!$bNowLia) {
  602.                     $fGda = $this->_ScGetGDA($cost, $salvage, $life, $i, $factor);
  603.                     $fLia = $fRestwert / ($life1 - (float)($i - 1));
  604.                    
  605.                     if ($fLia > $fGda) {
  606.                         $fTerm   = $fLia;
  607.                         $bNowLia = true;
  608.                     } else {
  609.                         $fTerm      = $fGda;
  610.                         $fRestwert -= $fGda;
  611.                     }
  612.                 } else
  613.                     $fTerm = fLia;
  614.                    
  615.                 if ($i == $nLoopEnd)
  616.                     $fTerm *= ($period + 1.0 - $fIntEnd);
  617.                 $fVdb += $fTerm;
  618.             }
  619.             return $fVdb;
  620.         }
  621.    
  622.         /**
  623.         * VDB
  624.         * Returns the depreciation of an asset for any period you specify,
  625.         * including partial periods, using the double-declining balance method
  626.         * or some other method you specify. VDB stands for variable declining balance.
  627.         *
  628.         * @param  float   $cost         is the initial cost of the asset.
  629.         * @param  float   $salvage      is the value at the end of the depreciation (sometimes called the salvage value of the asset).
  630.         * @param  integer $life         is the number of periods over which the asset is depreciated (sometimes called the useful life of the asset).
  631.         * @param  integer $start_period is the starting period for which you want to calculate the depreciation. Start_period must use the same units as life.
  632.         * @param  integer $end_period   is the ending period for which you want to calculate the depreciation. End_period must use the same units as life.
  633.         * @param  float   $factor       is the rate at which the balance declines. If factor is omitted, it is assumed to be 2 (the double-declining balance method). Change factor if you do not want to use the double-declining balance method.
  634.         * @param  bool    $no_switch    is a logical value specifying whether to switch to straight-line depreciation when depreciation is greater than the declining balance calculation.
  635.         * @return float   the depreciation of an asset.
  636.         */
  637.         function VDB($cost, $salvage, $life, $start_period, $end_period, $factor = 2.0, $no_switch = false)
  638.         {
  639.             // pre-validations
  640.             if (($start_period < 0)
  641.                 || ($end_period < $start_period)
  642.                 || ($end_period > $life)
  643.                 || ($cost < 0) || ($salvage > $cost)
  644.                 || ($factor <= 0))
  645.                 return null;
  646.            
  647.             // this implementation is borrowed from OppenOffice 1.0,
  648.             // 'sc/source/core/tool/interpr2.cxx' with a small changes
  649.             // from me.
  650.             $fVdb = 0.0;
  651.             $fIntStart = floor($start_period);
  652.             $fIntEnd = ceil($end_period);
  653.            
  654.             if ($no_switch) {
  655.                 $nLoopStart = (int) $fIntStart;
  656.                 $nLoopEnd = (int) $fIntEnd;
  657.                
  658.                 for ($i = $nLoopStart + 1; $i <= $nLoopEnd; $i++) {
  659.                     $fTerm = $this->_ScGetGDA($cost, $salvage, $life, $i, $factor);
  660.                     if ($i == $nLoopStart + 1)
  661.                         $fTerm *= (min($end_period, $fIntStart + 1.0) - $start_period);
  662.                     elseif ($i == $nLoopEnd)
  663.                         $fTerm *= ($end_period + 1.0 - $fIntEnd);
  664.                     $fVdb += $fTerm;
  665.                 }
  666.             } else {
  667.                 $life1 = $life;
  668.                
  669.                 if ($start_period != $fIntStart)
  670.                     if ($factor > 1) {
  671.                         if ($start_period >= ($life / 2)) {
  672.                             $fPart        = $start_period - ($life / 2);
  673.                             $start_period = $life / 2;
  674.                             $end_period  -= $fPart;
  675.                             $life1       += 1;
  676.                         }
  677.                     }
  678.                
  679.                 $cost -= $this->_ScInterVDB($cost, $salvage, $life, $life1, $start_period, $factor);
  680.                 $fVdb = $this->_ScInterVDB($cost, $salvage, $life, $life - $start_period, $end_period - $start_period, $factor);
  681.             }
  682.            
  683.             return $fVdb;
  684.         }
  685.        
  686.         /**
  687.         * Present value interest factor
  688.         *
  689.         *                 nper
  690.         * PVIF = (1 + rate)
  691.         *
  692.         * @param  float   $rate is the interest rate per period.
  693.         * @param  integer $nper is the total number of periods.
  694.         * @return float  the present value interest factor
  695.         */
  696.         function _calculate_pvif ($rate, $nper)
  697.         {
  698.             return (pow(1 + $rate, $nper));
  699.         }
  700.        
  701.         /**
  702.         * Future value interest factor of annuities
  703.         *
  704.         *                   nper
  705.         *          (1 + rate)    - 1
  706.         * FVIFA = -------------------
  707.         *               rate
  708.         *
  709.         * @param  float   $rate is the interest rate per period.
  710.         * @param  integer $nper is the total number of periods.
  711.         * @return float  the present value interest factor of annuities
  712.         */
  713.         function _calculate_fvifa ($rate, $nper)
  714.         {
  715.             // Removable singularity at rate == 0
  716.             if ($rate == 0)
  717.                 return $nper;
  718.             else
  719.                 // FIXME: this sucks for very small rates
  720.                 return (pow(1 + $rate, $nper) - 1) / $rate;
  721.         }
  722.        
  723.         function _calculate_interest_part ($pv, $pmt, $rate, $per)
  724.         {
  725.             return -($pv * pow(1 + $rate, $per) * $rate +
  726.                 $pmt * (pow(1 + $rate, $per) - 1));
  727.         }
  728.        
  729.         function _calculate_pmt ($rate, $nper, $pv, $fv, $type)
  730.         {
  731.             // Calculate the PVIF and FVIFA
  732.             $pvif = $this->_calculate_pvif ($rate, $nper);
  733.             $fvifa = $this->_calculate_fvifa ($rate, $nper);
  734.            
  735.             return ((-$pv * $pvif - $fv ) / ((1.0 + $rate * $type) * $fvifa));
  736.         }
  737.        
  738.         /**
  739.         * PV
  740.         * Returns the present value of an investment. The present value is
  741.         * the total amount that a series of future payments is worth now.
  742.         * For example, when you borrow money, the loan amount is the present
  743.         * value to the lender.
  744.         *  
  745.         * If rate = 0:
  746.         * PV = -(FV + PMT * nper)
  747.         *
  748.         * Else
  749.         *                                 /              nper \
  750.         *                                 | 1 - (1 + rate)    |
  751.         *       PMT * (1 + rate * type) * | ----------------- | - FV
  752.         *                                 \        rate       /
  753.         * PV = ------------------------------------------------------
  754.         *                                nper
  755.         *                       (1 + rate)
  756.         *
  757.         * @param  float   $rate is the interest rate per period.
  758.         * @param  integer $nper is the total number of payment periods in an annuity.
  759.         * @param  float   $pmt  is the payment made each period and cannot change over the life of the annuity.
  760.         * @param  float   $fv   is the future value, or a cash balance you want to attain after the last payment is made.
  761.         * @param  integer $type is the number 0 or 1 and indicates when payments are due.
  762.         * @return float   the present value of an investment.
  763.         */
  764.         function PV($rate, $nper, $pmt, $fv = 0.0, $type = 0)
  765.         {
  766.             // Calculate the PVIF and FVIFA
  767.             $pvif  = $this->_calculate_pvif ($rate, $nper);
  768.             $fvifa = $this->_calculate_fvifa ($rate, $nper);
  769.            
  770.             if ($pvif == 0)
  771.                 return null;
  772.             $pv = ((-$fv - $pmt * (1.0 + $rate * $type) * $fvifa) / $pvif);
  773.             return (is_finite($pv) ? $pv: null);
  774.         }
  775.        
  776.         /**
  777.          * FV
  778.          * Returns the future value of an investment based on periodic,
  779.          * constant payments and a constant interest rate.
  780.          *
  781.          * For a more complete description of the arguments in FV, see the PV function.
  782.          *
  783.          * Rate: is the interest rate per period.
  784.          * Nper: is the total number of payment periods in an annuity.
  785.          * Pmt: is the payment made each period; it cannot change over the life of the
  786.          *  annuity. Typically, pmt contains principal and interest but no other fees
  787.          *  or taxes. If pmt is omitted, you must include the pv argument.
  788.          * Pv: is the present value, or the lump-sum amount that a series of future
  789.          *  payments is worth right now. If pv is omitted, it is assumed to be 0 (zero),
  790.          *  and you must include the pmt argument.
  791.          * Type: is the number 0 or 1 and indicates when payments are due. If type is
  792.          *  omitted, it is assumed to be 0.
  793.          *  0 or omitted, At the end of the period
  794.          *  1, At the beginning of the period
  795.          *
  796.          * If rate = 0:
  797.          * FV = -(PV + PMT * nper)
  798.          *
  799.          * Else
  800.          *                                  /             nper \
  801.          *                                 | 1 - (1 + rate)     |                 nper
  802.          * FV =  PMT * (1 + rate * type) * | ------------------ | - PV * (1 + rate)
  803.          *                                  \        rate      /
  804.          *
  805.          **/
  806.         function FV($rate, $nper, $pmt, $pv = 0.0, $type = 0)
  807.         {
  808.             $pvif = $this->_calculate_pvif ($rate, $nper);
  809.             $fvifa = $this->_calculate_fvifa ($rate, $nper);
  810.            
  811.             $fv = (-(($pv * $pvif) + $pmt *
  812.                     (1.0 + $rate * $type) * $fvifa));
  813.            
  814.             return (is_finite($fv) ? $fv: null);
  815.         }
  816.        
  817.         /**
  818.          * FVSCHEDULE
  819.          * Returns the future value of an initial principal after applying a series
  820.          * of compound interest rates. Use FVSCHEDULE to calculate the future value
  821.          * of an investment with a variable or adjustable rate.
  822.          *
  823.          **/
  824.         function FVSCHEDULE($principal, $schedule)
  825.         {
  826.             $n = count($schedule);
  827.             for ($i = 0; $i < $n; $i++)
  828.                 $principal *= 1 + $schedule[$i];
  829.             return $principal;
  830.         }
  831.        
  832.         /**
  833.          * PMT
  834.          * Calculates the payment for a loan based on constant payments
  835.          * and a constant interest rate.
  836.          *
  837.          * For a more complete description of the arguments in PMT, see the PV function.
  838.          *
  839.          * Rate: is the interest rate for the loan.
  840.          * Nper: is the total number of payments for the loan.
  841.          * Pv: is the present value, or the total amount that a series of future payments
  842.          *  is worth now; also known as the principal.
  843.          * Fv: is the future value, or a cash balance you want to attain after the last
  844.          *  payment is made. If fv is omitted, it is assumed to be 0 (zero), that is,
  845.          *  the future value of a loan is 0.
  846.          * Type: is the number 0 (zero) or 1 and indicates when payments are due.
  847.          *  0 or omitted, At the end of the period
  848.          *  1, At the beginning of the period
  849.          *
  850.          * If rate = 0:
  851.          *        -(FV + PV)
  852.          * PMT = ------------
  853.          *           nper
  854.          *
  855.          * Else
  856.          *
  857.          *                                      nper
  858.          *                   FV + PV * (1 + rate)
  859.          * PMT = --------------------------------------------
  860.          *                             /             nper \
  861.          *                            | 1 - (1 + rate)     |
  862.          *        (1 + rate * type) * | ------------------ |
  863.          *                             \        rate      /
  864.          *
  865.          **/
  866.         function PMT($rate, $nper, $pv, $fv = 0.0, $type = 0)
  867.         {
  868.             $pmt = $this->_calculate_pmt ($rate, $nper, $pv, $fv, $type);
  869.             return (is_finite($pmt) ? $pmt: null);
  870.         }
  871.        
  872.         /**
  873.          * IPMT
  874.          * Returns the interest payment for a given period for an investment based
  875.          * on periodic, constant payments and a constant interest rate.
  876.          *
  877.          * For a more complete description of the arguments in IPMT, see the PV function.
  878.          *
  879.          */
  880.         function IPMT($rate, $per, $nper, $pv, $fv = 0.0, $type = 0)
  881.         {
  882.             if (($per < 1) || ($per >= ($nper + 1)))
  883.                 return null;
  884.             else {
  885.                 $pmt = $this->_calculate_pmt ($rate, $nper, $pv, $fv, $type);
  886.                 $ipmt = $this->_calculate_interest_part ($pv, $pmt, $rate, $per - 1);
  887.                 return (is_finite($ipmt) ? $ipmt: null);
  888.             }
  889.         }
  890.        
  891.         /**
  892.          * PPMT
  893.          * Returns the payment on the principal for a given period for an
  894.          * investment based on periodic, constant payments and a constant
  895.          * interest rate.
  896.          *
  897.          **/
  898.         function PPMT($rate, $per, $nper, $pv, $fv = 0.0, $type = 0)
  899.         {
  900.             if (($per < 1) || ($per >= ($nper + 1)))
  901.                 return null;
  902.             else {
  903.                 $pmt = $this->_calculate_pmt ($rate, $nper, $pv, $fv, $type);
  904.                 $ipmt = $this->_calculate_interest_part ($pv, $pmt, $rate, $per - 1);
  905.                 return ((is_finite($pmt) && is_finite($ipmt)) ? $pmt - $ipmt: null);
  906.             }
  907.         }
  908.        
  909.         /**
  910.          * NPER
  911.          * Returns the number of periods for an investment based on periodic,
  912.          * constant payments and a constant interest rate.
  913.          *
  914.          * For a complete description of the arguments nper, pmt, pv, fv, and type, see PV.
  915.          *
  916.          * Nper: is the total number of payment periods in an annuity.
  917.          * Pmt: is the payment made each period and cannot change over the life
  918.          *  of the annuity. Typically, pmt includes principal and interest but no
  919.          *  other fees or taxes. If pmt is omitted, you must include the fv argument.
  920.          * Pv: is the present value — the total amount that a series of future payments
  921.          *  is worth now.
  922.          * Fv: is the future value, or a cash balance you want to attain after the
  923.          *  last payment is made. If fv is omitted, it is assumed to be 0 (the future
  924.          *  value of a loan, for example, is 0).
  925.          * Type: is the number 0 or 1 and indicates when payments are due.
  926.          *  0 or omitted, At the end of the period
  927.          *  1, At the beginning of the period
  928.          *
  929.          * If rate = 0:
  930.          *        -(FV + PV)
  931.          * nper = -----------
  932.          *           PMT
  933.          *
  934.          * Else
  935.          *              / PMT * (1 + rate * type) - FV * rate \
  936.          *         log | ------------------------------------- |
  937.          *              \ PMT * (1 + rate * type) + PV * rate /
  938.          * nper = -----------------------------------------------
  939.          *                          log (1 + rate)
  940.          *
  941.          **/
  942.         function NPER($rate, $pmt, $pv, $fv = 0.0, $type = 0)
  943.         {
  944.             if (($rate == 0) && ($pmt != 0))
  945.                 $nper = (-($fv + $pv) / $pmt);
  946.             elseif ($rate <= 0.0)
  947.                 return null;
  948.             else {
  949.                 $tmp = ($pmt * (1.0 + $rate * $type) - $fv * $rate) /
  950.                         ($pv * $rate + $pmt * (1.0 + $rate * $type));
  951.                 if ($tmp <= 0.0)
  952.                     return null;
  953.                 $nper = (log10($tmp) / log10(1.0 + $rate));
  954.             }
  955.             return (is_finite($nper) ? $nper: null);
  956.         }
  957.        
  958.         /*
  959.          * EFFECT
  960.          * Returns the effective annual interest rate, given the nominal annual
  961.          * interest rate and the number of compounding periods per year.
  962.          *
  963.          *           /     nominal_rate \ npery
  964.          * EFFECT = | 1 + -------------- |       - 1
  965.          *           \         npery    /
  966.          *
  967.          **/
  968.         function EFFECT($nominal_rate, $npery)
  969.         {
  970.             $npery = intval($npery);
  971.             if (($nominal_rate <= 0) || ($npery < 1)) return null;
  972.             $effect = pow(1 + $nominal_rate / $npery, $npery) - 1;
  973.             return (is_finite($effect) ? $effect: null);
  974.         }
  975.        
  976.         /**
  977.          * NOMINAL
  978.          * Returns the nominal annual interest rate, given the effective rate
  979.          * and the number of compounding periods per year.
  980.          *
  981.          *                                   (1 / npery)
  982.          * NOMINAL = npery * (1 + effect_rate)           -  npery
  983.          *
  984.          **/
  985.         function NOMINAL($effect_rate, $npery)
  986.         {
  987.             $npery = intval($npery);
  988.             if (($effect_rate <= 0) || ($npery < 1)) return null;
  989.             $nominal = $npery * pow(1 + $effect_rate, 1 / $npery) - $npery;
  990.             return (is_finite($nominal) ? $nominal: null);
  991.         }
  992.        
  993.         /**
  994.          * DISC
  995.          * Returns the discount rate for a security.
  996.          *
  997.          *             redemption - pr
  998.          * DISC = ---------------------------
  999.          *         redemption * yearfraction
  1000.          *
  1001.          **/
  1002.         function DISC($settlement, $maturity, $pr, $redemption, $basis = FINANCIAL_BASIS_MSRB_30_360)
  1003.         {
  1004.             if (!$this->_is_valid_basis($basis)) return null;
  1005.             if (($pr <= 0) || ($redemption <= 0)) return null;
  1006.             if ($settlement >= $maturity) return null;
  1007.            
  1008.             switch ($basis) {
  1009.                 case FINANCIAL_BASIS_MSRB_30_360: // US(NASD) 30/360
  1010.                     $dsm = $this->Thirty360USyearFraction($settlement, $maturity);
  1011.                     break;
  1012.                 case FINANCIAL_BASIS_ACT_ACT: // Actual days/actual days
  1013.                     $dsm = $this->ActualActualyearFraction($settlement, $maturity);
  1014.                     break;
  1015.                 case FINANCIAL_BASIS_ACT_360: // Actual days/360
  1016.                     $dsm = $this->Actual360yearFraction($settlement, $maturity);
  1017.                     break;
  1018.                 case FINANCIAL_BASIS_ACT_365: // Actual days/365
  1019.                     $dsm = $this->Actual365yearFraction($settlement, $maturity);
  1020.                     break;
  1021.                 case FINANCIAL_BASIS_30E_360: // European 30/360
  1022.                     $dsm = $this->Thirty360EUyearFraction($settlement, $maturity);
  1023.                     break;
  1024.             }
  1025.             $disc = ($redemption - $pr) / ($redemption * $dsm);
  1026.             return (is_finite($disc) ? $disc: null);
  1027.         }
  1028.        
  1029.         /**
  1030.          * RECEIVED
  1031.          * Returns the amount received at maturity for a fully invested security.
  1032.          *
  1033.          *                      investment
  1034.          * RECEIVED = -----------------------------
  1035.          *             1 - discount * yearfraction
  1036.          *
  1037.          **/
  1038.         function RECEIVED($settlement, $maturity, $investment, $discount, $basis = FINANCIAL_BASIS_MSRB_30_360)
  1039.         {
  1040.             if (!$this->_is_valid_basis($basis)) return null;
  1041.             if (($investment <= 0) || ($discount <= 0)) return null;
  1042.             if ($settlement >= $maturity) return null;
  1043.            
  1044.             switch ($basis) {
  1045.                 case FINANCIAL_BASIS_MSRB_30_360: // US(NASD) 30/360
  1046.                     $dsm = $this->Thirty360USyearFraction($settlement, $maturity);
  1047.                     break;
  1048.                 case FINANCIAL_BASIS_ACT_ACT: // Actual/actual
  1049.                     $dsm = $this->ActualActualyearFraction($settlement, $maturity);
  1050.                     break;
  1051.                 case FINANCIAL_BASIS_ACT_360: // Actual/360
  1052.                     $dsm = $this->Actual360yearFraction($settlement, $maturity);
  1053.                     break;
  1054.                 case FINANCIAL_BASIS_ACT_365: // Actual/365
  1055.                     $dsm = $this->Actual365yearFraction($settlement, $maturity);
  1056.                     break;
  1057.                 case FINANCIAL_BASIS_30E_360: // European 30/360
  1058.                     $dsm = $this->Thirty360EUyearFraction($settlement, $maturity);
  1059.                     break;
  1060.             }
  1061.             $received = $investment / (1 - $discount * $dsm);
  1062.             return (is_finite($received) ? $received: null);
  1063.         }
  1064.        
  1065.         /**
  1066.          * INTRATE
  1067.          * Returns the interest rate for a fully invested security.
  1068.          *
  1069.          *             redemption - investment
  1070.          * INTRATE = ---------------------------
  1071.          *            investment * yearfraction
  1072.          *
  1073.          **/
  1074.         function INTRATE($settlement, $maturity, $investment, $redemption, $basis = FINANCIAL_BASIS_MSRB_30_360)
  1075.         {
  1076.             if (!$this->_is_valid_basis($basis)) return null;
  1077.             if (($investment <= 0) || ($redemption <= 0)) return null;
  1078.             if ($settlement >= $maturity) return null;
  1079.            
  1080.             switch ($basis) {
  1081.                 case FINANCIAL_BASIS_MSRB_30_360: // US(NASD) 30/360
  1082.                     $dsm = $this->Thirty360USyearFraction($settlement, $maturity);
  1083.                     break;
  1084.                 case FINANCIAL_BASIS_ACT_ACT: // Actual/actual
  1085.                     $dsm = $this->ActualActualyearFraction($settlement, $maturity);
  1086.                     break;
  1087.                 case FINANCIAL_BASIS_ACT_365: // Actual/360
  1088.                     $dsm = $this->Actual360yearFraction($settlement, $maturity);
  1089.                     break;
  1090.                 case FINANCIAL_BASIS_ACT_365: // Actual/365
  1091.                     $dsm = $this->Actual365yearFraction($settlement, $maturity);
  1092.                     break;
  1093.                 case FINANCIAL_BASIS_30E_360: // European 30/360
  1094.                     $dsm = $this->Thirty360EUyearFraction($settlement, $maturity);
  1095.                     break;
  1096.             }
  1097.             $intrate = ($redemption - $investment) / ($investment * $dsm);
  1098.             return (is_finite($intrate) ? $intrate: null);
  1099.         }
  1100.        
  1101.         /**
  1102.          * NPV
  1103.          * Calculates the net present value of an investment by using a
  1104.          * discount rate and a series of future payments (negative values)
  1105.          * and income (positive values).
  1106.          *
  1107.          *        n   /   values(i)  \
  1108.          * NPV = SUM | -------------- |
  1109.          *       i=1 |            i   |
  1110.          *            \  (1 + rate)  /
  1111.          *
  1112.          **/
  1113.         function NPV($rate, $values)
  1114.         {
  1115.             if (!is_array($values)) return null;
  1116.            
  1117.             $npv = 0.0;
  1118.             for ($i = 0; $i < count($values); $i++)
  1119.             {
  1120.                 $npv += $values[$i] / pow(1 + $rate, $i + 1);
  1121.             }
  1122.             return (is_finite($npv) ? $npv: null);
  1123.         }
  1124.        
  1125.         /**
  1126.          * XNPV
  1127.          * Returns the net present value for a schedule of cash flows that
  1128.          * is not necessarily periodic. To calculate the net present value
  1129.          * for a series of cash flows that is periodic, use the NPV function.
  1130.          *
  1131.          *        n   /                values(i)               \
  1132.          * NPV = SUM | ---------------------------------------- |
  1133.          *       i=1 |           ((dates(i) - dates(1)) / 365)  |
  1134.          *            \ (1 + rate)                             /
  1135.          *
  1136.          **/
  1137.         function XNPV($rate, $values, $dates)
  1138.         {
  1139.             if ((!is_array($values)) || (!is_array($dates))) return null;
  1140.             if (count($values) != count($dates)) return null;
  1141.            
  1142.             $xnpv = 0.0;
  1143.             for ($i = 0; $i < count($values); $i++)
  1144.             {
  1145.                 $xnpv += $values[$i] / pow(1 + $rate, $this->DATEDIFF('day', $dates[0], $dates[$i]) / 365);
  1146.             }
  1147.             return (is_finite($xnpv) ? $xnpv: null);
  1148.         }
  1149.        
  1150.         /*
  1151.          * IRR
  1152.          * Returns the internal rate of return for a series of cash flows
  1153.          * represented by the numbers in values. These cash flows do not
  1154.          * have to be even, as they would be for an annuity. However, the
  1155.          * cash flows must occur at regular intervals, such as monthly or
  1156.          * annually. The internal rate of return is the interest rate
  1157.          * received for an investment consisting of payments (negative
  1158.          * values) and income (positive values) that occur at regular periods.
  1159.          *
  1160.          */
  1161.         function IRR($values, $guess = 0.1)
  1162.         {
  1163.             if (!is_array($values)) return null;
  1164.            
  1165.             // create an initial bracket, with a root somewhere between bot and top
  1166.             $x1 = 0.0;
  1167.             $x2 = $guess;
  1168.             $f1 = $this->NPV($x1, $values);
  1169.             $f2 = $this->NPV($x2, $values);
  1170.             for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; $i++)
  1171.             {
  1172.                 if (($f1 * $f2) < 0.0) break;
  1173.                 if (abs($f1) < abs($f2)) {
  1174.                     $f1 = $this->NPV($x1 += 1.6 * ($x1 - $x2), $values);
  1175.                 } else {
  1176.                     $f2 = $this->NPV($x2 += 1.6 * ($x2 - $x1), $values);
  1177.                 }
  1178.             }
  1179.             if (($f1 * $f2) > 0.0) return null;
  1180.            
  1181.             $f = $this->NPV($x1, $values);
  1182.             if ($f < 0.0) {
  1183.                 $rtb = $x1;
  1184.                 $dx = $x2 - $x1;
  1185.             } else {
  1186.                 $rtb = $x2;
  1187.                 $dx = $x1 - $x2;
  1188.             }
  1189.            
  1190.             for ($i = 0;  $i < FINANCIAL_MAX_ITERATIONS; $i++)
  1191.             {
  1192.                 $dx *= 0.5;
  1193.                 $x_mid = $rtb + $dx;
  1194.                 $f_mid = $this->NPV($x_mid, $values);
  1195.                 if ($f_mid <= 0.0) $rtb = $x_mid;
  1196.                 if ((abs($f_mid) < FINANCIAL_ACCURACY) || (abs($dx) < FINANCIAL_ACCURACY)) return $x_mid;
  1197.             }
  1198.             return null;
  1199.         }
  1200.        
  1201.         /*
  1202.          * MIRR
  1203.          * Returns the modified internal rate of return for a series
  1204.          * of periodic cash flows. MIRR considers both the cost of
  1205.          * the investment and the interest received on reinvestment
  1206.          * of cash.
  1207.          *
  1208.          **/
  1209.         function MIRR($values, $finance_rate, $reinvert_rate)
  1210.         {
  1211.             $n = count($values);
  1212.             for ($i = 0, $npv_pos = $npv_neg = 0; $i < $n; $i++) {
  1213.                 $v = $values[$i];
  1214.                 if ($v >= 0)
  1215.                     $npv_pos += $v / pow(1.0 + $reinvert_rate, $i);
  1216.                 else
  1217.                     $npv_neg += $v / pow(1.0 + $finance_rate, $i);
  1218.             }
  1219.            
  1220.             if (($npv_neg == 0) || ($npv_pos == 0) || ($reinvert_rate <= -1))
  1221.                 return null;
  1222.            
  1223.             /*
  1224.             * I have my doubts about this formula, but it sort of looks like
  1225.             * the one Microsoft claims to use and it produces the results
  1226.             * that Excel does.  -- MW.
  1227.             */
  1228.             $mirr = pow((-$npv_pos * pow(1 + $reinvert_rate, $n))
  1229.                     / ($npv_neg * (1 + $reinvert_rate)), (1.0 / ($n - 1))) - 1.0;
  1230.             return (is_finite($mirr) ? $mirr: null);
  1231.         }
  1232.        
  1233.         /*
  1234.          * XIRR
  1235.          * Returns the internal rate of return for a schedule of cash flows
  1236.          * that is not necessarily periodic. To calculate the internal rate
  1237.          * of return for a series of periodic cash flows, use the IRR function.
  1238.          *
  1239.          * Adapted from routine in Numerical Recipes in C, and translated
  1240.          * from the Bernt A Oedegaard algorithm in C
  1241.          *
  1242.          **/
  1243.         function XIRR($values, $dates, $guess = 0.1)
  1244.         {
  1245.             if ((!is_array($values)) && (!is_array($dates))) return null;
  1246.             if (count($values) != count($dates)) return null;
  1247.            
  1248.             // create an initial bracket, with a root somewhere between bot and top
  1249.             $x1 = 0.0;
  1250.             $x2 = $guess;
  1251.             $f1 = $this->XNPV($x1, $values, $dates);
  1252.             $f2 = $this->XNPV($x2, $values, $dates);
  1253.             for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; $i++)
  1254.             {
  1255.                 if (($f1 * $f2) < 0.0) break;
  1256.                 if (abs($f1) < abs($f2)) {
  1257.                     $f1 = $this->XNPV($x1 += 1.6 * ($x1 - $x2), $values, $dates);
  1258.                 } else {
  1259.                     $f2 = $this->XNPV($x2 += 1.6 * ($x2 - $x1), $values, $dates);
  1260.                 }
  1261.             }
  1262.             if (($f1 * $f2) > 0.0) return null;
  1263.            
  1264.             $f = $this->XNPV($x1, $values, $dates);
  1265.             if ($f < 0.0) {
  1266.                 $rtb = $x1;
  1267.                 $dx = $x2 - $x1;
  1268.             } else {
  1269.                 $rtb = $x2;
  1270.                 $dx = $x1 - $x2;
  1271.             }
  1272.            
  1273.             for ($i = 0;  $i < FINANCIAL_MAX_ITERATIONS; $i++)
  1274.             {
  1275.                 $dx *= 0.5;
  1276.                 $x_mid = $rtb + $dx;
  1277.                 $f_mid = $this->XNPV($x_mid, $values, $dates);
  1278.                 if ($f_mid <= 0.0) $rtb = $x_mid;
  1279.                 if ((abs($f_mid) < FINANCIAL_ACCURACY) || (abs($dx) < FINANCIAL_ACCURACY)) return $x_mid;
  1280.             }
  1281.             return null;
  1282.         }
  1283.        
  1284.         /**
  1285.          * RATE
  1286.          *
  1287.          **/
  1288.         function RATE($nper, $pmt, $pv, $fv = 0.0, $type = 0, $guess = 0.1)
  1289.         {
  1290.             $rate = $guess;
  1291.             $i  = 0;
  1292.             $x0 = 0;
  1293.             $x1 = $rate;
  1294.            
  1295.             if (abs($rate) < FINANCIAL_ACCURACY) {
  1296.                 $y = $pv * (1 + $nper * $rate) + $pmt * (1 + $rate * $type) * $nper + $fv;
  1297.             } else {
  1298.                 $f = exp($nper * log(1 + $rate));
  1299.                 $y = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
  1300.             }
  1301.             $y0 = $pv + $pmt * $nper + $fv;
  1302.             $y1 = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
  1303.            
  1304.             // find root by secant method
  1305.             while ((abs($y0 - $y1) > FINANCIAL_ACCURACY) && ($i < FINANCIAL_MAX_ITERATIONS))
  1306.             {
  1307.                 $rate = ($y1 * $x0 - $y0 * $x1) / ($y1 - $y0);
  1308.                 $x0 = $x1;
  1309.                 $x1 = $rate;
  1310.                
  1311.                 if (abs($rate) < FINANCIAL_ACCURACY) {
  1312.                     $y = $pv * (1 + $nper * $rate) + $pmt * (1 + $rate * $type) * $nper + $fv;
  1313.                 } else {
  1314.                     $f = exp($nper * log(1 + $rate));
  1315.                     $y = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
  1316.                 }
  1317.                
  1318.                 $y0 = $y1;
  1319.                 $y1 = $y;
  1320.                 $i++;
  1321.             }
  1322.             return $rate;
  1323.         }
  1324.         /**
  1325.          * DELTA
  1326.          * Tests whether two values are equal. Returns 1 if number1 = number2; returns 0 otherwise.
  1327.          * Use this function to filter a set of values. For example, by summing several DELTA functions
  1328.          * you calculate the count of equal pairs. This function is also known as the Kronecker Delta function.
  1329.          */
  1330.         function DELTA($number1, $number2 = 0)
  1331.         {
  1332.             if (is_nan($number1) || is_nan($number2)) return null;
  1333.             if ($number1 == $number2) {
  1334.                 return 1;
  1335.             } else {
  1336.                 return 0;
  1337.             }
  1338.         }
  1339.        
  1340.         /*
  1341.          * Returns the yield on a security that pays periodic interest.
  1342.          * Use YIELD to calculate bond yield.
  1343.          *
  1344.          * Settlement: is the security's settlement date. The security
  1345.          *  settlement date is the date after the issue date when the
  1346.          *  security is traded to the buyer.
  1347.          * Maturity: is the security's maturity date. The maturity date
  1348.          *  is the date when the security expires.
  1349.          * Rate: is the security's annual coupon rate.
  1350.          * Pr: is the security's price per $100 face value.
  1351.          * Redemption: is the security's redemption value per $100 face value.
  1352.          * Frequency: is the number of coupon payments per year. For annual
  1353.          *  payments, frequency = 1; for semiannual, frequency = 2; for
  1354.          *  quarterly, frequency = 4.
  1355.          * Basis: is the type of day count basis to use.
  1356.          *  0 or omitted US (NASD) 30/360
  1357.          *  1 Actual/actual
  1358.          *  2 Actual/360
  1359.          *  3 Actual/365
  1360.          *  4 European 30/360
  1361.          *
  1362.          */
  1363.         function YIELD($settlement, $maturity, $rate, $pr, $redemption, $frequency, $basis = FINANCIAL_BASIS_MSRB_30_360)
  1364.         {
  1365.             if (!$this->_is_valid_basis($basis)) return null;
  1366.             if (!$this->_is_valid_frequency($frequency)) return null;
  1367.             if ($rate < 0) return null;
  1368.             if (($pr <= 0) || ($redemption <= 0)) return null;
  1369.             if ($settlement >= $maturity) return null;
  1370.            
  1371.             // TODO: Not yet implemented
  1372.             return null;
  1373.         }
  1374.        
  1375.         /**
  1376.          * PRICEDISC
  1377.          * Returns the price per $100 face value of a discounted security.
  1378.          **/
  1379.         function PRICEDISC($settlement, $maturity, $discount, $redemption, $basis = FINANCIAL_BASIS_MSRB_30_360)
  1380.         {
  1381.             if (!$this->_is_valid_basis($basis)) return null;
  1382.             if (($discount <= 0) || ($redemption <= 0)) return null;
  1383.             if ($settlement >= $maturity) return null;
  1384.            
  1385.             switch ($basis) {
  1386.                 case FINANCIAL_BASIS_MSRB_30_360: // US(NASD) 30/360
  1387.                     $dsm = $this->Thirty360USyearFraction($settlement, $maturity);
  1388.                     break;
  1389.                 case FINANCIAL_BASIS_ACT_ACT: // Actual/actual
  1390.                     $dsm = $this->ActualActualyearFraction($settlement, $maturity);
  1391.                     break;
  1392.                 case FINANCIAL_BASIS_ACT_360: // Actual/360
  1393.                     $dsm = $this->Actual360yearFraction($settlement, $maturity);
  1394.                     break;
  1395.                 case FINANCIAL_BASIS_ACT_365: // Actual/365
  1396.                     $dsm = $this->Actual365yearFraction($settlement, $maturity);
  1397.                     break;
  1398.                 case FINANCIAL_BASIS_30E_360: // European 30/360
  1399.                     $dsm = $this->Thirty360EUyearFraction($settlement, $maturity);
  1400.                     break;
  1401.             }
  1402.            
  1403.             return $redemption - $discount * $redemption * $dsm;
  1404.         }
  1405.        
  1406.         /**
  1407.          * YIELDDISC
  1408.          * Returns the annual yield for a discounted security.
  1409.          **/
  1410.         function YIELDDISC($settlement, $maturity, $pr, $redemption, $basis = FINANCIAL_BASIS_MSRB_30_360)
  1411.         {
  1412.             if (!$this->_is_valid_basis($basis)) return null;
  1413.             if (($pr <= 0) || ($redemption <= 0)) return null;
  1414.             if ($settlement >= $maturity) return null;
  1415.            
  1416.             switch ($basis) {
  1417.                 case FINANCIAL_BASIS_MSRB_30_360: // US(NASD) 30/360
  1418.                     $dsm = $this->Thirty360USyearFraction($settlement, $maturity);
  1419.                     break;
  1420.                 case FINANCIAL_BASIS_ACT_ACT: // Actual/actual
  1421.                     $dsm = $this->ActualActualyearFraction($settlement, $maturity);
  1422.                     break;
  1423.                 case FINANCIAL_BASIS_ACT_360: // Actual/360
  1424.                     $dsm = $this->Actual360yearFraction($settlement, $maturity);
  1425.                     break;
  1426.                 case FINANCIAL_BASIS_ACT_365: // Actual/365
  1427.                     $dsm = $this->Actual365yearFraction($settlement, $maturity);
  1428.                     break;
  1429.                 case FINANCIAL_BASIS_30E_360: // European 30/360
  1430.                     $dsm = $this->Thirty360EUyearFraction($settlement, $maturity);
  1431.                     break;
  1432.             }
  1433.            
  1434.             return ($redemption - $pr) / ($pr * $dsm);
  1435.         }
  1436.        
  1437.         /**
  1438.          * COUPNUM
  1439.          * Returns the number of coupons payable between the settlement
  1440.          * date and maturity date, rounded up to the nearest whole coupon.
  1441.          *
  1442.          */
  1443.         function COUPNUM($settlement, $maturity, $frequency, $basis = FINANCIAL_BASIS_MSRB_30_360)
  1444.         {
  1445.             if (!$this->_is_valid_basis($basis)) return null;
  1446.             if (!$this->_is_valid_frequency($frequency)) return null;
  1447.             if ($settlement >= $maturity) return null;
  1448.            
  1449.             switch ($basis) {
  1450.                 case FINANCIAL_BASIS_MSRB_30_360: // US(NASD) 30/360
  1451.                     $dsm = $this->Thirty360USdayCount($settlement, $maturity);
  1452.                     break;
  1453.                 case FINANCIAL_BASIS_ACT_ACT: // Actual/actual
  1454.                     $dsm = $this->ActualActualdayCount($settlement, $maturity);
  1455.                     break;
  1456.                 case FINANCIAL_BASIS_ACT_360: // Actual/360
  1457.                     $dsm = $this->Actual360dayCount($settlement, $maturity);
  1458.                     break;
  1459.                 case FINANCIAL_BASIS_ACT_365: // Actual/365
  1460.                     $dsm = $this->Actual365yearFraction($settlement, $maturity);
  1461.                     break;
  1462.                 case FINANCIAL_BASIS_30E_360: // European 30/360
  1463.                     $dsm = $this->Thirty360EUdayCount($settlement, $maturity);
  1464.                     break;
  1465.             }
  1466.            
  1467.             switch ($frequency) {
  1468.                 case 1: // anual payments
  1469.                     return ceil($dsm / 360);
  1470.                 case 2: // semiannual
  1471.                     return ceil($dsm / 180);
  1472.                 case 4: // quarterly
  1473.                     return ceil($dsm / 90);
  1474.             }
  1475.             return null;
  1476.         }
  1477.        
  1478.         /**
  1479.          * COUPDAYBS
  1480.          * Returns the number of days in the coupon period that contains
  1481.          * the settlement date.
  1482.          *
  1483.          */
  1484.         function COUPDAYBS($settlement, $maturity, $frequency, $basis = FINANCIAL_BASIS_MSRB_30_360)
  1485.         {
  1486.             if (!$this->_is_valid_basis($basis)) return null;
  1487.             if (!$this->_is_valid_frequency($frequency)) return null;
  1488.             if ($settlement >= $maturity) return null;
  1489.            
  1490.             switch ($basis) {
  1491.                 case FINANCIAL_BASIS_MSRB_30_360: // US(NASD) 30/360
  1492.                     $dsm = $this->Thirty360USdayCount($settlement, $maturity);
  1493.                     break;
  1494.                 case FINANCIAL_BASIS_ACT_ACT: // Actual/actual
  1495.                     $dsm = $this->ActualActualdayCount($settlement, $maturity);
  1496.                     break;
  1497.                 case FINANCIAL_BASIS_ACT_360: // Actual/360
  1498.                     $dsm = $this->Actual360dayCount($settlement, $maturity);
  1499.                     break;
  1500.                 case FINANCIAL_BASIS_ACT_365: // Actual/365
  1501.                     $dsm = $this->Actual365yearFraction($settlement, $maturity);
  1502.                     break;
  1503.                 case FINANCIAL_BASIS_30E_360: // European 30/360
  1504.                     $dsm = $this->Thirty360EUdayCount($settlement, $maturity);
  1505.                     break;
  1506.             }
  1507.            
  1508.             switch ($frequency) {
  1509.                 case 1: // anual payments
  1510.                     return 365 - ($dsm % 360);
  1511.                 case 2: // semiannual
  1512.                     return 365 - ($dsm % 360);
  1513.                 case 4: // quarterly
  1514.                     return $this->DATEDIFF('day', $this->DATEADD('day', -ceil($dsm / 90) * 90 - ($dsm % 90), $maturity), $settlement);
  1515.             }
  1516.             return null;
  1517.         }
  1518.     }
  1519.     ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement