Passed
Push — master ( 5dee5a...729c4d )
by
unknown
20:09 queued 09:33
created

Price   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 269
Duplicated Lines 0 %

Test Coverage

Coverage 94.55%

Importance

Changes 0
Metric Value
wmc 20
eloc 111
c 0
b 0
f 0
dl 0
loc 269
ccs 104
cts 110
cp 0.9455
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A received() 0 36 5
A priceDiscounted() 0 33 4
B priceAtMaturity() 0 55 7
A price() 0 48 4
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial\Securities;
4
5
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
6
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
7
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
8
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Coupons;
9
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Helpers;
10
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
11
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
12
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
13
14
class Price
15
{
16
    /**
17
     * PRICE.
18
     *
19
     * Returns the price per $100 face value of a security that pays periodic interest.
20
     *
21
     * @param mixed $settlement The security's settlement date.
22
     *                              The security settlement date is the date after the issue date when the security
23
     *                              is traded to the buyer.
24
     * @param mixed $maturity The security's maturity date.
25
     *                                The maturity date is the date when the security expires.
26
     * @param mixed $rate the security's annual coupon rate
27
     * @param mixed $yield the security's annual yield
28
     * @param mixed $redemption The number of coupon payments per year.
29
     *                              For annual payments, frequency = 1;
30
     *                              for semiannual, frequency = 2;
31
     *                              for quarterly, frequency = 4.
32
     * @param mixed $basis The type of day count to use.
33
     *                         0 or omitted    US (NASD) 30/360
34
     *                         1               Actual/actual
35
     *                         2               Actual/360
36
     *                         3               Actual/365
37
     *                         4               European 30/360
38
     *
39
     * @return float|string Result, or a string containing an error
40
     */
41 28
    public static function price(
42
        mixed $settlement,
43
        mixed $maturity,
44
        mixed $rate,
45
        mixed $yield,
46
        mixed $redemption,
47
        mixed $frequency,
48
        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
49
    ): string|float {
50 28
        $settlement = Functions::flattenSingleValue($settlement);
51 28
        $maturity = Functions::flattenSingleValue($maturity);
52 28
        $rate = Functions::flattenSingleValue($rate);
53 28
        $yield = Functions::flattenSingleValue($yield);
54 28
        $redemption = Functions::flattenSingleValue($redemption);
55 28
        $frequency = Functions::flattenSingleValue($frequency);
56 28
        $basis = ($basis === null)
57 1
            ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
58 27
            : Functions::flattenSingleValue($basis);
59
60
        try {
61 28
            $settlement = SecurityValidations::validateSettlementDate($settlement);
62 27
            $maturity = SecurityValidations::validateMaturityDate($maturity);
63 26
            SecurityValidations::validateSecurityPeriod($settlement, $maturity);
64 25
            $rate = SecurityValidations::validateRate($rate);
65 24
            $yield = SecurityValidations::validateYield($yield);
66 23
            $redemption = SecurityValidations::validateRedemption($redemption);
67 22
            $frequency = SecurityValidations::validateFrequency($frequency);
68 18
            $basis = SecurityValidations::validateBasis($basis);
69 13
        } catch (Exception $e) {
70 13
            return $e->getMessage();
71
        }
72
73 15
        $dsc = (float) Coupons::COUPDAYSNC($settlement, $maturity, $frequency, $basis);
74 15
        $e = (float) Coupons::COUPDAYS($settlement, $maturity, $frequency, $basis);
75 15
        $n = (int) Coupons::COUPNUM($settlement, $maturity, $frequency, $basis);
76 15
        $a = (float) Coupons::COUPDAYBS($settlement, $maturity, $frequency, $basis);
77
78 15
        $baseYF = 1.0 + ($yield / $frequency);
79 15
        $rfp = 100 * ($rate / $frequency);
80 15
        $de = $dsc / $e;
81
82 15
        $result = $redemption / $baseYF ** (--$n + $de);
83 15
        for ($k = 0; $k <= $n; ++$k) {
84 15
            $result += $rfp / ($baseYF ** ($k + $de));
85
        }
86 15
        $result -= $rfp * ($a / $e);
87
88 15
        return $result;
89
    }
90
91
    /**
92
     * PRICEDISC.
93
     *
94
     * Returns the price per $100 face value of a discounted security.
95
     *
96
     * @param mixed $settlement The security's settlement date.
97
     *                              The security settlement date is the date after the issue date when the security
98
     *                              is traded to the buyer.
99
     * @param mixed $maturity The security's maturity date.
100
     *                                The maturity date is the date when the security expires.
101
     * @param mixed $discount The security's discount rate
102
     * @param mixed $redemption The security's redemption value per $100 face value
103
     * @param mixed $basis The type of day count to use.
104
     *                         0 or omitted    US (NASD) 30/360
105
     *                         1               Actual/actual
106
     *                         2               Actual/360
107
     *                         3               Actual/365
108
     *                         4               European 30/360
109
     *
110
     * @return float|string Result, or a string containing an error
111
     */
112 11
    public static function priceDiscounted(
113
        mixed $settlement,
114
        mixed $maturity,
115
        mixed $discount,
116
        mixed $redemption,
117
        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
118
    ) {
119 11
        $settlement = Functions::flattenSingleValue($settlement);
120 11
        $maturity = Functions::flattenSingleValue($maturity);
121 11
        $discount = Functions::flattenSingleValue($discount);
122 11
        $redemption = Functions::flattenSingleValue($redemption);
123 11
        $basis = ($basis === null)
124 1
            ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
125 10
            : Functions::flattenSingleValue($basis);
126
127
        try {
128 11
            $settlement = SecurityValidations::validateSettlementDate($settlement);
129 10
            $maturity = SecurityValidations::validateMaturityDate($maturity);
130 9
            SecurityValidations::validateSecurityPeriod($settlement, $maturity);
131 9
            $discount = SecurityValidations::validateDiscount($discount);
132 7
            $redemption = SecurityValidations::validateRedemption($redemption);
133 5
            $basis = SecurityValidations::validateBasis($basis);
134 8
        } catch (Exception $e) {
135 8
            return $e->getMessage();
136
        }
137
138 3
        $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
139 3
        if (!is_numeric($daysBetweenSettlementAndMaturity)) {
140
            //    return date error
141
            return StringHelper::convertToString($daysBetweenSettlementAndMaturity);
142
        }
143
144 3
        return $redemption * (1 - $discount * $daysBetweenSettlementAndMaturity);
145
    }
146
147
    /**
148
     * PRICEMAT.
149
     *
150
     * Returns the price per $100 face value of a security that pays interest at maturity.
151
     *
152
     * @param mixed $settlement The security's settlement date.
153
     *                              The security's settlement date is the date after the issue date when the
154
     *                              security is traded to the buyer.
155
     * @param mixed $maturity The security's maturity date.
156
     *                                The maturity date is the date when the security expires.
157
     * @param mixed $issue The security's issue date
158
     * @param mixed $rate The security's interest rate at date of issue
159
     * @param mixed $yield The security's annual yield
160
     * @param mixed $basis The type of day count to use.
161
     *                         0 or omitted    US (NASD) 30/360
162
     *                         1               Actual/actual
163
     *                         2               Actual/360
164
     *                         3               Actual/365
165
     *                         4               European 30/360
166
     *
167
     * @return float|string Result, or a string containing an error
168
     */
169 11
    public static function priceAtMaturity(
170
        mixed $settlement,
171
        mixed $maturity,
172
        mixed $issue,
173
        mixed $rate,
174
        mixed $yield,
175
        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
176
    ) {
177 11
        $settlement = Functions::flattenSingleValue($settlement);
178 11
        $maturity = Functions::flattenSingleValue($maturity);
179 11
        $issue = Functions::flattenSingleValue($issue);
180 11
        $rate = Functions::flattenSingleValue($rate);
181 11
        $yield = Functions::flattenSingleValue($yield);
182 11
        $basis = ($basis === null)
183 1
            ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
184 10
            : Functions::flattenSingleValue($basis);
185
186
        try {
187 11
            $settlement = SecurityValidations::validateSettlementDate($settlement);
188 10
            $maturity = SecurityValidations::validateMaturityDate($maturity);
189 9
            SecurityValidations::validateSecurityPeriod($settlement, $maturity);
190 9
            $issue = SecurityValidations::validateIssueDate($issue);
191 8
            $rate = SecurityValidations::validateRate($rate);
192 6
            $yield = SecurityValidations::validateYield($yield);
193 4
            $basis = SecurityValidations::validateBasis($basis);
194 7
        } catch (Exception $e) {
195 7
            return $e->getMessage();
196
        }
197
198 4
        $daysPerYear = Helpers::daysPerYear(Functions::scalar(DateTimeExcel\DateParts::year($settlement)), $basis);
199 4
        if (!is_numeric($daysPerYear)) {
200
            return $daysPerYear;
201
        }
202 4
        $daysBetweenIssueAndSettlement = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $settlement, $basis));
