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