@retiregolden/engine 0.1.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/LICENSE +661 -0
- package/README.md +79 -0
- package/dist/allocation/assetClasses.d.ts +94 -0
- package/dist/allocation/assetClasses.js +215 -0
- package/dist/decisions/annuitization.d.ts +72 -0
- package/dist/decisions/annuitization.js +184 -0
- package/dist/decisions/decisionFixtures.d.ts +48 -0
- package/dist/decisions/decisionFixtures.js +253 -0
- package/dist/decisions/evaluateCandidate.d.ts +61 -0
- package/dist/decisions/evaluateCandidate.js +193 -0
- package/dist/decisions/generators.d.ts +110 -0
- package/dist/decisions/generators.js +667 -0
- package/dist/decisions/index.d.ts +17 -0
- package/dist/decisions/index.js +17 -0
- package/dist/decisions/insightsAdapter.d.ts +27 -0
- package/dist/decisions/insightsAdapter.js +51 -0
- package/dist/decisions/objectives.d.ts +96 -0
- package/dist/decisions/objectives.js +319 -0
- package/dist/decisions/pensionElection.d.ts +112 -0
- package/dist/decisions/pensionElection.js +239 -0
- package/dist/decisions/search.d.ts +51 -0
- package/dist/decisions/search.js +110 -0
- package/dist/decisions/spendingSolver.d.ts +70 -0
- package/dist/decisions/spendingSolver.js +180 -0
- package/dist/decisions/spiaQuotes.d.ts +21 -0
- package/dist/decisions/spiaQuotes.js +43 -0
- package/dist/decisions/stochastic.d.ts +11 -0
- package/dist/decisions/stochastic.js +66 -0
- package/dist/decisions/swrComparator.d.ts +54 -0
- package/dist/decisions/swrComparator.js +89 -0
- package/dist/decisions/tournament.d.ts +53 -0
- package/dist/decisions/tournament.js +104 -0
- package/dist/decisions/types.d.ts +125 -0
- package/dist/decisions/types.js +12 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +12 -0
- package/dist/insights/detectors/annuitizationHeadroom.d.ts +11 -0
- package/dist/insights/detectors/annuitizationHeadroom.js +77 -0
- package/dist/insights/detectors/assetLocation.d.ts +9 -0
- package/dist/insights/detectors/assetLocation.js +86 -0
- package/dist/insights/detectors/hecmBufferCandidate.d.ts +12 -0
- package/dist/insights/detectors/hecmBufferCandidate.js +76 -0
- package/dist/insights/detectors/incomeFloorFunded.d.ts +8 -0
- package/dist/insights/detectors/incomeFloorFunded.js +52 -0
- package/dist/insights/detectors/irmaaTierEdge.d.ts +2 -0
- package/dist/insights/detectors/irmaaTierEdge.js +102 -0
- package/dist/insights/detectors/pensionElectionPending.d.ts +11 -0
- package/dist/insights/detectors/pensionElectionPending.js +62 -0
- package/dist/insights/detectors/qcdEfficiency.d.ts +2 -0
- package/dist/insights/detectors/qcdEfficiency.js +62 -0
- package/dist/insights/detectors/rothBridgeHeadroom.d.ts +2 -0
- package/dist/insights/detectors/rothBridgeHeadroom.js +77 -0
- package/dist/insights/detectors/spendingGuardrails.d.ts +2 -0
- package/dist/insights/detectors/spendingGuardrails.js +77 -0
- package/dist/insights/detectors/spendingHeadroom.d.ts +12 -0
- package/dist/insights/detectors/spendingHeadroom.js +92 -0
- package/dist/insights/detectors/ssBridgeGap.d.ts +9 -0
- package/dist/insights/detectors/ssBridgeGap.js +92 -0
- package/dist/insights/detectors/stateRelocation.d.ts +12 -0
- package/dist/insights/detectors/stateRelocation.js +99 -0
- package/dist/insights/detectors/widowsPenalty.d.ts +12 -0
- package/dist/insights/detectors/widowsPenalty.js +145 -0
- package/dist/insights/registry.d.ts +4 -0
- package/dist/insights/registry.js +62 -0
- package/dist/insights/runInsights.d.ts +6 -0
- package/dist/insights/runInsights.js +15 -0
- package/dist/insights/types.d.ts +70 -0
- package/dist/insights/types.js +1 -0
- package/dist/ladder/bridge.d.ts +72 -0
- package/dist/ladder/bridge.js +59 -0
- package/dist/ladder/fedInvest.d.ts +55 -0
- package/dist/ladder/fedInvest.js +77 -0
- package/dist/ladder/fundedRatio.d.ts +41 -0
- package/dist/ladder/fundedRatio.js +46 -0
- package/dist/ladder/ladderMath.d.ts +88 -0
- package/dist/ladder/ladderMath.js +140 -0
- package/dist/longevity/ssaPeriod2022.d.ts +10 -0
- package/dist/longevity/ssaPeriod2022.js +51 -0
- package/dist/longevity/types.d.ts +39 -0
- package/dist/longevity/types.js +1 -0
- package/dist/model/migrations.d.ts +20 -0
- package/dist/model/migrations.js +67 -0
- package/dist/model/plan.d.ts +3108 -0
- package/dist/model/plan.js +1514 -0
- package/dist/montecarlo/frontiers.d.ts +22 -0
- package/dist/montecarlo/frontiers.js +61 -0
- package/dist/montecarlo/historicalReturns.d.ts +27 -0
- package/dist/montecarlo/historicalReturns.js +124 -0
- package/dist/montecarlo/historicalSuites.d.ts +45 -0
- package/dist/montecarlo/historicalSuites.js +102 -0
- package/dist/montecarlo/ltcShock.d.ts +42 -0
- package/dist/montecarlo/ltcShock.js +62 -0
- package/dist/montecarlo/marketModels.d.ts +248 -0
- package/dist/montecarlo/marketModels.js +672 -0
- package/dist/montecarlo/mortality.d.ts +31 -0
- package/dist/montecarlo/mortality.js +61 -0
- package/dist/montecarlo/riskBasedGuardrails.d.ts +93 -0
- package/dist/montecarlo/riskBasedGuardrails.js +200 -0
- package/dist/montecarlo/rng.d.ts +18 -0
- package/dist/montecarlo/rng.js +44 -0
- package/dist/montecarlo/run.d.ts +210 -0
- package/dist/montecarlo/run.js +353 -0
- package/dist/montecarlo/sharedPaths.d.ts +43 -0
- package/dist/montecarlo/sharedPaths.js +28 -0
- package/dist/montecarlo/survival.d.ts +49 -0
- package/dist/montecarlo/survival.js +126 -0
- package/dist/params/data/realYieldCurve2026.d.ts +17 -0
- package/dist/params/data/realYieldCurve2026.js +26 -0
- package/dist/params/data/year2026.d.ts +14 -0
- package/dist/params/data/year2026.js +199 -0
- package/dist/params/index.d.ts +63 -0
- package/dist/params/index.js +134 -0
- package/dist/params/provenance.d.ts +29 -0
- package/dist/params/provenance.js +123 -0
- package/dist/params/state/data/year2026.d.ts +18 -0
- package/dist/params/state/data/year2026.js +581 -0
- package/dist/params/state/index.d.ts +13 -0
- package/dist/params/state/index.js +26 -0
- package/dist/params/state/types.d.ts +73 -0
- package/dist/params/state/types.js +10 -0
- package/dist/params/types.d.ts +201 -0
- package/dist/params/types.js +7 -0
- package/dist/projection/annuityForms.d.ts +62 -0
- package/dist/projection/annuityForms.js +87 -0
- package/dist/projection/compare.d.ts +96 -0
- package/dist/projection/compare.js +216 -0
- package/dist/projection/flatTax.d.ts +7 -0
- package/dist/projection/flatTax.js +14 -0
- package/dist/projection/optimizePlan.d.ts +328 -0
- package/dist/projection/optimizePlan.js +1039 -0
- package/dist/projection/relocation.d.ts +149 -0
- package/dist/projection/relocation.js +317 -0
- package/dist/projection/simulate.d.ts +61 -0
- package/dist/projection/simulate.js +2754 -0
- package/dist/projection/types.d.ts +381 -0
- package/dist/projection/types.js +9 -0
- package/dist/rmd/jointLifeTable.d.ts +8 -0
- package/dist/rmd/jointLifeTable.js +663 -0
- package/dist/rmd/rmd.d.ts +29 -0
- package/dist/rmd/rmd.js +34 -0
- package/dist/scenarios/scenarios.d.ts +70 -0
- package/dist/scenarios/scenarios.js +130 -0
- package/dist/socialSecurity/benefitFactor.d.ts +13 -0
- package/dist/socialSecurity/benefitFactor.js +40 -0
- package/dist/socialSecurity/claimFactor.d.ts +33 -0
- package/dist/socialSecurity/claimFactor.js +58 -0
- package/dist/socialSecurity/disability.d.ts +36 -0
- package/dist/socialSecurity/disability.js +42 -0
- package/dist/socialSecurity/familyMaximum.d.ts +38 -0
- package/dist/socialSecurity/familyMaximum.js +48 -0
- package/dist/socialSecurity/maritalBenefits.d.ts +57 -0
- package/dist/socialSecurity/maritalBenefits.js +87 -0
- package/dist/socialSecurity/nra.d.ts +30 -0
- package/dist/socialSecurity/nra.js +83 -0
- package/dist/socialSecurity/piaFromEarnings.d.ts +100 -0
- package/dist/socialSecurity/piaFromEarnings.js +202 -0
- package/dist/socialSecurity/ssaWageData.d.ts +68 -0
- package/dist/socialSecurity/ssaWageData.js +306 -0
- package/dist/socialSecurity/survivorBenefit.d.ts +61 -0
- package/dist/socialSecurity/survivorBenefit.js +61 -0
- package/dist/spending/abw.d.ts +57 -0
- package/dist/spending/abw.js +80 -0
- package/dist/spending/flexibleGoals.d.ts +62 -0
- package/dist/spending/flexibleGoals.js +136 -0
- package/dist/spending/guardrails.d.ts +78 -0
- package/dist/spending/guardrails.js +100 -0
- package/dist/spending/layers.d.ts +81 -0
- package/dist/spending/layers.js +67 -0
- package/dist/spending/shapePresets.d.ts +42 -0
- package/dist/spending/shapePresets.js +77 -0
- package/dist/strategies/accountEligibility.d.ts +81 -0
- package/dist/strategies/accountEligibility.js +98 -0
- package/dist/strategies/inheritedIra.d.ts +45 -0
- package/dist/strategies/inheritedIra.js +43 -0
- package/dist/strategies/iraBasis.d.ts +48 -0
- package/dist/strategies/iraBasis.js +46 -0
- package/dist/strategies/optimizer.d.ts +307 -0
- package/dist/strategies/optimizer.js +551 -0
- package/dist/strategies/rothBasis.d.ts +67 -0
- package/dist/strategies/rothBasis.js +83 -0
- package/dist/strategies/rothConversion.d.ts +55 -0
- package/dist/strategies/rothConversion.js +83 -0
- package/dist/strategies/sepp.d.ts +38 -0
- package/dist/strategies/sepp.js +53 -0
- package/dist/tax/aca.d.ts +40 -0
- package/dist/tax/aca.js +82 -0
- package/dist/tax/federalTax.d.ts +110 -0
- package/dist/tax/federalTax.js +249 -0
- package/dist/tax/medicare.d.ts +20 -0
- package/dist/tax/medicare.js +36 -0
- package/dist/tax/propertySale.d.ts +46 -0
- package/dist/tax/propertySale.js +35 -0
- package/dist/tax/stateTax.d.ts +66 -0
- package/dist/tax/stateTax.js +198 -0
- package/dist/testing/money.d.ts +7 -0
- package/dist/testing/money.js +17 -0
- package/dist/testing/planFixtures.d.ts +29 -0
- package/dist/testing/planFixtures.js +141 -0
- package/package.json +75 -0
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import type { AssetClassId } from '../model/plan.js';
|
|
2
|
+
import type { FilingStatus } from '../params/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Projection engine types. The deterministic annual ledger is the core v2
|
|
5
|
+
* artifact: Monte Carlo (roadmap V4) drives this same simulation with
|
|
6
|
+
* stochastic inputs, never a separate model.
|
|
7
|
+
*
|
|
8
|
+
* @see DOCS/architecture.md (simulation core)
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Per-year stochastic market conditions for one simulation path (roadmap V4).
|
|
12
|
+
* Index 0 = the projection's startYear. Years past the end of a series fall
|
|
13
|
+
* back to the deterministic assumptions, so a short series degrades gracefully.
|
|
14
|
+
*/
|
|
15
|
+
export interface MarketSeries {
|
|
16
|
+
/**
|
|
17
|
+
* Additive percentage-point shock applied each year to every non-cash
|
|
18
|
+
* investable account's expected return (single-factor market model). For
|
|
19
|
+
* allocated accounts it applies to the non-cash share of the blend unless
|
|
20
|
+
* per-class shocks are supplied below.
|
|
21
|
+
*/
|
|
22
|
+
returnShockPct?: number[];
|
|
23
|
+
/** Realized inflation rate (percent) per year, replacing assumptions.inflationPct from startYear on. */
|
|
24
|
+
inflationPct?: number[];
|
|
25
|
+
/**
|
|
26
|
+
* Per-class additive shocks for accounts with an opt-in allocation
|
|
27
|
+
* (asset-allocation-and-return-model-v2, step 6). A class without a series
|
|
28
|
+
* falls back to `returnShockPct` (cash: no shock). Unallocated accounts
|
|
29
|
+
* always use `returnShockPct`, so single-return plans are unaffected.
|
|
30
|
+
*/
|
|
31
|
+
classReturnShockPct?: Partial<Record<AssetClassId, number[]>>;
|
|
32
|
+
}
|
|
33
|
+
export type ProjectedFilingStatus = FilingStatus | 'qualifyingSurvivingSpouse';
|
|
34
|
+
/**
|
|
35
|
+
* QSS uses the joint tax tables, deduction, and AMT exemption. IRMAA is the
|
|
36
|
+
* exception: SSA's threshold tables group qualifying surviving spouses with
|
|
37
|
+
* single/HOH filers (POMS HI 01101.020), so the Medicare premium calculation
|
|
38
|
+
* maps QSS to `single` instead of using this helper.
|
|
39
|
+
*/
|
|
40
|
+
export declare function taxParameterFilingStatus(status: ProjectedFilingStatus): FilingStatus;
|
|
41
|
+
export interface TaxYearInput {
|
|
42
|
+
year: number;
|
|
43
|
+
filingStatus: ProjectedFilingStatus;
|
|
44
|
+
/** Wages, traditional withdrawals, pension/annuity taxable parts, taxable recurring/one-time income. */
|
|
45
|
+
ordinaryIncome: number;
|
|
46
|
+
/** Realized long-term gains (taxable-account withdrawals via basis ratio). */
|
|
47
|
+
capitalGains: number;
|
|
48
|
+
/** Realized gains before federal capital-loss carryforward netting; used by nonconforming states. */
|
|
49
|
+
realizedCapitalGainsBeforeCarryforward?: number;
|
|
50
|
+
/** Taxable interest generated in taxable brokerage accounts (already included in ordinaryIncome). */
|
|
51
|
+
taxableInterestIncome?: number;
|
|
52
|
+
/**
|
|
53
|
+
* Interest on U.S. government obligations (TIPS ladder coupons + inflation
|
|
54
|
+
* accretion), already included in ordinaryIncome AND taxableInterestIncome.
|
|
55
|
+
* Federal tax applies in full (incl. NIIT); every state exempts it, so the
|
|
56
|
+
* state calculator subtracts it from state taxable income.
|
|
57
|
+
*/
|
|
58
|
+
usGovernmentInterest?: number;
|
|
59
|
+
/** Non-qualified dividends generated in taxable brokerage accounts (already included in ordinaryIncome). */
|
|
60
|
+
ordinaryDividends?: number;
|
|
61
|
+
/** Qualified dividends taxed at preferential federal rates but included in AGI/MAGI. */
|
|
62
|
+
qualifiedDividends?: number;
|
|
63
|
+
/** Gross Social Security benefits received. */
|
|
64
|
+
ssBenefits: number;
|
|
65
|
+
/** Living household members aged 65+ this year (drives age-based deductions). */
|
|
66
|
+
peopleAged65Plus: number;
|
|
67
|
+
/** State of residence this year (two-letter code); drives state tax. */
|
|
68
|
+
state?: string;
|
|
69
|
+
/** Part-year state residency allocation for the tax year. */
|
|
70
|
+
stateResidency?: {
|
|
71
|
+
state: string;
|
|
72
|
+
months: number;
|
|
73
|
+
}[];
|
|
74
|
+
/**
|
|
75
|
+
* Portion of ordinaryIncome that is retirement income (pension + annuity
|
|
76
|
+
* taxable part + traditional/RMD distributions, excluding Roth conversions),
|
|
77
|
+
* for state retirement-income exclusions. Federal tax ignores this.
|
|
78
|
+
*/
|
|
79
|
+
retirementIncome?: number;
|
|
80
|
+
/**
|
|
81
|
+
* Private retirement income eligible for the state's private retirement rule.
|
|
82
|
+
* Replaces retirementIncome; the legacy field remains accepted by calculators.
|
|
83
|
+
*/
|
|
84
|
+
privateRetirementIncome?: number;
|
|
85
|
+
/** Public civil/military pension income eligible for the state's public pension rule. */
|
|
86
|
+
publicPensionIncome?: number;
|
|
87
|
+
/** Ages of living household members this year, for age-based state exclusions. */
|
|
88
|
+
agesAlive?: number[];
|
|
89
|
+
/**
|
|
90
|
+
* Itemized-deduction components in nominal dollars (roadmap V8). When present,
|
|
91
|
+
* federal tax uses the greater of the standard deduction and the itemized
|
|
92
|
+
* total. SALT is the user's estimated deductible state/local/property tax
|
|
93
|
+
* (kept as an input to avoid a circular dependency on the computed state tax).
|
|
94
|
+
*/
|
|
95
|
+
itemizedDeductions?: {
|
|
96
|
+
stateAndLocalTaxes: number;
|
|
97
|
+
mortgageInterest: number;
|
|
98
|
+
charitable: number;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Advanced calculator-only AMT preference/adjustment items. Projection does
|
|
102
|
+
* not populate this from Plan fields today; the federal tax calculator already
|
|
103
|
+
* derives standard-deduction and itemized-SALT add-backs from normal inputs.
|
|
104
|
+
*/
|
|
105
|
+
amtPreferenceItems?: number;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Pluggable tax computation. V1 ships a flat placeholder; the real federal
|
|
109
|
+
* engine (roadmap V2) implements the same interface.
|
|
110
|
+
*/
|
|
111
|
+
export interface TaxCalculator {
|
|
112
|
+
compute(input: TaxYearInput): number;
|
|
113
|
+
}
|
|
114
|
+
export interface PersonYearState {
|
|
115
|
+
personId: string;
|
|
116
|
+
/** Age attained during this calendar year (year − birth year). */
|
|
117
|
+
ageAttained: number;
|
|
118
|
+
/** Alive while ageAttained ≤ longevity planning age. */
|
|
119
|
+
alive: boolean;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Per-year linearization inputs the V8 optimizer needs from a baseline ledger
|
|
123
|
+
* run (roadmap V8). Emitted via `SimulateOptions.captureOptimizerInputs`; a
|
|
124
|
+
* no-op unless that sink is supplied, so the normal projection is unaffected.
|
|
125
|
+
* The optimizer's MILP carries balances forward itself, so only these exogenous
|
|
126
|
+
* quantities (not per-year balances) are probed. @see strategies/optimizer.ts
|
|
127
|
+
*/
|
|
128
|
+
export interface OptimizerYearProbe {
|
|
129
|
+
year: number;
|
|
130
|
+
/**
|
|
131
|
+
* Ordinary taxable income EXCLUDING any traditional-account distribution or
|
|
132
|
+
* Roth conversion, plus the baseline taxable Social-Security portion (which
|
|
133
|
+
* the LP holds fixed rather than re-deriving as conversions change).
|
|
134
|
+
*/
|
|
135
|
+
ordinaryIncomeBase: number;
|
|
136
|
+
/** Total cash uses besides tax/penalties this year (expenses + contributions). */
|
|
137
|
+
spendingNeed: number;
|
|
138
|
+
/** Non-account cash inflows this year (income streams: SS, pensions, etc.). */
|
|
139
|
+
exogenousCash: number;
|
|
140
|
+
/** Forced RMD this year in the baseline (0 when not age-eligible). */
|
|
141
|
+
rmd: number;
|
|
142
|
+
/** Start-of-year owner-convertible traditional balance, used to recover the owner RMD divisor ratio. */
|
|
143
|
+
startTraditional: number;
|
|
144
|
+
/** Forced inherited-traditional distribution this year in the baseline. */
|
|
145
|
+
inheritedDistribution: number;
|
|
146
|
+
/** Start-of-year inherited traditional balance, used to recover the inherited distribution divisor ratio. */
|
|
147
|
+
startInheritedTraditional: number;
|
|
148
|
+
/** Living people aged 65+ (drives the standard-deduction age addition). */
|
|
149
|
+
peopleAged65Plus: number;
|
|
150
|
+
/**
|
|
151
|
+
* Deposits landing in owner-traditional accounts this year: scheduled
|
|
152
|
+
* employee contributions plus employer match into traditional. The cash cost
|
|
153
|
+
* of employee contributions is already inside `spendingNeed`; this is the
|
|
154
|
+
* asset side, so the optimizer's compressed balances receive the same money
|
|
155
|
+
* the exact ledger does.
|
|
156
|
+
*/
|
|
157
|
+
traditionalInflow: number;
|
|
158
|
+
/** Deposits landing in Roth/taxable/cash/equity-comp/HSA accounts this year (contributions + any Roth-employer match). */
|
|
159
|
+
otherInflow: number;
|
|
160
|
+
/**
|
|
161
|
+
* Subset of `otherInflow` that lands specifically in taxable brokerage /
|
|
162
|
+
* equity-comp accounts this year. The optimizer (Step 2, taxable-gain
|
|
163
|
+
* realization) splits the lumped "other" bucket into a taxable bucket — whose
|
|
164
|
+
* withdrawals realize LTCG — and a tax-free bucket (Roth/cash/HSA); this is
|
|
165
|
+
* the taxable side of the split. `otherInflow − taxableInflow` is the tax-free
|
|
166
|
+
* side.
|
|
167
|
+
*/
|
|
168
|
+
taxableInflow: number;
|
|
169
|
+
/**
|
|
170
|
+
* Gross Social Security benefits received this year. Powers the optimizer's
|
|
171
|
+
* in-solve taxable-SS PWL (Step 3): with it, the LP re-derives the 0/50/85%
|
|
172
|
+
* provisional-income phase-in as conversions change instead of holding the
|
|
173
|
+
* probe-time taxable portion fixed.
|
|
174
|
+
*/
|
|
175
|
+
ssBenefits: number;
|
|
176
|
+
/**
|
|
177
|
+
* The taxable-SS portion folded into `ordinaryIncomeBase` at the probe run
|
|
178
|
+
* (the amount the in-solve PWL replaces with its own variable).
|
|
179
|
+
*/
|
|
180
|
+
taxableSsBase: number;
|
|
181
|
+
/**
|
|
182
|
+
* Realized capital gains + qualified dividends at the probe run, EXCLUDING
|
|
183
|
+
* gains from taxable-account withdrawals (the optimizer re-decides those as
|
|
184
|
+
* its own variable, so including them would double-count). Counts toward the
|
|
185
|
+
* SS phase-in's provisional income and IRMAA MAGI but is not in
|
|
186
|
+
* `ordinaryIncomeBase`.
|
|
187
|
+
*/
|
|
188
|
+
capitalGainsBase: number;
|
|
189
|
+
/**
|
|
190
|
+
* True when the ledger priced this premium year's IRMAA under an SSA-44
|
|
191
|
+
* redetermination (the two years after a qualifying life-changing event, see
|
|
192
|
+
* `healthcareConfigSchema.ssa44`). The optimizer's lookback treatment shifts
|
|
193
|
+
* this year's IRMAA MAGI source from year (t−2) to year (t−1) — the in-solve
|
|
194
|
+
* stand-in for the ledger's min(lookback, prior year).
|
|
195
|
+
*/
|
|
196
|
+
ssa44IrmaaRedetermination: boolean;
|
|
197
|
+
}
|
|
198
|
+
export interface YearIncomes {
|
|
199
|
+
wages: number;
|
|
200
|
+
socialSecurity: number;
|
|
201
|
+
pension: number;
|
|
202
|
+
annuity: number;
|
|
203
|
+
/** TIPS-ladder cash flows (coupons + maturing principal); 0 when the plan has no ladders. */
|
|
204
|
+
tipsLadder: number;
|
|
205
|
+
recurring: number;
|
|
206
|
+
oneTime: number;
|
|
207
|
+
taxableInterest: number;
|
|
208
|
+
ordinaryDividends: number;
|
|
209
|
+
qualifiedDividends: number;
|
|
210
|
+
taxableYield: number;
|
|
211
|
+
total: number;
|
|
212
|
+
}
|
|
213
|
+
export interface YearExpenses {
|
|
214
|
+
baseSpending: number;
|
|
215
|
+
oneTimeGoals: number;
|
|
216
|
+
/** Debt principal & interest (incl. any scheduled lump-sum payoff this year). */
|
|
217
|
+
debtService: number;
|
|
218
|
+
/** Property tax + homeowner's insurance on owned properties (continues after mortgage payoff). */
|
|
219
|
+
propertyCosts: number;
|
|
220
|
+
/** Pre-65 marketplace premiums net of ACA credit + Medicare (Part B incl. IRMAA, Part D surcharge, extras). */
|
|
221
|
+
healthcare: number;
|
|
222
|
+
/** Level (fixed-nominal) insurance premiums charged this year (LTC + permanent life). */
|
|
223
|
+
insurancePremiums: number;
|
|
224
|
+
/** Gross LTC care-episode cost this year (additive spending spike, before any policy offset). */
|
|
225
|
+
careCost: number;
|
|
226
|
+
/** LTC policy benefit applied against careCost this year (income-tax-free; reduces net spending). */
|
|
227
|
+
ltcBenefit: number;
|
|
228
|
+
/**
|
|
229
|
+
* Must-fund spending this year: system-computed costs (healthcare, debt,
|
|
230
|
+
* property, insurance, net LTC) + the required-floor lifestyle layer +
|
|
231
|
+
* required-classified funded goals. A guardrail policy never cuts below this.
|
|
232
|
+
*/
|
|
233
|
+
requiredSpending: number;
|
|
234
|
+
/**
|
|
235
|
+
* Full intended spending with no guardrail cut: requiredSpending + the full
|
|
236
|
+
* target lifestyle layer + target-classified goals. Equals `total` for older
|
|
237
|
+
* fixed-target plans without ideal/excess layers.
|
|
238
|
+
*/
|
|
239
|
+
targetSpending: number;
|
|
240
|
+
/** Incremental ideal spending intended this year above the target lifestyle. */
|
|
241
|
+
idealSpending: number;
|
|
242
|
+
/** Incremental excess/opportunistic spending intended this year above ideal. */
|
|
243
|
+
excessSpending: number;
|
|
244
|
+
/** Full intended spending with no guardrail cut across required/target/ideal/excess. */
|
|
245
|
+
intendedSpending: number;
|
|
246
|
+
/** Discretionary multiplier the guardrail policy applied this year (1 = no cut). */
|
|
247
|
+
guardrailFactor: number;
|
|
248
|
+
total: number;
|
|
249
|
+
}
|
|
250
|
+
/** Withdrawal totals by account category (sequential order: cash → taxable → traditional → roth → hsa). */
|
|
251
|
+
export interface YearWithdrawals {
|
|
252
|
+
cash: number;
|
|
253
|
+
taxable: number;
|
|
254
|
+
traditional: number;
|
|
255
|
+
roth: number;
|
|
256
|
+
hsa: number;
|
|
257
|
+
total: number;
|
|
258
|
+
}
|
|
259
|
+
export interface YearResult {
|
|
260
|
+
year: number;
|
|
261
|
+
people: PersonYearState[];
|
|
262
|
+
/** Filing treatment used for federal/state thresholds in this projection year. */
|
|
263
|
+
filingStatus: ProjectedFilingStatus;
|
|
264
|
+
incomes: YearIncomes;
|
|
265
|
+
expenses: YearExpenses;
|
|
266
|
+
/** Contributions actually made this year (after IRS caps). */
|
|
267
|
+
contributions: number;
|
|
268
|
+
/** Employer match contributions made this year. */
|
|
269
|
+
employerMatch: number;
|
|
270
|
+
/** Forced traditional-account distributions (included in withdrawals.traditional). */
|
|
271
|
+
rmd: number;
|
|
272
|
+
/** Penalty-free 72(t) SEPP distributions this year (included in withdrawals.traditional). */
|
|
273
|
+
sepp: number;
|
|
274
|
+
/** Forced inherited-IRA distributions this year under the 10-year rule (included in withdrawals.traditional). */
|
|
275
|
+
inheritedDistribution: number;
|
|
276
|
+
/** Qualified charitable distributions routed out of the RMD (excluded from income). */
|
|
277
|
+
qcd: number;
|
|
278
|
+
/** Dollars moved traditional → Roth this year (taxed as ordinary income, no penalty). */
|
|
279
|
+
rothConversion: number;
|
|
280
|
+
/** Early-withdrawal penalties (10% traditional pre-59½, 20% HSA non-medical pre-65); not in `tax`. */
|
|
281
|
+
penalties: number;
|
|
282
|
+
/** MAGI realized this year (drives IRMAA two years later and the ACA credit). */
|
|
283
|
+
magi: number;
|
|
284
|
+
/** Medicare premiums charged this year (Part B incl. IRMAA + Part D surcharge, all covered people; excludes the user's "extras"). */
|
|
285
|
+
medicarePremiums: number;
|
|
286
|
+
/** IRMAA tier the year's Medicare premiums were priced at (0 = standard premium; 1–5 = surcharge tiers). */
|
|
287
|
+
irmaaTier: number;
|
|
288
|
+
/** Federal alternative minimum tax included in `tax` when the planning-grade AMT screen binds. */
|
|
289
|
+
amt: number;
|
|
290
|
+
/** Additional long-term gains realizable this year still taxed at 0% (gain-harvesting advisory). */
|
|
291
|
+
ltcgZeroHeadroom: number;
|
|
292
|
+
/** Benefits withheld by the retirement earnings test (working early claimants). */
|
|
293
|
+
ssEarningsTestWithheld: number;
|
|
294
|
+
/** SSDI paid this year (included in `incomes.socialSecurity`; 0 when disability is off). */
|
|
295
|
+
ssdiPaid: number;
|
|
296
|
+
tax: number;
|
|
297
|
+
withdrawals: YearWithdrawals;
|
|
298
|
+
/** Realized capital gains embedded in taxable withdrawals + annual-rebalance sales. */
|
|
299
|
+
realizedGains: number;
|
|
300
|
+
/** Taxable account interest + dividends generated this year. */
|
|
301
|
+
taxableYield: number;
|
|
302
|
+
/** Capital-loss carryforward applied against this year's realized gains. */
|
|
303
|
+
capitalLossUsedAgainstGains: number;
|
|
304
|
+
/** Capital-loss carryforward applied against ordinary income (≤ annual limit). */
|
|
305
|
+
capitalLossUsedAgainstOrdinary: number;
|
|
306
|
+
/** Capital-loss carryforward balance carried into next year. */
|
|
307
|
+
capitalLossCarryforwardRemaining: number;
|
|
308
|
+
/** Surplus cashflow invested (into cash, else taxable, else unassigned). */
|
|
309
|
+
surplusInvested: number;
|
|
310
|
+
/** Spending the portfolio could not cover this year. */
|
|
311
|
+
shortfall: number;
|
|
312
|
+
/**
|
|
313
|
+
* Required-floor spending the portfolio could not cover this year — the
|
|
314
|
+
* serious failure signal (a portfolio shortfall is charged to the
|
|
315
|
+
* discretionary layer first and only reaches the floor once it is exhausted).
|
|
316
|
+
*/
|
|
317
|
+
requiredShortfall: number;
|
|
318
|
+
/**
|
|
319
|
+
* Target-lifestyle miss this year: a guardrail's deliberate discretionary cut
|
|
320
|
+
* plus any portfolio shortfall that ate into funded discretionary spending.
|
|
321
|
+
* Not the same as running out of money for essentials (see requiredShortfall).
|
|
322
|
+
*/
|
|
323
|
+
targetShortfall: number;
|
|
324
|
+
/** Ideal spending not funded this year. */
|
|
325
|
+
idealShortfall: number;
|
|
326
|
+
/** Excess/opportunistic spending not funded this year. */
|
|
327
|
+
excessShortfall: number;
|
|
328
|
+
/** Guardrail action taken this year under a withdrawal-rate policy ('hold' when inactive). */
|
|
329
|
+
guardrailAction: 'hold' | 'cut' | 'raise';
|
|
330
|
+
/** Flexible one-time-goal outcomes this year (all 0 outside guardrail mode). */
|
|
331
|
+
flexibleGoals: {
|
|
332
|
+
funded: number;
|
|
333
|
+
partiallyFunded: number;
|
|
334
|
+
deferred: number;
|
|
335
|
+
skipped: number;
|
|
336
|
+
fundedAmount: number;
|
|
337
|
+
unfundedAmount: number;
|
|
338
|
+
};
|
|
339
|
+
/** End-of-year balance per account id (after flows and growth). */
|
|
340
|
+
balances: Record<string, number>;
|
|
341
|
+
/** Cash + taxable + traditional + roth + hsa (+ unassigned). */
|
|
342
|
+
investableTotal: number;
|
|
343
|
+
/** Permanent-life cash value at year end (an asset, but held out of withdrawals). */
|
|
344
|
+
insuranceCashValue: number;
|
|
345
|
+
/**
|
|
346
|
+
* Remaining TIPS-ladder principal at year end (nominal book value: unmatured
|
|
347
|
+
* face × inflation to date, ignoring rate moves). A dedicated asset held out
|
|
348
|
+
* of withdrawals — counted in netWorth, not investableTotal. 0 without ladders.
|
|
349
|
+
*/
|
|
350
|
+
ladderValue: number;
|
|
351
|
+
/** Income-tax-free life death benefit paid into the estate/beneficiary this year. */
|
|
352
|
+
deathBenefit: number;
|
|
353
|
+
/** Tax-free HECM line-of-credit loan proceeds drawn this year (0 without a HECM). */
|
|
354
|
+
hecmDraw: number;
|
|
355
|
+
/** Total HECM loan balance at year end, before the non-recourse floor (0 without a HECM). */
|
|
356
|
+
hecmLoanBalance: number;
|
|
357
|
+
/**
|
|
358
|
+
* investableTotal + property + insuranceCashValue + ladderValue − debt −
|
|
359
|
+
* HECM loans (each capped at its home's value: non-recourse).
|
|
360
|
+
*/
|
|
361
|
+
netWorth: number;
|
|
362
|
+
}
|
|
363
|
+
export interface ProjectionResult {
|
|
364
|
+
startYear: number;
|
|
365
|
+
endYear: number;
|
|
366
|
+
years: YearResult[];
|
|
367
|
+
/** First year with any shortfall, else null. */
|
|
368
|
+
depletionYear: number | null;
|
|
369
|
+
endingInvestable: number;
|
|
370
|
+
endingNetWorth: number;
|
|
371
|
+
/**
|
|
372
|
+
* Remaining nondeductible (after-tax) traditional-IRA basis at the horizon,
|
|
373
|
+
* capped per owner at their ending aggregated-IRA balance. This is after-tax
|
|
374
|
+
* money an heir inherits tax-free (they file a separate Form 8606), so the
|
|
375
|
+
* after-tax estate metric excludes it from the traditional heir tax. 0 when
|
|
376
|
+
* no IRA carries nondeductible basis.
|
|
377
|
+
*/
|
|
378
|
+
endingNondeductibleIraBasis: number;
|
|
379
|
+
/** Modeling caveats hit during this run (e.g. SS stream without a PIA). */
|
|
380
|
+
warnings: string[];
|
|
381
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QSS uses the joint tax tables, deduction, and AMT exemption. IRMAA is the
|
|
3
|
+
* exception: SSA's threshold tables group qualifying surviving spouses with
|
|
4
|
+
* single/HOH filers (POMS HI 01101.020), so the Medicare premium calculation
|
|
5
|
+
* maps QSS to `single` instead of using this helper.
|
|
6
|
+
*/
|
|
7
|
+
export function taxParameterFilingStatus(status) {
|
|
8
|
+
return status === 'single' ? 'single' : 'marriedFilingJointly';
|
|
9
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 26 CFR 1.401(a)(9)-9(d) Table 3 (Joint and Last Survivor Table).
|
|
3
|
+
*
|
|
4
|
+
* Source: https://www.ecfr.gov/current/title-26/section-1.401(a)(9)-9, Table 3.
|
|
5
|
+
* Effective for RMD distribution years 2022+; extracted 2026-06-29.
|
|
6
|
+
* Rows are owner ages 72-120+; columns are spouse ages 0-120+.
|
|
7
|
+
*/
|
|
8
|
+
export declare function jointLifeTableDivisor(ownerAge: number, spouseAge: number): number | undefined;
|