203 4
        if (!is_numeric($daysBetweenIssueAndSettlement)) {
204
            //    return date error
205
            return StringHelper::convertToString($daysBetweenIssueAndSettlement);
206
        }
207 4
        $daysBetweenIssueAndSettlement *= $daysPerYear;
208 4
        $daysBetweenIssueAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $maturity, $basis));
209 4
        if (!is_numeric($daysBetweenIssueAndMaturity)) {
210
            //    return date error
211
            return StringHelper::convertToString($daysBetweenIssueAndMaturity);
212
        }
213 4
        $daysBetweenIssueAndMaturity *= $daysPerYear;
214 4
        $daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
215 4
        if (!is_numeric($daysBetweenSettlementAndMaturity)) {
216
            //    return date error
217
            return StringHelper::convertToString($daysBetweenSettlementAndMaturity);
218
        }
219 4
        $daysBetweenSettlementAndMaturity *= $daysPerYear;
220
221 4
        return (100 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate * 100))
222 4
            / (1 + (($daysBetweenSettlementAndMaturity / $daysPerYear) * $yield))
223 4
            - (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate * 100);
224
    }
225
226
    /**
227
     * RECEIVED.
228
     *
229
     * Returns the amount received at maturity for a fully invested Security.
230
     *
231
     * @param mixed $settlement The security's settlement date.
232
     *                              The security settlement date is the date after the issue date when the security
233
     *                                  is traded to the buyer.
234
     * @param mixed $maturity The security's maturity date.
235
     *                            The maturity date is the date when the security expires.
236
     * @param mixed $investment The amount invested in the security
237
     * @param mixed $discount The security's discount rate
238
     * @param mixed $basis The type of day count to use.
239
     *                         0 or omitted    US (NASD) 30/360
240
     *                         1               Actual/actual
241
     *                         2               Actual/360
242
     *                         3               Actual/365
243
     *                         4               European 30/360
244
     *
245
     * @return float|string Result, or a string containing an error
246
     */
