@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.
Files changed (199) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +79 -0
  3. package/dist/allocation/assetClasses.d.ts +94 -0
  4. package/dist/allocation/assetClasses.js +215 -0
  5. package/dist/decisions/annuitization.d.ts +72 -0
  6. package/dist/decisions/annuitization.js +184 -0
  7. package/dist/decisions/decisionFixtures.d.ts +48 -0
  8. package/dist/decisions/decisionFixtures.js +253 -0
  9. package/dist/decisions/evaluateCandidate.d.ts +61 -0
  10. package/dist/decisions/evaluateCandidate.js +193 -0
  11. package/dist/decisions/generators.d.ts +110 -0
  12. package/dist/decisions/generators.js +667 -0
  13. package/dist/decisions/index.d.ts +17 -0
  14. package/dist/decisions/index.js +17 -0
  15. package/dist/decisions/insightsAdapter.d.ts +27 -0
  16. package/dist/decisions/insightsAdapter.js +51 -0
  17. package/dist/decisions/objectives.d.ts +96 -0
  18. package/dist/decisions/objectives.js +319 -0
  19. package/dist/decisions/pensionElection.d.ts +112 -0
  20. package/dist/decisions/pensionElection.js +239 -0
  21. package/dist/decisions/search.d.ts +51 -0
  22. package/dist/decisions/search.js +110 -0
  23. package/dist/decisions/spendingSolver.d.ts +70 -0
  24. package/dist/decisions/spendingSolver.js +180 -0
  25. package/dist/decisions/spiaQuotes.d.ts +21 -0
  26. package/dist/decisions/spiaQuotes.js +43 -0
  27. package/dist/decisions/stochastic.d.ts +11 -0
  28. package/dist/decisions/stochastic.js +66 -0
  29. package/dist/decisions/swrComparator.d.ts +54 -0
  30. package/dist/decisions/swrComparator.js +89 -0
  31. package/dist/decisions/tournament.d.ts +53 -0
  32. package/dist/decisions/tournament.js +104 -0
  33. package/dist/decisions/types.d.ts +125 -0
  34. package/dist/decisions/types.js +12 -0
  35. package/dist/index.d.ts +13 -0
  36. package/dist/index.js +12 -0
  37. package/dist/insights/detectors/annuitizationHeadroom.d.ts +11 -0
  38. package/dist/insights/detectors/annuitizationHeadroom.js +77 -0
  39. package/dist/insights/detectors/assetLocation.d.ts +9 -0
  40. package/dist/insights/detectors/assetLocation.js +86 -0
  41. package/dist/insights/detectors/hecmBufferCandidate.d.ts +12 -0
  42. package/dist/insights/detectors/hecmBufferCandidate.js +76 -0
  43. package/dist/insights/detectors/incomeFloorFunded.d.ts +8 -0
  44. package/dist/insights/detectors/incomeFloorFunded.js +52 -0
  45. package/dist/insights/detectors/irmaaTierEdge.d.ts +2 -0
  46. package/dist/insights/detectors/irmaaTierEdge.js +102 -0
  47. package/dist/insights/detectors/pensionElectionPending.d.ts +11 -0
  48. package/dist/insights/detectors/pensionElectionPending.js +62 -0
  49. package/dist/insights/detectors/qcdEfficiency.d.ts +2 -0
  50. package/dist/insights/detectors/qcdEfficiency.js +62 -0
  51. package/dist/insights/detectors/rothBridgeHeadroom.d.ts +2 -0
  52. package/dist/insights/detectors/rothBridgeHeadroom.js +77 -0
  53. package/dist/insights/detectors/spendingGuardrails.d.ts +2 -0
  54. package/dist/insights/detectors/spendingGuardrails.js +77 -0
  55. package/dist/insights/detectors/spendingHeadroom.d.ts +12 -0
  56. package/dist/insights/detectors/spendingHeadroom.js +92 -0
  57. package/dist/insights/detectors/ssBridgeGap.d.ts +9 -0
  58. package/dist/insights/detectors/ssBridgeGap.js +92 -0
  59. package/dist/insights/detectors/stateRelocation.d.ts +12 -0
  60. package/dist/insights/detectors/stateRelocation.js +99 -0
  61. package/dist/insights/detectors/widowsPenalty.d.ts +12 -0
  62. package/dist/insights/detectors/widowsPenalty.js +145 -0
  63. package/dist/insights/registry.d.ts +4 -0
  64. package/dist/insights/registry.js +62 -0
  65. package/dist/insights/runInsights.d.ts +6 -0
  66. package/dist/insights/runInsights.js +15 -0
  67. package/dist/insights/types.d.ts +70 -0
  68. package/dist/insights/types.js +1 -0
  69. package/dist/ladder/bridge.d.ts +72 -0
  70. package/dist/ladder/bridge.js +59 -0
  71. package/dist/ladder/fedInvest.d.ts +55 -0
  72. package/dist/ladder/fedInvest.js +77 -0
  73. package/dist/ladder/fundedRatio.d.ts +41 -0
  74. package/dist/ladder/fundedRatio.js +46 -0
  75. package/dist/ladder/ladderMath.d.ts +88 -0
  76. package/dist/ladder/ladderMath.js +140 -0
  77. package/dist/longevity/ssaPeriod2022.d.ts +10 -0
  78. package/dist/longevity/ssaPeriod2022.js +51 -0
  79. package/dist/longevity/types.d.ts +39 -0
  80. package/dist/longevity/types.js +1 -0
  81. package/dist/model/migrations.d.ts +20 -0
  82. package/dist/model/migrations.js +67 -0
  83. package/dist/model/plan.d.ts +3108 -0
  84. package/dist/model/plan.js +1514 -0
  85. package/dist/montecarlo/frontiers.d.ts +22 -0
  86. package/dist/montecarlo/frontiers.js +61 -0
  87. package/dist/montecarlo/historicalReturns.d.ts +27 -0
  88. package/dist/montecarlo/historicalReturns.js +124 -0
  89. package/dist/montecarlo/historicalSuites.d.ts +45 -0
  90. package/dist/montecarlo/historicalSuites.js +102 -0
  91. package/dist/montecarlo/ltcShock.d.ts +42 -0
  92. package/dist/montecarlo/ltcShock.js +62 -0
  93. package/dist/montecarlo/marketModels.d.ts +248 -0
  94. package/dist/montecarlo/marketModels.js +672 -0
  95. package/dist/montecarlo/mortality.d.ts +31 -0
  96. package/dist/montecarlo/mortality.js +61 -0
  97. package/dist/montecarlo/riskBasedGuardrails.d.ts +93 -0
  98. package/dist/montecarlo/riskBasedGuardrails.js +200 -0
  99. package/dist/montecarlo/rng.d.ts +18 -0
  100. package/dist/montecarlo/rng.js +44 -0
  101. package/dist/montecarlo/run.d.ts +210 -0
  102. package/dist/montecarlo/run.js +353 -0
  103. package/dist/montecarlo/sharedPaths.d.ts +43 -0
  104. package/dist/montecarlo/sharedPaths.js +28 -0
  105. package/dist/montecarlo/survival.d.ts +49 -0
  106. package/dist/montecarlo/survival.js +126 -0
  107. package/dist/params/data/realYieldCurve2026.d.ts +17 -0
  108. package/dist/params/data/realYieldCurve2026.js +26 -0
  109. package/dist/params/data/year2026.d.ts +14 -0
  110. package/dist/params/data/year2026.js +199 -0
  111. package/dist/params/index.d.ts +63 -0
  112. package/dist/params/index.js +134 -0
  113. package/dist/params/provenance.d.ts +29 -0
  114. package/dist/params/provenance.js +123 -0
  115. package/dist/params/state/data/year2026.d.ts +18 -0
  116. package/dist/params/state/data/year2026.js +581 -0
  117. package/dist/params/state/index.d.ts +13 -0
  118. package/dist/params/state/index.js +26 -0
  119. package/dist/params/state/types.d.ts +73 -0
  120. package/dist/params/state/types.js +10 -0
  121. package/dist/params/types.d.ts +201 -0
  122. package/dist/params/types.js +7 -0
  123. package/dist/projection/annuityForms.d.ts +62 -0
  124. package/dist/projection/annuityForms.js +87 -0
  125. package/dist/projection/compare.d.ts +96 -0
  126. package/dist/projection/compare.js +216 -0
  127. package/dist/projection/flatTax.d.ts +7 -0
  128. package/dist/projection/flatTax.js +14 -0
  129. package/dist/projection/optimizePlan.d.ts +328 -0
  130. package/dist/projection/optimizePlan.js +1039 -0
  131. package/dist/projection/relocation.d.ts +149 -0
  132. package/dist/projection/relocation.js +317 -0
  133. package/dist/projection/simulate.d.ts +61 -0
  134. package/dist/projection/simulate.js +2754 -0
  135. package/dist/projection/types.d.ts +381 -0
  136. package/dist/projection/types.js +9 -0
  137. package/dist/rmd/jointLifeTable.d.ts +8 -0
  138. package/dist/rmd/jointLifeTable.js +663 -0
  139. package/dist/rmd/rmd.d.ts +29 -0
  140. package/dist/rmd/rmd.js +34 -0
  141. package/dist/scenarios/scenarios.d.ts +70 -0
  142. package/dist/scenarios/scenarios.js +130 -0
  143. package/dist/socialSecurity/benefitFactor.d.ts +13 -0
  144. package/dist/socialSecurity/benefitFactor.js +40 -0
  145. package/dist/socialSecurity/claimFactor.d.ts +33 -0
  146. package/dist/socialSecurity/claimFactor.js +58 -0
  147. package/dist/socialSecurity/disability.d.ts +36 -0
  148. package/dist/socialSecurity/disability.js +42 -0
  149. package/dist/socialSecurity/familyMaximum.d.ts +38 -0
  150. package/dist/socialSecurity/familyMaximum.js +48 -0
  151. package/dist/socialSecurity/maritalBenefits.d.ts +57 -0
  152. package/dist/socialSecurity/maritalBenefits.js +87 -0
  153. package/dist/socialSecurity/nra.d.ts +30 -0
  154. package/dist/socialSecurity/nra.js +83 -0
  155. package/dist/socialSecurity/piaFromEarnings.d.ts +100 -0
  156. package/dist/socialSecurity/piaFromEarnings.js +202 -0
  157. package/dist/socialSecurity/ssaWageData.d.ts +68 -0
  158. package/dist/socialSecurity/ssaWageData.js +306 -0
  159. package/dist/socialSecurity/survivorBenefit.d.ts +61 -0
  160. package/dist/socialSecurity/survivorBenefit.js +61 -0
  161. package/dist/spending/abw.d.ts +57 -0
  162. package/dist/spending/abw.js +80 -0
  163. package/dist/spending/flexibleGoals.d.ts +62 -0
  164. package/dist/spending/flexibleGoals.js +136 -0
  165. package/dist/spending/guardrails.d.ts +78 -0
  166. package/dist/spending/guardrails.js +100 -0
  167. package/dist/spending/layers.d.ts +81 -0
  168. package/dist/spending/layers.js +67 -0
  169. package/dist/spending/shapePresets.d.ts +42 -0
  170. package/dist/spending/shapePresets.js +77 -0
  171. package/dist/strategies/accountEligibility.d.ts +81 -0
  172. package/dist/strategies/accountEligibility.js +98 -0
  173. package/dist/strategies/inheritedIra.d.ts +45 -0
  174. package/dist/strategies/inheritedIra.js +43 -0
  175. package/dist/strategies/iraBasis.d.ts +48 -0
  176. package/dist/strategies/iraBasis.js +46 -0
  177. package/dist/strategies/optimizer.d.ts +307 -0
  178. package/dist/strategies/optimizer.js +551 -0
  179. package/dist/strategies/rothBasis.d.ts +67 -0
  180. package/dist/strategies/rothBasis.js +83 -0
  181. package/dist/strategies/rothConversion.d.ts +55 -0
  182. package/dist/strategies/rothConversion.js +83 -0
  183. package/dist/strategies/sepp.d.ts +38 -0
  184. package/dist/strategies/sepp.js +53 -0
  185. package/dist/tax/aca.d.ts +40 -0
  186. package/dist/tax/aca.js +82 -0
  187. package/dist/tax/federalTax.d.ts +110 -0
  188. package/dist/tax/federalTax.js +249 -0
  189. package/dist/tax/medicare.d.ts +20 -0
  190. package/dist/tax/medicare.js +36 -0
  191. package/dist/tax/propertySale.d.ts +46 -0
  192. package/dist/tax/propertySale.js +35 -0
  193. package/dist/tax/stateTax.d.ts +66 -0
  194. package/dist/tax/stateTax.js +198 -0
  195. package/dist/testing/money.d.ts +7 -0
  196. package/dist/testing/money.js +17 -0
  197. package/dist/testing/planFixtures.d.ts +29 -0
  198. package/dist/testing/planFixtures.js +141 -0
  199. 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;