@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.
- package/LICENSE +21 -0
- package/README.md +127 -0
- package/dist/advanced.d.ts +24 -0
- package/dist/advanced.d.ts.map +1 -0
- package/dist/advanced.js +747 -0
- package/dist/backtest.d.ts +28 -0
- package/dist/backtest.d.ts.map +1 -0
- package/dist/backtest.js +235 -0
- package/dist/defaults.d.ts +4 -0
- package/dist/defaults.d.ts.map +1 -0
- package/dist/defaults.js +84 -0
- package/dist/heatmap.d.ts +38 -0
- package/dist/heatmap.d.ts.map +1 -0
- package/dist/heatmap.js +63 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/monte-carlo.d.ts +43 -0
- package/dist/monte-carlo.d.ts.map +1 -0
- package/dist/monte-carlo.js +178 -0
- package/dist/optimizer.d.ts +40 -0
- package/dist/optimizer.d.ts.map +1 -0
- package/dist/optimizer.js +134 -0
- package/dist/portfolio.d.ts +43 -0
- package/dist/portfolio.d.ts.map +1 -0
- package/dist/portfolio.js +86 -0
- package/dist/projection.d.ts +16 -0
- package/dist/projection.d.ts.map +1 -0
- package/dist/projection.js +382 -0
- package/dist/sensitivity.d.ts +30 -0
- package/dist/sensitivity.d.ts.map +1 -0
- package/dist/sensitivity.js +92 -0
- package/dist/tax.d.ts +49 -0
- package/dist/tax.d.ts.map +1 -0
- package/dist/tax.js +210 -0
- package/dist/types.d.ts +250 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/withdrawal.d.ts +136 -0
- package/dist/withdrawal.d.ts.map +1 -0
- package/dist/withdrawal.js +241 -0
- package/package.json +33 -0
package/dist/tax.js
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-jurisdiction tax calculator for the retirement engine.
|
|
3
|
+
*
|
|
4
|
+
* Jurisdictions: Custom (flat rate), Cayman Islands (zero), US (progressive),
|
|
5
|
+
* UK (progressive with personal allowance taper).
|
|
6
|
+
*
|
|
7
|
+
* Also includes RMD (Required Minimum Distribution) and Roth conversion logic.
|
|
8
|
+
*/
|
|
9
|
+
const US_STANDARD_DEDUCTION_SINGLE = 15000;
|
|
10
|
+
const US_STANDARD_DEDUCTION_MFJ = 30000;
|
|
11
|
+
const US_BRACKETS_2025_SINGLE = [
|
|
12
|
+
{ ceiling: 11925, rate: 0.10 },
|
|
13
|
+
{ ceiling: 48475, rate: 0.12 },
|
|
14
|
+
{ ceiling: 103350, rate: 0.22 },
|
|
15
|
+
{ ceiling: 197300, rate: 0.24 },
|
|
16
|
+
{ ceiling: 250525, rate: 0.32 },
|
|
17
|
+
{ ceiling: 626350, rate: 0.35 },
|
|
18
|
+
{ ceiling: Infinity, rate: 0.37 },
|
|
19
|
+
];
|
|
20
|
+
const US_BRACKETS_2025_MFJ = [
|
|
21
|
+
{ ceiling: 23850, rate: 0.10 },
|
|
22
|
+
{ ceiling: 96950, rate: 0.12 },
|
|
23
|
+
{ ceiling: 206700, rate: 0.22 },
|
|
24
|
+
{ ceiling: 394600, rate: 0.24 },
|
|
25
|
+
{ ceiling: 501050, rate: 0.32 },
|
|
26
|
+
{ ceiling: 751600, rate: 0.35 },
|
|
27
|
+
{ ceiling: Infinity, rate: 0.37 },
|
|
28
|
+
];
|
|
29
|
+
// =============================================================================
|
|
30
|
+
// UK Tax Constants (2025-26)
|
|
31
|
+
// =============================================================================
|
|
32
|
+
const UK_PERSONAL_ALLOWANCE = 12570;
|
|
33
|
+
/** Income threshold above which the personal allowance begins to taper. */
|
|
34
|
+
const UK_TAPER_THRESHOLD = 100000;
|
|
35
|
+
/** Income at which the personal allowance is fully withdrawn:
|
|
36
|
+
* 100,000 + 2 * 12,570 = 125,140.
|
|
37
|
+
*
|
|
38
|
+
* In the GBP 100,000 - 125,140 band the effective marginal rate is 60%:
|
|
39
|
+
* for every additional GBP 1 of income, you lose GBP 0.50 of personal
|
|
40
|
+
* allowance, so you pay 40% on GBP 1 of income PLUS 40% on GBP 0.50
|
|
41
|
+
* of previously-sheltered income = 60p total.
|
|
42
|
+
*/
|
|
43
|
+
const UK_TAPER_FULL_WITHDRAWAL = 125140;
|
|
44
|
+
/** Bands applied to income ABOVE the (adjusted) personal allowance. */
|
|
45
|
+
const UK_BANDS_2025_26 = [
|
|
46
|
+
{ ceiling: 37700, rate: 0.20 },
|
|
47
|
+
{ ceiling: 112570, rate: 0.40 },
|
|
48
|
+
{ ceiling: Infinity, rate: 0.45 },
|
|
49
|
+
];
|
|
50
|
+
// =============================================================================
|
|
51
|
+
// IRS Uniform Lifetime Table (ages 72-120)
|
|
52
|
+
// =============================================================================
|
|
53
|
+
const RMD_DIVISORS = {
|
|
54
|
+
72: 27.4, 73: 26.5, 74: 25.5, 75: 24.6, 76: 23.7, 77: 22.9,
|
|
55
|
+
78: 22.0, 79: 21.1, 80: 20.2, 81: 19.4, 82: 18.5, 83: 17.7,
|
|
56
|
+
84: 16.8, 85: 16.0, 86: 15.2, 87: 14.4, 88: 13.7, 89: 12.9,
|
|
57
|
+
90: 12.2, 91: 11.5, 92: 10.8, 93: 10.1, 94: 9.5, 95: 8.9,
|
|
58
|
+
96: 8.4, 97: 7.8, 98: 7.3, 99: 6.8, 100: 6.4, 101: 6.0,
|
|
59
|
+
102: 5.6, 103: 5.2, 104: 4.9, 105: 4.6, 106: 4.3, 107: 4.1,
|
|
60
|
+
108: 3.9, 109: 3.7, 110: 3.5, 111: 3.4, 112: 3.3, 113: 3.1,
|
|
61
|
+
114: 3.0, 115: 2.9, 116: 2.8, 117: 2.7, 118: 2.5, 119: 2.3,
|
|
62
|
+
120: 2.0,
|
|
63
|
+
};
|
|
64
|
+
// =============================================================================
|
|
65
|
+
// Progressive bracket calculation (shared by US and UK)
|
|
66
|
+
// =============================================================================
|
|
67
|
+
/**
|
|
68
|
+
* Compute total tax by walking through progressive brackets.
|
|
69
|
+
* @param taxableIncome - Income subject to tax (after deductions/allowances).
|
|
70
|
+
* @param brackets - Ordered array of brackets (ceiling is cumulative, not width).
|
|
71
|
+
*/
|
|
72
|
+
function applyProgressiveBrackets(taxableIncome, brackets) {
|
|
73
|
+
if (taxableIncome <= 0)
|
|
74
|
+
return 0;
|
|
75
|
+
let tax = 0;
|
|
76
|
+
let prev = 0;
|
|
77
|
+
for (const { ceiling, rate } of brackets) {
|
|
78
|
+
if (taxableIncome <= prev)
|
|
79
|
+
break;
|
|
80
|
+
const taxableInBracket = Math.min(taxableIncome, ceiling) - prev;
|
|
81
|
+
tax += taxableInBracket * rate;
|
|
82
|
+
prev = ceiling;
|
|
83
|
+
}
|
|
84
|
+
return tax;
|
|
85
|
+
}
|
|
86
|
+
// =============================================================================
|
|
87
|
+
// US Tax
|
|
88
|
+
// =============================================================================
|
|
89
|
+
function calculateUSTax(grossIncome, filingStatus) {
|
|
90
|
+
if (grossIncome <= 0)
|
|
91
|
+
return 0;
|
|
92
|
+
const deduction = filingStatus === 'Married Filing Jointly'
|
|
93
|
+
? US_STANDARD_DEDUCTION_MFJ
|
|
94
|
+
: US_STANDARD_DEDUCTION_SINGLE;
|
|
95
|
+
const brackets = filingStatus === 'Married Filing Jointly'
|
|
96
|
+
? US_BRACKETS_2025_MFJ
|
|
97
|
+
: US_BRACKETS_2025_SINGLE;
|
|
98
|
+
const taxableIncome = Math.max(0, grossIncome - deduction);
|
|
99
|
+
return applyProgressiveBrackets(taxableIncome, brackets);
|
|
100
|
+
}
|
|
101
|
+
// =============================================================================
|
|
102
|
+
// UK Tax
|
|
103
|
+
// =============================================================================
|
|
104
|
+
/**
|
|
105
|
+
* Compute the adjusted personal allowance after the taper.
|
|
106
|
+
*
|
|
107
|
+
* For gross income above GBP 100,000 the allowance is reduced by GBP 1 for
|
|
108
|
+
* every GBP 2 of income above 100K. This creates an effective 60% marginal
|
|
109
|
+
* rate in the GBP 100,000 - 125,140 band.
|
|
110
|
+
*/
|
|
111
|
+
function ukAdjustedAllowance(grossIncome) {
|
|
112
|
+
if (grossIncome <= UK_TAPER_THRESHOLD) {
|
|
113
|
+
return UK_PERSONAL_ALLOWANCE;
|
|
114
|
+
}
|
|
115
|
+
const reduction = Math.floor((grossIncome - UK_TAPER_THRESHOLD) / 2);
|
|
116
|
+
return Math.max(0, UK_PERSONAL_ALLOWANCE - reduction);
|
|
117
|
+
}
|
|
118
|
+
function calculateUKTax(grossIncome) {
|
|
119
|
+
if (grossIncome <= 0)
|
|
120
|
+
return 0;
|
|
121
|
+
const allowance = ukAdjustedAllowance(grossIncome);
|
|
122
|
+
const taxableIncome = Math.max(0, grossIncome - allowance);
|
|
123
|
+
return applyProgressiveBrackets(taxableIncome, UK_BANDS_2025_26);
|
|
124
|
+
}
|
|
125
|
+
// =============================================================================
|
|
126
|
+
// Main entry point
|
|
127
|
+
// =============================================================================
|
|
128
|
+
/**
|
|
129
|
+
* Calculate income tax for the given jurisdiction.
|
|
130
|
+
*
|
|
131
|
+
* @param taxableIncome - Gross income before deductions/allowances.
|
|
132
|
+
* @param config - The scenario's TaxConfig.
|
|
133
|
+
* @param jurisdiction - One of 'Custom', 'Cayman Islands', 'US', 'UK'.
|
|
134
|
+
* @returns Tax amount (always >= 0).
|
|
135
|
+
*/
|
|
136
|
+
export function calculateTax(taxableIncome, config, jurisdiction) {
|
|
137
|
+
if (taxableIncome <= 0)
|
|
138
|
+
return 0;
|
|
139
|
+
switch (jurisdiction) {
|
|
140
|
+
case 'Cayman Islands':
|
|
141
|
+
return 0;
|
|
142
|
+
case 'US':
|
|
143
|
+
return calculateUSTax(taxableIncome, config.filing_status);
|
|
144
|
+
case 'UK':
|
|
145
|
+
return calculateUKTax(taxableIncome);
|
|
146
|
+
case 'Custom':
|
|
147
|
+
default:
|
|
148
|
+
return taxableIncome * (config.flat_rate_pct / 100);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// =============================================================================
|
|
152
|
+
// RMD (Required Minimum Distributions)
|
|
153
|
+
// =============================================================================
|
|
154
|
+
/**
|
|
155
|
+
* Determine the age at which RMDs must begin based on birth year.
|
|
156
|
+
*
|
|
157
|
+
* - Born <= 1950: age 72
|
|
158
|
+
* - Born 1951-1959: age 73
|
|
159
|
+
* - Born >= 1960: age 75
|
|
160
|
+
*/
|
|
161
|
+
export function getRMDStartAge(birthYear) {
|
|
162
|
+
if (birthYear <= 1950)
|
|
163
|
+
return 72;
|
|
164
|
+
if (birthYear <= 1959)
|
|
165
|
+
return 73;
|
|
166
|
+
return 75;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Calculate the Required Minimum Distribution for a given age and balance.
|
|
170
|
+
*
|
|
171
|
+
* @param age - The individual's current age.
|
|
172
|
+
* @param taxDeferredBalance - End-of-prior-year tax-deferred account balance.
|
|
173
|
+
* @param birthYear - Birth year, used to determine RMD start age.
|
|
174
|
+
* @returns The RMD amount, or 0 if not yet required or age > 120.
|
|
175
|
+
*/
|
|
176
|
+
export function getRMDAmount(age, taxDeferredBalance, birthYear) {
|
|
177
|
+
const startAge = getRMDStartAge(birthYear);
|
|
178
|
+
if (age < startAge || age > 120)
|
|
179
|
+
return 0;
|
|
180
|
+
if (taxDeferredBalance <= 0)
|
|
181
|
+
return 0;
|
|
182
|
+
const divisor = RMD_DIVISORS[age];
|
|
183
|
+
if (divisor === undefined || divisor <= 0)
|
|
184
|
+
return 0;
|
|
185
|
+
return taxDeferredBalance / divisor;
|
|
186
|
+
}
|
|
187
|
+
// =============================================================================
|
|
188
|
+
// Roth Conversion
|
|
189
|
+
// =============================================================================
|
|
190
|
+
/**
|
|
191
|
+
* Calculate the Roth conversion amount for a given age and tax config.
|
|
192
|
+
*
|
|
193
|
+
* If the current age is within the configured conversion window
|
|
194
|
+
* (roth_conversion_start_age <= age <= roth_conversion_end_age) and
|
|
195
|
+
* conversions are enabled, returns the configured conversion amount.
|
|
196
|
+
* Otherwise returns 0.
|
|
197
|
+
*
|
|
198
|
+
* Note: the converted amount adds to taxable income for the conversion year.
|
|
199
|
+
* The caller is responsible for clamping the conversion to the available
|
|
200
|
+
* tax-deferred balance and adding it to taxable income.
|
|
201
|
+
*/
|
|
202
|
+
export function calculateRothConversion(age, config) {
|
|
203
|
+
if (!config.enable_roth_conversion)
|
|
204
|
+
return 0;
|
|
205
|
+
if (age < config.roth_conversion_start_age)
|
|
206
|
+
return 0;
|
|
207
|
+
if (age > config.roth_conversion_end_age)
|
|
208
|
+
return 0;
|
|
209
|
+
return config.roth_conversion_amount;
|
|
210
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
export type CurrencyCode = 'USD' | 'EUR' | 'GBP' | 'CHF' | 'HKD' | 'SGD' | 'AED' | 'JPY' | 'CAD' | 'AUD' | 'NZD' | 'ZAR' | 'INR' | 'BRL' | 'MXN' | 'KYD';
|
|
2
|
+
export type Cadence = 'Annual' | 'Monthly' | 'Bi-Weekly' | 'Weekly';
|
|
3
|
+
export interface ContribStep {
|
|
4
|
+
start_age: number;
|
|
5
|
+
end_age: number;
|
|
6
|
+
amount: number;
|
|
7
|
+
}
|
|
8
|
+
export interface ProfitStep {
|
|
9
|
+
age: number;
|
|
10
|
+
end_age?: number;
|
|
11
|
+
pct: number;
|
|
12
|
+
}
|
|
13
|
+
export interface RaiseStep {
|
|
14
|
+
start_age: number;
|
|
15
|
+
end_age: number;
|
|
16
|
+
raise_pct: number;
|
|
17
|
+
}
|
|
18
|
+
export interface YieldStep {
|
|
19
|
+
start_age: number;
|
|
20
|
+
end_age: number;
|
|
21
|
+
rate_pct: number;
|
|
22
|
+
}
|
|
23
|
+
export interface IncomeStep {
|
|
24
|
+
start_age: number;
|
|
25
|
+
end_age: number;
|
|
26
|
+
amount: number;
|
|
27
|
+
frequency?: 'Annual' | 'Monthly';
|
|
28
|
+
}
|
|
29
|
+
export interface LoanDraw {
|
|
30
|
+
age: number;
|
|
31
|
+
amount: number;
|
|
32
|
+
}
|
|
33
|
+
export interface LumpRepayment {
|
|
34
|
+
age: number;
|
|
35
|
+
amount: number;
|
|
36
|
+
}
|
|
37
|
+
export interface SpendingPhase {
|
|
38
|
+
start_age: number;
|
|
39
|
+
end_age: number;
|
|
40
|
+
mode: 'percent' | 'amount';
|
|
41
|
+
amount: number;
|
|
42
|
+
}
|
|
43
|
+
export interface TaxConfig {
|
|
44
|
+
jurisdiction: string;
|
|
45
|
+
flat_rate_pct: number;
|
|
46
|
+
filing_status: 'Single' | 'Married Filing Jointly';
|
|
47
|
+
enable_rmd: boolean;
|
|
48
|
+
enable_roth_conversion: boolean;
|
|
49
|
+
roth_conversion_amount: number;
|
|
50
|
+
roth_conversion_start_age: number;
|
|
51
|
+
roth_conversion_end_age: number;
|
|
52
|
+
}
|
|
53
|
+
export interface IncomeSource {
|
|
54
|
+
label: string;
|
|
55
|
+
type: 'Other' | 'Social Security' | 'Pension' | 'Annuity' | 'Part-Time' | 'Retirement Income' | 'Rental';
|
|
56
|
+
amount: number;
|
|
57
|
+
frequency: 'Annual' | 'Monthly';
|
|
58
|
+
start_age: number;
|
|
59
|
+
end_age: number;
|
|
60
|
+
inflation_adjusted: boolean;
|
|
61
|
+
taxable: boolean;
|
|
62
|
+
tax_rate: number;
|
|
63
|
+
enabled: boolean;
|
|
64
|
+
}
|
|
65
|
+
export interface Asset {
|
|
66
|
+
[key: string]: unknown;
|
|
67
|
+
label: string;
|
|
68
|
+
current_value: number;
|
|
69
|
+
is_liquid: boolean;
|
|
70
|
+
rate_pct: number;
|
|
71
|
+
enabled: boolean;
|
|
72
|
+
sell_at_age: number | null;
|
|
73
|
+
sale_amount_override: number | null;
|
|
74
|
+
}
|
|
75
|
+
export interface LiquidityEvent {
|
|
76
|
+
type: 'Credit' | 'Debit';
|
|
77
|
+
label: string;
|
|
78
|
+
start_age: number;
|
|
79
|
+
end_age: number;
|
|
80
|
+
amount: number;
|
|
81
|
+
recurrence: 'One-Time' | 'Annual' | 'Monthly';
|
|
82
|
+
enabled: boolean;
|
|
83
|
+
taxable: boolean;
|
|
84
|
+
tax_rate: number;
|
|
85
|
+
}
|
|
86
|
+
export type FinancialItemCategory = 'Cash' | 'Investment' | 'Property' | 'Collectables' | 'Salary' | 'Pension' | 'Social Security' | 'Annuity' | 'Retirement Income' | 'Other' | 'Loan';
|
|
87
|
+
export interface FinancialItem {
|
|
88
|
+
label: string;
|
|
89
|
+
category: FinancialItemCategory;
|
|
90
|
+
enabled: boolean;
|
|
91
|
+
current_value: number;
|
|
92
|
+
rate_pct: number;
|
|
93
|
+
fee_pct: number;
|
|
94
|
+
perf_fee_pct: number;
|
|
95
|
+
is_liquid: boolean;
|
|
96
|
+
contrib_mode: 'flat' | 'staggered';
|
|
97
|
+
contrib_amount: number;
|
|
98
|
+
contrib_cadence: Cadence;
|
|
99
|
+
contrib_start_age: number | null;
|
|
100
|
+
contrib_end_age: number | null;
|
|
101
|
+
contrib_increase_pct: number;
|
|
102
|
+
contrib_steps: ContribStep[];
|
|
103
|
+
invest_start_age: number | null;
|
|
104
|
+
purchase_age: number | null;
|
|
105
|
+
profit_taking_mode: 'single' | 'staggered';
|
|
106
|
+
sell_at_age: number | null;
|
|
107
|
+
sale_amount_override: number | null;
|
|
108
|
+
profit_taking_steps: ProfitStep[];
|
|
109
|
+
taxable_on_sale: boolean;
|
|
110
|
+
sale_tax_rate: number;
|
|
111
|
+
estate_pct: number;
|
|
112
|
+
rental_amount: number;
|
|
113
|
+
rental_frequency: 'Monthly' | 'Annual';
|
|
114
|
+
rental_start_age: number;
|
|
115
|
+
rental_end_age: number;
|
|
116
|
+
rental_inflation_adjusted: boolean;
|
|
117
|
+
rental_taxable: boolean;
|
|
118
|
+
rental_tax_rate: number;
|
|
119
|
+
mortgage_payment: number;
|
|
120
|
+
mortgage_frequency: 'Monthly' | 'Annual';
|
|
121
|
+
mortgage_end_age: number;
|
|
122
|
+
income_amount: number;
|
|
123
|
+
income_frequency: 'Annual' | 'Monthly';
|
|
124
|
+
income_start_age: number;
|
|
125
|
+
income_end_age: number;
|
|
126
|
+
inflation_adjusted: boolean;
|
|
127
|
+
taxable: boolean;
|
|
128
|
+
tax_rate: number;
|
|
129
|
+
income_destination: string;
|
|
130
|
+
reinvest_target_item_index: number | null;
|
|
131
|
+
income_mode: 'flat' | 'staggered';
|
|
132
|
+
income_steps: IncomeStep[];
|
|
133
|
+
salary_raise_pct: number;
|
|
134
|
+
salary_raise_mode: 'flat' | 'staggered';
|
|
135
|
+
salary_raise_steps: RaiseStep[];
|
|
136
|
+
salary_bonus_pct: number;
|
|
137
|
+
cash_yield_mode: 'flat' | 'staggered';
|
|
138
|
+
cash_yield_steps: YieldStep[];
|
|
139
|
+
loan_opening_principal: number;
|
|
140
|
+
loan_interest_rate_pct: number;
|
|
141
|
+
loan_payment_mode: 'principal_and_interest' | 'interest_only';
|
|
142
|
+
loan_annual_principal_payment: number;
|
|
143
|
+
loan_draws: LoanDraw[];
|
|
144
|
+
loan_term_years: number;
|
|
145
|
+
loan_start_age: number;
|
|
146
|
+
loan_credit_at_start: boolean;
|
|
147
|
+
loan_lump_repayments: LumpRepayment[];
|
|
148
|
+
}
|
|
149
|
+
export interface Scenario {
|
|
150
|
+
[key: string]: unknown;
|
|
151
|
+
name: string;
|
|
152
|
+
current_age: number;
|
|
153
|
+
retirement_age: number;
|
|
154
|
+
end_age: number;
|
|
155
|
+
current_balance: number;
|
|
156
|
+
contrib_amount: number;
|
|
157
|
+
contrib_cadence: Cadence;
|
|
158
|
+
contrib_increase_pct: number;
|
|
159
|
+
nominal_return_pct: number;
|
|
160
|
+
return_stdev_pct: number;
|
|
161
|
+
return_distribution: 'log-normal' | 'normal';
|
|
162
|
+
inflation_pct: number;
|
|
163
|
+
inflation_enabled: boolean;
|
|
164
|
+
fee_pct: number;
|
|
165
|
+
perf_fee_pct: number;
|
|
166
|
+
withdrawal_method: 'Fixed % of prior-year end balance' | 'Fixed real-dollar amount';
|
|
167
|
+
withdrawal_pct: number;
|
|
168
|
+
withdrawal_real_amount: number;
|
|
169
|
+
withdrawal_frequency: 'Annual' | 'Monthly';
|
|
170
|
+
withdrawal_strategy: 'Standard' | 'Guyton-Klinger' | 'Age-Banded';
|
|
171
|
+
gk_ceiling_pct: number;
|
|
172
|
+
gk_floor_pct: number;
|
|
173
|
+
gk_prosperity_threshold: number;
|
|
174
|
+
gk_capital_preservation_threshold: number;
|
|
175
|
+
spending_phases: SpendingPhase[];
|
|
176
|
+
enable_mc: boolean;
|
|
177
|
+
mc_runs: number;
|
|
178
|
+
enable_taxes: boolean;
|
|
179
|
+
effective_tax_rate_pct: number;
|
|
180
|
+
tax_jurisdiction: 'Custom' | 'Cayman Islands' | 'US' | 'UK';
|
|
181
|
+
tax_config: TaxConfig | null;
|
|
182
|
+
tax_deferred_pct: number;
|
|
183
|
+
planning_mode: 'Individual' | 'Couple';
|
|
184
|
+
partner_name: string;
|
|
185
|
+
partner_current_age: number | null;
|
|
186
|
+
partner_retirement_age: number | null;
|
|
187
|
+
partner_income_sources: IncomeSource[];
|
|
188
|
+
assets: Asset[];
|
|
189
|
+
liquid_pct: number;
|
|
190
|
+
detail_mode: 'basic' | 'advanced';
|
|
191
|
+
financial_items: FinancialItem[];
|
|
192
|
+
income_sources: IncomeSource[];
|
|
193
|
+
liquidity_events: LiquidityEvent[];
|
|
194
|
+
desired_estate: number;
|
|
195
|
+
black_swan_enabled: boolean;
|
|
196
|
+
black_swan_age: number;
|
|
197
|
+
black_swan_loss_pct: number;
|
|
198
|
+
currency_code: string;
|
|
199
|
+
currency_symbol: string;
|
|
200
|
+
withdrawal_order: string;
|
|
201
|
+
}
|
|
202
|
+
export interface TimelineRow {
|
|
203
|
+
age: number;
|
|
204
|
+
start_balance_nominal: number;
|
|
205
|
+
contributions: number;
|
|
206
|
+
liquidity_net: number;
|
|
207
|
+
income: number;
|
|
208
|
+
withdrawals: number;
|
|
209
|
+
desired_spending: number;
|
|
210
|
+
fees: number;
|
|
211
|
+
taxes: number;
|
|
212
|
+
income_taxes: number;
|
|
213
|
+
growth: number;
|
|
214
|
+
end_balance_nominal: number;
|
|
215
|
+
cpi_index: number;
|
|
216
|
+
end_balance_real: number;
|
|
217
|
+
end_cash_nominal: number;
|
|
218
|
+
end_debt_nominal: number;
|
|
219
|
+
end_investments_nominal: number;
|
|
220
|
+
end_liquid_nominal: number;
|
|
221
|
+
end_illiquid_nominal: number;
|
|
222
|
+
loan_interest: number;
|
|
223
|
+
loan_principal_repaid: number;
|
|
224
|
+
mortgage_paid: number;
|
|
225
|
+
cash_yield: number;
|
|
226
|
+
insolvency: boolean;
|
|
227
|
+
shortfall_mandatory: number;
|
|
228
|
+
shortfall_contributions: number;
|
|
229
|
+
shortfall_withdrawals: number;
|
|
230
|
+
}
|
|
231
|
+
export interface FanChartRow {
|
|
232
|
+
age: number;
|
|
233
|
+
p10: number;
|
|
234
|
+
p25: number;
|
|
235
|
+
p50: number;
|
|
236
|
+
p75: number;
|
|
237
|
+
p90: number;
|
|
238
|
+
}
|
|
239
|
+
export interface Metrics {
|
|
240
|
+
terminal_nominal: number;
|
|
241
|
+
terminal_real: number;
|
|
242
|
+
first_shortfall_age: number | null;
|
|
243
|
+
readiness_score: number;
|
|
244
|
+
total_contributions: number;
|
|
245
|
+
total_withdrawals: number;
|
|
246
|
+
total_fees: number;
|
|
247
|
+
total_taxes: number;
|
|
248
|
+
estate_value: number;
|
|
249
|
+
}
|
|
250
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Engine Types — standalone type definitions (no Zod dependency)
|
|
3
|
+
//
|
|
4
|
+
// These are plain TypeScript equivalents of the subset of @shorecrest/schemas
|
|
5
|
+
// types that the engine uses for computation.
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Withdrawal Strategy Implementations
|
|
3
|
+
*
|
|
4
|
+
* Three strategies: Standard, Guyton-Klinger, Age-Banded.
|
|
5
|
+
* Dispatched via calculateWithdrawal() based on scenario.withdrawal_strategy.
|
|
6
|
+
*
|
|
7
|
+
* Edge cases handled:
|
|
8
|
+
* - Near-zero threshold ($100): prevents asymptotic depletion with high withdrawal rates
|
|
9
|
+
* - Age-Banded gaps: return 0, log warning
|
|
10
|
+
* - Age-Banded overlaps: first-match wins (Array.find behavior)
|
|
11
|
+
* - GK oscillation: bounded by floor/ceiling — no special handling needed
|
|
12
|
+
* - RMD override: caller responsibility (if RMD > withdrawal, caller uses RMD)
|
|
13
|
+
*/
|
|
14
|
+
import type { Scenario, SpendingPhase } from './types';
|
|
15
|
+
/** Balance below this threshold is treated as depleted (prevents asymptotic depletion). */
|
|
16
|
+
export declare const NEAR_ZERO_THRESHOLD = 100;
|
|
17
|
+
export interface GKState {
|
|
18
|
+
/** The withdrawal amount from the first retirement year (nominal). */
|
|
19
|
+
initialWithdrawal: number;
|
|
20
|
+
/** The initial withdrawal rate (withdrawal / balance * 100). */
|
|
21
|
+
initialRate: number;
|
|
22
|
+
/** The most recent withdrawal amount (nominal). */
|
|
23
|
+
priorWithdrawal: number;
|
|
24
|
+
}
|
|
25
|
+
export interface StandardWithdrawalParams {
|
|
26
|
+
withdrawalMethod: Scenario['withdrawal_method'];
|
|
27
|
+
withdrawalPct: number;
|
|
28
|
+
withdrawalRealAmount: number;
|
|
29
|
+
withdrawalFrequency: Scenario['withdrawal_frequency'];
|
|
30
|
+
priorEndBalance: number;
|
|
31
|
+
availableBalance: number;
|
|
32
|
+
cpiIndex: number;
|
|
33
|
+
}
|
|
34
|
+
export interface GuytonKlingerWithdrawalParams {
|
|
35
|
+
currentBalance: number;
|
|
36
|
+
availableBalance: number;
|
|
37
|
+
cpiIndex: number;
|
|
38
|
+
/** GK state from prior year, or null if this is the first retirement year. */
|
|
39
|
+
gkState: GKState | null;
|
|
40
|
+
/** Standard withdrawal params used to compute the first-year withdrawal. */
|
|
41
|
+
standardParams: StandardWithdrawalParams;
|
|
42
|
+
/** Guardrail configuration from the scenario. */
|
|
43
|
+
gkCeilingPct: number;
|
|
44
|
+
gkFloorPct: number;
|
|
45
|
+
gkProsperityThreshold: number;
|
|
46
|
+
gkCapitalPreservationThreshold: number;
|
|
47
|
+
}
|
|
48
|
+
export interface AgeBandedWithdrawalParams {
|
|
49
|
+
age: number;
|
|
50
|
+
currentBalance: number;
|
|
51
|
+
availableBalance: number;
|
|
52
|
+
spendingPhases: SpendingPhase[];
|
|
53
|
+
cpiIndex: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Calculate withdrawal using the Standard strategy.
|
|
57
|
+
*
|
|
58
|
+
* - "Fixed % of prior-year end balance": priorEndBalance * (withdrawal_pct / 100)
|
|
59
|
+
* - "Fixed real-dollar amount": withdrawal_real_amount * cpiIndex
|
|
60
|
+
*
|
|
61
|
+
* Monthly frequency is annualized (multiply by 12).
|
|
62
|
+
* Result is capped at available balance.
|
|
63
|
+
*/
|
|
64
|
+
export declare function calculateStandardWithdrawal(params: StandardWithdrawalParams): number;
|
|
65
|
+
/**
|
|
66
|
+
* Calculate withdrawal using the Guyton-Klinger guardrail strategy.
|
|
67
|
+
*
|
|
68
|
+
* First retirement year: compute via standard calculation to set the initial
|
|
69
|
+
* withdrawal and initial rate.
|
|
70
|
+
*
|
|
71
|
+
* Subsequent years:
|
|
72
|
+
* current_rate = priorWithdrawal / currentBalance * 100
|
|
73
|
+
*
|
|
74
|
+
* Prosperity rule (balance grew, rate dropped):
|
|
75
|
+
* if current_rate < initialRate * (1 - prosperity_threshold/100):
|
|
76
|
+
* withdrawal = priorWithdrawal * 1.10 (increase 10%)
|
|
77
|
+
*
|
|
78
|
+
* Capital preservation rule (balance dropped, rate rose):
|
|
79
|
+
* if current_rate > initialRate * (1 + preservation_threshold/100):
|
|
80
|
+
* withdrawal = priorWithdrawal * 0.90 (decrease 10%)
|
|
81
|
+
*
|
|
82
|
+
* Else: withdrawal = priorWithdrawal (no change)
|
|
83
|
+
*
|
|
84
|
+
* Hard limits:
|
|
85
|
+
* max = initialWithdrawal * (1 + ceiling_pct/100)
|
|
86
|
+
* min = initialWithdrawal * (1 - floor_pct/100)
|
|
87
|
+
* withdrawal = clamp(withdrawal, min, max)
|
|
88
|
+
*
|
|
89
|
+
* Capped at available balance.
|
|
90
|
+
*/
|
|
91
|
+
export declare function calculateGuytonKlingerWithdrawal(params: GuytonKlingerWithdrawalParams): {
|
|
92
|
+
withdrawal: number;
|
|
93
|
+
gkState: GKState;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Calculate withdrawal using the Age-Banded strategy.
|
|
97
|
+
*
|
|
98
|
+
* Finds the first spending phase where start_age <= age <= end_age.
|
|
99
|
+
* - If mode = 'percent': withdrawal = currentBalance * (amount / 100)
|
|
100
|
+
* - If mode = 'amount': withdrawal = amount * cpiIndex (inflation-adjusted)
|
|
101
|
+
*
|
|
102
|
+
* If no phase covers the current age, returns 0 and logs a warning (gap).
|
|
103
|
+
* If phases overlap, the first match wins (Array.find behavior).
|
|
104
|
+
*/
|
|
105
|
+
export declare function calculateAgeBandedWithdrawal(params: AgeBandedWithdrawalParams): number;
|
|
106
|
+
/** Parameters for the main dispatcher, combining scenario config and simulation state. */
|
|
107
|
+
export interface WithdrawalParams {
|
|
108
|
+
/** The full scenario configuration. */
|
|
109
|
+
scenario: Scenario;
|
|
110
|
+
/** Current simulation state. */
|
|
111
|
+
state: {
|
|
112
|
+
age: number;
|
|
113
|
+
currentBalance: number;
|
|
114
|
+
priorEndBalance: number;
|
|
115
|
+
availableBalance: number;
|
|
116
|
+
cpiIndex: number;
|
|
117
|
+
gkState: GKState | null;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/** Result from the withdrawal dispatcher. */
|
|
121
|
+
export interface WithdrawalResult {
|
|
122
|
+
/** The withdrawal amount for this year (capped at available balance). */
|
|
123
|
+
withdrawal: number;
|
|
124
|
+
/** Updated GK state (only present for Guyton-Klinger strategy). */
|
|
125
|
+
gkState?: GKState;
|
|
126
|
+
/** True if the balance is below the near-zero threshold post-withdrawal. */
|
|
127
|
+
effectivelyDepleted: boolean;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Main withdrawal dispatcher.
|
|
131
|
+
*
|
|
132
|
+
* Routes to the correct strategy based on scenario.withdrawal_strategy, then
|
|
133
|
+
* applies the near-zero depletion threshold ($100).
|
|
134
|
+
*/
|
|
135
|
+
export declare function calculateWithdrawal(scenario: Scenario, state: WithdrawalParams['state']): WithdrawalResult;
|
|
136
|
+
//# sourceMappingURL=withdrawal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"withdrawal.d.ts","sourceRoot":"","sources":["../src/withdrawal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAMvD,2FAA2F;AAC3F,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAMvC,MAAM,WAAW,OAAO;IACtB,sEAAsE;IACtE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,eAAe,EAAE,MAAM,CAAC;CACzB;AAMD,MAAM,WAAW,wBAAwB;IACvC,gBAAgB,EAAE,QAAQ,CAAC,mBAAmB,CAAC,CAAC;IAChD,aAAa,EAAE,MAAM,CAAC;IACtB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,QAAQ,CAAC,sBAAsB,CAAC,CAAC;IACtD,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAMD,MAAM,WAAW,6BAA6B;IAC5C,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IAEjB,8EAA8E;IAC9E,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAExB,4EAA4E;IAC5E,cAAc,EAAE,wBAAwB,CAAC;IAEzC,iDAAiD;IACjD,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,8BAA8B,EAAE,MAAM,CAAC;CACxC;AAMD,MAAM,WAAW,yBAAyB;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAMD;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,wBAAwB,GAC/B,MAAM,CA2BR;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,6BAA6B,GACpC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAkF1C;AAMD;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,yBAAyB,GAChC,MAAM,CA6BR;AAMD,0FAA0F;AAC1F,MAAM,WAAW,gBAAgB;IAC/B,uCAAuC;IACvC,QAAQ,EAAE,QAAQ,CAAC;IAEnB,gCAAgC;IAChC,KAAK,EAAE;QACL,GAAG,EAAE,MAAM,CAAC;QACZ,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;QACxB,gBAAgB,EAAE,MAAM,CAAC;QACzB,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;KACzB,CAAC;CACH;AAED,6CAA6C;AAC7C,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,4EAA4E;IAC5E,mBAAmB,EAAE,OAAO,CAAC;CAC9B;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,GAC/B,gBAAgB,CA0FlB"}
|