find_the_largest_withdrawals_size

PortfolioDCF.find_the_largest_withdrawals_size(goal, withdrawals_range=(0, 1), target_survival_period=25, percentile=20, threshold=0, tolerance_rel=0.1, iter_max=20)

Find the largest withdrawal size for Monte Carlo simulation according to a cash flow strategy.

You can find the largest withdrawal size for three goals:

  • ‘maintain_balance_pv’ to keep the purchasing power of the investments after inflation for the whole period defined in Monte Carlo parameters.

  • ‘maintain_balance_fv’ to keep the nominal size of the investments for the whole period defined in Monte Carlo parameters.

  • ‘survival_period’ to keep a positive balance for a period defined by target_survival_period.

The method works with IndexationStrategy and PercentageStrategy only.

The withdrawal size defined in cash flow strategy must be negative.

The result of finding a solution has the following parameters: - ‘success’ - whether the solution was found or not. - ‘withdrawal_abs’ - the absolute amount of withdrawal size (the best solution if found). - ‘withdrawal_rel’ - the relative amount of withdrawal size (the best solution if found). - ‘error_rel’ - characterizes how accurately the goal is fulfilled. - ‘solutions’ - the history of attempts to find solutions (withdrawal values and error level).

The algorithm evaluates the goal at both ends of withdrawals_range first and stops early if the solution lies outside the range; otherwise it finds the withdrawal size with Brent’s method (scipy.optimize.brentq) over the cached set of Monte Carlo scenarios.

Parameters:
goal{‘maintain_balance_fv’, ‘maintain_balance_pv’, ‘survival_period’}

‘maintain_balance_fv’ - the goal is to maintain the balance not lower than the nominal amount of the initial investment for the whole period defined in Monte Carlo parameters. ‘maintain_balance_pv’ - the goal is to keep the purchasing power of the investments after inflation for the whole period defined in Monte Carlo parameters. ‘survival_period’ - the goal is to keep positive balance for a period defined by ‘target_survival_period’.

withdrawals_rangetuple of (float, float), default (0, 1)

The expected range of annualized withdrawal size measured as a percentage of the Initial Investment (CashFlow.initial_investment). 0.01 stands for 1%. (0.02, 0.05) means that expected withdrawal is in range from 2% to 5% of Initial Investment. The first value is expected minimum withdrawal. The second value is expected maximum withdrawal. The search for a solution occurs only within this range.

percentileint, default 20

The percentile of Monte-Carlo simulation distribution where the goal is achieved. Percentile must be from 0 to 100. 1st or 5th percentiles are the examples of “bad” scenarios. 50th is median. 95th or 99th are optimistic scenarios.

thresholdfloat, default 0

The percentage of initial investments when the portfolio balance is considered voided. Important for the “fixed_percentage” Cash flow strategy.

target_survival_periodint, default 25

The smallest acceptable survival period. It works with the ‘survival_period’ goal only. The value must be less than the MonteCarlo.priod parameter.

iter_maxint, default 20

The maximum number of objective evaluations (each runs one Monte Carlo simulation), including the two evaluations at the ends of withdrawals_range.

tolerance_relfloat, default 0.10

The allowed tolerance for the solution. The tolerance is the largest error for the achieved goal.

Returns:
Result

The result of the solution search.

Examples

>>> pf = ok.Portfolio(
...     assets=["MCFTR.INDX", "RUCBTRNS.INDX"],
...     weights=[0.3, 0.7],
...     inflation=True,
...     ccy="RUB",
...     rebalancing_strategy=ok.Rebalance(period="year"),
... )
>>> # Fixed Percentage strategy
>>> pc = ok.PercentageStrategy(pf)
>>> pc.initial_investment = 10_000
>>> pc.frequency = "year"
>>> # Assign a strategy
>>> pf.dcf.cashflow_parameters = pc
>>> # Set Monte Carlo parameters (seed makes the search reproducible)
>>> pf.dcf.set_mc_parameters(distribution="norm", period=50, mc_number=200, seed=42)
>>> res = pf.dcf.find_the_largest_withdrawals_size(
...     percentile=50,
...     goal="survival_period",
...     threshold=0.05,
...     target_survival_period=25,
... )
>>> res
success                  True
withdrawal_abs   -1598.947236
withdrawal_rel       0.159895
error_rel               0.048
attempts                    7
dtype: object

In the result, withdrawal_abs is the absolute value of the withdrawal (the first withdrawal value), and withdrawal_rel is the relative withdrawal size (the first withdrawal value divided by the initial investment).

Even if the solution was not found, it is still possible to see the intermediate steps: the two evaluations at the range ends followed by the Brent root-finding steps. Exact values depend on the historical data window the distribution is fitted to.

>>> res.solutions
  withdrawal_abs withdrawal_rel error_rel error_rel_change
0       -10000.0              1     0.928                0
1            0.0              0       1.0            0.072
2   -5186.721992       0.518672     0.768           -0.232
3   -2593.360996       0.259336      0.46           -0.308
4   -1296.680498       0.129668     0.276           -0.184
5   -1782.935685       0.178294     0.168           -0.108
6   -1598.947236       0.159895     0.048            -0.12