[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/phpexcel/PHPExcel/Calculation/ -> Financial.php (source)

   1  <?php
   2  
   3  /** PHPExcel root directory */
   4  if (!defined('PHPEXCEL_ROOT')) {
   5      /**
   6       * @ignore
   7       */
   8      define('PHPEXCEL_ROOT', dirname(__FILE__) . '/../../');
   9      require (PHPEXCEL_ROOT . 'PHPExcel/Autoloader.php');
  10  }
  11  
  12  /** FINANCIAL_MAX_ITERATIONS */
  13  define('FINANCIAL_MAX_ITERATIONS', 128);
  14  
  15  /** FINANCIAL_PRECISION */
  16  define('FINANCIAL_PRECISION', 1.0e-08);
  17  
  18  /**
  19   * PHPExcel_Calculation_Financial
  20   *
  21   * Copyright (c) 2006 - 2015 PHPExcel
  22   *
  23   * This library is free software; you can redistribute it and/or
  24   * modify it under the terms of the GNU Lesser General Public
  25   * License as published by the Free Software Foundation; either
  26   * version 2.1 of the License, or (at your option) any later version.
  27   *
  28   * This library is distributed in the hope that it will be useful,
  29   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  30   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  31   * Lesser General Public License for more details.
  32   *
  33   * You should have received a copy of the GNU Lesser General Public
  34   * License along with this library; if not, write to the Free Software
  35   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  36   *
  37   * @category    PHPExcel
  38   * @package        PHPExcel_Calculation
  39   * @copyright    Copyright (c) 2006 - 2015 PHPExcel (http://www.codeplex.com/PHPExcel)
  40   * @license        http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
  41   * @version        ##VERSION##, ##DATE##
  42   */
  43  class PHPExcel_Calculation_Financial
  44  {
  45      /**
  46       * isLastDayOfMonth
  47       *
  48       * Returns a boolean TRUE/FALSE indicating if this date is the last date of the month
  49       *
  50       * @param    DateTime    $testDate    The date for testing
  51       * @return    boolean
  52       */
  53      private static function isLastDayOfMonth($testDate)
  54      {
  55          return ($testDate->format('d') == $testDate->format('t'));
  56      }
  57  
  58  
  59      /**
  60       * isFirstDayOfMonth
  61       *
  62       * Returns a boolean TRUE/FALSE indicating if this date is the first date of the month
  63       *
  64       * @param    DateTime    $testDate    The date for testing
  65       * @return    boolean
  66       */
  67      private static function isFirstDayOfMonth($testDate)
  68      {
  69          return ($testDate->format('d') == 1);
  70      }
  71  
  72  
  73      private static function couponFirstPeriodDate($settlement, $maturity, $frequency, $next)
  74      {
  75          $months = 12 / $frequency;
  76  
  77          $result = PHPExcel_Shared_Date::ExcelToPHPObject($maturity);
  78          $eom = self::isLastDayOfMonth($result);
  79  
  80          while ($settlement < PHPExcel_Shared_Date::PHPToExcel($result)) {
  81              $result->modify('-'.$months.' months');
  82          }
  83          if ($next) {
  84              $result->modify('+'.$months.' months');
  85          }
  86  
  87          if ($eom) {
  88              $result->modify('-1 day');
  89          }
  90  
  91          return PHPExcel_Shared_Date::PHPToExcel($result);
  92      }
  93  
  94  
  95      private static function isValidFrequency($frequency)
  96      {
  97          if (($frequency == 1) || ($frequency == 2) || ($frequency == 4)) {
  98              return true;
  99          }
 100          if ((PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC) &&
 101              (($frequency == 6) || ($frequency == 12))) {
 102              return true;
 103          }
 104          return false;
 105      }
 106  
 107  
 108      /**
 109       * daysPerYear
 110       *
 111       * Returns the number of days in a specified year, as defined by the "basis" value
 112       *
 113       * @param    integer        $year    The year against which we're testing
 114       * @param   integer        $basis    The type of day count:
 115       *                                    0 or omitted US (NASD)    360
 116       *                                    1                        Actual (365 or 366 in a leap year)
 117       *                                    2                        360
 118       *                                    3                        365
 119       *                                    4                        European 360
 120       * @return    integer
 121       */
 122      private static function daysPerYear($year, $basis = 0)
 123      {
 124          switch ($basis) {
 125              case 0:
 126              case 2:
 127              case 4:
 128                  $daysPerYear = 360;
 129                  break;
 130              case 3:
 131                  $daysPerYear = 365;
 132                  break;
 133              case 1:
 134                  $daysPerYear = (PHPExcel_Calculation_DateTime::isLeapYear($year)) ? 366 : 365;
 135                  break;
 136              default:
 137                  return PHPExcel_Calculation_Functions::NaN();
 138          }
 139          return $daysPerYear;
 140      }
 141  
 142  
 143      private static function interestAndPrincipal($rate = 0, $per = 0, $nper = 0, $pv = 0, $fv = 0, $type = 0)
 144      {
 145          $pmt = self::PMT($rate, $nper, $pv, $fv, $type);
 146          $capital = $pv;
 147          for ($i = 1; $i<= $per; ++$i) {
 148              $interest = ($type && $i == 1) ? 0 : -$capital * $rate;
 149              $principal = $pmt - $interest;
 150              $capital += $principal;
 151          }
 152          return array($interest, $principal);
 153      }
 154  
 155  
 156      /**
 157       * ACCRINT
 158       *
 159       * Returns the accrued interest for a security that pays periodic interest.
 160       *
 161       * Excel Function:
 162       *        ACCRINT(issue,firstinterest,settlement,rate,par,frequency[,basis])
 163       *
 164       * @access    public
 165       * @category Financial Functions
 166       * @param    mixed    $issue            The security's issue date.
 167       * @param    mixed    $firstinterest    The security's first interest date.
 168       * @param    mixed    $settlement        The security's settlement date.
 169       *                                    The security settlement date is the date after the issue date
 170       *                                    when the security is traded to the buyer.
 171       * @param    float    $rate            The security's annual coupon rate.
 172       * @param    float    $par            The security's par value.
 173       *                                    If you omit par, ACCRINT uses $1,000.
 174       * @param    integer    $frequency        the number of coupon payments per year.
 175       *                                    Valid frequency values are:
 176       *                                        1    Annual
 177       *                                        2    Semi-Annual
 178       *                                        4    Quarterly
 179       *                                    If working in Gnumeric Mode, the following frequency options are
 180       *                                    also available
 181       *                                        6    Bimonthly
 182       *                                        12    Monthly
 183       * @param    integer    $basis            The type of day count to use.
 184       *                                        0 or omitted    US (NASD) 30/360
 185       *                                        1                Actual/actual
 186       *                                        2                Actual/360
 187       *                                        3                Actual/365
 188       *                                        4                European 30/360
 189       * @return    float
 190       */
 191      public static function ACCRINT($issue, $firstinterest, $settlement, $rate, $par = 1000, $frequency = 1, $basis = 0)
 192      {
 193          $issue        = PHPExcel_Calculation_Functions::flattenSingleValue($issue);
 194          $firstinterest    = PHPExcel_Calculation_Functions::flattenSingleValue($firstinterest);
 195          $settlement    = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
 196          $rate        = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
 197          $par        = (is_null($par))        ? 1000 :    PHPExcel_Calculation_Functions::flattenSingleValue($par);
 198          $frequency    = (is_null($frequency))    ? 1    :         PHPExcel_Calculation_Functions::flattenSingleValue($frequency);
 199          $basis        = (is_null($basis))        ? 0    :        PHPExcel_Calculation_Functions::flattenSingleValue($basis);
 200  
 201          //    Validate
 202          if ((is_numeric($rate)) && (is_numeric($par))) {
 203              $rate    = (float) $rate;
 204              $par    = (float) $par;
 205              if (($rate <= 0) || ($par <= 0)) {
 206                  return PHPExcel_Calculation_Functions::NaN();
 207              }
 208              $daysBetweenIssueAndSettlement = PHPExcel_Calculation_DateTime::YEARFRAC($issue, $settlement, $basis);
 209              if (!is_numeric($daysBetweenIssueAndSettlement)) {
 210                  //    return date error
 211                  return $daysBetweenIssueAndSettlement;
 212              }
 213  
 214              return $par * $rate * $daysBetweenIssueAndSettlement;
 215          }
 216          return PHPExcel_Calculation_Functions::VALUE();
 217      }
 218  
 219  
 220      /**
 221       * ACCRINTM
 222       *
 223       * Returns the accrued interest for a security that pays interest at maturity.
 224       *
 225       * Excel Function:
 226       *        ACCRINTM(issue,settlement,rate[,par[,basis]])
 227       *
 228       * @access    public
 229       * @category Financial Functions
 230       * @param    mixed    issue        The security's issue date.
 231       * @param    mixed    settlement    The security's settlement (or maturity) date.
 232       * @param    float    rate        The security's annual coupon rate.
 233       * @param    float    par            The security's par value.
 234       *                                    If you omit par, ACCRINT uses $1,000.
 235       * @param    integer    basis        The type of day count to use.
 236       *                                        0 or omitted    US (NASD) 30/360
 237       *                                        1                Actual/actual
 238       *                                        2                Actual/360
 239       *                                        3                Actual/365
 240       *                                        4                European 30/360
 241       * @return    float
 242       */
 243      public static function ACCRINTM($issue, $settlement, $rate, $par = 1000, $basis = 0)
 244      {
 245          $issue        = PHPExcel_Calculation_Functions::flattenSingleValue($issue);
 246          $settlement    = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
 247          $rate        = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
 248          $par        = (is_null($par))    ? 1000 :    PHPExcel_Calculation_Functions::flattenSingleValue($par);
 249          $basis        = (is_null($basis))    ? 0 :        PHPExcel_Calculation_Functions::flattenSingleValue($basis);
 250  
 251          //    Validate
 252          if ((is_numeric($rate)) && (is_numeric($par))) {
 253              $rate    = (float) $rate;
 254              $par    = (float) $par;
 255              if (($rate <= 0) || ($par <= 0)) {
 256                  return PHPExcel_Calculation_Functions::NaN();
 257              }
 258              $daysBetweenIssueAndSettlement = PHPExcel_Calculation_DateTime::YEARFRAC($issue, $settlement, $basis);
 259              if (!is_numeric($daysBetweenIssueAndSettlement)) {
 260                  //    return date error
 261                  return $daysBetweenIssueAndSettlement;
 262              }
 263              return $par * $rate * $daysBetweenIssueAndSettlement;
 264          }
 265          return PHPExcel_Calculation_Functions::VALUE();
 266      }
 267  
 268  
 269      /**
 270       * AMORDEGRC
 271       *
 272       * Returns the depreciation for each accounting period.
 273       * This function is provided for the French accounting system. If an asset is purchased in
 274       * the middle of the accounting period, the prorated depreciation is taken into account.
 275       * The function is similar to AMORLINC, except that a depreciation coefficient is applied in
 276       * the calculation depending on the life of the assets.
 277       * This function will return the depreciation until the last period of the life of the assets
 278       * or until the cumulated value of depreciation is greater than the cost of the assets minus
 279       * the salvage value.
 280       *
 281       * Excel Function:
 282       *        AMORDEGRC(cost,purchased,firstPeriod,salvage,period,rate[,basis])
 283       *
 284       * @access    public
 285       * @category Financial Functions
 286       * @param    float    cost        The cost of the asset.
 287       * @param    mixed    purchased    Date of the purchase of the asset.
 288       * @param    mixed    firstPeriod    Date of the end of the first period.
 289       * @param    mixed    salvage        The salvage value at the end of the life of the asset.
 290       * @param    float    period        The period.
 291       * @param    float    rate        Rate of depreciation.
 292       * @param    integer    basis        The type of day count to use.
 293       *                                        0 or omitted    US (NASD) 30/360
 294       *                                        1                Actual/actual
 295       *                                        2                Actual/360
 296       *                                        3                Actual/365
 297       *                                        4                European 30/360
 298       * @return    float
 299       */
 300      public static function AMORDEGRC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis = 0)
 301      {
 302          $cost            = PHPExcel_Calculation_Functions::flattenSingleValue($cost);
 303          $purchased        = PHPExcel_Calculation_Functions::flattenSingleValue($purchased);
 304          $firstPeriod    = PHPExcel_Calculation_Functions::flattenSingleValue($firstPeriod);
 305          $salvage        = PHPExcel_Calculation_Functions::flattenSingleValue($salvage);
 306          $period            = floor(PHPExcel_Calculation_Functions::flattenSingleValue($period));
 307          $rate            = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
 308          $basis            = (is_null($basis))    ? 0 :    (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
 309  
 310          //    The depreciation coefficients are:
 311          //    Life of assets (1/rate)        Depreciation coefficient
 312          //    Less than 3 years            1
 313          //    Between 3 and 4 years        1.5
 314          //    Between 5 and 6 years        2
 315          //    More than 6 years            2.5
 316          $fUsePer = 1.0 / $rate;
 317          if ($fUsePer < 3.0) {
 318              $amortiseCoeff = 1.0;
 319          } elseif ($fUsePer < 5.0) {
 320              $amortiseCoeff = 1.5;
 321          } elseif ($fUsePer <= 6.0) {
 322              $amortiseCoeff = 2.0;
 323          } else {
 324              $amortiseCoeff = 2.5;
 325          }
 326  
 327          $rate *= $amortiseCoeff;
 328          $fNRate = round(PHPExcel_Calculation_DateTime::YEARFRAC($purchased, $firstPeriod, $basis) * $rate * $cost, 0);
 329          $cost -= $fNRate;
 330          $fRest = $cost - $salvage;
 331  
 332          for ($n = 0; $n < $period; ++$n) {
 333              $fNRate = round($rate * $cost, 0);
 334              $fRest -= $fNRate;
 335  
 336              if ($fRest < 0.0) {
 337                  switch ($period - $n) {
 338                      case 0:
 339                      case 1:
 340                          return round($cost * 0.5, 0);
 341                      default:
 342                          return 0.0;
 343                  }
 344              }
 345              $cost -= $fNRate;
 346          }
 347          return $fNRate;
 348      }
 349  
 350  
 351      /**
 352       * AMORLINC
 353       *
 354       * Returns the depreciation for each accounting period.
 355       * This function is provided for the French accounting system. If an asset is purchased in
 356       * the middle of the accounting period, the prorated depreciation is taken into account.
 357       *
 358       * Excel Function:
 359       *        AMORLINC(cost,purchased,firstPeriod,salvage,period,rate[,basis])
 360       *
 361       * @access    public
 362       * @category Financial Functions
 363       * @param    float    cost        The cost of the asset.
 364       * @param    mixed    purchased    Date of the purchase of the asset.
 365       * @param    mixed    firstPeriod    Date of the end of the first period.
 366       * @param    mixed    salvage        The salvage value at the end of the life of the asset.
 367       * @param    float    period        The period.
 368       * @param    float    rate        Rate of depreciation.
 369       * @param    integer    basis        The type of day count to use.
 370       *                                        0 or omitted    US (NASD) 30/360
 371       *                                        1                Actual/actual
 372       *                                        2                Actual/360
 373       *                                        3                Actual/365
 374       *                                        4                European 30/360
 375       * @return    float
 376       */
 377      public static function AMORLINC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis = 0)
 378      {
 379          $cost        = PHPExcel_Calculation_Functions::flattenSingleValue($cost);
 380          $purchased   = PHPExcel_Calculation_Functions::flattenSingleValue($purchased);
 381          $firstPeriod = PHPExcel_Calculation_Functions::flattenSingleValue($firstPeriod);
 382          $salvage     = PHPExcel_Calculation_Functions::flattenSingleValue($salvage);
 383          $period      = PHPExcel_Calculation_Functions::flattenSingleValue($period);
 384          $rate        = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
 385          $basis       = (is_null($basis)) ? 0 : (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
 386  
 387          $fOneRate = $cost * $rate;
 388          $fCostDelta = $cost - $salvage;
 389          //    Note, quirky variation for leap years on the YEARFRAC for this function
 390          $purchasedYear = PHPExcel_Calculation_DateTime::YEAR($purchased);
 391          $yearFrac = PHPExcel_Calculation_DateTime::YEARFRAC($purchased, $firstPeriod, $basis);
 392  
 393          if (($basis == 1) && ($yearFrac < 1) && (PHPExcel_Calculation_DateTime::isLeapYear($purchasedYear))) {
 394              $yearFrac *= 365 / 366;
 395          }
 396  
 397          $f0Rate = $yearFrac * $rate * $cost;
 398          $nNumOfFullPeriods = intval(($cost - $salvage - $f0Rate) / $fOneRate);
 399  
 400          if ($period == 0) {
 401              return $f0Rate;
 402          } elseif ($period <= $nNumOfFullPeriods) {
 403              return $fOneRate;
 404          } elseif ($period == ($nNumOfFullPeriods + 1)) {
 405              return ($fCostDelta - $fOneRate * $nNumOfFullPeriods - $f0Rate);
 406          } else {
 407              return 0.0;
 408          }
 409      }
 410  
 411  
 412      /**
 413       * COUPDAYBS
 414       *
 415       * Returns the number of days from the beginning of the coupon period to the settlement date.
 416       *
 417       * Excel Function:
 418       *        COUPDAYBS(settlement,maturity,frequency[,basis])
 419       *
 420       * @access    public
 421       * @category Financial Functions
 422       * @param    mixed    settlement    The security's settlement date.
 423       *                                The security settlement date is the date after the issue
 424       *                                date when the security is traded to the buyer.
 425       * @param    mixed    maturity    The security's maturity date.
 426       *                                The maturity date is the date when the security expires.
 427       * @param    mixed    frequency    the number of coupon payments per year.
 428       *                                    Valid frequency values are:
 429       *                                        1    Annual
 430       *                                        2    Semi-Annual
 431       *                                        4    Quarterly
 432       *                                    If working in Gnumeric Mode, the following frequency options are
 433       *                                    also available
 434       *                                        6    Bimonthly
 435       *                                        12    Monthly
 436       * @param    integer        basis        The type of day count to use.
 437       *                                        0 or omitted    US (NASD) 30/360
 438       *                                        1                Actual/actual
 439       *                                        2                Actual/360
 440       *                                        3                Actual/365
 441       *                                        4                European 30/360
 442       * @return    float
 443       */
 444      public static function COUPDAYBS($settlement, $maturity, $frequency, $basis = 0)
 445      {
 446          $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
 447          $maturity   = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
 448          $frequency  = (int) PHPExcel_Calculation_Functions::flattenSingleValue($frequency);
 449          $basis      = (is_null($basis)) ? 0 : (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
 450  
 451          if (is_string($settlement = PHPExcel_Calculation_DateTime::getDateValue($settlement))) {
 452              return PHPExcel_Calculation_Functions::VALUE();
 453          }
 454          if (is_string($maturity = PHPExcel_Calculation_DateTime::getDateValue($maturity))) {
 455              return PHPExcel_Calculation_Functions::VALUE();
 456          }
 457  
 458          if (($settlement > $maturity) ||
 459              (!self::isValidFrequency($frequency)) ||
 460              (($basis < 0) || ($basis > 4))) {
 461              return PHPExcel_Calculation_Functions::NaN();
 462          }
 463  
 464          $daysPerYear = self::daysPerYear(PHPExcel_Calculation_DateTime::YEAR($settlement), $basis);
 465          $prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, false);
 466  
 467          return PHPExcel_Calculation_DateTime::YEARFRAC($prev, $settlement, $basis) * $daysPerYear;
 468      }
 469  
 470  
 471      /**
 472       * COUPDAYS
 473       *
 474       * Returns the number of days in the coupon period that contains the settlement date.
 475       *
 476       * Excel Function:
 477       *        COUPDAYS(settlement,maturity,frequency[,basis])
 478       *
 479       * @access    public
 480       * @category Financial Functions
 481       * @param    mixed    settlement    The security's settlement date.
 482       *                                The security settlement date is the date after the issue
 483       *                                date when the security is traded to the buyer.
 484       * @param    mixed    maturity    The security's maturity date.
 485       *                                The maturity date is the date when the security expires.
 486       * @param    mixed    frequency    the number of coupon payments per year.
 487       *                                    Valid frequency values are:
 488       *                                        1    Annual
 489       *                                        2    Semi-Annual
 490       *                                        4    Quarterly
 491       *                                    If working in Gnumeric Mode, the following frequency options are
 492       *                                    also available
 493       *                                        6    Bimonthly
 494       *                                        12    Monthly
 495       * @param    integer        basis        The type of day count to use.
 496       *                                        0 or omitted    US (NASD) 30/360
 497       *                                        1                Actual/actual
 498       *                                        2                Actual/360
 499       *                                        3                Actual/365
 500       *                                        4                European 30/360
 501       * @return    float
 502       */
 503      public static function COUPDAYS($settlement, $maturity, $frequency, $basis = 0)
 504      {
 505          $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
 506          $maturity   = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
 507          $frequency  = (int) PHPExcel_Calculation_Functions::flattenSingleValue($frequency);
 508          $basis      = (is_null($basis)) ? 0 : (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
 509  
 510          if (is_string($settlement = PHPExcel_Calculation_DateTime::getDateValue($settlement))) {
 511              return PHPExcel_Calculation_Functions::VALUE();
 512          }
 513          if (is_string($maturity = PHPExcel_Calculation_DateTime::getDateValue($maturity))) {
 514              return PHPExcel_Calculation_Functions::VALUE();
 515          }
 516  
 517          if (($settlement > $maturity) ||
 518              (!self::isValidFrequency($frequency)) ||
 519              (($basis < 0) || ($basis > 4))) {
 520              return PHPExcel_Calculation_Functions::NaN();
 521          }
 522  
 523          switch ($basis) {
 524              case 3:
 525                  // Actual/365
 526                  return 365 / $frequency;
 527              case 1:
 528                  // Actual/actual
 529                  if ($frequency == 1) {
 530                      $daysPerYear = self::daysPerYear(PHPExcel_Calculation_DateTime::YEAR($maturity), $basis);
 531                      return ($daysPerYear / $frequency);
 532                  }
 533                  $prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, false);
 534                  $next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
 535                  return ($next - $prev);
 536              default:
 537                  // US (NASD) 30/360, Actual/360 or European 30/360
 538                  return 360 / $frequency;
 539          }
 540          return PHPExcel_Calculation_Functions::VALUE();
 541      }
 542  
 543  
 544      /**
 545       * COUPDAYSNC
 546       *
 547       * Returns the number of days from the settlement date to the next coupon date.
 548       *
 549       * Excel Function:
 550       *        COUPDAYSNC(settlement,maturity,frequency[,basis])
 551       *
 552       * @access    public
 553       * @category Financial Functions
 554       * @param    mixed    settlement    The security's settlement date.
 555       *                                The security settlement date is the date after the issue
 556       *                                date when the security is traded to the buyer.
 557       * @param    mixed    maturity    The security's maturity date.
 558       *                                The maturity date is the date when the security expires.
 559       * @param    mixed    frequency    the number of coupon payments per year.
 560       *                                    Valid frequency values are:
 561       *                                        1    Annual
 562       *                                        2    Semi-Annual
 563       *                                        4    Quarterly
 564       *                                    If working in Gnumeric Mode, the following frequency options are
 565       *                                    also available
 566       *                                        6    Bimonthly
 567       *                                        12    Monthly
 568       * @param    integer        basis        The type of day count to use.
 569       *                                        0 or omitted    US (NASD) 30/360
 570       *                                        1                Actual/actual
 571       *                                        2                Actual/360
 572       *                                        3                Actual/365
 573       *                                        4                European 30/360
 574       * @return    float
 575       */
 576      public static function COUPDAYSNC($settlement, $maturity, $frequency, $basis = 0)
 577      {
 578          $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
 579          $maturity   = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
 580          $frequency  = (int) PHPExcel_Calculation_Functions::flattenSingleValue($frequency);
 581          $basis      = (is_null($basis)) ? 0 : (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
 582  
 583          if (is_string($settlement = PHPExcel_Calculation_DateTime::getDateValue($settlement))) {
 584              return PHPExcel_Calculation_Functions::VALUE();
 585          }
 586          if (is_string($maturity = PHPExcel_Calculation_DateTime::getDateValue($maturity))) {
 587              return PHPExcel_Calculation_Functions::VALUE();
 588          }
 589  
 590          if (($settlement > $maturity) ||
 591              (!self::isValidFrequency($frequency)) ||
 592              (($basis < 0) || ($basis > 4))) {
 593              return PHPExcel_Calculation_Functions::NaN();
 594          }
 595  
 596          $daysPerYear = self::daysPerYear(PHPExcel_Calculation_DateTime::YEAR($settlement), $basis);
 597          $next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
 598  
 599          return PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $next, $basis) * $daysPerYear;
 600      }
 601  
 602  
 603      /**
 604       * COUPNCD
 605       *
 606       * Returns the next coupon date after the settlement date.
 607       *
 608       * Excel Function:
 609       *        COUPNCD(settlement,maturity,frequency[,basis])
 610       *
 611       * @access    public
 612       * @category Financial Functions
 613       * @param    mixed    settlement    The security's settlement date.
 614       *                                The security settlement date is the date after the issue
 615       *                                date when the security is traded to the buyer.
 616       * @param    mixed    maturity    The security's maturity date.
 617       *                                The maturity date is the date when the security expires.
 618       * @param    mixed    frequency    the number of coupon payments per year.
 619       *                                    Valid frequency values are:
 620       *                                        1    Annual
 621       *                                        2    Semi-Annual
 622       *                                        4    Quarterly
 623       *                                    If working in Gnumeric Mode, the following frequency options are
 624       *                                    also available
 625       *                                        6    Bimonthly
 626       *                                        12    Monthly
 627       * @param    integer        basis        The type of day count to use.
 628       *                                        0 or omitted    US (NASD) 30/360
 629       *                                        1                Actual/actual
 630       *                                        2                Actual/360
 631       *                                        3                Actual/365
 632       *                                        4                European 30/360
 633       * @return    mixed    Excel date/time serial value, PHP date/time serial value or PHP date/time object,
 634       *                        depending on the value of the ReturnDateType flag
 635       */
 636      public static function COUPNCD($settlement, $maturity, $frequency, $basis = 0)
 637      {
 638          $settlement    = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
 639          $maturity    = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
 640          $frequency    = (int) PHPExcel_Calculation_Functions::flattenSingleValue($frequency);
 641          $basis        = (is_null($basis))    ? 0 :    (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
 642  
 643          if (is_string($settlement = PHPExcel_Calculation_DateTime::getDateValue($settlement))) {
 644              return PHPExcel_Calculation_Functions::VALUE();
 645          }
 646          if (is_string($maturity = PHPExcel_Calculation_DateTime::getDateValue($maturity))) {
 647              return PHPExcel_Calculation_Functions::VALUE();
 648          }
 649  
 650          if (($settlement > $maturity) ||
 651              (!self::isValidFrequency($frequency)) ||
 652              (($basis < 0) || ($basis > 4))) {
 653              return PHPExcel_Calculation_Functions::NaN();
 654          }
 655  
 656          return self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
 657      }
 658  
 659  
 660      /**
 661       * COUPNUM
 662       *
 663       * Returns the number of coupons payable between the settlement date and maturity date,
 664       * rounded up to the nearest whole coupon.
 665       *
 666       * Excel Function:
 667       *        COUPNUM(settlement,maturity,frequency[,basis])
 668       *
 669       * @access    public
 670       * @category Financial Functions
 671       * @param    mixed    settlement    The security's settlement date.
 672       *                                The security settlement date is the date after the issue
 673       *                                date when the security is traded to the buyer.
 674       * @param    mixed    maturity    The security's maturity date.
 675       *                                The maturity date is the date when the security expires.
 676       * @param    mixed    frequency    the number of coupon payments per year.
 677       *                                    Valid frequency values are:
 678       *                                        1    Annual
 679       *                                        2    Semi-Annual
 680       *                                        4    Quarterly
 681       *                                    If working in Gnumeric Mode, the following frequency options are
 682       *                                    also available
 683       *                                        6    Bimonthly
 684       *                                        12    Monthly
 685       * @param    integer        basis        The type of day count to use.
 686       *                                        0 or omitted    US (NASD) 30/360
 687       *                                        1                Actual/actual
 688       *                                        2                Actual/360
 689       *                                        3                Actual/365
 690       *                                        4                European 30/360
 691       * @return    integer
 692       */
 693      public static function COUPNUM($settlement, $maturity, $frequency, $basis = 0)
 694      {
 695          $settlement    = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
 696          $maturity    = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
 697          $frequency    = (int) PHPExcel_Calculation_Functions::flattenSingleValue($frequency);
 698          $basis        = (is_null($basis))    ? 0 :    (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
 699  
 700          if (is_string($settlement = PHPExcel_Calculation_DateTime::getDateValue($settlement))) {
 701              return PHPExcel_Calculation_Functions::VALUE();
 702          }
 703          if (is_string($maturity = PHPExcel_Calculation_DateTime::getDateValue($maturity))) {
 704              return PHPExcel_Calculation_Functions::VALUE();
 705          }
 706  
 707          if (($settlement > $maturity) ||
 708              (!self::isValidFrequency($frequency)) ||
 709              (($basis < 0) || ($basis > 4))) {
 710              return PHPExcel_Calculation_Functions::NaN();
 711          }
 712  
 713          $settlement = self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
 714          $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity, $basis) * 365;
 715  
 716          switch ($frequency) {
 717              case 1: // annual payments
 718                  return ceil($daysBetweenSettlementAndMaturity / 360);
 719              case 2: // half-yearly
 720                  return ceil($daysBetweenSettlementAndMaturity / 180);
 721              case 4: // quarterly
 722                  return ceil($daysBetweenSettlementAndMaturity / 90);
 723              case 6: // bimonthly
 724                  return ceil($daysBetweenSettlementAndMaturity / 60);
 725              case 12: // monthly
 726                  return ceil($daysBetweenSettlementAndMaturity / 30);
 727          }
 728          return PHPExcel_Calculation_Functions::VALUE();
 729      }
 730  
 731  
 732      /**
 733       * COUPPCD
 734       *
 735       * Returns the previous coupon date before the settlement date.
 736       *
 737       * Excel Function:
 738       *        COUPPCD(settlement,maturity,frequency[,basis])
 739       *
 740       * @access    public
 741       * @category Financial Functions
 742       * @param    mixed    settlement    The security's settlement date.
 743       *                                The security settlement date is the date after the issue
 744       *                                date when the security is traded to the buyer.
 745       * @param    mixed    maturity    The security's maturity date.
 746       *                                The maturity date is the date when the security expires.
 747       * @param    mixed    frequency    the number of coupon payments per year.
 748       *                                    Valid frequency values are:
 749       *                                        1    Annual
 750       *                                        2    Semi-Annual
 751       *                                        4    Quarterly
 752       *                                    If working in Gnumeric Mode, the following frequency options are
 753       *                                    also available
 754       *                                        6    Bimonthly
 755       *                                        12    Monthly
 756       * @param    integer        basis        The type of day count to use.
 757       *                                        0 or omitted    US (NASD) 30/360
 758       *                                        1                Actual/actual
 759       *                                        2                Actual/360
 760       *                                        3                Actual/365
 761       *                                        4                European 30/360
 762       * @return    mixed    Excel date/time serial value, PHP date/time serial value or PHP date/time object,
 763       *                        depending on the value of the ReturnDateType flag
 764       */
 765      public static function COUPPCD($settlement, $maturity, $frequency, $basis = 0)
 766      {
 767          $settlement    = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
 768          $maturity    = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
 769          $frequency    = (int) PHPExcel_Calculation_Functions::flattenSingleValue($frequency);
 770          $basis        = (is_null($basis))    ? 0 :    (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
 771  
 772          if (is_string($settlement = PHPExcel_Calculation_DateTime::getDateValue($settlement))) {
 773              return PHPExcel_Calculation_Functions::VALUE();
 774          }
 775          if (is_string($maturity = PHPExcel_Calculation_DateTime::getDateValue($maturity))) {
 776              return PHPExcel_Calculation_Functions::VALUE();
 777          }
 778  
 779          if (($settlement > $maturity) ||
 780              (!self::isValidFrequency($frequency)) ||
 781              (($basis < 0) || ($basis > 4))) {
 782              return PHPExcel_Calculation_Functions::NaN();
 783          }
 784  
 785          return self::couponFirstPeriodDate($settlement, $maturity, $frequency, false);
 786      }
 787  
 788  
 789      /**
 790       * CUMIPMT
 791       *
 792       * Returns the cumulative interest paid on a loan between the start and end periods.
 793       *
 794       * Excel Function:
 795       *        CUMIPMT(rate,nper,pv,start,end[,type])
 796       *
 797       * @access    public
 798       * @category Financial Functions
 799       * @param    float    $rate    The Interest rate
 800       * @param    integer    $nper    The total number of payment periods
 801       * @param    float    $pv        Present Value
 802       * @param    integer    $start    The first period in the calculation.
 803       *                            Payment periods are numbered beginning with 1.
 804       * @param    integer    $end    The last period in the calculation.
 805       * @param    integer    $type    A number 0 or 1 and indicates when payments are due:
 806       *                                0 or omitted    At the end of the period.
 807       *                                1                At the beginning of the period.
 808       * @return    float
 809       */
 810      public static function CUMIPMT($rate, $nper, $pv, $start, $end, $type = 0)
 811      {
 812          $rate    = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
 813          $nper    = (int) PHPExcel_Calculation_Functions::flattenSingleValue($nper);
 814          $pv        = PHPExcel_Calculation_Functions::flattenSingleValue($pv);
 815          $start    = (int) PHPExcel_Calculation_Functions::flattenSingleValue($start);
 816          $end    = (int) PHPExcel_Calculation_Functions::flattenSingleValue($end);
 817          $type    = (int) PHPExcel_Calculation_Functions::flattenSingleValue($type);
 818  
 819          // Validate parameters
 820          if ($type != 0 && $type != 1) {
 821              return PHPExcel_Calculation_Functions::NaN();
 822          }
 823          if ($start < 1 || $start > $end) {
 824              return PHPExcel_Calculation_Functions::VALUE();
 825          }
 826  
 827          // Calculate
 828          $interest = 0;
 829          for ($per = $start; $per <= $end; ++$per) {
 830              $interest += self::IPMT($rate, $per, $nper, $pv, 0, $type);
 831          }
 832  
 833          return $interest;
 834      }
 835  
 836  
 837      /**
 838       * CUMPRINC
 839       *
 840       * Returns the cumulative principal paid on a loan between the start and end periods.
 841       *
 842       * Excel Function:
 843       *        CUMPRINC(rate,nper,pv,start,end[,type])
 844       *
 845       * @access    public
 846       * @category Financial Functions
 847       * @param    float    $rate    The Interest rate
 848       * @param    integer    $nper    The total number of payment periods
 849       * @param    float    $pv        Present Value
 850       * @param    integer    $start    The first period in the calculation.
 851       *                            Payment periods are numbered beginning with 1.
 852       * @param    integer    $end    The last period in the calculation.
 853       * @param    integer    $type    A number 0 or 1 and indicates when payments are due:
 854       *                                0 or omitted    At the end of the period.
 855       *                                1                At the beginning of the period.
 856       * @return    float
 857       */
 858      public static function CUMPRINC($rate, $nper, $pv, $start, $end, $type = 0)
 859      {
 860          $rate    = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
 861          $nper    = (int) PHPExcel_Calculation_Functions::flattenSingleValue($nper);
 862          $pv        = PHPExcel_Calculation_Functions::flattenSingleValue($pv);
 863          $start    = (int) PHPExcel_Calculation_Functions::flattenSingleValue($start);
 864          $end    = (int) PHPExcel_Calculation_Functions::flattenSingleValue($end);
 865          $type    = (int) PHPExcel_Calculation_Functions::flattenSingleValue($type);
 866  
 867          // Validate parameters
 868          if ($type != 0 && $type != 1) {
 869              return PHPExcel_Calculation_Functions::NaN();
 870          }
 871          if ($start < 1 || $start > $end) {
 872              return PHPExcel_Calculation_Functions::VALUE();
 873          }
 874  
 875          // Calculate
 876          $principal = 0;
 877          for ($per = $start; $per <= $end; ++$per) {
 878              $principal += self::PPMT($rate, $per, $nper, $pv, 0, $type);
 879          }
 880  
 881          return $principal;
 882      }
 883  
 884  
 885      /**
 886       * DB
 887       *
 888       * Returns the depreciation of an asset for a specified period using the
 889       * fixed-declining balance method.
 890       * This form of depreciation is used if you want to get a higher depreciation value
 891       * at the beginning of the depreciation (as opposed to linear depreciation). The
 892       * depreciation value is reduced with every depreciation period by the depreciation
 893       * already deducted from the initial cost.
 894       *
 895       * Excel Function:
 896       *        DB(cost,salvage,life,period[,month])
 897       *
 898       * @access    public
 899       * @category Financial Functions
 900       * @param    float    cost        Initial cost of the asset.
 901       * @param    float    salvage        Value at the end of the depreciation.
 902       *                                (Sometimes called the salvage value of the asset)
 903       * @param    integer    life        Number of periods over which the asset is depreciated.
 904       *                                (Sometimes called the useful life of the asset)
 905       * @param    integer    period        The period for which you want to calculate the
 906       *                                depreciation. Period must use the same units as life.
 907       * @param    integer    month        Number of months in the first year. If month is omitted,
 908       *                                it defaults to 12.
 909       * @return    float
 910       */
 911      public static function DB($cost, $salvage, $life, $period, $month = 12)
 912      {
 913          $cost        = PHPExcel_Calculation_Functions::flattenSingleValue($cost);
 914          $salvage    = PHPExcel_Calculation_Functions::flattenSingleValue($salvage);
 915          $life        = PHPExcel_Calculation_Functions::flattenSingleValue($life);
 916          $period        = PHPExcel_Calculation_Functions::flattenSingleValue($period);
 917          $month        = PHPExcel_Calculation_Functions::flattenSingleValue($month);
 918  
 919          //    Validate
 920          if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period)) && (is_numeric($month))) {
 921              $cost    = (float) $cost;
 922              $salvage = (float) $salvage;
 923              $life    = (int) $life;
 924              $period  = (int) $period;
 925              $month   = (int) $month;
 926              if ($cost == 0) {
 927                  return 0.0;
 928              } elseif (($cost < 0) || (($salvage / $cost) < 0) || ($life <= 0) || ($period < 1) || ($month < 1)) {
 929                  return PHPExcel_Calculation_Functions::NaN();
 930              }
 931              //    Set Fixed Depreciation Rate
 932              $fixedDepreciationRate = 1 - pow(($salvage / $cost), (1 / $life));
 933              $fixedDepreciationRate = round($fixedDepreciationRate, 3);
 934  
 935              //    Loop through each period calculating the depreciation
 936              $previousDepreciation = 0;
 937              for ($per = 1; $per <= $period; ++$per) {
 938                  if ($per == 1) {
 939                      $depreciation = $cost * $fixedDepreciationRate * $month / 12;
 940                  } elseif ($per == ($life + 1)) {
 941                      $depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate * (12 - $month) / 12;
 942                  } else {
 943                      $depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate;
 944                  }
 945                  $previousDepreciation += $depreciation;
 946              }
 947              if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC) {
 948                  $depreciation = round($depreciation, 2);
 949              }
 950              return $depreciation;
 951          }
 952          return PHPExcel_Calculation_Functions::VALUE();
 953      }
 954  
 955  
 956      /**
 957       * DDB
 958       *
 959       * Returns the depreciation of an asset for a specified period using the
 960       * double-declining balance method or some other method you specify.
 961       *
 962       * Excel Function:
 963       *        DDB(cost,salvage,life,period[,factor])
 964       *
 965       * @access    public
 966       * @category Financial Functions
 967       * @param    float    cost        Initial cost of the asset.
 968       * @param    float    salvage        Value at the end of the depreciation.
 969       *                                (Sometimes called the salvage value of the asset)
 970       * @param    integer    life        Number of periods over which the asset is depreciated.
 971       *                                (Sometimes called the useful life of the asset)
 972       * @param    integer    period        The period for which you want to calculate the
 973       *                                depreciation. Period must use the same units as life.
 974       * @param    float    factor        The rate at which the balance declines.
 975       *                                If factor is omitted, it is assumed to be 2 (the
 976       *                                double-declining balance method).
 977       * @return    float
 978       */
 979      public static function DDB($cost, $salvage, $life, $period, $factor = 2.0)
 980      {
 981          $cost        = PHPExcel_Calculation_Functions::flattenSingleValue($cost);
 982          $salvage    = PHPExcel_Calculation_Functions::flattenSingleValue($salvage);
 983          $life        = PHPExcel_Calculation_Functions::flattenSingleValue($life);
 984          $period        = PHPExcel_Calculation_Functions::flattenSingleValue($period);
 985          $factor        = PHPExcel_Calculation_Functions::flattenSingleValue($factor);
 986  
 987          //    Validate
 988          if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period)) && (is_numeric($factor))) {
 989              $cost    = (float) $cost;
 990              $salvage = (float) $salvage;
 991              $life    = (int) $life;
 992              $period  = (int) $period;
 993              $factor  = (float) $factor;
 994              if (($cost <= 0) || (($salvage / $cost) < 0) || ($life <= 0) || ($period < 1) || ($factor <= 0.0) || ($period > $life)) {
 995                  return PHPExcel_Calculation_Functions::NaN();
 996              }
 997              //    Set Fixed Depreciation Rate
 998              $fixedDepreciationRate = 1 - pow(($salvage / $cost), (1 / $life));
 999              $fixedDepreciationRate = round($fixedDepreciationRate, 3);
1000  
1001              //    Loop through each period calculating the depreciation
1002              $previousDepreciation = 0;
1003              for ($per = 1; $per <= $period; ++$per) {
1004                  $depreciation = min(($cost - $previousDepreciation) * ($factor / $life), ($cost - $salvage - $previousDepreciation));
1005                  $previousDepreciation += $depreciation;
1006              }
1007              if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC) {
1008                  $depreciation = round($depreciation, 2);
1009              }
1010              return $depreciation;
1011          }
1012          return PHPExcel_Calculation_Functions::VALUE();
1013      }
1014  
1015  
1016      /**
1017       * DISC
1018       *
1019       * Returns the discount rate for a security.
1020       *
1021       * Excel Function:
1022       *        DISC(settlement,maturity,price,redemption[,basis])
1023       *
1024       * @access    public
1025       * @category Financial Functions
1026       * @param    mixed    settlement    The security's settlement date.
1027       *                                The security settlement date is the date after the issue
1028       *                                date when the security is traded to the buyer.
1029       * @param    mixed    maturity    The security's maturity date.
1030       *                                The maturity date is the date when the security expires.
1031       * @param    integer    price        The security's price per $100 face value.
1032       * @param    integer    redemption    The security's redemption value per $100 face value.
1033       * @param    integer    basis        The type of day count to use.
1034       *                                        0 or omitted    US (NASD) 30/360
1035       *                                        1                Actual/actual
1036       *                                        2                Actual/360
1037       *                                        3                Actual/365
1038       *                                        4                European 30/360
1039       * @return    float
1040       */
1041      public static function DISC($settlement, $maturity, $price, $redemption, $basis = 0)
1042      {
1043          $settlement    = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
1044          $maturity    = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
1045          $price        = PHPExcel_Calculation_Functions::flattenSingleValue($price);
1046          $redemption    = PHPExcel_Calculation_Functions::flattenSingleValue($redemption);
1047          $basis        = PHPExcel_Calculation_Functions::flattenSingleValue($basis);
1048  
1049          //    Validate
1050          if ((is_numeric($price)) && (is_numeric($redemption)) && (is_numeric($basis))) {
1051              $price        = (float) $price;
1052              $redemption    = (float) $redemption;
1053              $basis        = (int) $basis;
1054              if (($price <= 0) || ($redemption <= 0)) {
1055                  return PHPExcel_Calculation_Functions::NaN();
1056              }
1057              $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity, $basis);
1058              if (!is_numeric($daysBetweenSettlementAndMaturity)) {
1059                  //    return date error
1060                  return $daysBetweenSettlementAndMaturity;
1061              }
1062  
1063              return ((1 - $price / $redemption) / $daysBetweenSettlementAndMaturity);
1064          }
1065          return PHPExcel_Calculation_Functions::VALUE();
1066      }
1067  
1068  
1069      /**
1070       * DOLLARDE
1071       *
1072       * Converts a dollar price expressed as an integer part and a fraction
1073       *        part into a dollar price expressed as a decimal number.
1074       * Fractional dollar numbers are sometimes used for security prices.
1075       *
1076       * Excel Function:
1077       *        DOLLARDE(fractional_dollar,fraction)
1078       *
1079       * @access    public
1080       * @category Financial Functions
1081       * @param    float    $fractional_dollar    Fractional Dollar
1082       * @param    integer    $fraction            Fraction
1083       * @return    float
1084       */
1085      public static function DOLLARDE($fractional_dollar = null, $fraction = 0)
1086      {
1087          $fractional_dollar    = PHPExcel_Calculation_Functions::flattenSingleValue($fractional_dollar);
1088          $fraction            = (int)PHPExcel_Calculation_Functions::flattenSingleValue($fraction);
1089  
1090          // Validate parameters
1091          if (is_null($fractional_dollar) || $fraction < 0) {
1092              return PHPExcel_Calculation_Functions::NaN();
1093          }
1094          if ($fraction == 0) {
1095              return PHPExcel_Calculation_Functions::DIV0();
1096          }
1097  
1098          $dollars = floor($fractional_dollar);
1099          $cents = fmod($fractional_dollar, 1);
1100          $cents /= $fraction;
1101          $cents *= pow(10, ceil(log10($fraction)));
1102          return $dollars + $cents;
1103      }
1104  
1105  
1106      /**
1107       * DOLLARFR
1108       *
1109       * Converts a dollar price expressed as a decimal number into a dollar price
1110       *        expressed as a fraction.
1111       * Fractional dollar numbers are sometimes used for security prices.
1112       *
1113       * Excel Function:
1114       *        DOLLARFR(decimal_dollar,fraction)
1115       *
1116       * @access    public
1117       * @category Financial Functions
1118       * @param    float    $decimal_dollar        Decimal Dollar
1119       * @param    integer    $fraction            Fraction
1120       * @return    float
1121       */
1122      public static function DOLLARFR($decimal_dollar = null, $fraction = 0)
1123      {
1124          $decimal_dollar    = PHPExcel_Calculation_Functions::flattenSingleValue($decimal_dollar);
1125          $fraction        = (int)PHPExcel_Calculation_Functions::flattenSingleValue($fraction);
1126  
1127          // Validate parameters
1128          if (is_null($decimal_dollar) || $fraction < 0) {
1129              return PHPExcel_Calculation_Functions::NaN();
1130          }
1131          if ($fraction == 0) {
1132              return PHPExcel_Calculation_Functions::DIV0();
1133          }
1134  
1135          $dollars = floor($decimal_dollar);
1136          $cents = fmod($decimal_dollar, 1);
1137          $cents *= $fraction;
1138          $cents *= pow(10, -ceil(log10($fraction)));
1139          return $dollars + $cents;
1140      }
1141  
1142  
1143      /**
1144       * EFFECT
1145       *
1146       * Returns the effective interest rate given the nominal rate and the number of
1147       *        compounding payments per year.
1148       *
1149       * Excel Function:
1150       *        EFFECT(nominal_rate,npery)
1151       *
1152       * @access    public
1153       * @category Financial Functions
1154       * @param    float    $nominal_rate        Nominal interest rate
1155       * @param    integer    $npery                Number of compounding payments per year
1156       * @return    float
1157       */
1158      public static function EFFECT($nominal_rate = 0, $npery = 0)
1159      {
1160          $nominal_rate    = PHPExcel_Calculation_Functions::flattenSingleValue($nominal_rate);
1161          $npery            = (int)PHPExcel_Calculation_Functions::flattenSingleValue($npery);
1162  
1163          // Validate parameters
1164          if ($nominal_rate <= 0 || $npery < 1) {
1165              return PHPExcel_Calculation_Functions::NaN();
1166          }
1167  
1168          return pow((1 + $nominal_rate / $npery), $npery) - 1;
1169      }
1170  
1171  
1172      /**
1173       * FV
1174       *
1175       * Returns the Future Value of a cash flow with constant payments and interest rate (annuities).
1176       *
1177       * Excel Function:
1178       *        FV(rate,nper,pmt[,pv[,type]])
1179       *
1180       * @access    public
1181       * @category Financial Functions
1182       * @param    float    $rate    The interest rate per period
1183       * @param    int        $nper    Total number of payment periods in an annuity
1184       * @param    float    $pmt    The payment made each period: it cannot change over the
1185       *                            life of the annuity. Typically, pmt contains principal
1186       *                            and interest but no other fees or taxes.
1187       * @param    float    $pv        Present Value, or the lump-sum amount that a series of
1188       *                            future payments is worth right now.
1189       * @param    integer    $type    A number 0 or 1 and indicates when payments are due:
1190       *                                0 or omitted    At the end of the period.
1191       *                                1                At the beginning of the period.
1192       * @return    float
1193       */
1194      public static function FV($rate = 0, $nper = 0, $pmt = 0, $pv = 0, $type = 0)
1195      {
1196          $rate    = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
1197          $nper    = PHPExcel_Calculation_Functions::flattenSingleValue($nper);
1198          $pmt    = PHPExcel_Calculation_Functions::flattenSingleValue($pmt);
1199          $pv        = PHPExcel_Calculation_Functions::flattenSingleValue($pv);
1200          $type    = PHPExcel_Calculation_Functions::flattenSingleValue($type);
1201  
1202          // Validate parameters
1203          if ($type != 0 && $type != 1) {
1204              return PHPExcel_Calculation_Functions::NaN();
1205          }
1206  
1207          // Calculate
1208          if (!is_null($rate) && $rate != 0) {
1209              return -$pv * pow(1 + $rate, $nper) - $pmt * (1 + $rate * $type) * (pow(1 + $rate, $nper) - 1) / $rate;
1210          }
1211          return -$pv - $pmt * $nper;
1212      }
1213  
1214  
1215      /**
1216       * FVSCHEDULE
1217       *
1218       * Returns the future value of an initial principal after applying a series of compound interest rates.
1219       * Use FVSCHEDULE to calculate the future value of an investment with a variable or adjustable rate.
1220       *
1221       * Excel Function:
1222       *        FVSCHEDULE(principal,schedule)
1223       *
1224       * @param    float    $principal    The present value.
1225       * @param    float[]    $schedule    An array of interest rates to apply.
1226       * @return    float
1227       */
1228      public static function FVSCHEDULE($principal, $schedule)
1229      {
1230          $principal    = PHPExcel_Calculation_Functions::flattenSingleValue($principal);
1231          $schedule    = PHPExcel_Calculation_Functions::flattenArray($schedule);
1232  
1233          foreach ($schedule as $rate) {
1234              $principal *= 1 + $rate;
1235          }
1236  
1237          return $principal;
1238      }
1239  
1240  
1241      /**
1242       * INTRATE
1243       *
1244       * Returns the interest rate for a fully invested security.
1245       *
1246       * Excel Function:
1247       *        INTRATE(settlement,maturity,investment,redemption[,basis])
1248       *
1249       * @param    mixed    $settlement    The security's settlement date.
1250       *                                The security settlement date is the date after the issue date when the security is traded to the buyer.
1251       * @param    mixed    $maturity    The security's maturity date.
1252       *                                The maturity date is the date when the security expires.
1253       * @param    integer    $investment    The amount invested in the security.
1254       * @param    integer    $redemption    The amount to be received at maturity.
1255       * @param    integer    $basis        The type of day count to use.
1256       *                                        0 or omitted    US (NASD) 30/360
1257       *                                        1                Actual/actual
1258       *                                        2                Actual/360
1259       *                                        3                Actual/365
1260       *                                        4                European 30/360
1261       * @return    float
1262       */
1263      public static function INTRATE($settlement, $maturity, $investment, $redemption, $basis = 0)
1264      {
1265          $settlement    = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
1266          $maturity    = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
1267          $investment    = PHPExcel_Calculation_Functions::flattenSingleValue($investment);
1268          $redemption    = PHPExcel_Calculation_Functions::flattenSingleValue($redemption);
1269          $basis        = PHPExcel_Calculation_Functions::flattenSingleValue($basis);
1270  
1271          //    Validate
1272          if ((is_numeric($investment)) && (is_numeric($redemption)) && (is_numeric($basis))) {
1273              $investment    = (float) $investment;
1274              $redemption    = (float) $redemption;
1275              $basis        = (int) $basis;
1276              if (($investment <= 0) || ($redemption <= 0)) {
1277                  return PHPExcel_Calculation_Functions::NaN();
1278              }
1279              $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity, $basis);
1280              if (!is_numeric($daysBetweenSettlementAndMaturity)) {
1281                  //    return date error
1282                  return $daysBetweenSettlementAndMaturity;
1283              }
1284  
1285              return (($redemption / $investment) - 1) / ($daysBetweenSettlementAndMaturity);
1286          }
1287          return PHPExcel_Calculation_Functions::VALUE();
1288      }
1289  
1290  
1291      /**
1292       * IPMT
1293       *
1294       * Returns the interest payment for a given period for an investment based on periodic, constant payments and a constant interest rate.
1295       *
1296       * Excel Function:
1297       *        IPMT(rate,per,nper,pv[,fv][,type])
1298       *
1299       * @param    float    $rate    Interest rate per period
1300       * @param    int        $per    Period for which we want to find the interest
1301       * @param    int        $nper    Number of periods
1302       * @param    float    $pv        Present Value
1303       * @param    float    $fv        Future Value
1304       * @param    int        $type    Payment type: 0 = at the end of each period, 1 = at the beginning of each period
1305       * @return    float
1306       */
1307      public static function IPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0)
1308      {
1309          $rate    = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
1310          $per    = (int) PHPExcel_Calculation_Functions::flattenSingleValue($per);
1311          $nper    = (int) PHPExcel_Calculation_Functions::flattenSingleValue($nper);
1312          $pv        = PHPExcel_Calculation_Functions::flattenSingleValue($pv);
1313          $fv        = PHPExcel_Calculation_Functions::flattenSingleValue($fv);
1314          $type    = (int) PHPExcel_Calculation_Functions::flattenSingleValue($type);
1315  
1316          // Validate parameters
1317          if ($type != 0 && $type != 1) {
1318              return PHPExcel_Calculation_Functions::NaN();
1319          }
1320          if ($per <= 0 || $per > $nper) {
1321              return PHPExcel_Calculation_Functions::VALUE();
1322          }
1323  
1324          // Calculate
1325          $interestAndPrincipal = self::interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
1326          return $interestAndPrincipal[0];
1327      }
1328  
1329      /**
1330       * IRR
1331       *
1332       * Returns the internal rate of return for a series of cash flows represented by the numbers in values.
1333       * These cash flows do not have to be even, as they would be for an annuity. However, the cash flows must occur
1334       * at regular intervals, such as monthly or annually. The internal rate of return is the interest rate received
1335       * for an investment consisting of payments (negative values) and income (positive values) that occur at regular
1336       * periods.
1337       *
1338       * Excel Function:
1339       *        IRR(values[,guess])
1340       *
1341       * @param    float[]    $values        An array or a reference to cells that contain numbers for which you want
1342       *                                    to calculate the internal rate of return.
1343       *                                Values must contain at least one positive value and one negative value to
1344       *                                    calculate the internal rate of return.
1345       * @param    float    $guess        A number that you guess is close to the result of IRR
1346       * @return    float
1347       */
1348      public static function IRR($values, $guess = 0.1)
1349      {
1350          if (!is_array($values)) {
1351              return PHPExcel_Calculation_Functions::VALUE();
1352          }
1353          $values = PHPExcel_Calculation_Functions::flattenArray($values);
1354          $guess = PHPExcel_Calculation_Functions::flattenSingleValue($guess);
1355  
1356          // create an initial range, with a root somewhere between 0 and guess
1357          $x1 = 0.0;
1358          $x2 = $guess;
1359          $f1 = self::NPV($x1, $values);
1360          $f2 = self::NPV($x2, $values);
1361          for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; ++$i) {
1362              if (($f1 * $f2) < 0.0) {
1363                  break;
1364              }
1365              if (abs($f1) < abs($f2)) {
1366                  $f1 = self::NPV($x1 += 1.6 * ($x1 - $x2), $values);
1367              } else {
1368                  $f2 = self::NPV($x2 += 1.6 * ($x2 - $x1), $values);
1369              }
1370          }
1371          if (($f1 * $f2) > 0.0) {
1372              return PHPExcel_Calculation_Functions::VALUE();
1373          }
1374  
1375          $f = self::NPV($x1, $values);
1376          if ($f < 0.0) {
1377              $rtb = $x1;
1378              $dx = $x2 - $x1;
1379          } else {
1380              $rtb = $x2;
1381              $dx = $x1 - $x2;
1382          }
1383  
1384          for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; ++$i) {
1385              $dx *= 0.5;
1386              $x_mid = $rtb + $dx;
1387              $f_mid = self::NPV($x_mid, $values);
1388              if ($f_mid <= 0.0) {
1389                  $rtb = $x_mid;
1390              }
1391              if ((abs($f_mid) < FINANCIAL_PRECISION) || (abs($dx) < FINANCIAL_PRECISION)) {
1392                  return $x_mid;
1393              }
1394          }
1395          return PHPExcel_Calculation_Functions::VALUE();
1396      }
1397  
1398  
1399      /**
1400       * ISPMT
1401       *
1402       * Returns the interest payment for an investment based on an interest rate and a constant payment schedule.
1403       *
1404       * Excel Function:
1405       *     =ISPMT(interest_rate, period, number_payments, PV)
1406       *
1407       * interest_rate is the interest rate for the investment
1408       *
1409       * period is the period to calculate the interest rate.  It must be betweeen 1 and number_payments.
1410       *
1411       * number_payments is the number of payments for the annuity
1412       *
1413       * PV is the loan amount or present value of the payments
1414       */
1415      public static function ISPMT()
1416      {
1417          // Return value
1418          $returnValue = 0;
1419  
1420          // Get the parameters
1421          $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
1422          $interestRate = array_shift($aArgs);
1423          $period = array_shift($aArgs);
1424          $numberPeriods = array_shift($aArgs);
1425          $principleRemaining = array_shift($aArgs);
1426  
1427          // Calculate
1428          $principlePayment = ($principleRemaining * 1.0) / ($numberPeriods * 1.0);
1429          for ($i=0; $i <= $period; ++$i) {
1430              $returnValue = $interestRate * $principleRemaining * -1;
1431              $principleRemaining -= $principlePayment;
1432              // principle needs to be 0 after the last payment, don't let floating point screw it up
1433              if ($i == $numberPeriods) {
1434                  $returnValue = 0;
1435              }
1436          }
1437          return($returnValue);
1438      }
1439  
1440  
1441      /**
1442       * MIRR
1443       *
1444       * Returns the modified internal rate of return for a series of periodic cash flows. MIRR considers both
1445       *        the cost of the investment and the interest received on reinvestment of cash.
1446       *
1447       * Excel Function:
1448       *        MIRR(values,finance_rate, reinvestment_rate)
1449       *
1450       * @param    float[]    $values                An array or a reference to cells that contain a series of payments and
1451       *                                            income occurring at regular intervals.
1452       *                                        Payments are negative value, income is positive values.
1453       * @param    float    $finance_rate        The interest rate you pay on the money used in the cash flows
1454       * @param    float    $reinvestment_rate    The interest rate you receive on the cash flows as you reinvest them
1455       * @return    float
1456       */
1457      public static function MIRR($values, $finance_rate, $reinvestment_rate)
1458      {
1459          if (!is_array($values)) {
1460              return PHPExcel_Calculation_Functions::VALUE();
1461          }
1462          $values                = PHPExcel_Calculation_Functions::flattenArray($values);
1463          $finance_rate        = PHPExcel_Calculation_Functions::flattenSingleValue($finance_rate);
1464          $reinvestment_rate    = PHPExcel_Calculation_Functions::flattenSingleValue($reinvestment_rate);
1465          $n = count($values);
1466  
1467          $rr = 1.0 + $reinvestment_rate;
1468          $fr = 1.0 + $finance_rate;
1469  
1470          $npv_pos = $npv_neg = 0.0;
1471          foreach ($values as $i => $v) {
1472              if ($v >= 0) {
1473                  $npv_pos += $v / pow($rr, $i);
1474              } else {
1475                  $npv_neg += $v / pow($fr, $i);
1476              }
1477          }
1478  
1479          if (($npv_neg == 0) || ($npv_pos == 0) || ($reinvestment_rate <= -1)) {
1480              return PHPExcel_Calculation_Functions::VALUE();
1481          }
1482  
1483          $mirr = pow((-$npv_pos * pow($rr, $n))
1484                  / ($npv_neg * ($rr)), (1.0 / ($n - 1))) - 1.0;
1485  
1486          return (is_finite($mirr) ? $mirr : PHPExcel_Calculation_Functions::VALUE());
1487      }
1488  
1489  
1490      /**
1491       * NOMINAL
1492       *
1493       * Returns the nominal interest rate given the effective rate and the number of compounding payments per year.
1494       *
1495       * @param    float    $effect_rate    Effective interest rate
1496       * @param    int        $npery            Number of compounding payments per year
1497       * @return    float
1498       */
1499      public static function NOMINAL($effect_rate = 0, $npery = 0)
1500      {
1501          $effect_rate    = PHPExcel_Calculation_Functions::flattenSingleValue($effect_rate);
1502          $npery            = (int)PHPExcel_Calculation_Functions::flattenSingleValue($npery);
1503  
1504          // Validate parameters
1505          if ($effect_rate <= 0 || $npery < 1) {
1506              return PHPExcel_Calculation_Functions::NaN();
1507          }
1508  
1509          // Calculate
1510          return $npery * (pow($effect_rate + 1, 1 / $npery) - 1);
1511      }
1512  
1513  
1514      /**
1515       * NPER
1516       *
1517       * Returns the number of periods for a cash flow with constant periodic payments (annuities), and interest rate.
1518       *
1519       * @param    float    $rate    Interest rate per period
1520       * @param    int        $pmt    Periodic payment (annuity)
1521       * @param    float    $pv        Present Value
1522       * @param    float    $fv        Future Value
1523       * @param    int        $type    Payment type: 0 = at the end of each period, 1 = at the beginning of each period
1524       * @return    float
1525       */
1526      public static function NPER($rate = 0, $pmt = 0, $pv = 0, $fv = 0, $type = 0)
1527      {
1528          $rate    = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
1529          $pmt    = PHPExcel_Calculation_Functions::flattenSingleValue($pmt);
1530          $pv        = PHPExcel_Calculation_Functions::flattenSingleValue($pv);
1531          $fv        = PHPExcel_Calculation_Functions::flattenSingleValue($fv);
1532          $type    = PHPExcel_Calculation_Functions::flattenSingleValue($type);
1533  
1534          // Validate parameters
1535          if ($type != 0 && $type != 1) {
1536              return PHPExcel_Calculation_Functions::NaN();
1537          }
1538  
1539          // Calculate
1540          if (!is_null($rate) && $rate != 0) {
1541              if ($pmt == 0 && $pv == 0) {
1542                  return PHPExcel_Calculation_Functions::NaN();
1543              }
1544              return log(($pmt * (1 + $rate * $type) / $rate - $fv) / ($pv + $pmt * (1 + $rate * $type) / $rate)) / log(1 + $rate);
1545          }
1546          if ($pmt == 0) {
1547              return PHPExcel_Calculation_Functions::NaN();
1548          }
1549          return (-$pv -$fv) / $pmt;
1550      }
1551  
1552      /**
1553       * NPV
1554       *
1555       * Returns the Net Present Value of a cash flow series given a discount rate.
1556       *
1557       * @return    float
1558       */
1559      public static function NPV()
1560      {
1561          // Return value
1562          $returnValue = 0;
1563  
1564          // Loop through arguments
1565          $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
1566  
1567          // Calculate
1568          $rate = array_shift($aArgs);
1569          for ($i = 1; $i <= count($aArgs); ++$i) {
1570              // Is it a numeric value?
1571              if (is_numeric($aArgs[$i - 1])) {
1572                  $returnValue += $aArgs[$i - 1] / pow(1 + $rate, $i);
1573              }
1574          }
1575  
1576          // Return
1577          return $returnValue;
1578      }
1579  
1580      /**
1581       * PMT
1582       *
1583       * Returns the constant payment (annuity) for a cash flow with a constant interest rate.
1584       *
1585       * @param    float    $rate    Interest rate per period
1586       * @param    int        $nper    Number of periods
1587       * @param    float    $pv        Present Value
1588       * @param    float    $fv        Future Value
1589       * @param    int        $type    Payment type: 0 = at the end of each period, 1 = at the beginning of each period
1590       * @return    float
1591       */
1592      public static function PMT($rate = 0, $nper = 0, $pv = 0, $fv = 0, $type = 0)
1593      {
1594          $rate    = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
1595          $nper    = PHPExcel_Calculation_Functions::flattenSingleValue($nper);
1596          $pv        = PHPExcel_Calculation_Functions::flattenSingleValue($pv);
1597          $fv        = PHPExcel_Calculation_Functions::flattenSingleValue($fv);
1598          $type    = PHPExcel_Calculation_Functions::flattenSingleValue($type);
1599  
1600          // Validate parameters
1601          if ($type != 0 && $type != 1) {
1602              return PHPExcel_Calculation_Functions::NaN();
1603          }
1604  
1605          // Calculate
1606          if (!is_null($rate) && $rate != 0) {
1607              return (-$fv - $pv * pow(1 + $rate, $nper)) / (1 + $rate * $type) / ((pow(1 + $rate, $nper) - 1) / $rate);
1608          }
1609          return (-$pv - $fv) / $nper;
1610      }
1611  
1612  
1613      /**
1614       * PPMT
1615       *
1616       * Returns the interest payment for a given period for an investment based on periodic, constant payments and a constant interest rate.
1617       *
1618       * @param    float    $rate    Interest rate per period
1619       * @param    int        $per    Period for which we want to find the interest
1620       * @param    int        $nper    Number of periods
1621       * @param    float    $pv        Present Value
1622       * @param    float    $fv        Future Value
1623       * @param    int        $type    Payment type: 0 = at the end of each period, 1 = at the beginning of each period
1624       * @return    float
1625       */
1626      public static function PPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0)
1627      {
1628          $rate    = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
1629          $per    = (int) PHPExcel_Calculation_Functions::flattenSingleValue($per);
1630          $nper    = (int) PHPExcel_Calculation_Functions::flattenSingleValue($nper);
1631          $pv        = PHPExcel_Calculation_Functions::flattenSingleValue($pv);
1632          $fv        = PHPExcel_Calculation_Functions::flattenSingleValue($fv);
1633          $type    = (int) PHPExcel_Calculation_Functions::flattenSingleValue($type);
1634  
1635          // Validate parameters
1636          if ($type != 0 && $type != 1) {
1637              return PHPExcel_Calculation_Functions::NaN();
1638          }
1639          if ($per <= 0 || $per > $nper) {
1640              return PHPExcel_Calculation_Functions::VALUE();
1641          }
1642  
1643          // Calculate
1644          $interestAndPrincipal = self::interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
1645          return $interestAndPrincipal[1];
1646      }
1647  
1648  
1649      public static function PRICE($settlement, $maturity, $rate, $yield, $redemption, $frequency, $basis = 0)
1650      {
1651          $settlement    = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
1652          $maturity    = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
1653          $rate        = (float) PHPExcel_Calculation_Functions::flattenSingleValue($rate);
1654          $yield        = (float) PHPExcel_Calculation_Functions::flattenSingleValue($yield);
1655          $redemption    = (float) PHPExcel_Calculation_Functions::flattenSingleValue($redemption);
1656          $frequency    = (int) PHPExcel_Calculation_Functions::flattenSingleValue($frequency);
1657          $basis        = (is_null($basis))    ? 0 :    (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
1658  
1659          if (is_string($settlement = PHPExcel_Calculation_DateTime::getDateValue($settlement))) {
1660              return PHPExcel_Calculation_Functions::VALUE();
1661          }
1662          if (is_string($maturity = PHPExcel_Calculation_DateTime::getDateValue($maturity))) {
1663              return PHPExcel_Calculation_Functions::VALUE();
1664          }
1665  
1666          if (($settlement > $maturity) ||
1667              (!self::isValidFrequency($frequency)) ||
1668              (($basis < 0) || ($basis > 4))) {
1669              return PHPExcel_Calculation_Functions::NaN();
1670          }
1671  
1672          $dsc = self::COUPDAYSNC($settlement, $maturity, $frequency, $basis);
1673          $e = self::COUPDAYS($settlement, $maturity, $frequency, $basis);
1674          $n = self::COUPNUM($settlement, $maturity, $frequency, $basis);
1675          $a = self::COUPDAYBS($settlement, $maturity, $frequency, $basis);
1676  
1677          $baseYF    = 1.0 + ($yield / $frequency);
1678          $rfp    = 100 * ($rate / $frequency);
1679          $de    = $dsc / $e;
1680  
1681          $result = $redemption / pow($baseYF, (--$n + $de));
1682          for ($k = 0; $k <= $n; ++$k) {
1683              $result += $rfp / (pow($baseYF, ($k + $de)));
1684          }
1685          $result -= $rfp * ($a / $e);
1686  
1687          return $result;
1688      }
1689  
1690  
1691      /**
1692       * PRICEDISC
1693       *
1694       * Returns the price per $100 face value of a discounted security.
1695       *
1696       * @param    mixed    settlement    The security's settlement date.
1697       *                                The security settlement date is the date after the issue date when the security is traded to the buyer.
1698       * @param    mixed    maturity    The security's maturity date.
1699       *                                The maturity date is the date when the security expires.
1700       * @param    int        discount    The security's discount rate.
1701       * @param    int        redemption    The security's redemption value per $100 face value.
1702       * @param    int        basis        The type of day count to use.
1703       *                                        0 or omitted    US (NASD) 30/360
1704       *                                        1                Actual/actual
1705       *                                        2                Actual/360
1706       *                                        3                Actual/365
1707       *                                        4                European 30/360
1708       * @return    float
1709       */
1710      public static function PRICEDISC($settlement, $maturity, $discount, $redemption, $basis = 0)
1711      {
1712          $settlement    = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
1713          $maturity    = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
1714          $discount    = (float) PHPExcel_Calculation_Functions::flattenSingleValue($discount);
1715          $redemption    = (float) PHPExcel_Calculation_Functions::flattenSingleValue($redemption);
1716          $basis        = (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
1717  
1718          //    Validate
1719          if ((is_numeric($discount)) && (is_numeric($redemption)) && (is_numeric($basis))) {
1720              if (($discount <= 0) || ($redemption <= 0)) {
1721                  return PHPExcel_Calculation_Functions::NaN();
1722              }
1723              $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity, $basis);
1724              if (!is_numeric($daysBetweenSettlementAndMaturity)) {
1725                  //    return date error
1726                  return $daysBetweenSettlementAndMaturity;
1727              }
1728  
1729              return $redemption * (1 - $discount * $daysBetweenSettlementAndMaturity);
1730          }
1731          return PHPExcel_Calculation_Functions::VALUE();
1732      }
1733  
1734  
1735      /**
1736       * PRICEMAT
1737       *
1738       * Returns the price per $100 face value of a security that pays interest at maturity.
1739       *
1740       * @param    mixed    settlement    The security's settlement date.
1741       *                                The security's settlement date is the date after the issue date when the security is traded to the buyer.
1742       * @param    mixed    maturity    The security's maturity date.
1743       *                                The maturity date is the date when the security expires.
1744       * @param    mixed    issue        The security's issue date.
1745       * @param    int        rate        The security's interest rate at date of issue.
1746       * @param    int        yield        The security's annual yield.
1747       * @param    int        basis        The type of day count to use.
1748       *                                        0 or omitted    US (NASD) 30/360
1749       *                                        1                Actual/actual
1750       *                                        2                Actual/360
1751       *                                        3                Actual/365
1752       *                                        4                European 30/360
1753       * @return    float
1754       */
1755      public static function PRICEMAT($settlement, $maturity, $issue, $rate, $yield, $basis = 0)
1756      {
1757          $settlement    = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
1758          $maturity    = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
1759          $issue        = PHPExcel_Calculation_Functions::flattenSingleValue($issue);
1760          $rate        = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
1761          $yield        = PHPExcel_Calculation_Functions::flattenSingleValue($yield);
1762          $basis        = (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
1763  
1764          //    Validate
1765          if (is_numeric($rate) && is_numeric($yield)) {
1766              if (($rate <= 0) || ($yield <= 0)) {
1767                  return PHPExcel_Calculation_Functions::NaN();
1768              }
1769              $daysPerYear = self::daysPerYear(PHPExcel_Calculation_DateTime::YEAR($settlement), $basis);
1770              if (!is_numeric($daysPerYear)) {
1771                  return $daysPerYear;
1772              }
1773              $daysBetweenIssueAndSettlement = PHPExcel_Calculation_DateTime::YEARFRAC($issue, $settlement, $basis);
1774              if (!is_numeric($daysBetweenIssueAndSettlement)) {
1775                  //    return date error
1776                  return $daysBetweenIssueAndSettlement;
1777              }
1778              $daysBetweenIssueAndSettlement *= $daysPerYear;
1779              $daysBetweenIssueAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($issue, $maturity, $basis);
1780              if (!is_numeric($daysBetweenIssueAndMaturity)) {
1781                  //    return date error
1782                  return $daysBetweenIssueAndMaturity;
1783              }
1784              $daysBetweenIssueAndMaturity *= $daysPerYear;
1785              $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity, $basis);
1786              if (!is_numeric($daysBetweenSettlementAndMaturity)) {
1787                  //    return date error
1788                  return $daysBetweenSettlementAndMaturity;
1789              }
1790              $daysBetweenSettlementAndMaturity *= $daysPerYear;
1791  
1792              return ((100 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate * 100)) /
1793                     (1 + (($daysBetweenSettlementAndMaturity / $daysPerYear) * $yield)) -
1794                     (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate * 100));
1795          }
1796          return PHPExcel_Calculation_Functions::VALUE();
1797      }
1798  
1799  
1800      /**
1801       * PV
1802       *
1803       * Returns the Present Value of a cash flow with constant payments and interest rate (annuities).
1804       *
1805       * @param    float    $rate    Interest rate per period
1806       * @param    int        $nper    Number of periods
1807       * @param    float    $pmt    Periodic payment (annuity)
1808       * @param    float    $fv        Future Value
1809       * @param    int        $type    Payment type: 0 = at the end of each period, 1 = at the beginning of each period
1810       * @return    float
1811       */
1812      public static function PV($rate = 0, $nper = 0, $pmt = 0, $fv = 0, $type = 0)
1813      {
1814          $rate    = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
1815          $nper    = PHPExcel_Calculation_Functions::flattenSingleValue($nper);
1816          $pmt    = PHPExcel_Calculation_Functions::flattenSingleValue($pmt);
1817          $fv        = PHPExcel_Calculation_Functions::flattenSingleValue($fv);
1818          $type    = PHPExcel_Calculation_Functions::flattenSingleValue($type);
1819  
1820          // Validate parameters
1821          if ($type != 0 && $type != 1) {
1822              return PHPExcel_Calculation_Functions::NaN();
1823          }
1824  
1825          // Calculate
1826          if (!is_null($rate) && $rate != 0) {
1827              return (-$pmt * (1 + $rate * $type) * ((pow(1 + $rate, $nper) - 1) / $rate) - $fv) / pow(1 + $rate, $nper);
1828          }
1829          return -$fv - $pmt * $nper;
1830      }
1831  
1832  
1833      /**
1834       * RATE
1835       *
1836       * Returns the interest rate per period of an annuity.
1837       * RATE is calculated by iteration and can have zero or more solutions.
1838       * If the successive results of RATE do not converge to within 0.0000001 after 20 iterations,
1839       * RATE returns the #NUM! error value.
1840       *
1841       * Excel Function:
1842       *        RATE(nper,pmt,pv[,fv[,type[,guess]]])
1843       *
1844       * @access    public
1845       * @category Financial Functions
1846       * @param    float    nper        The total number of payment periods in an annuity.
1847       * @param    float    pmt            The payment made each period and cannot change over the life
1848       *                                    of the annuity.
1849       *                                Typically, pmt includes principal and interest but no other
1850       *                                    fees or taxes.
1851       * @param    float    pv            The present value - the total amount that a series of future
1852       *                                    payments is worth now.
1853       * @param    float    fv            The future value, or a cash balance you want to attain after
1854       *                                    the last payment is made. If fv is omitted, it is assumed
1855       *                                    to be 0 (the future value of a loan, for example, is 0).
1856       * @param    integer    type        A number 0 or 1 and indicates when payments are due:
1857       *                                        0 or omitted    At the end of the period.
1858       *                                        1                At the beginning of the period.
1859       * @param    float    guess        Your guess for what the rate will be.
1860       *                                    If you omit guess, it is assumed to be 10 percent.
1861       * @return    float
1862       **/
1863      public static function RATE($nper, $pmt, $pv, $fv = 0.0, $type = 0, $guess = 0.1)
1864      {
1865          $nper    = (int) PHPExcel_Calculation_Functions::flattenSingleValue($nper);
1866          $pmt    = PHPExcel_Calculation_Functions::flattenSingleValue($pmt);
1867          $pv        = PHPExcel_Calculation_Functions::flattenSingleValue($pv);
1868          $fv        = (is_null($fv))    ? 0.0    :    PHPExcel_Calculation_Functions::flattenSingleValue($fv);
1869          $type    = (is_null($type))    ? 0        :    (int) PHPExcel_Calculation_Functions::flattenSingleValue($type);
1870          $guess    = (is_null($guess))    ? 0.1    :    PHPExcel_Calculation_Functions::flattenSingleValue($guess);
1871  
1872          $rate = $guess;
1873          if (abs($rate) < FINANCIAL_PRECISION) {
1874              $y = $pv * (1 + $nper * $rate) + $pmt * (1 + $rate * $type) * $nper + $fv;
1875          } else {
1876              $f = exp($nper * log(1 + $rate));
1877              $y = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
1878          }
1879          $y0 = $pv + $pmt * $nper + $fv;
1880          $y1 = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
1881  
1882          // find root by secant method
1883          $i  = $x0 = 0.0;
1884          $x1 = $rate;
1885          while ((abs($y0 - $y1) > FINANCIAL_PRECISION) && ($i < FINANCIAL_MAX_ITERATIONS)) {
1886              $rate = ($y1 * $x0 - $y0 * $x1) / ($y1 - $y0);
1887              $x0 = $x1;
1888              $x1 = $rate;
1889              if (($nper * abs($pmt)) > ($pv - $fv)) {
1890                  $x1 = abs($x1);
1891              }
1892              if (abs($rate) < FINANCIAL_PRECISION) {
1893                  $y = $pv * (1 + $nper * $rate) + $pmt * (1 + $rate * $type) * $nper + $fv;
1894              } else {
1895                  $f = exp($nper * log(1 + $rate));
1896                  $y = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
1897              }
1898  
1899              $y0 = $y1;
1900              $y1 = $y;
1901              ++$i;
1902          }
1903          return $rate;
1904      }
1905  
1906  
1907      /**
1908       * RECEIVED
1909       *
1910       * Returns the price per $100 face value of a discounted security.
1911       *
1912       * @param    mixed    settlement    The security's settlement date.
1913       *                                The security settlement date is the date after the issue date when the security is traded to the buyer.
1914       * @param    mixed    maturity    The security's maturity date.
1915       *                                The maturity date is the date when the security expires.
1916       * @param    int        investment    The amount invested in the security.
1917       * @param    int        discount    The security's discount rate.
1918       * @param    int        basis        The type of day count to use.
1919       *                                        0 or omitted    US (NASD) 30/360
1920       *                                        1                Actual/actual
1921       *                                        2                Actual/360
1922       *                                        3                Actual/365
1923       *                                        4                European 30/360
1924       * @return    float
1925       */
1926      public static function RECEIVED($settlement, $maturity, $investment, $discount, $basis = 0)
1927      {
1928          $settlement    = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
1929          $maturity    = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
1930          $investment    = (float) PHPExcel_Calculation_Functions::flattenSingleValue($investment);
1931          $discount    = (float) PHPExcel_Calculation_Functions::flattenSingleValue($discount);
1932          $basis        = (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
1933  
1934          //    Validate
1935          if ((is_numeric($investment)) && (is_numeric($discount)) && (is_numeric($basis))) {
1936              if (($investment <= 0) || ($discount <= 0)) {
1937                  return PHPExcel_Calculation_Functions::NaN();
1938              }
1939              $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity, $basis);
1940              if (!is_numeric($daysBetweenSettlementAndMaturity)) {
1941                  //    return date error
1942                  return $daysBetweenSettlementAndMaturity;
1943              }
1944  
1945              return $investment / ( 1 - ($discount * $daysBetweenSettlementAndMaturity));
1946          }
1947          return PHPExcel_Calculation_Functions::VALUE();
1948      }
1949  
1950  
1951      /**
1952       * SLN
1953       *
1954       * Returns the straight-line depreciation of an asset for one period
1955       *
1956       * @param    cost        Initial cost of the asset
1957       * @param    salvage        Value at the end of the depreciation
1958       * @param    life        Number of periods over which the asset is depreciated
1959       * @return    float
1960       */
1961      public static function SLN($cost, $salvage, $life)
1962      {
1963          $cost        = PHPExcel_Calculation_Functions::flattenSingleValue($cost);
1964          $salvage    = PHPExcel_Calculation_Functions::flattenSingleValue($salvage);
1965          $life        = PHPExcel_Calculation_Functions::flattenSingleValue($life);
1966  
1967          // Calculate
1968          if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life))) {
1969              if ($life < 0) {
1970                  return PHPExcel_Calculation_Functions::NaN();
1971              }
1972              return ($cost - $salvage) / $life;
1973          }
1974          return PHPExcel_Calculation_Functions::VALUE();
1975      }
1976  
1977  
1978      /**
1979       * SYD
1980       *
1981       * Returns the sum-of-years' digits depreciation of an asset for a specified period.
1982       *
1983       * @param    cost        Initial cost of the asset
1984       * @param    salvage        Value at the end of the depreciation
1985       * @param    life        Number of periods over which the asset is depreciated
1986       * @param    period        Period
1987       * @return    float
1988       */
1989      public static function SYD($cost, $salvage, $life, $period)
1990      {
1991          $cost        = PHPExcel_Calculation_Functions::flattenSingleValue($cost);
1992          $salvage    = PHPExcel_Calculation_Functions::flattenSingleValue($salvage);
1993          $life        = PHPExcel_Calculation_Functions::flattenSingleValue($life);
1994          $period        = PHPExcel_Calculation_Functions::flattenSingleValue($period);
1995  
1996          // Calculate
1997          if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period))) {
1998              if (($life < 1) || ($period > $life)) {
1999                  return PHPExcel_Calculation_Functions::NaN();
2000              }
2001              return (($cost - $salvage) * ($life - $period + 1) * 2) / ($life * ($life + 1));
2002          }
2003          return PHPExcel_Calculation_Functions::VALUE();
2004      }
2005  
2006  
2007      /**
2008       * TBILLEQ
2009       *
2010       * Returns the bond-equivalent yield for a Treasury bill.
2011       *
2012       * @param    mixed    settlement    The Treasury bill's settlement date.
2013       *                                The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
2014       * @param    mixed    maturity    The Treasury bill's maturity date.
2015       *                                The maturity date is the date when the Treasury bill expires.
2016       * @param    int        discount    The Treasury bill's discount rate.
2017       * @return    float
2018       */
2019      public static function TBILLEQ($settlement, $maturity, $discount)
2020      {
2021          $settlement    = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
2022          $maturity    = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
2023          $discount    = PHPExcel_Calculation_Functions::flattenSingleValue($discount);
2024  
2025          //    Use TBILLPRICE for validation
2026          $testValue = self::TBILLPRICE($settlement, $maturity, $discount);
2027          if (is_string($testValue)) {
2028              return $testValue;
2029          }
2030  
2031          if (is_string($maturity = PHPExcel_Calculation_DateTime::getDateValue($maturity))) {
2032              return PHPExcel_Calculation_Functions::VALUE();
2033          }
2034  
2035          if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
2036              ++$maturity;
2037              $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity) * 360;
2038          } else {
2039              $daysBetweenSettlementAndMaturity = (PHPExcel_Calculation_DateTime::getDateValue($maturity) - PHPExcel_Calculation_DateTime::getDateValue($settlement));
2040          }
2041  
2042          return (365 * $discount) / (360 - $discount * $daysBetweenSettlementAndMaturity);
2043      }
2044  
2045  
2046      /**
2047       * TBILLPRICE
2048       *
2049       * Returns the yield for a Treasury bill.
2050       *
2051       * @param    mixed    settlement    The Treasury bill's settlement date.
2052       *                                The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
2053       * @param    mixed    maturity    The Treasury bill's maturity date.
2054       *                                The maturity date is the date when the Treasury bill expires.
2055       * @param    int        discount    The Treasury bill's discount rate.
2056       * @return    float
2057       */
2058      public static function TBILLPRICE($settlement, $maturity, $discount)
2059      {
2060          $settlement    = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
2061          $maturity    = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
2062          $discount    = PHPExcel_Calculation_Functions::flattenSingleValue($discount);
2063  
2064          if (is_string($maturity = PHPExcel_Calculation_DateTime::getDateValue($maturity))) {
2065              return PHPExcel_Calculation_Functions::VALUE();
2066          }
2067  
2068          //    Validate
2069          if (is_numeric($discount)) {
2070              if ($discount <= 0) {
2071                  return PHPExcel_Calculation_Functions::NaN();
2072              }
2073  
2074              if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
2075                  ++$maturity;
2076                  $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity) * 360;
2077                  if (!is_numeric($daysBetweenSettlementAndMaturity)) {
2078                      //    return date error
2079                      return $daysBetweenSettlementAndMaturity;
2080                  }
2081              } else {
2082                  $daysBetweenSettlementAndMaturity = (PHPExcel_Calculation_DateTime::getDateValue($maturity) - PHPExcel_Calculation_DateTime::getDateValue($settlement));
2083              }
2084  
2085              if ($daysBetweenSettlementAndMaturity > 360) {
2086                  return PHPExcel_Calculation_Functions::NaN();
2087              }
2088  
2089              $price = 100 * (1 - (($discount * $daysBetweenSettlementAndMaturity) / 360));
2090              if ($price <= 0) {
2091                  return PHPExcel_Calculation_Functions::NaN();
2092              }
2093              return $price;
2094          }
2095          return PHPExcel_Calculation_Functions::VALUE();
2096      }
2097  
2098  
2099      /**
2100       * TBILLYIELD
2101       *
2102       * Returns the yield for a Treasury bill.
2103       *
2104       * @param    mixed    settlement    The Treasury bill's settlement date.
2105       *                                The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
2106       * @param    mixed    maturity    The Treasury bill's maturity date.
2107       *                                The maturity date is the date when the Treasury bill expires.
2108       * @param    int        price        The Treasury bill's price per $100 face value.
2109       * @return    float
2110       */
2111      public static function TBILLYIELD($settlement, $maturity, $price)
2112      {
2113          $settlement    = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
2114          $maturity    = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
2115          $price        = PHPExcel_Calculation_Functions::flattenSingleValue($price);
2116  
2117          //    Validate
2118          if (is_numeric($price)) {
2119              if ($price <= 0) {
2120                  return PHPExcel_Calculation_Functions::NaN();
2121              }
2122  
2123              if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
2124                  ++$maturity;
2125                  $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity) * 360;
2126                  if (!is_numeric($daysBetweenSettlementAndMaturity)) {
2127                      //    return date error
2128                      return $daysBetweenSettlementAndMaturity;
2129                  }
2130              } else {
2131                  $daysBetweenSettlementAndMaturity = (PHPExcel_Calculation_DateTime::getDateValue($maturity) - PHPExcel_Calculation_DateTime::getDateValue($settlement));
2132              }
2133  
2134              if ($daysBetweenSettlementAndMaturity > 360) {
2135                  return PHPExcel_Calculation_Functions::NaN();
2136              }
2137  
2138              return ((100 - $price) / $price) * (360 / $daysBetweenSettlementAndMaturity);
2139          }
2140          return PHPExcel_Calculation_Functions::VALUE();
2141      }
2142  
2143  
2144      public static function XIRR($values, $dates, $guess = 0.1)
2145      {
2146          if ((!is_array($values)) && (!is_array($dates))) {
2147              return PHPExcel_Calculation_Functions::VALUE();
2148          }
2149          $values    = PHPExcel_Calculation_Functions::flattenArray($values);
2150          $dates    = PHPExcel_Calculation_Functions::flattenArray($dates);
2151          $guess = PHPExcel_Calculation_Functions::flattenSingleValue($guess);
2152          if (count($values) != count($dates)) {
2153              return PHPExcel_Calculation_Functions::NaN();
2154          }
2155  
2156          // create an initial range, with a root somewhere between 0 and guess
2157          $x1 = 0.0;
2158          $x2 = $guess;
2159          $f1 = self::XNPV($x1, $values, $dates);
2160          $f2 = self::XNPV($x2, $values, $dates);
2161          for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; ++$i) {
2162              if (($f1 * $f2) < 0.0) {
2163                  break;
2164              } elseif (abs($f1) < abs($f2)) {
2165                  $f1 = self::XNPV($x1 += 1.6 * ($x1 - $x2), $values, $dates);
2166              } else {
2167                  $f2 = self::XNPV($x2 += 1.6 * ($x2 - $x1), $values, $dates);
2168              }
2169          }
2170          if (($f1 * $f2) > 0.0) {
2171              return PHPExcel_Calculation_Functions::VALUE();
2172          }
2173  
2174          $f = self::XNPV($x1, $values, $dates);
2175          if ($f < 0.0) {
2176              $rtb = $x1;
2177              $dx = $x2 - $x1;
2178          } else {
2179              $rtb = $x2;
2180              $dx = $x1 - $x2;
2181          }
2182  
2183          for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; ++$i) {
2184              $dx *= 0.5;
2185              $x_mid = $rtb + $dx;
2186              $f_mid = self::XNPV($x_mid, $values, $dates);
2187              if ($f_mid <= 0.0) {
2188                  $rtb = $x_mid;
2189              }
2190              if ((abs($f_mid) < FINANCIAL_PRECISION) || (abs($dx) < FINANCIAL_PRECISION)) {
2191                  return $x_mid;
2192              }
2193          }
2194          return PHPExcel_Calculation_Functions::VALUE();
2195      }
2196  
2197  
2198      /**
2199       * XNPV
2200       *
2201       * Returns the net present value for a schedule of cash flows that is not necessarily periodic.
2202       * To calculate the net present value for a series of cash flows that is periodic, use the NPV function.
2203       *
2204       * Excel Function:
2205       *        =XNPV(rate,values,dates)
2206       *
2207       * @param    float            $rate        The discount rate to apply to the cash flows.
2208       * @param    array of float    $values     A series of cash flows that corresponds to a schedule of payments in dates.
2209       *                                         The first payment is optional and corresponds to a cost or payment that occurs at the beginning of the investment.
2210       *                                         If the first value is a cost or payment, it must be a negative value. All succeeding payments are discounted based on a 365-day year.
2211       *                                         The series of values must contain at least one positive value and one negative value.
2212       * @param    array of mixed    $dates      A schedule of payment dates that corresponds to the cash flow payments.
2213       *                                         The first payment date indicates the beginning of the schedule of payments.
2214       *                                         All other dates must be later than this date, but they may occur in any order.
2215       * @return    float
2216       */
2217      public static function XNPV($rate, $values, $dates)
2218      {
2219          $rate = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
2220          if (!is_numeric($rate)) {
2221              return PHPExcel_Calculation_Functions::VALUE();
2222          }
2223          if ((!is_array($values)) || (!is_array($dates))) {
2224              return PHPExcel_Calculation_Functions::VALUE();
2225          }
2226          $values    = PHPExcel_Calculation_Functions::flattenArray($values);
2227          $dates    = PHPExcel_Calculation_Functions::flattenArray($dates);
2228          $valCount = count($values);
2229          if ($valCount != count($dates)) {
2230              return PHPExcel_Calculation_Functions::NaN();
2231          }
2232          if ((min($values) > 0) || (max($values) < 0)) {
2233              return PHPExcel_Calculation_Functions::VALUE();
2234          }
2235  
2236          $xnpv = 0.0;
2237          for ($i = 0; $i < $valCount; ++$i) {
2238              if (!is_numeric($values[$i])) {
2239                  return PHPExcel_Calculation_Functions::VALUE();
2240              }
2241              $xnpv += $values[$i] / pow(1 + $rate, PHPExcel_Calculation_DateTime::DATEDIF($dates[0], $dates[$i], 'd') / 365);
2242          }
2243          return (is_finite($xnpv)) ? $xnpv : PHPExcel_Calculation_Functions::VALUE();
2244      }
2245  
2246  
2247      /**
2248       * YIELDDISC
2249       *
2250       * Returns the annual yield of a security that pays interest at maturity.
2251       *
2252       * @param    mixed    settlement      The security's settlement date.
2253       *                                    The security's settlement date is the date after the issue date when the security is traded to the buyer.
2254       * @param    mixed    maturity        The security's maturity date.
2255       *                                    The maturity date is the date when the security expires.
2256       * @param    int        price         The security's price per $100 face value.
2257       * @param    int        redemption    The security's redemption value per $100 face value.
2258       * @param    int        basis         The type of day count to use.
2259       *                                        0 or omitted    US (NASD) 30/360
2260       *                                        1                Actual/actual
2261       *                                        2                Actual/360
2262       *                                        3                Actual/365
2263       *                                        4                European 30/360
2264       * @return    float
2265       */
2266      public static function YIELDDISC($settlement, $maturity, $price, $redemption, $basis = 0)
2267      {
2268          $settlement    = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
2269          $maturity    = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
2270          $price        = PHPExcel_Calculation_Functions::flattenSingleValue($price);
2271          $redemption    = PHPExcel_Calculation_Functions::flattenSingleValue($redemption);
2272          $basis        = (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
2273  
2274          //    Validate
2275          if (is_numeric($price) && is_numeric($redemption)) {
2276              if (($price <= 0) || ($redemption <= 0)) {
2277                  return PHPExcel_Calculation_Functions::NaN();
2278              }
2279              $daysPerYear = self::daysPerYear(PHPExcel_Calculation_DateTime::YEAR($settlement), $basis);
2280              if (!is_numeric($daysPerYear)) {
2281                  return $daysPerYear;
2282              }
2283              $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity, $basis);
2284              if (!is_numeric($daysBetweenSettlementAndMaturity)) {
2285                  //    return date error
2286                  return $daysBetweenSettlementAndMaturity;
2287              }
2288              $daysBetweenSettlementAndMaturity *= $daysPerYear;
2289  
2290              return (($redemption - $price) / $price) * ($daysPerYear / $daysBetweenSettlementAndMaturity);
2291          }
2292          return PHPExcel_Calculation_Functions::VALUE();
2293      }
2294  
2295  
2296      /**
2297       * YIELDMAT
2298       *
2299       * Returns the annual yield of a security that pays interest at maturity.
2300       *
2301       * @param    mixed    settlement     The security's settlement date.
2302       *                                   The security's settlement date is the date after the issue date when the security is traded to the buyer.
2303       * @param    mixed    maturity       The security's maturity date.
2304       *                                   The maturity date is the date when the security expires.
2305       * @param    mixed    issue          The security's issue date.
2306       * @param    int        rate         The security's interest rate at date of issue.
2307       * @param    int        price        The security's price per $100 face value.
2308       * @param    int        basis        The type of day count to use.
2309       *                                        0 or omitted    US (NASD) 30/360
2310       *                                        1                Actual/actual
2311       *                                        2                Actual/360
2312       *                                        3                Actual/365
2313       *                                        4                European 30/360
2314       * @return    float
2315       */
2316      public static function YIELDMAT($settlement, $maturity, $issue, $rate, $price, $basis = 0)
2317      {
2318          $settlement    = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
2319          $maturity    = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
2320          $issue        = PHPExcel_Calculation_Functions::flattenSingleValue($issue);
2321          $rate        = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
2322          $price        = PHPExcel_Calculation_Functions::flattenSingleValue($price);
2323          $basis        = (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
2324  
2325          //    Validate
2326          if (is_numeric($rate) && is_numeric($price)) {
2327              if (($rate <= 0) || ($price <= 0)) {
2328                  return PHPExcel_Calculation_Functions::NaN();
2329              }
2330              $daysPerYear = self::daysPerYear(PHPExcel_Calculation_DateTime::YEAR($settlement), $basis);
2331              if (!is_numeric($daysPerYear)) {
2332                  return $daysPerYear;
2333              }
2334              $daysBetweenIssueAndSettlement = PHPExcel_Calculation_DateTime::YEARFRAC($issue, $settlement, $basis);
2335              if (!is_numeric($daysBetweenIssueAndSettlement)) {
2336                  //    return date error
2337                  return $daysBetweenIssueAndSettlement;
2338              }
2339              $daysBetweenIssueAndSettlement *= $daysPerYear;
2340              $daysBetweenIssueAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($issue, $maturity, $basis);
2341              if (!is_numeric($daysBetweenIssueAndMaturity)) {
2342                  //    return date error
2343                  return $daysBetweenIssueAndMaturity;
2344              }
2345              $daysBetweenIssueAndMaturity *= $daysPerYear;
2346              $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity, $basis);
2347              if (!is_numeric($daysBetweenSettlementAndMaturity)) {
2348                  //    return date error
2349                  return $daysBetweenSettlementAndMaturity;
2350              }
2351              $daysBetweenSettlementAndMaturity *= $daysPerYear;
2352  
2353              return ((1 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate) - (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) /
2354                     (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) *
2355                     ($daysPerYear / $daysBetweenSettlementAndMaturity);
2356          }
2357          return PHPExcel_Calculation_Functions::VALUE();
2358      }
2359  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1