Add money.py, a class I'd like to start using in here instead of floats.
[retire.git] / tax_collector.py
1 import utils
2
3 class tax_collector(object):
4     def __init__(self):
5         # These four accumulate and then clear every year (i.e. every call
6         # to record_taxes_paid_and_reset_for_next_year)
7         self.ordinary_income = 0
8         self.short_term_gains = 0
9         self.dividends_and_long_term_gains = 0
10         self.roth_income = 0
11
12         # The rest of these aggregate and are used as part of the final stats.
13         self.total_tax_bill = 0
14         self.total_ordinary_income = 0
15         self.total_short_term_gains = 0
16         self.total_dividends_and_long_term_gains = 0
17         self.total_roth_income = 0
18         self.total_aggregate_income = 0
19
20     def record_taxes_paid_and_reset_for_next_year(self,
21                                                   taxes_paid):
22         assert taxes_paid >= 0, "You can't pay negative taxes"
23         self.total_tax_bill += taxes_paid
24         self.total_aggregate_income += self.get_total_income()
25         assert self.total_aggregate_income >= 0, "Accumulator should be >= 0"
26         self.ordinary_income = 0
27         self.short_term_gains = 0
28         self.dividends_and_long_term_gains = 0
29         self.roth_income = 0
30
31     def record_ordinary_income(self, amount):
32         assert amount >= 0, "Income should be non-negative"
33         self.ordinary_income += amount
34         self.total_ordinary_income += amount
35
36     def record_short_term_gain(self, amount):
37         assert amount >= 0, "Income should be non-negative"
38         self.short_term_gains += amount
39         self.total_short_term_gains += amount
40
41     def record_dividend_or_long_term_gain(self, amount):
42         assert amount >= 0, "Income should be non-negative"
43         self.dividends_and_long_term_gains += amount
44         self.total_dividends_and_long_term_gains += amount
45
46     def record_roth_income(self, amount):
47         assert amount >= 0, "Income should be non-negative"
48         self.roth_income += amount
49         self.total_roth_income += amount
50
51     def approximate_taxes(self,
52                           standard_deduction,
53                           ordinary_income_tax_brackets,
54                           dividends_and_long_term_gains_brackets):
55         assert standard_deduction >= 0, "Standard deduction should be non-negative"
56         taxes_due = 0
57
58         # Handle ordinary income:
59         ordinary_income = (self.ordinary_income +
60                            self.short_term_gains -
61                            standard_deduction)
62         if ordinary_income < 0:
63             ordinary_income = 0
64         taxes_due += ordinary_income_tax_brackets.compute_taxes_for_income(
65             ordinary_income)
66
67         # Handle dividends and long term gains:
68         taxes_due += dividends_and_long_term_gains_brackets.compute_taxes_for_income(
69             self.dividends_and_long_term_gains)
70
71         # Assume Roth money is still available tax free in the future.
72         assert taxes_due >= 0, "Computed negative taxes?!"
73         return taxes_due
74
75     def how_many_more_dollars_can_we_earn_without_changing_tax_rate(
76             self,
77             federal_ordinary_income_tax_brackets):
78         """Return number of ordinary income dollars we can make without
79            changing this year's tax rate.  Note: this may return None
80            which actually indicates something more like 'infinite since
81            you're already at the top tax bracket.'"""
82         income = self.ordinary_income
83         if income <= 1: income = 2
84         b = federal_ordinary_income_tax_brackets.get_bracket_above_income(
85             income)
86         if b[0] is not None:
87             delta = b[0] - income
88             return delta
89         return 0
90
91     def get_total_income(self):
92         return (self.ordinary_income +
93                 self.short_term_gains +
94                 self.dividends_and_long_term_gains +
95                 self.roth_income)
96
97     def dump_final_report(self):
98         print "  Taxes and income:"
99         print "    {:<50}: {:>14}".format("Total aggregate income",
100                                           utils.format_money(self.total_aggregate_income))
101         print "    ...{:<47}: {:>14}".format("Ordinary income",
102                                              utils.format_money(self.total_ordinary_income))
103         print "    ...{:<47}: {:>14}".format("Income from short term gains",
104                                              utils.format_money(self.total_short_term_gains))
105         print "    ...{:<47}: {:>14}".format("Income from dividends and long term gains",
106                                              utils.format_money(self.total_dividends_and_long_term_gains))
107         print "    ...{:<47}: {:>14}".format("Roth income",
108                                              utils.format_money(self.total_roth_income))
109         print "    {:<50}: {:>14}".format("Total taxes paid",
110                                           utils.format_money(self.total_tax_bill))
111         overall_tax_rate = float(self.total_tax_bill) / float(self.total_aggregate_income)
112         print "    {:<50}: {:>14}".format("Effective tax rate",
113                                           utils.format_rate(overall_tax_rate))