37 lines
1.1 KiB
Python
37 lines
1.1 KiB
Python
"""Withdrawal-strategy abstract base.
|
|
|
|
All strategies operate in REAL GBP terms — the simulator deflates by
|
|
the cumulative CPI index before calling. Brackets inside the tax
|
|
engines are also assumed to inflate with CPI (simplifying assumption
|
|
that tax thresholds keep pace with inflation — fiscal drag is a
|
|
documented v2 follow-up).
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from abc import ABC, abstractmethod
|
|
from dataclasses import dataclass
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class StrategyState:
|
|
"""Inputs to a strategy's per-year decision. Real GBP throughout."""
|
|
portfolio: float
|
|
initial_portfolio: float
|
|
initial_withdrawal: float
|
|
year_idx: int
|
|
horizon_years: int
|
|
last_withdrawal: float
|
|
expected_real_return: float = 0.04
|
|
|
|
|
|
class WithdrawalStrategy(ABC):
|
|
name: str
|
|
|
|
@abstractmethod
|
|
def propose_withdrawal(self, state: StrategyState) -> float:
|
|
"""Return the proposed withdrawal in real GBP for this year.
|
|
|
|
The simulator may clip downward if the portfolio is exhausted —
|
|
strategies can request more than the portfolio holds.
|
|
"""
|
|
raise NotImplementedError
|