mpal-cli 0.0.1__tar.gz → 0.6.0__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 (63) hide show
  1. mpal_cli-0.6.0/LICENSE +21 -0
  2. mpal_cli-0.6.0/PKG-INFO +211 -0
  3. mpal_cli-0.6.0/README.md +185 -0
  4. mpal_cli-0.6.0/pyproject.toml +57 -0
  5. mpal_cli-0.6.0/src/mpal/__init__.py +3 -0
  6. mpal_cli-0.6.0/src/mpal/__main__.py +12 -0
  7. mpal_cli-0.6.0/src/mpal/amounts.py +44 -0
  8. mpal_cli-0.6.0/src/mpal/asset_replay.py +289 -0
  9. mpal_cli-0.6.0/src/mpal/assets.py +25 -0
  10. mpal_cli-0.6.0/src/mpal/cli.py +966 -0
  11. mpal_cli-0.6.0/src/mpal/config.py +31 -0
  12. mpal_cli-0.6.0/src/mpal/dates.py +26 -0
  13. mpal_cli-0.6.0/src/mpal/errors.py +81 -0
  14. mpal_cli-0.6.0/src/mpal/numbers.py +155 -0
  15. mpal_cli-0.6.0/src/mpal/output/__init__.py +29 -0
  16. mpal_cli-0.6.0/src/mpal/output/console.py +718 -0
  17. mpal_cli-0.6.0/src/mpal/output/formatting.py +135 -0
  18. mpal_cli-0.6.0/src/mpal/output/theme.py +55 -0
  19. mpal_cli-0.6.0/src/mpal/storage/__init__.py +82 -0
  20. mpal_cli-0.6.0/src/mpal/storage/asset_logs.py +86 -0
  21. mpal_cli-0.6.0/src/mpal/storage/asset_transactions.py +723 -0
  22. mpal_cli-0.6.0/src/mpal/storage/assets.py +273 -0
  23. mpal_cli-0.6.0/src/mpal/storage/database.py +311 -0
  24. mpal_cli-0.6.0/src/mpal/storage/entries.py +299 -0
  25. mpal_cli-0.6.0/src/mpal/storage/logs.py +107 -0
  26. mpal_cli-0.6.0/src/mpal/storage/portfolios.py +121 -0
  27. mpal_cli-0.6.0/src/mpal/storage/summaries.py +174 -0
  28. mpal_cli-0.6.0/src/mpal_cli.egg-info/PKG-INFO +211 -0
  29. mpal_cli-0.6.0/src/mpal_cli.egg-info/SOURCES.txt +54 -0
  30. mpal_cli-0.6.0/src/mpal_cli.egg-info/entry_points.txt +2 -0
  31. mpal_cli-0.6.0/src/mpal_cli.egg-info/requires.txt +10 -0
  32. mpal_cli-0.6.0/tests/test_amounts.py +32 -0
  33. mpal_cli-0.6.0/tests/test_asset_accounting_audit.py +412 -0
  34. mpal_cli-0.6.0/tests/test_asset_cli.py +589 -0
  35. mpal_cli-0.6.0/tests/test_asset_delete_entry.py +428 -0
  36. mpal_cli-0.6.0/tests/test_asset_edit.py +578 -0
  37. mpal_cli-0.6.0/tests/test_asset_log.py +421 -0
  38. mpal_cli-0.6.0/tests/test_asset_replay.py +409 -0
  39. mpal_cli-0.6.0/tests/test_asset_summary.py +625 -0
  40. mpal_cli-0.6.0/tests/test_assets.py +42 -0
  41. mpal_cli-0.6.0/tests/test_branding.py +34 -0
  42. mpal_cli-0.6.0/tests/test_buy.py +584 -0
  43. mpal_cli-0.6.0/tests/test_cli.py +3106 -0
  44. mpal_cli-0.6.0/tests/test_dates.py +37 -0
  45. mpal_cli-0.6.0/tests/test_grouped_cli.py +251 -0
  46. mpal_cli-0.6.0/tests/test_help.py +235 -0
  47. mpal_cli-0.6.0/tests/test_income.py +406 -0
  48. mpal_cli-0.6.0/tests/test_numbers.py +183 -0
  49. mpal_cli-0.6.0/tests/test_output_polish.py +812 -0
  50. mpal_cli-0.6.0/tests/test_portfolio_allocation.py +238 -0
  51. mpal_cli-0.6.0/tests/test_release_readiness.py +214 -0
  52. mpal_cli-0.6.0/tests/test_sell.py +704 -0
  53. mpal_cli-0.6.0/tests/test_summary.py +629 -0
  54. mpal_cli-0.0.1/PKG-INFO +0 -9
  55. mpal_cli-0.0.1/pyproject.toml +0 -20
  56. mpal_cli-0.0.1/src/mpal/__init__.py +0 -0
  57. mpal_cli-0.0.1/src/mpal/__main__.py +0 -16
  58. mpal_cli-0.0.1/src/mpal_cli.egg-info/PKG-INFO +0 -9
  59. mpal_cli-0.0.1/src/mpal_cli.egg-info/SOURCES.txt +0 -8
  60. mpal_cli-0.0.1/src/mpal_cli.egg-info/entry_points.txt +0 -2
  61. {mpal_cli-0.0.1 → mpal_cli-0.6.0}/setup.cfg +0 -0
  62. {mpal_cli-0.0.1 → mpal_cli-0.6.0}/src/mpal_cli.egg-info/dependency_links.txt +0 -0
  63. {mpal_cli-0.0.1 → mpal_cli-0.6.0}/src/mpal_cli.egg-info/top_level.txt +0 -0
