Start work on breaking down some of the Financial Excel functions (#1941)
* Start work on breaking down some of the Financial Excel functions * Unhappy path unit tests for Treasury Bill functions * Codebase for Treasury Bills includes logic for a different days between settlement and maturity calculation for OpenOffice; but Open/Libre Office now uses the Excel days calculation, so this discrepancy between packages is no longer required * We've already converted the Settlement and Maturity dates to Excel timestamps, so there's no need to try doing it again when calculating the days between Settlement and Maturity * Add Unit Tests for the Days per Year helper function * Extract Interest Rate functions - EFFECT() and NOMINAL() - with additional validation, and unhappy path unit tests * First pass at extracting the Coupon Excel functions * Simplify the validation methods * Extended unit tests to cover all combinations of frequency and basis, including leap years Fix for COUPDAYSNC() when basis is US 360 and settlement date is the last day of the month * Ensure that all Financial function code uses the new Helpers class for Days Per Year
This commit is contained in:
parent
4e8a926cb4
commit
d346318c2b
|
|
@ -650,32 +650,32 @@ class Calculation
|
||||||
],
|
],
|
||||||
'COUPDAYBS' => [
|
'COUPDAYBS' => [
|
||||||
'category' => Category::CATEGORY_FINANCIAL,
|
'category' => Category::CATEGORY_FINANCIAL,
|
||||||
'functionCall' => [Financial::class, 'COUPDAYBS'],
|
'functionCall' => [Financial\Coupons::class, 'COUPDAYBS'],
|
||||||
'argumentCount' => '3,4',
|
'argumentCount' => '3,4',
|
||||||
],
|
],
|
||||||
'COUPDAYS' => [
|
'COUPDAYS' => [
|
||||||
'category' => Category::CATEGORY_FINANCIAL,
|
'category' => Category::CATEGORY_FINANCIAL,
|
||||||
'functionCall' => [Financial::class, 'COUPDAYS'],
|
'functionCall' => [Financial\Coupons::class, 'COUPDAYS'],
|
||||||
'argumentCount' => '3,4',
|
'argumentCount' => '3,4',
|
||||||
],
|
],
|
||||||
'COUPDAYSNC' => [
|
'COUPDAYSNC' => [
|
||||||
'category' => Category::CATEGORY_FINANCIAL,
|
'category' => Category::CATEGORY_FINANCIAL,
|
||||||
'functionCall' => [Financial::class, 'COUPDAYSNC'],
|
'functionCall' => [Financial\Coupons::class, 'COUPDAYSNC'],
|
||||||
'argumentCount' => '3,4',
|
'argumentCount' => '3,4',
|
||||||
],
|
],
|
||||||
'COUPNCD' => [
|
'COUPNCD' => [
|
||||||
'category' => Category::CATEGORY_FINANCIAL,
|
'category' => Category::CATEGORY_FINANCIAL,
|
||||||
'functionCall' => [Financial::class, 'COUPNCD'],
|
'functionCall' => [Financial\Coupons::class, 'COUPNCD'],
|
||||||
'argumentCount' => '3,4',
|
'argumentCount' => '3,4',
|
||||||
],
|
],
|
||||||
'COUPNUM' => [
|
'COUPNUM' => [
|
||||||
'category' => Category::CATEGORY_FINANCIAL,
|
'category' => Category::CATEGORY_FINANCIAL,
|
||||||
'functionCall' => [Financial::class, 'COUPNUM'],
|
'functionCall' => [Financial\Coupons::class, 'COUPNUM'],
|
||||||
'argumentCount' => '3,4',
|
'argumentCount' => '3,4',
|
||||||
],
|
],
|
||||||
'COUPPCD' => [
|
'COUPPCD' => [
|
||||||
'category' => Category::CATEGORY_FINANCIAL,
|
'category' => Category::CATEGORY_FINANCIAL,
|
||||||
'functionCall' => [Financial::class, 'COUPPCD'],
|
'functionCall' => [Financial\Coupons::class, 'COUPPCD'],
|
||||||
'argumentCount' => '3,4',
|
'argumentCount' => '3,4',
|
||||||
],
|
],
|
||||||
'COVAR' => [
|
'COVAR' => [
|
||||||
|
|
@ -875,12 +875,12 @@ class Calculation
|
||||||
],
|
],
|
||||||
'DOLLARDE' => [
|
'DOLLARDE' => [
|
||||||
'category' => Category::CATEGORY_FINANCIAL,
|
'category' => Category::CATEGORY_FINANCIAL,
|
||||||
'functionCall' => [Financial::class, 'DOLLARDE'],
|
'functionCall' => [Financial\Dollar::class, 'decimal'],
|
||||||
'argumentCount' => '2',
|
'argumentCount' => '2',
|
||||||
],
|
],
|
||||||
'DOLLARFR' => [
|
'DOLLARFR' => [
|
||||||
'category' => Category::CATEGORY_FINANCIAL,
|
'category' => Category::CATEGORY_FINANCIAL,
|
||||||
'functionCall' => [Financial::class, 'DOLLARFR'],
|
'functionCall' => [Financial\Dollar::class, 'fractional'],
|
||||||
'argumentCount' => '2',
|
'argumentCount' => '2',
|
||||||
],
|
],
|
||||||
'DPRODUCT' => [
|
'DPRODUCT' => [
|
||||||
|
|
@ -925,7 +925,7 @@ class Calculation
|
||||||
],
|
],
|
||||||
'EFFECT' => [
|
'EFFECT' => [
|
||||||
'category' => Category::CATEGORY_FINANCIAL,
|
'category' => Category::CATEGORY_FINANCIAL,
|
||||||
'functionCall' => [Financial::class, 'EFFECT'],
|
'functionCall' => [Financial\InterestRate::class, 'effective'],
|
||||||
'argumentCount' => '2',
|
'argumentCount' => '2',
|
||||||
],
|
],
|
||||||
'ENCODEURL' => [
|
'ENCODEURL' => [
|
||||||
|
|
@ -1771,7 +1771,7 @@ class Calculation
|
||||||
],
|
],
|
||||||
'NOMINAL' => [
|
'NOMINAL' => [
|
||||||
'category' => Category::CATEGORY_FINANCIAL,
|
'category' => Category::CATEGORY_FINANCIAL,
|
||||||
'functionCall' => [Financial::class, 'NOMINAL'],
|
'functionCall' => [Financial\InterestRate::class, 'nominal'],
|
||||||
'argumentCount' => '2',
|
'argumentCount' => '2',
|
||||||
],
|
],
|
||||||
'NORMDIST' => [
|
'NORMDIST' => [
|
||||||
|
|
@ -2376,17 +2376,17 @@ class Calculation
|
||||||
],
|
],
|
||||||
'TBILLEQ' => [
|
'TBILLEQ' => [
|
||||||
'category' => Category::CATEGORY_FINANCIAL,
|
'category' => Category::CATEGORY_FINANCIAL,
|
||||||
'functionCall' => [Financial::class, 'TBILLEQ'],
|
'functionCall' => [Financial\TreasuryBill::class, 'bondEquivalentYield'],
|
||||||
'argumentCount' => '3',
|
'argumentCount' => '3',
|
||||||
],
|
],
|
||||||
'TBILLPRICE' => [
|
'TBILLPRICE' => [
|
||||||
'category' => Category::CATEGORY_FINANCIAL,
|
'category' => Category::CATEGORY_FINANCIAL,
|
||||||
'functionCall' => [Financial::class, 'TBILLPRICE'],
|
'functionCall' => [Financial\TreasuryBill::class, 'price'],
|
||||||
'argumentCount' => '3',
|
'argumentCount' => '3',
|
||||||
],
|
],
|
||||||
'TBILLYIELD' => [
|
'TBILLYIELD' => [
|
||||||
'category' => Category::CATEGORY_FINANCIAL,
|
'category' => Category::CATEGORY_FINANCIAL,
|
||||||
'functionCall' => [Financial::class, 'TBILLYIELD'],
|
'functionCall' => [Financial\TreasuryBill::class, 'yield'],
|
||||||
'argumentCount' => '3',
|
'argumentCount' => '3',
|
||||||
],
|
],
|
||||||
'TDIST' => [
|
'TDIST' => [
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
namespace PhpOffice\PhpSpreadsheet\Calculation;
|
namespace PhpOffice\PhpSpreadsheet\Calculation;
|
||||||
|
|
||||||
use PhpOffice\PhpSpreadsheet\Shared\Date;
|
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Coupons;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Calculation\Financial\InterestRate;
|
||||||
|
|
||||||
class Financial
|
class Financial
|
||||||
{
|
{
|
||||||
|
|
@ -10,41 +11,6 @@ class Financial
|
||||||
|
|
||||||
const FINANCIAL_PRECISION = 1.0e-08;
|
const FINANCIAL_PRECISION = 1.0e-08;
|
||||||
|
|
||||||
/**
|
|
||||||
* isLastDayOfMonth.
|
|
||||||
*
|
|
||||||
* Returns a boolean TRUE/FALSE indicating if this date is the last date of the month
|
|
||||||
*
|
|
||||||
* @param \DateTime $testDate The date for testing
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private static function isLastDayOfMonth(\DateTime $testDate)
|
|
||||||
{
|
|
||||||
return $testDate->format('d') == $testDate->format('t');
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function couponFirstPeriodDate($settlement, $maturity, $frequency, $next)
|
|
||||||
{
|
|
||||||
$months = 12 / $frequency;
|
|
||||||
|
|
||||||
$result = Date::excelToDateTimeObject($maturity);
|
|
||||||
$eom = self::isLastDayOfMonth($result);
|
|
||||||
|
|
||||||
while ($settlement < Date::PHPToExcel($result)) {
|
|
||||||
$result->modify('-' . $months . ' months');
|
|
||||||
}
|
|
||||||
if ($next) {
|
|
||||||
$result->modify('+' . $months . ' months');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($eom) {
|
|
||||||
$result->modify('-1 day');
|
|
||||||
}
|
|
||||||
|
|
||||||
return Date::PHPToExcel($result);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function isValidFrequency($frequency)
|
private static function isValidFrequency($frequency)
|
||||||
{
|
{
|
||||||
if (($frequency == 1) || ($frequency == 2) || ($frequency == 4)) {
|
if (($frequency == 1) || ($frequency == 2) || ($frequency == 4)) {
|
||||||
|
|
@ -54,45 +20,6 @@ class Financial
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* daysPerYear.
|
|
||||||
*
|
|
||||||
* Returns the number of days in a specified year, as defined by the "basis" value
|
|
||||||
*
|
|
||||||
* @param int|string $year The year against which we're testing
|
|
||||||
* @param int|string $basis The type of day count:
|
|
||||||
* 0 or omitted US (NASD) 360
|
|
||||||
* 1 Actual (365 or 366 in a leap year)
|
|
||||||
* 2 360
|
|
||||||
* 3 365
|
|
||||||
* 4 European 360
|
|
||||||
*
|
|
||||||
* @return int|string Result, or a string containing an error
|
|
||||||
*/
|
|
||||||
private static function daysPerYear($year, $basis = 0)
|
|
||||||
{
|
|
||||||
switch ($basis) {
|
|
||||||
case 0:
|
|
||||||
case 2:
|
|
||||||
case 4:
|
|
||||||
$daysPerYear = 360;
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
$daysPerYear = 365;
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
$daysPerYear = (DateTime::isLeapYear($year)) ? 366 : 365;
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return Functions::NAN();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $daysPerYear;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function interestAndPrincipal($rate = 0, $per = 0, $nper = 0, $pv = 0, $fv = 0, $type = 0)
|
private static function interestAndPrincipal($rate = 0, $per = 0, $nper = 0, $pv = 0, $fv = 0, $type = 0)
|
||||||
{
|
{
|
||||||
$pmt = self::PMT($rate, $nper, $pv, $fv, $type);
|
$pmt = self::PMT($rate, $nper, $pv, $fv, $type);
|
||||||
|
|
@ -369,6 +296,10 @@ class Financial
|
||||||
* Excel Function:
|
* Excel Function:
|
||||||
* COUPDAYBS(settlement,maturity,frequency[,basis])
|
* COUPDAYBS(settlement,maturity,frequency[,basis])
|
||||||
*
|
*
|
||||||
|
* @Deprecated 1.18.0
|
||||||
|
*
|
||||||
|
* @see Use the COUPDAYBS() method in the Financial\Coupons class instead
|
||||||
|
*
|
||||||
* @param mixed $settlement The security's settlement date.
|
* @param mixed $settlement The security's settlement date.
|
||||||
* The security settlement date is the date after the issue
|
* The security settlement date is the date after the issue
|
||||||
* date when the security is traded to the buyer.
|
* date when the security is traded to the buyer.
|
||||||
|
|
@ -390,34 +321,7 @@ class Financial
|
||||||
*/
|
*/
|
||||||
public static function COUPDAYBS($settlement, $maturity, $frequency, $basis = 0)
|
public static function COUPDAYBS($settlement, $maturity, $frequency, $basis = 0)
|
||||||
{
|
{
|
||||||
$settlement = Functions::flattenSingleValue($settlement);
|
return Coupons::COUPDAYBS($settlement, $maturity, $frequency, $basis);
|
||||||
$maturity = Functions::flattenSingleValue($maturity);
|
|
||||||
$frequency = (int) Functions::flattenSingleValue($frequency);
|
|
||||||
$basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
|
|
||||||
|
|
||||||
if (is_string($settlement = DateTime::getDateValue($settlement))) {
|
|
||||||
return Functions::VALUE();
|
|
||||||
}
|
|
||||||
if (is_string($maturity = DateTime::getDateValue($maturity))) {
|
|
||||||
return Functions::VALUE();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
($settlement >= $maturity) ||
|
|
||||||
(!self::isValidFrequency($frequency)) ||
|
|
||||||
(($basis < 0) || ($basis > 4))
|
|
||||||
) {
|
|
||||||
return Functions::NAN();
|
|
||||||
}
|
|
||||||
|
|
||||||
$daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
|
|
||||||
$prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, false);
|
|
||||||
|
|
||||||
if ($basis == 1) {
|
|
||||||
return abs(DateTime::DAYS($prev, $settlement));
|
|
||||||
}
|
|
||||||
|
|
||||||
return DateTime::YEARFRAC($prev, $settlement, $basis) * $daysPerYear;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -428,6 +332,10 @@ class Financial
|
||||||
* Excel Function:
|
* Excel Function:
|
||||||
* COUPDAYS(settlement,maturity,frequency[,basis])
|
* COUPDAYS(settlement,maturity,frequency[,basis])
|
||||||
*
|
*
|
||||||
|
* @Deprecated 1.18.0
|
||||||
|
*
|
||||||
|
* @see Use the COUPDAYS() method in the Financial\Coupons class instead
|
||||||
|
*
|
||||||
* @param mixed $settlement The security's settlement date.
|
* @param mixed $settlement The security's settlement date.
|
||||||
* The security settlement date is the date after the issue
|
* The security settlement date is the date after the issue
|
||||||
* date when the security is traded to the buyer.
|
* date when the security is traded to the buyer.
|
||||||
|
|
@ -449,45 +357,7 @@ class Financial
|
||||||
*/
|
*/
|
||||||
public static function COUPDAYS($settlement, $maturity, $frequency, $basis = 0)
|
public static function COUPDAYS($settlement, $maturity, $frequency, $basis = 0)
|
||||||
{
|
{
|
||||||
$settlement = Functions::flattenSingleValue($settlement);
|
return Coupons::COUPDAYS($settlement, $maturity, $frequency, $basis);
|
||||||
$maturity = Functions::flattenSingleValue($maturity);
|
|
||||||
$frequency = (int) Functions::flattenSingleValue($frequency);
|
|
||||||
$basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
|
|
||||||
|
|
||||||
if (is_string($settlement = DateTime::getDateValue($settlement))) {
|
|
||||||
return Functions::VALUE();
|
|
||||||
}
|
|
||||||
if (is_string($maturity = DateTime::getDateValue($maturity))) {
|
|
||||||
return Functions::VALUE();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
($settlement >= $maturity) ||
|
|
||||||
(!self::isValidFrequency($frequency)) ||
|
|
||||||
(($basis < 0) || ($basis > 4))
|
|
||||||
) {
|
|
||||||
return Functions::NAN();
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($basis) {
|
|
||||||
case 3:
|
|
||||||
// Actual/365
|
|
||||||
return 365 / $frequency;
|
|
||||||
case 1:
|
|
||||||
// Actual/actual
|
|
||||||
if ($frequency == 1) {
|
|
||||||
$daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
|
|
||||||
|
|
||||||
return $daysPerYear / $frequency;
|
|
||||||
}
|
|
||||||
$prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, false);
|
|
||||||
$next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
|
|
||||||
|
|
||||||
return $next - $prev;
|
|
||||||
default:
|
|
||||||
// US (NASD) 30/360, Actual/360 or European 30/360
|
|
||||||
return 360 / $frequency;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -498,6 +368,10 @@ class Financial
|
||||||
* Excel Function:
|
* Excel Function:
|
||||||
* COUPDAYSNC(settlement,maturity,frequency[,basis])
|
* COUPDAYSNC(settlement,maturity,frequency[,basis])
|
||||||
*
|
*
|
||||||
|
* @Deprecated 1.18.0
|
||||||
|
*
|
||||||
|
* @see Use the COUPDAYSNC() method in the Financial\Coupons class instead
|
||||||
|
*
|
||||||
* @param mixed $settlement The security's settlement date.
|
* @param mixed $settlement The security's settlement date.
|
||||||
* The security settlement date is the date after the issue
|
* The security settlement date is the date after the issue
|
||||||
* date when the security is traded to the buyer.
|
* date when the security is traded to the buyer.
|
||||||
|
|
@ -519,30 +393,7 @@ class Financial
|
||||||
*/
|
*/
|
||||||
public static function COUPDAYSNC($settlement, $maturity, $frequency, $basis = 0)
|
public static function COUPDAYSNC($settlement, $maturity, $frequency, $basis = 0)
|
||||||
{
|
{
|
||||||
$settlement = Functions::flattenSingleValue($settlement);
|
return Coupons::COUPDAYSNC($settlement, $maturity, $frequency, $basis);
|
||||||
$maturity = Functions::flattenSingleValue($maturity);
|
|
||||||
$frequency = (int) Functions::flattenSingleValue($frequency);
|
|
||||||
$basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
|
|
||||||
|
|
||||||
if (is_string($settlement = DateTime::getDateValue($settlement))) {
|
|
||||||
return Functions::VALUE();
|
|
||||||
}
|
|
||||||
if (is_string($maturity = DateTime::getDateValue($maturity))) {
|
|
||||||
return Functions::VALUE();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
($settlement >= $maturity) ||
|
|
||||||
(!self::isValidFrequency($frequency)) ||
|
|
||||||
(($basis < 0) || ($basis > 4))
|
|
||||||
) {
|
|
||||||
return Functions::NAN();
|
|
||||||
}
|
|
||||||
|
|
||||||
$daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
|
|
||||||
$next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
|
|
||||||
|
|
||||||
return DateTime::YEARFRAC($settlement, $next, $basis) * $daysPerYear;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -553,6 +404,10 @@ class Financial
|
||||||
* Excel Function:
|
* Excel Function:
|
||||||
* COUPNCD(settlement,maturity,frequency[,basis])
|
* COUPNCD(settlement,maturity,frequency[,basis])
|
||||||
*
|
*
|
||||||
|
* @Deprecated 1.18.0
|
||||||
|
*
|
||||||
|
* @see Use the COUPNCD() method in the Financial\Coupons class instead
|
||||||
|
*
|
||||||
* @param mixed $settlement The security's settlement date.
|
* @param mixed $settlement The security's settlement date.
|
||||||
* The security settlement date is the date after the issue
|
* The security settlement date is the date after the issue
|
||||||
* date when the security is traded to the buyer.
|
* date when the security is traded to the buyer.
|
||||||
|
|
@ -575,27 +430,7 @@ class Financial
|
||||||
*/
|
*/
|
||||||
public static function COUPNCD($settlement, $maturity, $frequency, $basis = 0)
|
public static function COUPNCD($settlement, $maturity, $frequency, $basis = 0)
|
||||||
{
|
{
|
||||||
$settlement = Functions::flattenSingleValue($settlement);
|
return Coupons::COUPNCD($settlement, $maturity, $frequency, $basis);
|
||||||
$maturity = Functions::flattenSingleValue($maturity);
|
|
||||||
$frequency = (int) Functions::flattenSingleValue($frequency);
|
|
||||||
$basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
|
|
||||||
|
|
||||||
if (is_string($settlement = DateTime::getDateValue($settlement))) {
|
|
||||||
return Functions::VALUE();
|
|
||||||
}
|
|
||||||
if (is_string($maturity = DateTime::getDateValue($maturity))) {
|
|
||||||
return Functions::VALUE();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
($settlement >= $maturity) ||
|
|
||||||
(!self::isValidFrequency($frequency)) ||
|
|
||||||
(($basis < 0) || ($basis > 4))
|
|
||||||
) {
|
|
||||||
return Functions::NAN();
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -607,6 +442,10 @@ class Financial
|
||||||
* Excel Function:
|
* Excel Function:
|
||||||
* COUPNUM(settlement,maturity,frequency[,basis])
|
* COUPNUM(settlement,maturity,frequency[,basis])
|
||||||
*
|
*
|
||||||
|
* @Deprecated 1.18.0
|
||||||
|
*
|
||||||
|
* @see Use the COUPNUM() method in the Financial\Coupons class instead
|
||||||
|
*
|
||||||
* @param mixed $settlement The security's settlement date.
|
* @param mixed $settlement The security's settlement date.
|
||||||
* The security settlement date is the date after the issue
|
* The security settlement date is the date after the issue
|
||||||
* date when the security is traded to the buyer.
|
* date when the security is traded to the buyer.
|
||||||
|
|
@ -628,29 +467,7 @@ class Financial
|
||||||
*/
|
*/
|
||||||
public static function COUPNUM($settlement, $maturity, $frequency, $basis = 0)
|
public static function COUPNUM($settlement, $maturity, $frequency, $basis = 0)
|
||||||
{
|
{
|
||||||
$settlement = Functions::flattenSingleValue($settlement);
|
return Coupons::COUPNUM($settlement, $maturity, $frequency, $basis);
|
||||||
$maturity = Functions::flattenSingleValue($maturity);
|
|
||||||
$frequency = (int) Functions::flattenSingleValue($frequency);
|
|
||||||
$basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
|
|
||||||
|
|
||||||
if (is_string($settlement = DateTime::getDateValue($settlement))) {
|
|
||||||
return Functions::VALUE();
|
|
||||||
}
|
|
||||||
if (is_string($maturity = DateTime::getDateValue($maturity))) {
|
|
||||||
return Functions::VALUE();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
($settlement >= $maturity) ||
|
|
||||||
(!self::isValidFrequency($frequency)) ||
|
|
||||||
(($basis < 0) || ($basis > 4))
|
|
||||||
) {
|
|
||||||
return Functions::NAN();
|
|
||||||
}
|
|
||||||
|
|
||||||
$yearsBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, 0);
|
|
||||||
|
|
||||||
return ceil($yearsBetweenSettlementAndMaturity * $frequency);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -661,6 +478,10 @@ class Financial
|
||||||
* Excel Function:
|
* Excel Function:
|
||||||
* COUPPCD(settlement,maturity,frequency[,basis])
|
* COUPPCD(settlement,maturity,frequency[,basis])
|
||||||
*
|
*
|
||||||
|
* @Deprecated 1.18.0
|
||||||
|
*
|
||||||
|
* @see Use the COUPPCD() method in the Financial\Coupons class instead
|
||||||
|
*
|
||||||
* @param mixed $settlement The security's settlement date.
|
* @param mixed $settlement The security's settlement date.
|
||||||
* The security settlement date is the date after the issue
|
* The security settlement date is the date after the issue
|
||||||
* date when the security is traded to the buyer.
|
* date when the security is traded to the buyer.
|
||||||
|
|
@ -683,27 +504,7 @@ class Financial
|
||||||
*/
|
*/
|
||||||
public static function COUPPCD($settlement, $maturity, $frequency, $basis = 0)
|
public static function COUPPCD($settlement, $maturity, $frequency, $basis = 0)
|
||||||
{
|
{
|
||||||
$settlement = Functions::flattenSingleValue($settlement);
|
return Coupons::COUPPCD($settlement, $maturity, $frequency, $basis);
|
||||||
$maturity = Functions::flattenSingleValue($maturity);
|
|
||||||
$frequency = (int) Functions::flattenSingleValue($frequency);
|
|
||||||
$basis = ($basis === null) ? 0 : (int) Functions::flattenSingleValue($basis);
|
|
||||||
|
|
||||||
if (is_string($settlement = DateTime::getDateValue($settlement))) {
|
|
||||||
return Functions::VALUE();
|
|
||||||
}
|
|
||||||
if (is_string($maturity = DateTime::getDateValue($maturity))) {
|
|
||||||
return Functions::VALUE();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
($settlement >= $maturity) ||
|
|
||||||
(!self::isValidFrequency($frequency)) ||
|
|
||||||
(($basis < 0) || ($basis > 4))
|
|
||||||
) {
|
|
||||||
return Functions::NAN();
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::couponFirstPeriodDate($settlement, $maturity, $frequency, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -997,6 +798,10 @@ class Financial
|
||||||
* Excel Function:
|
* Excel Function:
|
||||||
* DOLLARDE(fractional_dollar,fraction)
|
* DOLLARDE(fractional_dollar,fraction)
|
||||||
*
|
*
|
||||||
|
* @Deprecated 1.18.0
|
||||||
|
*
|
||||||
|
* @see Use the decimal() method in the Financial\Dollar class instead
|
||||||
|
*
|
||||||
* @param float $fractional_dollar Fractional Dollar
|
* @param float $fractional_dollar Fractional Dollar
|
||||||
* @param int $fraction Fraction
|
* @param int $fraction Fraction
|
||||||
*
|
*
|
||||||
|
|
@ -1004,23 +809,7 @@ class Financial
|
||||||
*/
|
*/
|
||||||
public static function DOLLARDE($fractional_dollar = null, $fraction = 0)
|
public static function DOLLARDE($fractional_dollar = null, $fraction = 0)
|
||||||
{
|
{
|
||||||
$fractional_dollar = Functions::flattenSingleValue($fractional_dollar);
|
return Financial\Dollar::decimal($fractional_dollar, $fraction);
|
||||||
$fraction = (int) Functions::flattenSingleValue($fraction);
|
|
||||||
|
|
||||||
// Validate parameters
|
|
||||||
if ($fractional_dollar === null || $fraction < 0) {
|
|
||||||
return Functions::NAN();
|
|
||||||
}
|
|
||||||
if ($fraction == 0) {
|
|
||||||
return Functions::DIV0();
|
|
||||||
}
|
|
||||||
|
|
||||||
$dollars = floor($fractional_dollar);
|
|
||||||
$cents = fmod($fractional_dollar, 1);
|
|
||||||
$cents /= $fraction;
|
|
||||||
$cents *= 10 ** ceil(log10($fraction));
|
|
||||||
|
|
||||||
return $dollars + $cents;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1033,6 +822,10 @@ class Financial
|
||||||
* Excel Function:
|
* Excel Function:
|
||||||
* DOLLARFR(decimal_dollar,fraction)
|
* DOLLARFR(decimal_dollar,fraction)
|
||||||
*
|
*
|
||||||
|
* @Deprecated 1.18.0
|
||||||
|
*
|
||||||
|
* @see Use the fractional() method in the Financial\Dollar class instead
|
||||||
|
*
|
||||||
* @param float $decimal_dollar Decimal Dollar
|
* @param float $decimal_dollar Decimal Dollar
|
||||||
* @param int $fraction Fraction
|
* @param int $fraction Fraction
|
||||||
*
|
*
|
||||||
|
|
@ -1040,23 +833,7 @@ class Financial
|
||||||
*/
|
*/
|
||||||
public static function DOLLARFR($decimal_dollar = null, $fraction = 0)
|
public static function DOLLARFR($decimal_dollar = null, $fraction = 0)
|
||||||
{
|
{
|
||||||
$decimal_dollar = Functions::flattenSingleValue($decimal_dollar);
|
return Financial\Dollar::fractional($decimal_dollar, $fraction);
|
||||||
$fraction = (int) Functions::flattenSingleValue($fraction);
|
|
||||||
|
|
||||||
// Validate parameters
|
|
||||||
if ($decimal_dollar === null || $fraction < 0) {
|
|
||||||
return Functions::NAN();
|
|
||||||
}
|
|
||||||
if ($fraction == 0) {
|
|
||||||
return Functions::DIV0();
|
|
||||||
}
|
|
||||||
|
|
||||||
$dollars = floor($decimal_dollar);
|
|
||||||
$cents = fmod($decimal_dollar, 1);
|
|
||||||
$cents *= $fraction;
|
|
||||||
$cents *= 10 ** (-ceil(log10($fraction)));
|
|
||||||
|
|
||||||
return $dollars + $cents;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1068,22 +845,18 @@ class Financial
|
||||||
* Excel Function:
|
* Excel Function:
|
||||||
* EFFECT(nominal_rate,npery)
|
* EFFECT(nominal_rate,npery)
|
||||||
*
|
*
|
||||||
* @param float $nominal_rate Nominal interest rate
|
* @Deprecated 1.18.0
|
||||||
* @param int $npery Number of compounding payments per year
|
*
|
||||||
|
* @see Use the effective() method in the Financial\InterestRate class instead
|
||||||
|
*
|
||||||
|
* @param float $nominalRate Nominal interest rate
|
||||||
|
* @param int $periodsPerYear Number of compounding payments per year
|
||||||
*
|
*
|
||||||
* @return float|string
|
* @return float|string
|
||||||
*/
|
*/
|
||||||
public static function EFFECT($nominal_rate = 0, $npery = 0)
|
public static function EFFECT($nominalRate = 0, $periodsPerYear = 0)
|
||||||
{
|
{
|
||||||
$nominal_rate = Functions::flattenSingleValue($nominal_rate);
|
return Financial\InterestRate::effective($nominalRate, $periodsPerYear);
|
||||||
$npery = (int) Functions::flattenSingleValue($npery);
|
|
||||||
|
|
||||||
// Validate parameters
|
|
||||||
if ($nominal_rate <= 0 || $npery < 1) {
|
|
||||||
return Functions::NAN();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (1 + $nominal_rate / $npery) ** $npery - 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1412,23 +1185,21 @@ class Financial
|
||||||
*
|
*
|
||||||
* Returns the nominal interest rate given the effective rate and the number of compounding payments per year.
|
* Returns the nominal interest rate given the effective rate and the number of compounding payments per year.
|
||||||
*
|
*
|
||||||
* @param float $effect_rate Effective interest rate
|
* Excel Function:
|
||||||
* @param int $npery Number of compounding payments per year
|
* NOMINAL(effect_rate, npery)
|
||||||
|
*
|
||||||
|
* @Deprecated 1.18.0
|
||||||
|
*
|
||||||
|
* @see Use the nominal() method in the Financial\InterestRate class instead
|
||||||
|
*
|
||||||
|
* @param float $effectiveRate Effective interest rate
|
||||||
|
* @param int $periodsPerYear Number of compounding payments per year
|
||||||
*
|
*
|
||||||
* @return float|string Result, or a string containing an error
|
* @return float|string Result, or a string containing an error
|
||||||
*/
|
*/
|
||||||
public static function NOMINAL($effect_rate = 0, $npery = 0)
|
public static function NOMINAL($effectiveRate = 0, $periodsPerYear = 0)
|
||||||
{
|
{
|
||||||
$effect_rate = Functions::flattenSingleValue($effect_rate);
|
return InterestRate::nominal($effectiveRate, $periodsPerYear);
|
||||||
$npery = (int) Functions::flattenSingleValue($npery);
|
|
||||||
|
|
||||||
// Validate parameters
|
|
||||||
if ($effect_rate <= 0 || $npery < 1) {
|
|
||||||
return Functions::NAN();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate
|
|
||||||
return $npery * (($effect_rate + 1) ** (1 / $npery) - 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1754,7 +1525,7 @@ class Financial
|
||||||
if (($rate <= 0) || ($yield <= 0)) {
|
if (($rate <= 0) || ($yield <= 0)) {
|
||||||
return Functions::NAN();
|
return Functions::NAN();
|
||||||
}
|
}
|
||||||
$daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
|
$daysPerYear = Financial\Helpers::daysPerYear(DateTime::YEAR($settlement), $basis);
|
||||||
if (!is_numeric($daysPerYear)) {
|
if (!is_numeric($daysPerYear)) {
|
||||||
return $daysPerYear;
|
return $daysPerYear;
|
||||||
}
|
}
|
||||||
|
|
@ -2030,6 +1801,10 @@ class Financial
|
||||||
*
|
*
|
||||||
* Returns the bond-equivalent yield for a Treasury bill.
|
* Returns the bond-equivalent yield for a Treasury bill.
|
||||||
*
|
*
|
||||||
|
* @Deprecated 1.18.0
|
||||||
|
*
|
||||||
|
* @see Use the bondEquivalentYield() method in the Financial\TreasuryBill class instead
|
||||||
|
*
|
||||||
* @param mixed $settlement The Treasury bill's settlement date.
|
* @param mixed $settlement The Treasury bill's settlement date.
|
||||||
* The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
|
* The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
|
||||||
* @param mixed $maturity The Treasury bill's maturity date.
|
* @param mixed $maturity The Treasury bill's maturity date.
|
||||||
|
|
@ -2040,37 +1815,21 @@ class Financial
|
||||||
*/
|
*/
|
||||||
public static function TBILLEQ($settlement, $maturity, $discount)
|
public static function TBILLEQ($settlement, $maturity, $discount)
|
||||||
{
|
{
|
||||||
$settlement = Functions::flattenSingleValue($settlement);
|
return Financial\TreasuryBill::bondEquivalentYield($settlement, $maturity, $discount);
|
||||||
$maturity = Functions::flattenSingleValue($maturity);
|
|
||||||
$discount = Functions::flattenSingleValue($discount);
|
|
||||||
|
|
||||||
// Use TBILLPRICE for validation
|
|
||||||
$testValue = self::TBILLPRICE($settlement, $maturity, $discount);
|
|
||||||
if (is_string($testValue)) {
|
|
||||||
return $testValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_string($maturity = DateTime::getDateValue($maturity))) {
|
|
||||||
return Functions::VALUE();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
|
|
||||||
++$maturity;
|
|
||||||
$daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity) * 360;
|
|
||||||
} else {
|
|
||||||
$daysBetweenSettlementAndMaturity = (DateTime::getDateValue($maturity) - DateTime::getDateValue($settlement));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (365 * $discount) / (360 - $discount * $daysBetweenSettlementAndMaturity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TBILLPRICE.
|
* TBILLPRICE.
|
||||||
*
|
*
|
||||||
* Returns the yield for a Treasury bill.
|
* Returns the price per $100 face value for a Treasury bill.
|
||||||
|
*
|
||||||
|
* @Deprecated 1.18.0
|
||||||
|
*
|
||||||
|
* @see Use the price() method in the Financial\TreasuryBill class instead
|
||||||
*
|
*
|
||||||
* @param mixed $settlement The Treasury bill's settlement date.
|
* @param mixed $settlement The Treasury bill's settlement date.
|
||||||
* The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
|
* The Treasury bill's settlement date is the date after the issue date
|
||||||
|
* when the Treasury bill is traded to the buyer.
|
||||||
* @param mixed $maturity The Treasury bill's maturity date.
|
* @param mixed $maturity The Treasury bill's maturity date.
|
||||||
* The maturity date is the date when the Treasury bill expires.
|
* The maturity date is the date when the Treasury bill expires.
|
||||||
* @param int $discount The Treasury bill's discount rate
|
* @param int $discount The Treasury bill's discount rate
|
||||||
|
|
@ -2079,44 +1838,7 @@ class Financial
|
||||||
*/
|
*/
|
||||||
public static function TBILLPRICE($settlement, $maturity, $discount)
|
public static function TBILLPRICE($settlement, $maturity, $discount)
|
||||||
{
|
{
|
||||||
$settlement = Functions::flattenSingleValue($settlement);
|
return Financial\TreasuryBill::price($settlement, $maturity, $discount);
|
||||||
$maturity = Functions::flattenSingleValue($maturity);
|
|
||||||
$discount = Functions::flattenSingleValue($discount);
|
|
||||||
|
|
||||||
if (is_string($maturity = DateTime::getDateValue($maturity))) {
|
|
||||||
return Functions::VALUE();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate
|
|
||||||
if (is_numeric($discount)) {
|
|
||||||
if ($discount <= 0) {
|
|
||||||
return Functions::NAN();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
|
|
||||||
++$maturity;
|
|
||||||
$daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity) * 360;
|
|
||||||
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
|
|
||||||
// return date error
|
|
||||||
return $daysBetweenSettlementAndMaturity;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$daysBetweenSettlementAndMaturity = (DateTime::getDateValue($maturity) - DateTime::getDateValue($settlement));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($daysBetweenSettlementAndMaturity > self::daysPerYear(DateTime::YEAR($maturity), 1)) {
|
|
||||||
return Functions::NAN();
|
|
||||||
}
|
|
||||||
|
|
||||||
$price = 100 * (1 - (($discount * $daysBetweenSettlementAndMaturity) / 360));
|
|
||||||
if ($price <= 0) {
|
|
||||||
return Functions::NAN();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $price;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Functions::VALUE();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -2124,8 +1846,13 @@ class Financial
|
||||||
*
|
*
|
||||||
* Returns the yield for a Treasury bill.
|
* Returns the yield for a Treasury bill.
|
||||||
*
|
*
|
||||||
|
* @Deprecated 1.18.0
|
||||||
|
*
|
||||||
|
* @see Use the yield() method in the Financial\TreasuryBill class instead
|
||||||
|
*
|
||||||
* @param mixed $settlement The Treasury bill's settlement date.
|
* @param mixed $settlement The Treasury bill's settlement date.
|
||||||
* The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
|
* The Treasury bill's settlement date is the date after the issue date
|
||||||
|
* when the Treasury bill is traded to the buyer.
|
||||||
* @param mixed $maturity The Treasury bill's maturity date.
|
* @param mixed $maturity The Treasury bill's maturity date.
|
||||||
* The maturity date is the date when the Treasury bill expires.
|
* The maturity date is the date when the Treasury bill expires.
|
||||||
* @param int $price The Treasury bill's price per $100 face value
|
* @param int $price The Treasury bill's price per $100 face value
|
||||||
|
|
@ -2134,35 +1861,7 @@ class Financial
|
||||||
*/
|
*/
|
||||||
public static function TBILLYIELD($settlement, $maturity, $price)
|
public static function TBILLYIELD($settlement, $maturity, $price)
|
||||||
{
|
{
|
||||||
$settlement = Functions::flattenSingleValue($settlement);
|
return Financial\TreasuryBill::yield($settlement, $maturity, $price);
|
||||||
$maturity = Functions::flattenSingleValue($maturity);
|
|
||||||
$price = Functions::flattenSingleValue($price);
|
|
||||||
|
|
||||||
// Validate
|
|
||||||
if (is_numeric($price)) {
|
|
||||||
if ($price <= 0) {
|
|
||||||
return Functions::NAN();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
|
|
||||||
++$maturity;
|
|
||||||
$daysBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity) * 360;
|
|
||||||
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
|
|
||||||
// return date error
|
|
||||||
return $daysBetweenSettlementAndMaturity;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$daysBetweenSettlementAndMaturity = (DateTime::getDateValue($maturity) - DateTime::getDateValue($settlement));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($daysBetweenSettlementAndMaturity > 360) {
|
|
||||||
return Functions::NAN();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ((100 - $price) / $price) * (360 / $daysBetweenSettlementAndMaturity);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Functions::VALUE();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function bothNegAndPos($neg, $pos)
|
private static function bothNegAndPos($neg, $pos)
|
||||||
|
|
@ -2407,7 +2106,7 @@ class Financial
|
||||||
if (($price <= 0) || ($redemption <= 0)) {
|
if (($price <= 0) || ($redemption <= 0)) {
|
||||||
return Functions::NAN();
|
return Functions::NAN();
|
||||||
}
|
}
|
||||||
$daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
|
$daysPerYear = Financial\Helpers::daysPerYear(DateTime::YEAR($settlement), $basis);
|
||||||
if (!is_numeric($daysPerYear)) {
|
if (!is_numeric($daysPerYear)) {
|
||||||
return $daysPerYear;
|
return $daysPerYear;
|
||||||
}
|
}
|
||||||
|
|
@ -2459,7 +2158,7 @@ class Financial
|
||||||
if (($rate <= 0) || ($price <= 0)) {
|
if (($rate <= 0) || ($price <= 0)) {
|
||||||
return Functions::NAN();
|
return Functions::NAN();
|
||||||
}
|
}
|
||||||
$daysPerYear = self::daysPerYear(DateTime::YEAR($settlement), $basis);
|
$daysPerYear = Financial\Helpers::daysPerYear(DateTime::YEAR($settlement), $basis);
|
||||||
if (!is_numeric($daysPerYear)) {
|
if (!is_numeric($daysPerYear)) {
|
||||||
return $daysPerYear;
|
return $daysPerYear;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,435 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Calculation\DateTime;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Shared\Date;
|
||||||
|
|
||||||
|
class Coupons
|
||||||
|
{
|
||||||
|
public const FREQUENCY_ANNUAL = 1;
|
||||||
|
public const FREQUENCY_SEMI_ANNUAL = 2;
|
||||||
|
public const FREQUENCY_QUARTERLY = 4;
|
||||||
|
|
||||||
|
private const PERIOD_DATE_PREVIOUS = false;
|
||||||
|
private const PERIOD_DATE_NEXT = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* COUPDAYBS.
|
||||||
|
*
|
||||||
|
* Returns the number of days from the beginning of the coupon period to the settlement date.
|
||||||
|
*
|
||||||
|
* Excel Function:
|
||||||
|
* COUPDAYBS(settlement,maturity,frequency[,basis])
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
* @param mixed $maturity The security's maturity date.
|
||||||
|
* The maturity date is the date when the security expires.
|
||||||
|
* @param int $frequency the number of coupon payments per year.
|
||||||
|
* Valid frequency values are:
|
||||||
|
* 1 Annual
|
||||||
|
* 2 Semi-Annual
|
||||||
|
* 4 Quarterly
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
public static function COUPDAYBS($settlement, $maturity, $frequency, $basis = Helpers::DAYS_PER_YEAR_NASD)
|
||||||
|
{
|
||||||
|
$settlement = Functions::flattenSingleValue($settlement);
|
||||||
|
$maturity = Functions::flattenSingleValue($maturity);
|
||||||
|
$frequency = Functions::flattenSingleValue($frequency);
|
||||||
|
$basis = ($basis === null) ? 0 : Functions::flattenSingleValue($basis);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$settlement = self::validateSettlementDate($settlement);
|
||||||
|
$maturity = self::validateMaturityDate($maturity);
|
||||||
|
self::validateCouponPeriod($settlement, $maturity);
|
||||||
|
$frequency = self::validateFrequency($frequency);
|
||||||
|
$basis = self::validateBasis($basis);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
$daysPerYear = Helpers::daysPerYear(DateTime::YEAR($settlement), $basis);
|
||||||
|
$prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_PREVIOUS);
|
||||||
|
|
||||||
|
if ($basis === Helpers::DAYS_PER_YEAR_ACTUAL) {
|
||||||
|
return abs(DateTime::DAYS($prev, $settlement));
|
||||||
|
}
|
||||||
|
|
||||||
|
return DateTime::YEARFRAC($prev, $settlement, $basis) * $daysPerYear;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* COUPDAYS.
|
||||||
|
*
|
||||||
|
* Returns the number of days in the coupon period that contains the settlement date.
|
||||||
|
*
|
||||||
|
* Excel Function:
|
||||||
|
* COUPDAYS(settlement,maturity,frequency[,basis])
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
* @param mixed $maturity The security's maturity date.
|
||||||
|
* The maturity date is the date when the security expires.
|
||||||
|
* @param mixed $frequency the number of coupon payments per year.
|
||||||
|
* Valid frequency values are:
|
||||||
|
* 1 Annual
|
||||||
|
* 2 Semi-Annual
|
||||||
|
* 4 Quarterly
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
public static function COUPDAYS($settlement, $maturity, $frequency, $basis = Helpers::DAYS_PER_YEAR_NASD)
|
||||||
|
{
|
||||||
|
$settlement = Functions::flattenSingleValue($settlement);
|
||||||
|
$maturity = Functions::flattenSingleValue($maturity);
|
||||||
|
$frequency = Functions::flattenSingleValue($frequency);
|
||||||
|
$basis = ($basis === null) ? 0 : Functions::flattenSingleValue($basis);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$settlement = self::validateSettlementDate($settlement);
|
||||||
|
$maturity = self::validateMaturityDate($maturity);
|
||||||
|
self::validateCouponPeriod($settlement, $maturity);
|
||||||
|
$frequency = self::validateFrequency($frequency);
|
||||||
|
$basis = self::validateBasis($basis);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($basis) {
|
||||||
|
case Helpers::DAYS_PER_YEAR_365:
|
||||||
|
// Actual/365
|
||||||
|
return 365 / $frequency;
|
||||||
|
case Helpers::DAYS_PER_YEAR_ACTUAL:
|
||||||
|
// Actual/actual
|
||||||
|
if ($frequency == self::FREQUENCY_ANNUAL) {
|
||||||
|
$daysPerYear = Helpers::daysPerYear(DateTime::YEAR($settlement), $basis);
|
||||||
|
|
||||||
|
return $daysPerYear / $frequency;
|
||||||
|
}
|
||||||
|
$prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_PREVIOUS);
|
||||||
|
$next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_NEXT);
|
||||||
|
|
||||||
|
return $next - $prev;
|
||||||
|
default:
|
||||||
|
// US (NASD) 30/360, Actual/360 or European 30/360
|
||||||
|
return 360 / $frequency;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* COUPDAYSNC.
|
||||||
|
*
|
||||||
|
* Returns the number of days from the settlement date to the next coupon date.
|
||||||
|
*
|
||||||
|
* Excel Function:
|
||||||
|
* COUPDAYSNC(settlement,maturity,frequency[,basis])
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
* @param mixed $maturity The security's maturity date.
|
||||||
|
* The maturity date is the date when the security expires.
|
||||||
|
* @param mixed $frequency the number of coupon payments per year.
|
||||||
|
* Valid frequency values are:
|
||||||
|
* 1 Annual
|
||||||
|
* 2 Semi-Annual
|
||||||
|
* 4 Quarterly
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
public static function COUPDAYSNC($settlement, $maturity, $frequency, $basis = Helpers::DAYS_PER_YEAR_NASD)
|
||||||
|
{
|
||||||
|
$settlement = Functions::flattenSingleValue($settlement);
|
||||||
|
$maturity = Functions::flattenSingleValue($maturity);
|
||||||
|
$frequency = Functions::flattenSingleValue($frequency);
|
||||||
|
$basis = ($basis === null) ? 0 : Functions::flattenSingleValue($basis);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$settlement = self::validateSettlementDate($settlement);
|
||||||
|
$maturity = self::validateMaturityDate($maturity);
|
||||||
|
self::validateCouponPeriod($settlement, $maturity);
|
||||||
|
$frequency = self::validateFrequency($frequency);
|
||||||
|
$basis = self::validateBasis($basis);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
$daysPerYear = Helpers::daysPerYear(DateTime::YEAR($settlement), $basis);
|
||||||
|
$next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_NEXT);
|
||||||
|
|
||||||
|
if ($basis === Helpers::DAYS_PER_YEAR_NASD) {
|
||||||
|
$settlementDate = Date::excelToDateTimeObject($settlement);
|
||||||
|
$settlementEoM = self::isLastDayOfMonth($settlementDate);
|
||||||
|
if ($settlementEoM) {
|
||||||
|
++$settlement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DateTime::YEARFRAC($settlement, $next, $basis) * $daysPerYear;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* COUPNCD.
|
||||||
|
*
|
||||||
|
* Returns the next coupon date after the settlement date.
|
||||||
|
*
|
||||||
|
* Excel Function:
|
||||||
|
* COUPNCD(settlement,maturity,frequency[,basis])
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
* @param mixed $maturity The security's maturity date.
|
||||||
|
* The maturity date is the date when the security expires.
|
||||||
|
* @param mixed $frequency the number of coupon payments per year.
|
||||||
|
* Valid frequency values are:
|
||||||
|
* 1 Annual
|
||||||
|
* 2 Semi-Annual
|
||||||
|
* 4 Quarterly
|
||||||
|
* @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 mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||||
|
* depending on the value of the ReturnDateType flag
|
||||||
|
*/
|
||||||
|
public static function COUPNCD($settlement, $maturity, $frequency, $basis = Helpers::DAYS_PER_YEAR_NASD)
|
||||||
|
{
|
||||||
|
$settlement = Functions::flattenSingleValue($settlement);
|
||||||
|
$maturity = Functions::flattenSingleValue($maturity);
|
||||||
|
$frequency = Functions::flattenSingleValue($frequency);
|
||||||
|
$basis = ($basis === null) ? 0 : Functions::flattenSingleValue($basis);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$settlement = self::validateSettlementDate($settlement);
|
||||||
|
$maturity = self::validateMaturityDate($maturity);
|
||||||
|
self::validateCouponPeriod($settlement, $maturity);
|
||||||
|
$frequency = self::validateFrequency($frequency);
|
||||||
|
$basis = self::validateBasis($basis);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_NEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* COUPNUM.
|
||||||
|
*
|
||||||
|
* Returns the number of coupons payable between the settlement date and maturity date,
|
||||||
|
* rounded up to the nearest whole coupon.
|
||||||
|
*
|
||||||
|
* Excel Function:
|
||||||
|
* COUPNUM(settlement,maturity,frequency[,basis])
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
* @param mixed $maturity The security's maturity date.
|
||||||
|
* The maturity date is the date when the security expires.
|
||||||
|
* @param mixed $frequency the number of coupon payments per year.
|
||||||
|
* Valid frequency values are:
|
||||||
|
* 1 Annual
|
||||||
|
* 2 Semi-Annual
|
||||||
|
* 4 Quarterly
|
||||||
|
* @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 int|string
|
||||||
|
*/
|
||||||
|
public static function COUPNUM($settlement, $maturity, $frequency, $basis = Helpers::DAYS_PER_YEAR_NASD)
|
||||||
|
{
|
||||||
|
$settlement = Functions::flattenSingleValue($settlement);
|
||||||
|
$maturity = Functions::flattenSingleValue($maturity);
|
||||||
|
$frequency = Functions::flattenSingleValue($frequency);
|
||||||
|
$basis = ($basis === null) ? 0 : Functions::flattenSingleValue($basis);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$settlement = self::validateSettlementDate($settlement);
|
||||||
|
$maturity = self::validateMaturityDate($maturity);
|
||||||
|
self::validateCouponPeriod($settlement, $maturity);
|
||||||
|
$frequency = self::validateFrequency($frequency);
|
||||||
|
$basis = self::validateBasis($basis);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
$yearsBetweenSettlementAndMaturity = DateTime::YEARFRAC($settlement, $maturity, 0);
|
||||||
|
|
||||||
|
return ceil($yearsBetweenSettlementAndMaturity * $frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* COUPPCD.
|
||||||
|
*
|
||||||
|
* Returns the previous coupon date before the settlement date.
|
||||||
|
*
|
||||||
|
* Excel Function:
|
||||||
|
* COUPPCD(settlement,maturity,frequency[,basis])
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
* @param mixed $maturity The security's maturity date.
|
||||||
|
* The maturity date is the date when the security expires.
|
||||||
|
* @param mixed $frequency the number of coupon payments per year.
|
||||||
|
* Valid frequency values are:
|
||||||
|
* 1 Annual
|
||||||
|
* 2 Semi-Annual
|
||||||
|
* 4 Quarterly
|
||||||
|
* @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 mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
|
||||||
|
* depending on the value of the ReturnDateType flag
|
||||||
|
*/
|
||||||
|
public static function COUPPCD($settlement, $maturity, $frequency, $basis = Helpers::DAYS_PER_YEAR_NASD)
|
||||||
|
{
|
||||||
|
$settlement = Functions::flattenSingleValue($settlement);
|
||||||
|
$maturity = Functions::flattenSingleValue($maturity);
|
||||||
|
$frequency = Functions::flattenSingleValue($frequency);
|
||||||
|
$basis = ($basis === null) ? 0 : Functions::flattenSingleValue($basis);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$settlement = self::validateSettlementDate($settlement);
|
||||||
|
$maturity = self::validateMaturityDate($maturity);
|
||||||
|
self::validateCouponPeriod($settlement, $maturity);
|
||||||
|
$frequency = self::validateFrequency($frequency);
|
||||||
|
$basis = self::validateBasis($basis);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_PREVIOUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* isLastDayOfMonth.
|
||||||
|
*
|
||||||
|
* Returns a boolean TRUE/FALSE indicating if this date is the last date of the month
|
||||||
|
*
|
||||||
|
* @param \DateTime $testDate The date for testing
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private static function isLastDayOfMonth(\DateTime $testDate)
|
||||||
|
{
|
||||||
|
return $testDate->format('d') === $testDate->format('t');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function couponFirstPeriodDate($settlement, $maturity, int $frequency, $next)
|
||||||
|
{
|
||||||
|
$months = 12 / $frequency;
|
||||||
|
|
||||||
|
$result = Date::excelToDateTimeObject($maturity);
|
||||||
|
$maturityEoM = self::isLastDayOfMonth($result);
|
||||||
|
|
||||||
|
while ($settlement < Date::PHPToExcel($result)) {
|
||||||
|
$result->modify('-' . $months . ' months');
|
||||||
|
}
|
||||||
|
if ($next === true) {
|
||||||
|
$result->modify('+' . $months . ' months');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($maturityEoM === true) {
|
||||||
|
$result->modify('-1 day');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Date::PHPToExcel($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 validateCouponPeriod($settlement, $maturity): void
|
||||||
|
{
|
||||||
|
if ($settlement >= $maturity) {
|
||||||
|
throw new Exception(Functions::NAN());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function validateFrequency($frequency): int
|
||||||
|
{
|
||||||
|
if (!is_numeric($frequency)) {
|
||||||
|
throw new Exception(Functions::NAN());
|
||||||
|
}
|
||||||
|
|
||||||
|
$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)
|
||||||
|
{
|
||||||
|
if (!is_numeric($basis)) {
|
||||||
|
throw new Exception(Functions::NAN());
|
||||||
|
}
|
||||||
|
|
||||||
|
$basis = (int) $basis;
|
||||||
|
if (($basis < 0) || ($basis > 4)) {
|
||||||
|
throw new Exception(Functions::NAN());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $basis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||||
|
|
||||||
|
class Dollar
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* DOLLARDE.
|
||||||
|
*
|
||||||
|
* Converts a dollar price expressed as an integer part and a fraction
|
||||||
|
* part into a dollar price expressed as a decimal number.
|
||||||
|
* Fractional dollar numbers are sometimes used for security prices.
|
||||||
|
*
|
||||||
|
* Excel Function:
|
||||||
|
* DOLLARDE(fractional_dollar,fraction)
|
||||||
|
*
|
||||||
|
* @param float $fractionalDollar Fractional Dollar
|
||||||
|
* @param int $fraction Fraction
|
||||||
|
*
|
||||||
|
* @return float|string
|
||||||
|
*/
|
||||||
|
public static function decimal($fractionalDollar = null, $fraction = 0)
|
||||||
|
{
|
||||||
|
$fractionalDollar = Functions::flattenSingleValue($fractionalDollar);
|
||||||
|
$fraction = (int) Functions::flattenSingleValue($fraction);
|
||||||
|
|
||||||
|
// Validate parameters
|
||||||
|
if ($fractionalDollar === null || $fraction < 0) {
|
||||||
|
return Functions::NAN();
|
||||||
|
}
|
||||||
|
if ($fraction == 0) {
|
||||||
|
return Functions::DIV0();
|
||||||
|
}
|
||||||
|
|
||||||
|
$dollars = floor($fractionalDollar);
|
||||||
|
$cents = fmod($fractionalDollar, 1);
|
||||||
|
$cents /= $fraction;
|
||||||
|
$cents *= 10 ** ceil(log10($fraction));
|
||||||
|
|
||||||
|
return $dollars + $cents;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DOLLARFR.
|
||||||
|
*
|
||||||
|
* Converts a dollar price expressed as a decimal number into a dollar price
|
||||||
|
* expressed as a fraction.
|
||||||
|
* Fractional dollar numbers are sometimes used for security prices.
|
||||||
|
*
|
||||||
|
* Excel Function:
|
||||||
|
* DOLLARFR(decimal_dollar,fraction)
|
||||||
|
*
|
||||||
|
* @param float $decimalDollar Decimal Dollar
|
||||||
|
* @param int $fraction Fraction
|
||||||
|
*
|
||||||
|
* @return float|string
|
||||||
|
*/
|
||||||
|
public static function fractional($decimalDollar = null, $fraction = 0)
|
||||||
|
{
|
||||||
|
$decimalDollar = Functions::flattenSingleValue($decimalDollar);
|
||||||
|
$fraction = (int) Functions::flattenSingleValue($fraction);
|
||||||
|
|
||||||
|
// Validate parameters
|
||||||
|
if ($decimalDollar === null || $fraction < 0) {
|
||||||
|
return Functions::NAN();
|
||||||
|
}
|
||||||
|
if ($fraction == 0) {
|
||||||
|
return Functions::DIV0();
|
||||||
|
}
|
||||||
|
|
||||||
|
$dollars = floor($decimalDollar);
|
||||||
|
$cents = fmod($decimalDollar, 1);
|
||||||
|
$cents *= $fraction;
|
||||||
|
$cents *= 10 ** (-ceil(log10($fraction)));
|
||||||
|
|
||||||
|
return $dollars + $cents;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Calculation\DateTime;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||||
|
|
||||||
|
class Helpers
|
||||||
|
{
|
||||||
|
public const DAYS_PER_YEAR_NASD = 0;
|
||||||
|
public const DAYS_PER_YEAR_ACTUAL = 1;
|
||||||
|
public const DAYS_PER_YEAR_360 = 2;
|
||||||
|
public const DAYS_PER_YEAR_365 = 3;
|
||||||
|
public const DAYS_PER_YEAR_360_EUROPEAN = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* daysPerYear.
|
||||||
|
*
|
||||||
|
* Returns the number of days in a specified year, as defined by the "basis" value
|
||||||
|
*
|
||||||
|
* @param int|string $year The year against which we're testing
|
||||||
|
* @param int|string $basis The type of day count:
|
||||||
|
* 0 or omitted US (NASD) 360
|
||||||
|
* 1 Actual (365 or 366 in a leap year)
|
||||||
|
* 2 360
|
||||||
|
* 3 365
|
||||||
|
* 4 European 360
|
||||||
|
*
|
||||||
|
* @return int|string Result, or a string containing an error
|
||||||
|
*/
|
||||||
|
public static function daysPerYear($year, $basis = 0)
|
||||||
|
{
|
||||||
|
if (!is_numeric($basis)) {
|
||||||
|
return Functions::NAN();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($basis) {
|
||||||
|
case self::DAYS_PER_YEAR_NASD:
|
||||||
|
case self::DAYS_PER_YEAR_360:
|
||||||
|
case self::DAYS_PER_YEAR_360_EUROPEAN:
|
||||||
|
return 360;
|
||||||
|
case self::DAYS_PER_YEAR_365:
|
||||||
|
return 365;
|
||||||
|
case self::DAYS_PER_YEAR_ACTUAL:
|
||||||
|
return (DateTime::isLeapYear($year)) ? 366 : 365;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Functions::NAN();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||||
|
|
||||||
|
class InterestRate
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* EFFECT.
|
||||||
|
*
|
||||||
|
* Returns the effective interest rate given the nominal rate and the number of
|
||||||
|
* compounding payments per year.
|
||||||
|
*
|
||||||
|
* Excel Function:
|
||||||
|
* EFFECT(nominal_rate,npery)
|
||||||
|
*
|
||||||
|
* @param float $nominalRate Nominal interest rate
|
||||||
|
* @param int $periodsPerYear Number of compounding payments per year
|
||||||
|
*
|
||||||
|
* @return float|string
|
||||||
|
*/
|
||||||
|
public static function effective($nominalRate = 0, $periodsPerYear = 0)
|
||||||
|
{
|
||||||
|
$nominalRate = Functions::flattenSingleValue($nominalRate);
|
||||||
|
$periodsPerYear = Functions::flattenSingleValue($periodsPerYear);
|
||||||
|
|
||||||
|
// Validate parameters
|
||||||
|
if (!is_numeric($nominalRate) || !is_numeric($periodsPerYear)) {
|
||||||
|
return Functions::VALUE();
|
||||||
|
}
|
||||||
|
if ($nominalRate <= 0 || $periodsPerYear < 1) {
|
||||||
|
return Functions::NAN();
|
||||||
|
}
|
||||||
|
|
||||||
|
$periodsPerYear = (int) $periodsPerYear;
|
||||||
|
|
||||||
|
return ((1 + $nominalRate / $periodsPerYear) ** $periodsPerYear) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOMINAL.
|
||||||
|
*
|
||||||
|
* Returns the nominal interest rate given the effective rate and the number of compounding payments per year.
|
||||||
|
*
|
||||||
|
* @param float $effectiveRate Effective interest rate
|
||||||
|
* @param int $periodsPerYear Number of compounding payments per year
|
||||||
|
*
|
||||||
|
* @return float|string Result, or a string containing an error
|
||||||
|
*/
|
||||||
|
public static function nominal($effectiveRate = 0, $periodsPerYear = 0)
|
||||||
|
{
|
||||||
|
$effectiveRate = Functions::flattenSingleValue($effectiveRate);
|
||||||
|
$periodsPerYear = Functions::flattenSingleValue($periodsPerYear);
|
||||||
|
|
||||||
|
// Validate parameters
|
||||||
|
if (!is_numeric($effectiveRate) || !is_numeric($periodsPerYear)) {
|
||||||
|
return Functions::VALUE();
|
||||||
|
}
|
||||||
|
if ($effectiveRate <= 0 || $periodsPerYear < 1) {
|
||||||
|
return Functions::NAN();
|
||||||
|
}
|
||||||
|
|
||||||
|
$periodsPerYear = (int) $periodsPerYear;
|
||||||
|
|
||||||
|
// Calculate
|
||||||
|
return $periodsPerYear * (($effectiveRate + 1) ** (1 / $periodsPerYear) - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Calculation\DateTime;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||||
|
|
||||||
|
class TreasuryBill
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* TBILLEQ.
|
||||||
|
*
|
||||||
|
* Returns the bond-equivalent yield for a Treasury bill.
|
||||||
|
*
|
||||||
|
* @param mixed $settlement The Treasury bill's settlement date.
|
||||||
|
* The Treasury bill's settlement date is the date after the issue date
|
||||||
|
* when the Treasury bill is traded to the buyer.
|
||||||
|
* @param mixed $maturity The Treasury bill's maturity date.
|
||||||
|
* The maturity date is the date when the Treasury bill expires.
|
||||||
|
* @param int $discount The Treasury bill's discount rate
|
||||||
|
*
|
||||||
|
* @return float|string Result, or a string containing an error
|
||||||
|
*/
|
||||||
|
public static function bondEquivalentYield($settlement, $maturity, $discount)
|
||||||
|
{
|
||||||
|
$settlement = Functions::flattenSingleValue($settlement);
|
||||||
|
$maturity = Functions::flattenSingleValue($maturity);
|
||||||
|
$discount = Functions::flattenSingleValue($discount);
|
||||||
|
|
||||||
|
if (
|
||||||
|
is_string($maturity = DateTime::getDateValue($maturity)) ||
|
||||||
|
is_string($settlement = DateTime::getDateValue($settlement))
|
||||||
|
) {
|
||||||
|
return Functions::VALUE();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
if (is_numeric($discount)) {
|
||||||
|
if ($discount <= 0) {
|
||||||
|
return Functions::NAN();
|
||||||
|
}
|
||||||
|
|
||||||
|
$daysBetweenSettlementAndMaturity = $maturity - $settlement;
|
||||||
|
|
||||||
|
if (
|
||||||
|
$daysBetweenSettlementAndMaturity > Helpers::daysPerYear(DateTime::YEAR($maturity), Helpers::DAYS_PER_YEAR_ACTUAL) ||
|
||||||
|
$daysBetweenSettlementAndMaturity < 0
|
||||||
|
) {
|
||||||
|
return Functions::NAN();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (365 * $discount) / (360 - $discount * $daysBetweenSettlementAndMaturity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Functions::VALUE();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TBILLPRICE.
|
||||||
|
*
|
||||||
|
* Returns the price per $100 face value for a Treasury bill.
|
||||||
|
*
|
||||||
|
* @param mixed $settlement The Treasury bill's settlement date.
|
||||||
|
* The Treasury bill's settlement date is the date after the issue date
|
||||||
|
* when the Treasury bill is traded to the buyer.
|
||||||
|
* @param mixed $maturity The Treasury bill's maturity date.
|
||||||
|
* The maturity date is the date when the Treasury bill expires.
|
||||||
|
* @param int $discount The Treasury bill's discount rate
|
||||||
|
*
|
||||||
|
* @return float|string Result, or a string containing an error
|
||||||
|
*/
|
||||||
|
public static function price($settlement, $maturity, $discount)
|
||||||
|
{
|
||||||
|
$settlement = Functions::flattenSingleValue($settlement);
|
||||||
|
$maturity = Functions::flattenSingleValue($maturity);
|
||||||
|
$discount = Functions::flattenSingleValue($discount);
|
||||||
|
|
||||||
|
if (
|
||||||
|
is_string($maturity = DateTime::getDateValue($maturity)) ||
|
||||||
|
is_string($settlement = DateTime::getDateValue($settlement))
|
||||||
|
) {
|
||||||
|
return Functions::VALUE();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
if (is_numeric($discount)) {
|
||||||
|
if ($discount <= 0) {
|
||||||
|
return Functions::NAN();
|
||||||
|
}
|
||||||
|
|
||||||
|
$daysBetweenSettlementAndMaturity = $maturity - $settlement;
|
||||||
|
|
||||||
|
if (
|
||||||
|
$daysBetweenSettlementAndMaturity > Helpers::daysPerYear(DateTime::YEAR($maturity), Helpers::DAYS_PER_YEAR_ACTUAL) ||
|
||||||
|
$daysBetweenSettlementAndMaturity < 0
|
||||||
|
) {
|
||||||
|
return Functions::NAN();
|
||||||
|
}
|
||||||
|
$price = 100 * (1 - (($discount * $daysBetweenSettlementAndMaturity) / 360));
|
||||||
|
if ($price < 0.0) {
|
||||||
|
return Functions::NAN();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $price;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Functions::VALUE();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TBILLYIELD.
|
||||||
|
*
|
||||||
|
* Returns the yield for a Treasury bill.
|
||||||
|
*
|
||||||
|
* @param mixed $settlement The Treasury bill's settlement date.
|
||||||
|
* The Treasury bill's settlement date is the date after the issue date when
|
||||||
|
* the Treasury bill is traded to the buyer.
|
||||||
|
* @param mixed $maturity The Treasury bill's maturity date.
|
||||||
|
* The maturity date is the date when the Treasury bill expires.
|
||||||
|
* @param int $price The Treasury bill's price per $100 face value
|
||||||
|
*
|
||||||
|
* @return float|string
|
||||||
|
*/
|
||||||
|
public static function yield($settlement, $maturity, $price)
|
||||||
|
{
|
||||||
|
$settlement = Functions::flattenSingleValue($settlement);
|
||||||
|
$maturity = Functions::flattenSingleValue($maturity);
|
||||||
|
$price = Functions::flattenSingleValue($price);
|
||||||
|
|
||||||
|
if (
|
||||||
|
is_string($maturity = DateTime::getDateValue($maturity)) ||
|
||||||
|
is_string($settlement = DateTime::getDateValue($settlement))
|
||||||
|
) {
|
||||||
|
return Functions::VALUE();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
if (is_numeric($price)) {
|
||||||
|
if ($price <= 0) {
|
||||||
|
return Functions::NAN();
|
||||||
|
}
|
||||||
|
|
||||||
|
$daysBetweenSettlementAndMaturity = $maturity - $settlement;
|
||||||
|
|
||||||
|
if ($daysBetweenSettlementAndMaturity > 360 || $daysBetweenSettlementAndMaturity < 0) {
|
||||||
|
return Functions::NAN();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((100 - $price) / $price) * (360 / $daysBetweenSettlementAndMaturity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Functions::VALUE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,10 +17,12 @@ class EffectTest extends TestCase
|
||||||
* @dataProvider providerEFFECT
|
* @dataProvider providerEFFECT
|
||||||
*
|
*
|
||||||
* @param mixed $expectedResult
|
* @param mixed $expectedResult
|
||||||
|
* @param mixed $rate
|
||||||
|
* @param mixed $periods
|
||||||
*/
|
*/
|
||||||
public function testEFFECT($expectedResult, ...$args): void
|
public function testEFFECT($expectedResult, $rate, $periods): void
|
||||||
{
|
{
|
||||||
$result = Financial::EFFECT(...$args);
|
$result = Financial::EFFECT($rate, $periods);
|
||||||
self::assertEqualsWithDelta($expectedResult, $result, 1E-8);
|
self::assertEqualsWithDelta($expectedResult, $result, 1E-8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Financial;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Helpers;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class HelpersTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @dataProvider providerDaysPerYear
|
||||||
|
*
|
||||||
|
* @param mixed $expectedResult
|
||||||
|
* @param mixed $year
|
||||||
|
* @param mixed $basis
|
||||||
|
*/
|
||||||
|
public function testDaysPerYear($expectedResult, $year, $basis): void
|
||||||
|
{
|
||||||
|
$result = Helpers::daysPerYear($year, $basis);
|
||||||
|
self::assertSame($expectedResult, $result, 1E-8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function providerDaysPerYear()
|
||||||
|
{
|
||||||
|
return require 'tests/data/Calculation/Financial/DaysPerYear.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,10 +17,12 @@ class NominalTest extends TestCase
|
||||||
* @dataProvider providerNOMINAL
|
* @dataProvider providerNOMINAL
|
||||||
*
|
*
|
||||||
* @param mixed $expectedResult
|
* @param mixed $expectedResult
|
||||||
|
* @param mixed $rate
|
||||||
|
* @param mixed $periods
|
||||||
*/
|
*/
|
||||||
public function testNOMINAL($expectedResult, ...$args): void
|
public function testNOMINAL($expectedResult, $rate, $periods): void
|
||||||
{
|
{
|
||||||
$result = Financial::NOMINAL(...$args);
|
$result = Financial::NOMINAL($rate, $periods);
|
||||||
self::assertEqualsWithDelta($expectedResult, $result, 1E-8);
|
self::assertEqualsWithDelta($expectedResult, $result, 1E-8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,13 @@ return [
|
||||||
'2012-10-25',
|
'2012-10-25',
|
||||||
4,
|
4,
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
71,
|
||||||
|
'2011-01-25',
|
||||||
|
'2011-11-15',
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'#VALUE!',
|
'#VALUE!',
|
||||||
'Invalid Date',
|
'Invalid Date',
|
||||||
|
|
@ -30,11 +37,165 @@ return [
|
||||||
2,
|
2,
|
||||||
1,
|
1,
|
||||||
],
|
],
|
||||||
[
|
'Invalid Frequency' => [
|
||||||
'#NUM!',
|
'#NUM!',
|
||||||
'25-Jan-2007',
|
'25-Jan-2007',
|
||||||
'15-Nov-2008',
|
'15-Nov-2008',
|
||||||
3,
|
3,
|
||||||
1,
|
1,
|
||||||
],
|
],
|
||||||
|
'Non-Numeric Frequency' => [
|
||||||
|
'#NUM!',
|
||||||
|
'25-Jan-2007',
|
||||||
|
'15-Nov-2008',
|
||||||
|
'NaN',
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
'Invalid Basis' => [
|
||||||
|
'#NUM!',
|
||||||
|
'25-Jan-2007',
|
||||||
|
'15-Nov-2008',
|
||||||
|
4,
|
||||||
|
-1,
|
||||||
|
],
|
||||||
|
'Non-Numeric Basis' => [
|
||||||
|
'#NUM!',
|
||||||
|
'25-Jan-2007',
|
||||||
|
'15-Nov-2008',
|
||||||
|
4,
|
||||||
|
'NaN',
|
||||||
|
],
|
||||||
|
'Same Date' => [
|
||||||
|
'#NUM!',
|
||||||
|
'24-Dec-2000',
|
||||||
|
'24-Dec-2000',
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
311,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
317,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
317,
|
||||||
|
'31-Jan-2020',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
317,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
317,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
310,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
131,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
133,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
133,
|
||||||
|
'31-Jan-2020',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
133,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
133,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
130,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
4,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
41,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
42,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
42,
|
||||||
|
'31-Jan-2020',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
42,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
42,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
40,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
4,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -51,11 +51,165 @@ return [
|
||||||
2,
|
2,
|
||||||
1,
|
1,
|
||||||
],
|
],
|
||||||
[
|
'Invalid Frequency' => [
|
||||||
'#NUM!',
|
'#NUM!',
|
||||||
'25-Jan-2007',
|
'25-Jan-2007',
|
||||||
'15-Nov-2008',
|
'15-Nov-2008',
|
||||||
3,
|
3,
|
||||||
1,
|
1,
|
||||||
],
|
],
|
||||||
|
'Non-Numeric Frequency' => [
|
||||||
|
'#NUM!',
|
||||||
|
'25-Jan-2007',
|
||||||
|
'15-Nov-2008',
|
||||||
|
'NaN',
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
'Invalid Basis' => [
|
||||||
|
'#NUM!',
|
||||||
|
'25-Jan-2007',
|
||||||
|
'15-Nov-2008',
|
||||||
|
4,
|
||||||
|
-1,
|
||||||
|
],
|
||||||
|
'Non-Numeric Basis' => [
|
||||||
|
'#NUM!',
|
||||||
|
'25-Jan-2007',
|
||||||
|
'15-Nov-2008',
|
||||||
|
4,
|
||||||
|
'NaN',
|
||||||
|
],
|
||||||
|
'Same Date' => [
|
||||||
|
'#NUM!',
|
||||||
|
'24-Dec-2000',
|
||||||
|
'24-Dec-2000',
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
360,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
365,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
366,
|
||||||
|
'31-Jan-2020',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
360,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
365,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
360,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
180,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
181,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
182,
|
||||||
|
'31-Jan-2020',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
180,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
182.5,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
180,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
4,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
90,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
90,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
91,
|
||||||
|
'31-Jan-2020',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
90,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
91.25,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
90,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
4,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,186 @@ return [
|
||||||
2,
|
2,
|
||||||
1,
|
1,
|
||||||
],
|
],
|
||||||
[
|
'Invalid Frequency' => [
|
||||||
'#NUM!',
|
'#NUM!',
|
||||||
'25-Jan-2007',
|
'25-Jan-2007',
|
||||||
'15-Nov-2008',
|
'15-Nov-2008',
|
||||||
3,
|
3,
|
||||||
1,
|
1,
|
||||||
],
|
],
|
||||||
|
'Non-Numeric Frequency' => [
|
||||||
|
'#NUM!',
|
||||||
|
'25-Jan-2007',
|
||||||
|
'15-Nov-2008',
|
||||||
|
'NaN',
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
'Invalid Basis' => [
|
||||||
|
'#NUM!',
|
||||||
|
'25-Jan-2007',
|
||||||
|
'15-Nov-2008',
|
||||||
|
4,
|
||||||
|
-1,
|
||||||
|
],
|
||||||
|
'Non-Numeric Basis' => [
|
||||||
|
'#NUM!',
|
||||||
|
'25-Jan-2007',
|
||||||
|
'15-Nov-2008',
|
||||||
|
4,
|
||||||
|
'NaN',
|
||||||
|
],
|
||||||
|
'Same Date' => [
|
||||||
|
'#NUM!',
|
||||||
|
'24-Dec-2000',
|
||||||
|
'24-Dec-2000',
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
49,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
49,
|
||||||
|
'01-Feb-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
48,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
49,
|
||||||
|
'31-Jan-2020',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
48,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
48,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
50,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
49,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
49,
|
||||||
|
'01-Feb-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
48,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
49,
|
||||||
|
'31-Jan-2020',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
48,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
48,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
50,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
4,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
49,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
49,
|
||||||
|
'01-Feb-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
48,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
49,
|
||||||
|
'31-Jan-2020',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
48,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
48,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
50,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
4,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,35 @@ return [
|
||||||
2,
|
2,
|
||||||
1,
|
1,
|
||||||
],
|
],
|
||||||
[
|
'Invalid Frequency' => [
|
||||||
'#NUM!',
|
'#NUM!',
|
||||||
'25-Jan-2007',
|
'25-Jan-2007',
|
||||||
'15-Nov-2008',
|
'15-Nov-2008',
|
||||||
3,
|
3,
|
||||||
1,
|
1,
|
||||||
],
|
],
|
||||||
[
|
'Non-Numeric Frequency' => [
|
||||||
|
'#NUM!',
|
||||||
|
'25-Jan-2007',
|
||||||
|
'15-Nov-2008',
|
||||||
|
'NaN',
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
'Invalid Basis' => [
|
||||||
|
'#NUM!',
|
||||||
|
'25-Jan-2007',
|
||||||
|
'15-Nov-2008',
|
||||||
|
4,
|
||||||
|
-1,
|
||||||
|
],
|
||||||
|
'Non-Numeric Basis' => [
|
||||||
|
'#NUM!',
|
||||||
|
'25-Jan-2007',
|
||||||
|
'15-Nov-2008',
|
||||||
|
4,
|
||||||
|
'NaN',
|
||||||
|
],
|
||||||
|
'Same Date' => [
|
||||||
'#NUM!',
|
'#NUM!',
|
||||||
'24-Dec-2000',
|
'24-Dec-2000',
|
||||||
'24-Dec-2000',
|
'24-Dec-2000',
|
||||||
|
|
@ -65,4 +86,130 @@ return [
|
||||||
4,
|
4,
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
44275,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44275,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
43910,
|
||||||
|
'31-Jan-2020',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44275,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44275,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44275,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44275,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44275,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
43910,
|
||||||
|
'31-Jan-2020',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44275,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44275,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44275,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
4,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44275,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44275,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
43910,
|
||||||
|
'31-Jan-2020',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44275,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44275,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44275,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
4,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -31,13 +31,41 @@ return [
|
||||||
2,
|
2,
|
||||||
1,
|
1,
|
||||||
],
|
],
|
||||||
[
|
'Invalid Frequency' => [
|
||||||
'#NUM!',
|
'#NUM!',
|
||||||
'25-Jan-2007',
|
'25-Jan-2007',
|
||||||
'15-Nov-2008',
|
'15-Nov-2008',
|
||||||
3,
|
3,
|
||||||
1,
|
1,
|
||||||
],
|
],
|
||||||
|
'Non-Numeric Frequency' => [
|
||||||
|
'#NUM!',
|
||||||
|
'25-Jan-2007',
|
||||||
|
'15-Nov-2008',
|
||||||
|
'NaN',
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
'Invalid Basis' => [
|
||||||
|
'#NUM!',
|
||||||
|
'25-Jan-2007',
|
||||||
|
'15-Nov-2008',
|
||||||
|
4,
|
||||||
|
-1,
|
||||||
|
],
|
||||||
|
'Non-Numeric Basis' => [
|
||||||
|
'#NUM!',
|
||||||
|
'25-Jan-2007',
|
||||||
|
'15-Nov-2008',
|
||||||
|
4,
|
||||||
|
'NaN',
|
||||||
|
],
|
||||||
|
'Same Date' => [
|
||||||
|
'#NUM!',
|
||||||
|
'24-Dec-2000',
|
||||||
|
'24-Dec-2000',
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
],
|
||||||
[
|
[
|
||||||
5,
|
5,
|
||||||
'01-Jan-2008',
|
'01-Jan-2008',
|
||||||
|
|
@ -108,4 +136,130 @@ return [
|
||||||
2,
|
2,
|
||||||
4,
|
4,
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
2,
|
||||||
|
'31-Jan-2020',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
3,
|
||||||
|
'31-Jan-2020',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
4,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
5,
|
||||||
|
'31-Jan-2020',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
4,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,35 @@ return [
|
||||||
2,
|
2,
|
||||||
1,
|
1,
|
||||||
],
|
],
|
||||||
[
|
'Invalid Frequency' => [
|
||||||
'#NUM!',
|
'#NUM!',
|
||||||
'25-Jan-2007',
|
'25-Jan-2007',
|
||||||
'15-Nov-2008',
|
'15-Nov-2008',
|
||||||
3,
|
3,
|
||||||
1,
|
1,
|
||||||
],
|
],
|
||||||
[
|
'Non-Numeric Frequency' => [
|
||||||
|
'#NUM!',
|
||||||
|
'25-Jan-2007',
|
||||||
|
'15-Nov-2008',
|
||||||
|
'NaN',
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
'Invalid Basis' => [
|
||||||
|
'#NUM!',
|
||||||
|
'25-Jan-2007',
|
||||||
|
'15-Nov-2008',
|
||||||
|
4,
|
||||||
|
-1,
|
||||||
|
],
|
||||||
|
'Non-Numeric Basis' => [
|
||||||
|
'#NUM!',
|
||||||
|
'25-Jan-2007',
|
||||||
|
'15-Nov-2008',
|
||||||
|
4,
|
||||||
|
'NaN',
|
||||||
|
],
|
||||||
|
'Same Date' => [
|
||||||
'#NUM!',
|
'#NUM!',
|
||||||
'24-Dec-2000',
|
'24-Dec-2000',
|
||||||
'24-Dec-2000',
|
'24-Dec-2000',
|
||||||
|
|
@ -65,4 +86,130 @@ return [
|
||||||
4,
|
4,
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
43910,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
43910,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
43544,
|
||||||
|
'31-Jan-2020',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
43910,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
43910,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
43910,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44094,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44094,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
43728,
|
||||||
|
'31-Jan-2020',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44094,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44094,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44094,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
2,
|
||||||
|
4,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44185,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44185,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
43819,
|
||||||
|
'31-Jan-2020',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44185,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44185,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44185,
|
||||||
|
'31-Jan-2021',
|
||||||
|
'20-Mar-2021',
|
||||||
|
4,
|
||||||
|
4,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Helpers;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||||
|
|
||||||
|
return [
|
||||||
|
[360, 2020, Helpers::DAYS_PER_YEAR_NASD],
|
||||||
|
[366, 2020, Helpers::DAYS_PER_YEAR_ACTUAL],
|
||||||
|
[365, 2021, Helpers::DAYS_PER_YEAR_ACTUAL],
|
||||||
|
[360, 2020, Helpers::DAYS_PER_YEAR_360],
|
||||||
|
[365, 2020, Helpers::DAYS_PER_YEAR_365],
|
||||||
|
[360, 2020, Helpers::DAYS_PER_YEAR_360_EUROPEAN],
|
||||||
|
[Functions::NAN(), 2020, 'Invalid'],
|
||||||
|
[Functions::NAN(), 2020, 999],
|
||||||
|
];
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
// nominal_rate, npery, Result
|
// nominal_rate, npery, Result
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
0.053542667370758003,
|
0.053542667370758003,
|
||||||
|
|
@ -24,8 +26,23 @@ return [
|
||||||
2,
|
2,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'#NUM!',
|
Functions::NAN(),
|
||||||
|
0.025,
|
||||||
|
-1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Functions::NAN(),
|
||||||
|
-0.025,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Functions::VALUE(),
|
||||||
|
0.025,
|
||||||
|
'NaN',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Functions::VALUE(),
|
||||||
|
'NaN',
|
||||||
1,
|
1,
|
||||||
0,
|
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
// effect_rate, npery, result
|
// effect_rate, npery, result
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
0.052500319868356002,
|
0.052500319868356002,
|
||||||
|
|
@ -24,8 +26,23 @@ return [
|
||||||
12,
|
12,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'#NUM!',
|
Functions::NAN(),
|
||||||
-0.025000000000000001,
|
-0.025,
|
||||||
12,
|
12,
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
Functions::NAN(),
|
||||||
|
0.025,
|
||||||
|
-12,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Functions::VALUE(),
|
||||||
|
'NaN',
|
||||||
|
12,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Functions::VALUE(),
|
||||||
|
0.025,
|
||||||
|
'NaN',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,42 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
0.09415149356594302,
|
0.094151494,
|
||||||
'31-Mar-2008', '1-Jun-2008', 0.0914,
|
'31-Mar-2008', '1-Jun-2008', 0.0914,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0.025465926064094112,
|
Functions::VALUE(),
|
||||||
|
'Not a Valid Date', '1-Jun-2008', 0.09,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Functions::VALUE(),
|
||||||
|
'31-Mar-2008', 'Not a Valid Date', 0.09,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Functions::VALUE(),
|
||||||
|
'31-Mar-2008', '1-Jun-2008', 'NaN',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Functions::NAN(),
|
||||||
|
'31-Mar-2008', '1-Jun-2008', -0.09,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Functions::NAN(),
|
||||||
|
'31-Mar-2000', '1-Jun-2021', 0.09,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Functions::NAN(),
|
||||||
|
'1-Jun-2008', '31-Mar-2008', 0.09,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.025465926,
|
||||||
'5-Feb-2019', '1-Feb-2020', 0.0245,
|
'5-Feb-2019', '1-Feb-2020', 0.0245,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
0.036787997465875716,
|
0.036787997,
|
||||||
'1-Feb-2016', '30-Jan-2017', 0.035,
|
'1-Feb-2016', '30-Jan-2017', 0.035,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,50 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
98.45,
|
98.45,
|
||||||
'31-Mar-2008', '1-Jun-2008', 0.09,
|
'31-Mar-2008', '1-Jun-2008', 0.09,
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
Functions::VALUE(),
|
||||||
|
'Not a Valid Date', '1-Jun-2008', 0.09,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Functions::VALUE(),
|
||||||
|
'31-Mar-2008', 'Not a Valid Date', 0.09,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Functions::VALUE(),
|
||||||
|
'31-Mar-2008', '1-Jun-2008', 'NaN',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Functions::NAN(),
|
||||||
|
'31-Mar-2008', '1-Jun-2008', -0.09,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Functions::NAN(),
|
||||||
|
'31-Mar-2000', '1-Jun-2021', 0.09,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Functions::NAN(),
|
||||||
|
'1-Jun-2008', '31-Mar-2008', 0.09,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.0,
|
||||||
|
'31-Mar-2008', '1-Apr-2008', 360,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Functions::NAN(),
|
||||||
|
'31-Mar-2008', '1-Apr-2008', 361,
|
||||||
|
],
|
||||||
[
|
[
|
||||||
97.75,
|
97.75,
|
||||||
'1-Apr-2017', '30-Jun-2017', 0.09,
|
'1-Apr-2017', '30-Jun-2017', 0.09,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
97.54319444444445,
|
97.543194444,
|
||||||
'5-Feb-2019', '1-Feb-2020', 0.0245,
|
'5-Feb-2019', '1-Feb-2020', 0.0245,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,36 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
0.09141696292534264,
|
0.09141696292534264,
|
||||||
'31-Mar-2008', '1-Jun-2008', 98.45,
|
'31-Mar-2008', '1-Jun-2008', 98.45,
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
Functions::VALUE(),
|
||||||
|
'Not a Valid Date', '1-Jun-2008', 98.45,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Functions::VALUE(),
|
||||||
|
'31-Mar-2008', 'Not a Valid Date', 98.45,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Functions::VALUE(),
|
||||||
|
'31-Mar-2008', '1-Jun-2008', 'NaN',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Functions::NAN(),
|
||||||
|
'31-Mar-2008', '1-Jun-2008', -1.25,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Functions::NAN(),
|
||||||
|
'31-Mar-2000', '1-Jun-2021', 97.25,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Functions::NAN(),
|
||||||
|
'1-Jun-2008', '31-Mar-2008', 97.25,
|
||||||
|
],
|
||||||
[
|
[
|
||||||
0.024405125076266018,
|
0.024405125076266018,
|
||||||
'1-Feb-2017', '30-Jun-2017', 99,
|
'1-Feb-2017', '30-Jun-2017', 99,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue