Source for file Finance.php
Documentation is available at Finance.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.
* 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.
* @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: Finance.php 274632 2009-01-26 14:29:28Z clockwerx $
* @link http://pear.php.net/Math/Finance
* @since File available since Release 1.2.0
// to be able to throw PEAR errors
// precision of calculations
define('FINANCE_PRECISION', 1E-6 );
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
* @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
/*******************************************************************
** 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 PEAR ::raiseError ('Number of compounding payments per year is not positive');
$effect = pow((1 + $nominal_rate / $npery), $npery) - 1;
* 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 PEAR ::raiseError ('Number of compounding payments per year is not positive');
$nominal = $npery * (pow($effect_rate + 1 , 1/ $npery) - 1 );
/*******************************************************************
** TVM (annuities) Functions *****
*******************************************************************/
* Returns the Present Value of a cash flow with constant payments and interest rate (annuities)
* 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
function presentValue($rate, $nper, $pmt, $fv = 0 , $type = 0 )
return PEAR ::raiseError ('Number of periods must be positive');
return PEAR ::raiseError ('Payment type must be FINANCE_PAY_END or FINANCE_PAY_BEGIN');
$pv = (- $pmt * (1 + $rate * $type) * ((pow(1 + $rate, $nper) - 1 ) / $rate) - $fv) / pow(1 + $rate, $nper);
$pv = - $fv - $pmt * $nper;
* Returns the Future Value of a cash flow with constant payments and interest rate (annuities)
* @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
function futureValue($rate, $nper, $pmt, $pv = 0 , $type = 0 )
return PEAR ::raiseError ('Number of periods must be positive');
return PEAR ::raiseError ('Payment type must be FINANCE_PAY_END or FINANCE_PAY_BEGIN');
$fv = - $pv * pow(1 + $rate, $nper) - $pmt * (1 + $rate * $type) * (pow(1 + $rate, $nper) - 1 ) / $rate;
$fv = - $pv - $pmt * $nper;
* Returns the constant payment (annuity) for a cash flow with a constant 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
function payment($rate, $nper, $pv, $fv = 0 , $type = 0 )
return PEAR ::raiseError ('Number of periods must be positive');
return PEAR ::raiseError ('Payment type must be FINANCE_PAY_END or FINANCE_PAY_BEGIN');
$pmt = (- $fv - $pv * pow(1 + $rate, $nper)) / (1 + $rate * $type) / ((pow(1 + $rate, $nper) - 1 ) / $rate);
$pmt = (- $pv - $fv) / $nper;
* Returns the number of periods for a cash flow with constant periodic payments (annuities), and interest rate
* @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
function periods($rate, $pmt, $pv, $fv = 0 , $type = 0 )
return PEAR ::raiseError ('Payment type must be FINANCE_PAY_END or FINANCE_PAY_BEGIN');
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))
return PEAR ::raiseError ('Rate and Payment can\'t be both zero');
$nper = (- $pv - $fv) / $pmt;
* Returns the periodic interest rate for a cash flow with constant periodic payments (annuities)
* @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
function rate($nper, $pmt, $pv, $fv = 0 , $type = 0 , $guess = 0.1 )
require_once 'Math/Numerical/RootFinding/NewtonRaphson.php';
// To preserve some variables in the Newton-Raphson callback functions
require_once 'Math/Finance_FunctionParameters.php';
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_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
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
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 )
($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)
* @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 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)
* @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 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
function _interestAndPrincipal ($rate, $per, $nper, $pv, $fv, $type)
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;
//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
* @param float Discount interest rate
* @param array Cash flow series
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
* @param array Cash flow series
* @param float guess for the interest rate
require_once 'Math/Numerical/RootFinding/NewtonRaphson.php';
// To preserve some variables in the Newton-Raphson callback functions
require_once 'Math/Finance_FunctionParameters.php';
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
function _npv ($rate, $values = array ())
require_once 'Math/Finance_FunctionParameters.php';
$parameters_class = & Math_Finance_FunctionParameters ::getInstance ();
$values = $parameters_class->parameters ['values'];
for ($i = 1; $i <= $nper; $i++ ) {
$npv += $values[$i-1 ]/ pow(1 + $rate, $i);
* 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
function _dnpv ($rate, $values = array ())
require_once 'Math/Finance_FunctionParameters.php';
$parameters_class = & Math_Finance_FunctionParameters ::getInstance ();
$values = $parameters_class->parameters ['values'];
for ($i = 1; $i <= $nper; $i++ ) {
$dnpv += $values[$i-1 ] * (- $i) * pow(1 + $rate, $i - 1 ) / pow(1 + $rate, 2 * $i);
* Returns the internal rate of return of a cash flow series, considering both financial and reinvestment rates
* @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 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) {
$positive_flows[] = $value;
$negative_flows[] = $value;
/*******************************************************************
*******************************************************************/
* 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
if ($d2 == 31 && ($d1 == 30 || $d1 == 31 )) {
return ($y2 - $y1) * 360 + ($m2 - $m1) * 30 + $d2 - $d1;
return ($date2 - $date1) / 86400;
return ($y2 - $y1) * 360 + ($m2 - $m1) * 30 + $d2 - $d1;
* Returns the number of days in the year based on a daycount basis
* @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
* 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
if ($settlement >= $maturity) {
return PEAR ::raiseError ('Maturity must happen before settlement!');
$dsm = ($maturity - $settlement) / 86400; // transform to days
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
function TBillPrice($settlement, $maturity, $discount)
if ($settlement >= $maturity) {
return PEAR ::raiseError ('Maturity must happen before settlement!');
$dsm = ($maturity - $settlement) / 86400; // transform to days
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
if ($settlement >= $maturity) {
return PEAR ::raiseError ('Maturity must happen before settlement!');
// for one half year or less, the bond-equivalent-yield is equivalent to an actual/365 interest rate
return 365 * $discount / (360 - $discount * $dsm);
return 2 * (sqrt(1 - $discount * 366 / ($discount * 366 - 360 )) - 1 );
return PEAR ::raiseError ("maturity can't be more than one year after settlement");
// 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
* @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
function discountRate($settlement, $maturity, $pr, $redemption, $basis = 0 )
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
function priceDiscount($settlement, $maturity, $discount, $redemption, $basis = 0 )
return $redemption - $discount * $redemption * $dsm / $days_per_year;
/*******************************************************************
** Depreciation Functions *****
*******************************************************************/
* Returns the depreciation of an asset using the fixed-declining balance method
* @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
$salvage = (float) $salvage;
if ($cost < 0 || $life < 0 ) {
return PEAR ::raiseError ('cost and life must be absolute positive numbers');
return PEAR ::raiseError ('period must be greater or equal than one');
$rate = 1 - pow(($salvage / $cost), (1 / $life));
for ($i = 1; $i <= $period; $i++ ) {
$depreciation_period = $cost * $rate * $month / 12;
} elseif ($i == ($life + 1 )) {
$depreciation_period = ($cost - $acc_depreciation) * $rate * (12 - $month) / 12;
$depreciation_period = ($cost - $acc_depreciation) * $rate;
$acc_depreciation += $depreciation_period;
return $depreciation_period;
* Returns the straight-line depreciation of an asset for each period
* @param float The initial cost of the asset
* @param float Salvage value of the asset
* @param int Number of depreciation periods
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
* @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 (($cost - $salvage) * ($life - $per + 1 ) * 2 / ($life) / ($life +1 ));
Documentation generated on Sun, 24 Oct 2010 08:00:02 +0000 by phpDocumentor 1.4.3. PEAR Logo Copyright © PHP Group 2004.
|