Summary
Portfolio.dcf.find_the_largest_withdrawals_size() raises
ValueError: target_survival_period must be less than Monte Carlo simulation period (N).
for the maintain_balance_pv and maintain_balance_fv goals whenever the Monte
Carlo period is 27 years or fewer, even when the caller never passes
target_survival_period. The parameter is not used by these two goals, yet
it is validated unconditionally, so its default value (25) breaks the call.
okama version: 2.2.1 (same code in current master).
Steps to reproduce
import okama as ok
pf = ok.Portfolio(
["SPY.US", "AGG.US"],
weights=[0.6, 0.4],
inflation=True,
ccy="USD",
rebalancing_strategy=ok.Rebalance(period="year"),
)
pc = ok.PercentageStrategy(pf)
pc.initial_investment = 100_000
pc.frequency = "year"
pf.dcf.cashflow_parameters = pc
pf.dcf.set_mc_parameters(distribution="norm", period=25, mc_number=50)
# target_survival_period is NOT passed — it is irrelevant for this goal
pf.dcf.find_the_largest_withdrawals_size(goal="maintain_balance_pv", percentile=5)
Actual behavior
ValueError: target_survival_period must be less than Monte Carlo simulation period (25).
It also fails for goal="maintain_balance_fv", and for any MC period <= 27
(including small/default periods such as 10), because the check is
target_survival_period > period * (1 - tolerance_rel) → 25 > period * 0.9.
Workaround that confirms the diagnosis
Passing any value below 0.9 * period makes the call succeed, and the value has
no effect on the result for these goals:
pf.dcf.find_the_largest_withdrawals_size(
goal="maintain_balance_pv", percentile=5, target_survival_period=12
) # success=True
Root cause
In okama/portfolios/dcf.py:
find_the_largest_withdrawals_size defaults target_survival_period: int = 25
(signature, ~L874).
- It calls
self._validate_parameters(...) unconditionally for every goal
(~L999), and _validate_parameters does not receive goal.
_validate_parameters rejects target_survival_period > self.mc.period * (1 - tolerance_rel)
(~L1112-1119).
- But
target_survival_period is only consumed by the survival_period branch
of _calculate_goal_metrics (~L1150-1152). The maintain_balance_fv /
maintain_balance_pv branch (~L1140-1149) never reads it — its condition is
wealth_at_quantile >= start_investment and sp_at_quantile == self.mc.period.
So a parameter that is irrelevant to two of the three goals is validated against
all of them, and its default makes those goals unusable for typical (short) MC
horizons.
Expected behavior
For maintain_balance_pv / maintain_balance_fv, find_the_largest_withdrawals_size
should run regardless of target_survival_period (it is documented as working
with the survival_period goal only — see the target_survival_period docstring).
Suggested fix
Validate / apply target_survival_period only when goal == "survival_period".
For example, pass goal into _validate_parameters and guard the period check:
if goal == WithdrawalGoal.SURVIVAL_PERIOD and target_survival_period > self.mc.period * (1 - tolerance_rel):
raise ValueError(
f"target_survival_period must be less than Monte Carlo simulation period ({self.mc.period})."
)
(or move that single check into the survival_period branch). The other goals
then ignore the parameter entirely, as the docstring already implies.
Downstream impact
Surfaces in okama.io (okama-dash) Portfolio → "Find max withdrawal": selecting
goal "Keep purchasing power (PV)" or "Keep nominal balance (FV)" returns this
error for the default forecast period, because the UI does not pass
target_survival_period for these goals (correctly, since it is unused).
Summary
Portfolio.dcf.find_the_largest_withdrawals_size()raisesfor the
maintain_balance_pvandmaintain_balance_fvgoals whenever the MonteCarlo period is 27 years or fewer, even when the caller never passes
target_survival_period. The parameter is not used by these two goals, yetit is validated unconditionally, so its default value (
25) breaks the call.okama version: 2.2.1 (same code in current
master).Steps to reproduce
Actual behavior
It also fails for
goal="maintain_balance_fv", and for any MCperiod <= 27(including small/default periods such as 10), because the check is
target_survival_period > period * (1 - tolerance_rel)→25 > period * 0.9.Workaround that confirms the diagnosis
Passing any value below
0.9 * periodmakes the call succeed, and the value hasno effect on the result for these goals:
Root cause
In
okama/portfolios/dcf.py:find_the_largest_withdrawals_sizedefaultstarget_survival_period: int = 25(signature, ~L874).
self._validate_parameters(...)unconditionally for every goal(~L999), and
_validate_parametersdoes not receivegoal._validate_parametersrejectstarget_survival_period > self.mc.period * (1 - tolerance_rel)(~L1112-1119).
target_survival_periodis only consumed by thesurvival_periodbranchof
_calculate_goal_metrics(~L1150-1152). Themaintain_balance_fv/maintain_balance_pvbranch (~L1140-1149) never reads it — its condition iswealth_at_quantile >= start_investment and sp_at_quantile == self.mc.period.So a parameter that is irrelevant to two of the three goals is validated against
all of them, and its default makes those goals unusable for typical (short) MC
horizons.
Expected behavior
For
maintain_balance_pv/maintain_balance_fv,find_the_largest_withdrawals_sizeshould run regardless of
target_survival_period(it is documented as workingwith the
survival_periodgoal only — see thetarget_survival_perioddocstring).Suggested fix
Validate / apply
target_survival_periodonly whengoal == "survival_period".For example, pass
goalinto_validate_parametersand guard the period check:(or move that single check into the
survival_periodbranch). The other goalsthen ignore the parameter entirely, as the docstring already implies.
Downstream impact
Surfaces in okama.io (okama-dash) Portfolio → "Find max withdrawal": selecting
goal "Keep purchasing power (PV)" or "Keep nominal balance (FV)" returns this
error for the default forecast period, because the UI does not pass
target_survival_periodfor these goals (correctly, since it is unused).