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