@travishorn/financejs 1.0.0 → 1.10.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/src/ipmt.js CHANGED
@@ -1,19 +1,40 @@
1
- import { fv } from "./fv.js";
1
+ import { fv as calculateFv } from "./fv.js";
2
2
  import { pmt } from "./pmt.js";
3
3
 
4
4
  /**
5
- * Calculates the interest portion of a payment for a specific period.
5
+ * Returns the interest payment for a given period for an investment based on
6
+ * periodic, constant payments and a constant interest rate.
7
+ *
8
+ * Remarks:
9
+ * - Make sure that you are consistent about the units you use for specifying
10
+ * `rate` and `nper`. If you make monthly payments on a four-year loan at 12
11
+ * percent annual interest, use `.12/12` for `rate` and `4*12` for `nper`. If
12
+ * you make annual payments on the same loan, use `.12` for `rate` and `4` for
13
+ * `nper`.
14
+ * - For all the arguments, cash you pay out, such as deposits to savings, is
15
+ * represented by negative numbers. Cash you receive, such as dividend checks,
16
+ * is represented by positive numbers.
6
17
  *
7
18
  * @param {number} rate - The interest rate per period.
8
- * @param {number} per - The target period (1-based).
9
- * @param {number} nper - The total number of payment periods.
10
- * @param {number} pv - The present value.
11
- * @param {number} [futureValue=0] - The future value.
12
- * @param {0|1} [type=0] - Payment timing: 0 = end of period, 1 = beginning of period.
19
+ * @param {number} per - The period for which you want to find the interest and
20
+ * must be in the range `1` to `nper`.
21
+ * @param {number} nper - The total number of payment periods in an annuity.
22
+ * @param {number} pv - The present value, or the lump-sum amount that a series
23
+ * of future payments is worth right now.
24
+ * @param {number} [fv=0] - The future value, or a cash balance you
25
+ * want to attain after the last payment is made. If `fv` is omitted, it is
26
+ * assumed to be `0` (the future value of a loan, for example, is `0`).
27
+ * @param {0|1} [type=0] - The number `0` or `1` and indicates when payments are
28
+ * due. If type is omitted, it is assumed to be `0`. Set `type` equal to `0` if
29
+ * payments are due at the end of the period. Set `type` equal to `1` if
30
+ * payments are due at the beginning of the period.
13
31
  * @returns {number} The interest payment for the specified period.
14
32
  * @throws {RangeError} When `per` is outside the valid range.
33
+ *
34
+ * @example
35
+ * ipmt(0.1 / 12, 1, 3, 8000); // -66.67
15
36
  */
16
- export function ipmt(rate, per, nper, pv, futureValue = 0, type = 0) {
37
+ export function ipmt(rate, per, nper, pv, fv = 0, type = 0) {
17
38
  if (per <= 0 || per >= nper + 1) {
18
39
  throw new RangeError("Invalid period.");
19
40
  }
@@ -23,9 +44,9 @@ export function ipmt(rate, per, nper, pv, futureValue = 0, type = 0) {
23
44
  }
24
45
 
25
46
  const periodOffset = type !== 0 ? 2 : 1;
26
- const periodicPayment = pmt(rate, nper, pv, futureValue, type);
47
+ const periodicPayment = pmt(rate, nper, pv, fv, type);
27
48
  const adjustedPresentValue = type !== 0 ? pv + periodicPayment : pv;
28
- const periodFutureValue = fv(
49
+ const periodFutureValue = calculateFv(
29
50
  rate,
30
51
  per - periodOffset,
31
52
  periodicPayment,
package/src/irr.js CHANGED
@@ -1,9 +1,21 @@
1
1
  /**
2
- * Evaluates present value for an IRR iteration guess.
2
+ * Computes the net present value (NPV) of a series of cash flows at a given
3
+ * discount rate.
3
4
  *
4
- * @param {number[]} values - Cash flow values.
5
- * @param {number} [guess=0.1] - Rate guess.
6
- * @returns {number} Present value at the supplied guess.
5
+ * Used internally by the IRR algorithm to evaluate the present value of cash
6
+ * flows for a specific rate guess. Skips leading zero cash flows for
7
+ * efficiency. Cash flows are discounted in reverse order, from last to first
8
+ * nonzero value.
9
+ *
10
+ * @param {number[]} values - Array of cash flows, where each entry represents a
11
+ * payment (negative) or income (positive) at a regular interval.
12
+ * @param {number} [guess=0.1] - Discount rate guess (as a decimal, e.g., 0.1
13
+ * for 10%).
14
+ * @returns {number} The net present value of the cash flows at the supplied
15
+ * discount rate.
16
+ *
17
+ * @example
18
+ * internalPv([-1000, 300, 400, 500], 0.1); // -21.036814425244188
7
19
  */
8
20
  function internalPv(values, guess = 0.1) {
9
21
  let lowerBound = 0;
@@ -24,12 +36,37 @@ function internalPv(values, guess = 0.1) {
24
36
  }
25
37
 
26
38
  /**
27
- * Calculates the internal rate of return for a series of cash flows.
39
+ * Calculates the internal rate of return for a series of cash flows represented
40
+ * by the numbers in `values`. These cash flows do not have to be even, as they
41
+ * would be for an annuity. However, the cash flows must occur at regular
42
+ * intervals, such as monthly or annually. The internal rate of return is the
43
+ * interest rate received for an investment consisting of payments (negative
44
+ * values) and income (positive values) that occur at regular periods.
45
+ *
46
+ * Remarks:
47
+ * - Uses an iterative technique for calculating IRR. Starting with guess, this
48
+ * function cycles through the calculation until the result is accurate within
49
+ * a small absolute threshold. If this function can't find a result that works
50
+ * after 39 tries, a RangeError is thrown.
51
+ * - `irr()` is closely related to `npv()`, the net present value function. The
52
+ * rate of return calculated by this function is the interest rate
53
+ * corresponding to a 0 (zero) net present value.
28
54
  *
29
- * @param {number[]} values - Cash flow values where negatives are investments and positives are returns.
30
- * @param {number} [guess=0.1] - Initial guess for the IRR iteration.
55
+ * @param {number[]} values - An array that contains numbers for which you want
56
+ * to calculate the internal rate of return. Values must contain at least one
57
+ * positive value and one negative value to calculate the internal rate of
58
+ * return. This function uses the order of values to interpret the order of cash
59
+ * flows. Be sure to enter your payment and income values in the sequence you
60
+ * want.
61
+ * @param {number} [guess=0.1] - A number that you guess is close to the result
62
+ * of this function. In most cases you do not need to provide guess for this
63
+ * calculation. If a RangeError is thrown, or if the result is not close to what
64
+ * you expected, try again with a different value for `guess`.
31
65
  * @returns {number} The internal rate of return.
32
- * @throws {RangeError} When inputs are invalid or the algorithm cannot converge.
66
+ * @throws {RangeError} When inputs are invalid or the algorithm cannot
67
+ * converge.
68
+ * @example
69
+ * irr([-70000,12000,15000,18000,21000]); // -0.021244848272975403
33
70
  */
34
71
  export function irr(values, guess = 0.1) {
35
72
  if (guess <= -1) {
package/src/mirr.js ADDED
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Calculates the modified internal rate of return for a series of periodic cash
3
+ * flows. Considers both the cost of the investment and the interest received on
4
+ * reinvestment of cash.
5
+ *
6
+ * Remarks:
7
+ * - Uses the order of values to interpret the order of cash flows. Be sure to
8
+ * enter your payment and income values in the sequence you want and with the
9
+ * correct signs (positive values for cash received, negative values for cash
10
+ * paid).
11
+ * - If `n` is the number of cash flows in values, `frate` is the `financeRate`,
12
+ * and `rrate` is the `reinvestRate`, then the equation is: `((-NPV(rrate,
13
+ * values[positive]) * (1 + rrate)) / (NPV(frate, values[negative]) * (1 +
14
+ * frate)))^(1 / (n - 1)) - 1`
15
+ *
16
+ * @param {number[]} values - An array that contains numbers. These numbers
17
+ * represent a series of payments (negative values) and income (positive values)
18
+ * occurring at regular periods. Values must contain at least one positive value
19
+ * and one negative value to calculate the modified internal rate of return.
20
+ * Otherwise, an error is thrown (divide by zero).
21
+ * @param {number} [financeRate] - The interest rate you pay on the money used in the cash flows.
22
+ * @param {number} [reinvestRate] - The interest rate you receive on the cash flows as you reinvest them.
23
+ * @returns {number} the modified internal rate of return
24
+ * @throws {RangeError} If `values` is not an array of at least two elements, or if there are not both positive and negative cash flows.
25
+ * @throws {TypeError} If `financeRate` or `reinvestRate` is not a number.
26
+ *
27
+ * @example
28
+ * mirr([-120000, 39000, 30000, 21000, 37000, 46000], 0.1, 0.12); // 0.12609413
29
+ */
30
+ export function mirr(values, financeRate, reinvestRate) {
31
+ if (!Array.isArray(values) || values.length < 2) {
32
+ throw new RangeError("values must be an array with at least two elements");
33
+ }
34
+
35
+ if (typeof financeRate !== "number" || typeof reinvestRate !== "number") {
36
+ throw new TypeError("financeRate and reinvestRate must be numbers");
37
+ }
38
+
39
+ const n = values.length;
40
+ let fvPos = 0;
41
+ let pvNeg = 0;
42
+
43
+ for (let i = 0; i < n; i++) {
44
+ const v = values[i];
45
+ if (v > 0) {
46
+ fvPos += v * Math.pow(1 + reinvestRate, n - 1 - i);
47
+ } else if (v < 0) {
48
+ pvNeg += v * Math.pow(1 + financeRate, i);
49
+ }
50
+ }
51
+
52
+ if (fvPos === 0 || pvNeg === 0) {
53
+ throw new RangeError(
54
+ "At least one negative and one positive cash flow required",
55
+ );
56
+ }
57
+
58
+ return Math.pow(fvPos / -pvNeg, 1 / (n - 1)) - 1;
59
+ }
package/src/nominal.js ADDED
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Calculates the nominal annual interest rate, given the effective rate and the
3
+ * number of compounding periods per year.
4
+ *
5
+ * Remarks:
6
+ * - `npery` is truncated to an integer.
7
+ * - If either argument is nonnumeric, an error is thrown.
8
+ * - If `effectRate` <= `0` or if `npery` < `1`, an error is thrown.
9
+ * - `nominal()` is related to `effect()` through `effectiveRate = (1 +
10
+ * (nominalRate / npery)) * npery - 1`.
11
+ *
12
+ * @param {number} effectRate - The effective interest rate.
13
+ * @param {number} npery - The number of compounding periods per year.
14
+ * @returns {number} the nominal annual interest rate
15
+ *
16
+ * @example
17
+ * nominal(0.053543, 4); // 0.05250032
18
+ */
19
+ export function nominal(effectRate, npery) {
20
+ if (
21
+ typeof effectRate !== "number" ||
22
+ typeof npery !== "number" ||
23
+ isNaN(effectRate) ||
24
+ isNaN(npery)
25
+ ) {
26
+ throw new TypeError("Both arguments must be numbers");
27
+ }
28
+ if (effectRate <= 0) {
29
+ throw new RangeError("effectRate must be > 0");
30
+ }
31
+ npery = Math.trunc(npery);
32
+ if (npery < 1) {
33
+ throw new RangeError("npery must be >= 1");
34
+ }
35
+ return npery * (Math.pow(1 + effectRate, 1 / npery) - 1);
36
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Normalizes zero values to ensure consistent handling of positive and negative
3
+ * zero in calculations.
4
+ *
5
+ * In JavaScript, +0 and -0 are distinct values that can yield different results
6
+ * in equality checks (e.g., Object.is(+0, -0) is false), sign checks
7
+ * (Math.sign), and division (1/0 vs 1/-0). This function coerces any zero input
8
+ * (either +0 or -0) to positive zero (+0), providing a consistent
9
+ * representation for downstream calculations, comparisons, and serialization.
10
+ *
11
+ * @param {number} value - The numeric value to normalize. If the value is +0 or
12
+ * -0, returns +0; otherwise, returns the original value.
13
+ * @returns {number} The normalized value, with all zeroes represented as +0.
14
+ *
15
+ * @example
16
+ * normalizeZero(-0); // 0
17
+ * normalizeZero(+0); // 0
18
+ * normalizeZero(5); // 5
19
+ */
20
+ export function normalizeZero(value) {
21
+ return value === 0 ? 0 : value;
22
+ }
package/src/nper.js CHANGED
@@ -1,13 +1,27 @@
1
+ import { normalizeZero } from "./normalizeZero.js";
2
+
1
3
  /**
2
- * Calculates the number of periods for an investment/loan.
4
+ * Calculates the number of periods for an investment based on periodic,
5
+ * constant payments and a constant interest rate.
3
6
  *
4
- * @param {number} rate - Interest rate per period.
5
- * @param {number} pmt - Payment made each period.
6
- * @param {number} pv - Present value.
7
- * @param {number} [fv=0] - Future value.
8
- * @param {0|1} [type=0] - Payment timing: 0 = end of period, 1 = beginning.
7
+ * @param {number} rate - The interest rate per period.
8
+ * @param {number} pmt - The payment made each period; it cannot change over the
9
+ * life of the annuity. Typically, `pmt` contains principal and interest but no
10
+ * other fees or taxes.
11
+ * @param {number} pv - The present value, or the lump-sum amount that a series
12
+ * of future payments is worth right now.
13
+ * @param {number} [fv=0] - The future value, or a cash balance you want to
14
+ * attain after the last payment is made. If `fv` is omitted, it is assumed to
15
+ * be `0` (the future value of a loan, for example, is 0).
16
+ * @param {0|1} [type=0] - The number 0 or 1 and indicates when payments are
17
+ * due. Set `type` equal to `0` or omitted if payments are due at the end of the
18
+ * period. Set `type` equal to `1` if payments are due at the beginning of the
19
+ * period.
9
20
  * @returns {number} Number of periods.
10
21
  * @throws {RangeError} When calculation is impossible with the provided inputs.
22
+ *
23
+ * @example
24
+ * nper(0.12 / 12, -100, -1000, 10000, 1); // 59.67386567
11
25
  */
12
26
  export function nper(rate, pmt, pv, fv = 0, type = 0) {
13
27
  if (rate === 0) {
@@ -15,7 +29,7 @@ export function nper(rate, pmt, pv, fv = 0, type = 0) {
15
29
  throw new RangeError("Payment cannot be 0 when rate is 0.");
16
30
  }
17
31
 
18
- return -(pv + fv) / pmt;
32
+ return normalizeZero(-(pv + fv) / pmt);
19
33
  }
20
34
 
21
35
  const paymentAdjustment = type !== 0 ? pmt * (1 + rate) : pmt;
@@ -34,8 +48,8 @@ export function nper(rate, pmt, pv, fv = 0, type = 0) {
34
48
 
35
49
  const growthFactor = 1 + rate;
36
50
 
37
- return (
51
+ return normalizeZero(
38
52
  (Math.log(futureValueTerm) - Math.log(presentValueTerm)) /
39
- Math.log(growthFactor)
53
+ Math.log(growthFactor),
40
54
  );
41
55
  }
package/src/npv.js CHANGED
@@ -1,11 +1,25 @@
1
1
  /**
2
- * Evaluates net present value across a bounded portion of a values array.
2
+ * Calculates the net present value (NPV) of a subset of cash flows at a
3
+ * specified discount rate.
3
4
  *
4
- * @param {number} rate - Discount rate per period.
5
- * @param {number[]} values - Cash flow values.
6
- * @param {number} [lowerBound=0] - Start index (inclusive).
7
- * @param {number} [upperBound=values.length - 1] - End index (inclusive).
8
- * @returns {number} The evaluated NPV for the specified range.
5
+ * Used internally by the `npv()` function to evaluate the present value of cash
6
+ * flows between given indices. Each cash flow is discounted to its present
7
+ * value using the supplied rate, starting from lowerBound to upperBound
8
+ * (inclusive).
9
+ *
10
+ * @param {number} rate - Discount rate per period (as a decimal, e.g., `0.1`
11
+ * for 10%).
12
+ * @param {number[]} values - Array of cash flows, where each entry represents a
13
+ * payment (negative) or income (positive) at a regular interval.
14
+ * @param {number} [lowerBound=0] - Start index (inclusive) for the range of
15
+ * values to evaluate.
16
+ * @param {number} [upperBound=values.length - 1] - End index (inclusive) for
17
+ * the range of values to evaluate.
18
+ * @returns {number} The net present value of the specified range of cash flows
19
+ * at the given discount rate.
20
+ *
21
+ * @example
22
+ * evalNpv(0.1, [-1000, 300, 400, 500], 0, 2); // -360.6311044327573
9
23
  */
10
24
  function evalNpv(rate, values, lowerBound = 0, upperBound = values.length - 1) {
11
25
  let discountFactor = 1;
@@ -21,12 +35,34 @@ function evalNpv(rate, values, lowerBound = 0, upperBound = values.length - 1) {
21
35
  }
22
36
 
23
37
  /**
24
- * Calculates the net present value of a series of cash flows.
38
+ * Calculates the net present value of an investment by using a discount rate
39
+ * and a series of future payments (negative values) and income (positive
40
+ * values).
25
41
  *
26
- * @param {number} rate - Discount rate per period.
27
- * @param {...number} values - Cash flow values.
42
+ * Remarks:
43
+ * - The NPV investment begins one period before the date of the first value in
44
+ * the cash flow and ends with the last value in the cash flow. The NPV
45
+ * calculation is based on future cash flows. If your first cash flow occurs
46
+ * at the beginning of the first period, the first value must be added to the
47
+ * NPV result, not included in the values arguments.
48
+ * - `npv()` is similar to the `pv()` function (present value). The primary
49
+ * difference between `pv()` and `npv()` is that `pv()` allows cash flows to
50
+ * begin either at the end or at the beginning of the period. Unlike the
51
+ * variable NPV cash flow values, PV cash flows must be constant throughout
52
+ * the investment.
53
+ * - `npv()` is also related to the `irr()` function (internal rate of return).
54
+ * IRR is the rate for which NPV equals zero: `npv(irr(...), ...) = 0`.
55
+ *
56
+ * @param {number} rate - The rate of discount over the length of one period.
57
+ * @param {...number} values - At least one value is required. Values must be
58
+ * equally spaced in time and occur at the end of each period. This function
59
+ * uses the order of the values to interpret the order of cash flows. Be sure to
60
+ * enter your payment and income values in the correct sequence.
28
61
  * @returns {number} The net present value.
29
62
  * @throws {RangeError} When there are no cash flow values or rate is invalid.
63
+ *
64
+ * @example
65
+ * npv(0.1, -10000, 3000, 4200, 6800); // 1188.44
30
66
  */
31
67
  export function npv(rate, ...values) {
32
68
  if (values.length < 1) {
package/src/pmt.js CHANGED
@@ -1,25 +1,46 @@
1
+ import { normalizeZero } from "./normalizeZero.js";
2
+
1
3
  /**
2
- * Calculates the periodic payment for a loan or investment.
4
+ * Calculates the payment for a loan based on constant payments and a constant
5
+ * interest rate.
6
+ *
7
+ * Remarks:
8
+ * - The payment returned by this function includes principal and interest but
9
+ * no taxes, reserve payments, or fees sometimes associated with loans.
10
+ * - Make sure that you are consistent about the units you use for specifying
11
+ * `rate` and `nper`. If you make monthly payments on a four-year loan at an
12
+ * annual interest rate of 12 percent, use `0.12 / 12` for `rate` and `4 * 12`
13
+ * for `nper`. If you make annual payments on the same loan, use `0.12` for
14
+ * `rate` and `4` for `nper`.
3
15
  *
4
- * @param {number} rate - The interest rate per period.
5
- * @param {number} nper - The total number of payment periods.
6
- * @param {number} pv - The present value.
7
- * @param {number} [fv=0] - The future value, or remaining balance after the last payment. Defaults to 0.
8
- * @param {0|1} [type=0] - Payment timing: 0 = end of period, 1 = beginning of period. Defaults to 0.
16
+ * @param {number} rate - The interest rate for the loan.
17
+ * @param {number} nper - The total number of payments for the loan.
18
+ * @param {number} pv - The present value, or the total amount that a series of
19
+ * future payments is worth now; also known as the principal.
20
+ * @param {number} [fv=0] - The future value, or a cash balance you want to
21
+ * attain after the last payment is made. If `fv `is omitted, it is assumed to
22
+ * be `0 `(zero), that is, the future value of a loan is 0.
23
+ * @param {0|1} [type=0] - The number `0` (zero) or `1` and indicates when
24
+ * payments are due. Set `type` equal to `0` or omitted if payments are due at
25
+ * the end of the period. Set `type` equal to `1` if payments are due at the
26
+ * beginning of the period.
9
27
  * @returns {number} The periodic payment amount.
28
+ *
29
+ * @example
30
+ * pmt(0.08 / 12, 10, 10000); // -1037.03
10
31
  */
11
32
  export function pmt(rate, nper, pv, fv = 0, type = 0) {
12
33
  if (rate === 0) {
13
- return (-fv - pv) / nper;
34
+ return normalizeZero((-fv - pv) / nper);
14
35
  } else {
15
36
  const paymentTimingFactor = type !== 0 ? 1 + rate : 1;
16
37
  const interestFactor = 1 + rate;
17
38
  const compoundFactor = Math.pow(interestFactor, nper);
18
39
 
19
- return (
40
+ return normalizeZero(
20
41
  ((-fv - pv * compoundFactor) /
21
42
  (paymentTimingFactor * (compoundFactor - 1))) *
22
- rate
43
+ rate,
23
44
  );
24
45
  }
25
46
  }
package/src/ppmt.js CHANGED
@@ -1,17 +1,36 @@
1
1
  import { ipmt } from "./ipmt.js";
2
+ import { normalizeZero } from "./normalizeZero.js";
2
3
  import { pmt } from "./pmt.js";
3
4
 
4
5
  /**
5
- * Calculates the principal portion of a payment for a specific period.
6
+ * Calculates the payment on the principal for a given period for an investment
7
+ * based on periodic, constant payments and a constant interest rate.
8
+ *
9
+ * Remarks:
10
+ * - Make sure that you are consistent about the units you use for specifying
11
+ * `rate` and `nper`. If you make monthly payments on a four-year loan at 12
12
+ * percent annual interest, use `0.12 / 12` for `rate` and `4 * 12` for
13
+ * `nper`. If you make annual payments on the same loan, use `0.12` for `rate`
14
+ * and `4` for `nper`.
6
15
  *
7
16
  * @param {number} rate - The interest rate per period.
8
- * @param {number} per - The target period (1-based).
9
- * @param {number} nper - The total number of payment periods.
10
- * @param {number} pv - The present value.
11
- * @param {number} [futureValue=0] - The future value.
12
- * @param {0|1} [type=0] - Payment timing: 0 = end of period, 1 = beginning of period.
13
- * @returns {number} The principal payment for the specified period.
17
+ * @param {number} per - Specifies the period and must be in the range `1` to
18
+ * `nper`.
19
+ * @param {number} nper - The total number of payment periods in an annuity.
20
+ * @param {number} pv - The present value — the total amount that a series of
21
+ * future payments is worth now.
22
+ * @param {number} [futureValue=0] - The future value, or a cash balance you
23
+ * want to attain after the last payment is made. If `fv` is omitted, it is
24
+ * assumed to be `0` (zero), that is, the future value of a loan is 0.
25
+ * @param {0|1} [type=0] - The number `0` (zero) or `1` and indicates when
26
+ * payments are due. Set `type` equal to `0` or omitted if payments are due at
27
+ * the end of the period. Set `type` equal to `1` if payments are due at the
28
+ * beginning of the period.
29
+ * @returns {number} The payment on the principal for the specified period.
14
30
  * @throws {RangeError} When `per` is outside the valid range.
31
+ *
32
+ * @example
33
+ * ppmt(0.1 / 12, 1, 2 * 12, 2000); // -75.62
15
34
  */
16
35
  export function ppmt(rate, per, nper, pv, futureValue = 0, type = 0) {
17
36
  if (per <= 0 || per >= nper + 1) {
@@ -21,5 +40,5 @@ export function ppmt(rate, per, nper, pv, futureValue = 0, type = 0) {
21
40
  const periodicPayment = pmt(rate, nper, pv, futureValue, type);
22
41
  const interestPayment = ipmt(rate, per, nper, pv, futureValue, type);
23
42
 
24
- return periodicPayment - interestPayment;
43
+ return normalizeZero(periodicPayment - interestPayment);
25
44
  }
package/src/pv.js CHANGED
@@ -1,24 +1,61 @@
1
+ import { normalizeZero } from "./normalizeZero.js";
2
+
1
3
  /**
2
- * Calculates the present value of an investment based on a series of regular payments and a constant interest rate.
4
+ * Calculates the present value of a loan or an investment, based on a constant
5
+ * interest rate. You can use PV with either periodic, constant payments (such
6
+ * as a mortgage or other loan), or a future value that's your investment goal.
7
+ *
8
+ * Remarks:
9
+ * - Make sure that you are consistent about the units you use for specifying
10
+ * `rate` and `nper`. If you make monthly payments on a four-year loan at 12
11
+ * percent annual interest, use 12%/12 for `rate` and 4*12 for `nper`. If you
12
+ * make annual payments on the same loan, use 12% for `rate` and 4 for `nper`.
13
+ * - An annuity is a series of constant cash payments made over a continuous
14
+ * period. For example, a car loan or a mortgage is an annuity.
15
+ * - In annuity functions, cash you pay out, such as a deposit to savings, is
16
+ * represented by a negative number. Cash you receive, such as a dividend
17
+ * check, is represented by a positive number. For example, a $1,000 deposit
18
+ * to the bank would be represented by the argument -1000 if you are the
19
+ * depositor and by the argument 1000 if you are the bank.
3
20
  *
4
- * @param {number} rate - The interest rate per period.
5
- * @param {number} nper - The total number of payment periods.
6
- * @param {number} pmt - The payment made each period; cannot change over the life of the annuity.
7
- * @param {number} fv - The future value, or a cash balance you want to attain after the last payment is made. Defaults to 0.
8
- * @param {0|1} [type=0] - The number 0 or 1 and indicates when payments are due. 0 = end of period, 1 = beginning of period. Defaults to 0.
21
+ * @param {number} rate - The interest rate per period. For example, if you
22
+ * obtain an automobile loan at a 10 percent annual interest rate and make
23
+ * monthly payments, your interest rate per month is 10%/12, or 0.83%. You would
24
+ * enter 10%/12, or 0.83%, or 0.0083, into the formula as the rate.
25
+ * @param {number} nper - The total number of payment periods in an annuity. For
26
+ * example, if you get a four-year car loan and make monthly payments, your loan
27
+ * has 4*12 (or 48) periods. You would enter 48 into the formula for nper.
28
+ * @param {number} [pmt=0] - The payment made each period and cannot change over
29
+ * the life of the annuity. Typically, pmt includes principal and interest but
30
+ * no other fees or taxes. For example, the monthly payments on a $10,000,
31
+ * four-year car loan at 12 percent are $263.33. You would enter -263.33 into
32
+ * the formula as the pmt. If pmt is omitted, you must include the fv argument.
33
+ * @param {number} [fv=0] - The future value or a cash balance you want to
34
+ * attain after the last payment is made. If fv is omitted, it is assumed to be
35
+ * 0 (the future value of a loan, for example, is 0). For example, if you want
36
+ * to save $50,000 to pay for a special project in 18 years, then $50,000 is the
37
+ * future value. You could then make a conservative guess at an interest rate
38
+ * and determine how much you must save each month. If fv is omitted, you must
39
+ * include the pmt argument.
40
+ * @param {0|1} [type=0] - The number 0 or 1 and indicates when payments are
41
+ * due. Set `type` equal to `0` or omitted if payments are due at the end of the
42
+ * period. Set `type` equal to `1` if payments are due at the end of the period.
9
43
  * @returns {number} The present value of the investment.
44
+ *
45
+ * @example
46
+ * pv(0.08 / 12, 20 * 12, 500); // -59777.15
10
47
  */
11
- export function pv(rate, nper, pmt, fv = 0, type = 0) {
48
+ export function pv(rate, nper, pmt = 0, fv = 0, type = 0) {
12
49
  if (rate === 0) {
13
- return -pmt * nper - fv;
50
+ return normalizeZero(-pmt * nper - fv);
14
51
  } else {
15
52
  const paymentTimingFactor = type !== 0 ? 1 + rate : 1;
16
53
  const interestFactor = 1 + rate;
17
54
  const compoundFactor = Math.pow(interestFactor, nper);
18
55
 
19
- return (
56
+ return normalizeZero(
20
57
  -(fv + pmt * paymentTimingFactor * ((compoundFactor - 1) / rate)) /
21
- compoundFactor
58
+ compoundFactor,
22
59
  );
23
60
  }
24
61
  }
package/src/rate.js CHANGED
@@ -1,13 +1,30 @@
1
+ import { normalizeZero } from "./normalizeZero.js";
2
+
1
3
  /**
2
- * Evaluates the RATE equation for a candidate rate.
4
+ * Evaluates the annuity equation for a candidate interest rate.
5
+ *
6
+ * Used internally by the `rate()` function to compute the result of the annuity
7
+ * equation for a given rate guess. This function supports both ordinary
8
+ * annuities (payments at end of period) and annuities due (payments at
9
+ * beginning of period), and accounts for present value, future value, and
10
+ * periodic payments.
11
+ *
12
+ * @param {number} rate - Candidate interest rate per period (as a decimal,
13
+ * e.g., `0.1` for 10%).
14
+ * @param {number} nper - Total number of payment periods.
15
+ * @param {number} pmt - Payment made each period (negative for outflows,
16
+ * positive for inflows).
17
+ * @param {number} pv - Present value (the lump sum amount at the start).
18
+ * @param {number} [fv=0] - Future value (the desired balance after the last
19
+ * payment; defaults to `0`).
20
+ * @param {0|1} [type=0] - Payment timing: `0` = end of period (ordinary annuity),
21
+ * `1` = beginning of period (annuity due).
22
+ * @returns {number} The result of the annuity equation for the supplied rate
23
+ * and parameters. A value close to zero indicates a solution for the interest
24
+ * rate.
3
25
  *
4
- * @param {number} rate - Candidate rate.
5
- * @param {number} nper - Total number of periods.
6
- * @param {number} pmt - Periodic payment.
7
- * @param {number} pv - Present value.
8
- * @param {number} [fv=0] - Future value.
9
- * @param {0|1} [type=0] - Payment timing: 0 = end of period, 1 = beginning.
10
- * @returns {number} Equation result for the supplied rate.
26
+ * @example
27
+ * evalRate(0.05, 12, -100, 1000, 0, 0); // 204.1436739778701
11
28
  */
12
29
  function evalRate(rate, nper, pmt, pv, fv = 0, type = 0) {
13
30
  if (rate === 0) {
@@ -26,16 +43,43 @@ function evalRate(rate, nper, pmt, pv, fv = 0, type = 0) {
26
43
  }
27
44
 
28
45
  /**
29
- * Calculates the interest rate per period using iterative approximation.
46
+ * Calculates the interest rate per period of an annuity. The rate is calculated
47
+ * by iteration and can have zero or more solutions. If the successive results
48
+ * of this function do not converge to within 0.0000001 after 128 iterations, a
49
+ * RangeError is thrown.
50
+ *
51
+ * Remarks:
52
+ * - Make sure that you are consistent about the units you use for specifying
53
+ * `rate` and `nper`. If you make monthly payments on a four-year loan at 12
54
+ * percent annual interest, use `0.12 / 12` for `rate` and `4 * 12` for
55
+ * `nper`. If you make annual payments on the same loan, use `0.12` for `rate`
56
+ * and `4` for `nper`.
57
+ *
58
+ * @param {number} nper - The total number of payment periods in an annuity.
59
+ * @param {number} pmt - The payment made each period and cannot change over the
60
+ * life of the annuity. Typically, `pmt` includes principal and interest but no
61
+ * other fees or taxes. If `pmt` is omitted, you must include the `fv` argument
62
+ * for a meaningful equation.
63
+ * @param {number} pv - The present value — the total amount that a series of
64
+ * future payments is worth now.
65
+ * @param {number} [fv=0] - The future value, or a cash balance you want to
66
+ * attain after the last payment is made. If `fv` is omitted, it is assumed to
67
+ * be `0` (the future value of a loan, for example, is 0). If `fv` is omitted,
68
+ * you must include the `pmt` argument.
69
+ * @param {0|1} [type=0] - The number `0` (zero) or `1` and indicates when
70
+ * payments are due. Set `type` equal to `0` or omitted if payments are due at
71
+ * the end of the period. Set `type` equal to `1` if payments are due at the
72
+ * beginning of the period.
73
+ * @param {number} [guess=0.1] - Your guess for what the rate will be. If you
74
+ * omit `guess`, it is assumed to be `0.1` (10 percent). If the calculation does
75
+ * not converge, try different values for `guess`. The calculation usually
76
+ * converges if guess is between `0` and `1`.
77
+ * @returns {number} The calculated interest rate per period of an annuity.
78
+ * @throws {RangeError} When inputs are invalid or the algorithm cannot
79
+ * converge.
30
80
  *
31
- * @param {number} nper - Total number of periods.
32
- * @param {number} pmt - Periodic payment.
33
- * @param {number} pv - Present value.
34
- * @param {number} [fv=0] - Future value.
35
- * @param {0|1} [type=0] - Payment timing: 0 = end of period, 1 = beginning.
36
- * @param {number} [guess=0.1] - Initial guess for rate.
37
- * @returns {number} The calculated rate per period.
38
- * @throws {RangeError} When inputs are invalid or the algorithm cannot converge.
81
+ * @example
82
+ * rate(4 * 12, -200, 8000); // 0.007701472488210098
39
83
  */
40
84
  export function rate(nper, pmt, pv, fv = 0, type = 0, guess = 0.1) {
41
85
  if (nper <= 0) {
@@ -66,7 +110,7 @@ export function rate(nper, pmt, pv, fv = 0, type = 0, guess = 0.1) {
66
110
  y0 = evalRate(rate0, nper, pmt, pv, fv, type);
67
111
 
68
112
  if (Math.abs(y0) < epsilonMax) {
69
- return rate0;
113
+ return normalizeZero(rate0);
70
114
  }
71
115
 
72
116
  const nextY = y0;