@@ -15,7 +15,7 @@ export interface YearState {
1515 cashFlow : number ;
1616 incomePensionRights : number ;
1717 premiumPensionRights : number ;
18- //servicePensionSavings : number;
18+ occupationalPensionSavings : number ;
1919}
2020
2121export interface Parameters {
@@ -36,16 +36,27 @@ export interface Parameters {
3636 premiumPensionRate : number ;
3737 statePensionAge : number ;
3838 generalLifeExpectancy : number ;
39-
39+ occupationalPensionInterestRate : number ;
40+ occupationalPensionAge : number ;
41+ occupationalPensionDuration : number ;
4042}
4143
42- export interface TimeBoundCashFlow {
44+ export type TimeBoundCashFlow = GrossTaxableEarnedIncome | NetExpense ;
45+
46+ interface BaseTimeBoundCashFlow {
4347 startYear : number ; // Inclusive
4448 endYear ?: number ; // Exclusive
4549 yearlyCashFlow : number ;
46- kind : 'GrossTaxableEarnedIncome' | 'NetExpense' ;
47- publicPensionContributing : boolean ; // Only used for GrossTaxableEarnedIncome
48- //servicePensionContribution: number;
50+ }
51+
52+ interface GrossTaxableEarnedIncome extends BaseTimeBoundCashFlow {
53+ kind : 'GrossTaxableEarnedIncome' ;
54+ publicPensionContributing : boolean ;
55+ occupationalPensionContributionRate : number ;
56+ }
57+
58+ interface NetExpense extends BaseTimeBoundCashFlow {
59+ kind : 'NetExpense' ;
4960}
5061
5162export interface Scenario {
@@ -74,23 +85,23 @@ export function defaultScenario(): Scenario {
7485 premiumPensionRate : 0.074 ,
7586 statePensionAge : 70 ,
7687 generalLifeExpectancy : 80 ,
88+ occupationalPensionInterestRate : 0.07 ,
89+ occupationalPensionAge : 65 ,
90+ occupationalPensionDuration : 10 ,
7791 cashFlows : [
7892 {
7993 startYear : currentYear ,
80- endYear : currentYear + 9 ,
94+ endYear : currentYear + 8 ,
8195 yearlyCashFlow : 52000 * 12 ,
8296 kind : 'GrossTaxableEarnedIncome' ,
83- publicPensionContributing : true
84- // servicePensionContribution: 4.5
97+ publicPensionContributing : true ,
98+ occupationalPensionContributionRate : 0.045
8599 } ,
86100 {
87101 startYear : currentYear ,
88102 endYear : undefined ,
89103 yearlyCashFlow : 15000 * 12 ,
90- kind : 'NetExpense' ,
91- publicPensionContributing : false
92- // publicPensionContributing: false,
93- // servicePensionContribution: 0
104+ kind : 'NetExpense'
94105 }
95106 ]
96107 } ,
@@ -101,7 +112,8 @@ export function defaultScenario(): Scenario {
101112 compoundedInflation : 1 ,
102113 cashFlow : 0 ,
103114 premiumPensionRights : 0 ,
104- incomePensionRights : 0
115+ incomePensionRights : 0 ,
116+ occupationalPensionSavings : 0
105117 } ,
106118 endYear : 2080
107119 } ;
@@ -126,11 +138,12 @@ function simulateYear(state: YearState, params: Parameters): YearState {
126138
127139 const yearlyGrossPublicPensionContributingIncomePostInflation =
128140 Math . min (
129- yearlyCashFlows
130- . filter (
131- ( { kind, publicPensionContributing } ) =>
132- kind === 'GrossTaxableEarnedIncome' && publicPensionContributing
133- )
141+ (
142+ yearlyCashFlows . filter (
143+ ( { kind } ) => kind === 'GrossTaxableEarnedIncome'
144+ ) as GrossTaxableEarnedIncome [ ]
145+ )
146+ . filter ( ( { publicPensionContributing } ) => publicPensionContributing )
134147 . reduce ( ( acc , cf ) => acc + cf . yearlyCashFlow , 0 ) ,
135148 params . publicPensionContributionThreshold
136149 ) * compoundedInflationYearlyAverage ;
@@ -141,14 +154,18 @@ function simulateYear(state: YearState, params: Parameters): YearState {
141154 yearlyGrossPublicPensionContributingIncomePostInflation * params . premiumPensionContributionRate ;
142155
143156 // Simplification: No new pension rights after pension age
144- const incomePensionRights = newAge <= params . statePensionAge ?
145- ( state . incomePensionRights + incomePensionContributionPostInflation / 2 ) *
146- ( 1 + params . incomePensionRate ) +
147- incomePensionContributionPostInflation / 2 : state . incomePensionRights ;
148- const premiumPensionRights = newAge <= params . statePensionAge ?
149- ( state . premiumPensionRights + premiumPensionContributionPostInflation / 2 ) *
150- ( 1 + params . premiumPensionRate ) +
151- premiumPensionContributionPostInflation / 2 : state . premiumPensionRights ;
157+ const incomePensionRights =
158+ newAge <= params . statePensionAge
159+ ? ( state . incomePensionRights + incomePensionContributionPostInflation / 2 ) *
160+ ( 1 + params . incomePensionRate ) +
161+ incomePensionContributionPostInflation / 2
162+ : state . incomePensionRights ;
163+ const premiumPensionRights =
164+ newAge <= params . statePensionAge
165+ ? ( state . premiumPensionRights + premiumPensionContributionPostInflation / 2 ) *
166+ ( 1 + params . premiumPensionRate ) +
167+ premiumPensionContributionPostInflation / 2
168+ : state . premiumPensionRights ;
152169
153170 // Simplification: arvsvinst: https://www.pensionsmyndigheten.se/statistik/publikationer/orange-rapport-2021/a-berakningsfaktorer.html
154171 // Current we instead simulate arvsvinst by pretending that the pension is paid out based on life expectancy
@@ -159,14 +176,46 @@ function simulateYear(state: YearState, params: Parameters): YearState {
159176 // api grundavdrag: https://skatteverket.entryscape.net/rowstore/dataset/ebbd8d70-9b9c-4327-b2ce-a371ee66744c/html
160177
161178 // 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 ;
179+ const accumulatedPublicPensionRights = state . incomePensionRights + state . premiumPensionRights ;
180+ const grossPublicPensionPayout =
181+ newAge > params . statePensionAge
182+ ? accumulatedPublicPensionRights / ( params . generalLifeExpectancy - params . statePensionAge )
183+ : 0 ;
164184
185+ const yearlyGrossOccupationalPensionContributionPostInflation =
186+ (
187+ yearlyCashFlows . filter (
188+ ( { kind } ) => kind === 'GrossTaxableEarnedIncome'
189+ ) as GrossTaxableEarnedIncome [ ]
190+ ) . reduce ( ( acc , cf ) => acc + cf . yearlyCashFlow * cf . occupationalPensionContributionRate , 0 ) *
191+ compoundedInflationYearlyAverage ;
192+
193+ const grossOccupationalPensionPayout =
194+ state . age >= params . occupationalPensionAge &&
195+ state . age < params . occupationalPensionAge + params . occupationalPensionDuration
196+ ? state . occupationalPensionSavings *
197+ ( 1 / ( params . occupationalPensionDuration + params . occupationalPensionAge - state . age ) )
198+ : 0 ;
199+
200+ const accumulatedOccupationalPensionInterest =
201+ ( state . occupationalPensionSavings +
202+ ( yearlyGrossOccupationalPensionContributionPostInflation - grossOccupationalPensionPayout ) /
203+ 2 ) *
204+ params . occupationalPensionInterestRate ;
205+
206+ const accumulatedOccupationalPensionSavings =
207+ state . occupationalPensionSavings +
208+ yearlyGrossOccupationalPensionContributionPostInflation +
209+ accumulatedOccupationalPensionInterest -
210+ grossOccupationalPensionPayout ;
165211
166212 const yearlyGrossTaxableEarnedIncomePostInflation =
167213 yearlyCashFlows
168214 . filter ( ( { kind } ) => kind === 'GrossTaxableEarnedIncome' )
169- . reduce ( ( acc , cf ) => acc + cf . yearlyCashFlow , 0 ) * compoundedInflationYearlyAverage + grossPensionPayout ;
215+ . reduce ( ( acc , cf ) => acc + cf . yearlyCashFlow , 0 ) *
216+ compoundedInflationYearlyAverage +
217+ grossPublicPensionPayout +
218+ grossOccupationalPensionPayout ;
170219
171220 const thisYearCountyTaxBracketRate =
172221 params . countyTaxRate + ( params . churchTax ? params . churchTaxRate : 0 ) ;
@@ -196,12 +245,6 @@ function simulateYear(state: YearState, params: Parameters): YearState {
196245 const yearlyNetCashFlowPostInflation =
197246 yearlyNetIncomePostInflation - yearlyNetExpensePostInflation ;
198247
199- // const publicPensionContributingCashFlows = yearlyCashFlows.filter(cf => cf.publicPensionContributing);
200- // const publicPensionContributingCashFlow = publicPensionContributingCashFlows.reduce((acc, cf) => acc + cf.yearlyNetCashFlow, 0);
201-
202- // const yearlyIncomePensionRights = publicPensionContributingCashFlow * 0.16; // TODO: This should be calculated
203- // const yearlyPremiumPensionRights = publicPensionContributingCashFlow * 0.025;
204-
205248 const iskSavingsYearlyAverage = state . iskSavings + yearlyNetCashFlowPostInflation / 2 ;
206249 const iskInterest = iskSavingsYearlyAverage * params . iskInterestRate ;
207250 const iskTax = iskSavingsYearlyAverage * params . iskTax ;
@@ -214,8 +257,8 @@ function simulateYear(state: YearState, params: Parameters): YearState {
214257 compoundedInflation : compoundedInflation ,
215258 cashFlow : yearlyNetCashFlowPostInflation ,
216259 incomePensionRights : incomePensionRights ,
217- premiumPensionRights : premiumPensionRights
218- // servicePensionSavings: state.servicePensionSavings,
260+ premiumPensionRights : premiumPensionRights ,
261+ occupationalPensionSavings : accumulatedOccupationalPensionSavings
219262 } ;
220263}
221264
0 commit comments