After-tax value is the projected investment value left after taxes. It makes asset location quantitative: the same asset can produce different values in an IRA, Roth, or taxable account because accounts tax growth, income, and gains differently. section 8.1 section 8.3.2
Model an asset with expected return, payout rate, payout tax rate, capital-gains tax rate, ordinary-income tax rate, turnover, starting value, and cost basis. For an IRA projection, compound the starting value over the horizon, then apply the ordinary-income tax rate to the ending balance. Taxable projections also depend on payouts, realized turnover, and cost basis. section 8.1
Use after-tax value to compare choices under explicit assumptions:
- Compare taxable-account value with IRA value to rank an asset’s tax efficiency for a particular investor and tax brackets. section 8.2
- Build a value matrix whose rows are account types and columns are assets before assigning investments subject to account-size budgets. section 8.3.2
- Treat rankings as investor-specific; tax brackets and omitted state taxes can change the result. section 8.2
Stocks and bonds in different accounts
With a 30-year projection, a stock using the stated defaults produces a taxable value of $4,125,063, while a bond with 3% return, 3% payout, and 35% payout tax produces an IRA value of $788,860. Placing stocks in taxable and bonds in an IRA totals $4,913,923, versus $4,162,819 for the reverse placement. section 8.1
Listing 8.1 Defining the Asset() class
class Asset():
def __init__(self, avg_ret=0.08, payout_rate=0.0, tax_rate_payout=0.2, #1
tax_rate_cap_gains=0.2, tax_rate_ord_inc=0.35,
turnover_rate=0, starting_value=500000, cost_basis=500000):
self.avg_ret = avg_ret
self.payout_rate = payout_rate
self.tax_rate_payout = tax_rate_payout
self.tax_rate_cap_gains = tax_rate_cap_gains
self.tax_rate_ord_inc = tax_rate_ord_inc
self.turnover_rate = turnover_rate
self.starting_value = starting_value
self.cost_basis = cost_basis
def IRA_value(self, horizon=30):
pretax_value = self.starting_value * (1+self.avg_ret)**horizon
aftertax_value = pretax_value * (1-self.tax_rate_ord_inc)
return aftertax_value
def Taxable_value(self, horizon=30):
curr_cost_basis = self.cost_basis
curr_value = self.starting_value
for year in range(1, horizon+1):
taxes_div = self.payout_rate * curr_value * self.tax_rate_payout
taxes_cap_gain = (self.turnover_rate * curr_value
* self.tax_rate_cap_gains)
curr_cost_basis += ((self.payout_rate * curr_value) #2
+ (self.turnover_rate * curr_value)
- (taxes_div + taxes_cap_gain))
curr_value = (curr_value * (1+self.avg_ret)
- (taxes_div + taxes_cap_gain))
aftertax_value = (curr_value
- (curr_value - curr_cost_basis)
* self.tax_rate_cap_gains)
return aftertax_value
def Taxable_minus_IRA(self, horizon=30):
difference = (self.Taxable_value(horizon) - self.IRA_value(horizon)
- self.starting_value * self.tax_rate_ord_inc) #3
return difference
def Roth_value(self, horizon=30):
aftertax_value = self.starting_value * (1+self.avg_ret)**horizon
return aftertax_value
Inspect the Asset() implementation to see the inputs carried into IRA and taxable after-tax value projections. section 8.1
