Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- /**
- * EGM Mathematical Finance class.
- *
- * Financial functions with the Excel function names and
- * parameter order.
- *
- * @version $Id: financial_class.php,v 1.0.6 2004-06-30 13:20:56-05 egarcia Exp $
- * @author Enrique García M. <[email protected]>
- * @copyright (c) 2003-2004 Enrique García M.
- * @since Saturday, January 7, 2003
- **/
- /***************************************************************************
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- ***************************************************************************/
- /**
- * Constants to define the accuracy in numeric aproximations, and the max
- * iterations to solve
- */
- define('FINANCIAL_ACCURACY', 1.0e-6);
- define('FINANCIAL_MAX_ITERATIONS', 100);
- define('FINANCIAL_SECS_PER_DAY', 24 * 60 * 60);
- define('FINANCIAL_HALF_SEC', 0.5 / FINANCIAL_SECS_PER_DAY);
- /**
- * Constants for the day-counting
- * rules are mandated by NASD Uniform Practice Code, Section 46, and MSRB Rule G-33,
- * described in Mayle (1993, 17?23, A3?A15). See Public Securities Association (1990)
- * for other rules applicable to state and municipal bonds.
- *
- * base 0: BASIS_MSRB_30_360
- * MSRB/NSAD 30/360 daycount method
- * see MSRB Rule G-33
- * http://www.msrb.org/msrb1/rules/ruleg33.htm
- * http://www.msrb.org/msrb1/rules/interpg33.htm
- * Number of days = (Y2 - Y1) 360 + (M2 - M1) 30 + (D2 - D1)
- * The variables "Yl," "M1," and "D1" are defined as the year, month, and day, respectively,
- * of the date on which the computation period begins (June 15, 1982, in your example),
- * and "Y2,a" "M2," and "D2" as the year, month, and day of the date on which the
- * computation period ends (July 1, 1982, in your example).
- * For purposes of this formula, if the symbol "D2" has a value of "31," and the symbol "D1"
- * has a value of "30" or "31," the value of the symbol "D2" shall be changed to "30."
- * If the symbol "D1" has a value of "31," the value of the symbol "D1" shall be changed to
- * "30." For purposes of this rule time periods shall be computed to include the day
- * specified in the rule for the beginning of the period but not to include the day
- * specified for the end of the period.
- *
- * base 1: BASIS_ACTACT
- * Actual/Actual daycount method
- * date adjustment: no change
- * date difference: serial delta (# of days)
- *
- * base 2: BASIS_ACT_360
- * Actual/360 daycount method
- * date adjustment: no change
- * date difference: serial delta
- * 360/freq for length of coupon period
- *
- * base 3: BASIS_ACT_365
- * Actual/365 daycount method (short term and Canadian bonds only)
- * date adjustment: no change
- * date difference: serial delta
- * 365/freq for length of coupon period, (with decimal answer)
- * base 4: BASIS_30E_360
- * date adjustment: from_date is changed from 31st to 30th
- * to_date is changed from 31st to 30th
- * date difference: each month 30 days, within a month serial delta
- * base 5: BASIS_30Ep_360
- * date adjustment: from_date is changed from 31st to 30th
- * to_date is changed from 31st to 1st of following month
- * date difference: each month 30 days, within a month serial delta
- */
- define('FINANCIAL_BASIS_MSRB_30_360', 0);
- define('FINANCIAL_BASIS_ACT_ACT', 1);
- define('FINANCIAL_BASIS_ACT_360', 2);
- define('FINANCIAL_BASIS_ACT_365', 3);
- define('FINANCIAL_BASIS_30E_360', 4);
- define('FINANCIAL_BASIS_30Ep_360', 5);
- class Financial
- {
- function Financial()
- {
- // forces the precision for calculations
- ini_set('precision', '14');
- }
- /**
- * DATEADD
- * Returns a new Unix timestamp value based on adding an interval to the specified date.
- * @param string $datepart is the parameter that specifies on which part of the date to return a new value.
- * @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.
- * @param integer $date a Unix timestamp value.
- * @return integer a Unix timestamp.
- */
- function DATEADD($datepart, $number, $date)
- {
- $number = intval($number);
- switch (strtolower($datepart)) {
- case 'yy':
- case 'yyyy':
- case 'year':
- $d = getdate($date);
- $d['year'] += $number;
- if (($d['mday'] == 29) && ($d['mon'] == 2) && (date('L', mktime(0, 0, 0, 1, 1, $d['year'])) == 0)) $d['mday'] = 28;
- return mktime($d['hours'], $d['minutes'], $d['seconds'], $d['mon'], $d['mday'], $d['year']);
- break;
- case 'm':
- case 'mm':
- case 'month':
- $d = getdate($date);
- $d['mon'] += $number;
- while($d['mon'] > 12) {
- $d['mon'] -= 12;
- $d['year']++;
- }
- while($d['mon'] < 1) {
- $d['mon'] += 12;
- $d['year']--;
- }
- $l = date('t', mktime(0,0,0,$d['mon'],1,$d['year']));
- if ($d['mday'] > $l) $d['mday'] = $l;
- return mktime($d['hours'], $d['minutes'], $d['seconds'], $d['mon'], $d['mday'], $d['year']);
- break;
- case 'd':
- case 'dd':
- case 'day':
- return ($date + $number * 86400);
- break;
- default:
- die("Unsupported operation");
- }
- }
- /**
- * DATEDIFF
- * Returns the number of date and time boundaries crossed between two specified dates.
- * @param string $datepart is the parameter that specifies on which part of the date to calculate the difference.
- * @param integer $startdate is the beginning date (Unix timestamp) for the calculation.
- * @param integer $enddate is the ending date (Unix timestamp) for the calculation.
- * @return integer the number between the two dates.
- */
- function DATEDIFF($datepart, $startdate, $enddate)
- {
- switch (strtolower($datepart)) {
- case 'yy':
- case 'yyyy':
- case 'year':
- $di = getdate($startdate);
- $df = getdate($enddate);
- return $df['year'] - $di['year'];
- break;
- case 'q':
- case 'qq':
- case 'quarter':
- die("Unsupported operation");
- break;
- case 'n':
- case 'mi':
- case 'minute':
- return ceil(($enddate - $startdate) / 60);
- break;
- case 'hh':
- case 'hour':
- return ceil(($enddate - $startdate) / 3600);
- break;
- case 'd':
- case 'dd':
- case 'day':
- return ceil(($enddate - $startdate) / 86400);
- break;
- case 'wk':
- case 'ww':
- case 'week':
- return ceil(($enddate - $startdate) / 604800);
- break;
- case 'm':
- case 'mm':
- case 'month':
- $di = getdate($startdate);
- $df = getdate($enddate);
- return ($df['year'] - $di['year']) * 12 + ($df['mon'] - $di['mon']);
- break;
- default:
- die("Unsupported operation");
- }
- }
- /**
- * Determine if the basis is valid
- * @param integer $basis
- * @return bool
- */
- function _is_valid_basis($basis)
- {
- return (($basis >= FINANCIAL_BASIS_MSRB_30_360) && ($basis <= FINANCIAL_BASIS_30Ep_360));
- }
- /**
- * Determine if the frequency is valid
- * @param integer $frequency
- * @return bool
- */
- function _is_valid_frequency($frequency)
- {
- return (($frequency == 1) || ($frequency == 2) || ($frequency == 4));
- }
- /**
- * DAYS360
- * Returns the number of days between two dates based on a 360-day year
- * (twelve 30-day months), which is used in some accounting calculations.
- * Use this function to help compute payments if your accounting system
- * is based on twelve 30-day months.
- * @param integer $start_date is the beginning date (Unix timestamp) for the calculation.
- * @param integer $end_date is the ending date (Unix timestamp) for the calculation.
- * @param bool $method is a logical value that specifies whether to use the U.S. or European method in the calculation.
- * @return integer the number of days between two dates based on a 360-day year
- */
- function DAYS360($start_date, $end_date, $method = false)
- {
- if ($method) {
- return $this->Thirty360USdayCount($start_date, $end_date);
- } else {
- return $this->Thirty360EUdayCount($start_date, $end_date);
- }
- }
- /**
- * Thirty360USdayCount
- * Returns the number of days between two dates based on a 360-day year
- * (twelve 30-day months) using the US method.
- * @param integer $startdate is the beginning date (Unix timestamp) for the calculation.
- * @param integer $enddate is the ending date (Unix timestamp) for the calculation.
- * @return integer the number of days between two dates
- */
- function Thirty360USdayCount($startdate, $enddate)
- {
- $d1 = getdate($startdate);
- $d2 = getdate($enddate);
- $dd1 = $d1['mday']; $mm1 = $d1['mon']; $yy1 = $d1['year'];
- $dd2 = $d2['mday']; $mm2 = $d2['mon']; $yy2 = $d2['year'];
- if ($dd2 == 31 && $dd1 < 30) { $dd2 = 1; $mm2++; }
- return 360 * ($yy2 - $yy1) + 30 * ($mm2 - $mm1 - 1) + max(0, 30 - $dd1) + min(30, $dd2);
- }
- /**
- * Thirty360USyearFraction
- * Returns the period between two dates as a fraction of year.
- * @param integer $startdate is the beginning date (Unix timestamp) for the calculation.
- * @param integer $enddate is the ending date (Unix timestamp) for the calculation.
- * @return float the fraction of years between two dates
- */
- function Thirty360USyearFraction($startdate, $enddate)
- {
- return $this->Thirty360USdayCount($startdate, $enddate) / 360.0;
- }
- /**
- * Thirty360EUdayCount
- * Returns the number of days between two dates based on a 360-day year
- * (twelve 30-day months) using the EUROPEAN method.
- * @param integer $startdate is the beginning date (Unix timestamp) for the calculation.
- * @param integer $enddate is the ending date (Unix timestamp) for the calculation.
- * @return integer the number of days between two dates
- */
- function Thirty360EUdayCount($startdate, $enddate)
- {
- $d1 = getdate($startdate);
- $d2 = getdate($enddate);
- $dd1 = $d1['mday']; $mm1 = $d1['mon']; $yy1 = $d1['year'];
- $dd2 = $d2['mday']; $mm2 = $d2['mon']; $yy2 = $d2['year'];
- return 360 * ($yy2 - $yy1) + 30 * ($mm2 - $mm1 - 1) + max(0, 30 - $dd1) + min(30, $dd2);
- }
- /**
- * Thirty360EUyearFraction
- * Returns the period between two dates as a fraction of year.
- * @param integer $startdate is the beginning date (Unix timestamp) for the calculation.
- * @param integer $enddate is the ending date (Unix timestamp) for the calculation.
- * @return float the fraction of years between two dates
- */
- function Thirty360EUyearFraction($startdate, $enddate)
- {
- return $this->Thirty360EUdayCount($startdate, $enddate) / 360.0;
- }
- /**
- * ActualActualdayCount
- * Returns the number of days between two dates.
- * @param integer $startdate is the beginning date (Unix timestamp) for the calculation.
- * @param integer $enddate is the ending date (Unix timestamp) for the calculation.
- * @return integer the number of days between two dates
- */
- function ActualActualdayCount($startdate, $enddate)
- {
- return $this->DATEDIFF('day', $startdate, $enddate);
- }
- /**
- * ActualActualyearFraction
- * Returns the period between two dates as a fraction of year.
- * @param integer $startdate is the beginning date (Unix timestamp) for the calculation.
- * @param integer $enddate is the ending date (Unix timestamp) for the calculation.
- * @param date $refPeriodStart is the reference beginning date (Unix timestamp) for the inner calculation.
- * @param date $refPeriodEnd is the reference ending date (Unix timestamp) for the inner calculation.
- * @return float the fraction of years between two dates
- */
- function ActualActualyearFraction($startdate, $enddate, $refPeriodStart = null, $refPeriodEnd = null)
- {
- $t = time();
- if (!isset($refPeriodStart)) $refPeriodStart = $startdate;
- if (!isset($refPeriodEnd)) $refPeriodEnd = $enddate;
- /*
- if ($this->DATEDIFF('day', $startdate, $enddate) == 0) return 0.0;
- $d1 = getdate($startdate);
- $d2 = getdate($enddate);
- $dib1 = ((date('L', $startdate) == 1) ? 366.0 : 365.0);
- $dib2 = ((date('L', $enddate) == 1) ? 366.0 : 365.0);
- $sum = $d2['year'] - $d1['year'] - 1;
- $sum += $this->ActualActualdayCount($startdate, mktime(0,0,0,1,1,$d1['year'] + 1)) / $dib1;
- $sum += $this->ActualActualdayCount(mktime(0,0,0,1,1,$d2['year']), $enddate) / $dib2;
- return $sum;
- */
- if ($this->DATEDIFF('day', $startdate, $enddate) == 0) return 0.0;
- if ($startdate > $enddate) die("Invalid dates");
- if (!($refPeriodStart != $t) && ($refPeriodEnd != $t) &&
- ($refPeriodEnd > $refPeriodStart) && ($refPeriodEnd > $startdate)) die("Invalid reference period");
- $months = intval(0.5 + 12 * $this->DATEDIFF('day', $refPeriodStart, $refPeriodEnd) / 365);
- $period = $months / 12.0;
- if($months == 0) die("number of months does not divide 12 exactly");
- if ($enddate <= $refPeriodEnd) {
- // here refPeriodEnd is a future (notional?) payment date
- if ($startdate >= $refPeriodStart) {
- // here refPeriodStart is the last (maybe notional)
- // payment date.
- // refPeriodStart <= startdate <= enddate <= refPeriodEnd
- // [maybe the equality should be enforced, since
- // refPeriodStart < startdate <= enddate < refPeriodEnd
- // could give wrong results] ???
- return $period * $this->ActualActualdayCount($startdate, $enddate) /
- $this->ActualActualdayCount($refPeriodStart, $refPeriodEnd);
- } else {
- // here refPeriodStart is the next (maybe notional)
- // payment date and refPeriodEnd is the second next
- // (maybe notional) payment date.
- // startdate < refPeriodStart < refPeriodEnd
- // AND enddate <= refPeriodEnd
- // this case is long first coupon
- // the last notional payment date
- $previousRef = $this->DATEADD('month', -$months, $refPeriodStart);
- if ($enddate > $refPeriodStart)
- return $this->ActualActualyearFraction($startdate, $refPeriodStart, $previousRef,
- $refPeriodStart) +
- $this->ActualActualyearFraction($refPeriodStart, $enddate, $refPeriodStart,
- $refPeriodEnd);
- else
- return $this->ActualActualyearFraction($startdate,$enddate,$previousRef,$refPeriodStart);
- }
- } else {
- // here refPeriodEnd is the last (notional?) payment date
- // startdate < refPeriodEnd < enddate AND refPeriodStart < refPeriodEnd
- if ($refPeriodStart > $startdate) die("Invalid dates");
- // now it is: refPeriodStart <= startdate < refPeriodEnd < enddate
- // the part from startdate to refPeriodEnd
- $sum = $this->ActualActualyearFraction($startdate, $refPeriodEnd, $refPeriodStart, $refPeriodEnd);
- // the part from refPeriodEnd to enddate
- // count how many regular periods are in [refPeriodEnd, enddate],
- // then add the remaining time
- $i = 0;
- do {
- $newRefStart = $this->DATEADD('month', $months * $i, $refPeriodEnd);
- $newRefEnd = $this->DATEADD('month', $months * ($i + 1), $refPeriodEnd);
- if ($enddate < $newRefEnd) {
- break;
- } else {
- $sum += $period;
- $i++;
- }
- } while (true);
- $sum += $this->ActualActualyearFraction($newRefStart, $newRefEnd, $newRefStart, $newRefEnd);
- return $sum;
- }
- }
- /**
- * Actual360dayCount
- * Returns the number of days between two dates.
- * @param integer $startdate is the beginning date (Unix timestamp) for the calculation.
- * @param integer $enddate is the ending date (Unix timestamp) for the calculation.
- * @return integer the number of days between two dates
- */
- function Actual360dayCount($startdate, $enddate)
- {
- return $this->DATEDIFF('day', $startdate, $enddate);
- }
- /**
- * Actual360yearFraction
- * Returns the period between two dates as a fraction of 360 days year.
- * @param integer $startdate is the beginning date (Unix timestamp) for the calculation.
- * @param integer $enddate is the ending date (Unix timestamp) for the calculation.
- * @return float the fraction of years between two dates
- */
- function Actual360yearFraction($startdate, $enddate)
- {
- return $this->Actual360dayCount($startdate, $enddate) / 360.0;
- }
- /**
- * Actual365dayCount
- * Returns the number of days between two dates.
- * @param integer $startdate is the beginning date (Unix timestamp) for the calculation.
- * @param integer $enddate is the ending date (Unix timestamp) for the calculation.
- * @return integer the number of days between two dates
- */
- function Actual365dayCount($startdate, $enddate)
- {
- return $this->DATEDIFF('day', $startdate, $enddate);
- }
- /**
- * Actual365yearFraction
- * Returns the period between two dates as a fraction of 365 days year.
- * @param integer $startdate is the beginning date (Unix timestamp) for the calculation.
- * @param integer $enddate is the ending date (Unix timestamp) for the calculation.
- * @return float the fraction of years between two dates
- */
- function Actual365yearFraction($startdate, $enddate)
- {
- return $this->Actual365dayCount($startdate, $enddate) / 365.0;
- }
- /**
- * DOLLARDE
- * Converts a dollar price expressed as a fraction into a dollar
- * price expressed as a decimal number. Use DOLLARDE to convert
- * fractional dollar numbers, such as securities prices, to decimal
- * numbers.
- * @param float $fractional_dollar is a number expressed as a fraction.
- * @param integer $fraction is the integer to use in the denominator of the fraction.
- * @return float dollar price expressed as a decimal number.
- */
- function DOLLARDE($fractional_dollar, $fraction)
- {
- $fraction = intval($fraction);
- $integer = intval($fractional_dollar);
- return $integer + 100 * ($fractional_dollar - $integer) / $fraction;
- }
- /**
- * DOLLARFR
- * Converts a dollar price expressed as a decimal number into a
- * dollar price expressed as a fraction. Use DOLLARFR to convert
- * decimal numbers to fractional dollar numbers, such as securities
- * prices.
- * @param float $decimal_dollar is a decimal number.
- * @param integer $fraction is the integer to use in the denominator of the fraction.
- * @return float dollar price expressed as a fraction.
- */
- function DOLLARFR($decimal_dollar, $fraction)
- {
- $fraction = intval($fraction);
- $integer = intval($decimal_dollar);
- return ($decimal_dollar - $integer) * $fraction / 100 + $integer;
- }
- /**
- * DDB
- * Returns the depreciation of an asset for a specified period using
- * the double-declining balance method or some other method you specify.
- * @param float $cost is the initial cost of the asset.
- * @param float $salvage is the value at the end of the depreciation (sometimes called the salvage value of the asset).
- * @param integer $life is the number of periods over which the asset is being depreciated (sometimes called the useful life of the asset).
- * @param integer $period is the period for which you want to calculate the depreciation. Period must use the same units as life.
- * @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).
- * @return float the depreciation of n periods.
- */
- function DDB($cost, $salvage, $life, $period, $factor = 2)
- {
- $x = 0;
- $n = 0;
- $life = intval($life);
- $period = intval($period);
- while ($period > $n) {
- $x = $factor * $cost / $life;
- if (($cost - $x) < $salvage) $x = $cost- $salvage;
- if ($x < 0) $x = 0;
- $cost -= $x;
- $n++;
- }
- return $x;
- }
- /**
- * SLN
- * Returns the straight-line depreciation of an asset for one period.
- * @param float $cost is the initial cost of the asset.
- * @param float $salvage is the value at the end of the depreciation (sometimes called the salvage value of the asset).
- * @param integer $life is the number of periods over which the asset is being depreciated (sometimes called the useful life of the asset).
- * @return float the depreciation allowance for each period.
- */
- function SLN($cost, $salvage, $life)
- {
- $sln = ($cost - $salvage) / $life;
- return (is_finite($sln) ? $sln: null);
- }
- /**
- * SYD
- * Returns the sum-of-years' digits depreciation of an asset for
- * a specified period.
- *
- * (cost - salvage) * (life - per + 1) * 2
- * SYD = -----------------------------------------
- * life * (1 + life)
- *
- * @param float $cost is the initial cost of the asset.
- * @param float $salvage is the value at the end of the depreciation (sometimes called the salvage value of the asset).
- * @param integer $life is the number of periods over which the asset is depreciated (sometimes called the useful life of the asset).
- * @param integer $per is the period and must use the same units as life.
- */
- function SYD($cost, $salvage, $life, $per)
- {
- $life = intval($life);
- $per = intval($per);
- $syd = (($cost - $salvage) * ($life - $per + 1) * 2) / ($life * (1 + $life));
- return (is_finite($syd) ? $syd: null);
- }
- /**
- * @param float $fWert
- * @param float $fRest
- * @param float $fDauer
- * @param float $fPeriode
- * @param float $fFaktor
- * @return float
- */
- function _ScGetGDA($fWert, $fRest, $fDauer,$fPeriode, $fFaktor)
- {
- $fZins = $fFaktor / $fDauer;
- if ($fZins >= 1.0) {
- $fZins = 1.0;
- if ($fPeriode == 1.0)
- $fAlterWert = $fWert;
- else
- $fAlterWert = 0.0;
- } else
- $fAlterWert = $fWert * pow(1.0 - $fZins, $fPeriode - 1.0);
- $fNeuerWert = $fWert * pow(1.0 - $fZins, $fPeriode);
- if ($fNeuerWert < $fRest)
- $fGda = $fAlterWert - $fRest;
- else
- $fGda = $fAlterWert - $fNeuerWert;
- if ($fGda < 0.0) $fGda = 0.0;
- return $fGda;
- }
- /**
- * @param float $cost
- * @param float $salvage
- * @param float $life
- * @param float $life1
- * @param float $period
- * @param float $factor
- * @return float
- */
- function _ScInterVDB($cost, $salvage, $life, $life1, $period, $factor)
- {
- $fVdb = 0;
- $fIntEnd = ceil($period);
- $nLoopEnd = $fIntEnd;
- $fRestwert = $cost - $salvage;
- $bNowLia = false;
- $fLia = 0;
- for ($i = 1; $i <= $nLoopEnd; $i++) {
- if (!$bNowLia) {
- $fGda = $this->_ScGetGDA($cost, $salvage, $life, $i, $factor);
- $fLia = $fRestwert / ($life1 - (float)($i - 1));
- if ($fLia > $fGda) {
- $fTerm = $fLia;
- $bNowLia = true;
- } else {
- $fTerm = $fGda;
- $fRestwert -= $fGda;
- }
- } else
- $fTerm = fLia;
- if ($i == $nLoopEnd)
- $fTerm *= ($period + 1.0 - $fIntEnd);
- $fVdb += $fTerm;
- }
- return $fVdb;
- }
- /**
- * VDB
- * Returns the depreciation of an asset for any period you specify,
- * including partial periods, using the double-declining balance method
- * or some other method you specify. VDB stands for variable declining balance.
- *
- * @param float $cost is the initial cost of the asset.
- * @param float $salvage is the value at the end of the depreciation (sometimes called the salvage value of the asset).
- * @param integer $life is the number of periods over which the asset is depreciated (sometimes called the useful life of the asset).
- * @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.
- * @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.
- * @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.
- * @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.
- * @return float the depreciation of an asset.
- */
- function VDB($cost, $salvage, $life, $start_period, $end_period, $factor = 2.0, $no_switch = false)
- {
- // pre-validations
- if (($start_period < 0)
- || ($end_period < $start_period)
- || ($end_period > $life)
- || ($cost < 0) || ($salvage > $cost)
- || ($factor <= 0))
- return null;
- // this implementation is borrowed from OppenOffice 1.0,
- // 'sc/source/core/tool/interpr2.cxx' with a small changes
- // from me.
- $fVdb = 0.0;
- $fIntStart = floor($start_period);
- $fIntEnd = ceil($end_period);
- if ($no_switch) {
- $nLoopStart = (int) $fIntStart;
- $nLoopEnd = (int) $fIntEnd;
- for ($i = $nLoopStart + 1; $i <= $nLoopEnd; $i++) {
- $fTerm = $this->_ScGetGDA($cost, $salvage, $life, $i, $factor);
- if ($i == $nLoopStart + 1)
- $fTerm *= (min($end_period, $fIntStart + 1.0) - $start_period);
- elseif ($i == $nLoopEnd)
- $fTerm *= ($end_period + 1.0 - $fIntEnd);
- $fVdb += $fTerm;
- }
- } else {
- $life1 = $life;
- if ($start_period != $fIntStart)
- if ($factor > 1) {
- if ($start_period >= ($life / 2)) {
- $fPart = $start_period - ($life / 2);
- $start_period = $life / 2;
- $end_period -= $fPart;
- $life1 += 1;
- }
- }
- $cost -= $this->_ScInterVDB($cost, $salvage, $life, $life1, $start_period, $factor);
- $fVdb = $this->_ScInterVDB($cost, $salvage, $life, $life - $start_period, $end_period - $start_period, $factor);
- }
- return $fVdb;
- }
- /**
- * Present value interest factor
- *
- * nper
- * PVIF = (1 + rate)
- *
- * @param float $rate is the interest rate per period.
- * @param integer $nper is the total number of periods.
- * @return float the present value interest factor
- */
- function _calculate_pvif ($rate, $nper)
- {
- return (pow(1 + $rate, $nper));
- }
- /**
- * Future value interest factor of annuities
- *
- * nper
- * (1 + rate) - 1
- * FVIFA = -------------------
- * rate
- *
- * @param float $rate is the interest rate per period.
- * @param integer $nper is the total number of periods.
- * @return float the present value interest factor of annuities
- */
- function _calculate_fvifa ($rate, $nper)
- {
- // Removable singularity at rate == 0
- if ($rate == 0)
- return $nper;
- else
- // FIXME: this sucks for very small rates
- return (pow(1 + $rate, $nper) - 1) / $rate;
- }
- function _calculate_interest_part ($pv, $pmt, $rate, $per)
- {
- return -($pv * pow(1 + $rate, $per) * $rate +
- $pmt * (pow(1 + $rate, $per) - 1));
- }
- function _calculate_pmt ($rate, $nper, $pv, $fv, $type)
- {
- // Calculate the PVIF and FVIFA
- $pvif = $this->_calculate_pvif ($rate, $nper);
- $fvifa = $this->_calculate_fvifa ($rate, $nper);
- return ((-$pv * $pvif - $fv ) / ((1.0 + $rate * $type) * $fvifa));
- }
- /**
- * PV
- * Returns the present value of an investment. The present value is
- * the total amount that a series of future payments is worth now.
- * For example, when you borrow money, the loan amount is the present
- * value to the lender.
- *
- * If rate = 0:
- * PV = -(FV + PMT * nper)
- *
- * Else
- * / nper \
- * | 1 - (1 + rate) |
- * PMT * (1 + rate * type) * | ----------------- | - FV
- * \ rate /
- * PV = ------------------------------------------------------
- * nper
- * (1 + rate)
- *
- * @param float $rate is the interest rate per period.
- * @param integer $nper is the total number of payment periods in an annuity.
- * @param float $pmt is the payment made each period and cannot change over the life of the annuity.
- * @param float $fv is the future value, or a cash balance you want to attain after the last payment is made.
- * @param integer $type is the number 0 or 1 and indicates when payments are due.
- * @return float the present value of an investment.
- */
- function PV($rate, $nper, $pmt, $fv = 0.0, $type = 0)
- {
- // Calculate the PVIF and FVIFA
- $pvif = $this->_calculate_pvif ($rate, $nper);
- $fvifa = $this->_calculate_fvifa ($rate, $nper);
- if ($pvif == 0)
- return null;
- $pv = ((-$fv - $pmt * (1.0 + $rate * $type) * $fvifa) / $pvif);
- return (is_finite($pv) ? $pv: null);
- }
- /**
- * FV
- * Returns the future value of an investment based on periodic,
- * constant payments and a constant interest rate.
- *
- * For a more complete description of the arguments in FV, see the PV function.
- *
- * Rate: is the interest rate per period.
- * Nper: is the total number of payment periods in an annuity.
- * Pmt: is the payment made each period; it cannot change over the life of the
- * annuity. Typically, pmt contains principal and interest but no other fees
- * or taxes. If pmt is omitted, you must include the pv argument.
- * Pv: is the present value, or the lump-sum amount that a series of future
- * payments is worth right now. If pv is omitted, it is assumed to be 0 (zero),
- * and you must include the pmt argument.
- * Type: is the number 0 or 1 and indicates when payments are due. If type is
- * omitted, it is assumed to be 0.
- * 0 or omitted, At the end of the period
- * 1, At the beginning of the period
- *
- * If rate = 0:
- * FV = -(PV + PMT * nper)
- *
- * Else
- * / nper \
- * | 1 - (1 + rate) | nper
- * FV = PMT * (1 + rate * type) * | ------------------ | - PV * (1 + rate)
- * \ rate /
- *
- **/
- function FV($rate, $nper, $pmt, $pv = 0.0, $type = 0)
- {
- $pvif = $this->_calculate_pvif ($rate, $nper);
- $fvifa = $this->_calculate_fvifa ($rate, $nper);
- $fv = (-(($pv * $pvif) + $pmt *
- (1.0 + $rate * $type) * $fvifa));
- return (is_finite($fv) ? $fv: null);
- }
- /**
- * FVSCHEDULE
- * Returns the future value of an initial principal after applying a series
- * of compound interest rates. Use FVSCHEDULE to calculate the future value
- * of an investment with a variable or adjustable rate.
- *
- **/
- function FVSCHEDULE($principal, $schedule)
- {
- $n = count($schedule);
- for ($i = 0; $i < $n; $i++)
- $principal *= 1 + $schedule[$i];
- return $principal;
- }
- /**
- * PMT
- * Calculates the payment for a loan based on constant payments
- * and a constant interest rate.
- *
- * For a more complete description of the arguments in PMT, see the PV function.
- *
- * Rate: is the interest rate for the loan.
- * Nper: is the total number of payments for the loan.
- * Pv: is the present value, or the total amount that a series of future payments
- * is worth now; also known as the principal.
- * Fv: is the future value, or a cash balance you want to attain after the last
- * payment is made. If fv is omitted, it is assumed to be 0 (zero), that is,
- * the future value of a loan is 0.
- * Type: is the number 0 (zero) or 1 and indicates when payments are due.
- * 0 or omitted, At the end of the period
- * 1, At the beginning of the period
- *
- * If rate = 0:
- * -(FV + PV)
- * PMT = ------------
- * nper
- *
- * Else
- *
- * nper
- * FV + PV * (1 + rate)
- * PMT = --------------------------------------------
- * / nper \
- * | 1 - (1 + rate) |
- * (1 + rate * type) * | ------------------ |
- * \ rate /
- *
- **/
- function PMT($rate, $nper, $pv, $fv = 0.0, $type = 0)
- {
- $pmt = $this->_calculate_pmt ($rate, $nper, $pv, $fv, $type);
- return (is_finite($pmt) ? $pmt: null);
- }
- /**
- * IPMT
- * Returns the interest payment for a given period for an investment based
- * on periodic, constant payments and a constant interest rate.
- *
- * For a more complete description of the arguments in IPMT, see the PV function.
- *
- */
- function IPMT($rate, $per, $nper, $pv, $fv = 0.0, $type = 0)
- {
- if (($per < 1) || ($per >= ($nper + 1)))
- return null;
- else {
- $pmt = $this->_calculate_pmt ($rate, $nper, $pv, $fv, $type);
- $ipmt = $this->_calculate_interest_part ($pv, $pmt, $rate, $per - 1);
- return (is_finite($ipmt) ? $ipmt: null);
- }
- }
- /**
- * PPMT
- * Returns the payment on the principal for a given period for an
- * investment based on periodic, constant payments and a constant
- * interest rate.
- *
- **/
- function PPMT($rate, $per, $nper, $pv, $fv = 0.0, $type = 0)
- {
- if (($per < 1) || ($per >= ($nper + 1)))
- return null;
- else {
- $pmt = $this->_calculate_pmt ($rate, $nper, $pv, $fv, $type);
- $ipmt = $this->_calculate_interest_part ($pv, $pmt, $rate, $per - 1);
- return ((is_finite($pmt) && is_finite($ipmt)) ? $pmt - $ipmt: null);
- }
- }
- /**
- * NPER
- * Returns the number of periods for an investment based on periodic,
- * constant payments and a constant interest rate.
- *
- * For a complete description of the arguments nper, pmt, pv, fv, and type, see PV.
- *
- * Nper: is the total number of payment periods in an annuity.
- * Pmt: is the payment made each period and cannot change over the life
- * of the annuity. Typically, pmt includes principal and interest but no
- * other fees or taxes. If pmt is omitted, you must include the fv argument.
- * Pv: is the present value — the total amount that a series of future payments
- * is worth now.
- * Fv: is the future value, or a cash balance you want to attain after the
- * last payment is made. If fv is omitted, it is assumed to be 0 (the future
- * value of a loan, for example, is 0).
- * Type: is the number 0 or 1 and indicates when payments are due.
- * 0 or omitted, At the end of the period
- * 1, At the beginning of the period
- *
- * If rate = 0:
- * -(FV + PV)
- * nper = -----------
- * PMT
- *
- * Else
- * / PMT * (1 + rate * type) - FV * rate \
- * log | ------------------------------------- |
- * \ PMT * (1 + rate * type) + PV * rate /
- * nper = -----------------------------------------------
- * log (1 + rate)
- *
- **/
- function NPER($rate, $pmt, $pv, $fv = 0.0, $type = 0)
- {
- if (($rate == 0) && ($pmt != 0))
- $nper = (-($fv + $pv) / $pmt);
- elseif ($rate <= 0.0)
- return null;
- else {
- $tmp = ($pmt * (1.0 + $rate * $type) - $fv * $rate) /
- ($pv * $rate + $pmt * (1.0 + $rate * $type));
- if ($tmp <= 0.0)
- return null;
- $nper = (log10($tmp) / log10(1.0 + $rate));
- }
- return (is_finite($nper) ? $nper: null);
- }
- /*
- * EFFECT
- * Returns the effective annual interest rate, given the nominal annual
- * interest rate and the number of compounding periods per year.
- *
- * / nominal_rate \ npery
- * EFFECT = | 1 + -------------- | - 1
- * \ npery /
- *
- **/
- function EFFECT($nominal_rate, $npery)
- {
- $npery = intval($npery);
- if (($nominal_rate <= 0) || ($npery < 1)) return null;
- $effect = pow(1 + $nominal_rate / $npery, $npery) - 1;
- return (is_finite($effect) ? $effect: null);
- }
- /**
- * NOMINAL
- * Returns the nominal annual interest rate, given the effective rate
- * and the number of compounding periods per year.
- *
- * (1 / npery)
- * NOMINAL = npery * (1 + effect_rate) - npery
- *
- **/
- function NOMINAL($effect_rate, $npery)
- {
- $npery = intval($npery);
- if (($effect_rate <= 0) || ($npery < 1)) return null;
- $nominal = $npery * pow(1 + $effect_rate, 1 / $npery) - $npery;
- return (is_finite($nominal) ? $nominal: null);
- }
- /**
- * DISC
- * Returns the discount rate for a security.
- *
- * redemption - pr
- * DISC = ---------------------------
- * redemption * yearfraction
- *
- **/
- function DISC($settlement, $maturity, $pr, $redemption, $basis = FINANCIAL_BASIS_MSRB_30_360)
- {
- if (!$this->_is_valid_basis($basis)) return null;
- if (($pr <= 0) || ($redemption <= 0)) return null;
- if ($settlement >= $maturity) return null;
- switch ($basis) {
- case FINANCIAL_BASIS_MSRB_30_360: // US(NASD) 30/360
- $dsm = $this->Thirty360USyearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_ACT_ACT: // Actual days/actual days
- $dsm = $this->ActualActualyearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_ACT_360: // Actual days/360
- $dsm = $this->Actual360yearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_ACT_365: // Actual days/365
- $dsm = $this->Actual365yearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_30E_360: // European 30/360
- $dsm = $this->Thirty360EUyearFraction($settlement, $maturity);
- break;
- }
- $disc = ($redemption - $pr) / ($redemption * $dsm);
- return (is_finite($disc) ? $disc: null);
- }
- /**
- * RECEIVED
- * Returns the amount received at maturity for a fully invested security.
- *
- * investment
- * RECEIVED = -----------------------------
- * 1 - discount * yearfraction
- *
- **/
- function RECEIVED($settlement, $maturity, $investment, $discount, $basis = FINANCIAL_BASIS_MSRB_30_360)
- {
- if (!$this->_is_valid_basis($basis)) return null;
- if (($investment <= 0) || ($discount <= 0)) return null;
- if ($settlement >= $maturity) return null;
- switch ($basis) {
- case FINANCIAL_BASIS_MSRB_30_360: // US(NASD) 30/360
- $dsm = $this->Thirty360USyearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_ACT_ACT: // Actual/actual
- $dsm = $this->ActualActualyearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_ACT_360: // Actual/360
- $dsm = $this->Actual360yearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_ACT_365: // Actual/365
- $dsm = $this->Actual365yearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_30E_360: // European 30/360
- $dsm = $this->Thirty360EUyearFraction($settlement, $maturity);
- break;
- }
- $received = $investment / (1 - $discount * $dsm);
- return (is_finite($received) ? $received: null);
- }
- /**
- * INTRATE
- * Returns the interest rate for a fully invested security.
- *
- * redemption - investment
- * INTRATE = ---------------------------
- * investment * yearfraction
- *
- **/
- function INTRATE($settlement, $maturity, $investment, $redemption, $basis = FINANCIAL_BASIS_MSRB_30_360)
- {
- if (!$this->_is_valid_basis($basis)) return null;
- if (($investment <= 0) || ($redemption <= 0)) return null;
- if ($settlement >= $maturity) return null;
- switch ($basis) {
- case FINANCIAL_BASIS_MSRB_30_360: // US(NASD) 30/360
- $dsm = $this->Thirty360USyearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_ACT_ACT: // Actual/actual
- $dsm = $this->ActualActualyearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_ACT_365: // Actual/360
- $dsm = $this->Actual360yearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_ACT_365: // Actual/365
- $dsm = $this->Actual365yearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_30E_360: // European 30/360
- $dsm = $this->Thirty360EUyearFraction($settlement, $maturity);
- break;
- }
- $intrate = ($redemption - $investment) / ($investment * $dsm);
- return (is_finite($intrate) ? $intrate: null);
- }
- /**
- * NPV
- * Calculates the net present value of an investment by using a
- * discount rate and a series of future payments (negative values)
- * and income (positive values).
- *
- * n / values(i) \
- * NPV = SUM | -------------- |
- * i=1 | i |
- * \ (1 + rate) /
- *
- **/
- function NPV($rate, $values)
- {
- if (!is_array($values)) return null;
- $npv = 0.0;
- for ($i = 0; $i < count($values); $i++)
- {
- $npv += $values[$i] / pow(1 + $rate, $i + 1);
- }
- return (is_finite($npv) ? $npv: null);
- }
- /**
- * XNPV
- * Returns the net present value for a schedule of cash flows that
- * is not necessarily periodic. To calculate the net present value
- * for a series of cash flows that is periodic, use the NPV function.
- *
- * n / values(i) \
- * NPV = SUM | ---------------------------------------- |
- * i=1 | ((dates(i) - dates(1)) / 365) |
- * \ (1 + rate) /
- *
- **/
- function XNPV($rate, $values, $dates)
- {
- if ((!is_array($values)) || (!is_array($dates))) return null;
- if (count($values) != count($dates)) return null;
- $xnpv = 0.0;
- for ($i = 0; $i < count($values); $i++)
- {
- $xnpv += $values[$i] / pow(1 + $rate, $this->DATEDIFF('day', $dates[0], $dates[$i]) / 365);
- }
- return (is_finite($xnpv) ? $xnpv: null);
- }
- /*
- * IRR
- * Returns the internal rate of return for a series of cash flows
- * represented by the numbers in values. These cash flows do not
- * have to be even, as they would be for an annuity. However, the
- * cash flows must occur at regular intervals, such as monthly or
- * annually. The internal rate of return is the interest rate
- * received for an investment consisting of payments (negative
- * values) and income (positive values) that occur at regular periods.
- *
- */
- function IRR($values, $guess = 0.1)
- {
- if (!is_array($values)) return null;
- // create an initial bracket, with a root somewhere between bot and top
- $x1 = 0.0;
- $x2 = $guess;
- $f1 = $this->NPV($x1, $values);
- $f2 = $this->NPV($x2, $values);
- for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; $i++)
- {
- if (($f1 * $f2) < 0.0) break;
- if (abs($f1) < abs($f2)) {
- $f1 = $this->NPV($x1 += 1.6 * ($x1 - $x2), $values);
- } else {
- $f2 = $this->NPV($x2 += 1.6 * ($x2 - $x1), $values);
- }
- }
- if (($f1 * $f2) > 0.0) return null;
- $f = $this->NPV($x1, $values);
- if ($f < 0.0) {
- $rtb = $x1;
- $dx = $x2 - $x1;
- } else {
- $rtb = $x2;
- $dx = $x1 - $x2;
- }
- for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; $i++)
- {
- $dx *= 0.5;
- $x_mid = $rtb + $dx;
- $f_mid = $this->NPV($x_mid, $values);
- if ($f_mid <= 0.0) $rtb = $x_mid;
- if ((abs($f_mid) < FINANCIAL_ACCURACY) || (abs($dx) < FINANCIAL_ACCURACY)) return $x_mid;
- }
- return null;
- }
- /*
- * MIRR
- * Returns the modified internal rate of return for a series
- * of periodic cash flows. MIRR considers both the cost of
- * the investment and the interest received on reinvestment
- * of cash.
- *
- **/
- function MIRR($values, $finance_rate, $reinvert_rate)
- {
- $n = count($values);
- for ($i = 0, $npv_pos = $npv_neg = 0; $i < $n; $i++) {
- $v = $values[$i];
- if ($v >= 0)
- $npv_pos += $v / pow(1.0 + $reinvert_rate, $i);
- else
- $npv_neg += $v / pow(1.0 + $finance_rate, $i);
- }
- if (($npv_neg == 0) || ($npv_pos == 0) || ($reinvert_rate <= -1))
- return null;
- /*
- * I have my doubts about this formula, but it sort of looks like
- * the one Microsoft claims to use and it produces the results
- * that Excel does. -- MW.
- */
- $mirr = pow((-$npv_pos * pow(1 + $reinvert_rate, $n))
- / ($npv_neg * (1 + $reinvert_rate)), (1.0 / ($n - 1))) - 1.0;
- return (is_finite($mirr) ? $mirr: null);
- }
- /*
- * XIRR
- * Returns the internal rate of return for a schedule of cash flows
- * that is not necessarily periodic. To calculate the internal rate
- * of return for a series of periodic cash flows, use the IRR function.
- *
- * Adapted from routine in Numerical Recipes in C, and translated
- * from the Bernt A Oedegaard algorithm in C
- *
- **/
- function XIRR($values, $dates, $guess = 0.1)
- {
- if ((!is_array($values)) && (!is_array($dates))) return null;
- if (count($values) != count($dates)) return null;
- // create an initial bracket, with a root somewhere between bot and top
- $x1 = 0.0;
- $x2 = $guess;
- $f1 = $this->XNPV($x1, $values, $dates);
- $f2 = $this->XNPV($x2, $values, $dates);
- for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; $i++)
- {
- if (($f1 * $f2) < 0.0) break;
- if (abs($f1) < abs($f2)) {
- $f1 = $this->XNPV($x1 += 1.6 * ($x1 - $x2), $values, $dates);
- } else {
- $f2 = $this->XNPV($x2 += 1.6 * ($x2 - $x1), $values, $dates);
- }
- }
- if (($f1 * $f2) > 0.0) return null;
- $f = $this->XNPV($x1, $values, $dates);
- if ($f < 0.0) {
- $rtb = $x1;
- $dx = $x2 - $x1;
- } else {
- $rtb = $x2;
- $dx = $x1 - $x2;
- }
- for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; $i++)
- {
- $dx *= 0.5;
- $x_mid = $rtb + $dx;
- $f_mid = $this->XNPV($x_mid, $values, $dates);
- if ($f_mid <= 0.0) $rtb = $x_mid;
- if ((abs($f_mid) < FINANCIAL_ACCURACY) || (abs($dx) < FINANCIAL_ACCURACY)) return $x_mid;
- }
- return null;
- }
- /**
- * RATE
- *
- **/
- function RATE($nper, $pmt, $pv, $fv = 0.0, $type = 0, $guess = 0.1)
- {
- $rate = $guess;
- $i = 0;
- $x0 = 0;
- $x1 = $rate;
- if (abs($rate) < FINANCIAL_ACCURACY) {
- $y = $pv * (1 + $nper * $rate) + $pmt * (1 + $rate * $type) * $nper + $fv;
- } else {
- $f = exp($nper * log(1 + $rate));
- $y = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
- }
- $y0 = $pv + $pmt * $nper + $fv;
- $y1 = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
- // find root by secant method
- while ((abs($y0 - $y1) > FINANCIAL_ACCURACY) && ($i < FINANCIAL_MAX_ITERATIONS))
- {
- $rate = ($y1 * $x0 - $y0 * $x1) / ($y1 - $y0);
- $x0 = $x1;
- $x1 = $rate;
- if (abs($rate) < FINANCIAL_ACCURACY) {
- $y = $pv * (1 + $nper * $rate) + $pmt * (1 + $rate * $type) * $nper + $fv;
- } else {
- $f = exp($nper * log(1 + $rate));
- $y = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
- }
- $y0 = $y1;
- $y1 = $y;
- $i++;
- }
- return $rate;
- }
- /**
- * DELTA
- * Tests whether two values are equal. Returns 1 if number1 = number2; returns 0 otherwise.
- * Use this function to filter a set of values. For example, by summing several DELTA functions
- * you calculate the count of equal pairs. This function is also known as the Kronecker Delta function.
- */
- function DELTA($number1, $number2 = 0)
- {
- if (is_nan($number1) || is_nan($number2)) return null;
- if ($number1 == $number2) {
- return 1;
- } else {
- return 0;
- }
- }
- /*
- * Returns the yield on a security that pays periodic interest.
- * Use YIELD to calculate bond yield.
- *
- * Settlement: is the security's settlement date. The security
- * settlement date is the date after the issue date when the
- * security is traded to the buyer.
- * Maturity: is the security's maturity date. The maturity date
- * is the date when the security expires.
- * Rate: is the security's annual coupon rate.
- * Pr: is the security's price per $100 face value.
- * Redemption: is the security's redemption value per $100 face value.
- * Frequency: is the number of coupon payments per year. For annual
- * payments, frequency = 1; for semiannual, frequency = 2; for
- * quarterly, frequency = 4.
- * Basis: is the type of day count basis to use.
- * 0 or omitted US (NASD) 30/360
- * 1 Actual/actual
- * 2 Actual/360
- * 3 Actual/365
- * 4 European 30/360
- *
- */
- function YIELD($settlement, $maturity, $rate, $pr, $redemption, $frequency, $basis = FINANCIAL_BASIS_MSRB_30_360)
- {
- if (!$this->_is_valid_basis($basis)) return null;
- if (!$this->_is_valid_frequency($frequency)) return null;
- if ($rate < 0) return null;
- if (($pr <= 0) || ($redemption <= 0)) return null;
- if ($settlement >= $maturity) return null;
- // TODO: Not yet implemented
- return null;
- }
- /**
- * PRICEDISC
- * Returns the price per $100 face value of a discounted security.
- **/
- function PRICEDISC($settlement, $maturity, $discount, $redemption, $basis = FINANCIAL_BASIS_MSRB_30_360)
- {
- if (!$this->_is_valid_basis($basis)) return null;
- if (($discount <= 0) || ($redemption <= 0)) return null;
- if ($settlement >= $maturity) return null;
- switch ($basis) {
- case FINANCIAL_BASIS_MSRB_30_360: // US(NASD) 30/360
- $dsm = $this->Thirty360USyearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_ACT_ACT: // Actual/actual
- $dsm = $this->ActualActualyearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_ACT_360: // Actual/360
- $dsm = $this->Actual360yearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_ACT_365: // Actual/365
- $dsm = $this->Actual365yearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_30E_360: // European 30/360
- $dsm = $this->Thirty360EUyearFraction($settlement, $maturity);
- break;
- }
- return $redemption - $discount * $redemption * $dsm;
- }
- /**
- * YIELDDISC
- * Returns the annual yield for a discounted security.
- **/
- function YIELDDISC($settlement, $maturity, $pr, $redemption, $basis = FINANCIAL_BASIS_MSRB_30_360)
- {
- if (!$this->_is_valid_basis($basis)) return null;
- if (($pr <= 0) || ($redemption <= 0)) return null;
- if ($settlement >= $maturity) return null;
- switch ($basis) {
- case FINANCIAL_BASIS_MSRB_30_360: // US(NASD) 30/360
- $dsm = $this->Thirty360USyearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_ACT_ACT: // Actual/actual
- $dsm = $this->ActualActualyearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_ACT_360: // Actual/360
- $dsm = $this->Actual360yearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_ACT_365: // Actual/365
- $dsm = $this->Actual365yearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_30E_360: // European 30/360
- $dsm = $this->Thirty360EUyearFraction($settlement, $maturity);
- break;
- }
- return ($redemption - $pr) / ($pr * $dsm);
- }
- /**
- * COUPNUM
- * Returns the number of coupons payable between the settlement
- * date and maturity date, rounded up to the nearest whole coupon.
- *
- */
- function COUPNUM($settlement, $maturity, $frequency, $basis = FINANCIAL_BASIS_MSRB_30_360)
- {
- if (!$this->_is_valid_basis($basis)) return null;
- if (!$this->_is_valid_frequency($frequency)) return null;
- if ($settlement >= $maturity) return null;
- switch ($basis) {
- case FINANCIAL_BASIS_MSRB_30_360: // US(NASD) 30/360
- $dsm = $this->Thirty360USdayCount($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_ACT_ACT: // Actual/actual
- $dsm = $this->ActualActualdayCount($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_ACT_360: // Actual/360
- $dsm = $this->Actual360dayCount($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_ACT_365: // Actual/365
- $dsm = $this->Actual365yearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_30E_360: // European 30/360
- $dsm = $this->Thirty360EUdayCount($settlement, $maturity);
- break;
- }
- switch ($frequency) {
- case 1: // anual payments
- return ceil($dsm / 360);
- case 2: // semiannual
- return ceil($dsm / 180);
- case 4: // quarterly
- return ceil($dsm / 90);
- }
- return null;
- }
- /**
- * COUPDAYBS
- * Returns the number of days in the coupon period that contains
- * the settlement date.
- *
- */
- function COUPDAYBS($settlement, $maturity, $frequency, $basis = FINANCIAL_BASIS_MSRB_30_360)
- {
- if (!$this->_is_valid_basis($basis)) return null;
- if (!$this->_is_valid_frequency($frequency)) return null;
- if ($settlement >= $maturity) return null;
- switch ($basis) {
- case FINANCIAL_BASIS_MSRB_30_360: // US(NASD) 30/360
- $dsm = $this->Thirty360USdayCount($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_ACT_ACT: // Actual/actual
- $dsm = $this->ActualActualdayCount($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_ACT_360: // Actual/360
- $dsm = $this->Actual360dayCount($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_ACT_365: // Actual/365
- $dsm = $this->Actual365yearFraction($settlement, $maturity);
- break;
- case FINANCIAL_BASIS_30E_360: // European 30/360
- $dsm = $this->Thirty360EUdayCount($settlement, $maturity);
- break;
- }
- switch ($frequency) {
- case 1: // anual payments
- return 365 - ($dsm % 360);
- case 2: // semiannual
- return 365 - ($dsm % 360);
- case 4: // quarterly
- return $this->DATEDIFF('day', $this->DATEADD('day', -ceil($dsm / 90) * 90 - ($dsm % 90), $maturity), $settlement);
- }
- return null;
- }
- }
- ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement