Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import { applyRate } from "./money_apply_rate.ts"; ← from money.apply-rate ^1.0.0 · built alongside by fune
import { type Money, money } from "./money_amount.ts"; ← from money.amount ^1.0.0 · built alongside by fune
import { type FeeDrag, type FeeDragYear } from "./invest_fee_drag_types.ts";
/**
* The same investment grown twice, with and without an annual charge taken
* on the year-end value, rounding to minor units every year as a statement
* would.
*/
export function feeDrag(startValue: Money, annualGrowthBasisPoints: number, annualFeeBasisPoints: number, years: number): FeeDrag {
if (!Number.isInteger(startValue.minor) || startValue.minor < 0) {
throw new RangeError(`startValue must be whole minor units, 0 or more; received ${startValue.minor}`);
}
if (!Number.isInteger(annualGrowthBasisPoints) || annualGrowthBasisPoints < -10000) {
throw new RangeError(`annualGrowthBasisPoints must be a whole number, -10000 or more; received ${annualGrowthBasisPoints}`);
}
if (!Number.isInteger(annualFeeBasisPoints) || annualFeeBasisPoints < 0 || annualFeeBasisPoints > 10000) {
throw new RangeError(`annualFeeBasisPoints must be a whole number from 0 to 10000; received ${annualFeeBasisPoints}`);
}
if (!Number.isInteger(years) || years < 0 || years > 100) {
throw new RangeError(`years must be a whole number from 0 to 100; received ${years}`);
}
const currency = startValue.currency;
let without = startValue.minor;
let withFee = startValue.minor;
let fees = 0;
const schedule: FeeDragYear[] = [];
for (let year = 1; year <= years; year++) {
without += applyRate(money(without, currency), annualGrowthBasisPoints, "half-up").minor;
const grown = withFee + applyRate(money(withFee, currency), annualGrowthBasisPoints, "half-up").minor;
const fee = applyRate(money(grown, currency), annualFeeBasisPoints, "half-up").minor;
withFee = grown - fee;
fees += fee;
schedule.push({
year,
valueWithoutFee: money(without, currency),
valueWithFee: money(withFee, currency),
fee: money(fee, currency),
});
}
const drag = without - withFee;
let dragBasisPoints = 0;
if (without > 0) {
const n = BigInt(drag) * 10000n;
const d = BigInt(without);
const a = n < 0n ? -n : n;
let q = a / d;
if (2n * (a - q * d) >= d) q += 1n;
dragBasisPoints = Number(n < 0n ? -q : q);
}
return {
valueWithoutFee: money(without, currency),
valueWithFee: money(withFee, currency),
drag: money(drag, currency),
dragBasisPoints,
feesPaid: money(fees, currency),
schedule,
};
}