Fix a couple of bugs. Make the simulation run 100x and halt if
[retire.git] / parameters.py
1 import constants
2 import utils
3 from tax_brackets import tax_brackets
4 from money import money
5 from numpy import random
6
7 class parameters(object):
8     def get_initial_annual_expenses(self):
9         pass
10
11     def get_average_inflation_multiplier(self):
12         pass
13
14     def get_average_social_security_multiplier(self):
15         pass
16
17     def get_average_investment_return_multiplier(self):
18         pass
19
20     def get_initial_social_security_age(self, person):
21         pass
22
23     def get_initial_social_security_benefit(self, person):
24         pass
25
26     def get_federal_standard_deduction(self):
27         pass
28
29     def get_federal_ordinary_income_tax_brackets(self):
30         pass
31
32     def get_federal_dividends_and_long_term_gains_income_tax_brackets(self):
33         pass
34
35     def dump(self):
36         pass
37
38     def report_year(self, year):
39         pass
40
41 class mutable_default_parameters(parameters):
42     """A container to hold the initial states of several simulation
43        parameters.  Play with them as you see fit and see what happens."""
44
45     def __init__(self):
46         self.with_default_values()
47
48     def with_default_values(self):
49         # Annual expenses in USD at the start of the simulation.  This
50         # will be adjusted upwards with inflation_multiplier every year.
51         self.initial_annual_expenses = money(144300)
52
53         # The average US inflation rate during the simulation.  The
54         # Fed's target rate is 2.0% as of 2020.  The long term observed
55         # average historical inflation rate in the US is 2.1%.
56         #
57         # Note this is a multiplier... so 1.0 would be no inflation,
58         # 1.21 is 2.1% inflation, 0.9 is deflation, etc...
59         self.inflation_multiplier = 1.03
60
61         # We want to be able to model social security payments not
62         # keeping pace with inflation.  Like the inflation_multiplier
63         # above, this is a multiplier.  It affect the magnitide of
64         # social security payments year over year.
65         self.social_security_multiplier = 1.02
66
67         # This is the average investment return rate.  Asset allocation has
68         # a large effect on this as does the overall economy.  That said,
69         # the US stock market has returned 10%/year over a large enough
70         # time window.  Our investments at Fidelity have returned 6.91%
71         # in the lifetime they have been there.  The expected return of a
72         # 50/50 stock/bond investment mix based on historical data is 8.3%.
73         self.investment_multiplier = 1.04
74
75         # The age at which each person will take social security
76         # benefits in this simulation and how much she will get the
77         # first year.  Note, this benefit size increases year over
78         # year at social_security_multiplier.  This is what the social
79         # security website estimates if we both earn $0 from 2020 on:
80         #
81         #     Lynn's benefits:              Scott's benefits:
82         #      age 62 - $21,120              age 62 - $21,420
83         #      age 67 - $30,000              age 67 - $30,420
84         #      age 70 - $37,200              age 70 - $37,728
85         #
86         #                                        X        SCOTT          LYNN
87         self.social_security_age =             [0,           62,           62 ]
88         self.initial_social_security_dollars = [0, money(21000), money(21000) ]
89
90         # Tax details... the standard deduction amount and tax
91         # brackets for ordinary income and long term capital gains.
92         self.federal_standard_deduction_dollars = 24800
93         self.federal_ordinary_income_tax_brackets = tax_brackets(
94             constants.PESSIMISTIC_FEDERAL_INCOME_TAX_BRACKETS)
95         self.federal_dividends_and_long_term_gains_brackets = tax_brackets(
96             constants.CURRENT_LONG_TERM_GAIN_FEDERAL_TAX_BRACKETS)
97         return self
98
99     def set_initial_annual_expenses(self, expenses):
100         assert expenses >= 0, "You can't have negative expenses"
101         self.initial_annual_expenses = expenses
102         return self
103
104     def get_initial_annual_expenses(self):
105         return self.initial_annual_expenses
106
107     def set_average_inflation_multiplier(self, multiplier):
108         self.inflation_multiplier = multiplier
109         return self
110
111     def get_average_inflation_multiplier(self):
112         return self.inflation_multiplier
113
114     def set_average_social_security_multiplier(self, multiplier):
115         self.social_security_multiplier = multiplier
116         return self
117
118     def get_average_social_security_multiplier(self):
119         return self.social_security_multiplier
120
121     def set_average_investment_return_multiplier(self, multiplier):
122         self.investment_multiplier = multiplier
123         return self
124
125     def get_average_investment_return_multiplier(self):
126         return self.investment_multiplier
127
128     def set_initial_social_security_age_and_benefits(self,
129                                                      person,
130                                                      age,
131                                                      amount):
132         assert age >= 60 and age <= 70, "age should be between 60 and 70"
133         self.social_security_age[person] = age
134         assert amount >= 0, "Social security won't pay negative dollars"
135         self.initial_social_security_dollars[person] = amount
136         return self
137
138     def get_initial_social_security_age(self, person):
139         return self.social_security_age[person]
140
141     def get_initial_social_security_benefit(self, person):
142         return self.initial_social_security_dollars[person]
143
144     def set_federal_standard_deduction(self, deduction):
145         assert deduction >= 0, "Standard deduction should be non-negative"
146         self.federal_standard_deduction_dollars = deduction
147         return self
148
149     def get_federal_standard_deduction(self):
150         return self.federal_standard_deduction_dollars
151
152     def set_federal_ordinary_income_tax_brackets(self, brackets):
153         self.federal_ordinary_income_tax_brackets = brackets
154         return self
155
156     def get_federal_ordinary_income_tax_brackets(self):
157         return self.federal_ordinary_income_tax_brackets
158
159     def set_federal_dividends_and_long_term_gains_income_tax_brackets(self,
160                                                                       brackets):
161         self.federal_dividends_and_long_term_gains_brackets = brackets;
162         return self
163
164     def get_federal_dividends_and_long_term_gains_income_tax_brackets(self):
165         return self.federal_dividends_and_long_term_gains_brackets
166
167     def dump(self):
168         print "SIMULATION PARAMETERS"
169         print "  {:<50}: {:>14}".format("Initial year annual expenses",
170                                         self.initial_annual_expenses)
171         print "  {:<50}: {:>14}".format("Annual inflation rate",
172                                         utils.format_rate(self.inflation_multiplier))
173         print "  {:<50}: {:>14}".format("Average annual investment return rate",
174                                         utils.format_rate(self.investment_multiplier))
175         print "  {:<50}: {:>14}".format("Annual social security benefit increase rate",
176                                         utils.format_rate(self.social_security_multiplier))
177         print "  {:<50}: {:>14}".format("Age at which Lynn takes social security",
178                                         self.social_security_age[constants.LYNN])
179         print "  {:<50}: {:>14}".format("Lynn's first year social security benefit",
180                                         self.initial_social_security_dollars[constants.LYNN])
181         print "  {:<50}: {:>14}".format("Age at which Scott takes social security",
182                                         self.social_security_age[constants.SCOTT])
183         print "  {:<50}: {:>14}".format("Scott's first year social security benefit",
184                                         self.initial_social_security_dollars[constants.SCOTT])
185         print "  Federal tax brackets ["
186         self.federal_ordinary_income_tax_brackets.dump()
187         print "  ]"
188
189         print "  Federal dividend and long term gain brackets ["
190         self.federal_dividends_and_long_term_gains_brackets.dump()
191         print "  ]"
192         print "  We assume Roth money continues to be available tax-free."
193
194 class mutable_dynamic_historical_parameters(mutable_default_parameters):
195     def __init__(self):
196         self.selection_index = None
197         self.selection_duration = 0
198         self.historical_tuples = [
199            # year  stock  infl  bond
200             (2020,  3.14,  1.90,  1.54),
201             (2019, 31.49,  1.70,  2.15),
202             (2018, -4.38,  2.40,  2.33),
203             (2017, 21.83,  2.10,  1.19),
204             (2016, 11.96,  1.30,  0.61),
205             (2015,  1.38,  0.10,  0.32),
206             (2014, 13.69,  1.60,  0.12),
207             (2013, 32.39,  1.50,  0.13),
208             (2012, 16.00,  2.10,  0.17),
209             (2011,  2.11,  3.20,  0.18),
210             (2010, 15.06,  1.60,  0.32),
211             (2009, 26.46, -0.40,  0.47),
212             (2008,-37.00,  3.80,  1.83),
213             (2007,  5.49,  2.80,  4.53),
214             (2006, 15.79,  3.20,  4.94),
215             (2005,  4.91,  3.40,  3.62),
216             (2004, 10.88,  2.70,  1.89),
217             (2003, 28.68,  2.30,  1.24),
218             (2002,-22.10,  1.60,  2.00),
219             (2001,-11.89,  2.80,  3.49),
220             (2000, -9.10,  3.40,  6.11),
221             (1999, 21.04,  2.20,  5.08),
222             (1998, 28.58,  1.50,  5.05),
223             (1997, 33.36,  2.30,  5.63),
224             (1996, 22.96,  3.00,  5.52),
225             (1995, 37.58,  2.80,  5.94),
226             (1994,  1.32,  2.60,  5.32),
227             (1993, 10.08,  3.00,  3.43),
228             (1992,  7.62,  3.00,  3.89),
229             (1991, 30.47,  4.20,  5.86),
230             (1990, -3.10,  5.40,  7.89),
231             (1989, 31.69,  4.82,  8.54),
232             (1988, 16.61,  4.14,  7.65),
233             (1987, 15.25,  3.65,  6.77),
234             (1986, 18.67,  0.86,  6.45),
235             (1985, 31.73,  3.56,  8.42),
236             (1984,  6.27,  4.32, 10.91),
237             (1983, 22.56,  3.21,  9.58),
238             (1982, 21.55,  6.16, 12.27),
239             (1981, -4.91, 10.32, 14.80),
240             (1980, 32.42, 13.50, 12.00),
241             (1979, 18.44, 11.35, 10.65),
242             (1978,  6.65,  7.59,  7.00),
243             (1977, -7.18,  6.50,  6.08),
244             (1976, 23.84,  5.76,  5.88),
245             (1975, 37.20,  9.13,  6.78),
246             (1974,-26.47, 11.04,  8.20),
247             (1973,-14.66,  6.22,  7.32),
248             (1972, 18.90,  3.21,  4.95),
249             (1971, 14.31,  4.38,  4.89),
250             (1970, 14.01,  5.72,  6.90),
251             (1969, -8.50,  5.46,  7.12),
252             (1968, 11.06,  4.19,  5.69),
253             (1967, 23.98,  3.09,  4.88),
254             (1966,-10.06,  2.86,  5.20),
255             (1965, 12.45,  1.61,  4.15),
256             (1964, 16.48,  1.31,  3.85),
257             (1963, 22.80,  1.32,  3.36),
258             (1962, -8.73,  1.00,  2.90),
259             (1961, 26.89,  1.01,  2.60),
260             (1960,  0.47,  1.72,  3.24),
261             (1959, 11.96,  0.69,  3.83),
262             (1958, 43.36,  2.85,  3.5),
263             (1957,-10.78,  3.31,  3.6),
264             (1956,  6.56,  1.49,  2.9),
265             (1955, 31.56, -0.37,  2.5),
266             (1954, 52.62,  0.75,  2.37),
267             (1953, -0.99,  0.75,  2.71),
268             (1952, 18.37,  1.92,  2.19),
269             (1951, 24.02,  7.88,  2.00),
270             (1950, 31.71,  1.26,  1.98),
271             (1949, 18.79, -1.24,  2.21),
272             (1948,  5.50,  8.07,  2.40),
273             (1947,  5.71, 14.36,  2.01),
274             (1946, -8.07,  8.33,  1.64),
275             (1945, 36.44,  2.27,  1.67),
276             (1944, 19.75,  1.73,  1.86),
277             (1943, 25.90,  6.13,  2.05),
278             (1942, 20.34, 10.88,  2.36),
279             (1941,-11.59,  5.00,  2.10),
280             (1940, -9.78,  0.72,  2.76),
281             (1939, -0.41, -1.42,  2.76),
282             (1938, 31.12, -2.08,  2.94),
283             (1937,-35.03,  3.60,  2.76),
284             (1936, 33.92,  1.46,  3.39),
285             (1935, 47.67,  2.24,  2.76),
286             (1934, -1.44,  3.08,  2.76),
287             (1933, 53.99, -5.11,  4.71),
288             (1932, -8.19, -9.87,  4.71),
289             (1931,-43.34, -8.90,  3.99),
290             (1930,-24.90, -2.34,  4.71),
291             (1929, -8.42,  0.00,  4.27) ]
292         mutable_default_parameters.__init__(self)
293
294     def report_year(self, year):
295         if self.selection_index is None:
296             self.selection_index = random.randint(0,
297                                                   len(self.historical_tuples) - 1)
298             self.selection_duration = 1
299
300         t = self.historical_tuples[self.selection_index]
301         sim_year = t[0]
302         stock_return = t[1]
303         inflation = t[2]
304         bond_return = t[3]
305         print
306         print "## REPLAY YEAR %d" % sim_year
307         inflation_multiplier = utils.convert_rate_to_multiplier(inflation)
308         self.set_average_inflation_multiplier(inflation_multiplier)
309         print "## INFLATION is %s (%f)" % (
310             utils.format_rate(inflation_multiplier),
311             inflation_multiplier)
312         ss_multiplier = inflation_multiplier
313         if ss_multiplier >= 1.0:
314             ss_multiplier -= 1.0
315             ss_multiplier *= 0.6666
316             ss_multiplier += 1.0
317         self.set_average_social_security_multiplier(ss_multiplier)
318         print "## SS is %s (%f)" % (
319             utils.format_rate(self.get_average_social_security_multiplier()),
320             ss_multiplier)
321
322         # Assumes 50/50 Stocks/Bonds portfolio
323         our_return = (stock_return * 0.5 +
324                       bond_return * 0.5)
325         self.set_average_investment_return_multiplier(utils.convert_rate_to_multiplier(our_return))
326         print "## 50/50 INVESTMENTS RETURN is %s" % utils.format_rate(self.get_average_investment_return_multiplier())
327         self.selection_duration += 1
328         self.selection_index -= 1
329         if self.selection_index < 0 or random.randint(1, 100) < 20:
330             self.selection_index = None
331             self.selection_duration = None
332