Skip to content

Commit 4139d12

Browse files
committed
feat: operate on gross income
1 parent 0bd7d9b commit 4139d12

1 file changed

Lines changed: 66 additions & 29 deletions

File tree

src/lib/simulation.ts

Lines changed: 66 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,19 @@ export interface Parameters {
2222
iskInterestRate: number;
2323
iskTax: number;
2424
cashFlows: TimeBoundCashFlow[];
25+
countyTaxRate: number;
26+
churchTax: boolean;
27+
churchTaxRate: number;
28+
stateTaxRate: number;
29+
stateTaxThreshold: number;
30+
taxFreeThreshold: number;
2531
}
2632

2733
export interface TimeBoundCashFlow {
2834
startYear: number; // Inclusive
29-
endYear: number; // Exclusive
30-
yearlyNetCashFlow: number;
35+
endYear?: number; // Exclusive
36+
yearlyCashFlow: number;
37+
kind: 'GrossTaxableEarnedIncome' | 'NetExpense';
3138
//publicPensionContributing: boolean;
3239
//servicePensionContribution: number;
3340
}
@@ -45,32 +52,26 @@ export function defaultScenario(): Scenario {
4552
inflationRate: 0.025,
4653
iskInterestRate: 0.08,
4754
iskTax: 0.01086,
55+
countyTaxRate: 0.325, // Strängnäs:0.325, GBG: 0.326
56+
churchTax: false,
57+
churchTaxRate: 0.01045,
58+
stateTaxRate: 0.2,
59+
stateTaxThreshold: 52000 * 12,
60+
taxFreeThreshold: 24238,
4861
cashFlows: [
4962
{
5063
startYear: currentYear,
51-
endYear: currentYear + 8,
52-
yearlyNetCashFlow: 38500 * 12
64+
endYear: currentYear + 9,
65+
yearlyCashFlow: 52000 * 12,
66+
kind: 'GrossTaxableEarnedIncome'
5367
// publicPensionContributing: true,
5468
// servicePensionContribution: 4.5
5569
},
5670
{
5771
startYear: currentYear,
58-
endYear: currentYear + 8,
59-
yearlyNetCashFlow: 2755 * 12
60-
// publicPensionContributing: false,
61-
// servicePensionContribution: 100
62-
},
63-
{
64-
startYear: currentYear,
65-
endYear: currentYear + 8,
66-
yearlyNetCashFlow: -2755 * 12
67-
// publicPensionContributing: false,
68-
// servicePensionContribution: 0
69-
},
70-
{
71-
startYear: currentYear,
72-
endYear: currentYear + 50,
73-
yearlyNetCashFlow: -15000 * 12
72+
endYear: undefined,
73+
yearlyCashFlow: 15000 * 12,
74+
kind: 'NetExpense'
7475
// pensionContributing: false,
7576
// publicPensionContributing: false,
7677
// servicePensionContribution: 0
@@ -83,7 +84,7 @@ export function defaultScenario(): Scenario {
8384
compoundedInflation: 1,
8485
cashFlow: 0
8586
},
86-
endYear: 2100
87+
endYear: 2080
8788
};
8889
}
8990

@@ -94,28 +95,64 @@ function simulateYear(state: YearState, params: Parameters): YearState {
9495
const compoundedInflationYearlyAverage =
9596
state.compoundedInflation * (1 + params.inflationRate / 2);
9697

97-
const yearlyCashFlows = params.cashFlows.filter(
98-
(cf) => cf.startYear <= newYear && cf.endYear > newYear
99-
);
100-
const yearlyCashFlow = yearlyCashFlows.reduce((acc, cf) => acc + cf.yearlyNetCashFlow, 0);
101-
const yearlyCashFlowPostInflation = yearlyCashFlow * compoundedInflationYearlyAverage;
98+
const thisYearsStateTaxThreshold = params.stateTaxThreshold * compoundedInflationYearlyAverage;
99+
const thisYearsTaxFreeThreshold = params.taxFreeThreshold * compoundedInflationYearlyAverage;
100+
101+
const yearlyCashFlows = params.cashFlows.filter(({ startYear, endYear }) => {
102+
if (startYear > newYear) return false;
103+
if (endYear === undefined) return true;
104+
return endYear > newYear;
105+
});
106+
107+
const yearlyGrossTaxableEarnedIncomePostInflation =
108+
yearlyCashFlows
109+
.filter(({ kind }) => kind === 'GrossTaxableEarnedIncome')
110+
.reduce((acc, cf) => acc + cf.yearlyCashFlow, 0) * compoundedInflationYearlyAverage;
111+
112+
const thisYearCountyTaxBracketRate =
113+
params.countyTaxRate + (params.churchTax ? params.churchTaxRate : 0);
114+
const thisYearCountyTaxBracket =
115+
Math.max(
116+
Math.min(yearlyGrossTaxableEarnedIncomePostInflation, thisYearsStateTaxThreshold) -
117+
thisYearsTaxFreeThreshold,
118+
0
119+
) * thisYearCountyTaxBracketRate;
120+
121+
const thisYearStateTaxBracketRate = thisYearCountyTaxBracketRate + params.stateTaxRate;
122+
const thisYearStateTaxBracket =
123+
(Math.max(yearlyGrossTaxableEarnedIncomePostInflation, thisYearsStateTaxThreshold) -
124+
thisYearsStateTaxThreshold) *
125+
thisYearStateTaxBracketRate;
126+
127+
const yearlyNetIncomePostInflation =
128+
yearlyGrossTaxableEarnedIncomePostInflation -
129+
thisYearCountyTaxBracket -
130+
thisYearStateTaxBracket;
131+
132+
const yearlyNetExpensePostInflation =
133+
yearlyCashFlows
134+
.filter(({ kind }) => kind === 'NetExpense')
135+
.reduce((acc, cf) => acc + cf.yearlyCashFlow, 0) * compoundedInflationYearlyAverage;
136+
137+
const yearlyNetCashFlowPostInflation =
138+
yearlyNetIncomePostInflation - yearlyNetExpensePostInflation;
102139

103140
// const publicPensionContributingCashFlows = yearlyCashFlows.filter(cf => cf.publicPensionContributing);
104141
// const publicPensionContributingCashFlow = publicPensionContributingCashFlows.reduce((acc, cf) => acc + cf.yearlyNetCashFlow, 0);
105142

106143
// const yearlyIncomePensionRights = publicPensionContributingCashFlow * 0.16; // TODO: This should be calculated
107144
// const yearlyPremiumPensionRights = publicPensionContributingCashFlow * 0.025;
108145

109-
const iskSavingsYearlyAverage = state.iskSavings + yearlyCashFlowPostInflation / 2;
146+
const iskSavingsYearlyAverage = state.iskSavings + yearlyNetCashFlowPostInflation / 2;
110147
const iskInterest = iskSavingsYearlyAverage * params.iskInterestRate;
111148
const iskTax = iskSavingsYearlyAverage * params.iskTax;
112-
const newSavings = state.iskSavings + yearlyCashFlowPostInflation + iskInterest - iskTax;
149+
const newSavings = state.iskSavings + yearlyNetCashFlowPostInflation + iskInterest - iskTax;
113150

114151
return {
115152
year: state.year + 1,
116153
iskSavings: Math.floor(newSavings),
117154
compoundedInflation: compoundedInflation,
118-
cashFlow: yearlyCashFlow
155+
cashFlow: yearlyNetCashFlowPostInflation
119156
// premiumPensionRights: state.premiumPensionRights,
120157
// servicePensionSavings: state.servicePensionSavings,
121158
};

0 commit comments

Comments
 (0)