import constants import utils from tax_brackets import tax_brackets from money import money from numpy import random class parameters(object): def get_initial_annual_expenses(self): pass def get_average_inflation_multiplier(self): pass def get_average_social_security_multiplier(self): pass def get_average_investment_return_multiplier(self): pass def get_initial_social_security_age(self, person): pass def get_initial_social_security_benefit(self, person): pass def get_federal_standard_deduction(self): pass def get_federal_ordinary_income_tax_brackets(self): pass def get_federal_dividends_and_long_term_gains_income_tax_brackets(self): pass def dump(self): pass def report_year(self, year): pass class mutable_default_parameters(parameters): """A container to hold the initial states of several simulation parameters. Play with them as you see fit and see what happens.""" def __init__(self): self.with_default_values() def with_default_values(self): # Annual expenses in USD at the start of the simulation. This # will be adjusted upwards with inflation_multiplier every year. self.initial_annual_expenses = money(144300) # The average US inflation rate during the simulation. The # Fed's target rate is 2.0% as of 2020. The long term observed # average historical inflation rate in the US is 2.1%. # # Note this is a multiplier... so 1.0 would be no inflation, # 1.21 is 2.1% inflation, 0.9 is deflation, etc... self.inflation_multiplier = 1.03 # We want to be able to model social security payments not # keeping pace with inflation. Like the inflation_multiplier # above, this is a multiplier. It affect the magnitide of # social security payments year over year. self.social_security_multiplier = 1.02 # This is the average investment return rate. Asset allocation has # a large effect on this as does the overall economy. That said, # the US stock market has returned 10%/year over a large enough # time window. Our investments at Fidelity have returned 6.91% # in the lifetime they have been there. The expected return of a # 50/50 stock/bond investment mix based on historical data is 8.3%. self.investment_multiplier = 1.04 # The age at which each person will take social security # benefits in this simulation and how much she will get the # first year. Note, this benefit size increases year over # year at social_security_multiplier. This is what the social # security website estimates if we both earn $0 from 2020 on: # # Lynn's benefits: Scott's benefits: # age 62 - $21,120 age 62 - $21,420 # age 67 - $30,000 age 67 - $30,420 # age 70 - $37,200 age 70 - $37,728 # # X SCOTT LYNN self.social_security_age = [0, 62, 62 ] self.initial_social_security_dollars = [0, money(21000), money(21000) ] # Tax details... the standard deduction amount and tax # brackets for ordinary income and long term capital gains. self.federal_standard_deduction_dollars = 24800 self.federal_ordinary_income_tax_brackets = tax_brackets( constants.PESSIMISTIC_FEDERAL_INCOME_TAX_BRACKETS) self.federal_dividends_and_long_term_gains_brackets = tax_brackets( constants.CURRENT_LONG_TERM_GAIN_FEDERAL_TAX_BRACKETS) return self def set_initial_annual_expenses(self, expenses): assert expenses >= 0, "You can't have negative expenses" self.initial_annual_expenses = expenses return self def get_initial_annual_expenses(self): return self.initial_annual_expenses def set_average_inflation_multiplier(self, multiplier): self.inflation_multiplier = multiplier return self def get_average_inflation_multiplier(self): return self.inflation_multiplier def set_average_social_security_multiplier(self, multiplier): self.social_security_multiplier = multiplier return self def get_average_social_security_multiplier(self): return self.social_security_multiplier def set_average_investment_return_multiplier(self, multiplier): self.investment_multiplier = multiplier return self def get_average_investment_return_multiplier(self): return self.investment_multiplier def set_initial_social_security_age_and_benefits(self, person, age, amount): assert age >= 60 and age <= 70, "age should be between 60 and 70" self.social_security_age[person] = age assert amount >= 0, "Social security won't pay negative dollars" self.initial_social_security_dollars[person] = amount return self def get_initial_social_security_age(self, person): return self.social_security_age[person] def get_initial_social_security_benefit(self, person): return self.initial_social_security_dollars[person] def set_federal_standard_deduction(self, deduction): assert deduction >= 0, "Standard deduction should be non-negative" self.federal_standard_deduction_dollars = deduction return self def get_federal_standard_deduction(self): return self.federal_standard_deduction_dollars def set_federal_ordinary_income_tax_brackets(self, brackets): self.federal_ordinary_income_tax_brackets = brackets return self def get_federal_ordinary_income_tax_brackets(self): return self.federal_ordinary_income_tax_brackets def set_federal_dividends_and_long_term_gains_income_tax_brackets(self, brackets): self.federal_dividends_and_long_term_gains_brackets = brackets; return self def get_federal_dividends_and_long_term_gains_income_tax_brackets(self): return self.federal_dividends_and_long_term_gains_brackets def dump(self): print "SIMULATION PARAMETERS" print " {:<50}: {:>14}".format("Initial year annual expenses", self.initial_annual_expenses) print " {:<50}: {:>14}".format("Annual inflation rate", utils.format_rate(self.inflation_multiplier)) print " {:<50}: {:>14}".format("Average annual investment return rate", utils.format_rate(self.investment_multiplier)) print " {:<50}: {:>14}".format("Annual social security benefit increase rate", utils.format_rate(self.social_security_multiplier)) print " {:<50}: {:>14}".format("Age at which Lynn takes social security", self.social_security_age[constants.LYNN]) print " {:<50}: {:>14}".format("Lynn's first year social security benefit", self.initial_social_security_dollars[constants.LYNN]) print " {:<50}: {:>14}".format("Age at which Scott takes social security", self.social_security_age[constants.SCOTT]) print " {:<50}: {:>14}".format("Scott's first year social security benefit", self.initial_social_security_dollars[constants.SCOTT]) print " Federal tax brackets [" self.federal_ordinary_income_tax_brackets.dump() print " ]" print " Federal dividend and long term gain brackets [" self.federal_dividends_and_long_term_gains_brackets.dump() print " ]" print " We assume Roth money continues to be available tax-free." class mutable_dynamic_historical_parameters(mutable_default_parameters): def __init__(self): self.selection_index = None self.selection_duration = 0 self.historical_tuples = [ # year stock infl bond (2020, 3.14, 1.90, 1.54), (2019, 31.49, 1.70, 2.15), (2018, -4.38, 2.40, 2.33), (2017, 21.83, 2.10, 1.19), (2016, 11.96, 1.30, 0.61), (2015, 1.38, 0.10, 0.32), (2014, 13.69, 1.60, 0.12), (2013, 32.39, 1.50, 0.13), (2012, 16.00, 2.10, 0.17), (2011, 2.11, 3.20, 0.18), (2010, 15.06, 1.60, 0.32), (2009, 26.46, -0.40, 0.47), (2008,-37.00, 3.80, 1.83), (2007, 5.49, 2.80, 4.53), (2006, 15.79, 3.20, 4.94), (2005, 4.91, 3.40, 3.62), (2004, 10.88, 2.70, 1.89), (2003, 28.68, 2.30, 1.24), (2002,-22.10, 1.60, 2.00), (2001,-11.89, 2.80, 3.49), (2000, -9.10, 3.40, 6.11), (1999, 21.04, 2.20, 5.08), (1998, 28.58, 1.50, 5.05), (1997, 33.36, 2.30, 5.63), (1996, 22.96, 3.00, 5.52), (1995, 37.58, 2.80, 5.94), (1994, 1.32, 2.60, 5.32), (1993, 10.08, 3.00, 3.43), (1992, 7.62, 3.00, 3.89), (1991, 30.47, 4.20, 5.86), (1990, -3.10, 5.40, 7.89), (1989, 31.69, 4.82, 8.54), (1988, 16.61, 4.14, 7.65), (1987, 15.25, 3.65, 6.77), (1986, 18.67, 0.86, 6.45), (1985, 31.73, 3.56, 8.42), (1984, 6.27, 4.32, 10.91), (1983, 22.56, 3.21, 9.58), (1982, 21.55, 6.16, 12.27), (1981, -4.91, 10.32, 14.80), (1980, 32.42, 13.50, 12.00), (1979, 18.44, 11.35, 10.65), (1978, 6.65, 7.59, 7.00), (1977, -7.18, 6.50, 6.08), (1976, 23.84, 5.76, 5.88), (1975, 37.20, 9.13, 6.78), (1974,-26.47, 11.04, 8.20), (1973,-14.66, 6.22, 7.32), (1972, 18.90, 3.21, 4.95), (1971, 14.31, 4.38, 4.89), (1970, 14.01, 5.72, 6.90), (1969, -8.50, 5.46, 7.12), (1968, 11.06, 4.19, 5.69), (1967, 23.98, 3.09, 4.88), (1966,-10.06, 2.86, 5.20), (1965, 12.45, 1.61, 4.15), (1964, 16.48, 1.31, 3.85), (1963, 22.80, 1.32, 3.36), (1962, -8.73, 1.00, 2.90), (1961, 26.89, 1.01, 2.60), (1960, 0.47, 1.72, 3.24), (1959, 11.96, 0.69, 3.83), (1958, 43.36, 2.85, 3.5), (1957,-10.78, 3.31, 3.6), (1956, 6.56, 1.49, 2.9), (1955, 31.56, -0.37, 2.5), (1954, 52.62, 0.75, 2.37), (1953, -0.99, 0.75, 2.71), (1952, 18.37, 1.92, 2.19), (1951, 24.02, 7.88, 2.00), (1950, 31.71, 1.26, 1.98), (1949, 18.79, -1.24, 2.21), (1948, 5.50, 8.07, 2.40), (1947, 5.71, 14.36, 2.01), (1946, -8.07, 8.33, 1.64), (1945, 36.44, 2.27, 1.67), (1944, 19.75, 1.73, 1.86), (1943, 25.90, 6.13, 2.05), (1942, 20.34, 10.88, 2.36), (1941,-11.59, 5.00, 2.10), (1940, -9.78, 0.72, 2.76), (1939, -0.41, -1.42, 2.76), (1938, 31.12, -2.08, 2.94), (1937,-35.03, 3.60, 2.76), (1936, 33.92, 1.46, 3.39), (1935, 47.67, 2.24, 2.76), (1934, -1.44, 3.08, 2.76), (1933, 53.99, -5.11, 4.71), (1932, -8.19, -9.87, 4.71), (1931,-43.34, -8.90, 3.99), (1930,-24.90, -2.34, 4.71), (1929, -8.42, 0.00, 4.27) ] mutable_default_parameters.__init__(self) def report_year(self, year): if self.selection_index is None: self.selection_index = random.randint(0, len(self.historical_tuples) - 1) self.selection_duration = 1 t = self.historical_tuples[self.selection_index] sim_year = t[0] stock_return = t[1] inflation = t[2] bond_return = t[3] print print "## REPLAY YEAR %d" % sim_year inflation_multiplier = utils.convert_rate_to_multiplier(inflation) self.set_average_inflation_multiplier(inflation_multiplier) print "## INFLATION is %s (%f)" % ( utils.format_rate(inflation_multiplier), inflation_multiplier) ss_multiplier = inflation_multiplier if ss_multiplier >= 1.0: ss_multiplier -= 1.0 ss_multiplier *= 0.6666 ss_multiplier += 1.0 self.set_average_social_security_multiplier(ss_multiplier) print "## SS is %s (%f)" % ( utils.format_rate(self.get_average_social_security_multiplier()), ss_multiplier) # Assumes 50/50 Stocks/Bonds portfolio our_return = (stock_return * 0.5 + bond_return * 0.5) self.set_average_investment_return_multiplier(utils.convert_rate_to_multiplier(our_return)) print "## 50/50 INVESTMENTS RETURN is %s" % utils.format_rate(self.get_average_investment_return_multiplier()) self.selection_duration += 1 self.selection_index -= 1 if self.selection_index < 0 or random.randint(1, 100) < 20: self.selection_index = None self.selection_duration = None