owlplanner 2025.5.12__tar.gz → 2025.5.28__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.
Files changed (114) hide show
  1. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/.github/workflows/github-actions-runtests.yml +1 -1
  2. owlplanner-2025.5.28/.streamlit/config.toml +2 -0
  3. owlplanner-2025.5.28/.streamlit/fullconfig.toml +330 -0
  4. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/PKG-INFO +4 -2
  5. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/README.md +1 -1
  6. owlplanner-2025.5.28/docker/Dockerfile.build +24 -0
  7. owlplanner-2025.5.12/docker/Dockerfile → owlplanner-2025.5.28/docker/Dockerfile.run +6 -5
  8. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docker/README.md +5 -1
  9. owlplanner-2025.5.28/docker/buildentrypoint.sh +12 -0
  10. owlplanner-2025.5.28/docker/runentrypoint.sh +17 -0
  11. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docs/owl.pdf +0 -0
  12. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docs/owl.tex +1 -1
  13. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/examples/case_jack+jill.toml +1 -1
  14. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/examples/case_joe.toml +1 -1
  15. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/examples/case_john+sally.toml +1 -1
  16. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/examples/case_jon+jane.toml +2 -2
  17. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/examples/case_kim+sam-bequest.toml +1 -1
  18. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/examples/case_kim+sam-spending.toml +1 -1
  19. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/notebooks/template.ipynb +23 -18
  20. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/notebooks/tutorial_1.ipynb +1080 -1091
  21. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/notebooks/tutorial_2.ipynb +389 -389
  22. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/notebooks/tutorial_3.ipynb +422 -422
  23. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/owlplanner.cmd +2 -3
  24. owlplanner-2025.5.28/owlplanner.sh +9 -0
  25. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/pyproject.toml +5 -3
  26. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/src/owlplanner/config.py +6 -7
  27. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/src/owlplanner/plan.py +83 -84
  28. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/src/owlplanner/plotting/base.py +3 -3
  29. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/src/owlplanner/plotting/matplotlib_backend.py +18 -21
  30. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/src/owlplanner/plotting/plotly_backend.py +29 -32
  31. owlplanner-2025.5.28/src/owlplanner/version.py +1 -0
  32. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/tests/test_regressions.py +87 -65
  33. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/tests/test_repro.py +18 -18
  34. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/tests/test_toml_cases.py +4 -4
  35. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/About_Owl.py +1 -0
  36. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/Create_Case.py +0 -17
  37. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/Current_Assets.py +16 -0
  38. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/Documentation.py +323 -196
  39. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/Optimization_Parameters.py +2 -3
  40. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/Output_Files.py +15 -10
  41. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/Quick_Start.py +5 -5
  42. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/Rates_Selection.py +17 -8
  43. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/Settings.py +11 -2
  44. owlplanner-2025.5.12/ui/Wages_And_Contributions.py → owlplanner-2025.5.28/ui/Wages_and_Contributions.py +6 -1
  45. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/main.py +1 -11
  46. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/owlbridge.py +36 -17
  47. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/sskeys.py +18 -17
  48. owlplanner-2025.5.12/docker/fastentrypoint.sh +0 -9
  49. owlplanner-2025.5.12/owlplanner.sh +0 -10
  50. owlplanner-2025.5.12/src/owlplanner/version.py +0 -1
  51. owlplanner-2025.5.12/ui/requirements.txt +0 -11
  52. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/.devcontainer/devcontainer.json +0 -0
  53. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/.flake8 +0 -0
  54. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/.gitattributes +0 -0
  55. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/.gitignore +0 -0
  56. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/INSTALL.md +0 -0
  57. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/LICENSE +0 -0
  58. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/USER_GUIDE.md +0 -0
  59. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docker/docker-compose.yml +0 -0
  60. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docs/images/AD-taxDef.png +0 -0
  61. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docs/images/AD-taxFree.png +0 -0
  62. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docs/images/AD-taxable.png +0 -0
  63. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docs/images/Hist_Bequest.png +0 -0
  64. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docs/images/Hist_Spending.png +0 -0
  65. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docs/images/MC-tutorial2a.png +0 -0
  66. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docs/images/MC-tutorial2b.png +0 -0
  67. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docs/images/OwlUI.png +0 -0
  68. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docs/images/allocations.png +0 -0
  69. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docs/images/owl.png +0 -0
  70. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docs/images/profile.png +0 -0
  71. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docs/images/ratesCorrelations.png +0 -0
  72. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docs/images/ratesPlot.png +0 -0
  73. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docs/images/savingsPlot.png +0 -0
  74. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docs/images/sourcesPlot.png +0 -0
  75. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docs/images/spendingPlot.png +0 -0
  76. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docs/images/taxIncomePlot.png +0 -0
  77. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/docs/images/taxesPlot.png +0 -0
  78. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/examples/jack+jill.xlsx +0 -0
  79. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/examples/joe.xlsx +0 -0
  80. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/examples/john+sally.xlsx +0 -0
  81. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/examples/jon+jane.xlsx +0 -0
  82. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/examples/template.xlsx +0 -0
  83. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/notebooks/john+sally.ipynb +0 -0
  84. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/notebooks/kim+sam.ipynb +0 -0
  85. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/pytest.ini +0 -0
  86. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/requirements.txt +0 -0
  87. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/src/owlplanner/__init__.py +0 -0
  88. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/src/owlplanner/abcapi.py +0 -0
  89. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/src/owlplanner/data/__init__.py +0 -0
  90. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/src/owlplanner/data/rates.csv +0 -0
  91. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/src/owlplanner/mylogging.py +0 -0
  92. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/src/owlplanner/plotting/__init__.py +0 -0
  93. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/src/owlplanner/plotting/factory.py +0 -0
  94. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/src/owlplanner/progress.py +0 -0
  95. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/src/owlplanner/rates.py +0 -0
  96. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/src/owlplanner/tax2025.py +0 -0
  97. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/src/owlplanner/timelists.py +0 -0
  98. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/src/owlplanner/utils.py +0 -0
  99. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/tests/test_logger.py +0 -0
  100. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/tests/test_ui_asset_allocation.py +0 -0
  101. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/tests/test_ui_compare_summaries.py +0 -0
  102. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/tests/test_ui_sskeys.py +0 -0
  103. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/tests/test_units.py +0 -0
  104. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/Asset_Allocation.py +0 -0
  105. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/Fixed_Income.py +0 -0
  106. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/Graphs.py +0 -0
  107. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/Historical_Range.py +0 -0
  108. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/Logs.py +0 -0
  109. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/Monte_Carlo.py +0 -0
  110. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/README.md +0 -0
  111. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/Worksheets.py +0 -0
  112. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/__init__.py +0 -0
  113. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/progress.py +0 -0
  114. {owlplanner-2025.5.12 → owlplanner-2025.5.28}/ui/tomlexamples.py +0 -0
