Skip to content

Commit 239912f

Browse files
committed
feat: state pension
1 parent 5468e9d commit 239912f

2 files changed

Lines changed: 34 additions & 7 deletions

File tree

src/lib/ForecastGraph.svelte

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@
4646
.x((d) => xScale(d.year))
4747
.y((d) => yScale(d.cashFlow));
4848
49+
$: zeroLine = d3
50+
.line<{ year: Date; cashFlow: number }>()
51+
.x((d) => xScale(d.year))
52+
.y((d) => yScale(0));
53+
4954
afterUpdate(() => {
5055
const s = d3.select(svg);
5156
@@ -76,6 +81,7 @@
7681
<path d={savingsLine(data)} fill="none" stroke="steelblue" stroke-width="1.5" />
7782
<path d={cashFlowLine(data)} fill="none" stroke="green" stroke-width="1.5" />
7883
<path d={pensionRightsLine(data)} fill="none" stroke="red" stroke-width="1.5" />
84+
<path d={zeroLine(data)} fill="none" stroke="grey" stroke-width="0.5" />
7985
</svg>
8086

8187
<style>

src/lib/simulation.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
export interface YearState {
1111
year: number;
12+
age: number;
1213
iskSavings: number;
1314
compoundedInflation: number;
1415
cashFlow: number;
@@ -33,6 +34,9 @@ export interface Parameters {
3334
incomePensionRate: number;
3435
premiumPensionContributionRate: number;
3536
premiumPensionRate: number;
37+
statePensionAge: number;
38+
generalLifeExpectancy: number;
39+
3640
}
3741

3842
export interface TimeBoundCashFlow {
@@ -68,6 +72,8 @@ export function defaultScenario(): Scenario {
6872
incomePensionRate: 0.033,
6973
premiumPensionContributionRate: 0.025,
7074
premiumPensionRate: 0.074,
75+
statePensionAge: 70,
76+
generalLifeExpectancy: 80,
7177
cashFlows: [
7278
{
7379
startYear: currentYear,
@@ -90,6 +96,7 @@ export function defaultScenario(): Scenario {
9096
},
9197
startState: {
9298
year: currentYear,
99+
age: 30,
93100
iskSavings: 1000000,
94101
compoundedInflation: 1,
95102
cashFlow: 0,
@@ -102,6 +109,7 @@ export function defaultScenario(): Scenario {
102109

103110
function simulateYear(state: YearState, params: Parameters): YearState {
104111
const newYear = state.year + 1;
112+
const newAge = state.age + 1;
105113

106114
const compoundedInflation = state.compoundedInflation * (1 + params.inflationRate);
107115
const compoundedInflationYearlyAverage =
@@ -132,21 +140,33 @@ function simulateYear(state: YearState, params: Parameters): YearState {
132140
const premiumPensionContributionPostInflation =
133141
yearlyGrossPublicPensionContributingIncomePostInflation * params.premiumPensionContributionRate;
134142

135-
const incomePensionRights =
143+
// Simplification: No new pension rights after pension age
144+
const incomePensionRights = newAge <= params.statePensionAge ?
136145
(state.incomePensionRights + incomePensionContributionPostInflation / 2) *
137146
(1 + params.incomePensionRate) +
138-
incomePensionContributionPostInflation / 2;
139-
const premiumPensionRights =
147+
incomePensionContributionPostInflation / 2 : state.incomePensionRights;
148+
const premiumPensionRights = newAge <= params.statePensionAge ?
140149
(state.premiumPensionRights + premiumPensionContributionPostInflation / 2) *
141150
(1 + params.premiumPensionRate) +
142-
premiumPensionContributionPostInflation / 2;
151+
premiumPensionContributionPostInflation / 2 : state.premiumPensionRights;
152+
153+
// Simplification: arvsvinst: https://www.pensionsmyndigheten.se/statistik/publikationer/orange-rapport-2021/a-berakningsfaktorer.html
154+
// Current we instead simulate arvsvinst by pretending that the pension is paid out based on life expectancy
155+
156+
// Jobbskatteavdrag: https://www.bjornlunden.se/skatt/jobbskatteavdrag__196
157+
158+
// grundavdrag https://www4.skatteverket.se/rattsligvagledning/27071.html?date=2024-01-01#section63-3
159+
// api grundavdrag: https://skatteverket.entryscape.net/rowstore/dataset/ebbd8d70-9b9c-4327-b2ce-a371ee66744c/html
160+
161+
// Simplification: state pension payout is taxed as normal income
162+
const accumulatedPensionRights = state.incomePensionRights + state.premiumPensionRights;
163+
const grossPensionPayout = newAge > params.statePensionAge ? accumulatedPensionRights / (params.generalLifeExpectancy - params.statePensionAge): 0;
143164

144-
// TODO: arvsvinst: https://www.pensionsmyndigheten.se/statistik/publikationer/orange-rapport-2021/a-berakningsfaktorer.html
145165

146166
const yearlyGrossTaxableEarnedIncomePostInflation =
147167
yearlyCashFlows
148168
.filter(({ kind }) => kind === 'GrossTaxableEarnedIncome')
149-
.reduce((acc, cf) => acc + cf.yearlyCashFlow, 0) * compoundedInflationYearlyAverage;
169+
.reduce((acc, cf) => acc + cf.yearlyCashFlow, 0) * compoundedInflationYearlyAverage + grossPensionPayout;
150170

151171
const thisYearCountyTaxBracketRate =
152172
params.countyTaxRate + (params.churchTax ? params.churchTaxRate : 0);
@@ -188,7 +208,8 @@ function simulateYear(state: YearState, params: Parameters): YearState {
188208
const newSavings = state.iskSavings + yearlyNetCashFlowPostInflation + iskInterest - iskTax;
189209

190210
return {
191-
year: state.year + 1,
211+
year: newYear,
212+
age: newAge,
192213
iskSavings: Math.floor(newSavings),
193214
compoundedInflation: compoundedInflation,
194215
cashFlow: yearlyNetCashFlowPostInflation,

0 commit comments

Comments
 (0)