From 654fb1e256eb2eb24669039ea582bb1ebff4f104 Mon Sep 17 00:00:00 2001 From: Scott Gasch Date: Wed, 15 Jan 2020 15:10:41 -0800 Subject: [PATCH] Wires in the money class. --- accounts.py | 74 ++++++++++++++++++++++++------------------------ constants.py | 35 ++++++++++++----------- money.py | 46 +++++++++++++++--------------- parameters.py | 15 +++++----- retire.py | 40 +++++++++++++------------- tax_brackets.py | 6 ++-- tax_collector.py | 45 +++++++++++++++-------------- utils.py | 4 --- 8 files changed, 133 insertions(+), 132 deletions(-) diff --git a/accounts.py b/accounts.py index 5f0def0..0e53e04 100644 --- a/accounts.py +++ b/accounts.py @@ -1,5 +1,6 @@ import constants import utils +from money import * class account(object): """This defines an account base class which inherits from object, the @@ -76,12 +77,12 @@ class age_restricted_tax_deferred_account(account): tries to estimate taxes.""" assert total_balance >= 0, "Initial balance must be >= 0" assert roth_amount_subset <= total_balance, "Roth subset too high!" - self.roth = roth_amount_subset - self.pretax = total_balance - roth_amount_subset - self.total_roth_withdrawals = 0 - self.total_pretax_withdrawals = 0 - self.total_investment_gains = 0 - self.total_roth_conversions = 0 + self.roth = money(roth_amount_subset) + self.pretax = total_balance - money(roth_amount_subset) + self.total_roth_withdrawals = money(0) + self.total_pretax_withdrawals = money(0) + self.total_investment_gains = money(0) + self.total_roth_conversions = money(0) self.initial_balance = self.get_balance() assert self.initial_balance >= 0, "Bad initial balance" @@ -102,18 +103,20 @@ class age_restricted_tax_deferred_account(account): balance = self.get_balance() if balance < amount: raise Exception("Insufficient funds") - ratio = float(self.roth) / float(balance) + ratio = self.roth / balance roth_part = amount * ratio pretax_part = amount - roth_part if roth_part > 0: self.roth -= roth_part self.total_roth_withdrawals += roth_part - print "## Satisfying %s from %s with Roth money." % (utils.format_money(roth_part), self.name) + print "## Satisfying %s from %s with Roth money." % (roth_part, + self.name) taxes.record_roth_income(roth_part) if pretax_part > 0: self.pretax -= pretax_part self.total_pretax_withdrawals += pretax_part - print "## Satisfying %s from %s with pre-tax money." % (utils.format_money(pretax_part), self.name) + print "## Satisfying %s from %s with pre-tax money." % (pretax_part, + self.name) taxes.record_ordinary_income(pretax_part) def deposit(self, amount): @@ -253,24 +256,23 @@ class age_restricted_tax_deferred_account(account): self.total_roth_conversions += amount taxes.record_ordinary_income(amount) print "## Executed pre-tax --> Roth conversion of %s in %s" % ( - utils.format_money(amount), - self.get_name()) + amount, self.get_name()) return amount def dump_final_report(self): super(age_restricted_tax_deferred_account, self).dump_final_report() print " {:<50}: {:>14}".format("Initial balance", - utils.format_money(self.initial_balance)) + self.initial_balance) print " {:<50}: {:>14}".format("Ending balance", - utils.format_money(self.get_balance())) + self.get_balance()) print " {:<50}: {:>14}".format("Total investment gains", - utils.format_money(self.total_investment_gains)) + self.total_investment_gains) print " {:<50}: {:>14}".format("Total Roth withdrawals", - utils.format_money(self.total_roth_withdrawals)) + self.total_roth_withdrawals) print " {:<50}: {:>14}".format("Total pre-tax withdrawals", - utils.format_money(self.total_pretax_withdrawals)) + self.total_pretax_withdrawals) print " {:<50}: {:>14}".format("Total pre-tax converted to Roth", - utils.format_money(self.total_roth_conversions)) + self.total_roth_conversions) class age_restricted_roth_account(age_restricted_tax_deferred_account): """This is an object to represent a Roth account like a Roth IRA. All @@ -313,13 +315,13 @@ class brokerage_account(account): are taxed at 15% and as ordinary income, respectively.""" assert total_balance >= 0, "Initial balance must be >= 0" assert cost_basis <= total_balance, "Bad initial cost basis" - self.cost_basis = float(cost_basis) - self.short_term_gain = 0.0 - self.long_term_gain = float(total_balance - cost_basis) - self.total_cost_basis_withdrawals = 0 - self.total_long_term_gain_withdrawals = 0 - self.total_short_term_gain_withdrawals = 0 - self.total_investment_gains = 0 + self.cost_basis = money(cost_basis) + self.short_term_gain = money(0) + self.long_term_gain = money(total_balance - cost_basis) + self.total_cost_basis_withdrawals = money(0) + self.total_long_term_gain_withdrawals = money(0) + self.total_short_term_gain_withdrawals = money(0) + self.total_investment_gains = money(0) self.initial_balance = self.get_balance() # Call the base class' c'tor as well to set up the name. @@ -343,7 +345,8 @@ class brokerage_account(account): if self.cost_basis >= invested_capital_part: self.cost_basis -= invested_capital_part self.total_cost_basis_withdrawals += invested_capital_part - print "## Satisfying %s from %s as cost basis." % (utils.format_money(invested_capital_part), self.name) + print "## Satisfying %s from %s as cost basis." % (invested_capital_part, + self.name) self.withdraw_from_gains(gains_part, taxes) else: self.withdraw_from_gains(amount, taxes) @@ -359,15 +362,13 @@ class brokerage_account(account): self.long_term_gain -= amount self.total_long_term_gain_withdrawals += amount print "## Satisfying %s from %s as long term gains." % ( - utils.format_money(amount), - self.name) + amount, self.name) taxes.record_dividend_or_long_term_gain(amount) return else: print "## Satisfying %s from %s as long term gains (exhausting all current long term gains in account)." % ( - utils.format_money(self.long_term_gain), - self.name) + self.long_term_gain, self.name) amount -= self.long_term_gain self.total_long_term_gain_withdrawals += self.long_term_gain taxes.record_dividend_or_long_term_gain(self.long_term_gain) @@ -377,8 +378,7 @@ class brokerage_account(account): self.short_term_gain -= amount self.total_short_term_gain_withdrawals += amount print "## Satisfying %s from %s as short term gains." % ( - utils.format_money(amount), - self.name) + amount, self.name) taxes.record_short_term_gain(amount) def deposit(self, amount): @@ -415,14 +415,14 @@ class brokerage_account(account): def dump_final_report(self): super(brokerage_account, self).dump_final_report() print " {:<50}: {:>14}".format("Initial balance", - utils.format_money(self.initial_balance)) + self.initial_balance) print " {:<50}: {:>14}".format("Ending balance", - utils.format_money(self.get_balance())) + self.get_balance()) print " {:<50}: {:>14}".format("Total investment gains", - utils.format_money(self.total_investment_gains)) + self.total_investment_gains) print " {:<50}: {:>14}".format("Total cost basis withdrawals", - utils.format_money(self.total_cost_basis_withdrawals)) + self.total_cost_basis_withdrawals) print " {:<50}: {:>14}".format("Total long term gain withdrawals", - utils.format_money(self.total_long_term_gain_withdrawals)) + self.total_long_term_gain_withdrawals) print " {:<50}: {:>14}".format("Total short term gain withdrawals", - utils.format_money(self.total_short_term_gain_withdrawals)) + self.total_short_term_gain_withdrawals) diff --git a/constants.py b/constants.py index af0399a..d31b041 100644 --- a/constants.py +++ b/constants.py @@ -1,3 +1,4 @@ +from money import money # Consts DEFAULT = 0 @@ -9,27 +10,27 @@ def is_valid_owner(owner): return (owner >= DEFAULT and owner <= LYNN) PESSIMISTIC_FEDERAL_INCOME_TAX_BRACKETS = [ - [ 612351, 0.50 ], - [ 408201, 0.45 ], - [ 321451, 0.35 ], - [ 168401, 0.25 ], - [ 78951, 0.22 ], - [ 19401, 0.15 ], - [ 1, 0.12 ] + [ money(612351), 0.50 ], + [ money(408201), 0.45 ], + [ money(321451), 0.35 ], + [ money(168401), 0.25 ], + [ money( 78951), 0.22 ], + [ money( 19401), 0.15 ], + [ money( 1), 0.12 ] ] CURRENT_FEDERAL_INCOME_TAX_BRACKETS = [ - [ 612351, 0.37 ], - [ 408201, 0.35 ], - [ 321451, 0.32 ], - [ 168401, 0.24 ], - [ 78951, 0.22 ], - [ 19401, 0.12 ], - [ 1, 0.10 ] + [ money(612351), 0.37 ], + [ money(408201), 0.35 ], + [ money(321451), 0.32 ], + [ money(168401), 0.24 ], + [ money( 78951), 0.22 ], + [ money( 19401), 0.12 ], + [ money( 1), 0.10 ] ] CURRENT_LONG_TERM_GAIN_FEDERAL_TAX_BRACKETS = [ - [ 488851, 0.20 ], - [ 78751, 0.15 ], - [ 1, 0.00 ] + [ money(488851), 0.20 ], + [ money( 78751), 0.15 ], + [ money( 1), 0.00 ] ] PESSIMISTIC_LONG_TERM_GAIN_FEDERAL_TAX_BRACKETS = ( PESSIMISTIC_FEDERAL_INCOME_TAX_BRACKETS) diff --git a/money.py b/money.py index 52fe32b..848d972 100644 --- a/money.py +++ b/money.py @@ -3,40 +3,41 @@ import decimal class money(object): def __init__(self, amount="0"): try: + if isinstance(amount, money): + amount = amount.amount self.amount = decimal.Decimal(amount) except decimal.InvalidOperation: raise ValueError("amount value could not be converted to " "Decimal(): '{}'".format(amount)) - @property def amount(self): return self.amount def __hash__(self): - return hash(self._amount) + return hash(self.amount) def __repr__(self): - return "{}".format(self._amount) + return "{}".format(self.amount) def __str__(self): return self.__unicode__().encode('utf-8') def __unicode__(self): - return u"${:,.2f}".format(self._amount) + return u"${:,.2f}".format(self.amount) def __lt__(self, other): if not isinstance(other, money): - raise InvalidOperandType(other, '<') + other = money(other) return self.amount < other.amount def __le__(self, other): if not isinstance(other, money): - raise InvalidOperandType(other, '<=') - return self._amount <= other.amount + other = money(other) + return self.amount <= other.amount def __eq__(self, other): if isinstance(other, money): - return self._amount == other.amount + return self.amount == other.amount return False def __ne__(self, other): @@ -44,17 +45,17 @@ class money(object): def __gt__(self, other): if not isinstance(other, money): - raise InvalidOperandType(other, '>') - return self._amount > other.amount + other = money(other) + return self.amount > other.amount def __ge__(self, other): if not isinstance(other, money): - raise InvalidOperandType(other, '>=') - return self._amount >= other.amount + other = money(other) + return self.amount >= other.amount def __add__(self, other): if not isinstance(other, money): - raise InvalidOperandType(other, '+') + other = money(other) other = other.amount amount = self.amount + other return self.__class__(amount) @@ -64,9 +65,9 @@ class money(object): def __sub__(self, other): if not isinstance(other, money): - raise InvalidOperandType(other, '-') + other = money(other) other = other.amount - amount = self._amount - other + amount = self.amount - other return self.__class__(amount) def __rsub__(self, other): @@ -74,9 +75,8 @@ class money(object): def __mul__(self, other): if isinstance(other, money): - raise TypeError("multiplication is unsupported between " - "two money objects") - amount = self._amount * other + other = other.amount() + amount = self.amount * decimal.Decimal(other) return self.__class__(amount) def __rmul__(self, other): @@ -93,14 +93,14 @@ class money(object): else: if other == 0: raise ZeroDivisionError() - amount = self.amount / other + amount = self.amount / decimal.Decimal(other) return self.__class__(amount) def __floordiv__(self, other): if isinstance(other, money): if other.amount == 0: raise ZeroDivisionError() - return self._amount // other.amount + return self.amount // other.amount else: if other == 0: raise ZeroDivisionError() @@ -120,11 +120,11 @@ class money(object): if isinstance(other, money): if other.amount == 0: raise ZeroDivisionError() - return divmod(self._amount, other.amount) + return divmod(self.amount, other.amount) else: if other == 0: raise ZeroDivisionError() - whole, remainder = divmod(self._amount, other) + whole, remainder = divmod(self.amount, other) return (self.__class__(whole), self.__class__(remainder)) @@ -139,7 +139,7 @@ class money(object): return self.__class__(-self.amount) def __pos__(self): - return self.__class__(+self._amount) + return self.__class__(+self.amount) def __abs__(self): return self.__class__(abs(self.amount)) diff --git a/parameters.py b/parameters.py index ae66e15..989d526 100644 --- a/parameters.py +++ b/parameters.py @@ -1,6 +1,7 @@ import constants import utils from tax_brackets import tax_brackets +from money import money class parameters(object): """A container to hold the initial states of several simulation @@ -12,7 +13,7 @@ class parameters(object): 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 = 144300 + 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 @@ -47,9 +48,9 @@ class parameters(object): # 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, 21000, 21000 ] + # 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. @@ -131,7 +132,7 @@ class parameters(object): def dump(self): print "SIMULATION PARAMETERS" print " {:<50}: {:>14}".format("Initial year annual expenses", - utils.format_money(self.initial_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", @@ -141,11 +142,11 @@ class parameters(object): 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", - utils.format_money(self.initial_social_security_dollars[constants.LYNN])) + 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", - utils.format_money(self.initial_social_security_dollars[constants.SCOTT])) + self.initial_social_security_dollars[constants.SCOTT]) print " Federal tax brackets [" self.federal_ordinary_income_tax_brackets.dump() print " ]" diff --git a/retire.py b/retire.py index 3282a9e..5f567aa 100755 --- a/retire.py +++ b/retire.py @@ -8,7 +8,8 @@ from parameters import parameters from tax_brackets import tax_brackets from tax_collector import tax_collector import utils -import secrets +import real_secrets +from money import money class simulation(object): def __init__(self, parameters, accounts): @@ -24,7 +25,7 @@ class simulation(object): """Determine if any account that has RMDs will require someone to take money out of it this year and, if so, how much. Then do the withdrawal.""" - total_withdrawn = 0 + total_withdrawn = money(0) for x in self.accounts: if x.has_rmd() and x.get_owner() == constants.SCOTT: rmd = x.do_rmd_withdrawal(self.scott_age, taxes) @@ -42,7 +43,8 @@ class simulation(object): for x in self.accounts: if not x.is_age_restricted(): amount_to_withdraw = min(amount_needed, x.get_balance()) - print "## Withdrawing %s from %s" % (utils.format_money(amount_to_withdraw), x.get_name()) + print "## Withdrawing %s from %s" % (amount_to_withdraw, + x.get_name()) x.withdraw(amount_to_withdraw, taxes) amount_needed -= amount_to_withdraw if amount_needed <= 0: return @@ -55,7 +57,8 @@ class simulation(object): (x.belongs_to_scott() and self.scott_age > 60))): amount_to_withdraw = min(amount_needed, x.get_balance()) - print "## Withdrawing %s from %s" % (utils.format_money(amount_to_withdraw), x.get_name()) + print "## Withdrawing %s from %s" % (amount_to_withdraw, + x.get_name()) x.withdraw(amount_to_withdraw, taxes) amount_needed -= amount_to_withdraw if amount_needed <= 0: return @@ -68,11 +71,12 @@ class simulation(object): (x.belongs_to_scott() and self.scott_age > 60))): amount_to_withdraw = min(amount_needed, x.get_balance()) - print "## Withdrawing %s from %s" % (utils.format_money(amount_to_withdraw), x.get_name()) + print "## Withdrawing %s from %s" % (amount_to_withdraw, + x.get_name()) x.withdraw(amount_to_withdraw, taxes) amount_needed -= amount_to_withdraw if amount_needed <= 0: return - raise Exception("Unable to find enough money this year, still need %s more!" % utils.format_money(amount_needed)) + raise Exception("Unable to find enough money this year, still need %s more!" % amount_needed) def get_social_security(self, scott_annual_social_security_dollars, @@ -81,7 +85,7 @@ class simulation(object): """Figure out if Scott and/or Lynn are taking social security at their present age in the simulation and, if so, how much their annual benefit should be.""" - total_benefit = 0 + total_benefit = money(0) if self.scott_age >= self.params.get_initial_social_security_age(constants.SCOTT): total_benefit += scott_annual_social_security_dollars taxes.record_ordinary_income(scott_annual_social_security_dollars) @@ -112,7 +116,7 @@ class simulation(object): def dump_annual_header(self, money_needed): print "\nYear: %d, estimated annual expenses %s ---------------\n" % ( self.year, - utils.format_money(money_needed)) + money_needed) print "Scott is %d, Lynn is %d and Alex is %d.\n" % ( self.scott_age, self.lynn_age, self.alex_age) @@ -120,9 +124,8 @@ class simulation(object): total = 0 for x in self.accounts: total += x.get_balance() - print "{:<50}: {:>14}".format(x.get_name(), - utils.format_money(x.get_balance())) - print "{:<50}: {:>14}\n".format("TOTAL", utils.format_money(total)) + print "{:<50}: {:>14}".format(x.get_name(), x.get_balance()) + print "{:<50}: {:>14}\n".format("TOTAL", total) def dump_final_report(self, taxes): print "\nAGGREGATE STATS FINAL REPORT:" @@ -148,7 +151,7 @@ class simulation(object): self.alex_age = self.year - 2005 # Computed money needed this year based on inflated budget. - money_needed = adjusted_annual_expenses + money_needed = money(adjusted_annual_expenses) # When Alex is in college, we need $50K more per year. if self.alex_age > 18 and self.alex_age <= 22: @@ -158,13 +161,13 @@ class simulation(object): # Now, figure out how to find money to pay for this year # and how much of it is taxable. - total_income = 0 + total_income = money(0) # When we reach a certain age we have to take RMDs from # some accounts. Handle that here. rmds = self.do_rmd_withdrawals(taxes) if rmds > 0: - print "## Satisfied %s of RMDs from age-restricted accounts." % utils.format_money(rmds) + print "## Satisfied %s of RMDs from age-restricted accounts." % rmds total_income += rmds money_needed -= rmds @@ -174,7 +177,7 @@ class simulation(object): adjusted_lynn_annual_social_security_dollars, taxes) if ss > 0: - print "## Social security paid %s" % utils.format_money(ss) + print "## Social security paid %s" % ss total_income += ss money_needed -= ss @@ -185,7 +188,7 @@ class simulation(object): money_needed = 0 look_for_conversions = True - tax_limit = 25000 + tax_limit = money(25000) while True: # Maybe do some opportunistic Roth conversions. taxes_due = taxes.approximate_taxes( @@ -214,8 +217,7 @@ class simulation(object): # we can ignore this. if taxes_due > 0: print "## Estimated federal tax due: %s (this year's tax rate=%s)" % ( - utils.format_money(taxes_due), - utils.format_rate(tax_rate)) + taxes_due, utils.format_rate(tax_rate)) self.go_find_money(taxes_due, taxes) else: print "## No federal taxes due this year!" @@ -246,6 +248,6 @@ class simulation(object): # main params = parameters().with_default_values() -accounts = secrets.accounts +accounts = real_secrets.accounts s = simulation(params, accounts) s.run() diff --git a/tax_brackets.py b/tax_brackets.py index c64cf2b..e0a4660 100644 --- a/tax_brackets.py +++ b/tax_brackets.py @@ -1,4 +1,5 @@ import utils +from money import money class tax_brackets: """A class to represent tax brackets and some operations on them.""" @@ -8,7 +9,7 @@ class tax_brackets: def compute_taxes_for_income(self, income): """Compute the tax bill for income given our brackets.""" - taxes_due = 0 + taxes_due = money(0) while income > 1: (threshold, rate) = self.get_bracket_for_income(income) taxes_due += (income - threshold) * rate @@ -71,5 +72,4 @@ class tax_brackets: def dump(self): """Print out the tax brackets we're using in here.""" for x in self.brackets: - print "{:<20} -> {:<3}".format(utils.format_money(x[0]), - utils.format_rate(x[1])) + print "{:<20} -> {:<3}".format(x[0], utils.format_rate(x[1])) diff --git a/tax_collector.py b/tax_collector.py index 248c944..455d8b5 100644 --- a/tax_collector.py +++ b/tax_collector.py @@ -1,21 +1,22 @@ import utils +from money import money class tax_collector(object): def __init__(self): # These four accumulate and then clear every year (i.e. every call # to record_taxes_paid_and_reset_for_next_year) - self.ordinary_income = 0 - self.short_term_gains = 0 - self.dividends_and_long_term_gains = 0 - self.roth_income = 0 + self.ordinary_income = money(0) + self.short_term_gains = money(0) + self.dividends_and_long_term_gains = money(0) + self.roth_income = money(0) # The rest of these aggregate and are used as part of the final stats. - self.total_tax_bill = 0 - self.total_ordinary_income = 0 - self.total_short_term_gains = 0 - self.total_dividends_and_long_term_gains = 0 - self.total_roth_income = 0 - self.total_aggregate_income = 0 + self.total_tax_bill = money(0) + self.total_ordinary_income = money(0) + self.total_short_term_gains = money(0) + self.total_dividends_and_long_term_gains = money(0) + self.total_roth_income = money(0) + self.total_aggregate_income = money(0) def record_taxes_paid_and_reset_for_next_year(self, taxes_paid): @@ -23,10 +24,10 @@ class tax_collector(object): self.total_tax_bill += taxes_paid self.total_aggregate_income += self.get_total_income() assert self.total_aggregate_income >= 0, "Accumulator should be >= 0" - self.ordinary_income = 0 - self.short_term_gains = 0 - self.dividends_and_long_term_gains = 0 - self.roth_income = 0 + self.ordinary_income = money(0) + self.short_term_gains = money(0) + self.dividends_and_long_term_gains = money(0) + self.roth_income = money(0) def record_ordinary_income(self, amount): assert amount >= 0, "Income should be non-negative" @@ -53,14 +54,14 @@ class tax_collector(object): ordinary_income_tax_brackets, dividends_and_long_term_gains_brackets): assert standard_deduction >= 0, "Standard deduction should be non-negative" - taxes_due = 0 + taxes_due = money(0) # Handle ordinary income: ordinary_income = (self.ordinary_income + self.short_term_gains - standard_deduction) if ordinary_income < 0: - ordinary_income = 0 + ordinary_income = money(0) taxes_due += ordinary_income_tax_brackets.compute_taxes_for_income( ordinary_income) @@ -97,17 +98,17 @@ class tax_collector(object): def dump_final_report(self): print " Taxes and income:" print " {:<50}: {:>14}".format("Total aggregate income", - utils.format_money(self.total_aggregate_income)) + self.total_aggregate_income) print " ...{:<47}: {:>14}".format("Ordinary income", - utils.format_money(self.total_ordinary_income)) + self.total_ordinary_income) print " ...{:<47}: {:>14}".format("Income from short term gains", - utils.format_money(self.total_short_term_gains)) + self.total_short_term_gains) print " ...{:<47}: {:>14}".format("Income from dividends and long term gains", - utils.format_money(self.total_dividends_and_long_term_gains)) + self.total_dividends_and_long_term_gains) print " ...{:<47}: {:>14}".format("Roth income", - utils.format_money(self.total_roth_income)) + self.total_roth_income) print " {:<50}: {:>14}".format("Total taxes paid", - utils.format_money(self.total_tax_bill)) + self.total_tax_bill) overall_tax_rate = float(self.total_tax_bill) / float(self.total_aggregate_income) print " {:<50}: {:>14}".format("Effective tax rate", utils.format_rate(overall_tax_rate)) diff --git a/utils.py b/utils.py index ce66420..97b9819 100644 --- a/utils.py +++ b/utils.py @@ -6,10 +6,6 @@ def truncate(n, decimals=2): multiplier = 10 ** decimals return int(n * multiplier) / multiplier -def format_money(number): - """Format a monetary amount with a $ and comma thousands separators.""" - return ("${:,}".format(truncate(number))) - def format_rate(rate): """Format a multiplier nee rate to look nice.""" if rate >= 1.0: -- 2.45.0