247 10
    public static function received(
248
        mixed $settlement,
249
        mixed $maturity,
250
        mixed $investment,
251
        mixed $discount,
252
        mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
253
    ) {
254 10
        $settlement = Functions::flattenSingleValue($settlement);
255 10
        $maturity = Functions::flattenSingleValue($maturity);
256 10
        $investment = Functions::flattenSingleValue($investment);
257 10
        $discount = Functions::flattenSingleValue($discount);
258 10
        $basis = ($basis === null)
259 1
            ? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
260 9
            : Functions::flattenSingleValue($basis);
261
262
        try {
263 10
            $settlement = SecurityValidations::validateSettlementDate($settlement);
264 9
            $maturity = SecurityValidations::validateMaturityDate($maturity);
265 8
            SecurityValidations::validateSecurityPeriod($settlement, $maturity);
266 8
            $investment = SecurityValidations::validateFloat($investment);
267 7
            $discount = SecurityValidations::validateDiscount($discount);
268 6
            $basis = SecurityValidations::validateBasis($basis);
269 5
        } catch (Exception $e) {
270 5
            return $e->getMessage();
271
        }
272
273 5
        if ($investment <= 0) {
274 1
            return ExcelError::NAN();
275
        }
276 4
        $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
277 4
        if (!is_numeric($daysBetweenSettlementAndMaturity)) {
278
            //    return date error
279
            return StringHelper::convertToString(Functions::scalar($daysBetweenSettlementAndMaturity));
280
        }
281
282 4
        return $investment / (1 - ($discount * $daysBetweenSettlementAndMaturity));
283
    }
284
}
285