Skip to content
Browse files

Imported package to CVS

git-svn-id: http://svn.php.net/repository/pear/packages/Math_Finance/trunk@274632 c90b9560-bf6c-de11-be94-00142212c4b1
  • Loading branch information...
1 parent e119ae3 commit 63651fba476c511f4fc2e102d15efe5e6b2d70c2 @CloCkWeRX CloCkWeRX committed Jan 26, 2009
Showing with 1,286 additions and 0 deletions.
  1. +873 −0 Math/Finance.php
  2. +48 −0 Math/Finance_FunctionParameters.php
  3. +76 −0 package.xml
  4. +289 −0 tests/testMath_Finance.php
View
873 Math/Finance.php
@@ -0,0 +1,873 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Math_Finance: Class of financial functions
+ *
+ * Assorted financial functions for interest rates, bonds, amortizations and time value of money calculations (annuities)
+ * Same interface as Excel financial functions.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category Math
+ * @package Math_Finance
+ * @author Original Author <alejandro.pedraza@dataenlace.com>
+ * @copyright 2005 Alejandro Pedraza
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id$
+ * @link http://pear.php.net/Math/Finance
+ * @since File available since Release 1.2.0
+ */
+
+// to be able to throw PEAR errors
+require_once 'PEAR.php';
+
+// precision of calculations
+define('FINANCE_PRECISION', 1E-6);
+
+// payment types
+define('FINANCE_PAY_END', 0);
+define('FINANCE_PAY_BEGIN', 1);
+
+// types of daycount basis
+define('FINANCE_COUNT_NASD', 0);
+define('FINANCE_COUNT_ACTUAL_ACTUAL', 1);
+define('FINANCE_COUNT_ACTUAL_360', 2);
+define('FINANCE_COUNT_ACTUAL_365', 3);
+define('FINANCE_COUNT_EUROPEAN', 4);
+
+/**
+ * Math_Finance: Main class
+ *
+ * @category Math
+ * @package Math_Finance
+ * @author Original Author <alejandro.pedraza@dataenlace.com>
+ * @copyright 2005 Alejandro Pedraza
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/Math/Finance
+ * @since Class available since Release 1.2.0
+ */
+class Math_Finance
+{
+ /*******************************************************************
+ ** Interest Rates Conversion Functions *****
+ *******************************************************************/
+
+ /**
+ * Returns the effective interest rate given the nominal rate and the number of compounding payments per year
+ * Excel equivalent: EFFECT
+ *
+ * @param float Nominal interest rate
+ * @param int Number of compounding payments per year
+ * @return float
+ * @static
+ * @access public
+ */
+ function effectiveRate($nominal_rate, $npery)
+ {
+ $npery = (int)$npery;
+ if ($npery < 0) {
+ return PEAR::raiseError('Number of compounding payments per year is not positive');
+ }
+
+ $effect = pow((1 + $nominal_rate / $npery), $npery) - 1;
+ return $effect;
+ }
+
+ /**
+ * Returns the nominal interest rate given the effective rate and the number of compounding payments per year
+ * Excel equivalent: NOMINAL
+ *
+ * @param float Effective interest rate
+ * @param int Number of compounding payments per year
+ * @return float
+ * @static
+ * @access public
+ */
+ function nominalRate($effect_rate, $npery)
+ {
+ $npery = (int)$npery;
+ if ($npery < 0) {
+ return PEAR::raiseError('Number of compounding payments per year is not positive');
+ }
+
+ $nominal = $npery * (pow($effect_rate + 1, 1/$npery) - 1);
+ return $nominal;
+ }
+
+
+ /*******************************************************************
+ ** TVM (annuities) Functions *****
+ *******************************************************************/
+
+ /**
+ * Returns the Present Value of a cash flow with constant payments and interest rate (annuities)
+ * Excel equivalent: PV
+ *
+ * TVM functions solve for a term in the following formula:
+ * pv(1+r)^n + pmt(1+r.type)((1+r)^n - 1)/r) +fv = 0
+ *
+ *
+ * @param float Interest rate per period
+ * @param int Number of periods
+ * @param float Periodic payment (annuity)
+ * @param float Future Value
+ * @param int Payment type:
+ FINANCE_PAY_END (default): at the end of each period
+ FINANCE_PAY_BEGIN: at the beginning of each period
+ * @return float
+ * @static
+ * @access public
+ */
+ function presentValue($rate, $nper, $pmt, $fv = 0, $type = 0)
+ {
+ if ($nper < 0) {
+ return PEAR::raiseError('Number of periods must be positive');
+ }
+ if ($type != FINANCE_PAY_END && $type != FINANCE_PAY_BEGIN) {
+ return PEAR::raiseError('Payment type must be FINANCE_PAY_END or FINANCE_PAY_BEGIN');
+ }
+
+ if ($rate) {
+ $pv = (-$pmt * (1 + $rate * $type) * ((pow(1 + $rate, $nper) - 1) / $rate) - $fv) / pow(1 + $rate, $nper);
+ } else {
+ $pv = -$fv - $pmt * $nper;
+ }
+ return $pv;
+ }
+
+ /**
+ * Returns the Future Value of a cash flow with constant payments and interest rate (annuities)
+ * Excel equivalent: FV
+ *
+ * @param float Interest rate per period
+ * @param int Number of periods
+ * @param float Periodic payment (annuity)
+ * @param float Present Value
+ * @param int Payment type:
+ FINANCE_PAY_END (default): at the end of each period
+ FINANCE_PAY_BEGIN: at the beginning of each period
+ * @return float
+ * @static
+ * @access public
+ */
+ function futureValue($rate, $nper, $pmt, $pv = 0, $type = 0)
+ {
+ if ($nper < 0) {
+ return PEAR::raiseError('Number of periods must be positive');
+ }
+ if ($type != FINANCE_PAY_END && $type != FINANCE_PAY_BEGIN) {
+ return PEAR::raiseError('Payment type must be FINANCE_PAY_END or FINANCE_PAY_BEGIN');
+ }
+
+ if ($rate) {
+ $fv = -$pv * pow(1 + $rate, $nper) - $pmt * (1 + $rate * $type) * (pow(1 + $rate, $nper) - 1) / $rate;
+ } else {
+ $fv = -$pv - $pmt * $nper;
+ }
+ return $fv;
+ }
+
+ /**
+ * Returns the constant payment (annuity) for a cash flow with a constant interest rate
+ * Excel equivalent: PMT
+ *
+ * @param float Interest rate per period
+ * @param int Number of periods
+ * @param float Present Value
+ * @param float Future Value
+ * @param int Payment type:
+ FINANCE_PAY_END (default): at the end of each period
+ FINANCE_PAY_BEGIN: at the beginning of each period
+ * @return float
+ * @static
+ * @access public
+ */
+ function payment($rate, $nper, $pv, $fv = 0, $type = 0)
+ {
+ if ($nper < 0) {
+ return PEAR::raiseError('Number of periods must be positive');
+ }
+ if ($type != FINANCE_PAY_END && $type != FINANCE_PAY_BEGIN) {
+ return PEAR::raiseError('Payment type must be FINANCE_PAY_END or FINANCE_PAY_BEGIN');
+ }
+
+ if ($rate) {
+ $pmt = (-$fv - $pv * pow(1 + $rate, $nper)) / (1 + $rate * $type) / ((pow(1 + $rate, $nper) - 1) / $rate);
+ } else {
+ $pmt = (-$pv - $fv) / $nper;
+ }
+ return $pmt;
+ }
+
+ /**
+ * Returns the number of periods for a cash flow with constant periodic payments (annuities), and interest rate
+ * Excel equivalent: NPER
+ *
+ * @param float Interest rate per period
+ * @param float Periodic payment (annuity)
+ * @param float Present Value
+ * @param float Future Value
+ * @param int Payment type:
+ FINANCE_PAY_END (default): at the end of each period
+ FINANCE_PAY_BEGIN: at the beginning of each period
+ * @return float
+ * @static
+ * @access public
+ */
+ function periods($rate, $pmt, $pv, $fv = 0, $type = 0)
+ {
+ if ($type != FINANCE_PAY_END && $type != FINANCE_PAY_BEGIN) {
+ return PEAR::raiseError('Payment type must be FINANCE_PAY_END or FINANCE_PAY_BEGIN');
+ }
+
+ if ($rate) {
+ if ($pmt == 0 && $pv == 0) {
+ return PEAR::raiseError('Payment and Present Value can\'t be both zero when the rate is not zero');
+ }
+ $nper = log(($pmt * (1 + $rate * $type) / $rate - $fv) / ($pv + $pmt * (1 + $rate * $type) / $rate))
+ / log(1 + $rate);
+ } else {
+ if ($pmt == 0) {
+ return PEAR::raiseError('Rate and Payment can\'t be both zero');
+ }
+ $nper = (-$pv -$fv) / $pmt;
+ }
+ return $nper;
+ }
+
+ /**
+ * Returns the periodic interest rate for a cash flow with constant periodic payments (annuities)
+ * Excel equivalent: RATE
+ *
+ * @param int Number of periods
+ * @param float Periodic payment (annuity)
+ * @param float Present Value
+ * @param float Future Value
+ * @param int Payment type:
+ FINANCE_PAY_END (default): at the end of each period
+ FINANCE_PAY_BEGIN: at the beginning of each period
+ * @param float guess for the interest rate
+ * @return float
+ * @static
+ * @access public
+ */
+ function rate($nper, $pmt, $pv, $fv = 0, $type = 0, $guess = 0.1)
+ {
+ // To solve the equation
+ require_once 'Math/Numerical/RootFinding/NewtonRaphson.php';
+ // To preserve some variables in the Newton-Raphson callback functions
+ require_once 'Math/Finance_FunctionParameters.php';
+
+ if ($type != FINANCE_PAY_END && $type != FINANCE_PAY_BEGIN) {
+ return PEAR::raiseError('Payment type must be FINANCE_PAY_END or FINANCE_PAY_BEGIN');
+ }
+
+ // Utilization of a Singleton class to preserve given values of other variables in the callback functions
+ $parameters = array(
+ 'nper' => $nper,
+ 'pmt' => $pmt,
+ 'pv' => $pv,
+ 'fv' => $fv,
+ 'type' => $type,
+ );
+ $parameters_class =& Math_Finance_FunctionParameters::getInstance($parameters, True);
+
+ $newtonRaphson = new Math_Numerical_RootFinding_Newtonraphson(array('err_tolerance' => FINANCE_PRECISION));
+ return $newtonRaphson->compute(array('Math_Finance', '_tvm'), array('Math_Finance', '_dtvm'), $guess);
+ }
+
+ /**
+ * Callback function only used by Newton-Raphson algorithm. Returns value of function to be solved.
+ *
+ * Uses a previously instanced Singleton class to retrieve given values of other variables in the function
+ *
+ * @param float Interest rate
+ * @return float
+ * @static
+ * @access private
+ */
+ function _tvm($rate)
+ {
+ require_once 'Math/Finance_FunctionParameters.php';
+
+ $parameters_class =& Math_Finance_FunctionParameters::getInstance();
+ $nper = $parameters_class->parameters['nper'];
+ $pmt = $parameters_class->parameters['pmt'];
+ $pv = $parameters_class->parameters['pv'];
+ $fv = $parameters_class->parameters['fv'];
+ $type = $parameters_class->parameters['type'];
+
+ return $pv * pow(1 + $rate, $nper) + $pmt * (1 + $rate * $type) * (pow(1 + $rate, $nper) - 1) / $rate + $fv;
+ }
+
+ /**
+ * Callback function only used by Newton-Raphson algorithm. Returns value of derivative of function to be solved.
+ *
+ * Uses a previously instanced Singleton class to retrieve given values of other variables in the function
+ *
+ * @return float
+ * @static
+ * @access private
+ */
+ function _dtvm($rate)
+ {
+ require_once 'Math/Finance_FunctionParameters.php';
+
+ $parameters_class =& Math_Finance_FunctionParameters::getInstance();
+ $nper = $parameters_class->parameters['nper'];
+ $pmt = $parameters_class->parameters['pmt'];
+ $pv = $parameters_class->parameters['pv'];
+ $type = $parameters_class->parameters['type'];
+
+ return $nper * $pv * pow(1 + $rate, $nper - 1)
+ + $pmt *
+ ($type * (pow(1 + $rate, $nper) - 1) / $rate
+ + (1 + $rate * $type) * ($nper * $rate * pow(1 + $rate, $nper - 1) - pow(1 + $rate, $nper) + 1) / pow($rate,2));
+ }
+
+ /**
+ * Returns the interest payment for a given period for a cash flow with constant periodic payments (annuities)
+ * and interest rate.
+ * Excel equivalent: IMPT
+ *
+ * @param float Interest rate per period
+ * @param int Period for which the interest payment will be calculated
+ * @param int Number of periods
+ * @param float Present Value
+ * @param float Future Value
+ * @param int Payment type:
+ FINANCE_PAY_END (default): at the end of each period
+ FINANCE_PAY_BEGIN: at the beginning of each period
+ * @return float
+ * @static
+ * @access public
+ */
+ function interestPayment($rate, $per, $nper, $pv, $fv = 0, $type = 0)
+ {
+ if ($type != FINANCE_PAY_END && $type != FINANCE_PAY_BEGIN) {
+ return PEAR::raiseError('Payment type must be FINANCE_PAY_END or FINANCE_PAY_BEGIN');
+ }
+
+ $interestAndPrincipal = Math_Finance::_interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
+ return $interestAndPrincipal[0];
+ }
+
+ /**
+ * Returns the principal payment for a given period for a cash flow with constant periodic payments (annuities)
+ * and interest rate
+ * Excel equivalent: PPMT
+ *
+ * @param float Interest rate per period
+ * @param int Period for which the principal payment will be calculated
+ * @param int Number of periods
+ * @param float Present Value
+ * @param float Future Value
+ * @param int Payment type:
+ FINANCE_PAY_END (default): at the end of each period
+ FINANCE_PAY_BEGIN: at the beginning of each period
+ * @return float
+ * @static
+ * @access public
+ */
+ function principalPayment($rate, $per, $nper, $pv, $fv = 0, $type = 0)
+ {
+ if ($type != FINANCE_PAY_END && $type != FINANCE_PAY_BEGIN) {
+ return PEAR::raiseError('Payment type must be FINANCE_PAY_END or FINANCE_PAY_BEGIN');
+ }
+
+ $interestAndPrincipal = Math_Finance::_interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
+ return $interestAndPrincipal[1];
+ }
+
+ /**
+ * Returns the interest and principal payment for a given period for a cash flow with constant
+ * periodic payments (annuities) and interest rate
+ *
+ * @param float Interest rate per period
+ * @param int Number of periods
+ * @param float Present Value
+ * @param float Future Value
+ * @param int Payment type:
+ FINANCE_PAY_END (default): at the end of each period
+ FINANCE_PAY_BEGIN: at the beginning of each period
+ * @return array
+ * @static
+ * @access private
+ */
+ function _interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type)
+ {
+ $pmt = Math_Finance::payment($rate, $nper, $pv, $fv, $type);
+ //echo "pmt: $pmt\n\n";
+ $capital = $pv;
+ for ($i = 1; $i<= $per; $i++) {
+ // in first period of advanced payments no interests are paid
+ $interest = ($type && $i == 1)? 0 : -$capital * $rate;
+ $principal = $pmt - $interest;
+ $capital += $principal;
+ //echo "$i\t$capital\t$interest\t$principal\n";
+ }
+ return array($interest, $principal);
+ }
+
+ /*******************************************************************
+ ** Cash Flow Functions *****
+ *******************************************************************/
+
+ /**
+ * Returns the Net Present Value of a cash flow series given a discount rate
+ * Excel equivalent: NPV
+ *
+ * @param float Discount interest rate
+ * @param array Cash flow series
+ * @return float
+ * @static
+ * @access public
+ */
+
+ function netPresentValue($rate, $values)
+ {
+ if (!is_array($values)) {
+ return PEAR::raiseError('The cash flow series most be an array');
+ }
+
+ return MATH_Finance::_npv($rate, $values);
+ }
+
+ /**
+ * Returns the internal rate of return of a cash flow series
+ * Excel equivalent: IRR
+ *
+ * @param array Cash flow series
+ * @param float guess for the interest rate
+ * @return float
+ * @static
+ * @access public
+ */
+ function internalRateOfReturn($values, $guess = 0.1)
+ {
+ // To solve the equation
+ require_once 'Math/Numerical/RootFinding/NewtonRaphson.php';
+ // To preserve some variables in the Newton-Raphson callback functions
+ require_once 'Math/Finance_FunctionParameters.php';
+
+ if (!is_array($values)) {
+ return PEAR::raiseError('The cash flow series most be an array');
+ }
+ if (min($values) * max($values) >= 0) {
+ return PEAR::raiseError('Cash flow must contain at least one positive value and one negative value');
+ }
+
+ $parameters_class =& Math_Finance_FunctionParameters::getInstance(array('values' => $values), True);
+ $newtonRaphson = new Math_Numerical_RootFinding_Newtonraphson(array('err_tolerance' => FINANCE_PRECISION));
+ return $newtonRaphson->compute(array('Math_Finance', '_npv'), array('Math_Finance', '_dnpv'), $guess);
+ }
+
+ /**
+ * Function used by NPV() and as a callback by Newton-Raphson algorithm.
+ * Returns value of Net Present Value of a cash flow series.
+ *
+ * Uses a previously instanced Singleton class to retrieve given values of other variables in the function
+ *
+ * @param float Discount interest rate
+ * @param array Cash flow series
+ * @return float
+ * @static
+ * @access private
+ */
+ function _npv($rate, $values = array())
+ {
+ require_once 'Math/Finance_FunctionParameters.php';
+
+ if (!$values) {
+ // called from IRR
+ $parameters_class =& Math_Finance_FunctionParameters::getInstance();
+ $values = $parameters_class->parameters['values'];
+ }
+
+ $npv = 0;
+ $nper = count($values);
+ for ($i = 1; $i <= $nper; $i++) {
+ $npv += $values[$i-1]/ pow(1 + $rate, $i);
+ }
+ return $npv;
+ }
+
+ /**
+ * Callback function used by by Newton-Raphson algorithm to calculate IRR.
+ * Returns value of derivative function to be solved.
+ *
+ * Uses a previously instanced Singleton class to retrieve given values of other variables in the function
+ *
+ * @param float Discount interest rate
+ * @param array Cash flow series
+ * @return float
+ * @static
+ * @access private
+ */
+ function _dnpv($rate, $values = array())
+ {
+ require_once 'Math/Finance_FunctionParameters.php';
+
+ if (!$values) {
+ // called from IRR
+ $parameters_class =& Math_Finance_FunctionParameters::getInstance();
+ $values = $parameters_class->parameters['values'];
+ }
+
+ $dnpv = 0;
+ $nper = count($values);
+ for ($i = 1; $i <= $nper; $i++) {
+ $dnpv += $values[$i-1] * (-$i) * pow(1 + $rate, $i - 1) / pow(1 + $rate, 2 * $i);
+ }
+ return $dnpv;
+ }
+
+ /**
+ * Returns the internal rate of return of a cash flow series, considering both financial and reinvestment rates
+ * Excel equivalent: MIRR
+ *
+ * @param array Cash flow series
+ * @param float Interest rate on the money used in the cash flow
+ * @param float Interest rate received when reinvested
+ * @return float
+ * @static
+ * @access public
+ */
+ function modifiedInternalRateOfReturn($values, $finance_rate, $reinvest_rate)
+ {
+ if (!is_array($values)) {
+ return PEAR::raiseError('The cash flow series most be an array');
+ }
+ if (min($values) * max($values) >= 0) {
+ return PEAR::raiseError('Cash flow must contain at least one positive value and one negative value');
+ }
+
+ $positive_flows = $negative_flows = array();
+ foreach ($values as $value) {
+ if ($value >= 0) {
+ $positive_flows[] = $value;
+ $negative_flows[] = 0;
+ } else {
+ $positive_flows[] = 0;
+ $negative_flows[] = $value;
+ }
+ }
+
+ $nper = count($values);
+
+ return pow(-Math_Finance::netPresentValue($reinvest_rate, $positive_flows) * pow(1 + $reinvest_rate, $nper)
+ / Math_Finance::netPresentValue($finance_rate, $negative_flows) / (1 + $finance_rate), 1/($nper - 1)) - 1;
+ }
+
+ /*******************************************************************
+ ** Bonds Functions *****
+ *******************************************************************/
+
+ /**
+ * Returns the difference of days between two dates based on a daycount basis
+ *
+ * @param int First date (UNIX timestamp)
+ * @param int Second date (UNIX timestamp)
+ * @param int Type of day count basis:
+ FINANCE_COUNT_NASD(default): US(NASD) 30/360
+ FINANCE_COUNT_ACTUAL_ACTUAL: Actual/actual
+ FINANCE_COUNT_ACTUAL_360: Actual/360
+ FINANCE_COUNT_ACTUAL_365: Actual/365
+ FINANCE_COUNT_EUROPEAN: European 30/360
+ * @return int
+ * @static
+ * @access public
+ */
+ function daysDifference($date1, $date2, $basis)
+ {
+ $y1 = date('Y', $date1);
+ $m1 = date('n', $date1);
+ $d1 = date('j', $date1);
+ $y2 = date('Y', $date2);
+ $m2 = date('n', $date2);
+ $d2 = date('j', $date2);
+
+ switch ($basis) {
+ case FINANCE_COUNT_NASD:
+ if ($d2 == 31 && ($d1 == 30 || $d1 == 31)) {
+ $d2 = 30;
+ }
+ if ($d1 == 31) {
+ $d1 = 30;
+ }
+ return ($y2 - $y1) * 360 + ($m2 - $m1) * 30 + $d2 - $d1;
+ case FINANCE_COUNT_ACTUAL_ACTUAL:
+ case FINANCE_COUNT_ACTUAL_360:
+ case FINANCE_COUNT_ACTUAL_365:
+ return ($date2 - $date1) / 86400;
+ case FINANCE_COUNT_EUROPEAN: // European 30/360
+ return ($y2 - $y1) * 360 + ($m2 - $m1) * 30 + $d2 - $d1;
+ }
+ }
+
+ /**
+ * Returns the number of days in the year based on a daycount basis
+ *
+ * @param int Year
+ * @param int Type of day count basis:
+ FINANCE_COUNT_NASD(default): US(NASD) 30/360
+ FINANCE_COUNT_ACTUAL_ACTUAL: Actual/actual
+ FINANCE_COUNT_ACTUAL_360: Actual/360
+ FINANCE_COUNT_ACTUAL_365: Actual/365
+ FINANCE_COUNT_EUROPEAN: European 30/360
+ * @return int
+ * @static
+ * @access public
+ */
+ function daysPerYear($year, $basis)
+ {
+ switch ($basis) {
+ case FINANCE_COUNT_NASD:
+ return 360;
+ case FINANCE_COUNT_ACTUAL_ACTUAL:
+ return checkdate(2, 29, $year)? 366 : 365;
+ case FINANCE_COUNT_ACTUAL_360:
+ return 360;
+ case FINANCE_COUNT_ACTUAL_365:
+ return 365;
+ case FINANCE_COUNT_EUROPEAN:
+ return 360;
+ }
+ }
+
+ /**
+ * Returns the yield for a treasury bill
+ * Excel equivalent: TBILLYIELD
+ *
+ * @param int Settlement date (UNIX timestamp)
+ * @param int Maturity date (UNIX timestamp)
+ * @param float TBill price per $100 face value
+ * @return float
+ * @static
+ * @access public
+ */
+ function TBillYield($settlement, $maturity, $pr)
+ {
+ if ($settlement >= $maturity) {
+ return PEAR::raiseError('Maturity must happen before settlement!');
+ }
+
+ $dsm = ($maturity - $settlement) / 86400; // transform to days
+
+ if ($dsm > 360) {
+ return PEAR::raiseError("maturity can't be more than one year after settlement");
+ }
+
+ return (100 - $pr) * 360 / $pr / $dsm;
+ }
+
+ /**
+ * Returns the price per $100 face value for a Treasury bill
+ * Excel equivalent: TBILLPRICE
+ *
+ * @param int Settlement date (UNIX timestamp)
+ * @param int Maturity date (UNIX timestamp)
+ * @param float T-Bill discount rate
+ * @return float
+ * @static
+ * @access public
+ */
+ function TBillPrice($settlement, $maturity, $discount)
+ {
+ if ($settlement >= $maturity) {
+ return PEAR::raiseError('Maturity must happen before settlement!');
+ }
+
+ $dsm = ($maturity - $settlement) / 86400; // transform to days
+
+ if ($dsm > 360) {
+ return PEAR::raiseError("maturity can't be more than one year after settlement");
+ }
+
+ return 100 * (1 - $discount * $dsm / 360);
+ }
+
+ /**
+ * Returns the bond-equivalent yield for a Treasury bill
+ * Excel equivalent: TBILLEQ
+ *
+ * @param int Settlement date (UNIX timestamp)
+ * @param int Maturity date (UNIX timestamp)
+ * @param float T-Bill discount rate
+ * @return float
+ * @static
+ * @access public
+ */
+ function TBillEquivalentYield($settlement, $maturity, $discount)
+ {
+ if ($settlement >= $maturity) {
+ return PEAR::raiseError('Maturity must happen before settlement!');
+ }
+
+ $dsm = Math_Finance::daysDifference($settlement, $maturity, FINANCE_COUNT_ACTUAL_365);
+
+ if ($dsm <= 182) {
+ // for one half year or less, the bond-equivalent-yield is equivalent to an actual/365 interest rate
+ return 365 * $discount / (360 - $discount * $dsm);
+ } elseif ($dsm == 366
+ && ((date('m', $settlement) <= 2 && checkdate(2, 29, date('Y', $settlement)))
+ || (date('m', $settlement) > 2 && checkdate(2, 29, date('Y', $maturity))))) {
+ return 2 * (sqrt(1 - $discount * 366 / ($discount * 366 - 360)) - 1);
+ } elseif ($dsm > 365) {
+ return PEAR::raiseError("maturity can't be more than one year after settlement");
+ } else {
+ // thanks to Zhang Qingpo (zhangqingpo@yahoo.com.cn) for solving this riddle :)
+ return (-$dsm + sqrt(pow($dsm, 2) - (2 * $dsm - 365) * $discount * $dsm * 365 / ($discount * $dsm - 360))) / ($dsm - 365 / 2);
+ }
+ }
+
+ /**
+ * Returns the discount rate for a bond
+ * Excel equivalent: DISC
+ *
+ * @param int Settlement date (UNIX timestamp)
+ * @param int Maturity date (UNIX timestamp)
+ * @param float The bond's price per $100 face value
+ * @param float The bond's redemption value per $100 face value
+ * @param int Type of day count basis:
+ FINANCE_COUNT_NASD(default): US(NASD) 30/360
+ FINANCE_COUNT_ACTUAL_ACTUAL: Actual/actual
+ FINANCE_COUNT_ACTUAL_360: Actual/360
+ FINANCE_COUNT_ACTUAL_365: Actual/365
+ FINANCE_COUNT_EUROPEAN: European 30/360
+ * @return float
+ * @static
+ * @access public
+ */
+ function discountRate($settlement, $maturity, $pr, $redemption, $basis = 0)
+ {
+ $days_per_year = Math_Finance::daysPerYear(date('Y', $settlement), $basis);
+ $dsm = Math_Finance::daysDifference($settlement, $maturity, $basis);
+
+ return ($redemption - $pr) * $days_per_year / $redemption / $dsm;
+ }
+
+ /**
+ * Returns the price per $100 face value of a discounted bond
+ * Excel equivalent: PRICEDISC
+ *
+ * @param int Settlement date (UNIX timestamp)
+ * @param int Maturity date (UNIX timestamp)
+ * @param float The bond's discount rate
+ * @param float The bond's redemption value per $100 face value
+ * @param int Type of day count basis:
+ FINANCE_COUNT_NASD(default): US(NASD) 30/360
+ FINANCE_COUNT_ACTUAL_ACTUAL: Actual/actual
+ FINANCE_COUNT_ACTUAL_360: Actual/360
+ FINANCE_COUNT_ACTUAL_365: Actual/365
+ FINANCE_COUNT_EUROPEAN: European 30/360
+ * @return float
+ * @static
+ * @access public
+ */
+ function priceDiscount($settlement, $maturity, $discount, $redemption, $basis = 0)
+ {
+ $days_per_year = Math_Finance::daysPerYear(date('Y', $settlement), $basis);
+ $dsm = Math_Finance::daysDifference($settlement, $maturity, $basis);
+
+ return $redemption - $discount * $redemption * $dsm / $days_per_year;
+ }
+
+
+ /*******************************************************************
+ ** Depreciation Functions *****
+ *******************************************************************/
+
+ /**
+ * Returns the depreciation of an asset using the fixed-declining balance method
+ * Excel equivalent: DB
+ *
+ * @param float The initial cost of the asset
+ * @param float Salvage value of the asset
+ * @param int Number of depreciation periods (same unit as $life)
+ * @param int Number of months in the first year, defaults to 12
+ * @return float
+ * @static
+ * @access public
+ */
+ function depreciationFixedDeclining($cost, $salvage, $life, $period, $month = 12)
+ {
+ $cost = (float) $cost;
+ $salvage = (float) $salvage;
+ $life = (int) $life;
+ $period = (int) $period;
+ $month = (int) $month;
+ if ($cost < 0 || $life < 0) {
+ return PEAR::raiseError('cost and life must be absolute positive numbers');
+ }
+ if ($period < 1) {
+ return PEAR::raiseError('period must be greater or equal than one');
+ }
+
+ $rate = 1 - pow(($salvage / $cost), (1 / $life));
+ $rate = round($rate, 3);
+
+ $acc_depreciation = 0;
+ for ($i = 1; $i <= $period; $i++) {
+ if ($i == 1) {
+ $depreciation_period = $cost * $rate * $month / 12;
+ } elseif ($i == ($life + 1)) {
+ $depreciation_period = ($cost - $acc_depreciation) * $rate * (12 - $month) / 12;
+ } else {
+ $depreciation_period = ($cost - $acc_depreciation) * $rate;
+ }
+ $acc_depreciation += $depreciation_period;
+ }
+
+ return $depreciation_period;
+ }
+
+ /**
+ * Returns the straight-line depreciation of an asset for each period
+ * Excel equivalent: SLN
+ *
+ * @param float The initial cost of the asset
+ * @param float Salvage value of the asset
+ * @param int Number of depreciation periods
+ * @return float
+ * @static
+ * @access public
+ */
+ function depreciationStraightLine($cost, $salvage, $life)
+ {
+ $life = (int) $life;
+ if ($cost < 0 || $life < 0) {
+ return PEAR::raiseError('cost and life must be absolute positive numbers');
+ }
+
+ return (($cost - $salvage) / $life);
+ }
+
+ /**
+ * Returns the depreciation for an asset in a given period using the sum-of-years' digits method
+ * Excel equivalent: SYD
+ *
+ * @param float The initial cost of the asset
+ * @param float Salvage value of the asset
+ * @param int Number of depreciation periods
+ * @param int Period (must be in the same unit as $life)
+ * @return float
+ * @static
+ * @access public
+ */
+ function depreciationSYD($cost, $salvage, $life, $per)
+ {
+ return (($cost - $salvage) * ($life - $per + 1) * 2 / ($life) / ($life +1));
+ }
+}
+
+?>
View
48 Math/Finance_FunctionParameters.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Singleton class to preserve given values of other variables in the callback functions
+ */
+class Math_Finance_FunctionParameters
+{
+ var $parameters = array();
+
+ /**
+ * Constructor. Should be private, so used little hack.
+ *
+ * @param bool Whether constructor has been called from a method of the class
+ * @param array Parameters (variables values of the function) to be preserved
+ * @access private
+ */
+ function Math_Finance_FunctionParameters($called_from_get_instance = False, $parameters = array())
+ {
+ // PHP4 hack
+ if (!$called_from_get_instance)
+ trigger_error("Cannot instantiate Math_Finance_FunctionParameters class directly (It's a Singleton)", E_USER_ERROR);
+
+ foreach ($parameters as $name => $value) {
+ $this->parameters[$name] = $value;
+ }
+ }
+
+ /**
+ * Method to be called statically to create Singleton
+ *
+ * @param array Parameters (variables values of the function) to be preserved
+ * @param bool Whether the Singleton should be reset
+ * @static
+ * @access public
+ */
+ function &getInstance($parameters = array(), $reset = False)
+ {
+ static $singleton;
+
+ if ($reset) $singleton = null;
+
+ if (!is_object($singleton)) {
+ $singleton = new Math_Finance_FunctionParameters(True, $parameters);
+ }
+
+ return $singleton;
+ }
+}
+?>
View
76 package.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE package SYSTEM "http://pear.php.net/dtd/package-1.0">
+<package version="1.0" packagerversion="1.4.5">
+ <name>Math_Finance</name>
+ <summary>Financial functions</summary>
+ <description>Collection of financial functions for time value of money (annuities), cash flow, interest rate conversions, bonds and depreciation calculations.
+ </description>
+ <maintainers>
+ <maintainer>
+ <user>alejandrop</user>
+ <name>Alejandro Pedraza</name>
+ <email>alejandro.pedraza@dataenlace.com</email>
+ <role>lead</role>
+ </maintainer>
+ </maintainers>
+ <release>
+ <version>1.0.0</version>
+ <date>2006-03-04</date>
+ <license>PHP License</license>
+ <state>stable</state>
+ <notes>- Fixed futureValue function: wasn&apos;t returning proper values for rate = 0.
+- Fixed TBillEquivalentYield function: was yielding wrong results for periods between half year and a year.
+- Added parameter boundary check for periods function.
+- Fixed some notices in _tvm and _dtvm private functions.
+- Added missing phpdoc parameter documentation for interestPayment and principalPayment functions.
+- Now depends on the Math_Numerical_RootFinding stable release.
+ </notes>
+ <deps>
+ <dep type="pkg" rel="ge" version="1.0.0">Math_Numerical_RootFinding</dep>
+ </deps>
+ <provides type="class" name="Math_Finance" />
+ <provides type="function" name="Math_Finance::effectiveRate" />
+ <provides type="function" name="Math_Finance::nominalRate" />
+ <provides type="function" name="Math_Finance::presentValue" />
+ <provides type="function" name="Math_Finance::futureValue" />
+ <provides type="function" name="Math_Finance::payment" />
+ <provides type="function" name="Math_Finance::periods" />
+ <provides type="function" name="Math_Finance::rate" />
+ <provides type="function" name="Math_Finance::interestPayment" />
+ <provides type="function" name="Math_Finance::principalPayment" />
+ <provides type="function" name="Math_Finance::netPresentValue" />
+ <provides type="function" name="Math_Finance::internalRateOfReturn" />
+ <provides type="function" name="Math_Finance::modifiedInternalRateOfReturn" />
+ <provides type="function" name="Math_Finance::daysDifference" />
+ <provides type="function" name="Math_Finance::daysPerYear" />
+ <provides type="function" name="Math_Finance::TBillYield" />
+ <provides type="function" name="Math_Finance::TBillPrice" />
+ <provides type="function" name="Math_Finance::TBillEquivalentYield" />
+ <provides type="function" name="Math_Finance::discountRate" />
+ <provides type="function" name="Math_Finance::priceDiscount" />
+ <provides type="function" name="Math_Finance::depreciationFixedDeclining" />
+ <provides type="function" name="Math_Finance::depreciationStraightLine" />
+ <provides type="function" name="Math_Finance::depreciationSYD" />
+ <provides type="class" name="Math_Finance_FunctionParameters" />
+ <provides type="function" name="Math_Finance_FunctionParameters::getInstance" />
+ <filelist>
+ <file role="php" baseinstalldir="/Math" md5sum="f36a3072da58194f1225133d9fc75047" name="Finance.php"/>
+ <file role="php" baseinstalldir="/Math" md5sum="e400edccf47e5dd3d084fafa8b858c2c" name="Finance_FunctionParameters.php"/>
+ <file role="test" baseinstalldir="/Math" md5sum="0e6b8280af11616a5cfd7c1e19c80646" name="tests/testMath_Finance.php"/>
+ </filelist>
+ </release>
+ <changelog>
+ <release>
+ <version>1.0.0</version>
+ <date>2006-03-04</date>
+ <state>stable</state>
+ <notes>- Fixed futureValue function: wasn&apos;t returning proper values for rate = 0.
+- Fixed TBillEquivalentYield function: was yielding wrong results for periods between half year and a year.
+- Added parameter boundary check for periods function.
+- Fixed some notices in _tvm and _dtvm private functions.
+- Added missing phpdoc parameter documentation for interestPayment and principalPayment functions.
+- Now depends on the Math_Numerical_RootFinding stable release.
+ </notes>
+ </release>
+ </changelog>
+</package>
View
289 tests/testMath_Finance.php
@@ -0,0 +1,289 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+//require_once 'Math/Finance.php';
+require_once 'Math/Finance.php';
+require_once 'PHPUnit.php';
+
+Class FinanceTestCase extends PHPUnit_TestCase
+{
+ var $finance;
+
+ /*function testFunctionName()
+ {
+ $this->assertTrue(expr)
+ $this->assertFalse(expr)
+ $this->assertEquals(expected, actual) // uses ==
+ $this->assertNull(expr)
+ $this->assertSame(expected, actual) // uses ===
+ $this->assertNotSame(expcted, actual)
+ $this->assertRegExp(expected, actual) // PCRE regexpses
+ }*/
+
+ function setUp()
+ {
+ }
+
+ /*******************************************************************
+ ** Interest Rates Conversion Functions *****
+ *******************************************************************/
+
+ function testeffectiveRate()
+ {
+ // various random calculations
+ $this->assertTrue(abs(0.14597025 - Math_Finance::effectiveRate(0.141, 2)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.14553980 - Math_Finance::effectiveRate(0.139, 3)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.00903042 - Math_Finance::effectiveRate(0.009, 4)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.44 - Math_Finance::effectiveRate(0.4, 2)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.141 - Math_Finance::effectiveRate(0.141, 1)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.14694625 - Math_Finance::effectiveRate(0.1390, 5)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.00904009 - Math_Finance::effectiveRate(0.0090, 77)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.48285538 - Math_Finance::effectiveRate(0.40, 13)) < FINANCE_PRECISION);
+
+ // cannot pass negative number of periods per year
+ $this->assertType('object', Math_Finance::effectiveRate(0.40, -13));
+ }
+
+ function testnominalRate()
+ {
+ // various random calculations
+ $this->assertTrue(abs(0.497999 - Math_Finance::nominalRate(0.56, 2)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.303643 - Math_Finance::nominalRate(0.34, 4)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.611767 - Math_Finance::nominalRate(0.7450, 3)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.117417 - Math_Finance::nominalRate(0.1245, 88)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.031554 - Math_Finance::nominalRate(0.0320, 9)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.263683 - Math_Finance::nominalRate(0.2930, 5)) < FINANCE_PRECISION);
+
+ // cannot pass negative number of periods per year
+ $this->assertType('object', Math_Finance::nominalRate(0.2930, -5));
+ }
+
+
+ /*******************************************************************
+ ** TVM (annuities) Functions *****
+ *******************************************************************/
+
+ function testpresentValue()
+ {
+ // various random calculations
+ $this->assertTrue(abs(-4909.073704 - Math_Finance::presentValue(0.08, 20, 500)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(-915.941437 - Math_Finance::presentValue(0.03, 5, 200)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(-286.821438 - Math_Finance::presentValue(0.29, 7, 100)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(-700.000000 - Math_Finance::presentValue(0, 7, 100)) < FINANCE_PRECISION);
+
+ // cannot pass negative number of periods
+ $this->assertType('object', Math_Finance::presentValue(0.29, -7, 100));
+ // cannot pass a type different from 0 and 1
+ $this->assertType('object', Math_Finance::presentValue(0.29, 7, 100, 0, 3));
+ }
+
+ function testfutureValue()
+ {
+ // various random calculations
+ $this->assertTrue(abs(-22880.982149 - Math_Finance::futureValue(0.08, 20, 500)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(-1061.827162 - Math_Finance::futureValue(0.03, 5, 200)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(-1705.059664 - Math_Finance::futureValue(0.29, 7, 100)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(-700.000000 - Math_Finance::futureValue(0, 7, 100)) < FINANCE_PRECISION);
+
+ // cannot pass negative number of periods
+ $this->assertType('object', Math_Finance::futureValue(0.29, -7, 100));
+ // cannot pass a type differenet from 0 and 1
+ $this->assertType('object', Math_Finance::futureValue(0.29, 7, 100, 0, 3));
+ }
+
+ function testpayment()
+ {
+ // various random calculations
+ $this->assertTrue(abs(-36.157534 - Math_Finance::payment(0.08, 20, 355)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(-180.797585 - Math_Finance::payment(0.03, 5, 828)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(-166.305561 - Math_Finance::payment(0.29, 7, 477)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(-62.142857 - Math_Finance::payment(0, 7, 435)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(-258.137498 - Math_Finance::payment(0.1/12, 3*12, 8000, 0, FINANCE_PAY_END)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(-256.004130 - Math_Finance::payment(0.1/12, 3*12, 8000, 0, FINANCE_PAY_BEGIN)) < FINANCE_PRECISION);
+
+ // cannot pass negative number of periods
+ $this->assertType('object', Math_Finance::payment(0.29, -7, 435));
+ // cannot pass a type differenet from 0 and 1
+ $this->assertType('object', Math_Finance::payment(0.29, 7, 435, 0, 3));
+ }
+
+ function testperiods()
+ {
+ // various random calculations
+ $this->assertTrue(abs(0.759825 - Math_Finance::periods(0.08, -500, 355)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(4.486566 - Math_Finance::periods(0.03, -200, 828)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.084641 - Math_Finance::periods(0.45, -5000, 344)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(4.350000 - Math_Finance::periods(0, -100, 435)) < FINANCE_PRECISION);
+
+ // arguments check
+ $this->assertType('object', Math_Finance::periods(0.29, 100, 477, 0, 3));
+ $this->assertType('object', Math_Finance::periods(0.5, 0, 0));
+ $this->assertType('object', Math_Finance::periods(0, 0, 0));
+ }
+
+ function testrate()
+ {
+ // various random calculations
+ $this->assertTrue(abs(0.08000 - Math_Finance::rate(20, -36.157534, 355, 0, FINANCE_PAY_END, 0.1)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.03000 - Math_Finance::rate(5, -180.797585, 828, 0, FINANCE_PAY_END, 0.1)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.45000 - Math_Finance::rate(2, -295.208163, 344, 0, FINANCE_PAY_END, 0.1)) < FINANCE_PRECISION);
+
+ // cannot pass a type differenet from 0 and 1
+ $this->assertType('object', Math_Finance::rate(20, -36.157534, 255, 0, 3));
+ }
+
+ function testinterestPayment()
+ {
+ // various random calculations
+ $this->assertTrue(abs(-63.462189 - Math_Finance::interestPayment(0.1/12, 3, 3*12, 8000)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(-46.617168 - Math_Finance::interestPayment(0.1/12, 13, 3*12, 8000)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(-8.428265 - Math_Finance::interestPayment(0.1/12, 33, 3*12, 8000)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(-62.937708 - Math_Finance::interestPayment(0.1/12, 3, 3*12, 8000, 0, FINANCE_PAY_BEGIN)) < FINANCE_PRECISION);
+
+ // cannot pass a type differenet from 0 and 1
+ $this->assertType('object', Math_Finance::interestPayment(0.1/12, 3, 3*12, 8000, 0, 2));
+ }
+
+ function testprincipalPayment()
+ {
+ // various random calculations
+ $this->assertTrue(abs(-194.675308 - Math_Finance::principalPayment(0.1/12, 3, 3*12, 8000)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(-211.5203289 - Math_Finance::principalPayment(0.1/12, 13, 3*12, 8000)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(-249.709231 - Math_Finance::principalPayment(0.1/12, 33, 3*12, 8000)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(-193.066421 - Math_Finance::principalPayment(0.1/12, 3, 3*12, 8000, 0, FINANCE_PAY_BEGIN)) < FINANCE_PRECISION);
+
+ // cannot pass a type differenet from 0 and 1
+ $this->assertType('object', Math_Finance::principalPayment(0.1/12, 3, 3*12, 8000, 0, 2));
+ }
+
+ function testnetPresentValue()
+ {
+ // various random calculations
+ $this->assertTrue(abs(1188.443412 - Math_Finance::netPresentValue(0.1, array(-10000, 3000, 4200, 6800))) < FINANCE_PRECISION);
+
+ // cash flow series must be an array
+ $this->assertType('object', Math_Finance::netPresentValue(0.1, -1000, 3000, 4200, 6800));
+ }
+
+ function testinternalRateOfReturn()
+ {
+ // various random calculations
+ $this->assertTrue(abs(-0.02124485 - Math_Finance::internalRateOfReturn(array(-70000, 12000, 15000, 18000, 21000))) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.086630 - Math_Finance::internalRateOfReturn(array(-70000, 12000, 15000, 18000, 21000, 26000))) < FINANCE_PRECISION);
+ $this->assertTrue(abs(-0.443507 - Math_Finance::internalRateOfReturn(array(-70000, 12000, 15000), -0.40)) < FINANCE_PRECISION);
+
+ // cash flow series must be an array
+ $this->assertType('object', Math_Finance::internalRateOfReturn(-70000, 12000, 15000, 18000, 21000));
+ // cash flow must contain at least one positive value and one negative value
+ $this->assertType('object', Math_Finance::internalRateOfReturn(array(70000, 12000, 15000, 18000, 21000)));
+ }
+
+ function testdaysDifference()
+ {
+ $this->assertTrue(62 == Math_Finance::daysDifference(mktime(0, 0, 0, 7, 1, 2005), mktime(0, 0, 0, 9, 1, 2005), FINANCE_COUNT_ACTUAL_365));
+ }
+
+ function testmodifiedInternalRateOfReturn()
+ {
+ // various random calculations
+ $this->assertTrue(abs(0.126094 - Math_Finance::modifiedInternalRateOfReturn(array(-120000, 39000, 30000, 21000, 37000, 46000), 0.10, 0.12)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(-0.048044 - Math_Finance::modifiedInternalRateOfReturn(array(-120000, 39000, 30000, 21000), 0.10, 0.12)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.134759 - Math_Finance::modifiedInternalRateOfReturn(array(-120000, 39000, 30000, 21000, 37000, 46000), 0.10, 0.14)) < FINANCE_PRECISION);
+
+ // cash flow series must be an array
+ $this->assertType('object', Math_Finance::modifiedInternalRateOfReturn(-70000, 12000, 15000, 18000, 21000, 0.10, 0.12));
+ // cash flow must contain at least one positive value and one negative value
+ $this->assertType('object', Math_Finance::modifiedInternalRateOfReturn(array(70000, 12000, 15000, 18000, 21000), 0.10, 0.12));
+ }
+
+ function testTBillYield()
+ {
+ // various random calculations
+ $this->assertTrue(abs(0.091417- Math_Finance::TBillYield(mktime(0, 0, 0, 3, 31, 2008), mktime(0, 0, 0, 6, 1, 2008), 98.45)) < FINANCE_PRECISION);
+
+ // settlement must be before maturity
+ $this->assertType('object', Math_Finance::TBillYield(mktime(0, 0, 0, 6, 1, 2008), mktime(0, 0, 0, 3, 31, 2008), 98.45));
+ // maturity can't be more than one year after settlement
+ $this->assertType('object', Math_Finance::TBillYield(mktime(0, 0, 0, 6, 1, 2008), mktime(0, 0, 0, 3, 31, 2010), 98.45));
+ }
+
+ function testTBillPrice()
+ {
+ // various random calculations
+ $this->assertTrue(abs(98.450000- Math_Finance::TBillPrice(mktime(0, 0, 0, 3, 31, 2008), mktime(0, 0, 0, 6, 1, 2008), 0.09)) < FINANCE_PRECISION);
+
+ // settlement must be before maturity
+ $this->assertType('object', Math_Finance::TBillPrice(mktime(0, 0, 0, 6, 1, 2008), mktime(0, 0, 0, 3, 31, 2008), 0.09));
+ // maturity can't be more than one year after settlement
+ $this->assertType('object', Math_Finance::TBillPrice(mktime(0, 0, 0, 6, 1, 2008), mktime(0, 0, 0, 3, 31, 2010), 0.09));
+ }
+
+ function testTBillEquivalentYield()
+ {
+ // various random calculations
+ $this->assertTrue(abs(0.094151- Math_Finance::TBillEquivalentYield(mktime(0, 0, 0, 3, 31, 2008), mktime(0, 0, 0, 6, 1, 2008), 0.0914)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.09773985- Math_Finance::TBillEquivalentYield(mktime(0, 0, 0, 3, 20, 2008), mktime(0, 0, 0, 12, 1, 2008), 0.0914)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.09778005- Math_Finance::TBillEquivalentYield(mktime(0, 0, 0, 3, 31, 1993), mktime(0, 0, 0, 12, 15, 1993), 0.0914)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.09965155- Math_Finance::TBillEquivalentYield(mktime(0, 0, 0, 1, 10, 1999), mktime(0, 0, 0, 1, 10, 2000), 0.0914)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.09994538- Math_Finance::TBillEquivalentYield(mktime(0, 0, 0, 1, 10, 2000), mktime(0, 0, 0, 1, 10, 2001), 0.0914)) < FINANCE_PRECISION);
+
+ // settlement must be before maturity
+ $this->assertType('object', Math_Finance::TBillEquivalentYield(mktime(0, 0, 0, 6, 1, 2008), mktime(0, 0, 0, 3, 31, 2008), 0.09));
+ // maturity can't be more than one year after settlement
+ $this->assertType('object', Math_Finance::TBillEquivalentYield(mktime(0, 0, 0, 6, 1, 2008), mktime(0, 0, 0, 3, 31, 2010), 0.09));
+ }
+
+ function testdiscountRate()
+ {
+ // various random calculations
+ $this->assertTrue(abs(0.052071 - Math_Finance::discountRate(mktime(0, 0, 0, 1, 25, 2007), mktime(0, 0, 0, 6, 15, 2007), 97.975, 100, FINANCE_COUNT_NASD)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.052420 - Math_Finance::discountRate(mktime(0, 0, 0, 1, 25, 2007), mktime(0, 0, 0, 6, 15, 2007), 97.975, 100, FINANCE_COUNT_ACTUAL_ACTUAL)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.051702- Math_Finance::discountRate(mktime(0, 0, 0, 1, 25, 2007), mktime(0, 0, 0, 6, 15, 2007), 97.975, 100, FINANCE_COUNT_ACTUAL_360)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.052420- Math_Finance::discountRate(mktime(0, 0, 0, 1, 25, 2007), mktime(0, 0, 0, 6, 15, 2007), 97.975, 100, FINANCE_COUNT_ACTUAL_365)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(0.052071- Math_Finance::discountRate(mktime(0, 0, 0, 1, 25, 2007), mktime(0, 0, 0, 6, 15, 2007), 97.975, 100, FINANCE_COUNT_EUROPEAN)) < FINANCE_PRECISION);
+ }
+
+ function testpriceDiscount()
+ {
+ // various random calculations
+ $this->assertTrue(abs(99.781250 - Math_Finance::priceDiscount(mktime(0, 0, 0, 2, 16, 2008), mktime(0, 0, 0, 3, 1, 2008), 0.0525, 100, FINANCE_COUNT_NASD)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(99.799180 - Math_Finance::priceDiscount(mktime(0, 0, 0, 2, 16, 2008), mktime(0, 0, 0, 3, 1, 2008), 0.0525, 100, FINANCE_COUNT_ACTUAL_ACTUAL)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(99.795833 - Math_Finance::priceDiscount(mktime(0, 0, 0, 2, 16, 2008), mktime(0, 0, 0, 3, 1, 2008), 0.0525, 100, FINANCE_COUNT_ACTUAL_360)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(99.798630 - Math_Finance::priceDiscount(mktime(0, 0, 0, 2, 16, 2008), mktime(0, 0, 0, 3, 1, 2008), 0.0525, 100, FINANCE_COUNT_ACTUAL_365)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(99.781250 - Math_Finance::priceDiscount(mktime(0, 0, 0, 2, 16, 2008), mktime(0, 0, 0, 3, 1, 2008), 0.0525, 100, FINANCE_COUNT_EUROPEAN)) < FINANCE_PRECISION);
+ }
+
+
+ /*******************************************************************
+ ** Depreciation Functions *****
+ *******************************************************************/
+ function testdepreciationFixedDeclining()
+ {
+ $this->assertTrue(abs(186083.333333 - Math_Finance::depreciationFixedDeclining(1000000, 100000, 6, 1, 7)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(186083.333333 - Math_Finance::depreciationFixedDeclining(1000000, 100000, 6, 1, 7)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(259639.416667 - Math_Finance::depreciationFixedDeclining(1000000, 100000, 6, 2, 7)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(176814.442750 - Math_Finance::depreciationFixedDeclining(1000000, 100000, 6, 3, 7)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(120410.635513 - Math_Finance::depreciationFixedDeclining(1000000, 100000, 6, 4, 7)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(81999.642784 - Math_Finance::depreciationFixedDeclining(1000000, 100000, 6, 5, 7)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(55841.756736 - Math_Finance::depreciationFixedDeclining(1000000, 100000, 6, 6, 7)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(15845.098474 - Math_Finance::depreciationFixedDeclining(1000000, 100000, 6, 7, 7)) < FINANCE_PRECISION);
+ }
+
+ function testdepreciationStraightLine()
+ {
+ $this->assertTrue(abs(2250 - Math_Finance::depreciationStraightLine(30000, 7500, 10)) < FINANCE_PRECISION);
+ }
+
+ function testdepreciationSYD()
+ {
+ $this->assertTrue(abs(4090.909091 - Math_Finance::depreciationSYD(30000, 7500, 10, 1)) < FINANCE_PRECISION);
+ $this->assertTrue(abs(409.090909 - Math_Finance::depreciationSYD(30000, 7500, 10, 10)) < FINANCE_PRECISION);
+ }
+}
+
+$suite = new PHPUnit_TestSuite('FinanceTestCase');
+$result = PHPUnit::run($suite);
+echo $result->toString();
+
+?>

0 comments on commit 63651fb

Please sign in to comment.
Something went wrong with that request. Please try again.