@@ -12,7 +12,7 @@ jobs:
12
12
  strategy:
13
13
  fail-fast: false
14
14
  matrix:
15
- python-version: ["3.9", "3.10", "3.12", "3.13"]
15
+ python-version: ["3.9", "3.10", "3.13"]
16
16
 
17
17
  steps:
18
18
  - uses: actions/checkout@v4
@@ -0,0 +1,2 @@
1
+ [theme]
2
+ base = "dark"
@@ -0,0 +1,330 @@
1
+ 
2
+ [global]
3
+
4
+ # By default, Streamlit displays a warning when a user sets both a widget
5
+ # default value in the function defining the widget and a widget value via
6
+ # the widget's key in `st.session_state`.
7
+
8
+ # If you'd like to turn off this warning, set this to True.
9
+
10
+ # Default: false
11
+ # disableWidgetStateDuplicationWarning = false
12
+
13
+ # If True, will show a warning when you run a Streamlit-enabled script
14
+ # via "python my_script.py".
15
+
16
+ # Default: true
17
+ # showWarningOnDirectExecution = true
18
+
19
+
20
+ [logger]
21
+
22
+ # Level of logging for Streamlit's internal logger: "error", "warning",
23
+ # "info", or "debug".
24
+
25
+ # Default: "info"
26
+ # level = "info"
27
+
28
+ # String format for logging messages. If logger.datetimeFormat is set,
29
+ # logger messages will default to `%(asctime)s.%(msecs)03d %(message)s`. See
30
+ # Python's documentation for available attributes:
31
+ # https://docs.python.org/3/library/logging.html#formatter-objects
32
+
33
+ # Default: "%(asctime)s %(message)s"
34
+ # messageFormat = "%(asctime)s %(message)s"
35
+
36
+
37
+ [client]
38
+
39
+ # Controls whether uncaught app exceptions and deprecation warnings
40
+ # are displayed in the browser. This can be one of the following:
41
+
42
+ # - "full" : In the browser, Streamlit displays app deprecation
43
+ # warnings and exceptions, including exception types,
44
+ # exception messages, and associated tracebacks.
45
+ # - "stacktrace" : In the browser, Streamlit displays exceptions,
46
+ # including exception types, generic exception messages,
47
+ # and associated tracebacks. Deprecation warnings and
48
+ # full exception messages will only print to the
49
+ # console.
50
+ # - "type" : In the browser, Streamlit displays exception types and
51
+ # generic exception messages. Deprecation warnings, full
52
+ # exception messages, and associated tracebacks only
53
+ # print to the console.
54
+ # - "none" : In the browser, Streamlit displays generic exception
55
+ # messages. Deprecation warnings, full exception
56
+ # messages, associated tracebacks, and exception types
57
+ # will only print to the console.
58
+ # - True : This is deprecated. Streamlit displays "full"
59
+ # error details.
60
+ # - False : This is deprecated. Streamlit displays "stacktrace"
61
+ # error details.
62
+
63
+ # Default: [ "f", "u", "l", "l",]
64
+ # showErrorDetails = [ "f", "u", "l", "l",]
65
+
66
+ # Change the visibility of items in the toolbar, options menu,
67
+ # and settings dialog (top right of the app).
68
+
69
+ # Allowed values:
70
+ # - "auto" : Show the developer options if the app is accessed through
71
+ # localhost or through Streamlit Community Cloud as a developer.
72
+ # Hide them otherwise.
73
+ # - "developer" : Show the developer options.
74
+ # - "viewer" : Hide the developer options.
75
+ # - "minimal" : Show only options set externally (e.g. through
76
+ # Streamlit Community Cloud) or through st.set_page_config.
77
+ # If there are no options left, hide the menu.
78
+
79
+ # Default: "auto"
80
+ toolbarMode = "developer"
81
+
82
+ # Controls whether to display the default sidebar page navigation in a
83
+ # multi-page app. This only applies when app's pages are defined by the
84
+ # `pages/` directory.
85
+
86
+ # Default: true
87
+ # showSidebarNavigation = true
88
+
89
+ [runner]
90
+
91
+ # Allows you to type a variable or string by itself in a single line of
92
+ # Python code to write it to the app.
93
+
94
+ # Default: true
95
+ # magicEnabled = true
96
+
97
+ # Handle script rerun requests immediately, rather than waiting for script
98
+ # execution to reach a yield point. This makes Streamlit much more
99
+ # responsive to user interaction, but it can lead to race conditions in
100
+ # apps that mutate session_state data outside of explicit session_state
101
+ # assignment statements.
102
+
103
+ # Default: true
104
+ # fastReruns = true
105
+
106
+ # Raise an exception after adding unserializable data to Session State.
107
+ # Some execution environments may require serializing all data in Session
108
+ # State, so it may be useful to detect incompatibility during development,
109
+ # or when the execution environment will stop supporting it in the future.
110
+
111
+ # Default: false
112
+ # enforceSerializableSessionState = false
113
+
114
+ # Adjust how certain 'options' widgets like radio, selectbox, and
115
+ # multiselect coerce Enum members when the Enum class gets re-defined
116
+ # during a script re-run. For more information, check out the docs:
117
+ # https://docs.streamlit.io/develop/concepts/design/custom-classes#enums
118
+
119
+ # Allowed values:
120
+ # - "off": Disables Enum coercion.
121
+ # - "nameOnly": Enum classes can be coerced if their member names match.
122
+ # - "nameAndValue": Enum classes can be coerced if their member names AND
123
+ # member values match.
124
+
125
+ # Default: "nameOnly"
126
+ # enumCoercion = "nameOnly"
127
+
128
+
129
+ [server]
130
+
131
+ # List of folders that should not be watched for changes.
132
+
133
+ # Relative paths will be taken as relative to the current working directory.
134
+
135
+ # Example: ['/home/user1/env', 'relative/path/to/folder']
136
+
137
+ # Default: []
138
+ # folderWatchBlacklist = []
139
+
140
+ # Change the type of file watcher used by Streamlit, or turn it off
141
+ # completely.
142
+
143
+ # Allowed values:
144
+ # - "auto" : Streamlit will attempt to use the watchdog module, and
145
+ # falls back to polling if watchdog is not available.
146
+ # - "watchdog" : Force Streamlit to use the watchdog module.
147
+ # - "poll" : Force Streamlit to always use polling.
148
+ # - "none" : Streamlit will not watch files.
149
+
150
+ # Default: "auto"
151
+ # fileWatcherType = "auto"
152
+
153
+ # Symmetric key used to produce signed cookies. If deploying on multiple
154
+ # replicas, this should be set to the same value across all replicas to ensure
155
+ # they all share the same secret.
156
+
157
+ # Default: randomly generated secret key.
158
+ # cookieSecret = "b87888855015f2bb50b408cb9d958bb52ea434cc6dacdf971c9e2b4997216925"
159
+
160
+ # If false, will attempt to open a browser window on start.
161
+
162
+ # Default: false unless (1) we are on a Linux box where DISPLAY is unset, or
163
+ # (2) we are running in the Streamlit Atom plugin.
164
+ # headless = false
165
+
166
+ # Automatically rerun script when the file is modified on disk.
167
+
168
+ # Default: false
169
+ # runOnSave = false
170
+
171
+ # The address where the server will listen for client and browser
172
+ # connections. Use this if you want to bind the server to a specific address.
173
+ # If set, the server will only be accessible from this address, and not from
174
+ # any aliases (like localhost).
175
+
176
+ # Default: (unset)
177
+ # address =
178
+
179
+ # The port where the server will listen for browser connections.
180
+
181
+ # Don't use port 3000 which is reserved for internal development.
182
+
183
+ # Default: 8501
184
+ # port = 8501
185
+
186
+ # The base path for the URL where Streamlit should be served from.
187
+
188
+ # Default: ""
189
+ # baseUrlPath = ""
190
+
191
+ # Enables support for Cross-Origin Resource Sharing (CORS) protection,
192
+ # for added security.
193
+
194
+ # If XSRF protection is enabled and CORS protection is disabled at the
195
+ # same time, Streamlit will enable them both instead.
196
+
197
+ # Default: true
198
+ # enableCORS = true
199
+
200
+ # Enables support for Cross-Site Request Forgery (XSRF) protection, for
201
+ # added security.
202
+
203
+ # If XSRF protection is enabled and CORS protection is disabled at the
204
+ # same time, Streamlit will enable them both instead.
205
+
206
+ # Default: true
207
+ # enableXsrfProtection = true
208
+
209
+ # Max size, in megabytes, for files uploaded with the file_uploader.
210
+
211
+ # Default: 200
212
+ # maxUploadSize = 200
213
+
214
+ # Max size, in megabytes, of messages that can be sent via the WebSocket
215
+ # connection.
216
+
217
+ # Default: 200
218
+ # maxMessageSize = 200
219
+
220
+ # Enables support for websocket compression.
221
+
222
+ # Default: false
223
+ # enableWebsocketCompression = false
224
+
225
+ # Enable serving files from a `static` directory in the running app's
226
+ # directory.
227
+
228
+ # Default: false
229
+ # enableStaticServing = false
230
+
231
+ # TTL in seconds for sessions whose websockets have been disconnected. The server
232
+ # may choose to clean up session state, uploaded files, etc for a given session
233
+ # with no active websocket connection at any point after this time has passed.
234
+
235
+ # Default: 120
236
+ # disconnectedSessionTTL = 120
237
+
238
+ # Server certificate file for connecting via HTTPS.
239
+ # Must be set at the same time as "server.sslKeyFile".
240
+
241
+ # ['DO NOT USE THIS OPTION IN A PRODUCTION ENVIRONMENT. It has not gone through security audits or performance tests. For the production environment, we recommend performing SSL termination by the load balancer or the reverse proxy.']
242
+ # sslCertFile =
243
+
244
+ # Cryptographic key file for connecting via HTTPS.
245
+ # Must be set at the same time as "server.sslCertFile".
246
+
247
+ # ['DO NOT USE THIS OPTION IN A PRODUCTION ENVIRONMENT. It has not gone through security audits or performance tests. For the production environment, we recommend performing SSL termination by the load balancer or the reverse proxy.']
248
+ # sslKeyFile =
249
+
250
+
251
+ [browser]
252
+
253
+ # Internet address where users should point their browsers in order to
254
+ # connect to the app. Can be IP address or DNS name and path.
255
+
256
+ # This is used to:
257
+ # - Set the correct URL for CORS and XSRF protection purposes.
258
+ # - Show the URL on the terminal
259
+ # - Open the browser
260
+
261
+ # Default: "localhost"
262
+ # serverAddress = "localhost"
263
+
264
+ # Whether to send usage statistics to Streamlit.
265
+
266
+ # Default: true
267
+ # gatherUsageStats = true
268
+
269
+ # Port where users should point their browsers in order to connect to the
270
+ # app.
271
+
272
+ # This is used to:
273
+ # - Set the correct URL for XSRF protection purposes.
274
+ # - Show the URL on the terminal (part of `streamlit run`).
275
+ # - Open the browser automatically (part of `streamlit run`).
276
+
277
+ # This option is for advanced use cases. To change the port of your app, use
278
+ # `server.Port` instead. Don't use port 3000 which is reserved for internal
279
+ # development.
280
+
281
+ # Default: whatever value is set in server.port.
282
+ # serverPort = 8501
283
+
284
+
285
+ [mapbox]
286
+
287
+ # Configure Streamlit to use a custom Mapbox
288
+ # token for elements like st.pydeck_chart and st.map.
289
+ # To get a token for yourself, create an account at
290
+ # https://mapbox.com. It's free (for moderate usage levels)!
291
+
292
+ # Default: ""
293
+ # token = ""
294
+
295
+
296
+ [theme]
297
+
298
+ # The preset Streamlit theme that your custom theme inherits from.
299
+ # One of "light" or "dark".
300
+ # base =
301
+
302
+ # Primary accent color for interactive elements.
303
+ # primaryColor =
304
+
305
+ # Background color for the main content area.
306
+ # backgroundColor =
307
+
308
+ # Background color used for the sidebar and most interactive widgets.
309
+ # secondaryBackgroundColor =
310
+
311
+ # Color used for almost all text.
312
+ # textColor =
313
+
314
+ # The font family for all text in the app, except code blocks. One of "sans serif",
315
+ # "serif", or "monospace".
316
+ # To use a custom font, it needs to be added via [theme.fontFaces].
317
+ # font =
318
+
319
+
320
+ [secrets]
321
+
322
+ # List of locations where secrets are searched. An entry can be a path to a
323
+ # TOML file or directory path where Kubernetes style secrets are saved.
324
+ # Order is important, import is first to last, so secrets in later files
325
+ # will take precedence over earlier ones.
326
+
327
+ # Default: [ "C:\\Users\\marti\\.streamlit\\secrets.toml", "C:\\Users\\marti\\Dropbox\\Retirement\\MyProject\\Owl\\.streamlit\\.streamlit\\secrets.toml",]
328
+ # files = [ "C:\\Users\\marti\\.streamlit\\secrets.toml", "C:\\Users\\marti\\Dropbox\\Retirement\\MyProject\\Owl\\.streamlit\\.streamlit\\secrets.toml",]
329
+
330
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: owlplanner
3
- Version: 2025.5.12
3
+ Version: 2025.5.28
4
4
  Summary: Owl: 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
