jaxfolio 0.1.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.
- jaxfolio-0.1.0/.github/workflows/ci.yml +77 -0
- jaxfolio-0.1.0/.github/workflows/docs.yml +49 -0
- jaxfolio-0.1.0/.github/workflows/release.yml +21 -0
- jaxfolio-0.1.0/.gitignore +34 -0
- jaxfolio-0.1.0/LICENSE +21 -0
- jaxfolio-0.1.0/PKG-INFO +225 -0
- jaxfolio-0.1.0/README.md +182 -0
- jaxfolio-0.1.0/docs/dashboard.png +0 -0
- jaxfolio-0.1.0/docs/getting-started/concepts.md +212 -0
- jaxfolio-0.1.0/docs/getting-started/installation.md +109 -0
- jaxfolio-0.1.0/docs/getting-started/quickstart.md +154 -0
- jaxfolio-0.1.0/docs/guide/backtesting.md +139 -0
- jaxfolio-0.1.0/docs/guide/custom-strategies.md +154 -0
- jaxfolio-0.1.0/docs/guide/data.md +117 -0
- jaxfolio-0.1.0/docs/guide/llm.md +146 -0
- jaxfolio-0.1.0/docs/guide/optimizers.md +312 -0
- jaxfolio-0.1.0/docs/guide/options.md +162 -0
- jaxfolio-0.1.0/docs/guide/visualization.md +115 -0
- jaxfolio-0.1.0/docs/img/comparison_dashboard.png +0 -0
- jaxfolio-0.1.0/docs/img/correlation_network.png +0 -0
- jaxfolio-0.1.0/docs/img/custom_dashboard.png +0 -0
- jaxfolio-0.1.0/docs/img/dashboard.png +0 -0
- jaxfolio-0.1.0/docs/img/drawdown.png +0 -0
- jaxfolio-0.1.0/docs/img/equity_curves.png +0 -0
- jaxfolio-0.1.0/docs/img/hrp_dendrogram.png +0 -0
- jaxfolio-0.1.0/docs/img/iron_condor_greeks.png +0 -0
- jaxfolio-0.1.0/docs/img/iron_condor_payoff.png +0 -0
- jaxfolio-0.1.0/docs/img/llm_bl_weights.png +0 -0
- jaxfolio-0.1.0/docs/img/llm_strategies_frontier.png +0 -0
- jaxfolio-0.1.0/docs/img/quickstart_frontier.png +0 -0
- jaxfolio-0.1.0/docs/img/risk_contrib.png +0 -0
- jaxfolio-0.1.0/docs/img/straddle_payoff.png +0 -0
- jaxfolio-0.1.0/docs/img/vol_surface.png +0 -0
- jaxfolio-0.1.0/docs/index.md +127 -0
- jaxfolio-0.1.0/docs/javascripts/mathjax.js +19 -0
- jaxfolio-0.1.0/docs/logo.svg +11 -0
- jaxfolio-0.1.0/docs/reference/backtest.md +16 -0
- jaxfolio-0.1.0/docs/reference/data.md +33 -0
- jaxfolio-0.1.0/docs/reference/index.md +63 -0
- jaxfolio-0.1.0/docs/reference/llm.md +34 -0
- jaxfolio-0.1.0/docs/reference/optimizers.md +32 -0
- jaxfolio-0.1.0/docs/reference/options.md +27 -0
- jaxfolio-0.1.0/docs/reference/toolkit.md +23 -0
- jaxfolio-0.1.0/docs/reference/types.md +13 -0
- jaxfolio-0.1.0/docs/reference/viz.md +15 -0
- jaxfolio-0.1.0/docs/stylesheets/extra.css +411 -0
- jaxfolio-0.1.0/examples/01_quickstart.py +41 -0
- jaxfolio-0.1.0/examples/02_method_comparison.py +68 -0
- jaxfolio-0.1.0/examples/03_options_strategies.py +78 -0
- jaxfolio-0.1.0/examples/04_llm_strategies.py +100 -0
- jaxfolio-0.1.0/examples/05_custom_strategy.py +97 -0
- jaxfolio-0.1.0/mkdocs.yml +143 -0
- jaxfolio-0.1.0/pyproject.toml +94 -0
- jaxfolio-0.1.0/src/jaxfolio/__init__.py +110 -0
- jaxfolio-0.1.0/src/jaxfolio/backtest/__init__.py +39 -0
- jaxfolio-0.1.0/src/jaxfolio/backtest/engine.py +141 -0
- jaxfolio-0.1.0/src/jaxfolio/backtest/metrics.py +133 -0
- jaxfolio-0.1.0/src/jaxfolio/constraints/__init__.py +15 -0
- jaxfolio-0.1.0/src/jaxfolio/constraints/projections.py +87 -0
- jaxfolio-0.1.0/src/jaxfolio/custom.py +189 -0
- jaxfolio-0.1.0/src/jaxfolio/data/__init__.py +28 -0
- jaxfolio-0.1.0/src/jaxfolio/data/loaders.py +149 -0
- jaxfolio-0.1.0/src/jaxfolio/data/returns.py +96 -0
- jaxfolio-0.1.0/src/jaxfolio/data/synthetic.py +90 -0
- jaxfolio-0.1.0/src/jaxfolio/llm/__init__.py +45 -0
- jaxfolio-0.1.0/src/jaxfolio/llm/agents.py +116 -0
- jaxfolio-0.1.0/src/jaxfolio/llm/client.py +200 -0
- jaxfolio-0.1.0/src/jaxfolio/llm/sentiment.py +75 -0
- jaxfolio-0.1.0/src/jaxfolio/llm/strategies.py +134 -0
- jaxfolio-0.1.0/src/jaxfolio/llm/views.py +154 -0
- jaxfolio-0.1.0/src/jaxfolio/moments/__init__.py +19 -0
- jaxfolio-0.1.0/src/jaxfolio/moments/estimators.py +127 -0
- jaxfolio-0.1.0/src/jaxfolio/optimizers/__init__.py +41 -0
- jaxfolio-0.1.0/src/jaxfolio/optimizers/base.py +97 -0
- jaxfolio-0.1.0/src/jaxfolio/optimizers/classical.py +467 -0
- jaxfolio-0.1.0/src/jaxfolio/optimizers/graph.py +211 -0
- jaxfolio-0.1.0/src/jaxfolio/optimizers/learning.py +185 -0
- jaxfolio-0.1.0/src/jaxfolio/options/__init__.py +71 -0
- jaxfolio-0.1.0/src/jaxfolio/options/greeks.py +109 -0
- jaxfolio-0.1.0/src/jaxfolio/options/overlay.py +124 -0
- jaxfolio-0.1.0/src/jaxfolio/options/pricing.py +173 -0
- jaxfolio-0.1.0/src/jaxfolio/options/strategies.py +304 -0
- jaxfolio-0.1.0/src/jaxfolio/registry.py +131 -0
- jaxfolio-0.1.0/src/jaxfolio/results.py +83 -0
- jaxfolio-0.1.0/src/jaxfolio/toolkit.py +86 -0
- jaxfolio-0.1.0/src/jaxfolio/types.py +109 -0
- jaxfolio-0.1.0/src/jaxfolio/viz/__init__.py +39 -0
- jaxfolio-0.1.0/src/jaxfolio/viz/plots.py +922 -0
- jaxfolio-0.1.0/src/jaxfolio/viz/theme.py +112 -0
- jaxfolio-0.1.0/tests/conftest.py +19 -0
- jaxfolio-0.1.0/tests/test_backtest.py +67 -0
- jaxfolio-0.1.0/tests/test_constraints.py +62 -0
- jaxfolio-0.1.0/tests/test_data.py +45 -0
- jaxfolio-0.1.0/tests/test_llm.py +196 -0
- jaxfolio-0.1.0/tests/test_moments.py +67 -0
- jaxfolio-0.1.0/tests/test_optimizers_classical.py +97 -0
- jaxfolio-0.1.0/tests/test_optimizers_graph_learning.py +52 -0
- jaxfolio-0.1.0/tests/test_options.py +121 -0
- jaxfolio-0.1.0/tests/test_overlay_viz.py +56 -0
- jaxfolio-0.1.0/tests/test_registry_custom.py +147 -0
- jaxfolio-0.1.0/tests/test_robustness.py +166 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ci-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
lint:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- name: Install uv
|
|
19
|
+
uses: astral-sh/setup-uv@v5
|
|
20
|
+
with:
|
|
21
|
+
enable-cache: true
|
|
22
|
+
- name: Set up Python
|
|
23
|
+
run: uv python install 3.12
|
|
24
|
+
- name: Install dev deps
|
|
25
|
+
run: uv sync --all-extras
|
|
26
|
+
- name: Ruff lint
|
|
27
|
+
run: uv run ruff check .
|
|
28
|
+
- name: Ruff format check
|
|
29
|
+
run: uv run ruff format --check .
|
|
30
|
+
|
|
31
|
+
test:
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
strategy:
|
|
34
|
+
fail-fast: false
|
|
35
|
+
matrix:
|
|
36
|
+
python-version: ["3.11", "3.12"]
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@v4
|
|
39
|
+
- name: Install uv
|
|
40
|
+
uses: astral-sh/setup-uv@v5
|
|
41
|
+
with:
|
|
42
|
+
enable-cache: true
|
|
43
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
44
|
+
run: uv python install ${{ matrix.python-version }}
|
|
45
|
+
- name: Install deps
|
|
46
|
+
run: uv sync --all-extras
|
|
47
|
+
- name: Run tests
|
|
48
|
+
run: uv run pytest --cov=jaxfolio --cov-report=term-missing
|
|
49
|
+
|
|
50
|
+
build:
|
|
51
|
+
runs-on: ubuntu-latest
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@v4
|
|
54
|
+
- name: Install uv
|
|
55
|
+
uses: astral-sh/setup-uv@v5
|
|
56
|
+
- name: Build package
|
|
57
|
+
run: uv build
|
|
58
|
+
- name: Upload dist
|
|
59
|
+
uses: actions/upload-artifact@v4
|
|
60
|
+
with:
|
|
61
|
+
name: dist
|
|
62
|
+
path: dist/
|
|
63
|
+
|
|
64
|
+
docs:
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
steps:
|
|
67
|
+
- uses: actions/checkout@v4
|
|
68
|
+
- name: Install uv
|
|
69
|
+
uses: astral-sh/setup-uv@v5
|
|
70
|
+
with:
|
|
71
|
+
enable-cache: true
|
|
72
|
+
- name: Set up Python
|
|
73
|
+
run: uv python install 3.12
|
|
74
|
+
- name: Install docs deps
|
|
75
|
+
run: uv sync --extra docs
|
|
76
|
+
- name: Build docs (strict)
|
|
77
|
+
run: uv run mkdocs build --strict
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- "docs/**"
|
|
8
|
+
- "mkdocs.yml"
|
|
9
|
+
- "src/**" # API reference is generated from docstrings
|
|
10
|
+
- ".github/workflows/docs.yml"
|
|
11
|
+
workflow_dispatch: # allow manual runs
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: pages
|
|
15
|
+
cancel-in-progress: false
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
build:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
- name: Install uv
|
|
23
|
+
uses: astral-sh/setup-uv@v5
|
|
24
|
+
with:
|
|
25
|
+
enable-cache: true
|
|
26
|
+
- name: Set up Python
|
|
27
|
+
run: uv python install 3.12
|
|
28
|
+
- name: Install docs deps
|
|
29
|
+
run: uv sync --extra docs
|
|
30
|
+
- name: Build site
|
|
31
|
+
run: uv run mkdocs build --strict
|
|
32
|
+
- name: Upload Pages artifact
|
|
33
|
+
uses: actions/upload-pages-artifact@v3
|
|
34
|
+
with:
|
|
35
|
+
path: site
|
|
36
|
+
|
|
37
|
+
deploy:
|
|
38
|
+
needs: build
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
permissions:
|
|
41
|
+
pages: write # deploy to GitHub Pages
|
|
42
|
+
id-token: write # OIDC token used by deploy-pages
|
|
43
|
+
environment:
|
|
44
|
+
name: github-pages
|
|
45
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
46
|
+
steps:
|
|
47
|
+
- name: Deploy to GitHub Pages
|
|
48
|
+
id: deployment
|
|
49
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
environment: pypi
|
|
11
|
+
permissions:
|
|
12
|
+
# Trusted publishing to PyPI (no API token needed once configured on PyPI).
|
|
13
|
+
id-token: write
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- name: Install uv
|
|
17
|
+
uses: astral-sh/setup-uv@v5
|
|
18
|
+
- name: Build package
|
|
19
|
+
run: uv build
|
|
20
|
+
- name: Publish to PyPI
|
|
21
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
|
|
11
|
+
# uv
|
|
12
|
+
uv.lock
|
|
13
|
+
|
|
14
|
+
# Testing / coverage
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
.coverage
|
|
17
|
+
htmlcov/
|
|
18
|
+
.ruff_cache/
|
|
19
|
+
|
|
20
|
+
# Notebooks
|
|
21
|
+
.ipynb_checkpoints/
|
|
22
|
+
|
|
23
|
+
# Docs (mkdocs build output)
|
|
24
|
+
site/
|
|
25
|
+
|
|
26
|
+
# Generated artifacts
|
|
27
|
+
*.png
|
|
28
|
+
!docs/**/*.png
|
|
29
|
+
examples/output/
|
|
30
|
+
|
|
31
|
+
# OS / editors
|
|
32
|
+
.DS_Store
|
|
33
|
+
.idea/
|
|
34
|
+
.vscode/
|
jaxfolio-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 jaxfolio contributors
|
|
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.
|
jaxfolio-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jaxfolio
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Portfolio optimization and options strategies in JAX — traditional, learning-based, and graph-based methods with impressive dark-themed visualizations.
|
|
5
|
+
Project-URL: Homepage, https://github.com/bravant-oss/jaxfolio
|
|
6
|
+
Project-URL: Repository, https://github.com/bravant-oss/jaxfolio
|
|
7
|
+
Project-URL: Issues, https://github.com/bravant-oss/jaxfolio/issues
|
|
8
|
+
Author: jaxfolio contributors
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: black-litterman,finance,hierarchical-risk-parity,jax,options,portfolio-optimization,quant,risk-parity
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: jax>=0.4.28
|
|
22
|
+
Requires-Dist: jaxlib>=0.4.28
|
|
23
|
+
Requires-Dist: matplotlib>=3.8
|
|
24
|
+
Requires-Dist: numpy>=1.26
|
|
25
|
+
Requires-Dist: optax>=0.2.2
|
|
26
|
+
Requires-Dist: pandas>=2.1
|
|
27
|
+
Requires-Dist: scipy>=1.11
|
|
28
|
+
Provides-Extra: data
|
|
29
|
+
Requires-Dist: pyarrow>=15.0; extra == 'data'
|
|
30
|
+
Requires-Dist: yfinance>=0.2.40; extra == 'data'
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: ruff>=0.5.0; extra == 'dev'
|
|
35
|
+
Provides-Extra: docs
|
|
36
|
+
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
37
|
+
Requires-Dist: mkdocstrings[python]>=0.25; extra == 'docs'
|
|
38
|
+
Requires-Dist: pymdown-extensions>=10.0; extra == 'docs'
|
|
39
|
+
Requires-Dist: ruff>=0.5.0; extra == 'docs'
|
|
40
|
+
Provides-Extra: llm
|
|
41
|
+
Requires-Dist: requests>=2.31; extra == 'llm'
|
|
42
|
+
Description-Content-Type: text/markdown
|
|
43
|
+
|
|
44
|
+
<p align="center">
|
|
45
|
+
<img src="docs/logo.svg" alt="jaxfolio" width="120">
|
|
46
|
+
</p>
|
|
47
|
+
|
|
48
|
+
<h1 align="center">jaxfolio</h1>
|
|
49
|
+
|
|
50
|
+
<p align="center">
|
|
51
|
+
Differentiable portfolio optimization & options strategies, powered by JAX.
|
|
52
|
+
</p>
|
|
53
|
+
|
|
54
|
+
<p align="center">
|
|
55
|
+
<a href="https://bravant-oss.github.io/jaxfolio/"><img alt="docs" src="https://img.shields.io/badge/docs-jaxfolio-199e70?style=flat-square"></a>
|
|
56
|
+
<img alt="python" src="https://img.shields.io/badge/python-3.11+-3987e5?style=flat-square">
|
|
57
|
+
<img alt="jax" src="https://img.shields.io/badge/JAX-9085e9?style=flat-square">
|
|
58
|
+
<img alt="license" src="https://img.shields.io/badge/MIT-c98500?style=flat-square">
|
|
59
|
+
</p>
|
|
60
|
+
|
|
61
|
+
<br>
|
|
62
|
+
|
|
63
|
+
## Why jaxfolio
|
|
64
|
+
|
|
65
|
+
Portfolio construction has splintered into many methods — mean-variance, risk
|
|
66
|
+
parity, hierarchical clustering, learned policies — and, increasingly, options
|
|
67
|
+
overlays layered on top of an equity book. Each is powerful, but in practice they
|
|
68
|
+
arrive as **disconnected tools**: a QP solver here, a clustering script there, a
|
|
69
|
+
separate options pricer, each with its own inputs, quirks, and no common way to
|
|
70
|
+
compare them or hedge across them.
|
|
71
|
+
|
|
72
|
+
That fragmentation is the real cost. Swapping one strategy for another means
|
|
73
|
+
rewriting glue code; comparing them fairly means re-implementing the same
|
|
74
|
+
backtest three times; and taking a *gradient through* an allocation — the thing
|
|
75
|
+
modern, learning-based methods depend on — is simply impossible when the pieces
|
|
76
|
+
don't share a numerical foundation.
|
|
77
|
+
|
|
78
|
+
**jaxfolio unifies them on a single differentiable core.** Sixteen optimizers —
|
|
79
|
+
classical, learning-based, and graph-based — sit behind one interface,
|
|
80
|
+
`method(returns) → PortfolioResult`, and every constrained method is the *same*
|
|
81
|
+
jit-compiled projected-gradient solver with a different objective. Because the
|
|
82
|
+
whole pipeline (moment estimation → optimization → backtest) is JAX, it is
|
|
83
|
+
end-to-end differentiable and fast: you can backtest thousands of rebalances,
|
|
84
|
+
differentiate through an optimizer to train an allocation policy, and get exact
|
|
85
|
+
option Greeks for an entire chain from the same autodiff that prices it.
|
|
86
|
+
|
|
87
|
+
<br>
|
|
88
|
+
|
|
89
|
+
## Install
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
uv add jaxfolio # core
|
|
93
|
+
uv add "jaxfolio[data]" # + Yahoo Finance / Parquet loaders
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Quickstart
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
import jaxfolio as jf
|
|
100
|
+
from jaxfolio.backtest import compare
|
|
101
|
+
from jaxfolio import viz
|
|
102
|
+
|
|
103
|
+
returns = jf.generate_returns(n_assets=10, seed=7) # or load_yfinance / load_csv
|
|
104
|
+
|
|
105
|
+
results = compare(returns, {
|
|
106
|
+
"Max Sharpe": jf.maximum_sharpe,
|
|
107
|
+
"HRP": jf.hierarchical_risk_parity,
|
|
108
|
+
"Risk Parity": jf.risk_parity,
|
|
109
|
+
"1/N": jf.equal_weight,
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
viz.save(viz.dashboard(results, returns), "dashboard.png")
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
<p align="center">
|
|
116
|
+
<img src="docs/dashboard.png" alt="jaxfolio dashboard" width="880">
|
|
117
|
+
</p>
|
|
118
|
+
|
|
119
|
+
## Capabilities
|
|
120
|
+
|
|
121
|
+
| | |
|
|
122
|
+
|---|---|
|
|
123
|
+
| **Traditional** | min-variance · mean-variance · max-Sharpe · max-diversification · risk parity (ERC) · Kelly · min-CVaR · Black–Litterman |
|
|
124
|
+
| **Learning** | differentiable MLP Sharpe policy · online exponentiated-gradient |
|
|
125
|
+
| **Graph** | hierarchical risk parity (HRP) · HERC · MST centrality |
|
|
126
|
+
| **LLM (local)** | LLM→Black-Litterman views · news-sentiment tilt · multi-agent debate — all on a local Ollama model, no API keys |
|
|
127
|
+
| **Options** | Black-Scholes pricing · Greeks via autodiff · implied vol · 10+ multi-leg strategies · collar / covered-call overlays |
|
|
128
|
+
| **Extensible** | register your own strategy · a `toolkit` of reusable building blocks · works everywhere the built-ins do |
|
|
129
|
+
| **Backtest** | walk-forward engine · costs & turnover · Sharpe / Sortino / Calmar / VaR / CVaR / drawdown |
|
|
130
|
+
| **Data** | synthetic GBM · CSV · Parquet · Yahoo Finance · option chains |
|
|
131
|
+
|
|
132
|
+
## Options
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from jaxfolio.options import collar
|
|
136
|
+
from jaxfolio import viz
|
|
137
|
+
|
|
138
|
+
strat = collar(spot=100, put_strike=95, call_strike=110, expiry=0.25, vol=0.22)
|
|
139
|
+
strat.greeks(spot=100, vol=0.22) # net delta / gamma / vega / theta / rho
|
|
140
|
+
viz.save(viz.plot_payoff(strat, spot=100), "collar.png")
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## LLM strategies (local models)
|
|
144
|
+
|
|
145
|
+
State-of-the-art LLM-driven allocation, running entirely on a **local** model via
|
|
146
|
+
[Ollama](https://ollama.com) — no API keys, no data leaving your machine. Each
|
|
147
|
+
strategy elicits per-asset **views** from the model and routes them through
|
|
148
|
+
Black-Litterman, so they inherit the equilibrium prior and constraints.
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
uv add "jaxfolio[llm]" # adds the local-model client
|
|
152
|
+
ollama serve && ollama pull llama3.1
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
import jaxfolio as jf
|
|
157
|
+
from jaxfolio.llm import OllamaClient
|
|
158
|
+
|
|
159
|
+
client = OllamaClient("llama3.1") # any local model: mistral, qwen2.5, gemma…
|
|
160
|
+
returns = jf.generate_returns(n_assets=8, seed=7)
|
|
161
|
+
|
|
162
|
+
# 1 — LLM-enhanced Black-Litterman (ICLR 2025): sampled views, confidence from variance.
|
|
163
|
+
bl = jf.llm_black_litterman(returns, client=client, samples=5)
|
|
164
|
+
|
|
165
|
+
# 2 — News-sentiment tilt from a local model.
|
|
166
|
+
news = {"AAPL": "record revenue, raised guidance", "TSLA": "recall concerns"}
|
|
167
|
+
sent = jf.llm_sentiment_portfolio(returns, news, client=client)
|
|
168
|
+
|
|
169
|
+
# 3 — Multi-agent debate (bull / bear / risk agents negotiate the views).
|
|
170
|
+
agents = jf.llm_agent_portfolio(returns, client=client)
|
|
171
|
+
|
|
172
|
+
print(bl.metadata["llm_views"], bl.metadata["llm_confidence"])
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
No model installed? Every strategy accepts an injected client, so a `FakeLLM`
|
|
176
|
+
runs the whole flow offline (this is how the tests and
|
|
177
|
+
`examples/04_llm_strategies.py` work). References:
|
|
178
|
+
[LLM-BLM (ICLR 2025)](https://github.com/youngandbin/LLM-BLM) ·
|
|
179
|
+
[AlphaAgents](https://arxiv.org/abs/2508.11152) ·
|
|
180
|
+
[HARLF](https://arxiv.org/abs/2507.18560).
|
|
181
|
+
|
|
182
|
+
## Custom strategies
|
|
183
|
+
|
|
184
|
+
Write your own strategy and it works everywhere the built-ins do — backtester,
|
|
185
|
+
`compare()`, and the plots. Register it by name, or hand the shared solver a JAX
|
|
186
|
+
objective via the `toolkit`.
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
import numpy as np, jax.numpy as jnp
|
|
190
|
+
import jaxfolio as jf
|
|
191
|
+
from jaxfolio.custom import custom_strategy, CustomStrategy
|
|
192
|
+
|
|
193
|
+
# Mode 1 — return weights directly (a momentum tilt).
|
|
194
|
+
momentum = custom_strategy(
|
|
195
|
+
"momentum",
|
|
196
|
+
lambda r: np.clip(((1 + r).prod() - 1).to_numpy(), 0, None),
|
|
197
|
+
register=True,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
# Mode 2 — supply a JAX objective; reuse the shared projected-gradient solver.
|
|
201
|
+
def entropy_minvar(w, ctx): # ctx exposes mu, cov, returns, assets, n
|
|
202
|
+
return w @ ctx.cov @ w - 0.002 * -jnp.sum(w * jnp.log(w + 1e-9))
|
|
203
|
+
|
|
204
|
+
strat = CustomStrategy.from_objective("entropy_minvar", entropy_minvar, register=True)
|
|
205
|
+
|
|
206
|
+
jf.list_strategies(custom_only=True) # ['entropy_minvar', 'momentum']
|
|
207
|
+
jf.get_strategy("momentum")(returns) # a full PortfolioResult
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
The `jaxfolio.toolkit` module exposes the reusable building blocks —
|
|
211
|
+
`moments`, `make_projection`, `solve_projected_gradient`, the projections, and
|
|
212
|
+
`finalize_result` — so custom strategies are written the same idiomatic way as
|
|
213
|
+
the built-ins.
|
|
214
|
+
|
|
215
|
+
## Development
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
uv sync --all-extras
|
|
219
|
+
uv run pytest
|
|
220
|
+
uv run ruff check . && uv run ruff format --check .
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
<br>
|
|
224
|
+
|
|
225
|
+
<p align="center"><sub>MIT © jaxfolio contributors</sub></p>
|
jaxfolio-0.1.0/README.md
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/logo.svg" alt="jaxfolio" width="120">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">jaxfolio</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
Differentiable portfolio optimization & options strategies, powered by JAX.
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://bravant-oss.github.io/jaxfolio/"><img alt="docs" src="https://img.shields.io/badge/docs-jaxfolio-199e70?style=flat-square"></a>
|
|
13
|
+
<img alt="python" src="https://img.shields.io/badge/python-3.11+-3987e5?style=flat-square">
|
|
14
|
+
<img alt="jax" src="https://img.shields.io/badge/JAX-9085e9?style=flat-square">
|
|
15
|
+
<img alt="license" src="https://img.shields.io/badge/MIT-c98500?style=flat-square">
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
<br>
|
|
19
|
+
|
|
20
|
+
## Why jaxfolio
|
|
21
|
+
|
|
22
|
+
Portfolio construction has splintered into many methods — mean-variance, risk
|
|
23
|
+
parity, hierarchical clustering, learned policies — and, increasingly, options
|
|
24
|
+
overlays layered on top of an equity book. Each is powerful, but in practice they
|
|
25
|
+
arrive as **disconnected tools**: a QP solver here, a clustering script there, a
|
|
26
|
+
separate options pricer, each with its own inputs, quirks, and no common way to
|
|
27
|
+
compare them or hedge across them.
|
|
28
|
+
|
|
29
|
+
That fragmentation is the real cost. Swapping one strategy for another means
|
|
30
|
+
rewriting glue code; comparing them fairly means re-implementing the same
|
|
31
|
+
backtest three times; and taking a *gradient through* an allocation — the thing
|
|
32
|
+
modern, learning-based methods depend on — is simply impossible when the pieces
|
|
33
|
+
don't share a numerical foundation.
|
|
34
|
+
|
|
35
|
+
**jaxfolio unifies them on a single differentiable core.** Sixteen optimizers —
|
|
36
|
+
classical, learning-based, and graph-based — sit behind one interface,
|
|
37
|
+
`method(returns) → PortfolioResult`, and every constrained method is the *same*
|
|
38
|
+
jit-compiled projected-gradient solver with a different objective. Because the
|
|
39
|
+
whole pipeline (moment estimation → optimization → backtest) is JAX, it is
|
|
40
|
+
end-to-end differentiable and fast: you can backtest thousands of rebalances,
|
|
41
|
+
differentiate through an optimizer to train an allocation policy, and get exact
|
|
42
|
+
option Greeks for an entire chain from the same autodiff that prices it.
|
|
43
|
+
|
|
44
|
+
<br>
|
|
45
|
+
|
|
46
|
+
## Install
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
uv add jaxfolio # core
|
|
50
|
+
uv add "jaxfolio[data]" # + Yahoo Finance / Parquet loaders
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Quickstart
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import jaxfolio as jf
|
|
57
|
+
from jaxfolio.backtest import compare
|
|
58
|
+
from jaxfolio import viz
|
|
59
|
+
|
|
60
|
+
returns = jf.generate_returns(n_assets=10, seed=7) # or load_yfinance / load_csv
|
|
61
|
+
|
|
62
|
+
results = compare(returns, {
|
|
63
|
+
"Max Sharpe": jf.maximum_sharpe,
|
|
64
|
+
"HRP": jf.hierarchical_risk_parity,
|
|
65
|
+
"Risk Parity": jf.risk_parity,
|
|
66
|
+
"1/N": jf.equal_weight,
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
viz.save(viz.dashboard(results, returns), "dashboard.png")
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
<p align="center">
|
|
73
|
+
<img src="docs/dashboard.png" alt="jaxfolio dashboard" width="880">
|
|
74
|
+
</p>
|
|
75
|
+
|
|
76
|
+
## Capabilities
|
|
77
|
+
|
|
78
|
+
| | |
|
|
79
|
+
|---|---|
|
|
80
|
+
| **Traditional** | min-variance · mean-variance · max-Sharpe · max-diversification · risk parity (ERC) · Kelly · min-CVaR · Black–Litterman |
|
|
81
|
+
| **Learning** | differentiable MLP Sharpe policy · online exponentiated-gradient |
|
|
82
|
+
| **Graph** | hierarchical risk parity (HRP) · HERC · MST centrality |
|
|
83
|
+
| **LLM (local)** | LLM→Black-Litterman views · news-sentiment tilt · multi-agent debate — all on a local Ollama model, no API keys |
|
|
84
|
+
| **Options** | Black-Scholes pricing · Greeks via autodiff · implied vol · 10+ multi-leg strategies · collar / covered-call overlays |
|
|
85
|
+
| **Extensible** | register your own strategy · a `toolkit` of reusable building blocks · works everywhere the built-ins do |
|
|
86
|
+
| **Backtest** | walk-forward engine · costs & turnover · Sharpe / Sortino / Calmar / VaR / CVaR / drawdown |
|
|
87
|
+
| **Data** | synthetic GBM · CSV · Parquet · Yahoo Finance · option chains |
|
|
88
|
+
|
|
89
|
+
## Options
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from jaxfolio.options import collar
|
|
93
|
+
from jaxfolio import viz
|
|
94
|
+
|
|
95
|
+
strat = collar(spot=100, put_strike=95, call_strike=110, expiry=0.25, vol=0.22)
|
|
96
|
+
strat.greeks(spot=100, vol=0.22) # net delta / gamma / vega / theta / rho
|
|
97
|
+
viz.save(viz.plot_payoff(strat, spot=100), "collar.png")
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## LLM strategies (local models)
|
|
101
|
+
|
|
102
|
+
State-of-the-art LLM-driven allocation, running entirely on a **local** model via
|
|
103
|
+
[Ollama](https://ollama.com) — no API keys, no data leaving your machine. Each
|
|
104
|
+
strategy elicits per-asset **views** from the model and routes them through
|
|
105
|
+
Black-Litterman, so they inherit the equilibrium prior and constraints.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
uv add "jaxfolio[llm]" # adds the local-model client
|
|
109
|
+
ollama serve && ollama pull llama3.1
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
import jaxfolio as jf
|
|
114
|
+
from jaxfolio.llm import OllamaClient
|
|
115
|
+
|
|
116
|
+
client = OllamaClient("llama3.1") # any local model: mistral, qwen2.5, gemma…
|
|
117
|
+
returns = jf.generate_returns(n_assets=8, seed=7)
|
|
118
|
+
|
|
119
|
+
# 1 — LLM-enhanced Black-Litterman (ICLR 2025): sampled views, confidence from variance.
|
|
120
|
+
bl = jf.llm_black_litterman(returns, client=client, samples=5)
|
|
121
|
+
|
|
122
|
+
# 2 — News-sentiment tilt from a local model.
|
|
123
|
+
news = {"AAPL": "record revenue, raised guidance", "TSLA": "recall concerns"}
|
|
124
|
+
sent = jf.llm_sentiment_portfolio(returns, news, client=client)
|
|
125
|
+
|
|
126
|
+
# 3 — Multi-agent debate (bull / bear / risk agents negotiate the views).
|
|
127
|
+
agents = jf.llm_agent_portfolio(returns, client=client)
|
|
128
|
+
|
|
129
|
+
print(bl.metadata["llm_views"], bl.metadata["llm_confidence"])
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
No model installed? Every strategy accepts an injected client, so a `FakeLLM`
|
|
133
|
+
runs the whole flow offline (this is how the tests and
|
|
134
|
+
`examples/04_llm_strategies.py` work). References:
|
|
135
|
+
[LLM-BLM (ICLR 2025)](https://github.com/youngandbin/LLM-BLM) ·
|
|
136
|
+
[AlphaAgents](https://arxiv.org/abs/2508.11152) ·
|
|
137
|
+
[HARLF](https://arxiv.org/abs/2507.18560).
|
|
138
|
+
|
|
139
|
+
## Custom strategies
|
|
140
|
+
|
|
141
|
+
Write your own strategy and it works everywhere the built-ins do — backtester,
|
|
142
|
+
`compare()`, and the plots. Register it by name, or hand the shared solver a JAX
|
|
143
|
+
objective via the `toolkit`.
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
import numpy as np, jax.numpy as jnp
|
|
147
|
+
import jaxfolio as jf
|
|
148
|
+
from jaxfolio.custom import custom_strategy, CustomStrategy
|
|
149
|
+
|
|
150
|
+
# Mode 1 — return weights directly (a momentum tilt).
|
|
151
|
+
momentum = custom_strategy(
|
|
152
|
+
"momentum",
|
|
153
|
+
lambda r: np.clip(((1 + r).prod() - 1).to_numpy(), 0, None),
|
|
154
|
+
register=True,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Mode 2 — supply a JAX objective; reuse the shared projected-gradient solver.
|
|
158
|
+
def entropy_minvar(w, ctx): # ctx exposes mu, cov, returns, assets, n
|
|
159
|
+
return w @ ctx.cov @ w - 0.002 * -jnp.sum(w * jnp.log(w + 1e-9))
|
|
160
|
+
|
|
161
|
+
strat = CustomStrategy.from_objective("entropy_minvar", entropy_minvar, register=True)
|
|
162
|
+
|
|
163
|
+
jf.list_strategies(custom_only=True) # ['entropy_minvar', 'momentum']
|
|
164
|
+
jf.get_strategy("momentum")(returns) # a full PortfolioResult
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
The `jaxfolio.toolkit` module exposes the reusable building blocks —
|
|
168
|
+
`moments`, `make_projection`, `solve_projected_gradient`, the projections, and
|
|
169
|
+
`finalize_result` — so custom strategies are written the same idiomatic way as
|
|
170
|
+
the built-ins.
|
|
171
|
+
|
|
172
|
+
## Development
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
uv sync --all-extras
|
|
176
|
+
uv run pytest
|
|
177
|
+
uv run ruff check . && uv run ruff format --check .
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
<br>
|
|
181
|
+
|
|
182
|
+
<p align="center"><sub>MIT © jaxfolio contributors</sub></p>
|
|
Binary file
|