RE: financial_class.php

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Thanks Muthukumar,

I can not rectify the problem,

I place entire code here.

Look at this and sort out my prob

<?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. <egarcia@xxxxxx>
 * @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;
    }
}

?>





-- 
Best Regards

Eng. T. Kaneshalingam, AMIE(SL)
Project Manager,

E-commerce & Content Services
LankaCom Services (Pvt) Ltd
65C, Dharmapala Mawatha,
Colombo 07.
Sri Lanka.
E - kanesh@xxxxxxxxxxxx
M - +94 777227960
T - +94 112437545
F - +94 112437547
W - www.lankacom.net
       
---------------------------------
Looking for last minute shopping deals?  Find them fast with Yahoo! Search.

[Non-text portions of this message have been removed]


[Index of Archives]     [PHP Home]     [PHP Users]     [PHP Soap]     [Kernel Newbies]     [Yosemite]     [Yosemite Campsites]

  Powered by Linux