mpal_cli-0.6.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nasser
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,211 @@
1
+ Metadata-Version: 2.4
2
+ Name: mpal-cli
3
+ Version: 0.6.0
4
+ Summary: Multi-Portfolio Asset Ledger (mpal) - A minimal CLI tool for manual asset tracking and capital management.
5
+ License-Expression: MIT
6
+ Keywords: assets,capital,cli,ledger,portfolio
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Environment :: Console
9
+ Classifier: Intended Audience :: End Users/Desktop
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Topic :: Office/Business :: Financial
14
+ Requires-Python: >=3.11
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: rich>=13
18
+ Requires-Dist: typer>=0.12
19
+ Provides-Extra: dev
20
+ Requires-Dist: pytest>=8; extra == "dev"
21
+ Requires-Dist: ruff>=0.6; extra == "dev"
22
+ Provides-Extra: release
23
+ Requires-Dist: build>=1; extra == "release"
24
+ Requires-Dist: twine>=5; extra == "release"
25
+ Dynamic: license-file
26
+
27
+ # mpal
28
+
29
+ mpal — Multi-Portfolio Asset Ledger — is a minimal CLI tool for manual asset
30
+ tracking and capital management.
31
+
32
+ The CLI command is `mpal`. The package/distribution name is `mpal-cli`.
33
+ Current version: `0.6.0`. License: MIT.
34
+
35
+ ## Installation
36
+
37
+ mpal requires Python 3.11 or later.
38
+
39
+ For a future PyPI release:
40
+
41
+ ```console
42
+ python -m pip install mpal-cli
43
+ mpal --help
44
+ ```
45
+
46
+ From a project checkout:
47
+
48
+ ```console
49
+ python -m pip install -e .
50
+ mpal --help
51
+ ```
52
+
53
+ ## Storage
54
+
55
+ mpal stores records in a local SQLite database named `mpal.db`. By default it
56
+ uses the platform data directory, but you can set `MPAL_DATA_DIR` to choose a
57
+ different directory:
58
+
59
+ ```console
60
+ MPAL_DATA_DIR=/path/to/mpal-data mpal init
61
+ ```
62
+
63
+ ## What mpal is
64
+
65
+ - A manual capital ledger.
66
+ - A portfolio tracker.
67
+ - A deterministic, record-based calculator.
68
+ - A local database-backed CLI.
69
+ - A foundation for future extensions.
70
+
71
+ mpal calculates its results only from manually recorded operations.
72
+
73
+ ## Scope
74
+
75
+ mpal is intentionally simple. It does not fetch market data, calculate live
76
+ prices, calculate market value, calculate unrealized PnL, connect to market
77
+ APIs or other external services, or provide financial advice.
78
+
79
+ mpal only works with the records you enter manually.
80
+
81
+ mpal tracks capital, cash, open positions by book cost, manually recorded
82
+ asset income, realized P&L, and return. Portfolio summaries use
83
+ book/accounting values. Book Value is derived from manual records and is not
84
+ market value.
85
+
86
+ ## Quick Start
87
+
88
+ ```console
89
+ mpal init
90
+ mpal summary
91
+ mpal summary --explain
92
+ mpal portfolio create stocks
93
+ mpal deposit 1000 -p stocks
94
+ mpal withdraw 250 -p stocks
95
+ mpal capital -p stocks
96
+ mpal capital log -p stocks
97
+ mpal asset add AAPL -p stocks
98
+ mpal asset buy AAPL -p stocks --price 234.43 --quantity 3 --fee 2.30
99
+ mpal asset list
100
+ mpal summary -p stocks
101
+ mpal summary -p stocks -a AAPL
102
+ mpal asset log AAPL -p stocks
103
+ ```
104
+
105
+ ## Command Hierarchy
106
+
107
+ mpal groups commands by the records they manage:
108
+
109
+ ```console
110
+ mpal init
111
+
112
+ mpal summary
113
+ mpal summary --explain
114
+ mpal summary -p stocks
115
+ mpal summary -p stocks -a AAPL
116
+
117
+ mpal portfolio create stocks
118
+ mpal portfolio create stocks --initial 5000
119
+ mpal portfolio list
120
+ mpal portfolio allocation
121
+ mpal portfolio reset stocks --yes
122
+ mpal portfolio delete stocks --yes
123
+
124
+ mpal deposit 1000 -p stocks
125
+ mpal withdraw 250 --portfolio stocks --date 2026-06-19 --note "withdrawal"
126
+ mpal capital -p stocks
127
+ mpal capital log -p stocks
128
+ mpal capital entry edit 2 -p stocks --amount 500
129
+ mpal capital entry delete 2 -p stocks
130
+
131
+ mpal asset add AAPL AMZN MSFT -p stocks
132
+ mpal asset list
133
+ mpal asset list -p stocks
134
+ mpal asset log AAPL -p stocks
135
+ mpal asset delete AAPL -p stocks --yes
136
+ mpal asset entry edit AAPL 2 -p stocks --price 234.50 --quantity 3
137
+ mpal asset entry delete AAPL 2 -p stocks --yes
138
+ mpal asset income AAPL 32 -p stocks --date 2026-06-20 --note "Distribution"
139
+ mpal asset buy AAPL -p stocks --price 234.43 --quantity 3 --fee 2.30
140
+ mpal asset sell AAPL -p stocks --price 235.50 --quantity 1 --fee 1.25
141
+ ```
142
+
143
+ `mpal summary` is the unified summary/reporting command. With no options it
144
+ shows a global dashboard summary across all active portfolios: total capital,
145
+ total cash, positions, book value, total income, realized P&L, and return.
146
+ `mpal summary --explain` prints concise definitions below that global table.
147
+ `mpal summary -p <portfolio>` summarizes one active portfolio. `mpal summary
148
+ -p <portfolio> -a <asset>` summarizes one active asset within one active
149
+ portfolio; `-a` requires `-p`. Positions are open position book cost, not
150
+ market value. Book Value is total cash plus open position book cost. Summary
151
+ views do not use live prices, market value, or unrealized PnL. Global return
152
+ is computed from global totals, not by averaging portfolio returns.
153
+
154
+ `mpal portfolio allocation` shows active portfolio allocation by book value.
155
+ `BOOK VALUE = TOTAL CASH + POSITIONS`, where positions are open position book
156
+ cost. Allocation is not based on capital, cash alone, market value, live
157
+ prices, or unrealized PnL.
158
+
159
+ Portfolio-scoped capital and asset operations require `--portfolio` or `-p`.
160
+ Global collection views such as `mpal summary` and `mpal asset list` do not
161
+ require `-p`. No compatibility aliases are kept in this CLI redesign.
162
+
163
+ Use `mpal deposit <amount> -p <portfolio>` for external capital deposits and
164
+ `mpal withdraw <amount> -p <portfolio>` for external capital withdrawals. Use
165
+ `mpal capital -p <portfolio>` to inspect current capital and
166
+ `mpal capital log -p <portfolio>` to inspect capital history.
167
+
168
+ Asset list output uses an `Asset/Portfolio` column. Combined labels display
169
+ as `<SYMBOL> • <Portfolio>`, such as `AAPL • Stocks`; portfolio capitalization
170
+ there is display-only, and command syntax still uses `-p <portfolio>`. The
171
+ symbol uses the key style while the portfolio segment is muted for scanning.
172
+
173
+ Entry numbers shown by `mpal capital log` are stable, portfolio-local
174
+ numbers. Internal database IDs are not part of the CLI contract.
175
+ Capital entry correction is under `mpal capital entry edit/delete`; `mpal
176
+ capital log` remains the historical entry view, and `mpal capital -p
177
+ <portfolio>` is the capital-only current-state view.
178
+
179
+ Explicit transaction dates must use `YYYY-MM-DD` and cannot be in the future. If
180
+ `--date` is omitted, mpal uses the current local date.
181
+
182
+ The current release covers initialization, portfolio creation, optional initial capital,
183
+ deposits, withdrawals, summaries, logs, capital-entry correction and deletion,
184
+ portfolio reset, and soft deletion of portfolios. The asset foundation adds
185
+ manual symbol creation, current-state views, and soft deletion under existing
186
+ portfolios. The read-only asset log and its transaction storage foundation are
187
+ also present. Manual asset income updates asset and portfolio summaries.
188
+ Manual buys update open quantity, Cost Basis, portfolio Cash, and Positions.
189
+ Manual sells use moving-average book cost and update open quantity, Cost Basis,
190
+ Cash, Positions, and Realized PnL. Individual asset transactions can be edited
191
+ or soft-deleted by asset-local entry number, with active transactions replayed
192
+ before commit. `asset list` is the current asset collection view, and
193
+ `summary -p <portfolio> -a <asset>` reports current open quantity, Cost Basis,
194
+ Average Cost, Realized PnL, Income, and Realized Return for one asset from
195
+ active manual transactions.
196
+ Asset transaction correction is under `mpal asset entry edit/delete`; `mpal
197
+ asset log` remains the historical transaction view.
198
+
199
+ `summary` owns global, portfolio, and asset summary/reporting views. Daily
200
+ capital actions are top-level commands, and `capital -p` owns the current
201
+ capital view.
202
+
203
+ ## Planned capabilities
204
+
205
+ Later releases are planned to extend portfolio tracking, record management, calculations, reporting, backup, import and export, audit tooling, and release automation.
206
+
207
+ See [the roadmap](docs/ROADMAP.md) for the phased plan.
208
+
209
+ ## Financial advice disclaimer
210
+
211
+ mpal is a record-keeping and calculation tool. It does not provide financial, investment, tax, or legal advice. Users are responsible for verifying their records and decisions.
@@ -0,0 +1,185 @@
1
+ # mpal
2
+
3
+ mpal — Multi-Portfolio Asset Ledger — is a minimal CLI tool for manual asset
4
+ tracking and capital management.
5
+
6
+ The CLI command is `mpal`. The package/distribution name is `mpal-cli`.
7
+ Current version: `0.6.0`. License: MIT.
8
+
9
+ ## Installation
10
+
11
+ mpal requires Python 3.11 or later.
12
+
13
+ For a future PyPI release:
14
+
15
+ ```console
16
+ python -m pip install mpal-cli
17
+ mpal --help
18
+ ```
19
+
20
+ From a project checkout:
21
+
22
+ ```console
23
+ python -m pip install -e .
24
+ mpal --help
25
+ ```
26
+
27
+ ## Storage
28
+
29
+ mpal stores records in a local SQLite database named `mpal.db`. By default it
30
+ uses the platform data directory, but you can set `MPAL_DATA_DIR` to choose a
31
+ different directory:
32
+
33
+ ```console
34
+ MPAL_DATA_DIR=/path/to/mpal-data mpal init
35
+ ```
36
+
37
+ ## What mpal is
38
+
39
+ - A manual capital ledger.
40
+ - A portfolio tracker.
41
+ - A deterministic, record-based calculator.
42
+ - A local database-backed CLI.
43
+ - A foundation for future extensions.
44
+
45
+ mpal calculates its results only from manually recorded operations.
46
+
47
+ ## Scope
48
+
49
+ mpal is intentionally simple. It does not fetch market data, calculate live
50
+ prices, calculate market value, calculate unrealized PnL, connect to market
51
+ APIs or other external services, or provide financial advice.
52
+
53
+ mpal only works with the records you enter manually.
54
+
55
+ mpal tracks capital, cash, open positions by book cost, manually recorded
56
+ asset income, realized P&L, and return. Portfolio summaries use
57
+ book/accounting values. Book Value is derived from manual records and is not
58
+ market value.
59
+
60
+ ## Quick Start
61
+
62
+ ```console
63
+ mpal init
64
+ mpal summary
65
+ mpal summary --explain
66
+ mpal portfolio create stocks
67
+ mpal deposit 1000 -p stocks
68
+ mpal withdraw 250 -p stocks
69
+ mpal capital -p stocks
70
+ mpal capital log -p stocks
71
+ mpal asset add AAPL -p stocks
72
+ mpal asset buy AAPL -p stocks --price 234.43 --quantity 3 --fee 2.30
73
+ mpal asset list
74
+ mpal summary -p stocks
75
+ mpal summary -p stocks -a AAPL
76
+ mpal asset log AAPL -p stocks
77
+ ```
78
+
79
+ ## Command Hierarchy
80
+
81
+ mpal groups commands by the records they manage:
82
+
83
+ ```console
84
+ mpal init
85
+
86
+ mpal summary
87
+ mpal summary --explain
88
+ mpal summary -p stocks
89
+ mpal summary -p stocks -a AAPL
90
+
91
+ mpal portfolio create stocks
92
+ mpal portfolio create stocks --initial 5000
93
+ mpal portfolio list
94
+ mpal portfolio allocation
95
+ mpal portfolio reset stocks --yes
96
+ mpal portfolio delete stocks --yes
97
+
98
+ mpal deposit 1000 -p stocks
99
+ mpal withdraw 250 --portfolio stocks --date 2026-06-19 --note "withdrawal"
100
+ mpal capital -p stocks
101
+ mpal capital log -p stocks
102
+ mpal capital entry edit 2 -p stocks --amount 500
103
+ mpal capital entry delete 2 -p stocks
104
+
105
+ mpal asset add AAPL AMZN MSFT -p stocks
106
+ mpal asset list
107
+ mpal asset list -p stocks
108
+ mpal asset log AAPL -p stocks
109
+ mpal asset delete AAPL -p stocks --yes
110
+ mpal asset entry edit AAPL 2 -p stocks --price 234.50 --quantity 3
111
+ mpal asset entry delete AAPL 2 -p stocks --yes
112
+ mpal asset income AAPL 32 -p stocks --date 2026-06-20 --note "Distribution"
113
+ mpal asset buy AAPL -p stocks --price 234.43 --quantity 3 --fee 2.30
114
+ mpal asset sell AAPL -p stocks --price 235.50 --quantity 1 --fee 1.25
115
+ ```
116
+
117
+ `mpal summary` is the unified summary/reporting command. With no options it
118
+ shows a global dashboard summary across all active portfolios: total capital,
119
+ total cash, positions, book value, total income, realized P&L, and return.
120
+ `mpal summary --explain` prints concise definitions below that global table.
121
+ `mpal summary -p <portfolio>` summarizes one active portfolio. `mpal summary
122
+ -p <portfolio> -a <asset>` summarizes one active asset within one active
123
+ portfolio; `-a` requires `-p`. Positions are open position book cost, not
124
+ market value. Book Value is total cash plus open position book cost. Summary
125
+ views do not use live prices, market value, or unrealized PnL. Global return
126
+ is computed from global totals, not by averaging portfolio returns.
127
+
128
+ `mpal portfolio allocation` shows active portfolio allocation by book value.
129
+ `BOOK VALUE = TOTAL CASH + POSITIONS`, where positions are open position book
130
+ cost. Allocation is not based on capital, cash alone, market value, live
131
+ prices, or unrealized PnL.
132
+
133
+ Portfolio-scoped capital and asset operations require `--portfolio` or `-p`.
134
+ Global collection views such as `mpal summary` and `mpal asset list` do not
135
+ require `-p`. No compatibility aliases are kept in this CLI redesign.
136
+
137
+ Use `mpal deposit <amount> -p <portfolio>` for external capital deposits and
138
+ `mpal withdraw <amount> -p <portfolio>` for external capital withdrawals. Use
139
+ `mpal capital -p <portfolio>` to inspect current capital and
140
+ `mpal capital log -p <portfolio>` to inspect capital history.
141
+
142
+ Asset list output uses an `Asset/Portfolio` column. Combined labels display
143
+ as `<SYMBOL> • <Portfolio>`, such as `AAPL • Stocks`; portfolio capitalization
144
+ there is display-only, and command syntax still uses `-p <portfolio>`. The
145
+ symbol uses the key style while the portfolio segment is muted for scanning.
146
+
147
+ Entry numbers shown by `mpal capital log` are stable, portfolio-local
148
+ numbers. Internal database IDs are not part of the CLI contract.
149
+ Capital entry correction is under `mpal capital entry edit/delete`; `mpal
150
+ capital log` remains the historical entry view, and `mpal capital -p
151
+ <portfolio>` is the capital-only current-state view.
152
+
153
+ Explicit transaction dates must use `YYYY-MM-DD` and cannot be in the future. If
154
+ `--date` is omitted, mpal uses the current local date.
155
+
156
+ The current release covers initialization, portfolio creation, optional initial capital,
157
+ deposits, withdrawals, summaries, logs, capital-entry correction and deletion,
158
+ portfolio reset, and soft deletion of portfolios. The asset foundation adds
159
+ manual symbol creation, current-state views, and soft deletion under existing
160
+ portfolios. The read-only asset log and its transaction storage foundation are
161
+ also present. Manual asset income updates asset and portfolio summaries.
162
+ Manual buys update open quantity, Cost Basis, portfolio Cash, and Positions.
163
+ Manual sells use moving-average book cost and update open quantity, Cost Basis,
164
+ Cash, Positions, and Realized PnL. Individual asset transactions can be edited
165
+ or soft-deleted by asset-local entry number, with active transactions replayed
166
+ before commit. `asset list` is the current asset collection view, and
167
+ `summary -p <portfolio> -a <asset>` reports current open quantity, Cost Basis,
168
+ Average Cost, Realized PnL, Income, and Realized Return for one asset from
169
+ active manual transactions.
170
+ Asset transaction correction is under `mpal asset entry edit/delete`; `mpal
171
+ asset log` remains the historical transaction view.
172
+
173
+ `summary` owns global, portfolio, and asset summary/reporting views. Daily
174
+ capital actions are top-level commands, and `capital -p` owns the current
175
+ capital view.
176
+
177
+ ## Planned capabilities
178
+
179
+ Later releases are planned to extend portfolio tracking, record management, calculations, reporting, backup, import and export, audit tooling, and release automation.
180
+
181
+ See [the roadmap](docs/ROADMAP.md) for the phased plan.
182
+
183
+ ## Financial advice disclaimer
184
+
185
+ mpal is a record-keeping and calculation tool. It does not provide financial, investment, tax, or legal advice. Users are responsible for verifying their records and decisions.
@@ -0,0 +1,57 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "mpal-cli"
7
+ version = "0.6.0"
8
+ description = "Multi-Portfolio Asset Ledger (mpal) - A minimal CLI tool for manual asset tracking and capital management."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "MIT"
12
+ keywords = ["assets", "capital", "cli", "ledger", "portfolio"]
13
+ classifiers = [
14
+ "Development Status :: 3 - Alpha",
15
+ "Environment :: Console",
16
+ "Intended Audience :: End Users/Desktop",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Topic :: Office/Business :: Financial",
21
+ ]
22
+ dependencies = [
23
+ "rich>=13",
24
+ "typer>=0.12",
25
+ ]
26
+
27
+ [project.optional-dependencies]
28
+ dev = [
29
+ "pytest>=8",
30
+ "ruff>=0.6",
31
+ ]
32
+ release = [
33
+ "build>=1",
34
+ "twine>=5",
35
+ ]
36
+
37
+ [project.scripts]
38
+ mpal = "mpal.cli:app"
39
+
40
+ [tool.setuptools.packages.find]
41
+ where = ["src"]
42
+
43
+ [tool.pytest.ini_options]
44
+ addopts = "-ra"
45
+ pythonpath = ["src"]
46
+ testpaths = ["tests"]
47
+
48
+ [tool.ruff]
49
+ line-length = 88
50
+ target-version = "py311"
51
+
52
+ [tool.ruff.lint]
53
+ select = ["E", "F", "I", "UP"]
54
+
55
+ [tool.ruff.format]
56
+ quote-style = "double"
57
+ indent-style = "space"
@@ -0,0 +1,3 @@
1
+ """mpal command-line application."""
2
+
3
+ __version__ = "0.6.0"
@@ -0,0 +1,12 @@
1
+ """Run mpal with ``python -m mpal``."""
2
+
3
+ from mpal.cli import app
4
+
5
+
6
+ def main() -> None:
7
+ """Run the mpal CLI."""
8
+ app()
9
+
10
+
11
+ if __name__ == "__main__":
12
+ main()
@@ -0,0 +1,44 @@
1
+ """Exact monetary parsing and money-specific display formatting."""
2
+
3
+ from decimal import Decimal, InvalidOperation
4
+
5
+ from mpal.errors import InvalidAmountError
6
+
7
+ MINOR_UNITS_PER_UNIT = 100
8
+
9
+
10
+ def parse_amount_minor(amount: str, *, allow_zero: bool = False) -> int:
11
+ """Parse money with at most two decimal places."""
12
+ try:
13
+ decimal_amount = Decimal(amount)
14
+ except InvalidOperation as error:
15
+ raise InvalidAmountError(f"Invalid amount: '{amount}'.") from error
16
+
17
+ if not decimal_amount.is_finite():
18
+ raise InvalidAmountError(f"Invalid amount: '{amount}'.")
19
+ if decimal_amount < 0 or (decimal_amount == 0 and not allow_zero):
20
+ comparison = "nonnegative" if allow_zero else "greater than zero"
21
+ raise InvalidAmountError(f"Amount must be {comparison}.")
22
+ if decimal_amount.as_tuple().exponent < -2:
23
+ raise InvalidAmountError("Amount cannot have more than 2 decimal places.")
24
+
25
+ minor_amount = decimal_amount * MINOR_UNITS_PER_UNIT
26
+ if minor_amount != minor_amount.to_integral_value():
27
+ raise InvalidAmountError("Amount cannot have more than 2 decimal places.")
28
+
29
+ return int(minor_amount)
30
+
31
+
32
+ def format_money(amount_minor: int) -> str:
33
+ """Format integer minor units as money with grouping and two places."""
34
+ sign = "-" if amount_minor < 0 else ""
35
+ whole, fraction = divmod(abs(amount_minor), MINOR_UNITS_PER_UNIT)
36
+ return f"{sign}{whole:,}.{fraction:02d}"
37
+
38
+
39
+ def format_signed_money(amount_minor: int) -> str:
40
+ """Format profit or loss money with an explicit positive sign."""
41
+ formatted = format_money(amount_minor)
42
+ if amount_minor > 0:
43
+ return f"+{formatted}"
44
+ return formatted