@@ -693,9 +693,11 @@ Classifier: Topic :: Office/Business :: Financial :: Investment
693
693
  Requires-Python: >=3.8
694
694
  Requires-Dist: matplotlib
695
695
  Requires-Dist: numpy
696
+ Requires-Dist: odfpy
696
697
  Requires-Dist: openpyxl
697
698
  Requires-Dist: pandas
698
699
  Requires-Dist: plotly
700
+ Requires-Dist: pulp
699
701
  Requires-Dist: scipy
700
702
  Requires-Dist: seaborn
701
703
  Requires-Dist: streamlit
@@ -712,7 +714,7 @@ Description-Content-Type: text/markdown
712
714
  -------------------------------------------------------------------------------------
713
715
 
714
716
  ### TL;DR
715
- Owl is a financial retirement planning tool that uses a linear programming
717
+ Owl is a retirement financial planning tool that uses a linear programming
716
718
  optimization algorithm to provide guidance on retirement decisions
717
719
  such as contributions, withdrawals, Roth conversions, and more.
718
720
  Users can select varying return rates to perform historical back testing,
@@ -8,7 +8,7 @@
8
8
  -------------------------------------------------------------------------------------
9
9
 
10
10
  ### TL;DR
11
- Owl is a financial retirement planning tool that uses a linear programming
11
+ Owl is a retirement financial planning tool that uses a linear programming
12
12
  optimization algorithm to provide guidance on retirement decisions
