Failed Conditions
Push — master ( ea97af...216db0 )
by
unknown
15:51 queued 07:40
created

Price   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 261
Duplicated Lines 0 %

Test Coverage

Coverage 94.12%

Importance

Changes 0
Metric Value
wmc 16
eloc 103
c 0
b 0
f 0
dl 0
loc 261
ccs 96
cts 102
cp 0.9412
rs 10

4 Methods

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