def _calc(self): if self.portfolio.value >= (1 + self.up_threshold) * self.get_required_portfolio(): new_amount = pmt(self.get_rate(), self.get_life_expectancy(), self.portfolio.value) new_rate = new_amount / self.portfolio.value self.current_rate += (new_rate - self.current_rate) * self.upward_rate_adjustment self.current_rate = min(self.current_rate, self.ceiling_rate) if self.portfolio.value < (1 + self.down_threshold) * self.get_required_portfolio(): new_amount = pmt(self.get_rate(), self.get_life_expectancy(), self.portfolio.value) new_rate = new_amount / self.portfolio.value self.current_rate -= (self.current_rate - new_rate) * self.downward_rate_adjustment self.current_rate = max(self.current_rate, self.floor_rate) amount = self.current_rate * self.portfolio.value self.current_age += 1 return amount
def harvest(self): amount = yield while True: amount_left = amount if self.portfolio.bonds_pct > self.target_bonds: bonds_excess = (self.portfolio.bonds_pct - self.target_bonds) * self.portfolio.value bonds_excess = round(bonds_excess) bonds_to_sell = min(amount, bonds_excess) self.portfolio.sell_bonds(bonds_to_sell) amount_left -= bonds_to_sell else: bonds_deficit = -(self.portfolio.bonds_pct - self.target_bonds) * self.portfolio.value bonds_deficit = round(bonds_deficit) self.portfolio.sell_stocks(bonds_deficit) self.portfolio.buy_bonds(bonds_deficit) bonds_to_sell = amount_left * self.target_bonds stocks_to_sell = amount_left - bonds_to_sell self.portfolio.sell_bonds(bonds_to_sell) self.portfolio.sell_stocks(stocks_to_sell) pmt_amount = pmt(self.discount_rate, self.final_age - self.current_age, self.portfolio.value) if amount < pmt_amount and amount >= self.initial_withdrawal: harvest = pmt_amount - amount self.portfolio.buy_bonds(harvest) amount = yield self.portfolio.empty_cash()
def _calc(self): rate = (self.portfolio.stocks_pct * self.stock_growth_rate + self.portfolio.bonds_pct * self.bond_growth_rate) amt = pmt(rate, self.years_left, self.portfolio.value) # max out at 20% of the current portfolio...this allows it to run # "indefinitely" return min(amt, self.portfolio.value / 5)
def _calc(self): amount = pmt(self.rate, self.final_age - self.current_age, self.portfolio.value) desired_pmt = self.desired_payment * self.cumulative_inflation desired_value = pv(float(self.rate), self.final_age - self.current_age, float(-desired_pmt), when='begin') desired_value = Decimal(desired_value) tilt = (self.portfolio.value / desired_value) ** self.tilt return amount * tilt
def _calc(self): # Siegel & Waring suggest the discount rate should be # "TIPS interest rate (present-value-weighted average interest rate across the TIPS # ladder)" at the start of each year. # Currently, we don't support changing the discount_rate every year and use a constant rate rate = self.discount_rate # Siegel & Waring suggest using the average of 120 (the maximum known human life span) and # life expectancy based on current age according to the Social Security tables. years_left = average([ARVA.MAX_AGE, get_life_expectancy(self.current_age)]) return pmt(rate, years_left, self.portfolio.value)
def _calc(self): # shortcut if the portfolio is 0...some of the math # below doesn't like 0s. if self.portfolio.value == 0: return Decimal(0) amount = pmt(self.rate, self.final_age - self.current_age, self.portfolio.value) desired_pmt = self.desired_payment * self.cumulative_inflation desired_value = pv(float(self.rate), self.final_age - self.current_age, float(-desired_pmt), when='begin') desired_value = Decimal(desired_value) tilt = (self.portfolio.value / desired_value) ** self.tilt return amount * tilt
def _calc(self): p_v = pmt(self.discount_rate, self.final_age - self.current_age, self.portfolio.value) if p_v < self.current_withdrawal: self.retrenchments.append((self.current_age, self.current_withdrawal, p_v)) if self.only_down: if p_v < self.current_withdrawal: self.current_withdrawal = p_v else: self.current_withdrawal = p_v return self.current_withdrawal
def _calc(self): if self.portfolio.value >= ( 1 + self.up_threshold) * self.get_required_portfolio(): new_amount = pmt(self.get_rate(), self.get_life_expectancy(), self.portfolio.value) new_rate = new_amount / self.portfolio.value self.current_rate += ( new_rate - self.current_rate) * self.upward_rate_adjustment self.current_rate = min(self.current_rate, self.ceiling_rate) if self.portfolio.value < ( 1 + self.down_threshold) * self.get_required_portfolio(): new_amount = pmt(self.get_rate(), self.get_life_expectancy(), self.portfolio.value) new_rate = new_amount / self.portfolio.value self.current_rate -= (self.current_rate - new_rate) * self.downward_rate_adjustment self.current_rate = max(self.current_rate, self.floor_rate) amount = self.current_rate * self.portfolio.value self.current_age += 1 return amount
def _calc(self): # Siegel & Waring suggest the discount rate should be # "TIPS interest rate (present-value-weighted average interest rate across the TIPS # ladder)" at the start of each year. # Currently, we don't support changing the discount_rate every year and use a constant rate rate = self.discount_rate # Siegel & Waring suggest using the average of 120 (the maximum known human life span) and # life expectancy based on current age according to the Social Security tables. years_left = average( [ARVA.MAX_AGE, get_life_expectancy(self.current_age)]) return pmt(rate, years_left, self.portfolio.value)
def _calc(self): p_v = pmt(self.discount_rate, self.final_age - self.current_age, self.portfolio.value) if p_v < self.current_withdrawal: self.retrenchments.append( (self.current_age, self.current_withdrawal, p_v)) if self.only_down: if p_v < self.current_withdrawal: self.current_withdrawal = p_v else: self.current_withdrawal = p_v return self.current_withdrawal
def _calc(self): p_v = pmt(self.discount_rate, self.final_age - self.current_age, self.portfolio.value) # React immediately to downward market conditions. if p_v < self.current_withdrawal: self.current_withdrawal = p_v # Only do the harvest if we aren't under water from where we started elif p_v < self.initial_withdrawal: self.current_withdrawal = p_v else: excess_funds = p_v - self.current_withdrawal lifestyle_creep = excess_funds * self.lifestyle_pct self.current_withdrawal = min(lifestyle_creep + self.current_withdrawal, self.max_withdrawal) return self.current_withdrawal
def _calc(self): # shortcut if the portfolio is 0...some of the math # below doesn't like 0s. if self.portfolio.value == 0: return Decimal(0) amount = pmt(self.rate, self.final_age - self.current_age, self.portfolio.value) desired_pmt = self.desired_payment * self.cumulative_inflation desired_value = pv(float(self.rate), self.final_age - self.current_age, float(-desired_pmt), when='begin') desired_value = Decimal(desired_value) tilt = (self.portfolio.value / desired_value)**self.tilt return amount * tilt
def _calc(self): p_v = pmt(self.discount_rate, self.final_age - self.current_age, self.portfolio.value) # React immediately to downward market conditions. if p_v < self.current_withdrawal: self.current_withdrawal = p_v # Only do the harvest if we aren't under water from where we started elif p_v < self.initial_withdrawal: self.current_withdrawal = p_v else: excess_funds = p_v - self.current_withdrawal lifestyle_creep = excess_funds * self.lifestyle_pct self.current_withdrawal = min( lifestyle_creep + self.current_withdrawal, self.max_withdrawal) return self.current_withdrawal
def __init__(self, portfolio, harvest_strategy, current_age=65, final_age=110, discount_rate=.08, only_down=True): super().__init__(portfolio, harvest_strategy) self.discount_rate = discount_rate self.final_age = final_age self.current_age = current_age # Do we only "retrench" or do we also increase the withdrawals? # Setting this to true means using PMT directly. self.only_down = only_down self.current_withdrawal = pmt(self.discount_rate, self.final_age - self.current_age, self.portfolio.value) self.retrenchments = []
def __init__(self, portfolio, current_age=65, final_age=110, discount_rate=.06, lifestyle_pct=.25, lifestyle_cap=2): strategy = self.harvest() strategy.send(None) super().__init__(portfolio, strategy) self.discount_rate = discount_rate self.final_age = final_age self.current_age = current_age self.current_withdrawal = pmt(self.discount_rate, self.final_age - self.current_age, self.portfolio.value) self.initial_withdrawal = self.current_withdrawal self.lifestyle_pct = Decimal(lifestyle_pct) self.target_bonds = portfolio.bonds_pct self.max_withdrawal = self.initial_withdrawal * Decimal(lifestyle_cap)