owlplanner 2025.11.5__tar.gz → 2025.12.3__tar.gz
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.
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/PKG-INFO +23 -15
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/README.md +17 -10
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/RELEASE_NOTES.md +17 -1
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docker/README.md +1 -1
- owlplanner-2025.12.3/docker/build.cmd +7 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/examples/case_drawdowncalc-comparison-1.toml +2 -1
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/examples/case_jack+jill.toml +2 -1
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/examples/case_joe.toml +2 -1
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/examples/case_john+sally.toml +2 -1
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/examples/case_jon+jane.toml +2 -1
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/examples/case_kim+sam-bequest.toml +2 -1
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/examples/case_kim+sam-spending.toml +2 -1
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/notebooks/john+sally.ipynb +2 -2
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/notebooks/kim+sam.ipynb +2 -2
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/notebooks/template.ipynb +4 -4
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/notebooks/tutorial_1.ipynb +5 -5
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/notebooks/tutorial_2.ipynb +2 -2
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/notebooks/tutorial_3.ipynb +2 -2
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/pyproject.toml +9 -5
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/src/owlplanner/config.py +11 -8
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/src/owlplanner/plan.py +77 -33
- owlplanner-2025.12.3/src/owlplanner/socialsecurity.py +89 -0
- owlplanner-2025.12.3/src/owlplanner/version.py +1 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/tests/test_regressions.py +16 -6
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/tests/test_repro.py +14 -13
- owlplanner-2025.12.3/tests/test_socsec.py +72 -0
- owlplanner-2025.12.3/ui/AI +48 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/Create_Case.py +16 -4
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/Documentation.py +84 -42
- owlplanner-2025.12.3/ui/Fixed_Income.py +92 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/Quick_Start.py +13 -12
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/Wages_and_Contributions.py +2 -2
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/owlbridge.py +10 -5
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/sskeys.py +5 -2
- owlplanner-2025.11.5/src/owlplanner/version.py +0 -1
- owlplanner-2025.11.5/ui/Fixed_Income.py +0 -68
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/.devcontainer/devcontainer.json +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/.flake8 +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/.gitattributes +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/.github/workflows/github-actions-runtests.yml +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/.gitignore +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/.streamlit/config.toml +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/.streamlit/fullconfig.toml +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/INSTALL.md +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/LICENSE +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/USER_GUIDE.md +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docker/Dockerfile.bare +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docker/Dockerfile.static +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docker/buildentrypoint.sh +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docker/docker-compose.yml +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docker/runentrypoint.sh +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docs/images/AD-taxDef.png +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docs/images/AD-taxFree.png +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docs/images/AD-taxable.png +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docs/images/Hist_Bequest.png +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docs/images/Hist_Spending.png +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docs/images/MC-tutorial2a.png +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docs/images/MC-tutorial2b.png +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docs/images/OwlUI.png +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docs/images/allocations.png +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docs/images/owl.png +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docs/images/piecewiseConstant.png +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docs/images/profile.png +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docs/images/ratesCorrelations.png +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docs/images/ratesPlot.png +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docs/images/savingsPlot.png +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docs/images/sourcesPlot.png +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docs/images/spendingPlot.png +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docs/images/taxIncomePlot.png +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docs/images/taxesPlot.png +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docs/owl.pdf +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/docs/owl.tex +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/examples/jack+jill.xlsx +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/examples/joe.xlsx +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/examples/john+sally.xlsx +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/examples/jon+jane.xlsx +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/examples/kim+sam.xlsx +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/examples/template.xlsx +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/owlplanner.cmd +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/owlplanner.sh +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/pytest.ini +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/requirements.txt +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/src/owlplanner/__init__.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/src/owlplanner/abcapi.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/src/owlplanner/data/__init__.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/src/owlplanner/data/rates.csv +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/src/owlplanner/mylogging.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/src/owlplanner/plotting/__init__.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/src/owlplanner/plotting/base.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/src/owlplanner/plotting/factory.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/src/owlplanner/plotting/matplotlib_backend.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/src/owlplanner/plotting/plotly_backend.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/src/owlplanner/progress.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/src/owlplanner/rates.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/src/owlplanner/tax2025.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/src/owlplanner/tax2026.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/src/owlplanner/timelists.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/src/owlplanner/utils.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/tests/test_logger.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/tests/test_toml_cases.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/tests/test_ui_asset_allocation.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/tests/test_ui_compare_summaries.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/tests/test_ui_sskeys.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/tests/test_units.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/About_Owl.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/Asset_Allocation.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/Current_Assets.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/Graphs.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/Historical_Range.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/Logs.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/Monte_Carlo.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/Optimization_Parameters.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/Output_Files.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/README.md +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/Rates_Selection.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/Settings.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/Worksheets.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/__init__.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/main.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/progress.py +0 -0
- {owlplanner-2025.11.5 → owlplanner-2025.12.3}/ui/tomlexamples.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: owlplanner
|
|
3
|
-
Version: 2025.
|
|
4
|
-
Summary: Owl: Retirement planner with great wisdom
|
|
3
|
+
Version: 2025.12.3
|
|
4
|
+
Summary: Owl - Optimal Wealth Lab: Retirement planner with great wisdom
|
|
5
5
|
Project-URL: HomePage, https://github.com/mdlacasse/owl
|
|
6
6
|
Project-URL: Repository, https://github.com/mdlacasse/owl
|
|
7
7
|
Project-URL: Issues, https://github.com/mdlacasse/owl/issues
|
|
@@ -684,19 +684,20 @@ License: GNU GENERAL PUBLIC LICENSE
|
|
|
684
684
|
Public License instead of this License. But first, please read
|
|
685
685
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
|
686
686
|
License-File: LICENSE
|
|
687
|
-
Classifier: Development Status ::
|
|
687
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
688
688
|
Classifier: Intended Audience :: End Users/Desktop
|
|
689
689
|
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
690
690
|
Classifier: Operating System :: OS Independent
|
|
691
691
|
Classifier: Programming Language :: Python :: 3
|
|
692
692
|
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
693
|
-
Requires-Python: >=3.
|
|
693
|
+
Requires-Python: >=3.10
|
|
694
|
+
Requires-Dist: highspy
|
|
694
695
|
Requires-Dist: matplotlib
|
|
695
696
|
Requires-Dist: numpy
|
|
696
697
|
Requires-Dist: odfpy
|
|
697
698
|
Requires-Dist: openpyxl
|
|
698
699
|
Requires-Dist: pandas
|
|
699
|
-
Requires-Dist: plotly
|
|
700
|
+
Requires-Dist: plotly>=6.3
|
|
700
701
|
Requires-Dist: pulp
|
|
701
702
|
Requires-Dist: scipy
|
|
702
703
|
Requires-Dist: seaborn
|
|
@@ -721,16 +722,16 @@ Users can select varying return rates to perform historical back testing,
|
|
|
721
722
|
stochastic rates for performing Monte Carlo analyses,
|
|
722
723
|
or fixed rates either derived from historical averages, or set by the user.
|
|
723
724
|
|
|
724
|
-
There are
|
|
725
|
+
There are three ways to run Owl:
|
|
725
726
|
|
|
726
|
-
- Run Owl
|
|
727
|
+
- **Streamlit Hub:** Run Owl remotely as hosted on the Streamlit Community Server at
|
|
727
728
|
[owlplanner.streamlit.app](https://owlplanner.streamlit.app).
|
|
728
729
|
|
|
729
|
-
- Run locally on your computer using a Docker image.
|
|
730
|
-
Follow these [instructions](docker/README.md) for this option.
|
|
730
|
+
- **Docker Container:** Run Owl locally on your computer using a Docker image.
|
|
731
|
+
Follow these [instructions](docker/README.md) for using this option.
|
|
731
732
|
|
|
732
|
-
- Run locally on your computer using Python code and libraries.
|
|
733
|
-
Follow these [instructions](INSTALL.md) to install
|
|
733
|
+
- **Self-hosting:** Run Owl locally on your computer using Python code and libraries.
|
|
734
|
+
Follow these [instructions](INSTALL.md) to install from the source code and self-host on your own computer.
|
|
734
735
|
|
|
735
736
|
-------------------------------------------------------------------------------------
|
|
736
737
|
## Overview
|
|
@@ -750,13 +751,17 @@ your strategy under different market assumptions, helping to better understand y
|
|
|
750
751
|
|
|
751
752
|
-------------------------------------------------------------------------------------
|
|
752
753
|
## Purpose and vision
|
|
753
|
-
|
|
754
|
+
One goal of Owl is to provide a free and open-source ecosystem that has cutting-edge optimization capabilities,
|
|
754
755
|
allowing for the next generation of Python-literate retirees to experiment with their own financial future
|
|
755
|
-
while providing a codebase where they can learn and contribute.
|
|
756
|
+
while providing a codebase where they can learn and contribute. At the same time, an intuitive and easy-to-use
|
|
757
|
+
user interface based on Streamlit allows a broad set of users to benefit from the application as it only requires basic financial knowledge.
|
|
758
|
+
|
|
759
|
+
There are and were
|
|
756
760
|
good retirement optimizers in the recent past, but the vast majority of them are either proprietary platforms
|
|
757
761
|
collecting your data, or academic papers that share the results without really sharing the details of
|
|
758
762
|
the underlying mathematical models.
|
|
759
|
-
The algorithms in Owl rely on the open-source HiGHS linear programming solver
|
|
763
|
+
The algorithms in Owl rely on the open-source HiGHS linear programming solver but they have also been ported and tested on
|
|
764
|
+
other platforms such as Mosek and COIN-OR. The complete formulation and
|
|
760
765
|
detailed description of the underlying
|
|
761
766
|
mathematical model can be found [here](https://github.com/mdlacasse/Owl/blob/main/docs/owl.pdf).
|
|
762
767
|
|
|
@@ -844,13 +849,16 @@ They can also be optimized explicitly as an option, but this choice can lead to
|
|
|
844
849
|
due to the use of the many additional binary variables required by the formulation.
|
|
845
850
|
Future Medicare and IRMAA values are simple projections of current values with the assumed inflation rates.
|
|
846
851
|
|
|
852
|
+
Owl has a basic social security calculator that determines the actual benefits based on the individual's
|
|
853
|
+
primary insurance amount (PIA), full retirement age (FRA), and claiming age. Both
|
|
854
|
+
spousal's benefits and survivor's benefits are calculated for non-complex cases.
|
|
855
|
+
|
|
847
856
|
### Limitations
|
|
848
857
|
Owl is work in progress. At the current time:
|
|
849
858
|
- Only the US federal income tax is considered (and minimized through the optimization algorithm).
|
|
850
859
|
Head of household filing status has not been added but can easily be.
|
|
851
860
|
- Required minimum distributions are calculated, but tables for spouses more than 10 years apart are not included.
|
|
852
861
|
These cases are detected and will generate an error message.
|
|
853
|
-
- Social security rule for surviving spouse assumes that benefits were taken at full retirement age.
|
|
854
862
|
- Current version has no optimization of asset allocations between individuals and/or types of savings accounts.
|
|
855
863
|
If there is interest, that could be added in the future.
|
|
856
864
|
- In the current implementation, social securiy is always taxed at 85%, assuming that your taxable income will be larger than 34 k$ (single) or 44 k$ (married filing jointly).
|
|
@@ -15,16 +15,16 @@ Users can select varying return rates to perform historical back testing,
|
|
|
15
15
|
stochastic rates for performing Monte Carlo analyses,
|
|
16
16
|
or fixed rates either derived from historical averages, or set by the user.
|
|
17
17
|
|
|
18
|
-
There are
|
|
18
|
+
There are three ways to run Owl:
|
|
19
19
|
|
|
20
|
-
- Run Owl
|
|
20
|
+
- **Streamlit Hub:** Run Owl remotely as hosted on the Streamlit Community Server at
|
|
21
21
|
[owlplanner.streamlit.app](https://owlplanner.streamlit.app).
|
|
22
22
|
|
|
23
|
-
- Run locally on your computer using a Docker image.
|
|
24
|
-
Follow these [instructions](docker/README.md) for this option.
|
|
23
|
+
- **Docker Container:** Run Owl locally on your computer using a Docker image.
|
|
24
|
+
Follow these [instructions](docker/README.md) for using this option.
|
|
25
25
|
|
|
26
|
-
- Run locally on your computer using Python code and libraries.
|
|
27
|
-
Follow these [instructions](INSTALL.md) to install
|
|
26
|
+
- **Self-hosting:** Run Owl locally on your computer using Python code and libraries.
|
|
27
|
+
Follow these [instructions](INSTALL.md) to install from the source code and self-host on your own computer.
|
|
28
28
|
|
|
29
29
|
-------------------------------------------------------------------------------------
|
|
30
30
|
## Overview
|
|
@@ -44,13 +44,17 @@ your strategy under different market assumptions, helping to better understand y
|
|
|
44
44
|
|
|
45
45
|
-------------------------------------------------------------------------------------
|
|
46
46
|
## Purpose and vision
|
|
47
|
-
|
|
47
|
+
One goal of Owl is to provide a free and open-source ecosystem that has cutting-edge optimization capabilities,
|
|
48
48
|
allowing for the next generation of Python-literate retirees to experiment with their own financial future
|
|
49
|
-
while providing a codebase where they can learn and contribute.
|
|
49
|
+
while providing a codebase where they can learn and contribute. At the same time, an intuitive and easy-to-use
|
|
50
|
+
user interface based on Streamlit allows a broad set of users to benefit from the application as it only requires basic financial knowledge.
|
|
51
|
+
|
|
52
|
+
There are and were
|
|
50
53
|
good retirement optimizers in the recent past, but the vast majority of them are either proprietary platforms
|
|
51
54
|
collecting your data, or academic papers that share the results without really sharing the details of
|
|
52
55
|
the underlying mathematical models.
|
|
53
|
-
The algorithms in Owl rely on the open-source HiGHS linear programming solver
|
|
56
|
+
The algorithms in Owl rely on the open-source HiGHS linear programming solver but they have also been ported and tested on
|
|
57
|
+
other platforms such as Mosek and COIN-OR. The complete formulation and
|
|
54
58
|
detailed description of the underlying
|
|
55
59
|
mathematical model can be found [here](https://github.com/mdlacasse/Owl/blob/main/docs/owl.pdf).
|
|
56
60
|
|
|
@@ -138,13 +142,16 @@ They can also be optimized explicitly as an option, but this choice can lead to
|
|
|
138
142
|
due to the use of the many additional binary variables required by the formulation.
|
|
139
143
|
Future Medicare and IRMAA values are simple projections of current values with the assumed inflation rates.
|
|
140
144
|
|
|
145
|
+
Owl has a basic social security calculator that determines the actual benefits based on the individual's
|
|
146
|
+
primary insurance amount (PIA), full retirement age (FRA), and claiming age. Both
|
|
147
|
+
spousal's benefits and survivor's benefits are calculated for non-complex cases.
|
|
148
|
+
|
|
141
149
|
### Limitations
|
|
142
150
|
Owl is work in progress. At the current time:
|
|
143
151
|
- Only the US federal income tax is considered (and minimized through the optimization algorithm).
|
|
144
152
|
Head of household filing status has not been added but can easily be.
|
|
145
153
|
- Required minimum distributions are calculated, but tables for spouses more than 10 years apart are not included.
|
|
146
154
|
These cases are detected and will generate an error message.
|
|
147
|
-
- Social security rule for surviving spouse assumes that benefits were taken at full retirement age.
|
|
148
155
|
- Current version has no optimization of asset allocations between individuals and/or types of savings accounts.
|
|
149
156
|
If there is interest, that could be added in the future.
|
|
150
157
|
- In the current implementation, social securiy is always taxed at 85%, assuming that your taxable income will be larger than 34 k$ (single) or 44 k$ (married filing jointly).
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
### Version 2025.12.03
|
|
2
|
+
- Coded social security to use monthly PIA instead of annual amount
|
|
3
|
+
- Added exact routines for FRA and increase/decrease factors due to claiming age
|
|
4
|
+
- Added exact spousal benefits
|
|
5
|
+
- Adjusted documentation for social security
|
|
6
|
+
- Added birth month for more precise calculation on first year of social security
|
|
7
|
+
- Added month to age for claiming social security
|
|
8
|
+
|
|
9
|
+
### Version 2025.11.29
|
|
10
|
+
- Fixed social security for survivor
|
|
11
|
+
- Enhanced documentation for SS amounts
|
|
12
|
+
|
|
13
|
+
### Version 2025.11.09
|
|
14
|
+
- Moved development status to production/stable in pyproject
|
|
15
|
+
- Made version propagate everywhere needed
|
|
16
|
+
- Added node limit on milp to avoid Streamlit server shutdown on memory consumption
|
|
17
|
+
|
|
1
18
|
### Version 2025.11.05
|
|
2
19
|
- Mentioning Owl as Optimal Wealth Lab
|
|
3
20
|
- Port to Streamlit 1.50 which broke many widgets
|
|
@@ -5,7 +22,6 @@
|
|
|
5
22
|
- Rework Docker to smaller Alpine image and fix docs
|
|
6
23
|
|
|
7
24
|
### Version 2025.07.01
|
|
8
|
-
|
|
9
25
|
Added:
|
|
10
26
|
- Settings option for menu position thanks to Streamlit 1.46 top and sidebar capabilities. Default is top.
|
|
11
27
|
- Net Investment Income Tax calculations in self-consistent loop.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
This document describes how to run Owl using a Docker container.
|
|
10
10
|
|
|
11
11
|
------------------------------------------------------------------------------------
|
|
12
|
-
###
|
|
12
|
+
### Running Owl without the source code
|
|
13
13
|
Using this approach only requires downloading the Docker image from
|
|
14
14
|
the [Docker Hub](http://hub.docker.com) and having the [Docker](http://docker.com)
|
|
15
15
|
application installed on your computer.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
::
|
|
2
|
+
:: A simple script to build both Docker images
|
|
3
|
+
::
|
|
4
|
+
docker build --no-cache -f Dockerfile.bare -t owlplanner/owldocker.bare:latest .
|
|
5
|
+
docker push owlplanner/owldocker.bare
|
|
6
|
+
docker build --no-cache -f Dockerfile.static -t owlplanner/owldocker.static:latest .
|
|
7
|
+
docker push owlplanner/owldocker.static
|
|
@@ -5,6 +5,7 @@ Description = "This is a case involving a single individual. Case is used for co
|
|
|
5
5
|
Status = "single"
|
|
6
6
|
Names = [ "Charles",]
|
|
7
7
|
"Birth year" = [ 1966,]
|
|
8
|
+
"Birth month" = [ 1,]
|
|
8
9
|
"Life expectancy" = [ 89,]
|
|
9
10
|
"Start date" = "2025-01-01"
|
|
10
11
|
|
|
@@ -20,7 +21,7 @@ Names = [ "Charles",]
|
|
|
20
21
|
"Pension amounts" = [ 0.0,]
|
|
21
22
|
"Pension ages" = [ 65,]
|
|
22
23
|
"Pension indexed" = [ true,]
|
|
23
|
-
"Social security amounts" = [
|
|
24
|
+
"Social security PIA amounts" = [ 3000,]
|
|
24
25
|
"Social security ages" = [ 70,]
|
|
25
26
|
|
|
26
27
|
["Rates Selection"]
|
|
@@ -5,6 +5,7 @@ Description = "This example aims to demonstrate some of Owl's capabilities. Jack
|
|
|
5
5
|
Status = "married"
|
|
6
6
|
Names = [ "Jack", "Jill",]
|
|
7
7
|
"Birth year" = [ 1962, 1965,]
|
|
8
|
+
"Birth month" = [ 1, 1,]
|
|
8
9
|
"Life expectancy" = [ 89, 92,]
|
|
9
10
|
"Start date" = "01-01"
|
|
10
11
|
|
|
@@ -22,7 +23,7 @@ Names = [ "Jack", "Jill",]
|
|
|
22
23
|
"Pension amounts" = [ 0.0, 10.5,]
|
|
23
24
|
"Pension ages" = [ 65, 65,]
|
|
24
25
|
"Pension indexed" = [ false, false,]
|
|
25
|
-
"Social security amounts" = [
|
|
26
|
+
"Social security PIA amounts" = [ 2360, 1642,]
|
|
26
27
|
"Social security ages" = [ 70, 62,]
|
|
27
28
|
|
|
28
29
|
["Rates Selection"]
|
|
@@ -5,6 +5,7 @@ Description = "This is an example of a case involving a single individual. Joe i
|
|
|
5
5
|
Status = "single"
|
|
6
6
|
Names = [ "Joe",]
|
|
7
7
|
"Birth year" = [ 1966,]
|
|
8
|
+
"Birth month" = [ 1,]
|
|
8
9
|
"Life expectancy" = [ 89,]
|
|
9
10
|
"Start date" = "01-01"
|
|
10
11
|
|
|
@@ -20,7 +21,7 @@ Names = [ "Joe",]
|
|
|
20
21
|
"Pension amounts" = [ 18.0,]
|
|
21
22
|
"Pension ages" = [ 65,]
|
|
22
23
|
"Pension indexed" = [ true,]
|
|
23
|
-
"Social security amounts" = [
|
|
24
|
+
"Social security PIA amounts" = [ 2360,]
|
|
24
25
|
"Social security ages" = [ 67,]
|
|
25
26
|
|
|
26
27
|
["Rates Selection"]
|
|
@@ -5,6 +5,7 @@ Description = "This example reproduces the case of John and Sally, discussed by
|
|
|
5
5
|
Status = "married"
|
|
6
6
|
Names = [ "John", "Sally",]
|
|
7
7
|
"Birth year" = [ 1962, 1962,]
|
|
8
|
+
"Birth month" = [ 1, 1,]
|
|
8
9
|
"Life expectancy" = [ 92, 92,]
|
|
9
10
|
"Start date" = "01-01"
|
|
10
11
|
|
|
@@ -22,7 +23,7 @@ Names = [ "John", "Sally",]
|
|
|
22
23
|
"Pension amounts" = [ 0.0, 0.0,]
|
|
23
24
|
"Pension ages" = [ 65, 65,]
|
|
24
25
|
"Pension indexed" = [ false, false,]
|
|
25
|
-
"Social security amounts" = [
|
|
26
|
+
"Social security PIA amounts" = [ 3000, 1800,]
|
|
26
27
|
"Social security ages" = [ 67, 67,]
|
|
27
28
|
|
|
28
29
|
["Rates Selection"]
|
|
@@ -5,6 +5,7 @@ Description = "This case reproduces a similar case discussed a while back on i-o
|
|
|
5
5
|
Status = "married"
|
|
6
6
|
Names = [ "Jon", "Jane",]
|
|
7
7
|
"Birth year" = [ 1965, 1968,]
|
|
8
|
+
"Birth month" = [ 1, 1,]
|
|
8
9
|
"Life expectancy" = [ 92, 92,]
|
|
9
10
|
"Start date" = "01-01"
|
|
10
11
|
|
|
@@ -22,7 +23,7 @@ Names = [ "Jon", "Jane",]
|
|
|
22
23
|
"Pension amounts" = [ 0.0, 0.0,]
|
|
23
24
|
"Pension ages" = [ 65, 65,]
|
|
24
25
|
"Pension indexed" = [ false, false,]
|
|
25
|
-
"Social security amounts" = [
|
|
26
|
+
"Social security PIA amounts" = [ 1750, 1750,]
|
|
26
27
|
"Social security ages" = [ 65, 65,]
|
|
27
28
|
|
|
28
29
|
["Rates Selection"]
|
|
@@ -5,6 +5,7 @@ Description = "This is the case of Kim and Sam used as an example case for optim
|
|
|
5
5
|
Status = "married"
|
|
6
6
|
Names = [ "Kim", "Sam",]
|
|
7
7
|
"Birth year" = [ 1966, 1967,]
|
|
8
|
+
"Birth month" = [ 1, 1,]
|
|
8
9
|
"Life expectancy" = [ 86, 89,]
|
|
9
10
|
"Start date" = "01-01"
|
|
10
11
|
|
|
@@ -22,7 +23,7 @@ Names = [ "Kim", "Sam",]
|
|
|
22
23
|
"Pension amounts" = [ 0.0, 0.0,]
|
|
23
24
|
"Pension ages" = [ 65, 65,]
|
|
24
25
|
"Pension indexed" = [ false, false,]
|
|
25
|
-
"Social security amounts" = [
|
|
26
|
+
"Social security PIA amounts" = [ 3750, 2083,]
|
|
26
27
|
"Social security ages" = [ 70, 68,]
|
|
27
28
|
|
|
28
29
|
["Rates Selection"]
|
|
@@ -5,6 +5,7 @@ Description = "This is the case of Kim and Sam used as an example case for optim
|
|
|
5
5
|
Status = "married"
|
|
6
6
|
Names = [ "Kim", "Sam",]
|
|
7
7
|
"Birth year" = [ 1966, 1967,]
|
|
8
|
+
"Birth month" = [ 1, 1,]
|
|
8
9
|
"Life expectancy" = [ 86, 89,]
|
|
9
10
|
"Start date" = "01-01"
|
|
10
11
|
|
|
@@ -22,7 +23,7 @@ Names = [ "Kim", "Sam",]
|
|
|
22
23
|
"Pension amounts" = [ 0.0, 0.0,]
|
|
23
24
|
"Pension ages" = [ 65, 65,]
|
|
24
25
|
"Pension indexed" = [ false, false,]
|
|
25
|
-
"Social security amounts" = [
|
|
26
|
+
"Social security PIA amounts" = [ 3750, 2083,]
|
|
26
27
|
"Social security ages" = [ 70, 68,]
|
|
27
28
|
|
|
28
29
|
["Rates Selection"]
|
|
@@ -41,14 +41,14 @@
|
|
|
41
41
|
"%%time\n",
|
|
42
42
|
"import owlplanner as owl\n",
|
|
43
43
|
"\n",
|
|
44
|
-
"plan = owl.Plan(['John', 'Sally'], [1962, 1962], [92, 92], 'john+sally')\n",
|
|
44
|
+
"plan = owl.Plan(['John', 'Sally'], [1962, 1962], [1, 1], [92, 92], 'john+sally')\n",
|
|
45
45
|
"# plan.setPlotBackend(\"plotly\")\n",
|
|
46
46
|
"plan.setAccountBalances(taxable=[200, 200], taxDeferred=[750, 750], taxFree=[50, 50])\n",
|
|
47
47
|
"# Unrealistic empty contributions and wages\n",
|
|
48
48
|
"plan.readContributions('../examples/john+sally.xlsx')\n",
|
|
49
49
|
"# plan.setInterpolationMethod('s-curve')\n",
|
|
50
50
|
"plan.setAllocationRatios('individual', generic=[[[60, 40, 0, 0], [60, 40, 0, 0]], [[60, 40, 0, 0], [60, 40, 0, 0]]])\n",
|
|
51
|
-
"plan.setSocialSecurity([
|
|
51
|
+
"plan.setSocialSecurity([3000, 1800], [67, 67])\n",
|
|
52
52
|
"#plan.setSpendingProfile('smile')\n",
|
|
53
53
|
"plan.setSpendingProfile('flat')\n",
|
|
54
54
|
"plan.setRates('historical average', 1990, 2023)\n",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"outputs": [],
|
|
28
28
|
"source": [
|
|
29
29
|
"import owlplanner as owl\n",
|
|
30
|
-
"p = owl.Plan(['Kim', 'Sam'], [1966, 1967], [86, 89], 'kim+sam-spending', verbose=True)\n",
|
|
30
|
+
"p = owl.Plan(['Kim', 'Sam'], [1966, 1967], [1, 1], [86, 89], 'kim+sam-spending', verbose=True)\n",
|
|
31
31
|
"# p.setPlotBackend(\"plotly\")"
|
|
32
32
|
]
|
|
33
33
|
},
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"outputs": [],
|
|
40
40
|
"source": [
|
|
41
41
|
"# p.setPension([0, 0], [65, 65])\n",
|
|
42
|
-
"p.setSocialSecurity([
|
|
42
|
+
"p.setSocialSecurity([3750, 2083], [70, 68])"
|
|
43
43
|
]
|
|
44
44
|
},
|
|
45
45
|
{
|
|
@@ -135,7 +135,7 @@
|
|
|
135
135
|
"metadata": {},
|
|
136
136
|
"outputs": [],
|
|
137
137
|
"source": [
|
|
138
|
-
"plan = owl.Plan(['Kim', 'Sam'], [YYYY, YYYY], [AA, AA], 'Kim+Sam-spending', verbose=True)"
|
|
138
|
+
"plan = owl.Plan(['Kim', 'Sam'], [YYYY, YYYY], [MM, MM], [AA, AA], 'Kim+Sam-spending', verbose=True)"
|
|
139
139
|
]
|
|
140
140
|
},
|
|
141
141
|
{
|
|
@@ -340,13 +340,13 @@
|
|
|
340
340
|
"metadata": {},
|
|
341
341
|
"source": [
|
|
342
342
|
"## What about anticipated fixed income?\n",
|
|
343
|
-
"Pension and social security are fixed income.
|
|
343
|
+
"Pension and social security are fixed income. Pension income can be adjusted for inflation as social security benefits are. Numbers to be provided are the predicted annual amount for each spouse and the age of the commencement of benefits.\n",
|
|
344
344
|
"\n",
|
|
345
345
|
"By default, no pension benefits are assumed. This can also be specified explicitly by entering zeros (0) as entries, as in\n",
|
|
346
346
|
"\n",
|
|
347
|
-
" plan.setPension([0, 0], [65, 65])\n",
|
|
347
|
+
" plan.setPension([0, 0], [65, 65], [False, False])\n",
|
|
348
348
|
" \n",
|
|
349
|
-
"For social security, one must provide the predicted
|
|
349
|
+
"For social security, one must provide the predicted monthly Primary Insurance Amount(s) and the starting age(s) at which benefits are anticipated to be received. There are plenty of social security benefit estimators on the web, including the info you can get directly from your own account at the Social Security Administration (ssa.gov). Another interesting calculator can be found at www.opensocialsecurity.com. This calculator allows you to compare different scenarios regarding your commencement age through a sensitivity plot.\n"
|
|
350
350
|
]
|
|
351
351
|
},
|
|
352
352
|
{
|
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
"metadata": {},
|
|
134
134
|
"outputs": [],
|
|
135
135
|
"source": [
|
|
136
|
-
"plan = owl.Plan(['Jack', 'Jill'], [1962, 1965], [89, 92], 'jack+jill-spending-69')"
|
|
136
|
+
"plan = owl.Plan(['Jack', 'Jill'], [1962, 1965], [1, 1], [89, 92], 'jack+jill-spending-69')"
|
|
137
137
|
]
|
|
138
138
|
},
|
|
139
139
|
{
|
|
@@ -332,13 +332,13 @@
|
|
|
332
332
|
"metadata": {},
|
|
333
333
|
"source": [
|
|
334
334
|
"## What about anticipated fixed income?\n",
|
|
335
|
-
"Pension and social security are fixed income.
|
|
335
|
+
"Pension and social security are fixed income. Pension income can be inflation adjusted as social security benefits are. Numbers to be provided are the predicted annual amount for each spouse and the age of the commencement of benefits. Values are expressed in today's dollars (as do statements from the Social Security Administration).\n",
|
|
336
336
|
"\n",
|
|
337
337
|
"By default, no pension benefits are assumed. This can also be specified explicitly by entering zeros (0) as entries, as in\n",
|
|
338
338
|
"\n",
|
|
339
339
|
" plan.setPension([0, 0], [65, 65])\n",
|
|
340
340
|
" \n",
|
|
341
|
-
"For social security, one must provide the predicted
|
|
341
|
+
"For social security, one must provide the predicted monthly Primary Insurance Amount(s) and the starting age(s) at which benefits are anticipated to be received. There are plenty of social security benefit estimators on the web, including the info you can get directly from your own account at the Social Security Administration (ssa.gov). Another interesting calculator can be found at www.opensocialsecurity.com. This calculator allows you to compare different scenarios regarding your commencement age through a sensitivity plot.\n"
|
|
342
342
|
]
|
|
343
343
|
},
|
|
344
344
|
{
|
|
@@ -346,7 +346,7 @@
|
|
|
346
346
|
"id": "43bd9a2a-e2bf-434f-88ac-b0a70dcdd1fd",
|
|
347
347
|
"metadata": {},
|
|
348
348
|
"source": [
|
|
349
|
-
"Here, Jill has an unindexed pension of \\\\$10 k per year. Both Jack and Jill believe they have good genes and decided to take their social security benefits at age 70. The amounts provided (28k\\\\$ and 25k\\\\$) are estimation of the amounts they would receive at
|
|
349
|
+
"Here, Jill has an unindexed pension of \\\\$10 k per year. Both Jack and Jill believe they have good genes and decided to take their social security benefits at age 70. The amounts provided (28k\\\\$ and 25k\\\\$) are estimation of the amounts they would receive at Full Retirement Age (FRA)."
|
|
350
350
|
]
|
|
351
351
|
},
|
|
352
352
|
{
|
|
@@ -357,7 +357,7 @@
|
|
|
357
357
|
"outputs": [],
|
|
358
358
|
"source": [
|
|
359
359
|
"plan.setPension([0, 10], [65, 65])\n",
|
|
360
|
-
"plan.setSocialSecurity([
|
|
360
|
+
"plan.setSocialSecurity([2333, 2083], [70, 70])"
|
|
361
361
|
]
|
|
362
362
|
},
|
|
363
363
|
{
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"metadata": {},
|
|
96
96
|
"outputs": [],
|
|
97
97
|
"source": [
|
|
98
|
-
"plan = owl.Plan(['Jack', 'Jill'], [1962, 1965], [89, 92], 'jack+jill-spending-MC', verbose=True)"
|
|
98
|
+
"plan = owl.Plan(['Jack', 'Jill'], [1962, 1965], [1, 1], [89, 92], 'jack+jill-spending-MC', verbose=True)"
|
|
99
99
|
]
|
|
100
100
|
},
|
|
101
101
|
{
|
|
@@ -145,7 +145,7 @@
|
|
|
145
145
|
"outputs": [],
|
|
146
146
|
"source": [
|
|
147
147
|
"plan.setPension([0, 10], [65, 65])\n",
|
|
148
|
-
"plan.setSocialSecurity([
|
|
148
|
+
"plan.setSocialSecurity([2333, 2083], [70, 70])"
|
|
149
149
|
]
|
|
150
150
|
},
|
|
151
151
|
{
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"metadata": {},
|
|
96
96
|
"outputs": [],
|
|
97
97
|
"source": [
|
|
98
|
-
"plan = owl.Plan(['Jack', 'Jill'], [1962, 1965], [89, 92], 'jack+jill-tutorial3', verbose=True)"
|
|
98
|
+
"plan = owl.Plan(['Jack', 'Jill'], [1962, 1965], [1, 1], [89, 92], 'jack+jill-tutorial3', verbose=True)"
|
|
99
99
|
]
|
|
100
100
|
},
|
|
101
101
|
{
|
|
@@ -145,7 +145,7 @@
|
|
|
145
145
|
"outputs": [],
|
|
146
146
|
"source": [
|
|
147
147
|
"plan.setPension([0, 10], [65, 65])\n",
|
|
148
|
-
"plan.setSocialSecurity([
|
|
148
|
+
"plan.setSocialSecurity([2333, 2083], [70, 70])"
|
|
149
149
|
]
|
|
150
150
|
},
|
|
151
151
|
{
|
|
@@ -2,20 +2,23 @@
|
|
|
2
2
|
requires = ["hatchling"]
|
|
3
3
|
build-backend = "hatchling.build"
|
|
4
4
|
|
|
5
|
+
[tool.hatch.version]
|
|
6
|
+
path = "src/owlplanner/version.py"
|
|
7
|
+
|
|
5
8
|
[project]
|
|
6
9
|
name = "owlplanner"
|
|
7
|
-
|
|
10
|
+
dynamic = ["version"]
|
|
8
11
|
authors = [
|
|
9
12
|
{ name="Martin-D. Lacasse", email="martin.d.lacasse@gmail.com" },
|
|
10
13
|
]
|
|
11
14
|
maintainers = [
|
|
12
15
|
{ name="Martin-D. Lacasse", email="martin.d.lacasse@gmail.com" },
|
|
13
16
|
]
|
|
14
|
-
description = "Owl: Retirement planner with great wisdom"
|
|
17
|
+
description = "Owl - Optimal Wealth Lab: Retirement planner with great wisdom"
|
|
15
18
|
readme = "README.md"
|
|
16
|
-
requires-python = ">=3.
|
|
19
|
+
requires-python = ">=3.10"
|
|
17
20
|
classifiers = [
|
|
18
|
-
"Development Status ::
|
|
21
|
+
"Development Status :: 5 - Production/Stable",
|
|
19
22
|
"Programming Language :: Python :: 3",
|
|
20
23
|
"Intended Audience :: End Users/Desktop",
|
|
21
24
|
"Topic :: Office/Business :: Financial :: Investment",
|
|
@@ -24,12 +27,13 @@ classifiers = [
|
|
|
24
27
|
"Programming Language :: Python :: 3",
|
|
25
28
|
]
|
|
26
29
|
dependencies = [
|
|
30
|
+
"highspy",
|
|
27
31
|
"matplotlib",
|
|
28
32
|
"numpy",
|
|
29
33
|
"odfpy",
|
|
30
34
|
"openpyxl",
|
|
31
35
|
"pandas",
|
|
32
|
-
"plotly",
|
|
36
|
+
"plotly>=6.3",
|
|
33
37
|
"pulp",
|
|
34
38
|
"seaborn",
|
|
35
39
|
"scipy",
|
|
@@ -37,6 +37,7 @@ def saveConfig(myplan, file, mylog):
|
|
|
37
37
|
"Status": ["unknown", "single", "married"][myplan.N_i],
|
|
38
38
|
"Names": myplan.inames,
|
|
39
39
|
"Birth year": myplan.yobs.tolist(),
|
|
40
|
+
"Birth month": myplan.mobs.tolist(),
|
|
40
41
|
"Life expectancy": myplan.expectancy.tolist(),
|
|
41
42
|
"Start date": myplan.startDate,
|
|
42
43
|
}
|
|
@@ -55,10 +56,10 @@ def saveConfig(myplan, file, mylog):
|
|
|
55
56
|
|
|
56
57
|
# Fixed Income.
|
|
57
58
|
diconf["Fixed Income"] = {
|
|
58
|
-
"Pension amounts": (myplan.pensionAmounts
|
|
59
|
+
"Pension monthly amounts": (myplan.pensionAmounts).tolist(),
|
|
59
60
|
"Pension ages": myplan.pensionAges.tolist(),
|
|
60
61
|
"Pension indexed": myplan.pensionIsIndexed,
|
|
61
|
-
"Social security amounts": (myplan.ssecAmounts
|
|
62
|
+
"Social security PIA amounts": (myplan.ssecAmounts).tolist(),
|
|
62
63
|
"Social security ages": myplan.ssecAges.tolist(),
|
|
63
64
|
}
|
|
64
65
|
|
|
@@ -181,11 +182,13 @@ def readConfig(file, *, verbose=True, logstreams=None, readContributions=True):
|
|
|
181
182
|
inames = diconf["Basic Info"]["Names"]
|
|
182
183
|
# status = diconf['Basic Info']['Status']
|
|
183
184
|
yobs = diconf["Basic Info"]["Birth year"]
|
|
184
|
-
expectancy = diconf["Basic Info"]["Life expectancy"]
|
|
185
185
|
icount = len(yobs)
|
|
186
|
+
# Default to January if no month entry found.
|
|
187
|
+
mobs = diconf["Basic Info"].get("Birth month", [1]*icount)
|
|
188
|
+
expectancy = diconf["Basic Info"]["Life expectancy"]
|
|
186
189
|
s = ["", "s"][icount - 1]
|
|
187
190
|
mylog.vprint(f"Plan for {icount} individual{s}: {inames}.")
|
|
188
|
-
p = plan.Plan(inames, yobs, expectancy, name, verbose=True, logstreams=logstreams)
|
|
191
|
+
p = plan.Plan(inames, yobs, mobs, expectancy, name, verbose=True, logstreams=logstreams)
|
|
189
192
|
p._description = diconf.get("Description", "")
|
|
190
193
|
|
|
191
194
|
# Assets.
|
|
@@ -217,11 +220,11 @@ def readConfig(file, *, verbose=True, logstreams=None, readContributions=True):
|
|
|
217
220
|
mylog.vprint(f"Ignoring to read contributions file {timeListsFileName}.")
|
|
218
221
|
|
|
219
222
|
# Fixed Income.
|
|
220
|
-
ssecAmounts = np.array(diconf["Fixed Income"]
|
|
221
|
-
ssecAges = np.array(diconf["Fixed Income"]["Social security ages"]
|
|
223
|
+
ssecAmounts = np.array(diconf["Fixed Income"].get("Social security PIA amounts", [0]*icount), dtype=np.int32)
|
|
224
|
+
ssecAges = np.array(diconf["Fixed Income"]["Social security ages"])
|
|
222
225
|
p.setSocialSecurity(ssecAmounts, ssecAges)
|
|
223
|
-
pensionAmounts = np.array(diconf["Fixed Income"]
|
|
224
|
-
pensionAges = np.array(diconf["Fixed Income"]["Pension ages"]
|
|
226
|
+
pensionAmounts = np.array(diconf["Fixed Income"].get("Pension monthly amounts", [0]*icount), dtype=np.float32)
|
|
227
|
+
pensionAges = np.array(diconf["Fixed Income"]["Pension ages"])
|
|
225
228
|
pensionIsIndexed = diconf["Fixed Income"]["Pension indexed"]
|
|
226
229
|
p.setPension(pensionAmounts, pensionAges, pensionIsIndexed)
|
|
227
230
|
|