Savings domain pack
savingsPlan() builds a recurring balance, contribution, return, fee and withdrawal from existing DECCF calculations, fields and cashflows. The cashflow perspective is the saver: contributions are outflows and actual withdrawals are inflows. Fees reduce the balance and are not recorded as a second cash payment.
const { savingsPlan, withdrawals } = modules.savings;
const fund = savingsPlan({
owner: subject,
calendar: "monthly",
openingBalance: currentSavings,
annualRate: investmentReturn,
contribution: annualDeposit,
contributionPlacement: "end",
withdrawal: withdrawals.byDivisor({ divisor: distributionDivisor }),
});
return defineModel({
name: "savings projection",
currency: currencies.USD,
timeline: periods.monthly({ from: "2026-01", count: 120 }),
preconditions: { currentSavings, investmentReturn, annualDeposit, distributionDivisor },
entities: { subject },
components: { fund: fund.component },
metrics: { ending_balance: (ctx) => ctx.get(fund.balance) },
});Rates are nominal by default, and the configured calendar must match the model calendar. Contributions can be placed at the start or end of each period. feeRate is a period rate charged on opening balance. Withdrawals happen at period end; the actual withdrawal is capped to available funds by default and any shortfall is reported separately. Use insufficientFunds: "error" to reject any such shortfall.
The component returns ordinary field, calculation and flow references for composition. Callback sources may read only declared inputs, calculations and opening accounts. Withdrawal callbacks also receive the already calculated opening balance, return base, growth, fee and available amount. Use an explicit dependsOn for same-period calculation reads.