Financial functions next stage of refactoring (#1943)
* First steps splitting out the Amortization and Deprecation Excel functions from Financials * Verify which methods allow negative values for arguments * Additional unit tests for SLN() and SYD() * Additional unit tests for DDB() * Additional unit tests for DB() * Verify Amortization cases where salvage is greater than cost * More unit tests for Amortization * Resolve broken test in AMORLINC() and extract amortizationCoefficient calculation * verify amortizationCoefficient calculation * Extract YIELDDISC() and YIELDMAT() to Financial\Securities * Additional validation for Securities Yield functions
This commit is contained in:
parent
9beacd21be
commit
5ad5f787ab
|
|
@ -273,12 +273,12 @@ class Calculation
|
|||
],
|
||||
'AMORDEGRC' => [
|
||||
'category' => Category::CATEGORY_FINANCIAL,
|
||||
'functionCall' => [Financial::class, 'AMORDEGRC'],
|
||||
'functionCall' => [Financial\Amortization::class, 'AMORDEGRC'],
|
||||
'argumentCount' => '6,7',
|
||||
],
|
||||
'AMORLINC' => [
|
||||
'category' => Category::CATEGORY_FINANCIAL,
|
||||
'functionCall' => [Financial::class, 'AMORLINC'],
|
||||
'functionCall' => [Financial\Amortization::class, 'AMORLINC'],
|
||||
'argumentCount' => '6,7',
|
||||
],
|
||||
'AND' => [
|
||||
|
|
@ -1983,17 +1983,17 @@ class Calculation
|
|||
],
|
||||
'PRICE' => [
|
||||
'category' => Category::CATEGORY_FINANCIAL,
|
||||
'functionCall' => [Financial\Securities::class, 'price'],
|
||||
'functionCall' => [Financial\Securities\Price::class, 'price'],
|
||||
'argumentCount' => '6,7',
|
||||
],
|
||||
'PRICEDISC' => [
|
||||
'category' => Category::CATEGORY_FINANCIAL,
|
||||
'functionCall' => [Financial\Securities::class, 'discounted'],
|
||||
'functionCall' => [Financial\Securities\Price::class, 'priceDiscounted'],
|
||||
'argumentCount' => '4,5',
|
||||
],
|
||||
'PRICEMAT' => [
|
||||
'category' => Category::CATEGORY_FINANCIAL,
|
||||
'functionCall' => [Financial\Securities::class, 'maturity'],
|
||||
'functionCall' => [Financial\Securities\Price::class, 'priceAtMaturity'],
|
||||
'argumentCount' => '5,6',
|
||||
],
|
||||
'PROB' => [
|
||||
|
|
@ -2225,7 +2225,7 @@ class Calculation
|
|||
],
|
||||
'SLN' => [
|
||||
'category' => Category::CATEGORY_FINANCIAL,
|
||||
'functionCall' => [Financial::class, 'SLN'],
|
||||
'functionCall' => [Financial\Depreciation::class, 'SLN'],
|
||||
'argumentCount' => '3',
|
||||
],
|
||||
'SLOPE' => [
|
||||
|
|
@ -2356,7 +2356,7 @@ class Calculation
|
|||
],
|
||||
'SYD' => [
|
||||
'category' => Category::CATEGORY_FINANCIAL,
|
||||
'functionCall' => [Financial::class, 'SYD'],
|
||||
'functionCall' => [Financial\Depreciation::class, 'SYD'],
|
||||
'argumentCount' => '4',
|
||||
],
|
||||
'T' => [
|
||||
|
|
@ -2641,12 +2641,12 @@ class Calculation
|
|||
],
|
||||
'YIELDDISC' => [
|
||||
'category' => Category::CATEGORY_FINANCIAL,
|
||||
'functionCall' => [Financial::class, 'YIELDDISC'],
|
||||
'functionCall' => [Financial\Securities\Yields::class, 'yieldDiscounted'],
|
||||
'argumentCount' => '4,5',
|
||||
],
|
||||
'YIELDMAT' => [
|
||||
'category' => Category::CATEGORY_FINANCIAL,
|
||||
'functionCall' => [Financial::class, 'YIELDMAT'],
|
||||
'functionCall' => [Financial\Securities\Yields::class, 'yieldAtMaturity'],
|
||||
'argumentCount' => '5,6',
|
||||
],
|
||||
'ZTEST' => [
|
||||
|
|
|
|||
|
|
@ -2,8 +2,13 @@
|
|||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Amortization;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Coupons;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Depreciation;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Dollar;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Financial\InterestRate;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Securities;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Financial\TreasuryBill;
|
||||
|
||||
class Financial
|
||||
{
|
||||
|
|
@ -147,6 +152,10 @@ class Financial
|
|||
* Excel Function:
|
||||
* AMORDEGRC(cost,purchased,firstPeriod,salvage,period,rate[,basis])
|
||||
*
|
||||
* @Deprecated 1.18.0
|
||||
*
|
||||
* @see Use the AMORDEGRC() method in the Financial\Amortization class instead
|
||||
*
|
||||
* @param float $cost The cost of the asset
|
||||
* @param mixed $purchased Date of the purchase of the asset
|
||||
* @param mixed $firstPeriod Date of the end of the first period
|
||||
|
|
@ -164,57 +173,7 @@ class Financial
|
|||
*/
|
||||
public static function AMORDEGRC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis = 0)
|
||||
{
|
||||
$cost = Functions::flattenSingleValue($cost);
|
||||
$purchased = Functions::flattenSingleValue($purchased);
|
||||
$firstPeriod = Functions::flattenSingleValue($firstPeriod);
|
||||
$salvage = Functions::flattenSingleValue($salvage);
|
||||
$period = floor(Functions::flattenSingleValue($period));
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
|
||||
$yearFrac = DateTime::YEARFRAC($purchased, $firstPeriod, $basis);
|
||||
if (is_string($yearFrac)) {
|
||||
return $yearFrac;
|
||||
}
|
||||
|
||||
// The depreciation coefficients are:
|
||||
// Life of assets (1/rate) Depreciation coefficient
|
||||
// Less than 3 years 1
|
||||
// Between 3 and 4 years 1.5
|
||||
// Between 5 and 6 years 2
|
||||
// More than 6 years 2.5
|
||||
$fUsePer = 1.0 / $rate;
|
||||
if ($fUsePer < 3.0) {
|
||||
$amortiseCoeff = 1.0;
|
||||
} elseif ($fUsePer < 5.0) {
|
||||
$amortiseCoeff = 1.5;
|
||||
} elseif ($fUsePer <= 6.0) {
|
||||
$amortiseCoeff = 2.0;
|
||||
} else {
|
||||
$amortiseCoeff = 2.5;
|
||||
}
|
||||
|
||||
$rate *= $amortiseCoeff;
|
||||
$fNRate = round($yearFrac * $rate * $cost, 0);
|
||||
$cost -= $fNRate;
|
||||
$fRest = $cost - $salvage;
|
||||
|
||||
for ($n = 0; $n < $period; ++$n) {
|
||||
$fNRate = round($rate * $cost, 0);
|
||||
$fRest -= $fNRate;
|
||||
|
||||
if ($fRest < 0.0) {
|
||||
switch ($period - $n) {
|
||||
case 0:
|
||||
case 1:
|
||||
return round($cost * 0.5, 0);
|
||||
default:
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
$cost -= $fNRate;
|
||||
}
|
||||
|
||||
return $fNRate;
|
||||
return Amortization::AMORDEGRC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -227,6 +186,10 @@ class Financial
|
|||
* Excel Function:
|
||||
* AMORLINC(cost,purchased,firstPeriod,salvage,period,rate[,basis])
|
||||
*
|
||||
* @Deprecated 1.18.0
|
||||
*
|
||||
* @see Use the AMORLINC() method in the Financial\Amortization class instead
|
||||
*
|
||||
* @param float $cost The cost of the asset
|
||||
* @param mixed $purchased Date of the purchase of the asset
|
||||
* @param mixed $firstPeriod Date of the end of the first period
|
||||
|
|
@ -244,39 +207,7 @@ class Financial
|
|||
*/
|
||||
public static function AMORLINC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis = 0)
|
||||
{
|
||||
$cost = Functions::flattenSingleValue($cost);
|
||||
$purchased = Functions::flattenSingleValue($purchased);
|
||||
$firstPeriod = Functions::flattenSingleValue($firstPeriod);
|
||||
$salvage = Functions::flattenSingleValue($salvage);
|
||||
$period = Functions::flattenSingleValue($period);
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
|
||||
|
||||
$fOneRate = $cost * $rate;
|
||||
$fCostDelta = $cost - $salvage;
|
||||
// Note, quirky variation for leap years on the YEARFRAC for this function
|
||||
$purchasedYear = DateTime::YEAR($purchased);
|
||||
$yearFrac = DateTime::YEARFRAC($purchased, $firstPeriod, $basis);
|
||||
if (is_string($yearFrac)) {
|
||||
return $yearFrac;
|
||||
}
|
||||
|
||||
if (($basis == 1) && ($yearFrac < 1) && (DateTime::isLeapYear($purchasedYear))) {
|
||||
$yearFrac *= 365 / 366;
|
||||
}
|
||||
|
||||
$f0Rate = $yearFrac * $rate * $cost;
|
||||
$nNumOfFullPeriods = (int) (($cost - $salvage - $f0Rate) / $fOneRate);
|
||||
|
||||
if ($period == 0) {
|
||||
return $f0Rate;
|
||||
} elseif ($period <= $nNumOfFullPeriods) {
|
||||
return $fOneRate;
|
||||
} elseif ($period == ($nNumOfFullPeriods + 1)) {
|
||||
return $fCostDelta - $fOneRate * $nNumOfFullPeriods - $f0Rate;
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
return Amortization::AMORLINC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -613,6 +544,10 @@ class Financial
|
|||
* Excel Function:
|
||||
* DB(cost,salvage,life,period[,month])
|
||||
*
|
||||
* @Deprecated 1.18.0
|
||||
*
|
||||
* @see Use the DB() method in the Financial\Depreciation class instead
|
||||
*
|
||||
* @param float $cost Initial cost of the asset
|
||||
* @param float $salvage Value at the end of the depreciation.
|
||||
* (Sometimes called the salvage value of the asset)
|
||||
|
|
@ -627,46 +562,7 @@ class Financial
|
|||
*/
|
||||
public static function DB($cost, $salvage, $life, $period, $month = 12)
|
||||
{
|
||||
$cost = Functions::flattenSingleValue($cost);
|
||||
$salvage = Functions::flattenSingleValue($salvage);
|
||||
$life = Functions::flattenSingleValue($life);
|
||||
$period = Functions::flattenSingleValue($period);
|
||||
$month = Functions::flattenSingleValue($month);
|
||||
|
||||
// Validate
|
||||
if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period)) && (is_numeric($month))) {
|
||||
$cost = (float) $cost;
|
||||
$salvage = (float) $salvage;
|
||||
$life = (int) $life;
|
||||
$period = (int) $period;
|
||||
$month = (int) $month;
|
||||
if ($cost == 0) {
|
||||
return 0.0;
|
||||
} elseif (($cost < 0) || (($salvage / $cost) < 0) || ($life <= 0) || ($period < 1) || ($month < 1)) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
// Set Fixed Depreciation Rate
|
||||
$fixedDepreciationRate = 1 - ($salvage / $cost) ** (1 / $life);
|
||||
$fixedDepreciationRate = round($fixedDepreciationRate, 3);
|
||||
|
||||
// Loop through each period calculating the depreciation
|
||||
$previousDepreciation = 0;
|
||||
$depreciation = 0;
|
||||
for ($per = 1; $per <= $period; ++$per) {
|
||||
if ($per == 1) {
|
||||
$depreciation = $cost * $fixedDepreciationRate * $month / 12;
|
||||
} elseif ($per == ($life + 1)) {
|
||||
$depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate * (12 - $month) / 12;
|
||||
} else {
|
||||
$depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate;
|
||||
}
|
||||
$previousDepreciation += $depreciation;
|
||||
}
|
||||
|
||||
return $depreciation;
|
||||
}
|
||||
|
||||
return Functions::VALUE();
|
||||
return Depreciation::DB($cost, $salvage, $life, $period, $month);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -678,6 +574,10 @@ class Financial
|
|||
* Excel Function:
|
||||
* DDB(cost,salvage,life,period[,factor])
|
||||
*
|
||||
* @Deprecated 1.18.0
|
||||
*
|
||||
* @see Use the DDB() method in the Financial\Depreciation class instead
|
||||
*
|
||||
* @param float $cost Initial cost of the asset
|
||||
* @param float $salvage Value at the end of the depreciation.
|
||||
* (Sometimes called the salvage value of the asset)
|
||||
|
|
@ -693,38 +593,7 @@ class Financial
|
|||
*/
|
||||
public static function DDB($cost, $salvage, $life, $period, $factor = 2.0)
|
||||
{
|
||||
$cost = Functions::flattenSingleValue($cost);
|
||||
$salvage = Functions::flattenSingleValue($salvage);
|
||||
$life = Functions::flattenSingleValue($life);
|
||||
$period = Functions::flattenSingleValue($period);
|
||||
$factor = Functions::flattenSingleValue($factor);
|
||||
|
||||
// Validate
|
||||
if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period)) && (is_numeric($factor))) {
|
||||
$cost = (float) $cost;
|
||||
$salvage = (float) $salvage;
|
||||
$life = (int) $life;
|
||||
$period = (int) $period;
|
||||
$factor = (float) $factor;
|
||||
if (($cost <= 0) || (($salvage / $cost) < 0) || ($life <= 0) || ($period < 1) || ($factor <= 0.0) || ($period > $life)) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
// Set Fixed Depreciation Rate
|
||||
$fixedDepreciationRate = 1 - ($salvage / $cost) ** (1 / $life);
|
||||
$fixedDepreciationRate = round($fixedDepreciationRate, 3);
|
||||
|
||||
// Loop through each period calculating the depreciation
|
||||
$previousDepreciation = 0;
|
||||
$depreciation = 0;
|
||||
for ($per = 1; $per <= $period; ++$per) {
|
||||
$depreciation = min(($cost - $previousDepreciation) * ($factor / $life), ($cost - $salvage - $previousDepreciation));
|
||||
$previousDepreciation += $depreciation;
|
||||
}
|
||||
|
||||
return $depreciation;
|
||||
}
|
||||
|
||||
return Functions::VALUE();
|
||||
return Depreciation::DDB($cost, $salvage, $life, $period, $factor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -800,7 +669,7 @@ class Financial
|
|||
*/
|
||||
public static function DOLLARDE($fractional_dollar = null, $fraction = 0)
|
||||
{
|
||||
return Financial\Dollar::decimal($fractional_dollar, $fraction);
|
||||
return Dollar::decimal($fractional_dollar, $fraction);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -824,7 +693,7 @@ class Financial
|
|||
*/
|
||||
public static function DOLLARFR($decimal_dollar = null, $fraction = 0)
|
||||
{
|
||||
return Financial\Dollar::fractional($decimal_dollar, $fraction);
|
||||
return Dollar::fractional($decimal_dollar, $fraction);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1368,7 +1237,7 @@ class Financial
|
|||
*
|
||||
* @Deprecated 1.18.0
|
||||
*
|
||||
* @see Use the price() method in the Financial\Securities class instead
|
||||
* @see Use the price() method in the Financial\Securities\Price class instead
|
||||
*
|
||||
* @param mixed $settlement The security's settlement date.
|
||||
* The security settlement date is the date after the issue date when the security
|
||||
|
|
@ -1393,7 +1262,7 @@ class Financial
|
|||
*/
|
||||
public static function PRICE($settlement, $maturity, $rate, $yield, $redemption, $frequency, $basis = 0)
|
||||
{
|
||||
return Financial\Securities::price($settlement, $maturity, $rate, $yield, $redemption, $frequency, $basis);
|
||||
return Securities\Price::price($settlement, $maturity, $rate, $yield, $redemption, $frequency, $basis);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1403,12 +1272,13 @@ class Financial
|
|||
*
|
||||
* @Deprecated 1.18.0
|
||||
*
|
||||
* @see Use the discounted() method in the Financial\Securities class instead
|
||||
* @see Use the priceDiscounted() method in the Financial\Securities\Price class instead
|
||||
*
|
||||
* @param mixed $settlement The security's settlement date.
|
||||
* The security settlement date is the date after the issue date when the security is traded to the buyer.
|
||||
* The security settlement date is the date after the issue date when the security
|
||||
* is traded to the buyer.
|
||||
* @param mixed $maturity The security's maturity date.
|
||||
* The maturity date is the date when the security expires.
|
||||
* The maturity date is the date when the security expires.
|
||||
* @param int $discount The security's discount rate
|
||||
* @param int $redemption The security's redemption value per $100 face value
|
||||
* @param int $basis The type of day count to use.
|
||||
|
|
@ -1422,7 +1292,7 @@ class Financial
|
|||
*/
|
||||
public static function PRICEDISC($settlement, $maturity, $discount, $redemption, $basis = 0)
|
||||
{
|
||||
return Financial\Securities::discounted($settlement, $maturity, $discount, $redemption, $basis);
|
||||
return Securities\Price::priceDiscounted($settlement, $maturity, $discount, $redemption, $basis);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1432,12 +1302,13 @@ class Financial
|
|||
*
|
||||
* @Deprecated 1.18.0
|
||||
*
|
||||
* @see Use the maturity() method in the Financial\Securities class instead
|
||||
* @see Use the priceAtMaturity() method in the Financial\Securities\Price class instead
|
||||
*
|
||||
* @param mixed $settlement The security's settlement date.
|
||||
* The security's settlement date is the date after the issue date when the security is traded to the buyer.
|
||||
* The security's settlement date is the date after the issue date when the security
|
||||
* is traded to the buyer.
|
||||
* @param mixed $maturity The security's maturity date.
|
||||
* The maturity date is the date when the security expires.
|
||||
* The maturity date is the date when the security expires.
|
||||
* @param mixed $issue The security's issue date
|
||||
* @param int $rate The security's interest rate at date of issue
|
||||
* @param int $yield The security's annual yield
|
||||
|
|
@ -1452,7 +1323,7 @@ class Financial
|
|||
*/
|
||||
public static function PRICEMAT($settlement, $maturity, $issue, $rate, $yield, $basis = 0)
|
||||
{
|
||||
return Financial\Securities::maturity($settlement, $maturity, $issue, $rate, $yield, $basis);
|
||||
return Securities\Price::priceAtMaturity($settlement, $maturity, $issue, $rate, $yield, $basis);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1640,6 +1511,10 @@ class Financial
|
|||
*
|
||||
* Returns the straight-line depreciation of an asset for one period
|
||||
*
|
||||
* @Deprecated 1.18.0
|
||||
*
|
||||
* @see Use the SLN() method in the Financial\Depreciation class instead
|
||||
*
|
||||
* @param mixed $cost Initial cost of the asset
|
||||
* @param mixed $salvage Value at the end of the depreciation
|
||||
* @param mixed $life Number of periods over which the asset is depreciated
|
||||
|
|
@ -1648,20 +1523,7 @@ class Financial
|
|||
*/
|
||||
public static function SLN($cost, $salvage, $life)
|
||||
{
|
||||
$cost = Functions::flattenSingleValue($cost);
|
||||
$salvage = Functions::flattenSingleValue($salvage);
|
||||
$life = Functions::flattenSingleValue($life);
|
||||
|
||||
// Calculate
|
||||
if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life))) {
|
||||
if ($life < 0) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
|
||||
return ($cost - $salvage) / $life;
|
||||
}
|
||||
|
||||
return Functions::VALUE();
|
||||
return Depreciation::SLN($cost, $salvage, $life);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1669,6 +1531,10 @@ class Financial
|
|||
*
|
||||
* Returns the sum-of-years' digits depreciation of an asset for a specified period.
|
||||
*
|
||||
* @Deprecated 1.18.0
|
||||
*
|
||||
* @see Use the SYD() method in the Financial\Depreciation class instead
|
||||
*
|
||||
* @param mixed $cost Initial cost of the asset
|
||||
* @param mixed $salvage Value at the end of the depreciation
|
||||
* @param mixed $life Number of periods over which the asset is depreciated
|
||||
|
|
@ -1678,21 +1544,7 @@ class Financial
|
|||
*/
|
||||
public static function SYD($cost, $salvage, $life, $period)
|
||||
{
|
||||
$cost = Functions::flattenSingleValue($cost);
|
||||
$salvage = Functions::flattenSingleValue($salvage);
|
||||
$life = Functions::flattenSingleValue($life);
|
||||
$period = Functions::flattenSingleValue($period);
|
||||
|
||||
// Calculate
|
||||
if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period))) {
|
||||
if (($life < 1) || ($period > $life)) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
|
||||
return (($cost - $salvage) * ($life - $period + 1) * 2) / ($life * ($life + 1));
|
||||
}
|
||||
|
||||
return Functions::VALUE();
|
||||
return Depreciation::SYD($cost, $salvage, $life, $period);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1714,7 +1566,7 @@ class Financial
|
|||
*/
|
||||
public static function TBILLEQ($settlement, $maturity, $discount)
|
||||
{
|
||||
return Financial\TreasuryBill::bondEquivalentYield($settlement, $maturity, $discount);
|
||||
return TreasuryBill::bondEquivalentYield($settlement, $maturity, $discount);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1737,7 +1589,7 @@ class Financial
|
|||
*/
|
||||
public static function TBILLPRICE($settlement, $maturity, $discount)
|
||||
{
|
||||
return Financial\TreasuryBill::price($settlement, $maturity, $discount);
|
||||
return TreasuryBill::price($settlement, $maturity, $discount);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1760,7 +1612,7 @@ class Financial
|
|||
*/
|
||||
public static function TBILLYIELD($settlement, $maturity, $price)
|
||||
{
|
||||
return Financial\TreasuryBill::yield($settlement, $maturity, $price);
|
||||
return TreasuryBill::yield($settlement, $maturity, $price);
|
||||
}
|
||||
|
||||
private static function bothNegAndPos($neg, $pos)
|
||||
|
|
@ -1977,10 +1829,13 @@ class Financial
|
|||
*
|
||||
* Returns the annual yield of a security that pays interest at maturity.
|
||||
*
|
||||
* @see Use the yieldDiscounted() method in the Financial\Securities\Yields class instead
|
||||
*
|
||||
* @param mixed $settlement The security's settlement date.
|
||||
* The security's settlement date is the date after the issue date when the security is traded to the buyer.
|
||||
* The security's settlement date is the date after the issue date when the security
|
||||
* is traded to the buyer.
|
||||
* @param mixed $maturity The security's maturity date.
|
||||
* The maturity date is the date when the security expires.
|
||||
* The maturity date is the date when the security expires.
|
||||
* @param int $price The security's price per $100 face value
|
||||
* @param int $redemption The security's redemption value per $100 face value
|
||||
* @param int $basis The type of day count to use.
|
||||
|
|
@ -1994,32 +1849,7 @@ class Financial
|
|||
*/
|
||||
public static function YIELDDISC($settlement, $maturity, $price, $redemption, $basis = 0)
|
||||
{
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$price = Functions::flattenSingleValue($price);
|
||||
$redemption = Functions::flattenSingleValue($redemption);
|
||||
$basis = (int) Functions::flattenSingleValue($basis);
|
||||
|
||||
// Validate
|
||||
if (is_numeric($price) && is_numeric($redemption)) {
|
||||
if (($price <= 0) || ($redemption <= 0)) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
$daysPerYear = Financial\Helpers::daysPerYear(DateTime::YEAR($settlement), $basis);
|
||||
if (!is_numeric($daysPerYear)) {
|
||||
return $daysPerYear;
|
||||
}
|
||||
$daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
|
||||
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenSettlementAndMaturity;
|
||||
}
|
||||
$daysBetweenSettlementAndMaturity *= $daysPerYear;
|
||||
|
||||
return (($redemption - $price) / $price) * ($daysPerYear / $daysBetweenSettlementAndMaturity);
|
||||
}
|
||||
|
||||
return Functions::VALUE();
|
||||
return Securities\Yields::yieldDiscounted($settlement, $maturity, $price, $redemption, $basis);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2027,10 +1857,15 @@ class Financial
|
|||
*
|
||||
* Returns the annual yield of a security that pays interest at maturity.
|
||||
*
|
||||
* @Deprecated 1.18.0
|
||||
*
|
||||
* @see Use the yieldAtMaturity() method in the Financial\Securities\Yields class instead
|
||||
*
|
||||
* @param mixed $settlement The security's settlement date.
|
||||
* The security's settlement date is the date after the issue date when the security is traded to the buyer.
|
||||
* The security's settlement date is the date after the issue date when the security
|
||||
* is traded to the buyer.
|
||||
* @param mixed $maturity The security's maturity date.
|
||||
* The maturity date is the date when the security expires.
|
||||
* The maturity date is the date when the security expires.
|
||||
* @param mixed $issue The security's issue date
|
||||
* @param int $rate The security's interest rate at date of issue
|
||||
* @param int $price The security's price per $100 face value
|
||||
|
|
@ -2045,46 +1880,6 @@ class Financial
|
|||
*/
|
||||
public static function YIELDMAT($settlement, $maturity, $issue, $rate, $price, $basis = 0)
|
||||
{
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$issue = Functions::flattenSingleValue($issue);
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$price = Functions::flattenSingleValue($price);
|
||||
$basis = (int) Functions::flattenSingleValue($basis);
|
||||
|
||||
// Validate
|
||||
if (is_numeric($rate) && is_numeric($price)) {
|
||||
if (($rate <= 0) || ($price <= 0)) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
$daysPerYear = Financial\Helpers::daysPerYear(DateTime::YEAR($settlement), $basis);
|
||||
if (!is_numeric($daysPerYear)) {
|
||||
return $daysPerYear;
|
||||
}
|
||||
$daysBetweenIssueAndSettlement = DateTime::YEARFRAC($issue, $settlement, $basis);
|
||||
if (!is_numeric($daysBetweenIssueAndSettlement)) {
|
||||
// return date error
|
||||
return $daysBetweenIssueAndSettlement;
|
||||
}
|
||||
$daysBetweenIssueAndSettlement *= $daysPerYear;
|
||||
$daysBetweenIssueAndMaturity = DateTime::YEARFRAC($issue, $maturity, $basis);
|
||||
if (!is_numeric($daysBetweenIssueAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenIssueAndMaturity;
|
||||
}
|
||||
$daysBetweenIssueAndMaturity *= $daysPerYear;
|
||||
$daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
|
||||
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenSettlementAndMaturity;
|
||||
}
|
||||
$daysBetweenSettlementAndMaturity *= $daysPerYear;
|
||||
|
||||
return ((1 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate) - (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) /
|
||||
(($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) *
|
||||
($daysPerYear / $daysBetweenSettlementAndMaturity);
|
||||
}
|
||||
|
||||
return Functions::VALUE();
|
||||
return Securities\Yields::yieldAtMaturity($settlement, $maturity, $issue, $rate, $price, $basis);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,163 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\DateTime;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
|
||||
class Amortization
|
||||
{
|
||||
/**
|
||||
* AMORDEGRC.
|
||||
*
|
||||
* Returns the depreciation for each accounting period.
|
||||
* This function is provided for the French accounting system. If an asset is purchased in
|
||||
* the middle of the accounting period, the prorated depreciation is taken into account.
|
||||
* The function is similar to AMORLINC, except that a depreciation coefficient is applied in
|
||||
* the calculation depending on the life of the assets.
|
||||
* This function will return the depreciation until the last period of the life of the assets
|
||||
* or until the cumulated value of depreciation is greater than the cost of the assets minus
|
||||
* the salvage value.
|
||||
*
|
||||
* Excel Function:
|
||||
* AMORDEGRC(cost,purchased,firstPeriod,salvage,period,rate[,basis])
|
||||
*
|
||||
* @param float $cost The cost of the asset
|
||||
* @param mixed $purchased Date of the purchase of the asset
|
||||
* @param mixed $firstPeriod Date of the end of the first period
|
||||
* @param mixed $salvage The salvage value at the end of the life of the asset
|
||||
* @param float $period The period
|
||||
* @param float $rate Rate of depreciation
|
||||
* @param int $basis The type of day count to use.
|
||||
* 0 or omitted US (NASD) 30/360
|
||||
* 1 Actual/actual
|
||||
* 2 Actual/360
|
||||
* 3 Actual/365
|
||||
* 4 European 30/360
|
||||
*
|
||||
* @return float|string (string containing the error type if there is an error)
|
||||
*/
|
||||
public static function AMORDEGRC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis = 0)
|
||||
{
|
||||
$cost = Functions::flattenSingleValue($cost);
|
||||
$purchased = Functions::flattenSingleValue($purchased);
|
||||
$firstPeriod = Functions::flattenSingleValue($firstPeriod);
|
||||
$salvage = Functions::flattenSingleValue($salvage);
|
||||
$period = floor(Functions::flattenSingleValue($period));
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
|
||||
|
||||
$yearFrac = DateTime::YEARFRAC($purchased, $firstPeriod, $basis);
|
||||
if (is_string($yearFrac)) {
|
||||
return $yearFrac;
|
||||
}
|
||||
|
||||
$amortiseCoeff = self::getAmortizationCoefficient($rate);
|
||||
|
||||
$rate *= $amortiseCoeff;
|
||||
$fNRate = round($yearFrac * $rate * $cost, 0);
|
||||
$cost -= $fNRate;
|
||||
$fRest = $cost - $salvage;
|
||||
|
||||
for ($n = 0; $n < $period; ++$n) {
|
||||
$fNRate = round($rate * $cost, 0);
|
||||
$fRest -= $fNRate;
|
||||
|
||||
if ($fRest < 0.0) {
|
||||
switch ($period - $n) {
|
||||
case 0:
|
||||
case 1:
|
||||
return round($cost * 0.5, 0);
|
||||
default:
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
$cost -= $fNRate;
|
||||
}
|
||||
|
||||
return $fNRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* AMORLINC.
|
||||
*
|
||||
* Returns the depreciation for each accounting period.
|
||||
* This function is provided for the French accounting system. If an asset is purchased in
|
||||
* the middle of the accounting period, the prorated depreciation is taken into account.
|
||||
*
|
||||
* Excel Function:
|
||||
* AMORLINC(cost,purchased,firstPeriod,salvage,period,rate[,basis])
|
||||
*
|
||||
* @param float $cost The cost of the asset
|
||||
* @param mixed $purchased Date of the purchase of the asset
|
||||
* @param mixed $firstPeriod Date of the end of the first period
|
||||
* @param mixed $salvage The salvage value at the end of the life of the asset
|
||||
* @param float $period The period
|
||||
* @param float $rate Rate of depreciation
|
||||
* @param int $basis The type of day count to use.
|
||||
* 0 or omitted US (NASD) 30/360
|
||||
* 1 Actual/actual
|
||||
* 2 Actual/360
|
||||
* 3 Actual/365
|
||||
* 4 European 30/360
|
||||
*
|
||||
* @return float|string (string containing the error type if there is an error)
|
||||
*/
|
||||
public static function AMORLINC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis = 0)
|
||||
{
|
||||
$cost = Functions::flattenSingleValue($cost);
|
||||
$purchased = Functions::flattenSingleValue($purchased);
|
||||
$firstPeriod = Functions::flattenSingleValue($firstPeriod);
|
||||
$salvage = Functions::flattenSingleValue($salvage);
|
||||
$period = Functions::flattenSingleValue($period);
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
|
||||
|
||||
$fOneRate = $cost * $rate;
|
||||
$fCostDelta = $cost - $salvage;
|
||||
// Note, quirky variation for leap years on the YEARFRAC for this function
|
||||
$purchasedYear = DateTime::YEAR($purchased);
|
||||
$yearFrac = DateTime::YEARFRAC($purchased, $firstPeriod, $basis);
|
||||
if (is_string($yearFrac)) {
|
||||
return $yearFrac;
|
||||
}
|
||||
|
||||
if (($basis == 1) && ($yearFrac < 1) && (DateTime::isLeapYear($purchasedYear))) {
|
||||
$yearFrac *= 365 / 366;
|
||||
}
|
||||
|
||||
$f0Rate = $yearFrac * $rate * $cost;
|
||||
$nNumOfFullPeriods = (int) (($cost - $salvage - $f0Rate) / $fOneRate);
|
||||
|
||||
if ($period == 0) {
|
||||
return $f0Rate;
|
||||
} elseif ($period <= $nNumOfFullPeriods) {
|
||||
return $fOneRate;
|
||||
} elseif ($period == ($nNumOfFullPeriods + 1)) {
|
||||
return $fCostDelta - $fOneRate * $nNumOfFullPeriods - $f0Rate;
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
private static function getAmortizationCoefficient(float $rate): float
|
||||
{
|
||||
// The depreciation coefficients are:
|
||||
// Life of assets (1/rate) Depreciation coefficient
|
||||
// Less than 3 years 1
|
||||
// Between 3 and 4 years 1.5
|
||||
// Between 5 and 6 years 2
|
||||
// More than 6 years 2.5
|
||||
$fUsePer = 1.0 / $rate;
|
||||
|
||||
if ($fUsePer < 3.0) {
|
||||
return 1.0;
|
||||
} elseif ($fUsePer < 4.0) {
|
||||
return 1.5;
|
||||
} elseif ($fUsePer <= 6.0) {
|
||||
return 2.0;
|
||||
}
|
||||
|
||||
return 2.5;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,287 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
|
||||
class Depreciation
|
||||
{
|
||||
/**
|
||||
* DB.
|
||||
*
|
||||
* Returns the depreciation of an asset for a specified period using the
|
||||
* fixed-declining balance method.
|
||||
* This form of depreciation is used if you want to get a higher depreciation value
|
||||
* at the beginning of the depreciation (as opposed to linear depreciation). The
|
||||
* depreciation value is reduced with every depreciation period by the depreciation
|
||||
* already deducted from the initial cost.
|
||||
*
|
||||
* Excel Function:
|
||||
* DB(cost,salvage,life,period[,month])
|
||||
*
|
||||
* @param float $cost Initial cost of the asset
|
||||
* @param float $salvage Value at the end of the depreciation.
|
||||
* (Sometimes called the salvage value of the asset)
|
||||
* @param int $life Number of periods over which the asset is depreciated.
|
||||
* (Sometimes called the useful life of the asset)
|
||||
* @param int $period The period for which you want to calculate the
|
||||
* depreciation. Period must use the same units as life.
|
||||
* @param int $month Number of months in the first year. If month is omitted,
|
||||
* it defaults to 12.
|
||||
*
|
||||
* @return float|string
|
||||
*/
|
||||
public static function DB($cost, $salvage, $life, $period, $month = 12)
|
||||
{
|
||||
$cost = Functions::flattenSingleValue($cost);
|
||||
$salvage = Functions::flattenSingleValue($salvage);
|
||||
$life = Functions::flattenSingleValue($life);
|
||||
$period = Functions::flattenSingleValue($period);
|
||||
$month = Functions::flattenSingleValue($month);
|
||||
|
||||
try {
|
||||
$cost = self::validateCost($cost);
|
||||
$salvage = self::validateSalvage($salvage);
|
||||
$life = self::validateLife($life);
|
||||
$period = self::validatePeriod($period);
|
||||
$month = self::validateMonth($month);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
if ($cost === 0.0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Set Fixed Depreciation Rate
|
||||
$fixedDepreciationRate = 1 - ($salvage / $cost) ** (1 / $life);
|
||||
$fixedDepreciationRate = round($fixedDepreciationRate, 3);
|
||||
|
||||
// Loop through each period calculating the depreciation
|
||||
// TODO Handle period value between 0 and 1 (e.g. 0.5)
|
||||
$previousDepreciation = 0;
|
||||
$depreciation = 0;
|
||||
for ($per = 1; $per <= $period; ++$per) {
|
||||
if ($per == 1) {
|
||||
$depreciation = $cost * $fixedDepreciationRate * $month / 12;
|
||||
} elseif ($per == ($life + 1)) {
|
||||
$depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate * (12 - $month) / 12;
|
||||
} else {
|
||||
$depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate;
|
||||
}
|
||||
$previousDepreciation += $depreciation;
|
||||
}
|
||||
|
||||
return $depreciation;
|
||||
}
|
||||
|
||||
/**
|
||||
* DDB.
|
||||
*
|
||||
* Returns the depreciation of an asset for a specified period using the
|
||||
* double-declining balance method or some other method you specify.
|
||||
*
|
||||
* Excel Function:
|
||||
* DDB(cost,salvage,life,period[,factor])
|
||||
*
|
||||
* @param float $cost Initial cost of the asset
|
||||
* @param float $salvage Value at the end of the depreciation.
|
||||
* (Sometimes called the salvage value of the asset)
|
||||
* @param int $life Number of periods over which the asset is depreciated.
|
||||
* (Sometimes called the useful life of the asset)
|
||||
* @param int $period The period for which you want to calculate the
|
||||
* depreciation. Period must use the same units as life.
|
||||
* @param float $factor The rate at which the balance declines.
|
||||
* If factor is omitted, it is assumed to be 2 (the
|
||||
* double-declining balance method).
|
||||
*
|
||||
* @return float|string
|
||||
*/
|
||||
public static function DDB($cost, $salvage, $life, $period, $factor = 2.0)
|
||||
{
|
||||
$cost = Functions::flattenSingleValue($cost);
|
||||
$salvage = Functions::flattenSingleValue($salvage);
|
||||
$life = Functions::flattenSingleValue($life);
|
||||
$period = Functions::flattenSingleValue($period);
|
||||
$factor = Functions::flattenSingleValue($factor);
|
||||
|
||||
try {
|
||||
$cost = self::validateCost($cost);
|
||||
$salvage = self::validateSalvage($salvage);
|
||||
$life = self::validateLife($life);
|
||||
$period = self::validatePeriod($period);
|
||||
$factor = self::validateFactor($factor);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
if ($period > $life) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
|
||||
// Loop through each period calculating the depreciation
|
||||
// TODO Handling for fractional $period values
|
||||
$previousDepreciation = 0;
|
||||
$depreciation = 0;
|
||||
for ($per = 1; $per <= $period; ++$per) {
|
||||
$depreciation = min(($cost - $previousDepreciation) * ($factor / $life), ($cost - $salvage - $previousDepreciation));
|
||||
$previousDepreciation += $depreciation;
|
||||
}
|
||||
|
||||
return $depreciation;
|
||||
}
|
||||
|
||||
/**
|
||||
* SLN.
|
||||
*
|
||||
* Returns the straight-line depreciation of an asset for one period
|
||||
*
|
||||
* @param mixed $cost Initial cost of the asset
|
||||
* @param mixed $salvage Value at the end of the depreciation
|
||||
* @param mixed $life Number of periods over which the asset is depreciated
|
||||
*
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function SLN($cost, $salvage, $life)
|
||||
{
|
||||
$cost = Functions::flattenSingleValue($cost);
|
||||
$salvage = Functions::flattenSingleValue($salvage);
|
||||
$life = Functions::flattenSingleValue($life);
|
||||
|
||||
try {
|
||||
$cost = self::validateCost($cost, true);
|
||||
$salvage = self::validateSalvage($salvage, true);
|
||||
$life = self::validateLife($life, true);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
if ($life === 0.0) {
|
||||
return Functions::DIV0();
|
||||
}
|
||||
|
||||
return ($cost - $salvage) / $life;
|
||||
}
|
||||
|
||||
/**
|
||||
* SYD.
|
||||
*
|
||||
* Returns the sum-of-years' digits depreciation of an asset for a specified period.
|
||||
*
|
||||
* @param mixed $cost Initial cost of the asset
|
||||
* @param mixed $salvage Value at the end of the depreciation
|
||||
* @param mixed $life Number of periods over which the asset is depreciated
|
||||
* @param mixed $period Period
|
||||
*
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function SYD($cost, $salvage, $life, $period)
|
||||
{
|
||||
$cost = Functions::flattenSingleValue($cost);
|
||||
$salvage = Functions::flattenSingleValue($salvage);
|
||||
$life = Functions::flattenSingleValue($life);
|
||||
$period = Functions::flattenSingleValue($period);
|
||||
|
||||
try {
|
||||
$cost = self::validateCost($cost, true);
|
||||
$salvage = self::validateSalvage($salvage);
|
||||
$life = self::validateLife($life);
|
||||
$period = self::validatePeriod($period);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
if ($period > $life) {
|
||||
return Functions::NAN();
|
||||
}
|
||||
|
||||
$syd = (($cost - $salvage) * ($life - $period + 1) * 2) / ($life * ($life + 1));
|
||||
|
||||
return $syd;
|
||||
}
|
||||
|
||||
private static function validateCost($cost, bool $negativeValueAllowed = false): float
|
||||
{
|
||||
if (!is_numeric($cost)) {
|
||||
throw new Exception(Functions::VALUE());
|
||||
}
|
||||
|
||||
$cost = (float) $cost;
|
||||
if ($cost < 0.0 && $negativeValueAllowed === false) {
|
||||
throw new Exception(Functions::NAN());
|
||||
}
|
||||
|
||||
return $cost;
|
||||
}
|
||||
|
||||
private static function validateSalvage($salvage, bool $negativeValueAllowed = false): float
|
||||
{
|
||||
if (!is_numeric($salvage)) {
|
||||
throw new Exception(Functions::VALUE());
|
||||
}
|
||||
|
||||
$salvage = (float) $salvage;
|
||||
if ($salvage < 0.0 && $negativeValueAllowed === false) {
|
||||
throw new Exception(Functions::NAN());
|
||||
}
|
||||
|
||||
return $salvage;
|
||||
}
|
||||
|
||||
private static function validateLife($life, bool $negativeValueAllowed = false): float
|
||||
{
|
||||
if (!is_numeric($life)) {
|
||||
throw new Exception(Functions::VALUE());
|
||||
}
|
||||
|
||||
$life = (float) $life;
|
||||
if ($life < 0.0 && $negativeValueAllowed === false) {
|
||||
throw new Exception(Functions::NAN());
|
||||
}
|
||||
|
||||
return $life;
|
||||
}
|
||||
|
||||
private static function validatePeriod($period, bool $negativeValueAllowed = false): float
|
||||
{
|
||||
if (!is_numeric($period)) {
|
||||
throw new Exception(Functions::VALUE());
|
||||
}
|
||||
|
||||
$period = (float) $period;
|
||||
if ($period <= 0.0 && $negativeValueAllowed === false) {
|
||||
throw new Exception(Functions::NAN());
|
||||
}
|
||||
|
||||
return $period;
|
||||
}
|
||||
|
||||
private static function validateMonth($month): int
|
||||
{
|
||||
if (!is_numeric($month)) {
|
||||
throw new Exception(Functions::VALUE());
|
||||
}
|
||||
|
||||
$month = (int) $month;
|
||||
if ($month < 1) {
|
||||
throw new Exception(Functions::NAN());
|
||||
}
|
||||
|
||||
return $month;
|
||||
}
|
||||
|
||||
private static function validateFactor($factor): float
|
||||
{
|
||||
if (!is_numeric($factor)) {
|
||||
throw new Exception(Functions::VALUE());
|
||||
}
|
||||
|
||||
$factor = (float) $factor;
|
||||
if ($factor <= 0.0) {
|
||||
throw new Exception(Functions::NAN());
|
||||
}
|
||||
|
||||
return $factor;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial\Securities;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\DateTime;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Securities\Constants as SecuritiesConstants;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
|
||||
abstract class BaseValidations
|
||||
{
|
||||
protected static function validateInputDate($date)
|
||||
{
|
||||
$date = DateTime::getDateValue($date);
|
||||
if (is_string($date)) {
|
||||
throw new Exception(Functions::VALUE());
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
protected static function validateSettlementDate($settlement)
|
||||
{
|
||||
return self::validateInputDate($settlement);
|
||||
}
|
||||
|
||||
protected static function validateMaturityDate($maturity)
|
||||
{
|
||||
return self::validateInputDate($maturity);
|
||||
}
|
||||
|
||||
protected static function validateIssueDate($issue)
|
||||
{
|
||||
return self::validateInputDate($issue);
|
||||
}
|
||||
|
||||
protected static function validateSecurityPeriod($settlement, $maturity): void
|
||||
{
|
||||
if ($settlement >= $maturity) {
|
||||
throw new Exception(Functions::NAN());
|
||||
}
|
||||
}
|
||||
|
||||
protected static function validateRate($rate): float
|
||||
{
|
||||
if (!is_numeric($rate)) {
|
||||
throw new Exception(Functions::VALUE());
|
||||
}
|
||||
|
||||
$rate = (float) $rate;
|
||||
if ($rate < 0.0) {
|
||||
throw new Exception(Functions::NAN());
|
||||
}
|
||||
|
||||
return $rate;
|
||||
}
|
||||
|
||||
protected static function validatePrice($price): float
|
||||
{
|
||||
if (!is_numeric($price)) {
|
||||
throw new Exception(Functions::VALUE());
|
||||
}
|
||||
|
||||
$price = (float) $price;
|
||||
if ($price < 0.0) {
|
||||
throw new Exception(Functions::NAN());
|
||||
}
|
||||
|
||||
return $price;
|
||||
}
|
||||
|
||||
protected static function validateYield($yield): float
|
||||
{
|
||||
if (!is_numeric($yield)) {
|
||||
throw new Exception(Functions::VALUE());
|
||||
}
|
||||
|
||||
$yield = (float) $yield;
|
||||
if ($yield < 0.0) {
|
||||
throw new Exception(Functions::NAN());
|
||||
}
|
||||
|
||||
return $yield;
|
||||
}
|
||||
|
||||
protected static function validateRedemption($redemption): float
|
||||
{
|
||||
if (!is_numeric($redemption)) {
|
||||
throw new Exception(Functions::VALUE());
|
||||
}
|
||||
|
||||
$redemption = (float) $redemption;
|
||||
if ($redemption <= 0.0) {
|
||||
throw new Exception(Functions::NAN());
|
||||
}
|
||||
|
||||
return $redemption;
|
||||
}
|
||||
|
||||
protected static function validateDiscount($discount): float
|
||||
{
|
||||
if (!is_numeric($discount)) {
|
||||
throw new Exception(Functions::VALUE());
|
||||
}
|
||||
|
||||
$discount = (float) $discount;
|
||||
if ($discount <= 0.0) {
|
||||
throw new Exception(Functions::NAN());
|
||||
}
|
||||
|
||||
return $discount;
|
||||
}
|
||||
|
||||
protected static function validateFrequency($frequency): int
|
||||
{
|
||||
if (!is_numeric($frequency)) {
|
||||
throw new Exception(Functions::VALUE());
|
||||
}
|
||||
|
||||
$frequency = (int) $frequency;
|
||||
if (
|
||||
($frequency !== SecuritiesConstants::FREQUENCY_ANNUAL) &&
|
||||
($frequency !== SecuritiesConstants::FREQUENCY_SEMI_ANNUAL) &&
|
||||
($frequency !== SecuritiesConstants::FREQUENCY_QUARTERLY)
|
||||
) {
|
||||
throw new Exception(Functions::NAN());
|
||||
}
|
||||
|
||||
return $frequency;
|
||||
}
|
||||
|
||||
protected static function validateBasis($basis): int
|
||||
{
|
||||
if (!is_numeric($basis)) {
|
||||
throw new Exception(Functions::VALUE());
|
||||
}
|
||||
|
||||
$basis = (int) $basis;
|
||||
if (($basis < 0) || ($basis > 4)) {
|
||||
throw new Exception(Functions::NAN());
|
||||
}
|
||||
|
||||
return $basis;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial\Securities;
|
||||
|
||||
class Constants
|
||||
{
|
||||
public const FREQUENCY_ANNUAL = 1;
|
||||
public const FREQUENCY_SEMI_ANNUAL = 2;
|
||||
public const FREQUENCY_QUARTERLY = 4;
|
||||
}
|
||||
|
|
@ -1,17 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial;
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial\Securities;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\DateTime;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Coupons;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Helpers;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
|
||||
class Securities
|
||||
class Price extends BaseValidations
|
||||
{
|
||||
public const FREQUENCY_ANNUAL = 1;
|
||||
public const FREQUENCY_SEMI_ANNUAL = 2;
|
||||
public const FREQUENCY_QUARTERLY = 4;
|
||||
|
||||
/**
|
||||
* PRICE.
|
||||
*
|
||||
|
|
@ -100,7 +98,7 @@ class Securities
|
|||
*
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function discounted($settlement, $maturity, $discount, $redemption, $basis = 0)
|
||||
public static function priceDiscounted($settlement, $maturity, $discount, $redemption, $basis = 0)
|
||||
{
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
|
|
@ -150,7 +148,7 @@ class Securities
|
|||
*
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function maturity($settlement, $maturity, $issue, $rate, $yield, $basis = 0)
|
||||
public static function priceAtMaturity($settlement, $maturity, $issue, $rate, $yield, $basis = 0)
|
||||
{
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
|
|
@ -198,124 +196,4 @@ class Securities
|
|||
(1 + (($daysBetweenSettlementAndMaturity / $daysPerYear) * $yield)) -
|
||||
(($daysBetweenIssueAndSettlement / $daysPerYear) * $rate * 100);
|
||||
}
|
||||
|
||||
private static function validateInputDate($date)
|
||||
{
|
||||
$date = DateTime::getDateValue($date);
|
||||
if (is_string($date)) {
|
||||
throw new Exception(Functions::VALUE());
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
private static function validateSettlementDate($settlement)
|
||||
{
|
||||
return self::validateInputDate($settlement);
|
||||
}
|
||||
|
||||
private static function validateMaturityDate($maturity)
|
||||
{
|
||||
return self::validateInputDate($maturity);
|
||||
}
|
||||
|
||||
private static function validateIssueDate($issue)
|
||||
{
|
||||
return self::validateInputDate($issue);
|
||||
}
|
||||
|
||||
private static function validateSecurityPeriod($settlement, $maturity): void
|
||||
{
|
||||
if ($settlement >= $maturity) {
|
||||
throw new Exception(Functions::NAN());
|
||||
}
|
||||
}
|
||||
|
||||
private static function validateRate($rate): float
|
||||
{
|
||||
if (!is_numeric($rate)) {
|
||||
throw new Exception(Functions::VALUE());
|
||||
}
|
||||
|
||||
$rate = (float) $rate;
|
||||
if ($rate < 0.0) {
|
||||
throw new Exception(Functions::NAN());
|
||||
}
|
||||
|
||||
return $rate;
|
||||
}
|
||||
|
||||
private static function validateYield($yield): float
|
||||
{
|
||||
if (!is_numeric($yield)) {
|
||||
throw new Exception(Functions::VALUE());
|
||||
}
|
||||
|
||||
$yield = (float) $yield;
|
||||
if ($yield < 0.0) {
|
||||
throw new Exception(Functions::NAN());
|
||||
}
|
||||
|
||||
return $yield;
|
||||
}
|
||||
|
||||
private static function validateRedemption($redemption): float
|
||||
{
|
||||
if (!is_numeric($redemption)) {
|
||||
throw new Exception(Functions::VALUE());
|
||||
}
|
||||
|
||||
$redemption = (float) $redemption;
|
||||
if ($redemption <= 0.0) {
|
||||
throw new Exception(Functions::NAN());
|
||||
}
|
||||
|
||||
return $redemption;
|
||||
}
|
||||
|
||||
private static function validateDiscount($discount): float
|
||||
{
|
||||
if (!is_numeric($discount)) {
|
||||
throw new Exception(Functions::VALUE());
|
||||
}
|
||||
|
||||
$discount = (float) $discount;
|
||||
if ($discount <= 0.0) {
|
||||
throw new Exception(Functions::NAN());
|
||||
}
|
||||
|
||||
return $discount;
|
||||
}
|
||||
|
||||
private static function validateFrequency($frequency): int
|
||||
{
|
||||
if (!is_numeric($frequency)) {
|
||||
throw new Exception(Functions::VALUE());
|
||||
}
|
||||
|
||||
$frequency = (int) $frequency;
|
||||
if (
|
||||
($frequency !== self::FREQUENCY_ANNUAL) &&
|
||||
($frequency !== self::FREQUENCY_SEMI_ANNUAL) &&
|
||||
($frequency !== self::FREQUENCY_QUARTERLY)
|
||||
) {
|
||||
throw new Exception(Functions::NAN());
|
||||
}
|
||||
|
||||
return $frequency;
|
||||
}
|
||||
|
||||
private static function validateBasis($basis): int
|
||||
{
|
||||
if (!is_numeric($basis)) {
|
||||
throw new Exception(Functions::VALUE());
|
||||
}
|
||||
|
||||
$basis = (int) $basis;
|
||||
if (($basis < 0) || ($basis > 4)) {
|
||||
throw new Exception(Functions::NAN());
|
||||
}
|
||||
|
||||
return $basis;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial\Securities;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\DateTime;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Helpers;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
|
||||
class Yields extends BaseValidations
|
||||
{
|
||||
/**
|
||||
* YIELDDISC.
|
||||
*
|
||||
* Returns the annual yield of a security that pays interest at maturity.
|
||||
*
|
||||
* @param mixed $settlement The security's settlement date.
|
||||
* The security's settlement date is the date after the issue date when the security
|
||||
* is traded to the buyer.
|
||||
* @param mixed $maturity The security's maturity date.
|
||||
* The maturity date is the date when the security expires.
|
||||
* @param int $price The security's price per $100 face value
|
||||
* @param int $redemption The security's redemption value per $100 face value
|
||||
* @param int $basis The type of day count to use.
|
||||
* 0 or omitted US (NASD) 30/360
|
||||
* 1 Actual/actual
|
||||
* 2 Actual/360
|
||||
* 3 Actual/365
|
||||
* 4 European 30/360
|
||||
*
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function yieldDiscounted($settlement, $maturity, $price, $redemption, $basis = 0)
|
||||
{
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$price = Functions::flattenSingleValue($price);
|
||||
$redemption = Functions::flattenSingleValue($redemption);
|
||||
$basis = (int) Functions::flattenSingleValue($basis);
|
||||
|
||||
try {
|
||||
$settlement = self::validateSettlementDate($settlement);
|
||||
$maturity = self::validateMaturityDate($maturity);
|
||||
self::validateSecurityPeriod($settlement, $maturity);
|
||||
$price = self::validatePrice($price);
|
||||
$redemption = self::validateRedemption($redemption);
|
||||
$basis = self::validateBasis($basis);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
$daysPerYear = Helpers::daysPerYear(DateTime::YEAR($settlement), $basis);
|
||||
if (!is_numeric($daysPerYear)) {
|
||||
return $daysPerYear;
|
||||
}
|
||||
$daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
|
||||
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenSettlementAndMaturity;
|
||||
}
|
||||
$daysBetweenSettlementAndMaturity *= $daysPerYear;
|
||||
|
||||
return (($redemption - $price) / $price) * ($daysPerYear / $daysBetweenSettlementAndMaturity);
|
||||
}
|
||||
|
||||
/**
|
||||
* YIELDMAT.
|
||||
*
|
||||
* Returns the annual yield of a security that pays interest at maturity.
|
||||
*
|
||||
* @param mixed $settlement The security's settlement date.
|
||||
* The security's settlement date is the date after the issue date when the security
|
||||
* is traded to the buyer.
|
||||
* @param mixed $maturity The security's maturity date.
|
||||
* The maturity date is the date when the security expires.
|
||||
* @param mixed $issue The security's issue date
|
||||
* @param int $rate The security's interest rate at date of issue
|
||||
* @param int $price The security's price per $100 face value
|
||||
* @param int $basis The type of day count to use.
|
||||
* 0 or omitted US (NASD) 30/360
|
||||
* 1 Actual/actual
|
||||
* 2 Actual/360
|
||||
* 3 Actual/365
|
||||
* 4 European 30/360
|
||||
*
|
||||
* @return float|string Result, or a string containing an error
|
||||
*/
|
||||
public static function yieldAtMaturity($settlement, $maturity, $issue, $rate, $price, $basis = 0)
|
||||
{
|
||||
$settlement = Functions::flattenSingleValue($settlement);
|
||||
$maturity = Functions::flattenSingleValue($maturity);
|
||||
$issue = Functions::flattenSingleValue($issue);
|
||||
$rate = Functions::flattenSingleValue($rate);
|
||||
$price = Functions::flattenSingleValue($price);
|
||||
$basis = Functions::flattenSingleValue($basis);
|
||||
|
||||
try {
|
||||
$settlement = self::validateSettlementDate($settlement);
|
||||
$maturity = self::validateMaturityDate($maturity);
|
||||
self::validateSecurityPeriod($settlement, $maturity);
|
||||
$issue = self::validateIssueDate($issue);
|
||||
$rate = self::validateRate($rate);
|
||||
$price = self::validatePrice($price);
|
||||
$basis = self::validateBasis($basis);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
$daysPerYear = Helpers::daysPerYear(DateTime::YEAR($settlement), $basis);
|
||||
if (!is_numeric($daysPerYear)) {
|
||||
return $daysPerYear;
|
||||
}
|
||||
$daysBetweenIssueAndSettlement = DateTime::YEARFRAC($issue, $settlement, $basis);
|
||||
if (!is_numeric($daysBetweenIssueAndSettlement)) {
|
||||
// return date error
|
||||
return $daysBetweenIssueAndSettlement;
|
||||
}
|
||||
$daysBetweenIssueAndSettlement *= $daysPerYear;
|
||||
$daysBetweenIssueAndMaturity = DateTime::YEARFRAC($issue, $maturity, $basis);
|
||||
if (!is_numeric($daysBetweenIssueAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenIssueAndMaturity;
|
||||
}
|
||||
$daysBetweenIssueAndMaturity *= $daysPerYear;
|
||||
$daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, $basis);
|
||||
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
|
||||
// return date error
|
||||
return $daysBetweenSettlementAndMaturity;
|
||||
}
|
||||
$daysBetweenSettlementAndMaturity *= $daysPerYear;
|
||||
|
||||
return ((1 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate) - (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) /
|
||||
(($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) *
|
||||
($daysPerYear / $daysBetweenSettlementAndMaturity);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,30 @@ return [
|
|||
776,
|
||||
2400, '2008-08-19', '2008-12-31', 300, 1, 0.15, 1,
|
||||
],
|
||||
[
|
||||
820,
|
||||
2400, '2008-08-19', '2008-12-31', 300, 1, 0.2, 1,
|
||||
],
|
||||
[
|
||||
492,
|
||||
2400, '2008-08-19', '2008-12-31', 300, 2, 0.2, 1,
|
||||
],
|
||||
[
|
||||
886,
|
||||
2400, '2008-08-19', '2008-12-31', 300, 1, 0.22, 1,
|
||||
],
|
||||
[
|
||||
949,
|
||||
2400, '2008-08-19', '2008-12-31', 300, 1, 0.24, 1,
|
||||
],
|
||||
[
|
||||
494,
|
||||
2400, '2008-08-19', '2008-12-31', 300, 2, 0.24, 1,
|
||||
],
|
||||
[
|
||||
902,
|
||||
2400, '2008-08-19', '2008-12-31', 300, 1, 0.3, 1,
|
||||
],
|
||||
[
|
||||
42,
|
||||
150, '2011-01-01', '2011-09-30', 20, 1, 0.2, 4,
|
||||
|
|
@ -27,4 +51,8 @@ return [
|
|||
'#VALUE!',
|
||||
550, 'notADate', '2020-12-25', 20, 1, 0.2, 4,
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
550, '2011-01-01', 'notADate', 20, 1, 0.2, 4,
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -5,26 +5,30 @@
|
|||
return [
|
||||
[
|
||||
360,
|
||||
2400, '2008-08-19', '2008-12-31', 300, 1, 0.14999999999999999, 1,
|
||||
2400, '2008-08-19', '2008-12-31', 300, 1, 0.15, 1,
|
||||
],
|
||||
[
|
||||
576,
|
||||
2400, '2008-08-19', '2008-12-31', 300, 2, 0.24, 1,
|
||||
],
|
||||
[
|
||||
30,
|
||||
150, '2011-01-01', '2011-09-30', 20, 1, 0.20000000000000001, 4,
|
||||
150, '2011-01-01', '2011-09-30', 20, 1, 0.2, 4,
|
||||
],
|
||||
[
|
||||
22.41666667,
|
||||
150, '2011-01-01', '2011-09-30', 20, 0, 0.20000000000000001, 4,
|
||||
150, '2011-01-01', '2011-09-30', 20, 0, 0.2, 4,
|
||||
],
|
||||
[
|
||||
17.58333333,
|
||||
150, '2011-01-01', '2011-09-30', 20, 4, 0.20000000000000001, 4,
|
||||
150, '2011-01-01', '2011-09-30', 20, 4, 0.2, 4,
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
150, '2011-01-01', '2011-09-30', 20, 5, 0.20000000000000001, 4,
|
||||
150, '2011-01-01', '2011-09-30', 20, 5, 0.2, 4,
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
150, 'notADate', '2011-09-30', 20, 1, 0.20000000000000001, 4,
|
||||
150, 'notADate', '2011-09-30', 20, 1, 0.2, 4,
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -115,6 +115,47 @@ return [
|
|||
6,
|
||||
6,
|
||||
],
|
||||
[
|
||||
4651.199,
|
||||
12000,
|
||||
2000,
|
||||
3.5,
|
||||
2,
|
||||
1,
|
||||
],
|
||||
[
|
||||
3521.399,
|
||||
12000,
|
||||
2000,
|
||||
5,
|
||||
2.5,
|
||||
1,
|
||||
],
|
||||
[
|
||||
3521.399,
|
||||
12000,
|
||||
2000,
|
||||
5,
|
||||
2.5,
|
||||
1.2,
|
||||
],
|
||||
// Period value between 0 and 1 not yet handled in code
|
||||
// [
|
||||
// 301.0,
|
||||
// 12000,
|
||||
// 2000,
|
||||
// 5,
|
||||
// 0.5,
|
||||
// 1,
|
||||
// ],
|
||||
[
|
||||
-554.116,
|
||||
12000,
|
||||
15000,
|
||||
5,
|
||||
2,
|
||||
1,
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
-1000,
|
||||
|
|
@ -125,10 +166,82 @@ return [
|
|||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
'ABC',
|
||||
100,
|
||||
'Invalid',
|
||||
1000,
|
||||
5,
|
||||
6,
|
||||
6,
|
||||
2,
|
||||
1,
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
12000,
|
||||
'Invalid',
|
||||
5,
|
||||
2,
|
||||
1,
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
12000,
|
||||
1000,
|
||||
'Invalid',
|
||||
2,
|
||||
1,
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
12000,
|
||||
1000,
|
||||
5,
|
||||
'Invalid',
|
||||
1,
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
12000,
|
||||
1000,
|
||||
5,
|
||||
2,
|
||||
'Invalid',
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
-12000,
|
||||
1000,
|
||||
5,
|
||||
2,
|
||||
1,
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
12000,
|
||||
-1000,
|
||||
5,
|
||||
2,
|
||||
1,
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
12000,
|
||||
1000,
|
||||
5,
|
||||
0,
|
||||
1,
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
12000,
|
||||
1000,
|
||||
5,
|
||||
-2,
|
||||
1,
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
12000,
|
||||
1000,
|
||||
5,
|
||||
2,
|
||||
0,
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -98,6 +98,47 @@ return [
|
|||
5,
|
||||
5,
|
||||
],
|
||||
[
|
||||
972.0,
|
||||
12000,
|
||||
1000,
|
||||
5,
|
||||
3,
|
||||
0.5,
|
||||
],
|
||||
[
|
||||
1259.4752186588921,
|
||||
12000,
|
||||
1000,
|
||||
3.5,
|
||||
3,
|
||||
0.5,
|
||||
],
|
||||
[
|
||||
1080.00,
|
||||
12000,
|
||||
1000,
|
||||
5,
|
||||
2,
|
||||
0.5,
|
||||
],
|
||||
[
|
||||
0.0,
|
||||
12000,
|
||||
15000,
|
||||
5,
|
||||
2,
|
||||
0.5,
|
||||
],
|
||||
// Code does not yet handle fractional period values for DDB, only integer
|
||||
// [
|
||||
// 1024.58,
|
||||
// 12000,
|
||||
// 1000,
|
||||
// 5,
|
||||
// 2.5,
|
||||
// 0.5,
|
||||
// ],
|
||||
[
|
||||
'#NUM!',
|
||||
-2400,
|
||||
|
|
@ -112,4 +153,76 @@ return [
|
|||
36500,
|
||||
1,
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
12000,
|
||||
'INVALID',
|
||||
5,
|
||||
3,
|
||||
0.5,
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
12000,
|
||||
1000,
|
||||
'INVALID',
|
||||
3,
|
||||
0.5,
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
12000,
|
||||
1000,
|
||||
5,
|
||||
'INVALID',
|
||||
0.5,
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
12000,
|
||||
1000,
|
||||
5,
|
||||
3,
|
||||
'INVALID',
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
12000,
|
||||
-1000,
|
||||
5,
|
||||
3,
|
||||
0.5,
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
12000,
|
||||
1000,
|
||||
5,
|
||||
-3,
|
||||
0.5,
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
12000,
|
||||
1000,
|
||||
5,
|
||||
3,
|
||||
-0.5,
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
12000,
|
||||
1000,
|
||||
5,
|
||||
0,
|
||||
0.5,
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
12000,
|
||||
1000,
|
||||
2,
|
||||
3,
|
||||
0.5,
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -30,11 +30,39 @@ return [
|
|||
[45000, 7500, 10],
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
[10000, 1000, -1],
|
||||
-10500,
|
||||
[12000, 1500, -1],
|
||||
],
|
||||
[
|
||||
21000,
|
||||
[12000, 1500, 0.5],
|
||||
],
|
||||
[
|
||||
3250,
|
||||
[12000, -1000, 4],
|
||||
],
|
||||
[
|
||||
-250,
|
||||
[0, 1000, 4],
|
||||
],
|
||||
[
|
||||
-600,
|
||||
[12000, 15000, 5],
|
||||
],
|
||||
[
|
||||
'#DIV/0!',
|
||||
[12000, 1500, 0],
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
['INVALID', 1000, -1],
|
||||
['INVALID', 1000, 1],
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
[12000, 'INVALID', 1],
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
[12000, 1000, 'INVALID'],
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -33,6 +33,30 @@ return [
|
|||
409.09090909090907,
|
||||
[30000, 7500, 10, 10],
|
||||
],
|
||||
[
|
||||
-800,
|
||||
[-2000, 1000, 5, 2],
|
||||
],
|
||||
[
|
||||
3771.4285714285716,
|
||||
[12000, 1000, 2.5, 2],
|
||||
],
|
||||
[
|
||||
5028.571428571428,
|
||||
[12000, 1000, 2.5, 1.5],
|
||||
],
|
||||
[
|
||||
-600,
|
||||
[-2000, 1000, 5, 3],
|
||||
],
|
||||
[
|
||||
-800,
|
||||
[12000, 15000, 5, 2],
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
[12000, -1000, 5, 3],
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
[10000, 1000, 5, 10],
|
||||
|
|
@ -41,4 +65,32 @@ return [
|
|||
'#VALUE!',
|
||||
['INVALID', 1000, 5, 1],
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
[12000, 'INVALID', 5, 1],
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
[12000, 1000, 'INVALID', 1],
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
[12000, 1000, 5, 'INVALID'],
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
[12000, -1, 5, 2],
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
[12000, 1000, -5, 1],
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
[12000, 1000, 5, 0],
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
[12000, 1000, 5, -1],
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -9,4 +9,28 @@ return [
|
|||
0.06220123250590336,
|
||||
'1-Jan-2017', '30-Jun-2017', 97, 100,
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
'Invalid', '30-Jun-2017', 97, 100,
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
'1-Jan-2017', 'Invalid', 97, 100,
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
'1-Jan-2017', '30-Jun-2017', 'NaN', 100,
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
'1-Jan-2017', '30-Jun-2017', 97, 'NaN',
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
'1-Jan-2017', '30-Jun-2017', -97, 100,
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
'1-Jan-2017', '30-Jun-2017', 97, -100,
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -9,4 +9,32 @@ return [
|
|||
0.04210977320221025,
|
||||
'1-Jan-2017', '30-Jun-2018', '01-Jul-2014', 0.055, 101,
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
'Invalid', '30-Jun-2018', '01-Jul-2014', 0.055, 101,
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
'1-Jan-2017', 'Invalid', '01-Jul-2014', 0.055, 101,
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
'1-Jan-2017', '30-Jun-2018', 'Invalid', 0.055, 101,
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
'1-Jan-2017', '30-Jun-2018', '01-Jul-2014', 'NaN', 101,
|
||||
],
|
||||
[
|
||||
'#VALUE!',
|
||||
'1-Jan-2017', '30-Jun-2018', '01-Jul-2014', 0.055, 'NaN',
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
'1-Jan-2017', '30-Jun-2018', '01-Jul-2014', -0.055, 101,
|
||||
],
|
||||
[
|
||||
'#NUM!',
|
||||
'1-Jan-2017', '30-Jun-2018', '01-Jul-2014', 0.055, -101,
|
||||
],
|
||||
];
|
||||
|
|
|
|||
Loading…
Reference in New Issue