-
Notifications
You must be signed in to change notification settings - Fork 0
/
monte_carlo.R
153 lines (123 loc) · 6.75 KB
/
monte_carlo.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
single_run_preretirement <- function(df) {
# convert to a data matrix, much much faster than a dataframe for these calculations
# cannot store character strings though!
df <- data.matrix(df)
df[,'stock_return_percentage'] = round(sample(stock_normal_dist(), nrow(df)), 2)
df[,'bond_return_percentage'] = round(sample(bond_normal_dist(), nrow(df)), 2)
# apply inflation
df[,'inflation_percentage'] = round(sample(inflation_normal_dist(), nrow(df)), 2)
for (i in 2:nrow(df)) {
# apply inflation to FIRE target
df[i,]['fire_target'] = round(df[i,]['fire_target'] * (1+(df[i,]['inflation_percentage']/100)), 2)
}
for (i in 2:nrow(df)) {
# apply income
df[i,]['income'] = round(df[i-1,]['income'] * (1+(df[i,]['income_growth_percentage']/100)), 2)
df[i,]['savings'] = round(df[i-1,]['income'] * (savings_percentage()/100), 2)
}
# apply income to purchasing additional brokerage
df[,'brokerage_stock_amount'] = round(df[,'brokerage_stock_amount'] + (df[,'savings'] * (input$brokerage_stock_percentage/100)), 2)
df[,'brokerage_bond_amount'] = round(df[,'brokerage_bond_amount'] + (df[,'savings'] * (input$brokerage_bond_percentage/100)), 2)
for (i in 2:nrow(df)) {
# apply brokerage
df[i,]['brokerage_stock_amount'] = round(df[i-1,]['brokerage_stock_amount'] * (1+(df[i,]['stock_return_percentage']/100)), 2)
df[i,]['brokerage_bond_amount'] = round(df[i-1,]['brokerage_bond_amount'] * (1+(df[i,]['bond_return_percentage']/100)), 2)
# brokerage asset rebalance
if (input$rebalanceassets) {
df[i,]['brokerage_amount'] = round(df[i,]['brokerage_stock_amount'] + df[i,]['brokerage_bond_amount'], 2)
delta_stock = round(df[i,]['brokerage_stock_amount'] - (df[i,]['brokerage_amount'] * (input$target_stock_percentage/100)), 2)
delta_bond = round(df[i,]['brokerage_bond_amount'] - (df[i,]['brokerage_amount'] * (input$target_bond_percentage/100)), 2)
df[i,]['brokerage_stock_amount'] = df[i,]['brokerage_stock_amount'] - delta_stock
df[i,]['brokerage_bond_amount'] = df[i,]['brokerage_bond_amount'] - delta_bond
}
}
df[,'brokerage_amount'] = df[,'brokerage_stock_amount'] + df[,'brokerage_bond_amount']
df[,'brokerage_stock_percentage'] = round((df[,'brokerage_stock_amount'] / df[,'brokerage_amount']) * 100, 2)
df[,'brokerage_bond_percentage'] = round((df[,'brokerage_bond_amount'] / df[,'brokerage_amount']) * 100, 2)
# did we hit the FIRE goal? remember this adjusts over time for inflation
df[,'hit_fire_goal'] = df[,'brokerage_amount'] >= df[,'fire_target']
# clean out some of the variables set in the first row since they don't make sense
df[1,]['inflation_percentage'] = NA
df[1,]['savings'] = NA
df[1,]['income_growth_percentage'] = NA
df[1,]['stock_return_percentage'] = NA
df[1,]['bond_return_percentage'] = NA
# convert back to a dataframe
df <- data.frame(df)
return(df)
}
single_run_retirement <- function(df, yrs, death_projections) {
# convert to a data matrix, much much faster than a dataframe for these calculations
# cannot store character strings though!
df <- data.matrix(df)
# take yrs and run for that many iterations to simulate a "starting" retirement spending
# amount adjusted for inflation
spending = input$retirement_spending
for (i in 1:yrs) {
spending = spending * (1+(sample(inflation_normal_dist(), 1)/100))
}
df[,'retirement_spending'] = round(spending, 2)
# apply year going forward
for (i in 2:nrow(df)) {
df[i,]['year'] = df[i-1,]['year']+1
}
if (input$useavglife) {
# apply mortality chance based on gender
for (i in 2:nrow(df)) {
mortality_df <- death_projections %>%
filter(gender %in% input$gender) %>%
filter(age == df[i,]['age']) %>%
filter(Year == df[i,]['year'])
# account for cases outside the range of mortality data we have available
# TODO: should we try to extrapolate future predictions outside of the social security ones?
if (nrow(mortality_df) != 0) {
if (df[i-1,]['deceased'] == 0 & runif(1) <= mortality_df$probability) {
df[i,]['deceased'] = 1
} else if (df[i-1,]['deceased'] == 1) {
df[i,]['deceased'] = 1
} else {
# no-op, already set to 0
}
} else {
# unknown, no data available
df[i,]['deceased'] = 2
}
}
}
df[,'stock_return_percentage'] = round(sample(stock_normal_dist(), nrow(df)), 2)
df[,'bond_return_percentage'] = round(sample(bond_normal_dist(), nrow(df)), 2)
# apply inflation
df[,'inflation_percentage'] = round(sample(inflation_normal_dist(), nrow(df)), 2)
for (i in 2:nrow(df)) {
# apply inflation to spending
df[i,]['retirement_spending'] = round(df[i,]['retirement_spending'] * (1+(df[i,]['inflation_percentage']/100)), 2)
}
for (i in 2:nrow(df)) {
# apply brokerage
df[i,]['brokerage_stock_amount'] = round(df[i-1,]['brokerage_stock_amount'] * (1+(df[i,]['stock_return_percentage']/100)), 2)
df[i,]['brokerage_bond_amount'] = round(df[i-1,]['brokerage_bond_amount'] * (1+(df[i,]['bond_return_percentage']/100)), 2)
# apply spending
df[i,]['brokerage_stock_amount'] = round(df[i,]['brokerage_stock_amount'] - (df[i,]['retirement_spending'] * (input$target_stock_retirement_percentage/100)), 2)
df[i,]['brokerage_bond_amount'] = round(df[i,]['brokerage_bond_amount'] - (df[i,]['retirement_spending'] * (input$target_bond_retirement_percentage/100)), 2)
# brokerage asset rebalance
df[i,]['brokerage_amount'] = round(df[i,]['brokerage_stock_amount'] + df[i,]['brokerage_bond_amount'], 2)
delta_stock = round(df[i,]['brokerage_stock_amount'] - (df[i,]['brokerage_amount'] * (input$target_stock_retirement_percentage/100)), 2)
delta_bond = round(df[i,]['brokerage_bond_amount'] - (df[i,]['brokerage_amount'] * (input$target_bond_retirement_percentage/100)), 2)
df[i,]['brokerage_stock_amount'] = df[i,]['brokerage_stock_amount'] - delta_stock
df[i,]['brokerage_bond_amount'] = df[i,]['brokerage_bond_amount'] - delta_bond
}
df[,'brokerage_amount'] = df[,'brokerage_stock_amount'] + df[,'brokerage_bond_amount']
df[,'brokerage_stock_percentage'] = round((df[,'brokerage_stock_amount'] / df[,'brokerage_amount']) * 100, 2)
df[,'brokerage_bond_percentage'] = round((df[,'brokerage_bond_amount'] / df[,'brokerage_amount']) * 100, 2)
# are we broke?
df[,'broke'] = df[,'brokerage_amount'] <= 0
# clean out some of the variables set in the first row since they don't make sense
df[1,]['inflation_percentage'] = NA
df[1,]['stock_return_percentage'] = NA
df[1,]['bond_return_percentage'] = NA
# convert back to a dataframe
df <- data.frame(df)
# apply gender back in since it's a character and not numeric
df$gender = input$gender
return(df)
}