@travishorn/financejs 1.18.0 → 1.19.0

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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travishorn/financejs",
3
- "version": "1.18.0",
3
+ "version": "1.19.0",
4
4
  "description": "Modern JavaScript time value of money and cash-flow financial formulas with Excel-style behavior.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,64 @@
1
+ import {
2
+ coupdaybs as coupdaybsUtil,
3
+ getCouponBounds,
4
+ toUtcDate,
5
+ } from "./util.js";
6
+
7
+ /**
8
+ * Returns the number of days from the beginning of the coupon period to the
9
+ * settlement date.
10
+ *
11
+ * Remarks:
12
+ * - `settlement`, `maturity`, `frequency`, and `basis` are truncated to
13
+ * integers.
14
+ * - If `settlement` or `maturity` is not a valid date, an error is thrown.
15
+ * - If `frequency` is any number other than `1`, `2`, or `4`, an error is
16
+ * thrown.
17
+ * - If `basis` < `0` or if `basis` > `4`, an error is thrown.
18
+ * - If `settlement` >= `maturity`, an error is thrown.
19
+ *
20
+ * @param {Date} settlement - The security's settlement date.
21
+ * @param {Date} maturity - The security's maturity date.
22
+ * @param {1|2|4} frequency - The number of coupon payments per year. For annual
23
+ * payments, frequency = `1`; for semiannual, frequency = `2`; for quarterly,
24
+ * frequency = `4`.
25
+ * @param {0|1|2|3|4} [basis=0] - The type of day count basis to use. `0` or
26
+ * omitted = US (NASD 30/360), `1` = actual/actual, `2` = actual/360, `3` =
27
+ * actual/365, `4` = European 30/360.
28
+ * @returns {number} The number of days from the beginning of the coupon period
29
+ * to the settlement date.
30
+ *
31
+ * @example
32
+ * coupdaybs(new Date("2011-01-25"), new Date("2011-11-15"), 2, 1); // 71
33
+ */
34
+ export function coupdaybs(settlement, maturity, frequency, basis = 0) {
35
+ const settlementDate = toUtcDate(settlement);
36
+ const maturityDate = toUtcDate(maturity);
37
+
38
+ frequency = /** @type {1|2|4} */ (Math.trunc(frequency));
39
+ const basisNumber = Math.trunc(basis ?? 0);
40
+
41
+ if (![1, 2, 4].includes(frequency)) {
42
+ throw new RangeError("Invalid frequency.");
43
+ }
44
+
45
+ if (basisNumber < 0 || basisNumber > 4) {
46
+ throw new RangeError("Invalid basis.");
47
+ }
48
+
49
+ /** @type {0|1|2|3|4} */
50
+ const normalizedBasis = /** @type {0|1|2|3|4} */ (basisNumber);
51
+
52
+ if (settlementDate >= maturityDate) {
53
+ throw new RangeError("Settlement must be before maturity.");
54
+ }
55
+
56
+ const monthsPerCoupon = 12 / frequency;
57
+ const { previousCouponDate } = getCouponBounds(
58
+ settlementDate,
59
+ maturityDate,
60
+ monthsPerCoupon,
61
+ );
62
+
63
+ return coupdaybsUtil(previousCouponDate, settlementDate, normalizedBasis);
64
+ }
@@ -0,0 +1,69 @@
1
+ import {
2
+ coupdays as coupdaysUtil,
3
+ getCouponBounds,
4
+ toUtcDate,
5
+ } from "./util.js";
6
+
7
+ /**
8
+ * Returns the number of days in the coupon period that contains the settlement
9
+ * date.
10
+ *
11
+ * Remarks:
12
+ * - `settlement`, `maturity`, `frequency`, and `basis` are truncated to
13
+ * integers.
14
+ * - If `settlement` or `maturity` is not a valid date, an error is thrown.
15
+ * - If `frequency` is any number other than `1`, `2`, or `4`, an error is
16
+ * thrown.
17
+ * - If `basis` < `0` or if `basis` > `4`, an error is thrown.
18
+ * - If `settlement` >= `maturity`, an error is thrown.
19
+ *
20
+ * @param {Date} settlement - The security's settlement date.
21
+ * @param {Date} maturity - The security's maturity date.
22
+ * @param {1|2|4} frequency - The number of coupon payments per year. For annual
23
+ * payments, frequency = `1`; for semiannual, frequency = `2`; for quarterly,
24
+ * frequency = `4`.
25
+ * @param {0|1|2|3|4} [basis=0] - The type of day count basis to use. `0` or
26
+ * omitted = US (NASD 30/360), `1` = actual/actual, `2` = actual/360, `3` =
27
+ * actual/365, `4` = European 30/360.
28
+ * @returns {number} The number of days in the coupon period that contains the
29
+ * settlement date.
30
+ *
31
+ * @example
32
+ * coupdays(new Date("2011-01-25"), new Date("2011-11-15"), 2, 1); // 184
33
+ */
34
+ export function coupdays(settlement, maturity, frequency, basis = 0) {
35
+ const settlementDate = toUtcDate(settlement);
36
+ const maturityDate = toUtcDate(maturity);
37
+
38
+ frequency = /** @type {1|2|4} */ (Math.trunc(frequency));
39
+ const basisNumber = Math.trunc(basis ?? 0);
40
+
41
+ if (![1, 2, 4].includes(frequency)) {
42
+ throw new RangeError("Invalid frequency.");
43
+ }
44
+
45
+ if (basisNumber < 0 || basisNumber > 4) {
46
+ throw new RangeError("Invalid basis.");
47
+ }
48
+
49
+ /** @type {0|1|2|3|4} */
50
+ const normalizedBasis = /** @type {0|1|2|3|4} */ (basisNumber);
51
+
52
+ if (settlementDate >= maturityDate) {
53
+ throw new RangeError("Settlement must be before maturity.");
54
+ }
55
+
56
+ const monthsPerCoupon = 12 / frequency;
57
+ const { previousCouponDate, nextCouponDate } = getCouponBounds(
58
+ settlementDate,
59
+ maturityDate,
60
+ monthsPerCoupon,
61
+ );
62
+
63
+ return coupdaysUtil(
64
+ previousCouponDate,
65
+ nextCouponDate,
66
+ frequency,
67
+ normalizedBasis,
68
+ );
69
+ }
@@ -0,0 +1,63 @@
1
+ import {
2
+ coupdaysnc as coupdaysncUtil,
3
+ getCouponBounds,
4
+ toUtcDate,
5
+ } from "./util.js";
6
+
7
+ /**
8
+ * Returns the number of days from the settlement date to the next coupon date.
9
+ *
10
+ * Remarks:
11
+ * - `settlement`, `maturity`, `frequency`, and `basis` are truncated to
12
+ * integers.
13
+ * - If `settlement` or `maturity` is not a valid date, an error is thrown.
14
+ * - If `frequency` is any number other than `1`, `2`, or `4`, an error is
15
+ * thrown.
16
+ * - If `basis` < `0` or if `basis` > `4`, an error is thrown.
17
+ * - If `settlement` >= `maturity`, an error is thrown.
18
+ *
19
+ * @param {Date} settlement - The security's settlement date.
20
+ * @param {Date} maturity - The security's maturity date.
21
+ * @param {1|2|4} frequency - The number of coupon payments per year. For annual
22
+ * payments, frequency = `1`; for semiannual, frequency = `2`; for quarterly,
23
+ * frequency = `4`.
24
+ * @param {0|1|2|3|4} [basis=0] - The type of day count basis to use. `0` or
25
+ * omitted = US (NASD 30/360), `1` = actual/actual, `2` = actual/360, `3` =
26
+ * actual/365, `4` = European 30/360.
27
+ * @returns {number} The number of days from the settlement date to the next
28
+ * coupon date.
29
+ *
30
+ * @example
31
+ * coupdaysnc(new Date("2011-01-25"), new Date("2011-11-15"), 2, 1); // 113
32
+ */
33
+ export function coupdaysnc(settlement, maturity, frequency, basis = 0) {
34
+ const settlementDate = toUtcDate(settlement);
35
+ const maturityDate = toUtcDate(maturity);
36
+
37
+ frequency = /** @type {1|2|4} */ (Math.trunc(frequency));
38
+ const basisNumber = Math.trunc(basis ?? 0);
39
+
40
+ if (![1, 2, 4].includes(frequency)) {
41
+ throw new RangeError("Invalid frequency.");
42
+ }
43
+
44
+ if (basisNumber < 0 || basisNumber > 4) {
45
+ throw new RangeError("Invalid basis.");
46
+ }
47
+
48
+ /** @type {0|1|2|3|4} */
49
+ const normalizedBasis = /** @type {0|1|2|3|4} */ (basisNumber);
50
+
51
+ if (settlementDate >= maturityDate) {
52
+ throw new RangeError("Settlement must be before maturity.");
53
+ }
54
+
55
+ const monthsPerCoupon = 12 / frequency;
56
+ const { nextCouponDate } = getCouponBounds(
57
+ settlementDate,
58
+ maturityDate,
59
+ monthsPerCoupon,
60
+ );
61
+
62
+ return coupdaysncUtil(settlementDate, nextCouponDate, normalizedBasis);
63
+ }
package/src/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ export { coupdaybs } from "./coupdaybs.js";
2
+ export { coupdays } from "./coupdays.js";
3
+ export { coupdaysnc } from "./coupdaysnc.js";
1
4
  export { cumipmt } from "./cumipmt.js";
