def test_toml_input_retiree_info(self): toml_file_name = 't.toml' tf = working_toml_file(toml_file_name) taxinfo = tif.taxinfo() S = tomldata.Data(taxinfo) S.load_toml_file(toml_file_name) S.process_toml_info()
def test_toml_input_load_rmd_needed(self): toml_file_name = 't.toml' tf = working_toml_file(toml_file_name) taxinfo = tif.taxinfo() S = tomldata.Data(taxinfo) #S = tomldata.Data() S.load_toml_file(toml_file_name) S.process_toml_info() # toml has age 56, retire 58, through 72 primary so ageAtStart 58 (retire age) retiree1 = 'will' retiree2 = 'spouse' # toml has age 54, retire 60, through 75 secondary so ageAtStart 56 retireeNot = 'joe' rmd = S.rmd_needed(69 - 58, retiree1) self.assertEqual( rmd, 0, msg='At age 69 there should be no RMD (i.e., zero)') rmd = S.rmd_needed(70 - 58, retiree1) self.assertGreater( rmd, 0, msg='At age 70, RMD should be the IRS life expectancy') rmd = S.rmd_needed(69 - 56, retiree2) self.assertEqual( rmd, 0, msg='At age 69 there should be no RMD (i.e., zero)') rmd = S.rmd_needed(70 - 56, retiree2) self.assertGreater( rmd, 0, msg='At age 70, RMD should be the IRS life expectancy') rmd = S.rmd_needed(69 - 56, retireeNot) self.assertEqual( rmd, 0, msg='Non-valid Retiree should alway return rmd 0')
def lp_constraint_model_load_default_toml(self): toml_file_name = 'self_temp_toml.toml' tf = working_toml_file(toml_file_name) self.taxinfo = tif.taxinfo() S = tomldata.Data(self.taxinfo) #S = tomldata.Data() S.load_toml_file(toml_file_name) S.process_toml_info() return S
def test_toml_input_load_load_toml_file_to_match_crator_string(self): toml_file_name = 't.toml' tf = working_toml_file(toml_file_name) taxinfo = tif.taxinfo() S = tomldata.Data(taxinfo) S.load_toml_file(toml_file_name) s1 = tf.tomls.lstrip().rstrip() s2 = toml.dumps(S.toml_dict).rstrip() self.assertEqual(s1, s2)
def test_toml_input_load_update_dictionary(self): toml_file_name = 't.toml' skipfilewrite = True tf = working_toml_file(toml_file_name, skipfilewrite) dict =tf.toml_dict() dict['retirement_type'] = 'single' tf.toml_dict(dict) # update tf.tomls tf.write_working_toml_file(tf.tomls) taxinfo = tif.taxinfo() S = tomldata.Data(taxinfo) S.load_toml_file(toml_file_name) S.process_toml_info() self.assertEqual(S.retirement_type, 'single', msg='Explicitly setting retirement_type to single so it should match')
def test_toml_input_load_missing_toml_file_will_fail(self): toml_file_name = 't.toml' # Since the next two lines stops the toml file from # being written in the background it must be explicitly # written or will not exist as in this test. skipfilewrite = True tf = working_toml_file(toml_file_name, skipfilewrite) taxinfo = tif.taxinfo() S = tomldata.Data(taxinfo) expect_exception = FileNotFoundError(2,'No such file or directory: \'t.toml\'') with self.assertRaises(OSError) as cm: S.load_toml_file(toml_file_name) #This should fail self.assertEqual(str(cm.exception), str(expect_exception))
def test_toml_input_account_owner_age(self): toml_file_name = 't.toml' tf = working_toml_file(toml_file_name) taxinfo = tif.taxinfo() S = tomldata.Data(taxinfo) #S = tomldata.Data() S.load_toml_file(toml_file_name) S.process_toml_info() for account in S.accounttable: if account['acctype'] != 'aftertax': year = 59 - 58 # age - start of plan age for will if account['mykey'] != 'will': year = 59 - 56 # age - start of plan age for spouse a = S.account_owner_age(year, account) self.assertEqual( a, 59, msg='At age 59 account_owner_age() should be 59')
def test_toml_input_do_ss_details(self): toml_file_name = 't.toml' tf = working_toml_file(toml_file_name) taxinfo = tif.taxinfo() S = tomldata.Data(taxinfo) S.load_toml_file(toml_file_name) S.process_toml_info() # [iam.will] primary = true, age = 56, retire = 58, through = 72 # [SocialSecurity.will] amount = 31000 FRA = 67 age = "68-" # [iam.spouse] primary = false, age = 54, retire = 60, through = 75 # [SocialSecurity.spouse] amount = 21000 FRA = 67 age = "70-" # => let's check the SS when spouse is 70 and will is 72 ->SS[72-58] willcontrib = (31000 * (1.08**(68 - 67))) * 1.025**(72 - 56) spousecontrib = (21000 * (1.08**(70 - 67))) * 1.025**(70 - 54) expect = willcontrib + spousecontrib self.assertEqual(S.SS[72 - 58], expect)
def test_toml_input_match_retiree(self): toml_file_name = 't.toml' tf = working_toml_file(toml_file_name) taxinfo = tif.taxinfo() S = tomldata.Data(taxinfo) #S = tomldata.Data() S.load_toml_file(toml_file_name) S.process_toml_info() retiree1 = 'will' retiree2 = 'spouse' retireeNot = 'joe' v = S.match_retiree(retiree1) self.assertEqual(v['mykey'], retiree1) v = S.match_retiree(retiree2) self.assertEqual(v['mykey'], retiree2) v = S.match_retiree(retireeNot) self.assertEqual(v, None)
def test_toml_input_start_amount(self): S = tomldata.Data(None) amount = 12000 fra = 67 start = 62 a = S.startamount(amount, fra, start) b = amount / (1.067**(fra - start)) self.assertEqual(a, b) start = 70 a = S.startamount(amount, fra, start) b = amount * (1.08**(start - fra)) self.assertEqual(a, b) start = fra a = S.startamount(amount, fra, start) self.assertEqual(a, amount) start = 61 # test 61 and should fail too young a = S.startamount(amount, fra, start) # will not go to 61, 62 is lower bound b = amount / (1.067**(fra - 62)) self.assertEqual(a, b)
def test_toml_input_do_details(self): toml_file_name = 't.toml' tf = working_toml_file(toml_file_name) taxinfo = tif.taxinfo() S = tomldata.Data(taxinfo) S.load_toml_file(toml_file_name) S.process_toml_info() #[income.mytaxfree] amount = 3000 age = "56-" inflation = false tax = false #[income.rental_1] amount = 36000 age = "67-" inflation = true tax = true #[income.rental_2] amount = 2400 age = "67-" inflation = true tax = true # => let's check the INC when will is 65->INC[65-58] and 68->INC[68-58] self.assertEqual( S.income[65 - 58], 3300, msg='No Inflation so should equal configured amount') self.assertEqual( S.taxed[65 - 58], 300, msg='income.mytaxfree is not taxed but stopgap is so 300') expect = 3300 + (36000 + 2400) * 1.025**(68 - 56) taxexpect = expect - 3000 self.assertEqual( S.income[68 - 58], expect, msg='Sum of mytaxfree and inflation adjusted rental_1 and rental_2') self.assertEqual( S.taxed[68 - 58], taxexpect, msg='Same as income minus mytaxfee')
def test_toml_input_account_info(self): toml_file_name = 't.toml' tf = working_toml_file(toml_file_name) taxinfo = tif.taxinfo() S = tomldata.Data(taxinfo) #S = tomldata.Data() S.load_toml_file(toml_file_name) S.process_toml_info() # tmol Accounts # IRA.will bal=2,000,000 contrib=100 inflation=true period=56-65 {inflation applies to contrib} # IRA.spouse bal=200,000 contrib=0 inflation=false period= # Roth.spouse bal=100,000 contrib=0 inflation=false period= # aftertax bal=700,000 contrib=10 inflation=false period=56-65 # Plan period 58-73, age will 56 age spouse 54 for acc in S.accounttable: origbal = acc['origbal'] bal = acc['bal'] mykey = acc['mykey'] acctype = acc['acctype'] if (acctype == 'IRA' or acctype == 'roth') and mykey == 'spouse': self.assertEqual(bal, origbal * S.r_rate**(58 - 56), msg='Rate of return till plan start, no contributions') elif mykey == 'will' or mykey == 'nokey': calcb = origbal * S.r_rate**(58 - 56) contrib = acc['contrib'] p = contrib * (S.r_rate**(58 - 56) - 1) * \ (1 + (1 / (S.r_rate - 1))) pwicontrib = (contrib * (1 - (S.r_rate * S.i_rate) ** (60 - 58)) / (1 - S.r_rate * S.i_rate)) * (S.r_rate) age60contrib = acc['contributions'][60 - 58] if acc['inflation'] == False: self.assertEqual( bal, calcb + p, msg='Rate of return and contributions till plan start') self.assertEqual( contrib, age60contrib, msg='No inflation so all contrib values should match') else: self.assertEqual(round(bal, 0), round( calcb + pwicontrib, 0), msg='Rate of return and inflating contributions till plan start') self.assertEqual(contrib * S.i_rate**(60 - 56), age60contrib, msg='Inflation so contrib values should increase each year with inflation')
def test_input_through_solver_joint_first_year_spinding(self): toml_file_name = 't.toml' skipfilewrite = True tf = working_toml_file(toml_file_name, skipfilewrite) dict =tf.toml_dict() dict['retirement_type'] = 'joint' tf.toml_dict(dict) # update tf.tomls tf.write_working_toml_file(tf.tomls) taxinfo = tif.taxinfo() S = tomldata.Data(taxinfo) S.load_toml_file(toml_file_name) S.process_toml_info() years = S.numyr taxbins = len(taxinfo.taxtable) cgbins = len(taxinfo.capgainstable) accounts = len(S.accounttable) verbose = False disallowdeposits = False vindx = v.vector_var_index(years, taxbins, cgbins, accounts, S.accmap) lp = lpclass.lp_constraint_model(S, vindx, taxinfo.taxtable, taxinfo.capgainstable, taxinfo.penalty, taxinfo.stded, taxinfo.SS_taxable, verbose, disallowdeposits) c, A, b = lp.build_model() res = scipy.optimize.linprog(c, A_ub=A, b_ub=b, options={"disp": verbose, #"bland": True, "tol": 1.0e-7, "maxiter": 3000}) self.assertTrue(res.success, msg='res.success indicates solver failed') # If we get this far test the output year = 0 verifiedSolverResult = 220896.0970 latestSolverResult = res.x[vindx.s(year)] self.assertEqual(round(latestSolverResult,3), round(verifiedSolverResult,3), msg='Verified solver result is ${:0_.3f} but here we got ${:0_.3f}'.format(verifiedSolverResult, latestSolverResult))
def test_toml_input_check_record(self): taxinfo = tif.taxinfo() S = tomldata.Data(taxinfo) #S = tomldata.Data() orig = {'aftertax': {'bal': 700000, 'basis': 400000, 'contrib': 10, 'period': '56-65'}} to = {'aftertax': {'nokey': {'bal': 700000, 'basis': 400000, 'contrib': 10, 'period': '56-65'}}} d = json.loads(json.dumps(orig)) # thread safe deep copy S.check_record(d, 'aftertax', ('bal', 'rate', 'contrib', 'inflation', 'period', 'basis')) # Do a deep compare: self.assertEqual(pickle.dumps(to), pickle.dumps( d), msg='dictionary should be transformed') orig = {} to = {} d = json.loads(json.dumps(orig)) # thread safe deep copy S.check_record(d, 'aftertax', ('bal', 'rate', 'contrib', 'inflation', 'period', 'basis')) # Do a deep compare: self.assertEqual(pickle.dumps(to), pickle.dumps( d), msg='{} dictionary should remain {}')
def test_toml_input_maxcontribution(self): toml_file_name = 't.toml' tf = working_toml_file(toml_file_name) taxinfo = tif.taxinfo() S = tomldata.Data(taxinfo) #S = tomldata.Data() S.load_toml_file(toml_file_name) S.process_toml_info() # toml has age 56, retire 58, through 72 primary so ageAtStart 58 (retire age) retiree1 = 'will' retiree2 = 'spouse' # toml has age 54, retire 60, through 75 secondary so ageAtStart 56 retireeNot = 'joe' retireeNone = None year = 1 m = S.maxContribution(year, retiree1) self.assertEqual(m, (taxinfo.contribspecs['TDRA'] + taxinfo.contribspecs['TDRACatchup']) * S.i_rate**year, msg='TDRA+RothRA contribution plus catchup') m = S.maxContribution(year, retireeNot) self.assertEqual(m, 0, msg='zero if non-existant retiree') m = S.maxContribution(year, retireeNone) self.assertEqual(m, 2 * (taxinfo.contribspecs['TDRA'] + taxinfo.contribspecs['TDRACatchup']) * S.i_rate**year, msg='TDRA+RothRA contribution plus catchup for both Retirees')
def test_toml_input_apply_early_penalty(self): toml_file_name = 't.toml' tf = working_toml_file(toml_file_name) taxinfo = tif.taxinfo() S = tomldata.Data(taxinfo) #S = tomldata.Data() S.load_toml_file(toml_file_name) S.process_toml_info() # toml has age 56, retire 58, through 72 primary so ageAtStart 58 (retire age) retiree1 = 'will' retiree2 = 'spouse' # toml has age 54, retire 60, through 75 secondary so ageAtStart 56 retireeNot = 'joe' p = S.apply_early_penalty(59 - 58, retiree1) self.assertTrue( p, msg='At age 59 an early penalty is require, unless...') p = S.apply_early_penalty(60 - 58, retiree1) self.assertFalse(p, msg='At age 60 no early penalty is require') p = S.apply_early_penalty(59 - 56, retiree2) self.assertTrue( p, msg='At age 59 an early penalty is require, unless...') p = S.apply_early_penalty(60 - 56, retiree2) self.assertFalse(p, msg='At age 60 no early penalty is require') p = S.apply_early_penalty(59 - 56, retireeNot) self.assertFalse(p, msg='A non-existant retiree should return false')
help='Require configuration input toml file') args = parser.parse_args() if args.alltables: args.verboseaccounttrans = True args.verboseincome = True args.verbosetax = True args.verbosetaxbrackets = True csv_file_name = None if args.csv != '': csv_file_name = args.csv ao = app_out.app_output(csv_file_name) taxinfo = tif.taxinfo() S = tomldata.Data(taxinfo) S.load_toml_file(args.conffile) S.process_toml_info() #print("\naccounttable: ", S.accounttable) if S.accmap['IRA'] + S.accmap['roth'] + S.accmap['aftertax'] == 0: print( 'Error: This app optimizes the withdrawals from your retirement account(s); you must have at least one specified in the input toml file.' ) exit(0) if args.verbosewga: print("accounttable: ", S.accounttable) non_binding_only = True