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