nfTlWNl rn Ѻq$³*%yNk9Snc5<Ԧ 9g_mG, ]6ɱ =-f06+ꓻ=m'Y7a5DDg7*_;q?`!0\`Lٔ*ܝIc3ٿ ZmWvw# wO6GrJsf]ZV hHWgq!`f |$<r:Vc3*Y~ջp Ú uMHت =-f06+ꓻ=m'Y7#Β5! RmtNnHCoQ1g CӝINgU@v_=6x:eEd4%S 3P&oAY\QN#]>#[-IB{aA0wK E .WXO,byFZv)ΊrzEM`aĜ@N,@pp,byFZť|c0$"Q5'm:jp/٬q&~2 @ZT,,cV:+av#H 0đrx?gꆚ08wRr"{3bQvpN 9m/s߆,Z!j'V"ptHVO}໐ e(.iXٝ-Y"|j,7 ~\"k3#=hee{2zҰp؆P^94uCTV6E&Ra<~ٲ(|xE:' Yc{ ^ ĢQ$3G"6bmZQ3[?놜F2YzP Dדr&9 Xjָ X$o(U}E ĢQ$3{r!zğ:X5]+֊EqOot Lh` WRU)Fm{b| ĢQ$3ok Z 4CQ a>SHCgAʛJ pz>G&3 I]C63 ĢQ$3)taso$@gO\(D*x|nB?_r<.??0Pn6@e f9llr)i ĢQ$3}זbJNx#EA2E{DFo(.SfBS|w4.llPQܥ%%ד!{n 71? WLʈ@xic&g6ԐI`7ЕhcV>1˹L[D/B֚kfmX3V"<mS^Ȟ x#R5cf؞4Rmv7+,i!r>e@Ħs*܀kyCI :U?*iIu0 2Aր!"X/hIe@Ħs*܀kUpQL埀8˯\ȵ< n3.bGP-ȼPtWjpWc)9-%DpmX{O4}K<9rhsnZAV11KH8^-%Ë&xsH_K"NV3(66?G΁'RZg=tP.xS uBk7aC(H{Fub/W)έng`9\937i12]N7t /i±L 03X)>@:a7*QLrя{uKFZ\t%A2膽aF޿26g[-wWuȧ@-m 8bu, S'@ep*^W켧9&ϗ.rQm~7}B cy ROL@-m 8buC}yן̓"GPNBwF>3?-)F^ 2 ߯{ppPMW.`խ 7@ YqudOWLoҳg)./RvqF0,T ĢQ$3<&} 1ܗ8lSTV4<@][eTm8Oyvn#LX4]m'W䇏fo*qֽW^]r ĢQ$3WQK̽*GK|k(HAmRC>ͫ7Hwa6x]WIC~֝) ׀fLY9խA/-GcJTgetMessage(); } switch ($basis) { case FinancialConstants::BASIS_DAYS_PER_YEAR_365: // Actual/365 return 365 / $frequency; case FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL: // Actual/actual if ($frequency == FinancialConstants::FREQUENCY_ANNUAL) { $daysPerYear = (int) Helpers::daysPerYear(Functions::scalar(DateTimeExcel\DateParts::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 mixed $basis The type of day count to use (int) . * 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 = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD ) { $settlement = Functions::flattenSingleValue($settlement); $maturity = Functions::flattenSingleValue($maturity); $frequency = Functions::flattenSingleValue($frequency); $basis = ($basis === null) ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD : Functions::flattenSingleValue($basis); try { $settlement = FinancialValidations::validateSettlementDate($settlement); $maturity = FinancialValidations::validateMaturityDate($maturity); self::validateCouponPeriod($settlement, $maturity); $frequency = FinancialValidations::validateFrequency($frequency); $basis = FinancialValidations::validateBasis($basis); } catch (Exception $e) { return $e->getMessage(); } /** @var int */ $daysPerYear = Helpers::daysPerYear(Functions::Scalar(DateTimeExcel\DateParts::year($settlement)), $basis); $next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_NEXT); if ($basis === FinancialConstants::BASIS_DAYS_PER_YEAR_NASD) { $settlementDate = Date::excelToDateTimeObject($settlement); $settlementEoM = Helpers::isLastDayOfMonth($settlementDate); if ($settlementEoM) { ++$settlement; } } return (float) DateTimeExcel\YearFrac::fraction($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 mixed $basis The type of day count to use (int). * 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 = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD ) { $settlement = Functions::flattenSingleValue($settlement); $maturity = Functions::flattenSingleValue($maturity); $frequency = Functions::flattenSingleValue($frequency); $basis = ($basis === null) ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD : Functions::flattenSingleValue($basis); try { $settlement = FinancialValidations::validateSettlementDate($settlement); $maturity = FinancialValidations::validateMaturityDate($maturity); self::validateCouponPeriod($settlement, $maturity); $frequency = FinancialValidations::validateFrequency($frequency); $basis = FinancialValidations::validateBasis($basis); self::doNothing($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 mixed $basis The type of day count to use (int). * 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 = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD ) { $settlement = Functions::flattenSingleValue($settlement); $maturity = Functions::flattenSingleValue($maturity); $frequency = Functions::flattenSingleValue($frequency); $basis = ($basis === null) ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD : Functions::flattenSingleValue($basis); try { $settlement = FinancialValidations::validateSettlementDate($settlement); $maturity = FinancialValidations::validateMaturityDate($maturity); self::validateCouponPeriod($settlement, $maturity); $frequency = FinancialValidations::validateFrequency($frequency); $basis = FinancialValidations::validateBasis($basis); self::doNothing($basis); } catch (Exception $e) { return $e->getMessage(); } $yearsBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction( $settlement, $maturity, FinancialConstants::BASIS_DAYS_PER_YEAR_NASD ); return (int) ceil((float) $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 mixed $basis The type of day count to use (int). * 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 = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD ) { $settlement = Functions::flattenSingleValue($settlement); $maturity = Functions::flattenSingleValue($maturity); $frequency = Functions::flattenSingleValue($frequency); $basis = ($basis === null) ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD : Functions::flattenSingleValue($basis); try { $settlement = FinancialValidations::validateSettlementDate($settlement); $maturity = FinancialValidations::validateMaturityDate($maturity); self::validateCouponPeriod($settlement, $maturity); $frequency = FinancialValidations::validateFrequency($frequency); $basis = FinancialValidations::validateBasis($basis); self::doNothing($basis); } catch (Exception $e) { return $e->getMessage(); } return self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_PREVIOUS); } private static function monthsDiff(DateTime $result, int $months, string $plusOrMinus, int $day, bool $lastDayFlag): void { $result->setDate((int) $result->format('Y'), (int) $result->format('m'), 1); $result->modify("$plusOrMinus $months months"); $daysInMonth = (int) $result->format('t'); $result->setDate((int) $result->format('Y'), (int) $result->format('m'), $lastDayFlag ? $daysInMonth : min($day, $daysInMonth)); } private static function couponFirstPeriodDate(float $settlement, float $maturity, int $frequency, bool $next): float { $months = 12 / $frequency; $result = Date::excelToDateTimeObject($maturity); $day = (int) $result->format('d'); $lastDayFlag = Helpers::isLastDayOfMonth($result); while ($settlement < Date::PHPToExcel($result)) { self::monthsDiff($result, $months, '-', $day, $lastDayFlag); } if ($next === true) { self::monthsDiff($result, $months, '+', $day, $lastDayFlag); } return (float) Date::PHPToExcel($result); } private static function validateCouponPeriod(float $settlement, float $maturity): void { if ($settlement >= $maturity) { throw new Exception(ExcelError::NAN()); } } /** @param mixed $basis */ private static function doNothing($basis): bool { return $basis; } }