@robotixai/calculator-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.
@@ -0,0 +1,28 @@
1
+ import type { Scenario, Metrics } from './types';
2
+ export interface BacktestPeriod {
3
+ startYear: number;
4
+ endYear: number;
5
+ terminalReal: number;
6
+ survived: boolean;
7
+ }
8
+ export interface BacktestResult {
9
+ periods: BacktestPeriod[];
10
+ successRate: number;
11
+ }
12
+ /**
13
+ * Runs the scenario against historical Shiller data using rolling N-year windows.
14
+ *
15
+ * N = end_age - current_age (the full projection span).
16
+ * For each starting year where N years of data are available, the projection
17
+ * is run using historical real stock returns, and the terminal balance and
18
+ * survival status are recorded.
19
+ *
20
+ * @param scenario The base scenario (retirement_age, current_age, etc.)
21
+ * @param projectionFn A projection function that accepts a Scenario plus an
22
+ * array of annual real returns and produces Metrics.
23
+ * @returns Array of BacktestPeriod results and the overall success rate (0-100).
24
+ */
25
+ export declare function runHistoricalBacktest(scenario: Scenario, projectionFn: (s: Scenario, returns: number[]) => {
26
+ metrics: Metrics;
27
+ }): BacktestResult;
28
+ //# sourceMappingURL=backtest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backtest.d.ts","sourceRoot":"","sources":["../src/backtest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AA6KjD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,GACrE,cAAc,CAoEhB"}
@@ -0,0 +1,235 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Historical Backtest — Shiller Data (real total stock returns, 1871-2024)
3
+ // ---------------------------------------------------------------------------
4
+ /**
5
+ * Embedded Shiller real total stock return data (inflation-adjusted).
6
+ * Source: Robert Shiller's dataset — real total return on S&P composite index.
7
+ * Values represent annual real (after-inflation) total returns including
8
+ * dividends, expressed as decimals (e.g., 0.1478 = 14.78%).
9
+ */
10
+ const SHILLER_DATA = [
11
+ { year: 1871, realStockReturn: 0.1478 },
12
+ { year: 1872, realStockReturn: 0.1076 },
13
+ { year: 1873, realStockReturn: -0.0415 },
14
+ { year: 1874, realStockReturn: 0.1249 },
15
+ { year: 1875, realStockReturn: 0.0555 },
16
+ { year: 1876, realStockReturn: -0.0290 },
17
+ { year: 1877, realStockReturn: 0.0187 },
18
+ { year: 1878, realStockReturn: 0.1797 },
19
+ { year: 1879, realStockReturn: 0.2102 },
20
+ { year: 1880, realStockReturn: 0.2361 },
21
+ { year: 1881, realStockReturn: 0.0118 },
22
+ { year: 1882, realStockReturn: 0.0043 },
23
+ { year: 1883, realStockReturn: -0.0041 },
24
+ { year: 1884, realStockReturn: -0.0785 },
25
+ { year: 1885, realStockReturn: 0.3034 },
26
+ { year: 1886, realStockReturn: 0.1350 },
27
+ { year: 1887, realStockReturn: -0.0122 },
28
+ { year: 1888, realStockReturn: 0.0447 },
29
+ { year: 1889, realStockReturn: 0.0511 },
30
+ { year: 1890, realStockReturn: -0.0678 },
31
+ { year: 1891, realStockReturn: 0.1920 },
32
+ { year: 1892, realStockReturn: 0.0486 },
33
+ { year: 1893, realStockReturn: -0.1716 },
34
+ { year: 1894, realStockReturn: 0.0311 },
35
+ { year: 1895, realStockReturn: 0.0520 },
36
+ { year: 1896, realStockReturn: -0.0146 },
37
+ { year: 1897, realStockReturn: 0.1768 },
38
+ { year: 1898, realStockReturn: 0.2315 },
39
+ { year: 1899, realStockReturn: 0.0178 },
40
+ { year: 1900, realStockReturn: 0.1422 },
41
+ { year: 1901, realStockReturn: 0.2007 },
42
+ { year: 1902, realStockReturn: 0.0694 },
43
+ { year: 1903, realStockReturn: -0.1724 },
44
+ { year: 1904, realStockReturn: 0.3269 },
45
+ { year: 1905, realStockReturn: 0.2206 },
46
+ { year: 1906, realStockReturn: -0.0123 },
47
+ { year: 1907, realStockReturn: -0.3274 },
48
+ { year: 1908, realStockReturn: 0.4576 },
49
+ { year: 1909, realStockReturn: 0.1805 },
50
+ { year: 1910, realStockReturn: -0.0254 },
51
+ { year: 1911, realStockReturn: 0.0399 },
52
+ { year: 1912, realStockReturn: 0.0196 },
53
+ { year: 1913, realStockReturn: -0.1087 },
54
+ { year: 1914, realStockReturn: -0.0641 },
55
+ { year: 1915, realStockReturn: 0.3197 },
56
+ { year: 1916, realStockReturn: -0.0159 },
57
+ { year: 1917, realStockReturn: -0.3180 },
58
+ { year: 1918, realStockReturn: 0.1073 },
59
+ { year: 1919, realStockReturn: 0.0759 },
60
+ { year: 1920, realStockReturn: -0.1702 },
61
+ { year: 1921, realStockReturn: 0.2315 },
62
+ { year: 1922, realStockReturn: 0.2934 },
63
+ { year: 1923, realStockReturn: 0.0540 },
64
+ { year: 1924, realStockReturn: 0.2772 },
65
+ { year: 1925, realStockReturn: 0.2808 },
66
+ { year: 1926, realStockReturn: 0.1124 },
67
+ { year: 1927, realStockReturn: 0.3727 },
68
+ { year: 1928, realStockReturn: 0.4362 },
69
+ { year: 1929, realStockReturn: -0.0885 },
70
+ { year: 1930, realStockReturn: -0.2512 },
71
+ { year: 1931, realStockReturn: -0.3887 },
72
+ { year: 1932, realStockReturn: -0.0157 },
73
+ { year: 1933, realStockReturn: 0.5701 },
74
+ { year: 1934, realStockReturn: 0.0225 },
75
+ { year: 1935, realStockReturn: 0.4577 },
76
+ { year: 1936, realStockReturn: 0.3274 },
77
+ { year: 1937, realStockReturn: -0.3788 },
78
+ { year: 1938, realStockReturn: 0.2862 },
79
+ { year: 1939, realStockReturn: 0.0230 },
80
+ { year: 1940, realStockReturn: -0.0935 },
81
+ { year: 1941, realStockReturn: -0.1792 },
82
+ { year: 1942, realStockReturn: 0.1218 },
83
+ { year: 1943, realStockReturn: 0.2275 },
84
+ { year: 1944, realStockReturn: 0.1741 },
85
+ { year: 1945, realStockReturn: 0.3413 },
86
+ { year: 1946, realStockReturn: -0.2449 },
87
+ { year: 1947, realStockReturn: -0.0434 },
88
+ { year: 1948, realStockReturn: 0.0233 },
89
+ { year: 1949, realStockReturn: 0.2115 },
90
+ { year: 1950, realStockReturn: 0.2493 },
91
+ { year: 1951, realStockReturn: 0.1458 },
92
+ { year: 1952, realStockReturn: 0.1470 },
93
+ { year: 1953, realStockReturn: -0.0124 },
94
+ { year: 1954, realStockReturn: 0.5266 },
95
+ { year: 1955, realStockReturn: 0.3139 },
96
+ { year: 1956, realStockReturn: 0.0389 },
97
+ { year: 1957, realStockReturn: -0.1401 },
98
+ { year: 1958, realStockReturn: 0.4292 },
99
+ { year: 1959, realStockReturn: 0.1055 },
100
+ { year: 1960, realStockReturn: -0.0012 },
101
+ { year: 1961, realStockReturn: 0.2638 },
102
+ { year: 1962, realStockReturn: -0.1001 },
103
+ { year: 1963, realStockReturn: 0.2055 },
104
+ { year: 1964, realStockReturn: 0.1555 },
105
+ { year: 1965, realStockReturn: 0.1040 },
106
+ { year: 1966, realStockReturn: -0.1369 },
107
+ { year: 1967, realStockReturn: 0.2084 },
108
+ { year: 1968, realStockReturn: 0.0658 },
109
+ { year: 1969, realStockReturn: -0.1464 },
110
+ { year: 1970, realStockReturn: -0.0203 },
111
+ { year: 1971, realStockReturn: 0.1068 },
112
+ { year: 1972, realStockReturn: 0.1520 },
113
+ { year: 1973, realStockReturn: -0.2343 },
114
+ { year: 1974, realStockReturn: -0.3728 },
115
+ { year: 1975, realStockReturn: 0.2896 },
116
+ { year: 1976, realStockReturn: 0.1924 },
117
+ { year: 1977, realStockReturn: -0.1248 },
118
+ { year: 1978, realStockReturn: -0.0186 },
119
+ { year: 1979, realStockReturn: 0.0574 },
120
+ { year: 1980, realStockReturn: 0.1934 },
121
+ { year: 1981, realStockReturn: -0.1269 },
122
+ { year: 1982, realStockReturn: 0.1703 },
123
+ { year: 1983, realStockReturn: 0.1871 },
124
+ { year: 1984, realStockReturn: 0.0122 },
125
+ { year: 1985, realStockReturn: 0.2810 },
126
+ { year: 1986, realStockReturn: 0.1683 },
127
+ { year: 1987, realStockReturn: 0.0192 },
128
+ { year: 1988, realStockReturn: 0.1201 },
129
+ { year: 1989, realStockReturn: 0.2662 },
130
+ { year: 1990, realStockReturn: -0.0917 },
131
+ { year: 1991, realStockReturn: 0.2654 },
132
+ { year: 1992, realStockReturn: 0.0441 },
133
+ { year: 1993, realStockReturn: 0.0720 },
134
+ { year: 1994, realStockReturn: -0.0138 },
135
+ { year: 1995, realStockReturn: 0.3452 },
136
+ { year: 1996, realStockReturn: 0.1920 },
137
+ { year: 1997, realStockReturn: 0.3128 },
138
+ { year: 1998, realStockReturn: 0.2709 },
139
+ { year: 1999, realStockReturn: 0.1826 },
140
+ { year: 2000, realStockReturn: -0.1249 },
141
+ { year: 2001, realStockReturn: -0.1347 },
142
+ { year: 2002, realStockReturn: -0.2388 },
143
+ { year: 2003, realStockReturn: 0.2637 },
144
+ { year: 2004, realStockReturn: 0.0769 },
145
+ { year: 2005, realStockReturn: 0.0146 },
146
+ { year: 2006, realStockReturn: 0.1338 },
147
+ { year: 2007, realStockReturn: 0.0111 },
148
+ { year: 2008, realStockReturn: -0.3685 },
149
+ { year: 2009, realStockReturn: 0.2646 },
150
+ { year: 2010, realStockReturn: 0.1306 },
151
+ { year: 2011, realStockReturn: -0.0183 },
152
+ { year: 2012, realStockReturn: 0.1396 },
153
+ { year: 2013, realStockReturn: 0.3069 },
154
+ { year: 2014, realStockReturn: 0.1156 },
155
+ { year: 2015, realStockReturn: 0.0065 },
156
+ { year: 2016, realStockReturn: 0.0977 },
157
+ { year: 2017, realStockReturn: 0.1930 },
158
+ { year: 2018, realStockReturn: -0.0624 },
159
+ { year: 2019, realStockReturn: 0.2880 },
160
+ { year: 2020, realStockReturn: 0.1640 },
161
+ { year: 2021, realStockReturn: 0.2168 },
162
+ { year: 2022, realStockReturn: -0.2455 },
163
+ { year: 2023, realStockReturn: 0.2214 },
164
+ { year: 2024, realStockReturn: 0.2315 },
165
+ ];
166
+ /**
167
+ * Runs the scenario against historical Shiller data using rolling N-year windows.
168
+ *
169
+ * N = end_age - current_age (the full projection span).
170
+ * For each starting year where N years of data are available, the projection
171
+ * is run using historical real stock returns, and the terminal balance and
172
+ * survival status are recorded.
173
+ *
174
+ * @param scenario The base scenario (retirement_age, current_age, etc.)
175
+ * @param projectionFn A projection function that accepts a Scenario plus an
176
+ * array of annual real returns and produces Metrics.
177
+ * @returns Array of BacktestPeriod results and the overall success rate (0-100).
178
+ */
179
+ export function runHistoricalBacktest(scenario, projectionFn) {
180
+ const span = scenario.end_age - scenario.current_age;
181
+ // Guard: span must be at least 1
182
+ if (span < 1) {
183
+ return { periods: [], successRate: 0 };
184
+ }
185
+ const periods = [];
186
+ const firstYear = SHILLER_DATA[0].year;
187
+ const lastYear = SHILLER_DATA[SHILLER_DATA.length - 1].year;
188
+ const dataLength = lastYear - firstYear + 1;
189
+ // Number of rolling windows we can create
190
+ const windowCount = dataLength - span + 1;
191
+ if (windowCount < 1) {
192
+ // Projection span exceeds available data — return empty with a note
193
+ // (CONTRACT-005: "fewer periods returned; minimum 1 period required" —
194
+ // but if we truly can't make even 1 window, return empty.)
195
+ return { periods: [], successRate: 0 };
196
+ }
197
+ // Build a lookup map for O(1) access by year
198
+ const returnsByYear = new Map();
199
+ for (const entry of SHILLER_DATA) {
200
+ returnsByYear.set(entry.year, entry.realStockReturn);
201
+ }
202
+ let survivedCount = 0;
203
+ for (let i = 0; i < windowCount; i++) {
204
+ const startYear = firstYear + i;
205
+ const endYear = startYear + span - 1;
206
+ // Extract returns for this window
207
+ const returns = [];
208
+ for (let y = startYear; y <= endYear; y++) {
209
+ const r = returnsByYear.get(y);
210
+ if (r === undefined)
211
+ break;
212
+ returns.push(r);
213
+ }
214
+ // If we didn't get enough years (shouldn't happen given windowCount calc), skip
215
+ if (returns.length < span)
216
+ continue;
217
+ // Run projection with historical returns
218
+ const result = projectionFn(scenario, returns);
219
+ // Balance floor at 0 (CONTRACT-005 invariant)
220
+ const terminalReal = Math.max(0, result.metrics.terminal_real);
221
+ const survived = result.metrics.first_shortfall_age === null;
222
+ periods.push({
223
+ startYear,
224
+ endYear,
225
+ terminalReal,
226
+ survived,
227
+ });
228
+ if (survived)
229
+ survivedCount++;
230
+ }
231
+ const successRate = periods.length > 0
232
+ ? (survivedCount / periods.length) * 100
233
+ : 0;
234
+ return { periods, successRate };
235
+ }
@@ -0,0 +1,4 @@
1
+ import type { Cadence, Scenario } from './types';
2
+ export declare const CadenceMultiplier: Record<Cadence, number>;
3
+ export declare const DEFAULT_SCENARIO: Scenario;
4
+ //# sourceMappingURL=defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAMjD,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAKrD,CAAC;AAMF,eAAO,MAAM,gBAAgB,EAAE,QAqF9B,CAAC"}
@@ -0,0 +1,84 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Engine Defaults — standalone default values (no Zod dependency)
3
+ // ---------------------------------------------------------------------------
4
+ // ---------------------------------------------------------------------------
5
+ // Cadence Multiplier
6
+ // ---------------------------------------------------------------------------
7
+ export const CadenceMultiplier = {
8
+ Annual: 1,
9
+ Monthly: 12,
10
+ 'Bi-Weekly': 26,
11
+ Weekly: 52,
12
+ };
13
+ // ---------------------------------------------------------------------------
14
+ // Default Scenario
15
+ // ---------------------------------------------------------------------------
16
+ export const DEFAULT_SCENARIO = {
17
+ name: 'Scenario A',
18
+ // Timeline
19
+ current_age: 30,
20
+ retirement_age: 65,
21
+ end_age: 100,
22
+ // Portfolio
23
+ current_balance: 100000,
24
+ contrib_amount: 500,
25
+ contrib_cadence: 'Monthly',
26
+ contrib_increase_pct: 0,
27
+ // Returns
28
+ nominal_return_pct: 8,
29
+ return_stdev_pct: 15,
30
+ return_distribution: 'log-normal',
31
+ inflation_pct: 3,
32
+ inflation_enabled: true,
33
+ fee_pct: 0.5,
34
+ perf_fee_pct: 0,
35
+ // Withdrawal
36
+ withdrawal_method: 'Fixed % of prior-year end balance',
37
+ withdrawal_pct: 4,
38
+ withdrawal_real_amount: 50000,
39
+ withdrawal_frequency: 'Annual',
40
+ withdrawal_strategy: 'Standard',
41
+ // Guyton-Klinger
42
+ gk_ceiling_pct: 20,
43
+ gk_floor_pct: 20,
44
+ gk_prosperity_threshold: 20,
45
+ gk_capital_preservation_threshold: 20,
46
+ // Spending phases
47
+ spending_phases: [],
48
+ // Monte Carlo
49
+ enable_mc: true,
50
+ mc_runs: 1000,
51
+ // Tax
52
+ enable_taxes: false,
53
+ effective_tax_rate_pct: 0,
54
+ tax_jurisdiction: 'Custom',
55
+ tax_config: null,
56
+ tax_deferred_pct: 85,
57
+ // Planning
58
+ planning_mode: 'Individual',
59
+ partner_name: '',
60
+ partner_current_age: null,
61
+ partner_retirement_age: null,
62
+ partner_income_sources: [],
63
+ // Assets
64
+ assets: [],
65
+ liquid_pct: 100,
66
+ // Advanced
67
+ detail_mode: 'basic',
68
+ financial_items: [],
69
+ // Income
70
+ income_sources: [],
71
+ // Liquidity events
72
+ liquidity_events: [],
73
+ // Estate
74
+ desired_estate: 0,
75
+ // Black swan
76
+ black_swan_enabled: false,
77
+ black_swan_age: 70,
78
+ black_swan_loss_pct: 50,
79
+ // Currency
80
+ currency_code: 'USD',
81
+ currency_symbol: '$',
82
+ // Withdrawal order
83
+ withdrawal_order: 'Tax-Efficient',
84
+ };
@@ -0,0 +1,38 @@
1
+ import type { Scenario, Metrics } from './types';
2
+ export interface HeatmapCell {
3
+ retirementAge: number;
4
+ annualSpending: number;
5
+ viable: boolean;
6
+ terminalReal: number;
7
+ }
8
+ export interface HeatmapOptions {
9
+ /** [min, max] retirement age range. Defaults to [current_age+1, end_age-1]. */
10
+ retirementAgeRange?: [number, number];
11
+ /** [min, max] annual spending range. Defaults to [10_000, 120_000]. */
12
+ spendingRange?: [number, number];
13
+ /** Number of steps on each axis. Defaults to 10. */
14
+ steps?: number;
15
+ }
16
+ /**
17
+ * Generates a 2D grid of retirement_age x annual_spending cells.
18
+ *
19
+ * For each combination, the scenario is adjusted and a deterministic projection
20
+ * is run. Each cell records whether the scenario is viable (no shortfall and
21
+ * terminal_real >= desired_estate) along with the terminal_real value.
22
+ *
23
+ * The spending adjustment modifies:
24
+ * - withdrawal_pct (when using "Fixed % of prior-year end balance")
25
+ * - withdrawal_real_amount (when using "Fixed real-dollar amount")
26
+ *
27
+ * For percentage-based withdrawal, the spending value is treated as a dollar
28
+ * amount and the withdrawal_real_amount is set (method switched to fixed-dollar)
29
+ * so the heatmap consistently represents dollar-denominated spending levels.
30
+ *
31
+ * @param scenario Base scenario
32
+ * @param projectionFn Deterministic projection function
33
+ * @param options Optional axis ranges and step count
34
+ */
35
+ export declare function generateHeatmap(scenario: Scenario, projectionFn: (s: Scenario) => {
36
+ metrics: Metrics;
37
+ }, options?: HeatmapOptions): HeatmapCell[];
38
+ //# sourceMappingURL=heatmap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heatmap.d.ts","sourceRoot":"","sources":["../src/heatmap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAMjD,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,+EAA+E;IAC/E,kBAAkB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,uEAAuE;IACvE,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AASD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,CAAC,CAAC,EAAE,QAAQ,KAAK;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,EACnD,OAAO,CAAC,EAAE,cAAc,GACvB,WAAW,EAAE,CAoDf"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Deep-clone a scenario.
3
+ */
4
+ function cloneScenario(s) {
5
+ return JSON.parse(JSON.stringify(s));
6
+ }
7
+ /**
8
+ * Generates a 2D grid of retirement_age x annual_spending cells.
9
+ *
10
+ * For each combination, the scenario is adjusted and a deterministic projection
11
+ * is run. Each cell records whether the scenario is viable (no shortfall and
12
+ * terminal_real >= desired_estate) along with the terminal_real value.
13
+ *
14
+ * The spending adjustment modifies:
15
+ * - withdrawal_pct (when using "Fixed % of prior-year end balance")
16
+ * - withdrawal_real_amount (when using "Fixed real-dollar amount")
17
+ *
18
+ * For percentage-based withdrawal, the spending value is treated as a dollar
19
+ * amount and the withdrawal_real_amount is set (method switched to fixed-dollar)
20
+ * so the heatmap consistently represents dollar-denominated spending levels.
21
+ *
22
+ * @param scenario Base scenario
23
+ * @param projectionFn Deterministic projection function
24
+ * @param options Optional axis ranges and step count
25
+ */
26
+ export function generateHeatmap(scenario, projectionFn, options) {
27
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
28
+ const steps = (_a = options === null || options === void 0 ? void 0 : options.steps) !== null && _a !== void 0 ? _a : 10;
29
+ const ageMin = (_c = (_b = options === null || options === void 0 ? void 0 : options.retirementAgeRange) === null || _b === void 0 ? void 0 : _b[0]) !== null && _c !== void 0 ? _c : scenario.current_age + 1;
30
+ const ageMax = (_e = (_d = options === null || options === void 0 ? void 0 : options.retirementAgeRange) === null || _d === void 0 ? void 0 : _d[1]) !== null && _e !== void 0 ? _e : scenario.end_age - 1;
31
+ const spendMin = (_g = (_f = options === null || options === void 0 ? void 0 : options.spendingRange) === null || _f === void 0 ? void 0 : _f[0]) !== null && _g !== void 0 ? _g : 10000;
32
+ const spendMax = (_j = (_h = options === null || options === void 0 ? void 0 : options.spendingRange) === null || _h === void 0 ? void 0 : _h[1]) !== null && _j !== void 0 ? _j : 120000;
33
+ // Ensure at least 2 steps to avoid division by zero
34
+ const effectiveSteps = Math.max(steps, 2);
35
+ const ageStep = (ageMax - ageMin) / (effectiveSteps - 1);
36
+ const spendStep = (spendMax - spendMin) / (effectiveSteps - 1);
37
+ const cells = [];
38
+ const desiredEstate = (_k = scenario.desired_estate) !== null && _k !== void 0 ? _k : 0;
39
+ for (let ai = 0; ai < effectiveSteps; ai++) {
40
+ const retirementAge = Math.round(ageMin + ai * ageStep);
41
+ // Clamp retirement age to valid range
42
+ const clampedAge = Math.max(scenario.current_age + 1, Math.min(scenario.end_age - 1, retirementAge));
43
+ for (let si = 0; si < effectiveSteps; si++) {
44
+ const annualSpending = Math.round(spendMin + si * spendStep);
45
+ const s = cloneScenario(scenario);
46
+ s.retirement_age = clampedAge;
47
+ // Set spending as a fixed real-dollar amount for consistent comparison
48
+ s.withdrawal_method = 'Fixed real-dollar amount';
49
+ s.withdrawal_real_amount = Math.max(0, annualSpending);
50
+ const result = projectionFn(s);
51
+ const terminalReal = result.metrics.terminal_real;
52
+ const survived = result.metrics.first_shortfall_age === null;
53
+ const viable = survived && terminalReal >= desiredEstate;
54
+ cells.push({
55
+ retirementAge: clampedAge,
56
+ annualSpending,
57
+ viable,
58
+ terminalReal,
59
+ });
60
+ }
61
+ }
62
+ return cells;
63
+ }
@@ -0,0 +1,11 @@
1
+ export type { Cadence, CurrencyCode, ContribStep, ProfitStep, RaiseStep, YieldStep, IncomeStep, LoanDraw, LumpRepayment, SpendingPhase, TaxConfig, IncomeSource, Asset, LiquidityEvent, FinancialItemCategory, FinancialItem, Scenario, TimelineRow, FanChartRow, Metrics, } from './types';
2
+ export { CadenceMultiplier, DEFAULT_SCENARIO } from './defaults';
3
+ export { runProjection } from './projection';
4
+ export { runAdvancedProjection } from './advanced';
5
+ export { runMonteCarloSimulation, type MCOptions, type MCResult, type ProjectionFn, } from './monte-carlo';
6
+ export { runSensitivityAnalysis, type SensitivityFactor, } from './sensitivity';
7
+ export { runHistoricalBacktest, type BacktestPeriod, type BacktestResult, } from './backtest';
8
+ export { findEarliestRetirementAge, type OptimizerResult, type OptimizerOutput, type OptimizerOptions, } from './optimizer';
9
+ export { generateHeatmap, type HeatmapCell, type HeatmapOptions, } from './heatmap';
10
+ export { blendPortfolio, calculateEstateValue, type BlendedPortfolio, } from './portfolio';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,YAAY,EACV,OAAO,EACP,YAAY,EACZ,WAAW,EACX,UAAU,EACV,SAAS,EACT,SAAS,EACT,UAAU,EACV,QAAQ,EACR,aAAa,EACb,aAAa,EACb,SAAS,EACT,YAAY,EACZ,KAAK,EACL,cAAc,EACd,qBAAqB,EACrB,aAAa,EACb,QAAQ,EACR,WAAW,EACX,WAAW,EACX,OAAO,GACR,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGjE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAGnD,OAAO,EACL,uBAAuB,EACvB,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,YAAY,GAClB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,sBAAsB,EACtB,KAAK,iBAAiB,GACvB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,qBAAqB,EACrB,KAAK,cAAc,EACnB,KAAK,cAAc,GACpB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,yBAAyB,EACzB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,cAAc,GACpB,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Engine — barrel export
3
+ // ---------------------------------------------------------------------------
4
+ export { CadenceMultiplier, DEFAULT_SCENARIO } from './defaults';
5
+ // Deterministic projection (basic & advanced)
6
+ export { runProjection } from './projection';
7
+ export { runAdvancedProjection } from './advanced';
8
+ // Monte Carlo simulation
9
+ export { runMonteCarloSimulation, } from './monte-carlo';
10
+ // Sensitivity (Tornado chart)
11
+ export { runSensitivityAnalysis, } from './sensitivity';
12
+ // Historical backtest (Shiller data)
13
+ export { runHistoricalBacktest, } from './backtest';
14
+ // Retirement age optimizer
15
+ export { findEarliestRetirementAge, } from './optimizer';
16
+ // Retirement age x spending heatmap
17
+ export { generateHeatmap, } from './heatmap';
18
+ // Portfolio blending & estate value
19
+ export { blendPortfolio, calculateEstateValue, } from './portfolio';
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Monte Carlo Simulation Engine
3
+ *
4
+ * Deterministic, seeded PRNG-based Monte Carlo runner for retirement projections.
5
+ * Generates randomized annual returns and delegates year-by-year calculation to
6
+ * a caller-provided projection function, keeping MC fully decoupled from the
7
+ * projection engine.
8
+ */
9
+ import type { Scenario, TimelineRow, Metrics, FanChartRow } from './types';
10
+ export type ProjectionFn = (scenario: Scenario, overrideReturns?: number[]) => {
11
+ timeline: TimelineRow[];
12
+ metrics: Metrics;
13
+ };
14
+ export interface MCOptions {
15
+ /** Number of simulation runs. Default 1000, validated 100-10000. */
16
+ runs?: number;
17
+ /** PRNG seed for reproducibility. Default 42. */
18
+ seed?: number;
19
+ /** Wall-clock budget in ms before aborting. Default 50000. */
20
+ budgetMs?: number;
21
+ }
22
+ export interface MCResult {
23
+ probability_no_shortfall: number;
24
+ median_terminal: number;
25
+ p10_terminal: number;
26
+ p90_terminal: number;
27
+ fan_chart: FanChartRow[];
28
+ terminal_distribution: number[];
29
+ runs_completed: number;
30
+ truncated: boolean;
31
+ }
32
+ export declare class SeededRNG {
33
+ private state;
34
+ constructor(seed?: number);
35
+ /** Returns a uniform random number in [0, 1). Mulberry32 algorithm. */
36
+ next(): number;
37
+ /** Returns a standard normal random variate via Box-Muller transform. */
38
+ gaussian(): number;
39
+ }
40
+ export declare function generateReturn(rng: SeededRNG, mean: number, stdev: number, distribution: 'log-normal' | 'normal'): number;
41
+ export declare function extractPercentile(sortedArray: number[], p: number): number;
42
+ export declare function runMonteCarloSimulation(scenario: Scenario, projectionFn: ProjectionFn, options?: MCOptions): MCResult;
43
+ //# sourceMappingURL=monte-carlo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monte-carlo.d.ts","sourceRoot":"","sources":["../src/monte-carlo.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAM3E,MAAM,MAAM,YAAY,GAAG,CACzB,QAAQ,EAAE,QAAQ,EAClB,eAAe,CAAC,EAAE,MAAM,EAAE,KACvB;IAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAEnD,MAAM,WAAW,SAAS;IACxB,oEAAoE;IACpE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,wBAAwB,EAAE,MAAM,CAAC;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,WAAW,EAAE,CAAC;IACzB,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;CACpB;AAMD,qBAAa,SAAS;IACpB,OAAO,CAAC,KAAK,CAAS;gBAEV,IAAI,GAAE,MAAW;IAI7B,uEAAuE;IACvE,IAAI,IAAI,MAAM;IAQd,yEAAyE;IACzE,QAAQ,IAAI,MAAM;CAUnB;AAMD,wBAAgB,cAAc,CAC5B,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,YAAY,GAAG,QAAQ,GACpC,MAAM,CAuBR;AAMD,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAK1E;AAMD,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,YAAY,EAC1B,OAAO,GAAE,SAAc,GACtB,QAAQ,CAoIV"}