Wires in the money class.
authorScott Gasch <[email protected]>
Wed, 15 Jan 2020 23:10:41 +0000 (15:10 -0800)
committerScott Gasch <[email protected]>
Wed, 15 Jan 2020 23:10:41 +0000 (15:10 -0800)
accounts.py
constants.py
money.py
parameters.py
retire.py
tax_brackets.py
tax_collector.py
utils.py

index 5f0def0479bf33221e829f6939bf834a742941dc..0e53e045949ecd3708e00a8ac24054e48a113690 100644 (file)
@@ -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)
index af0399af20a0532015e85161e0fc013e34648a74..d31b0417811bb10188096c34da919b431e5496b3 100644 (file)
@@ -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)
index 52fe32b5c72926b37d3a74f48c3e4daff1c9ec9f..848d9725e2387ac342e93814088d3a4a1b47e195 100644 (file)
--- 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))
index ae66e1560d2b6875ca1ffc380b6fe78c367b8c92..989d5265468cce99ea9f64589d4ba003cfbbd390 100644 (file)
@@ -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 "  ]"
index 3282a9e15dc180db5d9c18fbccc2b4010a504f57..5f567aa96d933a637c86e7b577a5d9bea138fb62 100755 (executable)
--- 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()
index c64cf2b842086b51bc1259c25852997c31663960..e0a46607fcfa8e524945acc7d98f03c393c003c6 100644 (file)
@@ -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]))
index 248c944c1a8fc771befc357201540aa5ac0abafb..455d8b5eceff893e02c5cd6277a034079ccaa85f 100644 (file)
@@ -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))
index ce664204666e749bb1343fa86aa945a28004fbc5..97b98199c1ac2786714d8a2f6f32a178e337fed7 100644 (file)
--- 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: