rebalance 0.3.1__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 (38) hide show
  1. rebalance-0.3.1/.github/.release-please-manifest.json +3 -0
  2. rebalance-0.3.1/.github/branch-protection-ruleset.json +45 -0
  3. rebalance-0.3.1/.github/release-please-config.json +13 -0
  4. rebalance-0.3.1/.github/renovate.json5 +53 -0
  5. rebalance-0.3.1/.github/workflows/build.yaml +50 -0
  6. rebalance-0.3.1/.github/workflows/lint.yaml +84 -0
  7. rebalance-0.3.1/.github/workflows/publish.yaml +131 -0
  8. rebalance-0.3.1/.github/workflows/release-please.yaml +38 -0
  9. rebalance-0.3.1/.github/workflows/test.yaml +69 -0
  10. rebalance-0.3.1/.gitignore +62 -0
  11. rebalance-0.3.1/.yamllint.yaml +11 -0
  12. rebalance-0.3.1/AGENTS.md +5 -0
  13. rebalance-0.3.1/CHANGELOG.md +36 -0
  14. rebalance-0.3.1/PKG-INFO +78 -0
  15. rebalance-0.3.1/README.md +61 -0
  16. rebalance-0.3.1/justfile +28 -0
  17. rebalance-0.3.1/portfolios/example.json +15 -0
  18. rebalance-0.3.1/prek.toml +88 -0
  19. rebalance-0.3.1/pyproject.toml +53 -0
  20. rebalance-0.3.1/rebalance/__init__.py +7 -0
  21. rebalance-0.3.1/rebalance/__main__.py +48 -0
  22. rebalance-0.3.1/rebalance/asset.py +166 -0
  23. rebalance-0.3.1/rebalance/fetchers.py +83 -0
  24. rebalance-0.3.1/rebalance/loader.py +76 -0
  25. rebalance-0.3.1/rebalance/logging_setup.py +66 -0
  26. rebalance-0.3.1/rebalance/money.py +135 -0
  27. rebalance-0.3.1/rebalance/notifications.py +71 -0
  28. rebalance-0.3.1/rebalance/portfolio.py +599 -0
  29. rebalance-0.3.1/rebalance/rebalancing_helper.py +162 -0
  30. rebalance-0.3.1/rebalance/schemas.py +59 -0
  31. rebalance-0.3.1/rebalance/tests/__init__.py +0 -0
  32. rebalance-0.3.1/rebalance/tests/asset_test.py +80 -0
  33. rebalance-0.3.1/rebalance/tests/cash_test.py +46 -0
  34. rebalance-0.3.1/rebalance/tests/cli_test.py +88 -0
  35. rebalance-0.3.1/rebalance/tests/conftest.py +39 -0
  36. rebalance-0.3.1/rebalance/tests/loader_test.py +217 -0
  37. rebalance-0.3.1/rebalance/tests/portfolio_test.py +254 -0
  38. rebalance-0.3.1/uv.lock +1195 -0
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.3.1"
3
+ }
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "Protect main branch",
3
+ "target": "branch",
4
+ "enforcement": "active",
5
+ "conditions": {
6
+ "ref_name": {
7
+ "include": [
8
+ "refs/heads/main"
9
+ ],
10
+ "exclude": []
11
+ }
12
+ },
13
+ "rules": [
14
+ {
15
+ "type": "pull_request",
16
+ "parameters": {
17
+ "required_approving_review_count": 0,
18
+ "dismiss_stale_reviews_on_push": false,
19
+ "require_code_owner_review": false,
20
+ "require_last_push_approval": false,
21
+ "required_review_thread_resolution": false
22
+ }
23
+ },
24
+ {
25
+ "type": "deletion"
26
+ },
27
+ {
28
+ "type": "non_fast_forward"
29
+ },
30
+ {
31
+ "type": "required_linear_history"
32
+ },
33
+ {
34
+ "type": "required_signatures",
35
+ "parameters": {}
36
+ }
37
+ ],
38
+ "bypass_actors": [
39
+ {
40
+ "actor_id": 5,
41
+ "actor_type": "RepositoryRole",
42
+ "bypass_mode": "always"
43
+ }
44
+ ]
45
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
3
+ "release-type": "python",
4
+ "packages": {
5
+ ".": {
6
+ "release-type": "python",
7
+ "package-name": "rebalance",
8
+ "include-component-in-tag": false,
9
+ "bump-minor-pre-major": true,
10
+ "bump-patch-for-minor-pre-major": false
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,53 @@
1
+ {
2
+ $schema: 'https://docs.renovatebot.com/renovate-schema.json',
3
+ extends: [
4
+ 'config:recommended',
5
+ 'customManagers:githubActionsVersions',
6
+ ],
7
+ schedule: [
8
+ 'every weekend',
9
+ ],
10
+ labels: [
11
+ 'dependencies',
12
+ ],
13
+ customManagers: [
14
+ {
15
+ customType: 'regex',
16
+ managerFilePatterns: [
17
+ '/^prek\\.toml$/',
18
+ ],
19
+ matchStrings: [
20
+ '# renovate: datasource=(?<datasource>[\\w-]+) depName=(?<depName>\\S+)\\s+rev\\s*=\\s*"(?<currentValue>[^"]+)"',
21
+ ],
22
+ versioningTemplate: 'semver',
23
+ },
24
+ {
25
+ customType: 'regex',
26
+ managerFilePatterns: [
27
+ '/^\\.github/workflows/.*\\.ya?ml$/',
28
+ ],
29
+ matchStrings: [
30
+ '# renovate: datasource=(?<datasource>[\\w-]+) depName=(?<depName>\\S+)\\s+PREK_VERSION:\\s+"(?<currentValue>[^"]+)"',
31
+ ],
32
+ versioningTemplate: 'semver',
33
+ },
34
+ ],
35
+ packageRules: [
36
+ {
37
+ matchManagers: [
38
+ 'github-actions',
39
+ ],
40
+ groupName: 'GitHub Actions',
41
+ pinDigests: true,
42
+ },
43
+ {
44
+ matchManagers: [
45
+ 'pip_requirements',
46
+ 'pip-compile',
47
+ 'pipenv',
48
+ 'pep621',
49
+ ],
50
+ groupName: 'Python dependencies',
51
+ },
52
+ ],
53
+ }
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: Build
3
+
4
+ "on":
5
+ pull_request:
6
+ branches: [main, develop]
7
+
8
+ concurrency:
9
+ group: ${{ github.workflow }}-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ # Deny all permissions by default for security
13
+ permissions: {}
14
+
15
+ jobs:
16
+ build:
17
+ name: Build Package
18
+ runs-on: ubuntu-latest
19
+ permissions:
20
+ contents: read
21
+ steps:
22
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
23
+ with:
24
+ persist-credentials: false
25
+
26
+ - name: Install uv
27
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
28
+ with:
29
+ version: "latest"
30
+ enable-cache: true
31
+ github-token: ${{ secrets.GITHUB_TOKEN }}
32
+
33
+ - name: Set up Python
34
+ run: uv python install 3.12
35
+
36
+ - name: Install dependencies
37
+ run: uv sync --group dev
38
+
39
+ - name: Install just
40
+ uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4
41
+
42
+ - name: Build package
43
+ run: uv run just build
44
+
45
+ - name: Upload build artifacts
46
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
47
+ with:
48
+ name: dist
49
+ path: dist/
50
+ retention-days: 7
@@ -0,0 +1,84 @@
1
+ ---
2
+ name: Lint
3
+
4
+ "on":
5
+ pull_request:
6
+ branches: ["**"]
7
+
8
+ concurrency:
9
+ group: ${{ github.workflow }}-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ permissions: {}
13
+
14
+ jobs:
15
+ pre-commit:
16
+ name: Pre-commit Hooks
17
+ runs-on: ubuntu-latest
18
+ permissions:
19
+ contents: read
20
+ steps:
21
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
22
+ with:
23
+ persist-credentials: false
24
+
25
+ - name: Install uv
26
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
27
+ with:
28
+ version: "latest"
29
+ enable-cache: true
30
+ github-token: ${{ secrets.GITHUB_TOKEN }}
31
+
32
+ - name: Set up Python
33
+ run: uv python install 3.12
34
+
35
+ - name: Install dependencies
36
+ run: uv sync --group dev
37
+
38
+ - name: Install prek
39
+ env:
40
+ # renovate: datasource=github-releases depName=j178/prek
41
+ PREK_VERSION: "0.3.13"
42
+ run: |
43
+ curl --proto '=https' --tlsv1.2 -LsSf \
44
+ "https://github.com/j178/prek/releases/download/v${PREK_VERSION}/prek-x86_64-unknown-linux-gnu.tar.gz" \
45
+ | tar -xz --strip-components=1 -C /usr/local/bin
46
+
47
+ - name: Install just
48
+ uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4
49
+
50
+ - name: Run linters
51
+ run: uv run just lint
52
+
53
+ actionlint:
54
+ name: GitHub Actions Syntax
55
+ runs-on: ubuntu-latest
56
+ permissions:
57
+ contents: read # Required to checkout repository code
58
+ pull-requests: write # Required for reviewdog to post PR review comments
59
+ steps:
60
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
61
+ with:
62
+ persist-credentials: false
63
+
64
+ - name: Run actionlint
65
+ uses: reviewdog/action-actionlint@6fb7acc99f4a1008869fa8a0f09cfca740837d9d # v1.72
66
+ with:
67
+ reporter: github-pr-review
68
+ github_token: ${{ secrets.GITHUB_TOKEN }}
69
+
70
+ zizmor:
71
+ name: Security Audit with zizmor
72
+ runs-on: ubuntu-latest
73
+ permissions:
74
+ security-events: write # Required to upload SARIF file to GitHub Security
75
+ contents: read # Required to checkout repository code
76
+ actions: read # Required to read workflow and action metadata
77
+ steps:
78
+ - name: Checkout repository
79
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
80
+ with:
81
+ persist-credentials: false
82
+
83
+ - name: Run zizmor
84
+ uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3
@@ -0,0 +1,131 @@
1
+ ---
2
+ name: Publish to PyPI
3
+
4
+ "on":
5
+ release:
6
+ types: [published]
7
+
8
+ concurrency:
9
+ group: ${{ github.workflow }}-${{ github.ref }}
10
+ cancel-in-progress: false
11
+
12
+ # Deny all permissions by default for security
13
+ permissions: {}
14
+
15
+ jobs:
16
+ publish:
17
+ name: Publish to PyPI
18
+ runs-on: ubuntu-latest
19
+ # Grant minimal permissions required for PyPI trusted publishing
20
+ permissions:
21
+ actions: read # Required for SBOM action to find workflow artifacts when attaching release assets
22
+ contents: write # Required to upload release artifacts
23
+ id-token: write # Required for trusted publishing
24
+ attestations: write # Required for generating attestations
25
+ environment:
26
+ name: pypi
27
+ url: https://pypi.org/p/rebalance
28
+
29
+ steps:
30
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
31
+ with:
32
+ persist-credentials: false
33
+
34
+ - name: Install uv
35
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
36
+ with:
37
+ version: "latest"
38
+ enable-cache: false # avoids cache poisoning upon build and publish
39
+ github-token: ${{ secrets.GITHUB_TOKEN }}
40
+
41
+ - name: Set up Python
42
+ run: uv python install 3.12
43
+
44
+ - name: Install dependencies
45
+ run: uv sync --group dev
46
+
47
+ - name: Install just
48
+ uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4
49
+
50
+ - name: Build package
51
+ run: uv run just build
52
+
53
+ - name: Generate SHA256 checksums
54
+ run: |
55
+ cd dist
56
+ sha256sum ./* > SHA256SUMS
57
+ cat SHA256SUMS
58
+
59
+ - name: Generate SBOM
60
+ uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
61
+ with:
62
+ path: ./
63
+ artifact-name: sbom.spdx.json
64
+ output-file: dist/sbom.spdx.json
65
+ format: spdx-json
66
+ upload-artifact: true
67
+ upload-release-assets: false
68
+ dependency-snapshot: true
69
+
70
+ - name: Attest SBOM
71
+ uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
72
+ with:
73
+ subject-path: "dist/*.whl"
74
+ sbom-path: "dist/sbom.spdx.json"
75
+
76
+ - name: Generate build provenance attestations
77
+ id: attest
78
+ uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
79
+ with:
80
+ subject-path: "dist/*"
81
+
82
+ - name: Publish to PyPI with trusted publishing
83
+ run: uv publish
84
+
85
+ - name: Capture build metadata
86
+ id: build-meta
87
+ run: |
88
+ {
89
+ echo "python_version=$(uv run python --version | cut -d' ' -f2)"
90
+ echo "uv_version=$(uv --version | cut -d' ' -f2)"
91
+ echo "build_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
92
+ } >> "$GITHUB_OUTPUT"
93
+
94
+ - name: Create GitHub Release
95
+ env:
96
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
97
+ ATTESTATION_URL: ${{ steps.attest.outputs.attestation-url }}
98
+ PYTHON_VERSION: ${{ steps.build-meta.outputs.python_version }}
99
+ UV_VERSION: ${{ steps.build-meta.outputs.uv_version }}
100
+ BUILD_TIME: ${{ steps.build-meta.outputs.build_time }}
101
+ run: |
102
+ # Preserve existing release notes from release-please
103
+ gh release view "$GITHUB_REF_NAME" --json body --jq '.body // ""' \
104
+ > /tmp/release-notes.md
105
+
106
+ # Append build metadata and verification instructions
107
+ cat >> /tmp/release-notes.md << EOF
108
+
109
+ ---
110
+
111
+ ### Build Information
112
+ - **Python:** ${PYTHON_VERSION}
113
+ - **uv:** ${UV_VERSION}
114
+ - **Built:** ${BUILD_TIME}
115
+ - **Commit:** ${GITHUB_SHA}
116
+
117
+ ### Verification
118
+
119
+ This release includes [SLSA Build Level 3](https://slsa.dev/spec/v1.0/levels) attestations.
120
+
121
+ \`\`\`bash
122
+ gh attestation verify rebalance-*.whl --owner ${GITHUB_REPOSITORY_OWNER}
123
+ gh attestation verify rebalance-*.whl --owner ${GITHUB_REPOSITORY_OWNER} --sbom
124
+ \`\`\`
125
+
126
+ View SLSA Provenance: ${ATTESTATION_URL}
127
+ EOF
128
+
129
+ gh release edit "$GITHUB_REF_NAME" --notes-file /tmp/release-notes.md
130
+ gh release upload "$GITHUB_REF_NAME" \
131
+ dist/*.whl dist/*.tar.gz dist/sbom.spdx.json dist/SHA256SUMS LICENSE
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: Release Please
3
+
4
+ "on":
5
+ push:
6
+ branches:
7
+ - main
8
+
9
+ concurrency:
10
+ group: ${{ github.workflow }}-${{ github.ref }}
11
+ cancel-in-progress: false
12
+
13
+ # Deny all permissions by default for security
14
+ permissions: {}
15
+
16
+ jobs:
17
+ release-please:
18
+ name: Create Release PR
19
+ runs-on: ubuntu-latest
20
+ # Grant minimal permissions required for release-please to create releases and PRs
21
+ permissions:
22
+ contents: write # Required to create releases and push commits
23
+ issues: write # Required to create release issues
24
+ pull-requests: write # Required to create and update release PRs
25
+ steps:
26
+ - name: Generate GitHub App token
27
+ id: app-token
28
+ uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
29
+ with:
30
+ client-id: ${{ secrets.RELEASE_PLEASE_APP_ID }}
31
+ private-key: ${{ secrets.RELEASE_PLEASE_APP_PRIVATE_KEY }}
32
+
33
+ - uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
34
+ id: release
35
+ with:
36
+ token: ${{ steps.app-token.outputs.token }}
37
+ config-file: .github/release-please-config.json
38
+ manifest-file: .github/.release-please-manifest.json
@@ -0,0 +1,69 @@
1
+ ---
2
+ name: Tests
3
+
4
+ "on":
5
+ pull_request:
6
+ branches: [main, develop]
7
+ paths:
8
+ - "rebalance/**"
9
+ - "pyproject.toml"
10
+ - "uv.lock"
11
+
12
+ concurrency:
13
+ group: ${{ github.workflow }}-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ # Deny all permissions by default for security
17
+ permissions: {}
18
+
19
+ jobs:
20
+ test:
21
+ name: Test (Python ${{ matrix.python-version }})
22
+ runs-on: ubuntu-latest
23
+ permissions:
24
+ contents: read
25
+ strategy:
26
+ fail-fast: false
27
+ matrix:
28
+ python-version: ["3.11", "3.12", "3.13"]
29
+
30
+ env:
31
+ PYTHON_VERSION: ${{ matrix.python-version }}
32
+
33
+ steps:
34
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
35
+ with:
36
+ persist-credentials: false
37
+
38
+ - name: Install uv
39
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
40
+ with:
41
+ version: "latest"
42
+ enable-cache: true
43
+ github-token: ${{ secrets.GITHUB_TOKEN }}
44
+
45
+ - name: Set up Python ${{ env.PYTHON_VERSION }}
46
+ run: |
47
+ # Use env variable to avoid template injection
48
+ uv python install "$PYTHON_VERSION"
49
+
50
+ - name: Install dependencies
51
+ run: |
52
+ uv sync --group dev
53
+
54
+ - name: Run all tests with coverage
55
+ run: |
56
+ uv run pytest rebalance/tests/ -v \
57
+ --cov=rebalance \
58
+ --cov-report=xml \
59
+ --cov-report=term-missing \
60
+ --cov-branch
61
+
62
+ - name: Upload coverage to Codecov
63
+ uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6
64
+ if: always()
65
+ with:
66
+ files: ./coverage.xml
67
+ fail_ci_if_error: false
68
+ verbose: true
69
+ token: ${{ secrets.CODECOV_TOKEN }}
@@ -0,0 +1,62 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ .DS_Store
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ bin/
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ eggs/
15
+ lib/
16
+ lib64/
17
+ parts/
18
+ sdist/
19
+ var/
20
+ *.egg-info/
21
+ .installed.cfg
22
+ *.egg
23
+
24
+ # Installer logs
25
+ pip-log.txt
26
+ pip-delete-this-directory.txt
27
+
28
+ # Unit test / coverage reports
29
+ .tox/
30
+ .coverage
31
+ .cache
32
+ nosetests.xml
33
+ coverage.xml
34
+ htmlcov
35
+
36
+ # Translations
37
+ *.mo
38
+
39
+ # Mr Developer
40
+ .mr.developer.cfg
41
+ .project
42
+ .pydevproject
43
+
44
+ # Rope
45
+ .ropeproject
46
+
47
+ # Django stuff:
48
+ *.log
49
+ *.pot
50
+
51
+ # Portfolio data (contains personal financial positions)
52
+ portfolios/*.json
53
+ !portfolios/example.json
54
+
55
+ # Downloaded market data
56
+ *.csv
57
+
58
+ # Sphinx documentation
59
+ docs/_build/
60
+
61
+ # Data files
62
+ *.csv
@@ -0,0 +1,11 @@
1
+ ---
2
+ extends: default
3
+
4
+ rules:
5
+ line-length:
6
+ max: 120
7
+ indentation:
8
+ spaces: 2
9
+ indent-sequences: consistent
10
+ comments:
11
+ min-spaces-from-content: 1
@@ -0,0 +1,5 @@
1
+ # AGENTS.md
2
+
3
+ - Use tools when handling files. Never use the terminal for file handling.
4
+ - Use the tool when fetching websites. Never use the terminal for fetching websites.
5
+ - Use conventional commits when committing code.
@@ -0,0 +1,36 @@
1
+ # Changelog
2
+
3
+ ## [0.3.1](https://github.com/kallegrens/rebalance/compare/v0.3.0...v0.3.1) (2026-05-09)
4
+
5
+
6
+ ### Documentation
7
+
8
+ * replace README.rst with README.md (RST raw directives rejected by PyPI) ([e7da7e6](https://github.com/kallegrens/rebalance/commit/e7da7e67a8a74f434fdfb74935ba3263970511a8))
9
+
10
+ ## [0.3.0](https://github.com/kallegrens/rebalance/compare/v0.2.0...v0.3.0) (2026-05-09)
11
+
12
+
13
+ ### Features
14
+
15
+ * initial release ([2036616](https://github.com/kallegrens/rebalance/commit/203661664856c7fb233abd3dc578c0ba0cf00ee4))
16
+
17
+ ## [0.2.0](https://github.com/kallegrens/rebalance/compare/v0.1.0...v0.2.0) (2026-05-09)
18
+
19
+
20
+ ### Features
21
+
22
+ * add common_currency field and validator to PortfolioConfig ([fd8299b](https://github.com/kallegrens/rebalance/commit/fd8299b755d5563da59c90daa4dbe28e87c12772))
23
+ * add loguru logging with LOG_LEVEL env var and notification stub ([6247747](https://github.com/kallegrens/rebalance/commit/624774751381b81313ae9731f013871a88191446))
24
+
25
+
26
+ ### Bug Fixes
27
+
28
+ * improved table printing ([122fdb3](https://github.com/kallegrens/rebalance/commit/122fdb39de672626aaa2a034534f7885eeb4068f))
29
+ * set yfinance session to None ([19956de](https://github.com/kallegrens/rebalance/commit/19956de279be41221f14918a6a8409c2e1e6f8c4))
30
+ * switch currency dependency ([197200e](https://github.com/kallegrens/rebalance/commit/197200ea1e841a939bc7b6ac16cd100db79824ae))
31
+ * TargetException check ([eac494d](https://github.com/kallegrens/rebalance/commit/eac494d7f0d827b12c0e3d2707a93fbb698ec9e4))
32
+
33
+
34
+ ### Reverts
35
+
36
+ * remove Pipfile ([59f09f3](https://github.com/kallegrens/rebalance/commit/59f09f35b6d47d2ffbdc37be5a937e0ccfa142db))
@@ -0,0 +1,78 @@
1
+ Metadata-Version: 2.4
2
+ Name: rebalance
3
+ Version: 0.3.1
4
+ Summary: A calculator which tells you how to split your investment amongst your portfolio's assets based on your target asset allocation.
5
+ Project-URL: Homepage, https://github.com/kallegrens/rebalance
6
+ Author-email: Karl Lundgren <k.github@lundgrens.net>
7
+ Requires-Python: >=3.11
8
+ Requires-Dist: loguru>=0.7
9
+ Requires-Dist: numpy>=1.26.1
10
+ Requires-Dist: pydantic>=2.0
11
+ Requires-Dist: requests>=2.31.0
12
+ Requires-Dist: rich>=13.0
13
+ Requires-Dist: scipy>=1.11.3
14
+ Requires-Dist: tenacity>=9.1.4
15
+ Requires-Dist: yfinance>=0.2.65
16
+ Description-Content-Type: text/markdown
17
+
18
+ # Rebalance
19
+
20
+ A calculator that tells you how to split your investment amongst your portfolio's assets based on your target asset allocation.
21
+
22
+ To use it, install the package and write a portfolio JSON file as described below.
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ pip install rebalance
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ```bash
33
+ rebalance portfolios/my_portfolio.json
34
+ ```
35
+
36
+ ## Portfolio file format
37
+
38
+ Create a JSON file describing your portfolio:
39
+
40
+ ```json
41
+ {
42
+ "name": "My portfolio",
43
+ "selling_allowed": false,
44
+ "cash": [
45
+ {"amount": 3000.0, "currency": "USD"},
46
+ {"amount": 200.0, "currency": "CAD"}
47
+ ],
48
+ "assets": [
49
+ {"ticker": "XBB.TO", "quantity": 36, "target_allocation": 20},
50
+ {"ticker": "XIC.TO", "quantity": 64, "target_allocation": 20},
51
+ {"ticker": "ITOT", "quantity": 32, "target_allocation": 36},
52
+ {"ticker": "IEFA", "quantity": 8, "target_allocation": 20},
53
+ {"ticker": "IEMG", "quantity": 7, "target_allocation": 4}
54
+ ]
55
+ }
56
+ ```
57
+
58
+ ## Example output
59
+
60
+ ```
61
+ Ticker Ask Quantity Amount Currency Old allocation New allocation Target allocation
62
+ to buy ($) (%) (%) (%)
63
+ ---------------------------------------------------------------------------------------------------------------
64
+ XBB.TO 33.43 30 1002.90 CAD 17.52 19.99 20.00
65
+ XIC.TO 24.27 27 655.29 CAD 22.61 20.01 20.00
66
+ ITOT 69.38 10 693.80 USD 43.93 35.88 36.00
67
+ IEFA 57.65 20 1153.00 USD 9.13 19.88 20.00
68
+ IEMG 49.14 0 0.00 USD 6.81 4.24 4.00
69
+
70
+ Largest discrepancy between the new and the target asset allocation is 0.24 %.
71
+
72
+ Before making the above purchases, the following currency conversion is required:
73
+ 1072.88 USD to 1458.19 CAD at a rate of 1.3591.
74
+
75
+ Remaining cash:
76
+ 80.32 USD.
77
+ 0.00 CAD.
78
+ ```