Initial extraction from monorepo
This commit is contained in:
commit
f7ef7ca4ab
56 changed files with 6163 additions and 0 deletions
46
fire_planner/glide_path.py
Normal file
46
fire_planner/glide_path.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
"""Glide-path functions — stock-allocation as a function of years
|
||||
since retirement.
|
||||
|
||||
Pfau & Kitces (2014) showed that *rising* equity glide paths
|
||||
(starting low and rising) reduce sequence-of-returns risk in the
|
||||
critical first decade of retirement. We default to that, with a
|
||||
classic static 60/40 also available.
|
||||
|
||||
Each glide returns a fraction in [0, 1] for stock allocation — the
|
||||
remainder is bonds.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
|
||||
GlideFn = Callable[[int], float]
|
||||
|
||||
|
||||
def rising_equity(start: float = 0.30, end: float = 0.70, ramp_years: int = 15) -> GlideFn:
|
||||
"""Linear interpolation from `start` to `end` over `ramp_years`,
|
||||
then constant at `end`."""
|
||||
span = end - start
|
||||
|
||||
def fn(year: int) -> float:
|
||||
if year >= ramp_years:
|
||||
return end
|
||||
return start + span * (year / ramp_years)
|
||||
|
||||
return fn
|
||||
|
||||
|
||||
def static(allocation: float) -> GlideFn:
|
||||
"""Constant allocation, e.g. 60/40 = static(0.60)."""
|
||||
return lambda _year: allocation
|
||||
|
||||
|
||||
GLIDE_PATHS: dict[str, GlideFn] = {
|
||||
"rising": rising_equity(),
|
||||
"static_60_40": static(0.60),
|
||||
}
|
||||
|
||||
|
||||
def get(name: str) -> GlideFn:
|
||||
if name not in GLIDE_PATHS:
|
||||
raise KeyError(f"Unknown glide path: {name!r}. Known: {sorted(GLIDE_PATHS)}")
|
||||
return GLIDE_PATHS[name]
|
||||
Loading…
Add table
Add a link
Reference in a new issue