2
5
  export { cumprinc } from "./cumprinc.js";
3
6
  export { db } from "./db.js";
package/src/price.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import {
2
+ actualDays,
2
3
  coupdaybs,
3
4
  coupdays,
4
5
  coupdaysnc,
@@ -108,13 +109,17 @@ export function price(
108
109
 
109
110
  const a = coupdaybs(previousCouponDate, settlementDate, normalizedBasis);
110
111
  let dsc = coupdaysnc(settlementDate, nextCouponDate, normalizedBasis);
111
- const e = coupdays(
112
+ let e = coupdays(
112
113
  previousCouponDate,
113
114
  nextCouponDate,
114
115
  frequency,
115
116
  normalizedBasis,
116
117
  );
117
118
 
119
+ if (normalizedBasis === 2) {
120
+ e = actualDays(previousCouponDate, nextCouponDate);
121
+ }
122
+
118
123
  if (normalizedBasis === 3) {
119
124
  dsc = e - a;
120
125
  }
package/src/util.js CHANGED
@@ -283,7 +283,7 @@ export function coupdaybs(previousCouponDate, settlementDate, basis) {
283
283
  * coupdays(new Date("2024-01-01"), new Date("2024-07-01"), 2, 3); // 182.5
284
284
  */
285
285
  export function coupdays(previousCouponDate, nextCouponDate, frequency, basis) {
286
- if (basis === 0 || basis === 4) {
286
+ if (basis === 0 || basis === 2 || basis === 4) {
287
287
  return 360 / frequency;
288
288
  }
289
289
 
package/src/yield_.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import {
2
+ actualDays,
2
3
  coupdaybs,
3
4
  coupdays,
4
5
  coupdaysnc,
@@ -108,13 +109,17 @@ export function yield_(
108
109
 
109
110
  const a = coupdaybs(previousCouponDate, settlementDate, normalizedBasis);
110
111
  let dsc = coupdaysnc(settlementDate, nextCouponDate, normalizedBasis);
111
- const e = coupdays(
112
+ let e = coupdays(
112
113
  previousCouponDate,
113
114
  nextCouponDate,
114
115
  frequency,
115
116
  normalizedBasis,
116
117
  );
117
118
 
119
+ if (normalizedBasis === 2) {
120
+ e = actualDays(previousCouponDate, nextCouponDate);
121
+ }
122
+
118
123
  if (normalizedBasis === 3) {
119
124
  dsc = e - a;
120
125
  }