13
13
  such as contributions, withdrawals, Roth conversions, and more.
14
14
  Users can select varying return rates to perform historical back testing,
@@ -0,0 +1,24 @@
1
+ # docker/Dockerfile
2
+
3
+ FROM python:3.13-slim
4
+
5
+ WORKDIR /app
6
+
7
+ RUN apt-get update; \
8
+ apt-get upgrade; \
9
+ apt-get install -y --no-install-recommends \
10
+ curl \
11
+ git \
12
+ ; \
13
+ rm -rf /var/lib/apt/lists/*
14
+
15
+ COPY buildentrypoint.sh /usr/bin/entrypoint.sh
16
+ COPY runentrypoint.sh /usr/bin/runentrypoint.sh
17
+
18
+ RUN chmod 555 /usr/bin/entrypoint.sh /usr/bin/runentrypoint.sh
19
+
20
+ EXPOSE 8501
21
+
22
+ HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health
23
+
24
+ ENTRYPOINT ["/bin/bash", "/usr/bin/entrypoint.sh"]
@@ -12,20 +12,21 @@ RUN apt-get update; \
12
12
  ; \
13
13
  rm -rf /var/lib/apt/lists/*
14
14
 
15
- COPY fastentrypoint.sh /usr/bin/entrypoint.sh
15
+ COPY runentrypoint.sh /usr/bin/entrypoint.sh
16
16
 
17
17
  RUN chmod 555 /usr/bin/entrypoint.sh
18
18
 
19
19
  # Build in the container for faster starts.
20
20
  RUN python -m pip install --no-cache-dir --upgrade pip
21
21
 
22
- RUN pip install --no-cache-dir build
22
+ # RUN pip install --no-cache-dir build
23
+ RUN rm -rf /app/owl
23
24
 
24
25
  RUN git clone --depth 1 https://github.com/mdlacasse/Owl.git owl
25
26
 
26
- RUN cd /app/owl && python -m build
27
-
28
- RUN cd /app/owl && pip install --no-cache-dir .
27
+ RUN cd /app/owl && python -m pip install -r requirements.txt
28
+ # RUN cd /app/owl && python -m build
29
+ # RUN cd /app/owl && pip install --no-cache-dir .
29
30
 
30
31
  EXPOSE 8501
31
32
 
@@ -37,10 +37,14 @@ This approach requires cloning the Owl package from GitHub,
37
37
  and having both Python and Docker installed on your computer.
38
38
 
39
39
  ##### Docker image
40
+ There are two images you can create. One builds Owl at run time, while
41
+ the other builds it statically in the image.
42
+ These two approaches trade startup time for image space
43
+ (~300 MB vs. 1.8 GB, ~22 vs. 200 sec).
40
44
  First build the Docker image from the `docker` directory:
41
45
  ```shell
42
46
  cd docker
43
- docker build --no-cache -t owldocker .
47
+ docker build --no-cache -f Dockerfile.{build or run} -t owldocker .
44
48
  ```
45
49
 
46
50
  #### Running the container
@@ -0,0 +1,12 @@
1
+ #/bin/bash
2
+
3
+ # Build at run time for smaller image but slower starts.
4
+ rm -rf /app/owl
5
+ cd /app && git clone --depth 1 https://github.com/mdlacasse/Owl.git owl
6
+
7
+ export OWLDIR=/app/owl
8
+
9
+ python -m pip install --no-cache-dir --upgrade pip
10
+ cd ${OWLDIR} && python -m pip install -r requirements.txt
11
+
12
+ exec /usr/bin/runentrypoint.sh
@@ -0,0 +1,17 @@
1
+ #/bin/bash
2
+
3
+ if ! [[ -v OWLDIR ]]; then
4
+ OWLDIR=/app/owl
5
+ fi
6
+
7
+ export PYTHONPATH=${OWLDIR}/src:${PYTHONPATH}
8
+
9
+ echo ""
10
+ echo "Owl is now running locally: Point your browser to http://localhost:8501"
11
+
12
+ if type -P streamlit >& /dev/null; then
13
+ streamlit run ${OWLDIR}/ui/main.py --server.port=8501 --server.address=0.0.0.0 --browser.gatherUsageStats=false
14
+ else
15
+ python3 -m streamlit run ${OWLDIR}/ui/main.py --server.port=8501 --server.address=0.0.0.0 --browser.gatherUsageStats=false
16
+ fi
17
+
@@ -435,7 +435,7 @@ All intermediate variables are in uppercase letters.
435
435
  Notice that we are using return rates from the previous year.
436
436
  The first terms on the right-hand side represent dividends generated by
437
437
  equities $(k=0)$ in the $(j=0)$ taxable savings account plus
438
- half the yearly contributions. The second term account for withdrawals $w$
438
+ half the yearly contributions. The second term accounts for withdrawals $w$
439
439
  of equities assumed to have been purchased a year ago.
440
440
  It does not account for losses, but a market drop
441
441
  would most likely result in stock purchase rather than sale.
@@ -28,7 +28,7 @@ Names = [ "Jack", "Jill",]
28
28
  ["Rates Selection"]
29
29
  "Heirs rate on tax-deferred estate" = 30.0
30
30
  "Long-term capital gain tax rate" = 15.0
31
- "Dividend tax rate" = 2.0
31
+ "Dividend rate" = 1.8
32
32
  "TCJA expiration year" = 2026
33
33
  Method = "historical"
34
34
  From = 1969
@@ -26,7 +26,7 @@ Names = [ "Joe",]
26
26
  ["Rates Selection"]
27
27
  "Heirs rate on tax-deferred estate" = 30.0
28
28
  "Long-term capital gain tax rate" = 15.0
29
- "Dividend tax rate" = 2.0
29
+ "Dividend rate" = 1.8
30
30
  "TCJA expiration year" = 2026
31
31
  Method = "historical average"
32
32
  From = 1969
@@ -28,7 +28,7 @@ Names = [ "John", "Sally",]
28
28
  ["Rates Selection"]
29
29
  "Heirs rate on tax-deferred estate" = 30.0
30
30
  "Long-term capital gain tax rate" = 15.0
31
- "Dividend tax rate" = 2.0
31
+ "Dividend rate" = 1.8
32
32
  "TCJA expiration year" = 2026
33
33
  Method = "historical average"
34
34
  From = 1990
@@ -28,10 +28,10 @@ Names = [ "Jon", "Jane",]
28
28
  ["Rates Selection"]
29
29
  "Heirs rate on tax-deferred estate" = 30.0
30
30
  "Long-term capital gain tax rate" = 15.0
31
- "Dividend tax rate" = 2.0
31
+ "Dividend rate" = 1.8
32
32
  "TCJA expiration year" = 2026
33
33
  Method = "user"
34
- Values = [ 10.0, 0.0, 0.0, 3.5000000000000004,]
34
+ Values = [ 10.0, 6.0, 5.0, 3.5000000000000004,]
35
35
  From = 1928
36
36
  To = 2024
37
37
 
@@ -28,7 +28,7 @@ Names = [ "Kim", "Sam",]
28
28
  ["Rates Selection"]
29
29
  "Heirs rate on tax-deferred estate" = 33.0
30
30
  "Long-term capital gain tax rate" = 15.0
31
- "Dividend tax rate" = 2.0
31
+ "Dividend rate" = 1.8
32
32
  "TCJA expiration year" = 2026
33
33
  Method = "conservative"
34
34
  From = 1922
@@ -28,7 +28,7 @@ Names = [ "Kim", "Sam",]
28
28
  ["Rates Selection"]
29
29
  "Heirs rate on tax-deferred estate" = 33.0
30
30
  "Long-term capital gain tax rate" = 15.0
31
- "Dividend tax rate" = 2.0
31
+ "Dividend rate" = 1.8
32
32
  "TCJA expiration year" = 2026
33
33
  Method = "conservative"
34
34
  From = 1922
@@ -138,24 +138,6 @@
138
138
  "plan = owl.Plan(['Kim', 'Sam'], [YYYY, YYYY], [AA, AA], 'Kim+Sam-spending', verbose=True)"
139
139
  ]
140
140
  },
141
- {
142
- "cell_type": "markdown",
143
- "id": "d1b5aabe",
144
- "metadata": {},
145
- "source": [
146
- "By default, the starting date is today, and therefore the returns of the current year are reduced in proportion of how late we are in the year. An arbitrary date can be chosen as a starting point in the current year, using the optional parameter startDate. This will only affect the first year: the plan will still end at the end of the last year. For example, using startDate='01-01' would select January 1st, the beginning of the year. This is useful for reproducibility studies. Here is an example:"
147
- ]
148
- },
149
- {
150
- "cell_type": "code",
151
- "execution_count": null,
152
- "id": "8a106c03",
153
- "metadata": {},
154
- "outputs": [],
155
- "source": [
156
- "# plan = owl.Plan(['Kim', 'Sam'], [YYYY, YYYY], [AA, AA], 'Kim+Sam-spending', startDate='01-01')"
157
- ]
158
- },
159
141
  {
160
142
  "cell_type": "markdown",
161
143
  "id": "47f309e7-0654-4f75-87b5-921167db4a6a",
@@ -204,6 +186,29 @@
204
186
  "Benificiary fractions are entered as [1, 1, 1], i.e., one for each type of account. Default is to leave everything to the living spouse."
205
187
  ]
206
188
  },
189
+ {
190
+ "cell_type": "markdown",
191
+ "id": "090914eb-78f3-4e85-bdc9-e7fa03b75aaf",
192
+ "metadata": {},
193
+ "source": [
194
+ "By default, the account balances are assumed to be known as of today, and amounts will be back projected to Jan 1st using the assumed return rates. An arbitrary date can be chosen as a reference point in the current year, using the optional parameter startDate. For example, using startDate='01-01' would select January 1st, the beginning of the year. This is useful for reproducibility studies. Here is an example:"
195
+ ]
196
+ },
197
+ {
198
+ "cell_type": "code",
199
+ "execution_count": null,
200
+ "id": "49109e26-bea3-49df-8377-3343111ebfac",
201
+ "metadata": {},
202
+ "outputs": [],
203
+ "source": [
204
+ "plan.setAccountBalances(\n",
205
+ " taxable=[XX, XX],\n",
206
+ " taxDeferred=[XX, XX],\n",
207
+ " taxFree=[XX, XX],\n",
208
+ " startDate=\"01-01\"\n",
209
+ ")"
210
+ ]
211
+ },
207
212
  {
208
213
  "cell_type": "code",
209
214
  "execution_count": null,