Skip to content
/ swr Public

Safe withdrawal rate using flexible investing and withdrawal schedules and optimizers

License

Notifications You must be signed in to change notification settings

druce/swr

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

swr - Safe Withdrawal for Retirement

outcome.png

optimal_by_gamma_table.png

A Python module for determining safe withdrawal rates, designed to accommodate:

  • Any generator of asset returns (historical cohorts, Monte Carlo, roll your own market environment)
  • Any asset allocation strategy (fixed weights, glidepath schedules, roll your own based on any parameters)
  • Any withdrawal strategy (fixed withdrawal, variable percentage, combinations, glidepaths)
  • Any metrics to evaluate retirement cohort outcomes (e.g. total spending, certainty equivalent spending, roll your own. Support for survival tables, i.e. calculate expected metric for living retirees taking into account retirement age and survivorship)
  • Any (gradient-free) optimizer to find optimal parameters (e.g. asset allocation, withdrawal parameters) to maximize a metric in the given market environment

Example (see example.ipynb):

  • Market environment: historical returns 1928-2020; 2 assets, stocks and intermediate-term corporate bonds; analyze 30-year retirement cohorts.
  • Allocation rule: 1 parameter = stock_alloc. Allocate a fixed percentage to stocks. bond_alloc = 1 - stock_alloc.
  • Withdrawal rule: 3 parameters = fixed_pct, variable_pct, floor_pct. Withdrawal is a linear function of the portfolio value, with a floor. Withdraw fixed_pct (intercept), plus variable_pct (slope) * portfolio value, with a floor: max(floor_pct, fixed_pct + variable_pct * portval / 100).
  • Metric to maximize: certainty-equivalent spending under CRRA utility with a gamma risk aversion parameter.
  • In this example, for each gamma value, we run optimizers to find the parameters (stock_alloc, fixed_pct, variable_pct, floor_pct) that would have maximized certainty-equivalent spending over all available 30-year retirement cohorts 1928-1991.
N_RET_YEARS = 30
FIXED_PCT = 3.5
VARIABLE_PCT = 1.0
FLOOR_PCT = 0.0
ALLOC_STOCKS = 0.75
ALLOC_BONDS = 0.25
GAMMA  = 1.0

s = SWRsimulationCE({
    'simulation': {'returns_df': real_return_df,
                   'n_ret_years': N_RET_YEARS,
                  },
    'allocation': {'asset_weights': np.array([ALLOC_STOCKS, ALLOC_BONDS])}, 
    'withdrawal': {'fixed_pct': FIXED_PCT,
                   'variable_pct': VARIABLE_PCT,
                   'floor_pct': FLOOR_PCT,
                  },
    'evaluation': {'gamma': GAMMA},
    'visualization': {'histogram': True, 
                      'chart_1' : {'title': 'Years to Exhaustion by Retirement Year',
                                   'annotation': "Fixed spend %.1f, Variable spend %.1f, stocks %.1f%%" % (FIXED_PCT, 
                                                                                                           VARIABLE_PCT, 
                                                                                                           100 * ALLOC_STOCKS)
                                  },
                      'chart_2' : {'title': 'Spending By Retirement Year',
                                  },
                      'chart_3' : {'title': 'Portfolio Value By Retirement Year',
                                  },
                     }    
    
})

s.simulate()

s.visualize()

To implement a different strategy context, see SWRsimulationCE.py, which inherits from SWRsimulation.py. Override relevant methods:

  • To use a different market environment, override init_simulation which should initialize an appropriate generator function in self.simulation['trials'].
  • To use a different allocation rule, override init_allocation and get_allocations.
  • To use a different spending rule, override init_withdrawal and get_withdrawal.
  • To calculate different metrics, override eval_trial.
  • See optimize.ipynb for examples of running gradient-free optimizers to find retirement parameters that optimize a specified metric.

Work-in-progress, YMMV. Open issues with any questions, suggestions. Pull requests are welcome.

Interactive app that does calculations and visualizations (React code)

About

Safe withdrawal rate using flexible investing and withdrawal schedules and optimizers

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published