@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,306 @@
1
+ /**
2
+ * Official SSA series for wage indexing, bend points, family maximum bend
3
+ * points, and taxable maximum.
4
+ * @see https://www.ssa.gov/oact/COLA/awiseries.html
5
+ * @see https://www.ssa.gov/oact/COLA/bendpoints.html
6
+ * @see https://www.ssa.gov/oact/COLA/cbb.html
7
+ */
8
+ /** National average wage index (AWI), dollars. */
9
+ export const AWI_BY_YEAR = {
10
+ 1951: 2799.16,
11
+ 1952: 2973.32,
12
+ 1953: 3139.44,
13
+ 1954: 3155.64,
14
+ 1955: 3301.44,
15
+ 1956: 3532.36,
16
+ 1957: 3641.72,
17
+ 1958: 3673.8,
18
+ 1959: 3855.8,
19
+ 1960: 4007.12,
20
+ 1961: 4086.76,
21
+ 1962: 4291.4,
22
+ 1963: 4396.64,
23
+ 1964: 4576.32,
24
+ 1965: 4658.72,
25
+ 1966: 4938.36,
26
+ 1967: 5213.44,
27
+ 1968: 5571.76,
28
+ 1969: 5893.76,
29
+ 1970: 6186.24,
30
+ 1971: 6497.08,
31
+ 1972: 7133.8,
32
+ 1973: 7580.16,
33
+ 1974: 8030.76,
34
+ 1975: 8630.92,
35
+ 1976: 9226.48,
36
+ 1977: 9779.44,
37
+ 1978: 10556.03,
38
+ 1979: 11479.46,
39
+ 1980: 12513.46,
40
+ 1981: 13773.1,
41
+ 1982: 14531.34,
42
+ 1983: 15239.24,
43
+ 1984: 16135.07,
44
+ 1985: 16822.51,
45
+ 1986: 17321.82,
46
+ 1987: 18426.51,
47
+ 1988: 19334.04,
48
+ 1989: 20099.55,
49
+ 1990: 21027.98,
50
+ 1991: 21811.6,
51
+ 1992: 22935.42,
52
+ 1993: 23132.67,
53
+ 1994: 23753.53,
54
+ 1995: 24705.66,
55
+ 1996: 25913.9,
56
+ 1997: 27426.0,
57
+ 1998: 28861.44,
58
+ 1999: 30469.84,
59
+ 2000: 32154.82,
60
+ 2001: 32921.92,
61
+ 2002: 33252.09,
62
+ 2003: 34064.95,
63
+ 2004: 35648.55,
64
+ 2005: 36952.94,
65
+ 2006: 38651.41,
66
+ 2007: 40405.48,
67
+ 2008: 41334.97,
68
+ 2009: 40711.61,
69
+ 2010: 41673.83,
70
+ 2011: 42979.61,
71
+ 2012: 44321.67,
72
+ 2013: 44888.16,
73
+ 2014: 46481.52,
74
+ 2015: 48098.63,
75
+ 2016: 48642.15,
76
+ 2017: 50321.89,
77
+ 2018: 52145.8,
78
+ 2019: 54099.99,
79
+ 2020: 55628.6,
80
+ 2021: 60575.07,
81
+ 2022: 63795.13,
82
+ 2023: 66621.8,
83
+ 2024: 69846.57,
84
+ };
85
+ /** PIA formula bend points by year of eligibility (attain age 62 / disability / death). */
86
+ export const PIA_BEND_POINTS = {
87
+ 1979: { first: 180, second: 1085 },
88
+ 1980: { first: 194, second: 1171 },
89
+ 1981: { first: 211, second: 1274 },
90
+ 1982: { first: 230, second: 1388 },
91
+ 1983: { first: 254, second: 1528 },
92
+ 1984: { first: 267, second: 1612 },
93
+ 1985: { first: 280, second: 1691 },
94
+ 1986: { first: 297, second: 1790 },
95
+ 1987: { first: 310, second: 1866 },
96
+ 1988: { first: 319, second: 1922 },
97
+ 1989: { first: 339, second: 2044 },
98
+ 1990: { first: 356, second: 2145 },
99
+ 1991: { first: 370, second: 2230 },
100
+ 1992: { first: 387, second: 2333 },
101
+ 1993: { first: 401, second: 2420 },
102
+ 1994: { first: 422, second: 2545 },
103
+ 1995: { first: 426, second: 2567 },
104
+ 1996: { first: 437, second: 2635 },
105
+ 1997: { first: 455, second: 2741 },
106
+ 1998: { first: 477, second: 2875 },
107
+ 1999: { first: 505, second: 3043 },
108
+ 2000: { first: 531, second: 3202 },
109
+ 2001: { first: 561, second: 3381 },
110
+ 2002: { first: 592, second: 3567 },
111
+ 2003: { first: 606, second: 3653 },
112
+ 2004: { first: 612, second: 3689 },
113
+ 2005: { first: 627, second: 3779 },
114
+ 2006: { first: 656, second: 3955 },
115
+ 2007: { first: 680, second: 4100 },
116
+ 2008: { first: 711, second: 4288 },
117
+ 2009: { first: 744, second: 4483 },
118
+ 2010: { first: 761, second: 4586 },
119
+ 2011: { first: 749, second: 4517 },
120
+ 2012: { first: 767, second: 4624 },
121
+ 2013: { first: 791, second: 4768 },
122
+ 2014: { first: 816, second: 4917 },
123
+ 2015: { first: 826, second: 4980 },
124
+ 2016: { first: 856, second: 5157 },
125
+ 2017: { first: 885, second: 5336 },
126
+ 2018: { first: 895, second: 5397 },
127
+ 2019: { first: 926, second: 5583 },
128
+ 2020: { first: 960, second: 5785 },
129
+ 2021: { first: 996, second: 6002 },
130
+ 2022: { first: 1024, second: 6172 },
131
+ 2023: { first: 1115, second: 6721 },
132
+ 2024: { first: 1174, second: 7078 },
133
+ 2025: { first: 1226, second: 7391 },
134
+ 2026: { first: 1286, second: 7749 },
135
+ };
136
+ /** Retirement/survivor family-maximum formula bend points by year of eligibility. */
137
+ export const FAMILY_MAXIMUM_BEND_POINTS = {
138
+ 1979: { first: 230, second: 332, third: 433 },
139
+ 1980: { first: 248, second: 358, third: 467 },
140
+ 1981: { first: 270, second: 390, third: 508 },
141
+ 1982: { first: 294, second: 425, third: 554 },
142
+ 1983: { first: 324, second: 468, third: 610 },
143
+ 1984: { first: 342, second: 493, third: 643 },
144
+ 1985: { first: 358, second: 517, third: 675 },
145
+ 1986: { first: 379, second: 548, third: 714 },
146
+ 1987: { first: 396, second: 571, third: 745 },
147
+ 1988: { first: 407, second: 588, third: 767 },
148
+ 1989: { first: 433, second: 626, third: 816 },
149
+ 1990: { first: 455, second: 656, third: 856 },
150
+ 1991: { first: 473, second: 682, third: 890 },
151
+ 1992: { first: 495, second: 714, third: 931 },
152
+ 1993: { first: 513, second: 740, third: 966 },
153
+ 1994: { first: 539, second: 779, third: 1016 },
154
+ 1995: { first: 544, second: 785, third: 1024 },
155
+ 1996: { first: 559, second: 806, third: 1052 },
156
+ 1997: { first: 581, second: 839, third: 1094 },
157
+ 1998: { first: 609, second: 880, third: 1147 },
158
+ 1999: { first: 645, second: 931, third: 1214 },
159
+ 2000: { first: 679, second: 980, third: 1278 },
160
+ 2001: { first: 717, second: 1034, third: 1349 },
161
+ 2002: { first: 756, second: 1092, third: 1424 },
162
+ 2003: { first: 774, second: 1118, third: 1458 },
163
+ 2004: { first: 782, second: 1129, third: 1472 },
164
+ 2005: { first: 801, second: 1156, third: 1508 },
165
+ 2006: { first: 838, second: 1210, third: 1578 },
166
+ 2007: { first: 869, second: 1255, third: 1636 },
167
+ 2008: { first: 909, second: 1312, third: 1711 },
168
+ 2009: { first: 950, second: 1372, third: 1789 },
169
+ 2010: { first: 972, second: 1403, third: 1830 },
170
+ 2011: { first: 957, second: 1382, third: 1803 },
171
+ 2012: { first: 980, second: 1415, third: 1845 },
172
+ 2013: { first: 1011, second: 1459, third: 1903 },
173
+ 2014: { first: 1042, second: 1505, third: 1962 },
174
+ 2015: { first: 1056, second: 1524, third: 1987 },
175
+ 2016: { first: 1093, second: 1578, third: 2058 },
176
+ 2017: { first: 1131, second: 1633, third: 2130 },
177
+ 2018: { first: 1144, second: 1651, third: 2154 },
178
+ 2019: { first: 1184, second: 1708, third: 2228 },
179
+ 2020: { first: 1226, second: 1770, third: 2309 },
180
+ 2021: { first: 1272, second: 1837, third: 2395 },
181
+ 2022: { first: 1308, second: 1889, third: 2463 },
182
+ 2023: { first: 1425, second: 2056, third: 2682 },
183
+ 2024: { first: 1500, second: 2166, third: 2825 },
184
+ 2025: { first: 1567, second: 2262, third: 2950 },
185
+ 2026: { first: 1643, second: 2371, third: 3093 },
186
+ };
187
+ /** OASDI contribution & benefit base (taxable maximum), dollars. */
188
+ export const WAGE_BASE_BY_YEAR = {
189
+ 1979: 22900,
190
+ 1980: 25900,
191
+ 1981: 29700,
192
+ 1982: 32400,
193
+ 1983: 35700,
194
+ 1984: 37800,
195
+ 1985: 39600,
196
+ 1986: 42000,
197
+ 1987: 43800,
198
+ 1988: 45000,
199
+ 1989: 48000,
200
+ 1990: 51300,
201
+ 1991: 53400,
202
+ 1992: 55500,
203
+ 1993: 57600,
204
+ 1994: 60600,
205
+ 1995: 61200,
206
+ 1996: 62700,
207
+ 1997: 65400,
208
+ 1998: 68400,
209
+ 1999: 72600,
210
+ 2000: 76200,
211
+ 2001: 80400,
212
+ 2002: 84900,
213
+ 2003: 87000,
214
+ 2004: 87900,
215
+ 2005: 90000,
216
+ 2006: 94200,
217
+ 2007: 97500,
218
+ 2008: 102000,
219
+ 2009: 106800,
220
+ 2010: 106800,
221
+ 2011: 106800,
222
+ 2012: 110100,
223
+ 2013: 113700,
224
+ 2014: 117000,
225
+ 2015: 118500,
226
+ 2016: 118500,
227
+ 2017: 127200,
228
+ 2018: 128400,
229
+ 2019: 132900,
230
+ 2020: 137700,
231
+ 2021: 142800,
232
+ 2022: 147000,
233
+ 2023: 160200,
234
+ 2024: 168600,
235
+ 2025: 176100,
236
+ 2026: 184500,
237
+ };
238
+ function maxTableYear(table) {
239
+ let m = -Infinity;
240
+ for (const k of Object.keys(table)) {
241
+ const y = Number(k);
242
+ if (Number.isFinite(y) && y > m)
243
+ m = y;
244
+ }
245
+ return m;
246
+ }
247
+ /** Latest calendar year for which `AWI_BY_YEAR` has an official value. */
248
+ export const LATEST_PUBLISHED_AWI_YEAR = maxTableYear(AWI_BY_YEAR);
249
+ /** Latest eligibility year for which `PIA_BEND_POINTS` has an official pair. */
250
+ export const LATEST_PIA_BEND_POINT_ELIGIBILITY_YEAR = maxTableYear(PIA_BEND_POINTS);
251
+ /** Latest eligibility year for which `FAMILY_MAXIMUM_BEND_POINTS` has an official triplet. */
252
+ export const LATEST_FAMILY_MAXIMUM_BEND_POINT_ELIGIBILITY_YEAR = maxTableYear(FAMILY_MAXIMUM_BEND_POINTS);
253
+ /** Latest calendar year for which `WAGE_BASE_BY_YEAR` has an official taxable maximum. */
254
+ export const LATEST_PUBLISHED_WAGE_BASE_YEAR = maxTableYear(WAGE_BASE_BY_YEAR);
255
+ export function awiForYear(year) {
256
+ return AWI_BY_YEAR[year];
257
+ }
258
+ /**
259
+ * Published AWI, or the latest on file when the year is not yet released (illustrative only).
260
+ */
261
+ export function awiForYearOrLatest(year) {
262
+ const v = AWI_BY_YEAR[year];
263
+ if (v !== undefined && v > 0)
264
+ return v;
265
+ return AWI_BY_YEAR[LATEST_PUBLISHED_AWI_YEAR];
266
+ }
267
+ export function bendPointsForEligibilityYear(year) {
268
+ return PIA_BEND_POINTS[year];
269
+ }
270
+ /**
271
+ * Official bend points for that eligibility year, or the latest on file when SSA has not yet
272
+ * published figures (illustrative only).
273
+ */
274
+ export function bendPointsForEligibilityYearOrLatest(eligibilityYear) {
275
+ const direct = PIA_BEND_POINTS[eligibilityYear];
276
+ if (direct)
277
+ return direct;
278
+ return PIA_BEND_POINTS[LATEST_PIA_BEND_POINT_ELIGIBILITY_YEAR];
279
+ }
280
+ export function familyMaximumBendPointsForEligibilityYear(year) {
281
+ return FAMILY_MAXIMUM_BEND_POINTS[year];
282
+ }
283
+ /**
284
+ * Official family-maximum bend points for that eligibility year, or the latest
285
+ * on file when SSA has not yet published figures (illustrative only).
286
+ */
287
+ export function familyMaximumBendPointsForEligibilityYearOrLatest(eligibilityYear) {
288
+ const direct = FAMILY_MAXIMUM_BEND_POINTS[eligibilityYear];
289
+ if (direct)
290
+ return direct;
291
+ return FAMILY_MAXIMUM_BEND_POINTS[LATEST_FAMILY_MAXIMUM_BEND_POINT_ELIGIBILITY_YEAR];
292
+ }
293
+ export function wageBaseForYear(year) {
294
+ return WAGE_BASE_BY_YEAR[year];
295
+ }
296
+ /**
297
+ * Social Security taxable maximum (wage base) for a year, or the latest on file
298
+ * when SSA has not yet published it (illustrative only). Used to cap projected
299
+ * future earnings so they cannot exceed the taxable maximum.
300
+ */
301
+ export function wageBaseForYearOrLatest(year) {
302
+ const v = WAGE_BASE_BY_YEAR[year];
303
+ if (v !== undefined && v > 0)
304
+ return v;
305
+ return WAGE_BASE_BY_YEAR[LATEST_PUBLISHED_WAGE_BASE_YEAR];
306
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Survivor (widow(er)) benefit — the shared, SSA-cited computation used by
3
+ * **both** the actuarial PV view (`survivorSwitching.ts`) and the projection
4
+ * ledger (`simulate.ts` survivor step-up + `maritalBenefits.ts` former-spouse
5
+ * path), so the two can't drift (the gap-analysis row this closes was exactly
6
+ * that drift: the PV view had a reduction, the ledger didn't).
7
+ *
8
+ * Rules encoded (see DOCS/domain/domain-rules-reference.md §4, each cited):
9
+ * - **Survivor base = the deceased's actual (claim-age-adjusted) benefit**,
10
+ * including the deceased's delayed-retirement credits when the deceased
11
+ * delayed past FRA (not flat PIA).
12
+ * - **RIB-LIM ("widow's limit")**: when the deceased claimed reduced benefits
13
+ * early, the survivor is capped at the larger of the deceased's actual
14
+ * reduced benefit or 82.5% of the deceased's PIA — equivalently
15
+ * `max(deceasedActual, 0.825 × PIA)`, which both floors the survivor when the
16
+ * deceased claimed early and passes delayed credits through when the deceased
17
+ * delayed.
18
+ * - **Early-claim widow(er) reduction**: claiming survivor between 60 and the
19
+ * survivor's FRA reduces the benefit by up to 28.5% at 60 (linear monthly
20
+ * proration) — a floor of 71.5% of the survivor base.
21
+ *
22
+ * Inputs are monthly, today's dollars, pre-COLA/haircut; callers scale to the
23
+ * annual COLA-adjusted frame they need. Illustrative, not a filing tool.
24
+ */
25
+ import type { ClaimAge } from './claimFactor.js';
26
+ /** Earliest age a (non-disabled) widow(er) can claim survivor benefits. */
27
+ export declare const SURVIVOR_EARLIEST_AGE = 60;
28
+ /** Maximum widow(er) reduction, applied at the earliest claim age (28.5% → 71.5% payable). */
29
+ export declare const SURVIVOR_MAX_REDUCTION = 0.285;
30
+ /** RIB-LIM: the widow's-limit floor as a fraction of the deceased's PIA. */
31
+ export declare const WIDOW_LIMIT_PIA_FRACTION = 0.825;
32
+ export interface SurvivorBenefitInput {
33
+ /** The deceased worker's PIA (today's dollars, pre-COLA/haircut). */
34
+ deceasedPiaMonthly: number;
35
+ /**
36
+ * The deceased's actual monthly benefit = PIA × the deceased's claim factor
37
+ * (claim-age-adjusted, including delayed-retirement credits). Pre-COLA/haircut.
38
+ */
39
+ deceasedActualMonthly: number;
40
+ /** The survivor's claim age (years + months, ≥60) — months are honored. */
41
+ survivorClaimAge: ClaimAge;
42
+ /** Survivor (widow(er)) FRA in total months — see `survivorFraForBirthYear`. */
43
+ survivorFraMonths: number;
44
+ }
45
+ /**
46
+ * Widow(er) reduction factor for claiming survivor at `ageMonths` total months
47
+ * (linear from a 28.5% reduction at age 60 to no reduction at the survivor's
48
+ * FRA). 1.0 at/after FRA; 0.715 at/before 60. Accepts total months so a survivor
49
+ * claiming at exactly their survivor FRA (e.g. 66y8m for born 1960+) is not
50
+ * reduced.
51
+ */
52
+ export declare function survivorReductionFactor(ageMonths: number, survivorFraMonths: number): number;
53
+ /**
54
+ * The survivor monthly benefit payable (today's dollars, pre-COLA/haircut):
55
+ * `max(deceasedActual, 82.5% × PIA)` (RIB-LIM) × the widow(er) reduction factor.
56
+ * The survivor's claim-age **months** are carried through (a survivor at exactly
57
+ * their survivor FRA is unreduced). Returns 0 when the deceased had no PIA.
58
+ */
59
+ export declare function survivorBenefitMonthly(input: SurvivorBenefitInput): number;
60
+ /** Convenience: a deceased claim age of "at/after FRA" (no early reduction, no DRCs). */
61
+ export declare const DECEASED_CLAIMED_AT_FRA: ClaimAge;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Survivor (widow(er)) benefit — the shared, SSA-cited computation used by
3
+ * **both** the actuarial PV view (`survivorSwitching.ts`) and the projection
4
+ * ledger (`simulate.ts` survivor step-up + `maritalBenefits.ts` former-spouse
5
+ * path), so the two can't drift (the gap-analysis row this closes was exactly
6
+ * that drift: the PV view had a reduction, the ledger didn't).
7
+ *
8
+ * Rules encoded (see DOCS/domain/domain-rules-reference.md §4, each cited):
9
+ * - **Survivor base = the deceased's actual (claim-age-adjusted) benefit**,
10
+ * including the deceased's delayed-retirement credits when the deceased
11
+ * delayed past FRA (not flat PIA).
12
+ * - **RIB-LIM ("widow's limit")**: when the deceased claimed reduced benefits
13
+ * early, the survivor is capped at the larger of the deceased's actual
14
+ * reduced benefit or 82.5% of the deceased's PIA — equivalently
15
+ * `max(deceasedActual, 0.825 × PIA)`, which both floors the survivor when the
16
+ * deceased claimed early and passes delayed credits through when the deceased
17
+ * delayed.
18
+ * - **Early-claim widow(er) reduction**: claiming survivor between 60 and the
19
+ * survivor's FRA reduces the benefit by up to 28.5% at 60 (linear monthly
20
+ * proration) — a floor of 71.5% of the survivor base.
21
+ *
22
+ * Inputs are monthly, today's dollars, pre-COLA/haircut; callers scale to the
23
+ * annual COLA-adjusted frame they need. Illustrative, not a filing tool.
24
+ */
25
+ /** Earliest age a (non-disabled) widow(er) can claim survivor benefits. */
26
+ export const SURVIVOR_EARLIEST_AGE = 60;
27
+ /** Maximum widow(er) reduction, applied at the earliest claim age (28.5% → 71.5% payable). */
28
+ export const SURVIVOR_MAX_REDUCTION = 0.285;
29
+ /** RIB-LIM: the widow's-limit floor as a fraction of the deceased's PIA. */
30
+ export const WIDOW_LIMIT_PIA_FRACTION = 0.825;
31
+ /**
32
+ * Widow(er) reduction factor for claiming survivor at `ageMonths` total months
33
+ * (linear from a 28.5% reduction at age 60 to no reduction at the survivor's
34
+ * FRA). 1.0 at/after FRA; 0.715 at/before 60. Accepts total months so a survivor
35
+ * claiming at exactly their survivor FRA (e.g. 66y8m for born 1960+) is not
36
+ * reduced.
37
+ */
38
+ export function survivorReductionFactor(ageMonths, survivorFraMonths) {
39
+ if (ageMonths >= survivorFraMonths)
40
+ return 1;
41
+ const earliest = SURVIVOR_EARLIEST_AGE * 12;
42
+ if (ageMonths <= earliest)
43
+ return 1 - SURVIVOR_MAX_REDUCTION;
44
+ const frac = (ageMonths - earliest) / (survivorFraMonths - earliest);
45
+ return 1 - SURVIVOR_MAX_REDUCTION * (1 - frac);
46
+ }
47
+ /**
48
+ * The survivor monthly benefit payable (today's dollars, pre-COLA/haircut):
49
+ * `max(deceasedActual, 82.5% × PIA)` (RIB-LIM) × the widow(er) reduction factor.
50
+ * The survivor's claim-age **months** are carried through (a survivor at exactly
51
+ * their survivor FRA is unreduced). Returns 0 when the deceased had no PIA.
52
+ */
53
+ export function survivorBenefitMonthly(input) {
54
+ if (input.deceasedPiaMonthly <= 0)
55
+ return 0;
56
+ const base = Math.max(input.deceasedActualMonthly, WIDOW_LIMIT_PIA_FRACTION * input.deceasedPiaMonthly);
57
+ const ageMonths = input.survivorClaimAge.years * 12 + input.survivorClaimAge.months;
58
+ return base * survivorReductionFactor(ageMonths, input.survivorFraMonths);
59
+ }
60
+ /** Convenience: a deceased claim age of "at/after FRA" (no early reduction, no DRCs). */
61
+ export const DECEASED_CLAIMED_AT_FRA = { years: 67, months: 0 };
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Amortization-based withdrawal (ABW) — spending-paths & SWR-lenses plan, Goal 2.
3
+ *
4
+ * The rule the Bogleheads wiki formalized and that VPW, TPAW, and CAPE-based
5
+ * rules are parameterizations of: every year, re-amortize the *actual*
6
+ * start-of-year portfolio over the remaining horizon at an expected return,
7
+ * and spend this year's payment. Because the payment is recomputed from the
8
+ * real balance annually, the rule self-corrects after market surprises and —
9
+ * by the amortization identity — depletes exactly at the horizon when realized
10
+ * returns equal expected returns.
11
+ *
12
+ * Timing convention matches the ledger (simulate.ts): expenses are funded from
13
+ * the start-of-year balance and growth applies to what remains at year end —
14
+ * an annuity-due. With payments planned to grow at a real tilt g and an
15
+ * expected real return r, the payment ratio x = (1+g)/(1+r) is the same in
16
+ * real and nominal terms (inflation cancels), so the payment can be computed
17
+ * directly on the nominal start-of-year balance:
18
+ *
19
+ * payment = balance × (1 − x) / (1 − x^n) (x ≠ 1), or balance / n (x = 1)
20
+ *
21
+ * where n is the remaining years including the current one. n = 1 spends the
22
+ * whole remaining balance — the exact-depletion terminal case.
23
+ *
24
+ * These functions are pure; the ledger owns per-year state and funds the
25
+ * payment through the same tax/withdrawal cascade as every other expense.
26
+ */
27
+ import type { AbwPolicy } from '../model/plan.js';
28
+ /** Defaults documented in DOCS/domain/domain-rules-reference.md §14 ("Spending paths & SWR lenses"). */
29
+ export declare const ABW_DEFAULTS: {
30
+ /**
31
+ * VPW-style fixed expected real return: the Bogleheads VPW table's global
32
+ * internal rates of return (stocks 5.0%/yr real, bonds 1.9%/yr real)
33
+ * weighted 60/40 ⇒ 3.8%/yr real.
34
+ */
35
+ fixedRealReturnPct: number;
36
+ /** Matches the CAPE-conditioned market model's default starting CAPE. */
37
+ startingCape: number;
38
+ equitySharePct: number;
39
+ /** Long-run TIPS real yield near the mid-2026 curve (~2%/yr real). */
40
+ bondRealYieldPct: number;
41
+ tiltPct: number;
42
+ horizon: "planningAge";
43
+ };
44
+ /**
45
+ * Expected real return %/yr implied by the policy's return source:
46
+ * fixed as entered; CAPE = 100/CAPE (the cyclically-adjusted earnings yield)
47
+ * blended with the bond real yield at the equity share; TIPS = the bond real
48
+ * yield alone.
49
+ */
50
+ export declare function abwExpectedRealReturnPct(abw: AbwPolicy | undefined): number;
51
+ /**
52
+ * This year's amortized payment from a start-of-year balance over
53
+ * `remainingYears` (current year inclusive) at `realReturnPct` expected real
54
+ * return, with payments planned to grow `tiltPct`%/yr real. Balance ≤ 0 or a
55
+ * non-finite input pays 0; remainingYears ≤ 1 pays the whole balance.
56
+ */
57
+ export declare function abwAnnualPayment(balance: number, realReturnPct: number, tiltPct: number, remainingYears: number): number;
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Amortization-based withdrawal (ABW) — spending-paths & SWR-lenses plan, Goal 2.
3
+ *
4
+ * The rule the Bogleheads wiki formalized and that VPW, TPAW, and CAPE-based
5
+ * rules are parameterizations of: every year, re-amortize the *actual*
6
+ * start-of-year portfolio over the remaining horizon at an expected return,
7
+ * and spend this year's payment. Because the payment is recomputed from the
8
+ * real balance annually, the rule self-corrects after market surprises and —
9
+ * by the amortization identity — depletes exactly at the horizon when realized
10
+ * returns equal expected returns.
11
+ *
12
+ * Timing convention matches the ledger (simulate.ts): expenses are funded from
13
+ * the start-of-year balance and growth applies to what remains at year end —
14
+ * an annuity-due. With payments planned to grow at a real tilt g and an
15
+ * expected real return r, the payment ratio x = (1+g)/(1+r) is the same in
16
+ * real and nominal terms (inflation cancels), so the payment can be computed
17
+ * directly on the nominal start-of-year balance:
18
+ *
19
+ * payment = balance × (1 − x) / (1 − x^n) (x ≠ 1), or balance / n (x = 1)
20
+ *
21
+ * where n is the remaining years including the current one. n = 1 spends the
22
+ * whole remaining balance — the exact-depletion terminal case.
23
+ *
24
+ * These functions are pure; the ledger owns per-year state and funds the
25
+ * payment through the same tax/withdrawal cascade as every other expense.
26
+ */
27
+ /** Defaults documented in DOCS/domain/domain-rules-reference.md §14 ("Spending paths & SWR lenses"). */
28
+ export const ABW_DEFAULTS = {
29
+ /**
30
+ * VPW-style fixed expected real return: the Bogleheads VPW table's global
31
+ * internal rates of return (stocks 5.0%/yr real, bonds 1.9%/yr real)
32
+ * weighted 60/40 ⇒ 3.8%/yr real.
33
+ */
34
+ fixedRealReturnPct: 3.8,
35
+ /** Matches the CAPE-conditioned market model's default starting CAPE. */
36
+ startingCape: 25,
37
+ equitySharePct: 60,
38
+ /** Long-run TIPS real yield near the mid-2026 curve (~2%/yr real). */
39
+ bondRealYieldPct: 2.0,
40
+ tiltPct: 0,
41
+ horizon: 'planningAge',
42
+ };
43
+ /**
44
+ * Expected real return %/yr implied by the policy's return source:
45
+ * fixed as entered; CAPE = 100/CAPE (the cyclically-adjusted earnings yield)
46
+ * blended with the bond real yield at the equity share; TIPS = the bond real
47
+ * yield alone.
48
+ */
49
+ export function abwExpectedRealReturnPct(abw) {
50
+ const source = abw?.returnSource ?? 'fixed';
51
+ const bond = abw?.bondRealYieldPct ?? ABW_DEFAULTS.bondRealYieldPct;
52
+ if (source === 'tips')
53
+ return bond;
54
+ if (source === 'cape') {
55
+ const cape = abw?.startingCape ?? ABW_DEFAULTS.startingCape;
56
+ const equityShare = (abw?.equitySharePct ?? ABW_DEFAULTS.equitySharePct) / 100;
57
+ const caey = 100 / cape;
58
+ return caey * equityShare + bond * (1 - equityShare);
59
+ }
60
+ return abw?.fixedRealReturnPct ?? ABW_DEFAULTS.fixedRealReturnPct;
61
+ }
62
+ /**
63
+ * This year's amortized payment from a start-of-year balance over
64
+ * `remainingYears` (current year inclusive) at `realReturnPct` expected real
65
+ * return, with payments planned to grow `tiltPct`%/yr real. Balance ≤ 0 or a
66
+ * non-finite input pays 0; remainingYears ≤ 1 pays the whole balance.
67
+ */
68
+ export function abwAnnualPayment(balance, realReturnPct, tiltPct, remainingYears) {
69
+ if (!Number.isFinite(balance) || balance <= 0)
70
+ return 0;
71
+ const n = Math.floor(remainingYears);
72
+ if (n <= 1)
73
+ return balance;
74
+ const x = (1 + tiltPct / 100) / (1 + realReturnPct / 100);
75
+ if (!Number.isFinite(x) || x <= 0)
76
+ return balance / n;
77
+ if (Math.abs(x - 1) < 1e-9)
78
+ return balance / n;
79
+ return (balance * (1 - x)) / (1 - Math.pow(x, n));
80
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Flexible one-time-goal scheduler. One scheduler instance belongs to one
3
+ * projection path, so goal resolution is stateful and deterministic: a goal can
4
+ * be funded, partially funded, or skipped at most once.
5
+ */
6
+ import type { GoalFlexibility, OneTimeGoal, SpendingClassification } from '../model/plan.js';
7
+ export interface SchedulableGoal {
8
+ id: string;
9
+ classification: SpendingClassification;
10
+ flexibility: GoalFlexibility;
11
+ earliestYear: number;
12
+ targetYear: number;
13
+ latestYear: number;
14
+ priority: number;
15
+ order: number;
16
+ amountTodayDollars: number;
17
+ minFundingPct: number;
18
+ allowPartialFunding: boolean;
19
+ }
20
+ /** Resolve a stored goal's optional flexibility fields to their defaults. */
21
+ export declare function toSchedulableGoal(goal: OneTimeGoal, index: number): SchedulableGoal;
22
+ export type GoalOutcome = 'funded' | 'partiallyFunded' | 'deferred' | 'skipped';
23
+ export interface GoalYearResult {
24
+ id: string;
25
+ classification: SpendingClassification;
26
+ outcome: GoalOutcome;
27
+ /** The goal's intended (inflated) cost this year, regardless of outcome. */
28
+ amountNominal: number;
29
+ /** Nominal dollars funded this year. */
30
+ fundedNominal: number;
31
+ /** Nominal dollars not funded when the goal resolves below its intended cost. */
32
+ unfundedNominal: number;
33
+ }
34
+ export interface GoalYearContext {
35
+ /** Cumulative inflation factor from the projection start to this year. */
36
+ inflFactor: number;
37
+ /** True when the guardrail policy is cutting discretionary spending this year. */
38
+ cutting: boolean;
39
+ /** True when strong-path rules may pull goals earlier than their target year. */
40
+ canPullForward?: boolean;
41
+ /**
42
+ * Hard budget for flexible goals this year. `null` means unlimited; omitted
43
+ * means no flexible-goal budget while cutting and unlimited otherwise.
44
+ */
45
+ availableBudget?: number | null;
46
+ }
47
+ export interface GoalPlanningResult {
48
+ results: GoalYearResult[];
49
+ remainingBudget: number | null;
50
+ }
51
+ export interface GoalScheduler {
52
+ /** Decide every goal touching `year`; each goal resolves at most once. */
53
+ planYear(year: number, ctx: GoalYearContext): GoalPlanningResult;
54
+ /** True after a goal has been funded, partially funded, or skipped. */
55
+ isResolved(id: string): boolean;
56
+ }
57
+ /**
58
+ * Build a scheduler for a plan's goals. Flexible goals are evaluated by
59
+ * classification, then explicit priority, then original list order so a target
60
+ * car replacement cannot be crowded out by a lower-layer ideal remodel.
61
+ */
62
+ export declare function createGoalScheduler(goals: SchedulableGoal[]): GoalScheduler;