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.
- rebalance-0.3.1/.github/.release-please-manifest.json +3 -0
- rebalance-0.3.1/.github/branch-protection-ruleset.json +45 -0
- rebalance-0.3.1/.github/release-please-config.json +13 -0
- rebalance-0.3.1/.github/renovate.json5 +53 -0
- rebalance-0.3.1/.github/workflows/build.yaml +50 -0
- rebalance-0.3.1/.github/workflows/lint.yaml +84 -0
- rebalance-0.3.1/.github/workflows/publish.yaml +131 -0
- rebalance-0.3.1/.github/workflows/release-please.yaml +38 -0
- rebalance-0.3.1/.github/workflows/test.yaml +69 -0
- rebalance-0.3.1/.gitignore +62 -0
- rebalance-0.3.1/.yamllint.yaml +11 -0
- rebalance-0.3.1/AGENTS.md +5 -0
- rebalance-0.3.1/CHANGELOG.md +36 -0
- rebalance-0.3.1/PKG-INFO +78 -0
- rebalance-0.3.1/README.md +61 -0
- rebalance-0.3.1/justfile +28 -0
- rebalance-0.3.1/portfolios/example.json +15 -0
- rebalance-0.3.1/prek.toml +88 -0
- rebalance-0.3.1/pyproject.toml +53 -0
- rebalance-0.3.1/rebalance/__init__.py +7 -0
- rebalance-0.3.1/rebalance/__main__.py +48 -0
- rebalance-0.3.1/rebalance/asset.py +166 -0
- rebalance-0.3.1/rebalance/fetchers.py +83 -0
- rebalance-0.3.1/rebalance/loader.py +76 -0
- rebalance-0.3.1/rebalance/logging_setup.py +66 -0
- rebalance-0.3.1/rebalance/money.py +135 -0
- rebalance-0.3.1/rebalance/notifications.py +71 -0
- rebalance-0.3.1/rebalance/portfolio.py +599 -0
- rebalance-0.3.1/rebalance/rebalancing_helper.py +162 -0
- rebalance-0.3.1/rebalance/schemas.py +59 -0
- rebalance-0.3.1/rebalance/tests/__init__.py +0 -0
- rebalance-0.3.1/rebalance/tests/asset_test.py +80 -0
- rebalance-0.3.1/rebalance/tests/cash_test.py +46 -0
- rebalance-0.3.1/rebalance/tests/cli_test.py +88 -0
- rebalance-0.3.1/rebalance/tests/conftest.py +39 -0
- rebalance-0.3.1/rebalance/tests/loader_test.py +217 -0
- rebalance-0.3.1/rebalance/tests/portfolio_test.py +254 -0
- rebalance-0.3.1/uv.lock +1195 -0
|
@@ -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,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))
|
rebalance-0.3.1/PKG-INFO
ADDED
|
@@ -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
|
+
```
|