glidepath 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- glidepath/__init__.py +3 -0
- glidepath/app/__init__.py +364 -0
- glidepath/app/backtest.py +281 -0
- glidepath/app/charts.py +759 -0
- glidepath/app/copy.py +174 -0
- glidepath/app/display.py +148 -0
- glidepath/app/drawdown.py +436 -0
- glidepath/app/example.py +66 -0
- glidepath/app/exports.py +487 -0
- glidepath/app/files.py +249 -0
- glidepath/app/firstrun.py +114 -0
- glidepath/app/forms.py +1750 -0
- glidepath/app/inspector.py +506 -0
- glidepath/app/labels.py +66 -0
- glidepath/app/montecarlo.py +399 -0
- glidepath/app/plan.py +354 -0
- glidepath/app/retirement.py +446 -0
- glidepath/app/scenarios.py +831 -0
- glidepath/app/shell.py +185 -0
- glidepath/app/tables.py +138 -0
- glidepath/core/__init__.py +390 -0
- glidepath/core/annuities.py +240 -0
- glidepath/core/backtest.py +514 -0
- glidepath/core/comparison.py +278 -0
- glidepath/core/config.py +82 -0
- glidepath/core/contributions.py +337 -0
- glidepath/core/engine.py +2811 -0
- glidepath/core/entities.py +264 -0
- glidepath/core/glide.py +289 -0
- glidepath/core/investments.py +175 -0
- glidepath/core/money.py +107 -0
- glidepath/core/montecarlo.py +609 -0
- glidepath/core/pensions.py +298 -0
- glidepath/core/periods.py +367 -0
- glidepath/core/provenance.py +271 -0
- glidepath/core/randomness.py +128 -0
- glidepath/core/region.py +46 -0
- glidepath/core/reporting.py +231 -0
- glidepath/core/results.py +504 -0
- glidepath/core/retirement.py +291 -0
- glidepath/core/returns.py +312 -0
- glidepath/core/scenarios.py +579 -0
- glidepath/core/state_pension.py +264 -0
- glidepath/core/tax.py +139 -0
- glidepath/core/withdrawals.py +461 -0
- glidepath/core/wrappers.py +278 -0
- glidepath/gui/__init__.py +6 -0
- glidepath/gui/assets/icon_128.png +0 -0
- glidepath/gui/assets/icon_16.png +0 -0
- glidepath/gui/assets/icon_24.png +0 -0
- glidepath/gui/assets/icon_256.png +0 -0
- glidepath/gui/assets/icon_32.png +0 -0
- glidepath/gui/assets/icon_48.png +0 -0
- glidepath/gui/assets/icon_64.png +0 -0
- glidepath/gui/assets/wordmark.png +0 -0
- glidepath/gui/charts.py +829 -0
- glidepath/gui/forms.py +359 -0
- glidepath/gui/inspector.py +186 -0
- glidepath/gui/main.py +51 -0
- glidepath/gui/scenarios.py +402 -0
- glidepath/gui/style.py +376 -0
- glidepath/gui/tableview.py +67 -0
- glidepath/gui/widgets.py +989 -0
- glidepath/persistence/__init__.py +48 -0
- glidepath/persistence/assumptions.py +112 -0
- glidepath/persistence/decode.py +747 -0
- glidepath/persistence/document.py +101 -0
- glidepath/persistence/encode.py +433 -0
- glidepath/persistence/migrations.py +158 -0
- glidepath/persistence/values.py +298 -0
- glidepath/py.typed +0 -0
- glidepath/regions/__init__.py +7 -0
- glidepath/regions/uk/__init__.py +189 -0
- glidepath/regions/uk/ages.py +156 -0
- glidepath/regions/uk/contributions.py +717 -0
- glidepath/regions/uk/data/age_rules.toml +78 -0
- glidepath/regions/uk/data/assumptions_default.toml +170 -0
- glidepath/regions/uk/data/returns_history.toml +150 -0
- glidepath/regions/uk/data/tax_year_2026_27.toml +98 -0
- glidepath/regions/uk/extension.py +479 -0
- glidepath/regions/uk/loader.py +704 -0
- glidepath/regions/uk/region.py +160 -0
- glidepath/regions/uk/schema.py +563 -0
- glidepath/regions/uk/state_pension.py +129 -0
- glidepath/regions/uk/tax.py +466 -0
- glidepath/regions/uk/wrappers.py +283 -0
- glidepath/regions/uk/years.py +92 -0
- glidepath-0.2.0.dist-info/METADATA +189 -0
- glidepath-0.2.0.dist-info/RECORD +93 -0
- glidepath-0.2.0.dist-info/WHEEL +4 -0
- glidepath-0.2.0.dist-info/entry_points.txt +3 -0
- glidepath-0.2.0.dist-info/licenses/LICENSE +21 -0
- glidepath-0.2.0.dist-info/licenses/LICENSE-DATA +28 -0
glidepath/app/shell.py
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""Shell view models: everything a UI shell renders, as plain data (§4.7).
|
|
2
|
+
|
|
3
|
+
Shells (PySide6 today, possibly web later) bind these to widgets and
|
|
4
|
+
forward user actions back; they add no copy or formatting of their own.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from importlib import metadata
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
from glidepath.app.copy import (
|
|
12
|
+
ABOUT_TITLE,
|
|
13
|
+
APP_NAME,
|
|
14
|
+
DISCLAIMER_ACCEPT_LABEL,
|
|
15
|
+
DISCLAIMER_BODY,
|
|
16
|
+
DISCLAIMER_DECLINE_LABEL,
|
|
17
|
+
DISCLAIMER_TITLE,
|
|
18
|
+
HELP_GUIDE_INTRO,
|
|
19
|
+
HELP_GUIDE_SECTIONS,
|
|
20
|
+
HELP_GUIDE_TITLE,
|
|
21
|
+
HELP_MENU_LABEL,
|
|
22
|
+
)
|
|
23
|
+
from glidepath.app.exports import (
|
|
24
|
+
CASH_FLOW_FILE_FILTER,
|
|
25
|
+
CASH_FLOW_FILE_SUFFIX,
|
|
26
|
+
EXPORT_CASH_FLOW_DIALOG_TITLE,
|
|
27
|
+
EXPORT_CASH_FLOW_LABEL,
|
|
28
|
+
EXPORT_REPORT_DIALOG_TITLE,
|
|
29
|
+
EXPORT_REPORT_LABEL,
|
|
30
|
+
REPORT_FILE_FILTER,
|
|
31
|
+
REPORT_FILE_SUFFIX,
|
|
32
|
+
)
|
|
33
|
+
from glidepath.app.files import (
|
|
34
|
+
FILE_MENU_LABEL,
|
|
35
|
+
OPEN_DIALOG_TITLE,
|
|
36
|
+
OPEN_PLAN_LABEL,
|
|
37
|
+
PLAN_FILE_FILTER,
|
|
38
|
+
PLAN_FILE_SUFFIX,
|
|
39
|
+
QUIT_LABEL,
|
|
40
|
+
SAVE_DIALOG_TITLE,
|
|
41
|
+
SAVE_PLAN_AS_LABEL,
|
|
42
|
+
SAVE_PLAN_LABEL,
|
|
43
|
+
)
|
|
44
|
+
from glidepath.app.forms import FactsFormViewModel, build_facts_form_view_model
|
|
45
|
+
|
|
46
|
+
if TYPE_CHECKING:
|
|
47
|
+
from glidepath.app.firstrun import FirstRunState
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass(frozen=True)
|
|
51
|
+
class FileMenuViewModel:
|
|
52
|
+
"""The File menu: open, save, and export the plan (§4.5, 9.19)."""
|
|
53
|
+
|
|
54
|
+
menu_label: str
|
|
55
|
+
open_label: str
|
|
56
|
+
save_label: str
|
|
57
|
+
save_as_label: str
|
|
58
|
+
open_dialog_title: str
|
|
59
|
+
save_dialog_title: str
|
|
60
|
+
file_filter: str
|
|
61
|
+
file_suffix: str
|
|
62
|
+
export_cash_flow_label: str
|
|
63
|
+
export_cash_flow_dialog_title: str
|
|
64
|
+
export_cash_flow_filter: str
|
|
65
|
+
export_cash_flow_suffix: str
|
|
66
|
+
export_report_label: str
|
|
67
|
+
export_report_dialog_title: str
|
|
68
|
+
export_report_filter: str
|
|
69
|
+
export_report_suffix: str
|
|
70
|
+
quit_label: str
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass(frozen=True)
|
|
74
|
+
class DisclaimerViewModel:
|
|
75
|
+
"""The first-run disclaimer screen (§1 product requirement)."""
|
|
76
|
+
|
|
77
|
+
title: str
|
|
78
|
+
body: str
|
|
79
|
+
accept_label: str
|
|
80
|
+
decline_label: str
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass(frozen=True)
|
|
84
|
+
class AboutViewModel:
|
|
85
|
+
"""The About screen; repeats the disclaimer (§1 product requirement)."""
|
|
86
|
+
|
|
87
|
+
title: str
|
|
88
|
+
body: str
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass(frozen=True)
|
|
92
|
+
class HelpSectionViewModel:
|
|
93
|
+
"""One titled passage of the how-to-use guide."""
|
|
94
|
+
|
|
95
|
+
heading: str
|
|
96
|
+
body: str
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@dataclass(frozen=True)
|
|
100
|
+
class HelpGuideViewModel:
|
|
101
|
+
"""The how-to-use guide: what each part of the shell is for."""
|
|
102
|
+
|
|
103
|
+
title: str
|
|
104
|
+
intro: str
|
|
105
|
+
sections: tuple[HelpSectionViewModel, ...]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclass(frozen=True)
|
|
109
|
+
class ShellViewModel:
|
|
110
|
+
"""Top-level application shell."""
|
|
111
|
+
|
|
112
|
+
window_title: str
|
|
113
|
+
file_menu: FileMenuViewModel
|
|
114
|
+
help_menu_label: str
|
|
115
|
+
facts_tab_label: str
|
|
116
|
+
charts_tab_label: str
|
|
117
|
+
scenarios_tab_label: str
|
|
118
|
+
inspector_tab_label: str
|
|
119
|
+
disclaimer: DisclaimerViewModel
|
|
120
|
+
about: AboutViewModel
|
|
121
|
+
help_guide: HelpGuideViewModel
|
|
122
|
+
facts_form: FactsFormViewModel
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _installed_version() -> str:
|
|
126
|
+
"""The installed distribution version, or a placeholder outside an install."""
|
|
127
|
+
try:
|
|
128
|
+
return metadata.version(APP_NAME)
|
|
129
|
+
except metadata.PackageNotFoundError:
|
|
130
|
+
return "unknown"
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def build_shell_view_model() -> ShellViewModel:
|
|
134
|
+
"""Assemble the shell view model from canonical copy and metadata."""
|
|
135
|
+
return ShellViewModel(
|
|
136
|
+
window_title=APP_NAME,
|
|
137
|
+
file_menu=FileMenuViewModel(
|
|
138
|
+
menu_label=FILE_MENU_LABEL,
|
|
139
|
+
open_label=OPEN_PLAN_LABEL,
|
|
140
|
+
save_label=SAVE_PLAN_LABEL,
|
|
141
|
+
save_as_label=SAVE_PLAN_AS_LABEL,
|
|
142
|
+
open_dialog_title=OPEN_DIALOG_TITLE,
|
|
143
|
+
save_dialog_title=SAVE_DIALOG_TITLE,
|
|
144
|
+
file_filter=PLAN_FILE_FILTER,
|
|
145
|
+
file_suffix=PLAN_FILE_SUFFIX,
|
|
146
|
+
export_cash_flow_label=EXPORT_CASH_FLOW_LABEL,
|
|
147
|
+
export_cash_flow_dialog_title=EXPORT_CASH_FLOW_DIALOG_TITLE,
|
|
148
|
+
export_cash_flow_filter=CASH_FLOW_FILE_FILTER,
|
|
149
|
+
export_cash_flow_suffix=CASH_FLOW_FILE_SUFFIX,
|
|
150
|
+
export_report_label=EXPORT_REPORT_LABEL,
|
|
151
|
+
export_report_dialog_title=EXPORT_REPORT_DIALOG_TITLE,
|
|
152
|
+
export_report_filter=REPORT_FILE_FILTER,
|
|
153
|
+
export_report_suffix=REPORT_FILE_SUFFIX,
|
|
154
|
+
quit_label=QUIT_LABEL,
|
|
155
|
+
),
|
|
156
|
+
help_menu_label=HELP_MENU_LABEL,
|
|
157
|
+
facts_tab_label="Facts",
|
|
158
|
+
charts_tab_label="Charts",
|
|
159
|
+
scenarios_tab_label="Scenarios",
|
|
160
|
+
inspector_tab_label="Stated vs assumed",
|
|
161
|
+
disclaimer=DisclaimerViewModel(
|
|
162
|
+
title=DISCLAIMER_TITLE,
|
|
163
|
+
body=DISCLAIMER_BODY,
|
|
164
|
+
accept_label=DISCLAIMER_ACCEPT_LABEL,
|
|
165
|
+
decline_label=DISCLAIMER_DECLINE_LABEL,
|
|
166
|
+
),
|
|
167
|
+
about=AboutViewModel(
|
|
168
|
+
title=ABOUT_TITLE,
|
|
169
|
+
body=f"{APP_NAME} {_installed_version()}\n\n{DISCLAIMER_BODY}",
|
|
170
|
+
),
|
|
171
|
+
help_guide=HelpGuideViewModel(
|
|
172
|
+
title=HELP_GUIDE_TITLE,
|
|
173
|
+
intro=HELP_GUIDE_INTRO,
|
|
174
|
+
sections=tuple(
|
|
175
|
+
HelpSectionViewModel(heading=heading, body=body)
|
|
176
|
+
for heading, body in HELP_GUIDE_SECTIONS
|
|
177
|
+
),
|
|
178
|
+
),
|
|
179
|
+
facts_form=build_facts_form_view_model(),
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def should_show_disclaimer(state: FirstRunState) -> bool:
|
|
184
|
+
"""The disclaimer shows on every run until an acknowledgement is recorded."""
|
|
185
|
+
return state.disclaimer_acknowledged_on is None
|
glidepath/app/tables.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Text form for table-valued assumption overrides (planning §1, issue #70/#71).
|
|
2
|
+
|
|
3
|
+
Structured defaults are edited as plain text — one ``key = value``
|
|
4
|
+
line per figure, dotted keys for nested tables — so every shipped
|
|
5
|
+
default is overridable in place without a bespoke editor per table
|
|
6
|
+
shape. Serialising and parsing are exact inverses over the value
|
|
7
|
+
shapes the catalogue holds (planning §7): ``Decimal`` and ``int``
|
|
8
|
+
figures, string tags, and nested tables. Content validation is not
|
|
9
|
+
this module's job — the per-key policy parsers vet a parsed table
|
|
10
|
+
before it can reach the assumption set (:mod:`glidepath.app.plan`).
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
from collections.abc import Mapping
|
|
15
|
+
from decimal import Decimal, InvalidOperation
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from glidepath.regions.uk import AssumptionValue
|
|
20
|
+
|
|
21
|
+
_INT_PATTERN = re.compile(r"[+-]?\d+")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def table_edit_text(value: Mapping[str, object]) -> str:
|
|
25
|
+
"""The table as editable text: one ``key = value`` line per figure.
|
|
26
|
+
|
|
27
|
+
Nested tables flatten to dotted keys, so the text round-trips
|
|
28
|
+
through :func:`parse_table_text` for every shipped table shape.
|
|
29
|
+
"""
|
|
30
|
+
lines: list[str] = []
|
|
31
|
+
_serialise(lines, value, prefix="")
|
|
32
|
+
return "\n".join(lines)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _serialise(lines: list[str], table: Mapping[str, object], prefix: str) -> None:
|
|
36
|
+
"""Append one dotted-key line per leaf of ``table`` to ``lines``."""
|
|
37
|
+
for key, entry in table.items():
|
|
38
|
+
path = f"{prefix}{key}"
|
|
39
|
+
if isinstance(entry, Mapping):
|
|
40
|
+
_serialise(lines, entry, f"{path}.")
|
|
41
|
+
else:
|
|
42
|
+
lines.append(f"{path} = {_leaf_text(entry)}")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _leaf_text(entry: object) -> str:
|
|
46
|
+
"""One leaf value as text that re-parses to an equal figure.
|
|
47
|
+
|
|
48
|
+
An integral ``Decimal`` (e.g. a hand-edited plan's ``1``) would
|
|
49
|
+
otherwise serialise to bare-integer text and re-parse as ``int``,
|
|
50
|
+
which the policy parsers reject — so it gains a fractional digit,
|
|
51
|
+
keeping numeric equality.
|
|
52
|
+
"""
|
|
53
|
+
text = str(entry)
|
|
54
|
+
if isinstance(entry, Decimal) and _INT_PATTERN.fullmatch(text):
|
|
55
|
+
return f"{text}.0"
|
|
56
|
+
return text
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def parse_table_text(text: str) -> dict[str, AssumptionValue]:
|
|
60
|
+
"""Parse ``key = value`` lines back into a nested table.
|
|
61
|
+
|
|
62
|
+
Whole numbers parse as ``int``, other numerics as ``Decimal``, and
|
|
63
|
+
anything else as a string tag — the same convention the catalogue's
|
|
64
|
+
TOML loader applies, so a serialised default parses back exactly.
|
|
65
|
+
Blank lines are ignored.
|
|
66
|
+
|
|
67
|
+
Raises:
|
|
68
|
+
ValueError: On a line without ``=``, a missing value, an empty
|
|
69
|
+
key segment, or a duplicate/conflicting key path.
|
|
70
|
+
"""
|
|
71
|
+
table: dict[str, AssumptionValue] = {}
|
|
72
|
+
for line in text.splitlines():
|
|
73
|
+
stripped = line.strip()
|
|
74
|
+
if not stripped:
|
|
75
|
+
continue
|
|
76
|
+
key_text, separator, value_text = stripped.partition("=")
|
|
77
|
+
if not separator:
|
|
78
|
+
msg = f"line {stripped!r}: expected 'key = value'"
|
|
79
|
+
raise ValueError(msg)
|
|
80
|
+
raw_value = value_text.strip()
|
|
81
|
+
if not raw_value:
|
|
82
|
+
msg = f"line {stripped!r}: missing value"
|
|
83
|
+
raise ValueError(msg)
|
|
84
|
+
path = [segment.strip() for segment in key_text.strip().split(".")]
|
|
85
|
+
if not all(path):
|
|
86
|
+
msg = f"line {stripped!r}: empty key"
|
|
87
|
+
raise ValueError(msg)
|
|
88
|
+
_assign(table, path, _parsed_scalar(raw_value))
|
|
89
|
+
return table
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _assign(
|
|
93
|
+
table: dict[str, AssumptionValue], path: list[str], value: AssumptionValue
|
|
94
|
+
) -> None:
|
|
95
|
+
"""Set ``value`` at the dotted ``path`` in ``table``, refusing conflicts.
|
|
96
|
+
|
|
97
|
+
Raises:
|
|
98
|
+
ValueError: If the path duplicates an earlier line or mixes a
|
|
99
|
+
figure with a nested table at the same key.
|
|
100
|
+
"""
|
|
101
|
+
node = table
|
|
102
|
+
for depth, segment in enumerate(path[:-1], start=1):
|
|
103
|
+
child = node.setdefault(segment, {})
|
|
104
|
+
if not isinstance(child, dict):
|
|
105
|
+
# A user-text conflict, not a Python typing error, so the
|
|
106
|
+
# message must travel the ValueError rejection channel.
|
|
107
|
+
msg = f"key {'.'.join(path[:depth])!r} holds both a value and a table"
|
|
108
|
+
raise ValueError(msg) # noqa: TRY004
|
|
109
|
+
node = child
|
|
110
|
+
leaf = path[-1]
|
|
111
|
+
if leaf in node:
|
|
112
|
+
msg = f"duplicate key {'.'.join(path)!r}"
|
|
113
|
+
raise ValueError(msg)
|
|
114
|
+
node[leaf] = value
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _parsed_scalar(raw: str) -> AssumptionValue:
|
|
118
|
+
"""The typed value one line's text denotes.
|
|
119
|
+
|
|
120
|
+
Non-finite numerics (``Infinity``, ``NaN``) fall through as string
|
|
121
|
+
tags — the policy parsers reject them by type, so they can never
|
|
122
|
+
enter a table as figures.
|
|
123
|
+
"""
|
|
124
|
+
if _INT_PATTERN.fullmatch(raw):
|
|
125
|
+
return int(raw)
|
|
126
|
+
try:
|
|
127
|
+
value = Decimal(raw)
|
|
128
|
+
except InvalidOperation:
|
|
129
|
+
return raw
|
|
130
|
+
if not value.is_finite():
|
|
131
|
+
return raw
|
|
132
|
+
return value
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
__all__ = [
|
|
136
|
+
"parse_table_text",
|
|
137
|
+
"table_edit_text",
|
|
138
|
+
]
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
"""Region-agnostic core primitives (roadmap Phase 1).
|
|
2
|
+
|
|
3
|
+
The core defines value types, calendars, provenance kinds and entities.
|
|
4
|
+
It never imports region code (planning §4.2) — enforced by a guard test —
|
|
5
|
+
and never contains policy figures, which live in region data files.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from glidepath.core.annuities import (
|
|
9
|
+
AnnuityBasis,
|
|
10
|
+
AnnuityPurchase,
|
|
11
|
+
AnnuityRateTable,
|
|
12
|
+
AnnuityType,
|
|
13
|
+
annuity_base_rate_key,
|
|
14
|
+
annuity_start_date,
|
|
15
|
+
)
|
|
16
|
+
from glidepath.core.backtest import (
|
|
17
|
+
BacktestResult,
|
|
18
|
+
HistoricalSeries,
|
|
19
|
+
HistoricalWindowModel,
|
|
20
|
+
HistoricalYear,
|
|
21
|
+
WindowOutcome,
|
|
22
|
+
historical_window_factory,
|
|
23
|
+
run_windows,
|
|
24
|
+
)
|
|
25
|
+
from glidepath.core.comparison import (
|
|
26
|
+
BASE_RUN_NAME,
|
|
27
|
+
ComparisonRow,
|
|
28
|
+
PeriodMetrics,
|
|
29
|
+
ScenarioComparison,
|
|
30
|
+
ScenarioPeriodEntry,
|
|
31
|
+
compare_scenario_results,
|
|
32
|
+
run_scenarios,
|
|
33
|
+
)
|
|
34
|
+
from glidepath.core.config import EngineError, RunConfig, RunMode
|
|
35
|
+
from glidepath.core.contributions import (
|
|
36
|
+
AnnualAllowanceFunding,
|
|
37
|
+
AnnualAllowanceMeasurement,
|
|
38
|
+
AnnualAllowanceOutcome,
|
|
39
|
+
ContributionRuleset,
|
|
40
|
+
ContributionSchedule,
|
|
41
|
+
DbArrangementInput,
|
|
42
|
+
MemberContributionOutcome,
|
|
43
|
+
MemberContributionRequest,
|
|
44
|
+
SchemeInput,
|
|
45
|
+
SchemePayment,
|
|
46
|
+
)
|
|
47
|
+
from glidepath.core.engine import run
|
|
48
|
+
from glidepath.core.entities import (
|
|
49
|
+
EntityId,
|
|
50
|
+
Household,
|
|
51
|
+
Person,
|
|
52
|
+
PlannedOutflow,
|
|
53
|
+
Sex,
|
|
54
|
+
SpendingPlan,
|
|
55
|
+
TaxResidencyId,
|
|
56
|
+
new_entity_id,
|
|
57
|
+
validate_household_v1,
|
|
58
|
+
)
|
|
59
|
+
from glidepath.core.glide import (
|
|
60
|
+
GlidePathConfig,
|
|
61
|
+
GlidePathPoint,
|
|
62
|
+
LifeStage,
|
|
63
|
+
glide_path_from_shape,
|
|
64
|
+
years_to_target_retirement,
|
|
65
|
+
)
|
|
66
|
+
from glidepath.core.investments import (
|
|
67
|
+
AssetAllocation,
|
|
68
|
+
AssetReturns,
|
|
69
|
+
FeesAndGrowthOutcome,
|
|
70
|
+
FeeSchedule,
|
|
71
|
+
apply_fees_and_growth,
|
|
72
|
+
period_fee,
|
|
73
|
+
)
|
|
74
|
+
from glidepath.core.money import Money, Rate
|
|
75
|
+
from glidepath.core.montecarlo import (
|
|
76
|
+
MonteCarloResult,
|
|
77
|
+
PathOutcome,
|
|
78
|
+
PathParallelism,
|
|
79
|
+
SustainableIncomeSearch,
|
|
80
|
+
run_paths,
|
|
81
|
+
sustainable_income,
|
|
82
|
+
)
|
|
83
|
+
from glidepath.core.pensions import (
|
|
84
|
+
DBActiveMembership,
|
|
85
|
+
DBPension,
|
|
86
|
+
FactorTable,
|
|
87
|
+
RevaluationBasis,
|
|
88
|
+
RevaluationReference,
|
|
89
|
+
db_early_late_factor,
|
|
90
|
+
db_service_end_date,
|
|
91
|
+
db_start_date,
|
|
92
|
+
db_taken_age,
|
|
93
|
+
revaluation_factor_for_months,
|
|
94
|
+
)
|
|
95
|
+
from glidepath.core.periods import (
|
|
96
|
+
AgeRules,
|
|
97
|
+
AnnualCalendar,
|
|
98
|
+
FiscalCalendar,
|
|
99
|
+
Period,
|
|
100
|
+
add_months,
|
|
101
|
+
age_on,
|
|
102
|
+
birthday_in_year,
|
|
103
|
+
date_age_attained,
|
|
104
|
+
entitlement_active_fraction,
|
|
105
|
+
is_age_attained_by_period_start,
|
|
106
|
+
period_active_fraction,
|
|
107
|
+
prorata_fraction,
|
|
108
|
+
service_active_fraction,
|
|
109
|
+
whole_months_between,
|
|
110
|
+
)
|
|
111
|
+
from glidepath.core.provenance import (
|
|
112
|
+
Assumption,
|
|
113
|
+
AssumptionKey,
|
|
114
|
+
AssumptionReadRecorder,
|
|
115
|
+
AssumptionSet,
|
|
116
|
+
Decision,
|
|
117
|
+
Fact,
|
|
118
|
+
Provenance,
|
|
119
|
+
TrackedAssumptions,
|
|
120
|
+
decimal_assumption_value,
|
|
121
|
+
int_assumption_value,
|
|
122
|
+
mapping_assumption_value,
|
|
123
|
+
)
|
|
124
|
+
from glidepath.core.randomness import (
|
|
125
|
+
RandomSource,
|
|
126
|
+
SeededRandomSource,
|
|
127
|
+
derive_seed,
|
|
128
|
+
)
|
|
129
|
+
from glidepath.core.region import Region
|
|
130
|
+
from glidepath.core.reporting import (
|
|
131
|
+
PeriodReportRow,
|
|
132
|
+
ProjectionReport,
|
|
133
|
+
ReportBasis,
|
|
134
|
+
WrapperReportBalance,
|
|
135
|
+
build_report,
|
|
136
|
+
)
|
|
137
|
+
from glidepath.core.results import (
|
|
138
|
+
BalanceRollForward,
|
|
139
|
+
LabelledDecision,
|
|
140
|
+
LabelledFact,
|
|
141
|
+
PeriodSnapshot,
|
|
142
|
+
PersonPeriodResult,
|
|
143
|
+
ProjectionResult,
|
|
144
|
+
RunProvenance,
|
|
145
|
+
WrapperPeriodResult,
|
|
146
|
+
collect_plan_decisions,
|
|
147
|
+
collect_plan_facts,
|
|
148
|
+
)
|
|
149
|
+
from glidepath.core.retirement import (
|
|
150
|
+
RetirementAgeSearch,
|
|
151
|
+
earliest_retirement_age,
|
|
152
|
+
sustainable_income_at_age,
|
|
153
|
+
)
|
|
154
|
+
from glidepath.core.returns import (
|
|
155
|
+
DeterministicReturnModel,
|
|
156
|
+
PeriodReturns,
|
|
157
|
+
ReturnModel,
|
|
158
|
+
ReturnModelFactory,
|
|
159
|
+
StochasticReturnModel,
|
|
160
|
+
cholesky_lower,
|
|
161
|
+
nominal_rate,
|
|
162
|
+
)
|
|
163
|
+
from glidepath.core.scenarios import (
|
|
164
|
+
AppliedOverride,
|
|
165
|
+
AssumptionTarget,
|
|
166
|
+
DecisionTarget,
|
|
167
|
+
DecisionTargetInfo,
|
|
168
|
+
Override,
|
|
169
|
+
OverrideTarget,
|
|
170
|
+
Scenario,
|
|
171
|
+
ScenarioError,
|
|
172
|
+
ScenarioResolution,
|
|
173
|
+
decision_target_catalogue,
|
|
174
|
+
is_scenario_valid,
|
|
175
|
+
resolve_scenario,
|
|
176
|
+
scenario_orphans,
|
|
177
|
+
)
|
|
178
|
+
from glidepath.core.state_pension import (
|
|
179
|
+
StatePensionEntitlement,
|
|
180
|
+
StatePensionRecord,
|
|
181
|
+
StatePensionScheme,
|
|
182
|
+
StatePensionUprating,
|
|
183
|
+
UpratingRule,
|
|
184
|
+
deferral_months,
|
|
185
|
+
)
|
|
186
|
+
from glidepath.core.tax import TaxInput, TaxLine, TaxResult, TaxSystem
|
|
187
|
+
from glidepath.core.withdrawals import (
|
|
188
|
+
FixedPercentWithdrawalStrategy,
|
|
189
|
+
FixedRealWithdrawalStrategy,
|
|
190
|
+
GrossDraw,
|
|
191
|
+
GrossWithdrawalPlan,
|
|
192
|
+
GuardrailsWithdrawalStrategy,
|
|
193
|
+
NaturalYieldWithdrawalStrategy,
|
|
194
|
+
NetWithdrawalPlan,
|
|
195
|
+
TaxFreeCashStrategy,
|
|
196
|
+
WithdrawalPlan,
|
|
197
|
+
WithdrawalSource,
|
|
198
|
+
WithdrawalSourceId,
|
|
199
|
+
WithdrawalState,
|
|
200
|
+
WithdrawalStrategy,
|
|
201
|
+
tax_aware_order,
|
|
202
|
+
)
|
|
203
|
+
from glidepath.core.wrappers import (
|
|
204
|
+
ContributionCap,
|
|
205
|
+
ContributionTaxTreatment,
|
|
206
|
+
ContributionTerms,
|
|
207
|
+
GrowthTaxTreatment,
|
|
208
|
+
ReliefMechanic,
|
|
209
|
+
WithdrawalTaxTreatment,
|
|
210
|
+
Wrapper,
|
|
211
|
+
WrapperKindId,
|
|
212
|
+
WrapperRuleset,
|
|
213
|
+
WrapperTaxTreatment,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
__all__ = [
|
|
217
|
+
"BASE_RUN_NAME",
|
|
218
|
+
"AgeRules",
|
|
219
|
+
"AnnualAllowanceFunding",
|
|
220
|
+
"AnnualAllowanceMeasurement",
|
|
221
|
+
"AnnualAllowanceOutcome",
|
|
222
|
+
"AnnualCalendar",
|
|
223
|
+
"AnnuityBasis",
|
|
224
|
+
"AnnuityPurchase",
|
|
225
|
+
"AnnuityRateTable",
|
|
226
|
+
"AnnuityType",
|
|
227
|
+
"AppliedOverride",
|
|
228
|
+
"AssetAllocation",
|
|
229
|
+
"AssetReturns",
|
|
230
|
+
"Assumption",
|
|
231
|
+
"AssumptionKey",
|
|
232
|
+
"AssumptionReadRecorder",
|
|
233
|
+
"AssumptionSet",
|
|
234
|
+
"AssumptionTarget",
|
|
235
|
+
"BacktestResult",
|
|
236
|
+
"BalanceRollForward",
|
|
237
|
+
"ComparisonRow",
|
|
238
|
+
"ContributionCap",
|
|
239
|
+
"ContributionRuleset",
|
|
240
|
+
"ContributionSchedule",
|
|
241
|
+
"ContributionTaxTreatment",
|
|
242
|
+
"ContributionTerms",
|
|
243
|
+
"DBActiveMembership",
|
|
244
|
+
"DBPension",
|
|
245
|
+
"DbArrangementInput",
|
|
246
|
+
"Decision",
|
|
247
|
+
"DecisionTarget",
|
|
248
|
+
"DecisionTargetInfo",
|
|
249
|
+
"DeterministicReturnModel",
|
|
250
|
+
"EngineError",
|
|
251
|
+
"EntityId",
|
|
252
|
+
"Fact",
|
|
253
|
+
"FactorTable",
|
|
254
|
+
"FeeSchedule",
|
|
255
|
+
"FeesAndGrowthOutcome",
|
|
256
|
+
"FiscalCalendar",
|
|
257
|
+
"FixedPercentWithdrawalStrategy",
|
|
258
|
+
"FixedRealWithdrawalStrategy",
|
|
259
|
+
"GlidePathConfig",
|
|
260
|
+
"GlidePathPoint",
|
|
261
|
+
"GrossDraw",
|
|
262
|
+
"GrossWithdrawalPlan",
|
|
263
|
+
"GrowthTaxTreatment",
|
|
264
|
+
"GuardrailsWithdrawalStrategy",
|
|
265
|
+
"HistoricalSeries",
|
|
266
|
+
"HistoricalWindowModel",
|
|
267
|
+
"HistoricalYear",
|
|
268
|
+
"Household",
|
|
269
|
+
"LabelledDecision",
|
|
270
|
+
"LabelledFact",
|
|
271
|
+
"LifeStage",
|
|
272
|
+
"MemberContributionOutcome",
|
|
273
|
+
"MemberContributionRequest",
|
|
274
|
+
"Money",
|
|
275
|
+
"MonteCarloResult",
|
|
276
|
+
"NaturalYieldWithdrawalStrategy",
|
|
277
|
+
"NetWithdrawalPlan",
|
|
278
|
+
"Override",
|
|
279
|
+
"OverrideTarget",
|
|
280
|
+
"PathOutcome",
|
|
281
|
+
"PathParallelism",
|
|
282
|
+
"Period",
|
|
283
|
+
"PeriodMetrics",
|
|
284
|
+
"PeriodReportRow",
|
|
285
|
+
"PeriodReturns",
|
|
286
|
+
"PeriodSnapshot",
|
|
287
|
+
"Person",
|
|
288
|
+
"PersonPeriodResult",
|
|
289
|
+
"PlannedOutflow",
|
|
290
|
+
"ProjectionReport",
|
|
291
|
+
"ProjectionResult",
|
|
292
|
+
"Provenance",
|
|
293
|
+
"RandomSource",
|
|
294
|
+
"Rate",
|
|
295
|
+
"Region",
|
|
296
|
+
"ReliefMechanic",
|
|
297
|
+
"ReportBasis",
|
|
298
|
+
"RetirementAgeSearch",
|
|
299
|
+
"ReturnModel",
|
|
300
|
+
"ReturnModelFactory",
|
|
301
|
+
"RevaluationBasis",
|
|
302
|
+
"RevaluationReference",
|
|
303
|
+
"RunConfig",
|
|
304
|
+
"RunMode",
|
|
305
|
+
"RunProvenance",
|
|
306
|
+
"Scenario",
|
|
307
|
+
"ScenarioComparison",
|
|
308
|
+
"ScenarioError",
|
|
309
|
+
"ScenarioPeriodEntry",
|
|
310
|
+
"ScenarioResolution",
|
|
311
|
+
"SchemeInput",
|
|
312
|
+
"SchemePayment",
|
|
313
|
+
"SeededRandomSource",
|
|
314
|
+
"Sex",
|
|
315
|
+
"SpendingPlan",
|
|
316
|
+
"StatePensionEntitlement",
|
|
317
|
+
"StatePensionRecord",
|
|
318
|
+
"StatePensionScheme",
|
|
319
|
+
"StatePensionUprating",
|
|
320
|
+
"StochasticReturnModel",
|
|
321
|
+
"SustainableIncomeSearch",
|
|
322
|
+
"TaxFreeCashStrategy",
|
|
323
|
+
"TaxInput",
|
|
324
|
+
"TaxLine",
|
|
325
|
+
"TaxResidencyId",
|
|
326
|
+
"TaxResult",
|
|
327
|
+
"TaxSystem",
|
|
328
|
+
"TrackedAssumptions",
|
|
329
|
+
"UpratingRule",
|
|
330
|
+
"WindowOutcome",
|
|
331
|
+
"WithdrawalPlan",
|
|
332
|
+
"WithdrawalSource",
|
|
333
|
+
"WithdrawalSourceId",
|
|
334
|
+
"WithdrawalState",
|
|
335
|
+
"WithdrawalStrategy",
|
|
336
|
+
"WithdrawalTaxTreatment",
|
|
337
|
+
"Wrapper",
|
|
338
|
+
"WrapperKindId",
|
|
339
|
+
"WrapperPeriodResult",
|
|
340
|
+
"WrapperReportBalance",
|
|
341
|
+
"WrapperRuleset",
|
|
342
|
+
"WrapperTaxTreatment",
|
|
343
|
+
"add_months",
|
|
344
|
+
"age_on",
|
|
345
|
+
"annuity_base_rate_key",
|
|
346
|
+
"annuity_start_date",
|
|
347
|
+
"apply_fees_and_growth",
|
|
348
|
+
"birthday_in_year",
|
|
349
|
+
"build_report",
|
|
350
|
+
"cholesky_lower",
|
|
351
|
+
"collect_plan_decisions",
|
|
352
|
+
"collect_plan_facts",
|
|
353
|
+
"compare_scenario_results",
|
|
354
|
+
"date_age_attained",
|
|
355
|
+
"db_early_late_factor",
|
|
356
|
+
"db_service_end_date",
|
|
357
|
+
"db_start_date",
|
|
358
|
+
"db_taken_age",
|
|
359
|
+
"decimal_assumption_value",
|
|
360
|
+
"decision_target_catalogue",
|
|
361
|
+
"deferral_months",
|
|
362
|
+
"derive_seed",
|
|
363
|
+
"earliest_retirement_age",
|
|
364
|
+
"entitlement_active_fraction",
|
|
365
|
+
"glide_path_from_shape",
|
|
366
|
+
"historical_window_factory",
|
|
367
|
+
"int_assumption_value",
|
|
368
|
+
"is_age_attained_by_period_start",
|
|
369
|
+
"is_scenario_valid",
|
|
370
|
+
"mapping_assumption_value",
|
|
371
|
+
"new_entity_id",
|
|
372
|
+
"nominal_rate",
|
|
373
|
+
"period_active_fraction",
|
|
374
|
+
"period_fee",
|
|
375
|
+
"prorata_fraction",
|
|
376
|
+
"resolve_scenario",
|
|
377
|
+
"revaluation_factor_for_months",
|
|
378
|
+
"run",
|
|
379
|
+
"run_paths",
|
|
380
|
+
"run_scenarios",
|
|
381
|
+
"run_windows",
|
|
382
|
+
"scenario_orphans",
|
|
383
|
+
"service_active_fraction",
|
|
384
|
+
"sustainable_income",
|
|
385
|
+
"sustainable_income_at_age",
|
|
386
|
+
"tax_aware_order",
|
|
387
|
+
"validate_household_v1",
|
|
388
|
+
"whole_months_between",
|
|
389
|
+
"years_to_target_retirement",
|
|
390
|
+
]
|