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.
Files changed (101) hide show
  1. jaxfolio-0.1.0/.github/workflows/ci.yml +77 -0
  2. jaxfolio-0.1.0/.github/workflows/docs.yml +49 -0
  3. jaxfolio-0.1.0/.github/workflows/release.yml +21 -0
  4. jaxfolio-0.1.0/.gitignore +34 -0
  5. jaxfolio-0.1.0/LICENSE +21 -0
  6. jaxfolio-0.1.0/PKG-INFO +225 -0
  7. jaxfolio-0.1.0/README.md +182 -0
  8. jaxfolio-0.1.0/docs/dashboard.png +0 -0
  9. jaxfolio-0.1.0/docs/getting-started/concepts.md +212 -0
  10. jaxfolio-0.1.0/docs/getting-started/installation.md +109 -0
  11. jaxfolio-0.1.0/docs/getting-started/quickstart.md +154 -0
  12. jaxfolio-0.1.0/docs/guide/backtesting.md +139 -0
  13. jaxfolio-0.1.0/docs/guide/custom-strategies.md +154 -0
  14. jaxfolio-0.1.0/docs/guide/data.md +117 -0
  15. jaxfolio-0.1.0/docs/guide/llm.md +146 -0
  16. jaxfolio-0.1.0/docs/guide/optimizers.md +312 -0
  17. jaxfolio-0.1.0/docs/guide/options.md +162 -0
  18. jaxfolio-0.1.0/docs/guide/visualization.md +115 -0
  19. jaxfolio-0.1.0/docs/img/comparison_dashboard.png +0 -0
  20. jaxfolio-0.1.0/docs/img/correlation_network.png +0 -0
  21. jaxfolio-0.1.0/docs/img/custom_dashboard.png +0 -0
  22. jaxfolio-0.1.0/docs/img/dashboard.png +0 -0
  23. jaxfolio-0.1.0/docs/img/drawdown.png +0 -0
  24. jaxfolio-0.1.0/docs/img/equity_curves.png +0 -0
  25. jaxfolio-0.1.0/docs/img/hrp_dendrogram.png +0 -0
  26. jaxfolio-0.1.0/docs/img/iron_condor_greeks.png +0 -0
  27. jaxfolio-0.1.0/docs/img/iron_condor_payoff.png +0 -0
  28. jaxfolio-0.1.0/docs/img/llm_bl_weights.png +0 -0
  29. jaxfolio-0.1.0/docs/img/llm_strategies_frontier.png +0 -0
  30. jaxfolio-0.1.0/docs/img/quickstart_frontier.png +0 -0
  31. jaxfolio-0.1.0/docs/img/risk_contrib.png +0 -0
  32. jaxfolio-0.1.0/docs/img/straddle_payoff.png +0 -0
  33. jaxfolio-0.1.0/docs/img/vol_surface.png +0 -0
  34. jaxfolio-0.1.0/docs/index.md +127 -0
  35. jaxfolio-0.1.0/docs/javascripts/mathjax.js +19 -0
  36. jaxfolio-0.1.0/docs/logo.svg +11 -0
  37. jaxfolio-0.1.0/docs/reference/backtest.md +16 -0
  38. jaxfolio-0.1.0/docs/reference/data.md +33 -0
  39. jaxfolio-0.1.0/docs/reference/index.md +63 -0
  40. jaxfolio-0.1.0/docs/reference/llm.md +34 -0
  41. jaxfolio-0.1.0/docs/reference/optimizers.md +32 -0
  42. jaxfolio-0.1.0/docs/reference/options.md +27 -0
  43. jaxfolio-0.1.0/docs/reference/toolkit.md +23 -0
  44. jaxfolio-0.1.0/docs/reference/types.md +13 -0
  45. jaxfolio-0.1.0/docs/reference/viz.md +15 -0
  46. jaxfolio-0.1.0/docs/stylesheets/extra.css +411 -0
  47. jaxfolio-0.1.0/examples/01_quickstart.py +41 -0
  48. jaxfolio-0.1.0/examples/02_method_comparison.py +68 -0
  49. jaxfolio-0.1.0/examples/03_options_strategies.py +78 -0
  50. jaxfolio-0.1.0/examples/04_llm_strategies.py +100 -0
  51. jaxfolio-0.1.0/examples/05_custom_strategy.py +97 -0
  52. jaxfolio-0.1.0/mkdocs.yml +143 -0
  53. jaxfolio-0.1.0/pyproject.toml +94 -0
  54. jaxfolio-0.1.0/src/jaxfolio/__init__.py +110 -0
  55. jaxfolio-0.1.0/src/jaxfolio/backtest/__init__.py +39 -0
  56. jaxfolio-0.1.0/src/jaxfolio/backtest/engine.py +141 -0
  57. jaxfolio-0.1.0/src/jaxfolio/backtest/metrics.py +133 -0
  58. jaxfolio-0.1.0/src/jaxfolio/constraints/__init__.py +15 -0
  59. jaxfolio-0.1.0/src/jaxfolio/constraints/projections.py +87 -0
  60. jaxfolio-0.1.0/src/jaxfolio/custom.py +189 -0
  61. jaxfolio-0.1.0/src/jaxfolio/data/__init__.py +28 -0
  62. jaxfolio-0.1.0/src/jaxfolio/data/loaders.py +149 -0
  63. jaxfolio-0.1.0/src/jaxfolio/data/returns.py +96 -0
  64. jaxfolio-0.1.0/src/jaxfolio/data/synthetic.py +90 -0
  65. jaxfolio-0.1.0/src/jaxfolio/llm/__init__.py +45 -0
  66. jaxfolio-0.1.0/src/jaxfolio/llm/agents.py +116 -0
  67. jaxfolio-0.1.0/src/jaxfolio/llm/client.py +200 -0
  68. jaxfolio-0.1.0/src/jaxfolio/llm/sentiment.py +75 -0
  69. jaxfolio-0.1.0/src/jaxfolio/llm/strategies.py +134 -0
  70. jaxfolio-0.1.0/src/jaxfolio/llm/views.py +154 -0
  71. jaxfolio-0.1.0/src/jaxfolio/moments/__init__.py +19 -0
  72. jaxfolio-0.1.0/src/jaxfolio/moments/estimators.py +127 -0
  73. jaxfolio-0.1.0/src/jaxfolio/optimizers/__init__.py +41 -0
  74. jaxfolio-0.1.0/src/jaxfolio/optimizers/base.py +97 -0
  75. jaxfolio-0.1.0/src/jaxfolio/optimizers/classical.py +467 -0
  76. jaxfolio-0.1.0/src/jaxfolio/optimizers/graph.py +211 -0
  77. jaxfolio-0.1.0/src/jaxfolio/optimizers/learning.py +185 -0
  78. jaxfolio-0.1.0/src/jaxfolio/options/__init__.py +71 -0
  79. jaxfolio-0.1.0/src/jaxfolio/options/greeks.py +109 -0
  80. jaxfolio-0.1.0/src/jaxfolio/options/overlay.py +124 -0
  81. jaxfolio-0.1.0/src/jaxfolio/options/pricing.py +173 -0
  82. jaxfolio-0.1.0/src/jaxfolio/options/strategies.py +304 -0
  83. jaxfolio-0.1.0/src/jaxfolio/registry.py +131 -0
  84. jaxfolio-0.1.0/src/jaxfolio/results.py +83 -0
  85. jaxfolio-0.1.0/src/jaxfolio/toolkit.py +86 -0
  86. jaxfolio-0.1.0/src/jaxfolio/types.py +109 -0
  87. jaxfolio-0.1.0/src/jaxfolio/viz/__init__.py +39 -0
  88. jaxfolio-0.1.0/src/jaxfolio/viz/plots.py +922 -0
  89. jaxfolio-0.1.0/src/jaxfolio/viz/theme.py +112 -0
  90. jaxfolio-0.1.0/tests/conftest.py +19 -0
  91. jaxfolio-0.1.0/tests/test_backtest.py +67 -0
  92. jaxfolio-0.1.0/tests/test_constraints.py +62 -0
  93. jaxfolio-0.1.0/tests/test_data.py +45 -0
  94. jaxfolio-0.1.0/tests/test_llm.py +196 -0
  95. jaxfolio-0.1.0/tests/test_moments.py +67 -0
  96. jaxfolio-0.1.0/tests/test_optimizers_classical.py +97 -0
  97. jaxfolio-0.1.0/tests/test_optimizers_graph_learning.py +52 -0
  98. jaxfolio-0.1.0/tests/test_options.py +121 -0
  99. jaxfolio-0.1.0/tests/test_overlay_viz.py +56 -0
  100. jaxfolio-0.1.0/tests/test_registry_custom.py +147 -0
  101. 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.
@@ -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>
@@ -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