@travishorn/financejs 1.18.0 → 1.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/coupdaybs.js +74 -0
- package/src/coupdays.js +68 -0
- package/src/coupdaysnc.js +73 -0
- package/src/index.js +3 -0
- package/src/price.js +13 -8
- package/src/util.js +0 -90
- package/src/yield_.js +13 -8
package/README.md
CHANGED
|
@@ -113,7 +113,7 @@ Tiers 1-3 are complete.
|
|
|
113
113
|
- **Tier 3:** ✓rri, ✓pduration, ✓vdb, ✓fvschedule, ✓dollarde, ✓dollarfr, ✓ispmt
|
|
114
114
|
- **Tier 4:** ✓yield, ✓price, duration, mduration, disc, intrate, received,
|
|
115
115
|
pricedisc, pricemat, yielddisc, yieldmat
|
|
116
|
-
- **Tier 5:** all others
|
|
116
|
+
- **Tier 5:** ✓coupdaybs, ✓coupdays, ✓coupdaysnc, all others
|
|
117
117
|
|
|
118
118
|
## License
|
|
119
119
|
|
package/package.json
CHANGED
package/src/coupdaybs.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import {
|
|
2
|
+
actualDays,
|
|
3
|
+
days360Eu,
|
|
4
|
+
days360Us,
|
|
5
|
+
getCouponBounds,
|
|
6
|
+
toUtcDate,
|
|
7
|
+
} from "./util.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Returns the number of days from the beginning of the coupon period to the
|
|
11
|
+
* settlement date.
|
|
12
|
+
*
|
|
13
|
+
* Remarks:
|
|
14
|
+
* - `settlement`, `maturity`, `frequency`, and `basis` are truncated to
|
|
15
|
+
* integers.
|
|
16
|
+
* - If `settlement` or `maturity` is not a valid date, an error is thrown.
|
|
17
|
+
* - If `frequency` is any number other than `1`, `2`, or `4`, an error is
|
|
18
|
+
* thrown.
|
|
19
|
+
* - If `basis` < `0` or if `basis` > `4`, an error is thrown.
|
|
20
|
+
* - If `settlement` >= `maturity`, an error is thrown.
|
|
21
|
+
*
|
|
22
|
+
* @param {Date} settlement - The security's settlement date.
|
|
23
|
+
* @param {Date} maturity - The security's maturity date.
|
|
24
|
+
* @param {1|2|4} frequency - The number of coupon payments per year. For annual
|
|
25
|
+
* payments, frequency = `1`; for semiannual, frequency = `2`; for quarterly,
|
|
26
|
+
* frequency = `4`.
|
|
27
|
+
* @param {0|1|2|3|4} [basis=0] - The type of day count basis to use. `0` or
|
|
28
|
+
* omitted = US (NASD 30/360), `1` = actual/actual, `2` = actual/360, `3` =
|
|
29
|
+
* actual/365, `4` = European 30/360.
|
|
30
|
+
* @returns {number} The number of days from the beginning of the coupon period
|
|
31
|
+
* to the settlement date.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* coupdaybs(new Date("2011-01-25"), new Date("2011-11-15"), 2, 1); // 71
|
|
35
|
+
*/
|
|
36
|
+
export function coupdaybs(settlement, maturity, frequency, basis = 0) {
|
|
37
|
+
const settlementDate = toUtcDate(settlement);
|
|
38
|
+
const maturityDate = toUtcDate(maturity);
|
|
39
|
+
|
|
40
|
+
frequency = /** @type {1|2|4} */ (Math.trunc(frequency));
|
|
41
|
+
const basisNumber = Math.trunc(basis ?? 0);
|
|
42
|
+
|
|
43
|
+
if (![1, 2, 4].includes(frequency)) {
|
|
44
|
+
throw new RangeError("Invalid frequency.");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (basisNumber < 0 || basisNumber > 4) {
|
|
48
|
+
throw new RangeError("Invalid basis.");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** @type {0|1|2|3|4} */
|
|
52
|
+
const normalizedBasis = /** @type {0|1|2|3|4} */ (basisNumber);
|
|
53
|
+
|
|
54
|
+
if (settlementDate >= maturityDate) {
|
|
55
|
+
throw new RangeError("Settlement must be before maturity.");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const monthsPerCoupon = 12 / frequency;
|
|
59
|
+
const { previousCouponDate } = getCouponBounds(
|
|
60
|
+
settlementDate,
|
|
61
|
+
maturityDate,
|
|
62
|
+
monthsPerCoupon,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
if (normalizedBasis === 0) {
|
|
66
|
+
return days360Us(previousCouponDate, settlementDate);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (normalizedBasis === 4) {
|
|
70
|
+
return days360Eu(previousCouponDate, settlementDate);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return actualDays(previousCouponDate, settlementDate);
|
|
74
|
+
}
|
package/src/coupdays.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { actualDays, getCouponBounds, toUtcDate } from "./util.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns the number of days in the coupon period that contains the settlement
|
|
5
|
+
* date.
|
|
6
|
+
*
|
|
7
|
+
* Remarks:
|
|
8
|
+
* - `settlement`, `maturity`, `frequency`, and `basis` are truncated to
|
|
9
|
+
* integers.
|
|
10
|
+
* - If `settlement` or `maturity` is not a valid date, an error is thrown.
|
|
11
|
+
* - If `frequency` is any number other than `1`, `2`, or `4`, an error is
|
|
12
|
+
* thrown.
|
|
13
|
+
* - If `basis` < `0` or if `basis` > `4`, an error is thrown.
|
|
14
|
+
* - If `settlement` >= `maturity`, an error is thrown.
|
|
15
|
+
*
|
|
16
|
+
* @param {Date} settlement - The security's settlement date.
|
|
17
|
+
* @param {Date} maturity - The security's maturity date.
|
|
18
|
+
* @param {1|2|4} frequency - The number of coupon payments per year. For annual
|
|
19
|
+
* payments, frequency = `1`; for semiannual, frequency = `2`; for quarterly,
|
|
20
|
+
* frequency = `4`.
|
|
21
|
+
* @param {0|1|2|3|4} [basis=0] - The type of day count basis to use. `0` or
|
|
22
|
+
* omitted = US (NASD 30/360), `1` = actual/actual, `2` = actual/360, `3` =
|
|
23
|
+
* actual/365, `4` = European 30/360.
|
|
24
|
+
* @returns {number} The number of days in the coupon period that contains the
|
|
25
|
+
* settlement date.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* coupdays(new Date("2011-01-25"), new Date("2011-11-15"), 2, 1); // 184
|
|
29
|
+
*/
|
|
30
|
+
export function coupdays(settlement, maturity, frequency, basis = 0) {
|
|
31
|
+
const settlementDate = toUtcDate(settlement);
|
|
32
|
+
const maturityDate = toUtcDate(maturity);
|
|
33
|
+
|
|
34
|
+
frequency = /** @type {1|2|4} */ (Math.trunc(frequency));
|
|
35
|
+
const basisNumber = Math.trunc(basis ?? 0);
|
|
36
|
+
|
|
37
|
+
if (![1, 2, 4].includes(frequency)) {
|
|
38
|
+
throw new RangeError("Invalid frequency.");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (basisNumber < 0 || basisNumber > 4) {
|
|
42
|
+
throw new RangeError("Invalid basis.");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** @type {0|1|2|3|4} */
|
|
46
|
+
const normalizedBasis = /** @type {0|1|2|3|4} */ (basisNumber);
|
|
47
|
+
|
|
48
|
+
if (settlementDate >= maturityDate) {
|
|
49
|
+
throw new RangeError("Settlement must be before maturity.");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const monthsPerCoupon = 12 / frequency;
|
|
53
|
+
const { previousCouponDate, nextCouponDate } = getCouponBounds(
|
|
54
|
+
settlementDate,
|
|
55
|
+
maturityDate,
|
|
56
|
+
monthsPerCoupon,
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
if (normalizedBasis === 0 || normalizedBasis === 2 || normalizedBasis === 4) {
|
|
60
|
+
return 360 / frequency;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (normalizedBasis === 3) {
|
|
64
|
+
return 365 / frequency;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return actualDays(previousCouponDate, nextCouponDate);
|
|
68
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {
|
|
2
|
+
actualDays,
|
|
3
|
+
days360Eu,
|
|
4
|
+
days360Us,
|
|
5
|
+
getCouponBounds,
|
|
6
|
+
toUtcDate,
|
|
7
|
+
} from "./util.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Returns the number of days from the settlement date to the next coupon date.
|
|
11
|
+
*
|
|
12
|
+
* Remarks:
|
|
13
|
+
* - `settlement`, `maturity`, `frequency`, and `basis` are truncated to
|
|
14
|
+
* integers.
|
|
15
|
+
* - If `settlement` or `maturity` is not a valid date, an error is thrown.
|
|
16
|
+
* - If `frequency` is any number other than `1`, `2`, or `4`, an error is
|
|
17
|
+
* thrown.
|
|
18
|
+
* - If `basis` < `0` or if `basis` > `4`, an error is thrown.
|
|
19
|
+
* - If `settlement` >= `maturity`, an error is thrown.
|
|
20
|
+
*
|
|
21
|
+
* @param {Date} settlement - The security's settlement date.
|
|
22
|
+
* @param {Date} maturity - The security's maturity date.
|
|
23
|
+
* @param {1|2|4} frequency - The number of coupon payments per year. For annual
|
|
24
|
+
* payments, frequency = `1`; for semiannual, frequency = `2`; for quarterly,
|
|
25
|
+
* frequency = `4`.
|
|
26
|
+
* @param {0|1|2|3|4} [basis=0] - The type of day count basis to use. `0` or
|
|
27
|
+
* omitted = US (NASD 30/360), `1` = actual/actual, `2` = actual/360, `3` =
|
|
28
|
+
* actual/365, `4` = European 30/360.
|
|
29
|
+
* @returns {number} The number of days from the settlement date to the next
|
|
30
|
+
* coupon date.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* coupdaysnc(new Date("2011-01-25"), new Date("2011-11-15"), 2, 1); // 113
|
|
34
|
+
*/
|
|
35
|
+
export function coupdaysnc(settlement, maturity, frequency, basis = 0) {
|
|
36
|
+
const settlementDate = toUtcDate(settlement);
|
|
37
|
+
const maturityDate = toUtcDate(maturity);
|
|
38
|
+
|
|
39
|
+
frequency = /** @type {1|2|4} */ (Math.trunc(frequency));
|
|
40
|
+
const basisNumber = Math.trunc(basis ?? 0);
|
|
41
|
+
|
|
42
|
+
if (![1, 2, 4].includes(frequency)) {
|
|
43
|
+
throw new RangeError("Invalid frequency.");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (basisNumber < 0 || basisNumber > 4) {
|
|
47
|
+
throw new RangeError("Invalid basis.");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** @type {0|1|2|3|4} */
|
|
51
|
+
const normalizedBasis = /** @type {0|1|2|3|4} */ (basisNumber);
|
|
52
|
+
|
|
53
|
+
if (settlementDate >= maturityDate) {
|
|
54
|
+
throw new RangeError("Settlement must be before maturity.");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const monthsPerCoupon = 12 / frequency;
|
|
58
|
+
const { nextCouponDate } = getCouponBounds(
|
|
59
|
+
settlementDate,
|
|
60
|
+
maturityDate,
|
|
61
|
+
monthsPerCoupon,
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
if (normalizedBasis === 0) {
|
|
65
|
+
return days360Us(settlementDate, nextCouponDate);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (normalizedBasis === 4) {
|
|
69
|
+
return days360Eu(settlementDate, nextCouponDate);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return actualDays(settlementDate, nextCouponDate);
|
|
73
|
+
}
|
package/src/index.js
CHANGED
package/src/price.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
coupdays,
|
|
4
|
-
coupdaysnc,
|
|
2
|
+
actualDays,
|
|
5
3
|
couponsRemaining,
|
|
6
4
|
getCouponBounds,
|
|
7
5
|
normalizeZero,
|
|
8
6
|
toUtcDate,
|
|
9
7
|
} from "./util.js";
|
|
8
|
+
import { coupdaybs } from "./coupdaybs.js";
|
|
9
|
+
import { coupdays } from "./coupdays.js";
|
|
10
|
+
import { coupdaysnc } from "./coupdaysnc.js";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Calculates the price per $100 face value of a security that pays periodic
|
|
@@ -106,14 +107,18 @@ export function price(
|
|
|
106
107
|
monthsPerCoupon,
|
|
107
108
|
);
|
|
108
109
|
|
|
109
|
-
const a = coupdaybs(
|
|
110
|
-
let dsc = coupdaysnc(
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
nextCouponDate,
|
|
110
|
+
const a = coupdaybs(settlementDate, maturityDate, frequency, normalizedBasis);
|
|
111
|
+
let dsc = coupdaysnc(
|
|
112
|
+
settlementDate,
|
|
113
|
+
maturityDate,
|
|
114
114
|
frequency,
|
|
115
115
|
normalizedBasis,
|
|
116
116
|
);
|
|
117
|
+
let e = coupdays(settlementDate, maturityDate, frequency, normalizedBasis);
|
|
118
|
+
|
|
119
|
+
if (normalizedBasis === 2) {
|
|
120
|
+
e = actualDays(previousCouponDate, nextCouponDate);
|
|
121
|
+
}
|
|
117
122
|
|
|
118
123
|
if (normalizedBasis === 3) {
|
|
119
124
|
dsc = e - a;
|
package/src/util.js
CHANGED
|
@@ -204,96 +204,6 @@ export function couponsRemaining(
|
|
|
204
204
|
return n;
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
-
/**
|
|
208
|
-
* Computes days from settlement to next coupon date (`DSC`) by day-count basis.
|
|
209
|
-
*
|
|
210
|
-
* Basis mapping:
|
|
211
|
-
* - `0`: US (NASD) 30/360
|
|
212
|
-
* - `1`: Actual/actual
|
|
213
|
-
* - `2`: Actual/360
|
|
214
|
-
* - `3`: Actual/365
|
|
215
|
-
* - `4`: European 30/360
|
|
216
|
-
*
|
|
217
|
-
* @param {Date} settlementDate - Settlement date.
|
|
218
|
-
* @param {Date} nextCouponDate - Next coupon date.
|
|
219
|
-
* @param {0|1|2|3|4} basis - Day-count basis code.
|
|
220
|
-
* @returns {number} Days between settlement and next coupon under `basis`.
|
|
221
|
-
*
|
|
222
|
-
* @example
|
|
223
|
-
* coupdaysnc(new Date("2024-01-15"), new Date("2024-04-15"), 0);
|
|
224
|
-
*/
|
|
225
|
-
export function coupdaysnc(settlementDate, nextCouponDate, basis) {
|
|
226
|
-
if (basis === 0) {
|
|
227
|
-
return days360Us(settlementDate, nextCouponDate);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
if (basis === 4) {
|
|
231
|
-
return days360Eu(settlementDate, nextCouponDate);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
return actualDays(settlementDate, nextCouponDate);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Computes days from previous coupon date to settlement (`A`) by day-count
|
|
239
|
-
* basis.
|
|
240
|
-
*
|
|
241
|
-
* Basis mapping:
|
|
242
|
-
* - `0`: US (NASD) 30/360
|
|
243
|
-
* - `1`: Actual/actual
|
|
244
|
-
* - `2`: Actual/360
|
|
245
|
-
* - `3`: Actual/365
|
|
246
|
-
* - `4`: European 30/360
|
|
247
|
-
*
|
|
248
|
-
* @param {Date} previousCouponDate - Coupon date immediately before settlement.
|
|
249
|
-
* @param {Date} settlementDate - Settlement date.
|
|
250
|
-
* @param {0|1|2|3|4} basis - Day-count basis code.
|
|
251
|
-
* @returns {number} Days between previous coupon and settlement under `basis`.
|
|
252
|
-
*
|
|
253
|
-
* @example
|
|
254
|
-
* coupdaybs(new Date("2023-10-15"), new Date("2024-01-15"), 1);
|
|
255
|
-
*/
|
|
256
|
-
export function coupdaybs(previousCouponDate, settlementDate, basis) {
|
|
257
|
-
if (basis === 0) {
|
|
258
|
-
return days360Us(previousCouponDate, settlementDate);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (basis === 4) {
|
|
262
|
-
return days360Eu(previousCouponDate, settlementDate);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
return actualDays(previousCouponDate, settlementDate);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Computes total days in the coupon period (`E`) by basis and payment
|
|
270
|
-
* frequency.
|
|
271
|
-
*
|
|
272
|
-
* For 30/360 bases (`0` and `4`), period length is fixed at `360/frequency`.
|
|
273
|
-
* For basis `3` (Actual/365), period length is fixed at `365/frequency`.
|
|
274
|
-
* Otherwise, calendar days between coupon boundaries are used.
|
|
275
|
-
*
|
|
276
|
-
* @param {Date} previousCouponDate - Coupon date before settlement.
|
|
277
|
-
* @param {Date} nextCouponDate - Coupon date after settlement.
|
|
278
|
-
* @param {number} frequency - Coupon payments per year.
|
|
279
|
-
* @param {0|1|2|3|4} basis - Day-count basis code.
|
|
280
|
-
* @returns {number} Coupon period length in days.
|
|
281
|
-
*
|
|
282
|
-
* @example
|
|
283
|
-
* coupdays(new Date("2024-01-01"), new Date("2024-07-01"), 2, 3); // 182.5
|
|
284
|
-
*/
|
|
285
|
-
export function coupdays(previousCouponDate, nextCouponDate, frequency, basis) {
|
|
286
|
-
if (basis === 0 || basis === 4) {
|
|
287
|
-
return 360 / frequency;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
if (basis === 3) {
|
|
291
|
-
return 365 / frequency;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
return actualDays(previousCouponDate, nextCouponDate);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
207
|
/**
|
|
298
208
|
* Converts a `Date` to a UTC-midnight date-only representation.
|
|
299
209
|
*
|
package/src/yield_.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
coupdays,
|
|
4
|
-
coupdaysnc,
|
|
2
|
+
actualDays,
|
|
5
3
|
couponsRemaining,
|
|
6
4
|
getCouponBounds,
|
|
7
5
|
normalizeZero,
|
|
8
6
|
toUtcDate,
|
|
9
7
|
} from "./util.js";
|
|
8
|
+
import { coupdaybs } from "./coupdaybs.js";
|
|
9
|
+
import { coupdays } from "./coupdays.js";
|
|
10
|
+
import { coupdaysnc } from "./coupdaysnc.js";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Calculates the yield on a security that pays periodic interest. Use to
|
|
@@ -106,14 +107,18 @@ export function yield_(
|
|
|
106
107
|
monthsPerCoupon,
|
|
107
108
|
);
|
|
108
109
|
|
|
109
|
-
const a = coupdaybs(
|
|
110
|
-
let dsc = coupdaysnc(
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
nextCouponDate,
|
|
110
|
+
const a = coupdaybs(settlementDate, maturityDate, frequency, normalizedBasis);
|
|
111
|
+
let dsc = coupdaysnc(
|
|
112
|
+
settlementDate,
|
|
113
|
+
maturityDate,
|
|
114
114
|
frequency,
|
|
115
115
|
normalizedBasis,
|
|
116
116
|
);
|
|
117
|
+
let e = coupdays(settlementDate, maturityDate, frequency, normalizedBasis);
|
|
118
|
+
|
|
119
|
+
if (normalizedBasis === 2) {
|
|
120
|
+
e = actualDays(previousCouponDate, nextCouponDate);
|
|
121
|
+
}
|
|
117
122
|
|
|
118
123
|
if (normalizedBasis === 3) {
|
|
119
124
|
dsc = e - a;
|