aihf 2.2.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.
- aihf-2.2.0/LICENSE +21 -0
- aihf-2.2.0/PKG-INFO +132 -0
- aihf-2.2.0/README.md +98 -0
- aihf-2.2.0/hedge_fund/README.md +119 -0
- aihf-2.2.0/hedge_fund/__init__.py +7 -0
- aihf-2.2.0/hedge_fund/backtesting/__init__.py +25 -0
- aihf-2.2.0/hedge_fund/backtesting/__main__.py +190 -0
- aihf-2.2.0/hedge_fund/backtesting/engine.py +299 -0
- aihf-2.2.0/hedge_fund/backtesting/fund.py +204 -0
- aihf-2.2.0/hedge_fund/backtesting/models.py +47 -0
- aihf-2.2.0/hedge_fund/brokers/__init__.py +7 -0
- aihf-2.2.0/hedge_fund/brokers/models.py +39 -0
- aihf-2.2.0/hedge_fund/brokers/protocol.py +34 -0
- aihf-2.2.0/hedge_fund/brokers/sim.py +58 -0
- aihf-2.2.0/hedge_fund/data/__init__.py +32 -0
- aihf-2.2.0/hedge_fund/data/cached.py +158 -0
- aihf-2.2.0/hedge_fund/data/client.py +303 -0
- aihf-2.2.0/hedge_fund/data/models.py +275 -0
- aihf-2.2.0/hedge_fund/data/protocol.py +88 -0
- aihf-2.2.0/hedge_fund/event_study/__init__.py +33 -0
- aihf-2.2.0/hedge_fund/event_study/__main__.py +135 -0
- aihf-2.2.0/hedge_fund/event_study/engine.py +413 -0
- aihf-2.2.0/hedge_fund/event_study/models.py +103 -0
- aihf-2.2.0/hedge_fund/event_study/plot.py +190 -0
- aihf-2.2.0/hedge_fund/event_study/stats.py +119 -0
- aihf-2.2.0/hedge_fund/features/__init__.py +20 -0
- aihf-2.2.0/hedge_fund/features/snapshot.py +199 -0
- aihf-2.2.0/hedge_fund/fund/__init__.py +23 -0
- aihf-2.2.0/hedge_fund/fund/example.yaml +29 -0
- aihf-2.2.0/hedge_fund/fund/spec.py +210 -0
- aihf-2.2.0/hedge_fund/llm/__init__.py +40 -0
- aihf-2.2.0/hedge_fund/llm/api_models.json +42 -0
- aihf-2.2.0/hedge_fund/llm/cache.py +48 -0
- aihf-2.2.0/hedge_fund/llm/client.py +222 -0
- aihf-2.2.0/hedge_fund/llm/registry.py +66 -0
- aihf-2.2.0/hedge_fund/llm/watch.py +99 -0
- aihf-2.2.0/hedge_fund/models.py +36 -0
- aihf-2.2.0/hedge_fund/paths.py +32 -0
- aihf-2.2.0/hedge_fund/pipeline/__init__.py +8 -0
- aihf-2.2.0/hedge_fund/pipeline/execution.py +53 -0
- aihf-2.2.0/hedge_fund/pipeline/models.py +57 -0
- aihf-2.2.0/hedge_fund/pipeline/run_cycle.py +173 -0
- aihf-2.2.0/hedge_fund/portfolio/__init__.py +8 -0
- aihf-2.2.0/hedge_fund/portfolio/construction.py +89 -0
- aihf-2.2.0/hedge_fund/risk/__init__.py +8 -0
- aihf-2.2.0/hedge_fund/risk/limits.py +82 -0
- aihf-2.2.0/hedge_fund/run.py +171 -0
- aihf-2.2.0/hedge_fund/signals/__init__.py +42 -0
- aihf-2.2.0/hedge_fund/signals/base.py +105 -0
- aihf-2.2.0/hedge_fund/signals/buffett.py +54 -0
- aihf-2.2.0/hedge_fund/signals/druckenmiller.py +63 -0
- aihf-2.2.0/hedge_fund/signals/graham.py +59 -0
- aihf-2.2.0/hedge_fund/signals/llm_agent.py +172 -0
- aihf-2.2.0/hedge_fund/signals/lynch.py +59 -0
- aihf-2.2.0/hedge_fund/signals/munger.py +55 -0
- aihf-2.2.0/hedge_fund/signals/pead.py +138 -0
- aihf-2.2.0/hedge_fund/strategies/deep-value.yaml +13 -0
- aihf-2.2.0/hedge_fund/strategies/earnings-drift.yaml +10 -0
- aihf-2.2.0/hedge_fund/strategies/fundamental-ls.yaml +17 -0
- aihf-2.2.0/hedge_fund/strategies/inflections.yaml +11 -0
- aihf-2.2.0/hedge_fund/tui/__init__.py +18 -0
- aihf-2.2.0/hedge_fund/tui/__main__.py +11 -0
- aihf-2.2.0/hedge_fund/tui/app.py +2099 -0
- aihf-2.2.0/hedge_fund/tui/app.tcss +437 -0
- aihf-2.2.0/hedge_fund/tui/keys.py +92 -0
- aihf-2.2.0/hedge_fund/tui/shared.py +264 -0
- aihf-2.2.0/hedge_fund/validation/__init__.py +5 -0
- aihf-2.2.0/pyproject.toml +57 -0
aihf-2.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Virat Singh
|
|
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.
|
aihf-2.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: aihf
|
|
3
|
+
Version: 2.2.0
|
|
4
|
+
Summary: An AI-powered hedge fund: LLM investor agents and quant alpha models you can build, backtest, and run from your terminal
|
|
5
|
+
Home-page: https://github.com/virattt/ai-hedge-fund
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: ai,hedge-fund,trading,llm,backtesting,agents
|
|
8
|
+
Author: Virat Singh
|
|
9
|
+
Author-email: virat.dot@gmail.com
|
|
10
|
+
Requires-Python: >=3.11,<4.0
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Requires-Dist: langchain-anthropic (==0.3.5)
|
|
17
|
+
Requires-Dist: langchain-deepseek (>=0.1.2,<0.2.0)
|
|
18
|
+
Requires-Dist: langchain-google-genai (>=2.0.11,<3.0.0)
|
|
19
|
+
Requires-Dist: langchain-openai (>=0.3.5,<0.4.0)
|
|
20
|
+
Requires-Dist: langchain-xai (>=0.2.5,<0.3.0)
|
|
21
|
+
Requires-Dist: matplotlib (>=3.9.2,<4.0.0)
|
|
22
|
+
Requires-Dist: numpy (>=1.24.0,<2.0.0)
|
|
23
|
+
Requires-Dist: pandas (>=2.1.0,<3.0.0)
|
|
24
|
+
Requires-Dist: pydantic (>=2.4.2,<3.0.0)
|
|
25
|
+
Requires-Dist: python-dotenv (==1.0.0)
|
|
26
|
+
Requires-Dist: pyyaml (>=6.0.3,<7.0.0)
|
|
27
|
+
Requires-Dist: requests (>=2.32.0,<3.0.0)
|
|
28
|
+
Requires-Dist: rich (>=14.2.0,<15.0.0)
|
|
29
|
+
Requires-Dist: scipy (>=1.11.0,<2.0.0)
|
|
30
|
+
Requires-Dist: textual (>=8.2.8,<9.0.0)
|
|
31
|
+
Project-URL: Repository, https://github.com/virattt/ai-hedge-fund
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# AI Hedge Fund
|
|
35
|
+
|
|
36
|
+
This is a proof of concept for an AI-powered hedge fund. The goal of this project is to explore the use of AI to make trading decisions. This project is for **educational** purposes only and is not intended for real trading or investment.
|
|
37
|
+
|
|
38
|
+
> **🚧 The project is evolving.** We're rebuilding it into a persistent, always-on AI hedge fund — a *fund* as a first-class entity you can backtest, paper-trade, and (opt-in) run live, with the investor agents reimagined as pluggable, backtestable "alpha models." Read the **[Vision →](VISION.md)** and the **[Roadmap →](ROADMAP.md)**.
|
|
39
|
+
|
|
40
|
+
Note: the system does not actually make any trades.
|
|
41
|
+
|
|
42
|
+
[](https://twitter.com/virattt)
|
|
43
|
+
|
|
44
|
+
## Disclaimer
|
|
45
|
+
|
|
46
|
+
This project is for **educational and research purposes only**.
|
|
47
|
+
|
|
48
|
+
- Not intended for real trading or investment
|
|
49
|
+
- No investment advice or guarantees provided
|
|
50
|
+
- Creator assumes no liability for financial losses
|
|
51
|
+
- Consult a financial advisor for investment decisions
|
|
52
|
+
- Past performance does not indicate future results
|
|
53
|
+
|
|
54
|
+
By using this software, you agree to use it solely for learning purposes.
|
|
55
|
+
|
|
56
|
+
## How to Install
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pipx install aihf
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
(or `uv tool install aihf`, or `pip install aihf` into an environment of your choice)
|
|
63
|
+
|
|
64
|
+
Then run it from anywhere:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
aihf
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### API keys
|
|
71
|
+
|
|
72
|
+
The app asks for keys the first time it needs them and saves them to `~/.hedge-fund/.env` — nothing to configure up front. It needs:
|
|
73
|
+
|
|
74
|
+
- A [Financial Datasets](https://financialdatasets.ai) API key, for prices, fundamentals, and earnings.
|
|
75
|
+
- One LLM API key for the LLM-powered alpha models. Supported providers: Anthropic, OpenAI, DeepSeek, Google, xAI, Kimi.
|
|
76
|
+
|
|
77
|
+
Keys exported in your shell always win over the saved file.
|
|
78
|
+
|
|
79
|
+
## How to Run
|
|
80
|
+
|
|
81
|
+
### Interactive app
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
aihf
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
With no arguments, this launches the interactive terminal app. Build a fund — pick stocks, strategies, rebalance cadence — or backtest a saved fund and watch its equity curve draw against its benchmark. Funds you build are saved as mandate files in `~/.hedge-fund/mandates/`.
|
|
88
|
+
|
|
89
|
+
### Non-interactive
|
|
90
|
+
|
|
91
|
+
Run one fund cycle from a mandate file. The full cycle record prints to stdout as JSON; a short human summary goes to stderr:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
aihf ~/.hedge-fund/mandates/example.yaml --tickers AAPL,MSFT
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Backtest the mandate over history at its rebalance cadence:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
aihf ~/.hedge-fund/mandates/example.yaml --tickers AAPL,MSFT --backtest
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
A mandate is the desk — strategies, staff, risk, capital, cadence — and never names tickers; `--tickers` says what to point it at for this run.
|
|
104
|
+
|
|
105
|
+
## Development
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
git clone https://github.com/virattt/ai-hedge-fund.git
|
|
109
|
+
cd ai-hedge-fund
|
|
110
|
+
poetry install
|
|
111
|
+
poetry run aihf
|
|
112
|
+
poetry run pytest hedge_fund
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## How to Contribute
|
|
116
|
+
|
|
117
|
+
1. Fork the repository
|
|
118
|
+
2. Create a feature branch
|
|
119
|
+
3. Commit your changes
|
|
120
|
+
4. Push to the branch
|
|
121
|
+
5. Create a Pull Request
|
|
122
|
+
|
|
123
|
+
**Important**: Please keep your pull requests small and focused. This will make it easier to review and merge.
|
|
124
|
+
|
|
125
|
+
## Feature Requests
|
|
126
|
+
|
|
127
|
+
If you have a feature request, please open an [issue](https://github.com/virattt/ai-hedge-fund/issues) and make sure it is tagged with `enhancement`.
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
132
|
+
|
aihf-2.2.0/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# AI Hedge Fund
|
|
2
|
+
|
|
3
|
+
This is a proof of concept for an AI-powered hedge fund. The goal of this project is to explore the use of AI to make trading decisions. This project is for **educational** purposes only and is not intended for real trading or investment.
|
|
4
|
+
|
|
5
|
+
> **🚧 The project is evolving.** We're rebuilding it into a persistent, always-on AI hedge fund — a *fund* as a first-class entity you can backtest, paper-trade, and (opt-in) run live, with the investor agents reimagined as pluggable, backtestable "alpha models." Read the **[Vision →](VISION.md)** and the **[Roadmap →](ROADMAP.md)**.
|
|
6
|
+
|
|
7
|
+
Note: the system does not actually make any trades.
|
|
8
|
+
|
|
9
|
+
[](https://twitter.com/virattt)
|
|
10
|
+
|
|
11
|
+
## Disclaimer
|
|
12
|
+
|
|
13
|
+
This project is for **educational and research purposes only**.
|
|
14
|
+
|
|
15
|
+
- Not intended for real trading or investment
|
|
16
|
+
- No investment advice or guarantees provided
|
|
17
|
+
- Creator assumes no liability for financial losses
|
|
18
|
+
- Consult a financial advisor for investment decisions
|
|
19
|
+
- Past performance does not indicate future results
|
|
20
|
+
|
|
21
|
+
By using this software, you agree to use it solely for learning purposes.
|
|
22
|
+
|
|
23
|
+
## How to Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pipx install aihf
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
(or `uv tool install aihf`, or `pip install aihf` into an environment of your choice)
|
|
30
|
+
|
|
31
|
+
Then run it from anywhere:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
aihf
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### API keys
|
|
38
|
+
|
|
39
|
+
The app asks for keys the first time it needs them and saves them to `~/.hedge-fund/.env` — nothing to configure up front. It needs:
|
|
40
|
+
|
|
41
|
+
- A [Financial Datasets](https://financialdatasets.ai) API key, for prices, fundamentals, and earnings.
|
|
42
|
+
- One LLM API key for the LLM-powered alpha models. Supported providers: Anthropic, OpenAI, DeepSeek, Google, xAI, Kimi.
|
|
43
|
+
|
|
44
|
+
Keys exported in your shell always win over the saved file.
|
|
45
|
+
|
|
46
|
+
## How to Run
|
|
47
|
+
|
|
48
|
+
### Interactive app
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
aihf
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
With no arguments, this launches the interactive terminal app. Build a fund — pick stocks, strategies, rebalance cadence — or backtest a saved fund and watch its equity curve draw against its benchmark. Funds you build are saved as mandate files in `~/.hedge-fund/mandates/`.
|
|
55
|
+
|
|
56
|
+
### Non-interactive
|
|
57
|
+
|
|
58
|
+
Run one fund cycle from a mandate file. The full cycle record prints to stdout as JSON; a short human summary goes to stderr:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
aihf ~/.hedge-fund/mandates/example.yaml --tickers AAPL,MSFT
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Backtest the mandate over history at its rebalance cadence:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
aihf ~/.hedge-fund/mandates/example.yaml --tickers AAPL,MSFT --backtest
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
A mandate is the desk — strategies, staff, risk, capital, cadence — and never names tickers; `--tickers` says what to point it at for this run.
|
|
71
|
+
|
|
72
|
+
## Development
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
git clone https://github.com/virattt/ai-hedge-fund.git
|
|
76
|
+
cd ai-hedge-fund
|
|
77
|
+
poetry install
|
|
78
|
+
poetry run aihf
|
|
79
|
+
poetry run pytest hedge_fund
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## How to Contribute
|
|
83
|
+
|
|
84
|
+
1. Fork the repository
|
|
85
|
+
2. Create a feature branch
|
|
86
|
+
3. Commit your changes
|
|
87
|
+
4. Push to the branch
|
|
88
|
+
5. Create a Pull Request
|
|
89
|
+
|
|
90
|
+
**Important**: Please keep your pull requests small and focused. This will make it easier to review and merge.
|
|
91
|
+
|
|
92
|
+
## Feature Requests
|
|
93
|
+
|
|
94
|
+
If you have a feature request, please open an [issue](https://github.com/virattt/ai-hedge-fund/issues) and make sure it is tagged with `enhancement`.
|
|
95
|
+
|
|
96
|
+
## License
|
|
97
|
+
|
|
98
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# v2 — AI Hedge Fund core
|
|
2
|
+
|
|
3
|
+
> **Status: Work in progress.** A ground-up rebuild of the engine, developed
|
|
4
|
+
> alongside the shipped v1 app (`src/`, `app/`) but not yet wired into it.
|
|
5
|
+
> See [`../VISION.md`](../VISION.md) and [`../ROADMAP.md`](../ROADMAP.md) for
|
|
6
|
+
> where this is headed.
|
|
7
|
+
|
|
8
|
+
v2 rebuilds the fund as a persistent, point-in-time-honest system, mirroring a
|
|
9
|
+
real shop's hierarchy:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
FUND = capital slices over STRATEGIES (master risk on the netted book)
|
|
13
|
+
STRATEGY = a blend policy over MODELS (a "pod")
|
|
14
|
+
MODEL = an alpha model → a Signal (conviction in [-1,+1] + thesis)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
A fund is the **desk**, not a watchlist: the mandate names no tickers. Which
|
|
18
|
+
names to trade is supplied per run (`--tickers`, or the app's ticker prompt)
|
|
19
|
+
and recorded on every `CycleRecord` — so one fund can be pointed at anything,
|
|
20
|
+
and every run remembers what it traded.
|
|
21
|
+
|
|
22
|
+
A fund runs two kinds of pods, like a real shop. **Discretionary** strategies
|
|
23
|
+
are staffed by **agents** — LLM investor personas (Warren Buffett, Charlie
|
|
24
|
+
Munger, Benjamin Graham, Peter Lynch, Stanley Druckenmiller) whose judgment
|
|
25
|
+
is the edge; blend them long-biased or market-neutral. **Systematic**
|
|
26
|
+
strategies are powered by quant models (post-earnings drift) — the model *is*
|
|
27
|
+
the strategy, no persona attached. Both kinds implement one interface and
|
|
28
|
+
plug into the same engine unchanged.
|
|
29
|
+
|
|
30
|
+
Run a fund two ways: **one cycle** (today's data → today's target book) or a
|
|
31
|
+
**backtest** — the same cycle looped over history at the mandate's rebalance
|
|
32
|
+
cadence, producing an equity curve against your benchmark and a full
|
|
33
|
+
`CycleRecord` for every tick. Same code path, so a backtest is honest by
|
|
34
|
+
construction: it's the fund, replayed, not a separate simulator.
|
|
35
|
+
|
|
36
|
+
## Quickstart
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
poetry install # dependencies
|
|
40
|
+
|
|
41
|
+
# .env needs (at repo root):
|
|
42
|
+
# FINANCIAL_DATASETS_API_KEY=... # market/fundamentals data
|
|
43
|
+
# ANTHROPIC_API_KEY=... # only for LLM agents (Buffett)
|
|
44
|
+
|
|
45
|
+
# THE command. No arguments: launch the interactive app (a Textual TUI).
|
|
46
|
+
# Build a fund — pick stocks, strategies, rebalance cadence — or backtest a
|
|
47
|
+
# saved fund and watch its equity curve draw against its benchmark.
|
|
48
|
+
poetry run aihf # or, equivalently: python -m hedge_fund.tui
|
|
49
|
+
|
|
50
|
+
# With a mandate: run one cycle non-interactively (data → strategies →
|
|
51
|
+
# netting → risk → execution), full CycleRecord as JSON on stdout. A mandate
|
|
52
|
+
# carries no tickers — --tickers says what to point the fund at this run.
|
|
53
|
+
poetry run aihf ~/.hedge-fund/mandates/example.yaml --tickers AAPL,MSFT,NVDA
|
|
54
|
+
|
|
55
|
+
# Backtest a mandate: the same run_cycle looped over history at the
|
|
56
|
+
# mandate's rebalance cadence, full result JSON (every CycleRecord) on stdout.
|
|
57
|
+
poetry run aihf ~/.hedge-fund/mandates/example.yaml --tickers AAPL,MSFT --backtest
|
|
58
|
+
|
|
59
|
+
# Tests
|
|
60
|
+
poetry run pytest hedge_fund/
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
All API responses cache to disk (`~/.hedge-fund/cache/`), so reruns are fast,
|
|
64
|
+
free, and work offline once warmed.
|
|
65
|
+
|
|
66
|
+
## Architecture
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
Data (point-in-time) → Alpha models → Portfolio → Risk → Execution → Ledger
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
| Module | What | Status |
|
|
73
|
+
|--------|------|--------|
|
|
74
|
+
| `data/` | `DataClient` protocol, Financial Datasets client, disk cache | ✅ |
|
|
75
|
+
| `signals/` | `AlphaModel` interface, PEAD, `LLMAgent` + 5 investor personas | ✅ |
|
|
76
|
+
| `llm/` | LLM provider protocol, Anthropic client, prompt cache | ✅ |
|
|
77
|
+
| `features/` | Point-in-time fundamentals snapshot (more features planned) | ◐ |
|
|
78
|
+
| `fund/` | `FundSpec`/`StrategySpec` — mandates as YAML data — and the `Fund` object | ✅ |
|
|
79
|
+
| `strategies/` | Strategy library (fundamental-ls, deep-value, inflections, earnings-drift) — add yours as a YAML | ✅ |
|
|
80
|
+
| `portfolio/` | View blending → target weights (conviction-weighted, optional market-neutral) | ✅ |
|
|
81
|
+
| `risk/` | Hard limits — per-position and gross-exposure clamps | ✅ |
|
|
82
|
+
| `brokers/` | `Broker` protocol + `SimBroker` (paper/live brokers planned) | ◐ |
|
|
83
|
+
| `pipeline/` | `run_cycle` — one code path for backtest/paper/live; `CycleRecord` | ✅ |
|
|
84
|
+
| `backtesting/` | `backtest_fund` — the whole fund over history on `run_cycle` — plus the per-model engine | ✅ |
|
|
85
|
+
| `event_study/` | Market-model abnormal returns (CARs) | ✅ |
|
|
86
|
+
| `validation/` | Combinatorial purged CV (CPCV), backtest-overfitting prob (PBO) | ⬜ |
|
|
87
|
+
| `tui/` | The interactive app (Textual): fund builder + live backtest board | ✅ |
|
|
88
|
+
|
|
89
|
+
✅ built · ◐ partial · ⬜ planned
|
|
90
|
+
|
|
91
|
+
## Principles (non-negotiable)
|
|
92
|
+
|
|
93
|
+
- **Point-in-time by construction.** On any simulated date, only data actually
|
|
94
|
+
filed by then is visible — the data layer filters on filing date, not report
|
|
95
|
+
period. No lookahead, ever.
|
|
96
|
+
- **Fail loud.** Infrastructure failures raise; only genuine "no data" returns
|
|
97
|
+
empty. A silent empty would poison a backtest as a fake "no signal."
|
|
98
|
+
- **The LLM never touches the trade.** Agents form *views* and *narrate*;
|
|
99
|
+
deterministic code sizes and places orders; risk limits are hard gates.
|
|
100
|
+
- **One interface for every analyst.** Implement `AlphaModel.predict(ticker,
|
|
101
|
+
date, data_client) -> Signal` and it plugs into the engine unchanged.
|
|
102
|
+
|
|
103
|
+
## Data contracts (`models.py`)
|
|
104
|
+
|
|
105
|
+
- `Signal` — an alpha model's output: `value` in `[-1, +1]`, plus `reasoning`,
|
|
106
|
+
`components`, and `metadata`.
|
|
107
|
+
- `QuantSignals` — all signals for a ticker on a date.
|
|
108
|
+
|
|
109
|
+
## Contributing
|
|
110
|
+
|
|
111
|
+
Two high-leverage contributions:
|
|
112
|
+
|
|
113
|
+
- **A new agent or quant model** (code): read `signals/base.py` for the
|
|
114
|
+
`AlphaModel` interface, use `signals/buffett.py` (an agent is just a system
|
|
115
|
+
prompt) or `signals/pead.py` (quant) as a template, register it, add a test.
|
|
116
|
+
- **A new strategy** (no code): drop a YAML in `strategies/` bundling existing
|
|
117
|
+
models with a blend policy — the fund builder picks it up automatically.
|
|
118
|
+
|
|
119
|
+
See [`../ROADMAP.md`](../ROADMAP.md) for the open list.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""v2 backtesting — simulate a fund (or a single alpha model) over history."""
|
|
2
|
+
|
|
3
|
+
from hedge_fund.backtesting.engine import BacktestEngine
|
|
4
|
+
from hedge_fund.backtesting.fund import (
|
|
5
|
+
FundBacktestMetrics,
|
|
6
|
+
FundBacktestResult,
|
|
7
|
+
backtest_fund,
|
|
8
|
+
rebalance_grid,
|
|
9
|
+
)
|
|
10
|
+
from hedge_fund.backtesting.models import (
|
|
11
|
+
BacktestResult,
|
|
12
|
+
PerformanceMetrics,
|
|
13
|
+
Trade,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"BacktestEngine",
|
|
18
|
+
"BacktestResult",
|
|
19
|
+
"FundBacktestMetrics",
|
|
20
|
+
"FundBacktestResult",
|
|
21
|
+
"PerformanceMetrics",
|
|
22
|
+
"Trade",
|
|
23
|
+
"backtest_fund",
|
|
24
|
+
"rebalance_grid",
|
|
25
|
+
]
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""Run the PEAD backtester. Screen-record friendly output.
|
|
2
|
+
|
|
3
|
+
Usage: poetry run python -m hedge_fund.backtesting
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
import time
|
|
11
|
+
from datetime import date
|
|
12
|
+
|
|
13
|
+
from hedge_fund.data import FDClient
|
|
14
|
+
from hedge_fund.backtesting import BacktestEngine
|
|
15
|
+
from hedge_fund.signals import PEADModel
|
|
16
|
+
|
|
17
|
+
TICKERS = [
|
|
18
|
+
# Tech (21)
|
|
19
|
+
"AAPL", "MSFT", "AMZN", "GOOGL", "META", "NVDA", "TSLA", "NFLX", "CRM", "ADBE",
|
|
20
|
+
"ORCL", "INTC", "AMD", "CSCO", "IBM", "UBER", "SHOP", "SNOW", "PLTR", "PANW", "CRWD",
|
|
21
|
+
# Financials (15)
|
|
22
|
+
"JPM", "GS", "BAC", "WFC", "MS", "C", "BLK", "SCHW", "AXP", "COF",
|
|
23
|
+
"USB", "PNC", "TFC", "BK", "CME",
|
|
24
|
+
# Healthcare (15)
|
|
25
|
+
"JNJ", "PFE", "UNH", "MRK", "LLY", "ABBV", "TMO", "ABT", "BMY", "AMGN",
|
|
26
|
+
"GILD", "ISRG", "VRTX", "REGN", "MDT",
|
|
27
|
+
# Energy (7)
|
|
28
|
+
"XOM", "CVX", "COP", "SLB", "EOG", "MPC", "PSX",
|
|
29
|
+
# Consumer / Retail (15)
|
|
30
|
+
"HD", "LOW", "COST", "WMT", "KO", "PEP", "MCD", "SBUX", "NKE", "TGT",
|
|
31
|
+
"TJX", "ROST", "DG", "DLTR", "YUM",
|
|
32
|
+
# Industrials (10)
|
|
33
|
+
"CAT", "DE", "HON", "UPS", "RTX", "BA", "LMT", "GE", "MMM", "UNP",
|
|
34
|
+
# Media / Telecom (7)
|
|
35
|
+
"DIS", "CMCSA", "T", "VZ", "TMUS", "CHTR", "WBD",
|
|
36
|
+
# Other (10)
|
|
37
|
+
"V", "MA", "PYPL", "NEE", "D", "SO", "DUK", "ABNB", "COIN", "NOW",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
HOLDING_DAYS = 5
|
|
41
|
+
CAPITAL = 100_000.0
|
|
42
|
+
PER_TRADE = 10_000.0
|
|
43
|
+
START_DATE = "2024-06-01"
|
|
44
|
+
END_DATE = date.today().isoformat()
|
|
45
|
+
|
|
46
|
+
# Colors
|
|
47
|
+
GREEN = "\033[32m"
|
|
48
|
+
RED = "\033[31m"
|
|
49
|
+
YELLOW = "\033[33m"
|
|
50
|
+
CYAN = "\033[36m"
|
|
51
|
+
DIM = "\033[90m"
|
|
52
|
+
BOLD = "\033[1m"
|
|
53
|
+
RESET = "\033[0m"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def clear():
|
|
57
|
+
os.system("clear")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def color_pnl(v: float) -> str:
|
|
61
|
+
c = GREEN if v >= 0 else RED
|
|
62
|
+
return f"{c}${v:>+10,.2f}{RESET}"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def color_pct(v: float) -> str:
|
|
66
|
+
c = GREEN if v >= 0 else RED
|
|
67
|
+
return f"{c}{v:>+7.2%}{RESET}"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def color_direction(d: str) -> str:
|
|
71
|
+
return f"{GREEN}LONG {RESET}" if d == "long" else f"{RED}SHORT{RESET}"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def color_eps(s: str | None) -> str:
|
|
75
|
+
if s == "BEAT":
|
|
76
|
+
return f"{GREEN}BEAT{RESET}"
|
|
77
|
+
if s == "MISS":
|
|
78
|
+
return f"{RED}MISS{RESET}"
|
|
79
|
+
return f"{DIM} - {RESET}"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def print_header(metrics, equity: float):
|
|
83
|
+
"""Print the portfolio summary dashboard."""
|
|
84
|
+
m = metrics
|
|
85
|
+
ret_color = GREEN if m.total_return_pct >= 0 else RED
|
|
86
|
+
sharpe_color = GREEN if m.sharpe_ratio > 1.0 else YELLOW if m.sharpe_ratio > 0 else RED
|
|
87
|
+
|
|
88
|
+
print(f" {BOLD}PEAD Backtest — Long BEATs, Short MISSes, {HOLDING_DAYS}-day hold{RESET}")
|
|
89
|
+
print(f" {DIM}{'─' * 70}{RESET}")
|
|
90
|
+
print(
|
|
91
|
+
f" Portfolio: {CYAN}${equity:>12,.2f}{RESET}"
|
|
92
|
+
f" Return: {ret_color}{m.total_return_pct:>+7.2%}{RESET}"
|
|
93
|
+
f" Sharpe: {sharpe_color}{m.sharpe_ratio:>5.2f}{RESET}"
|
|
94
|
+
f" Max DD: {RED}{m.max_drawdown_pct:>6.2%}{RESET}"
|
|
95
|
+
)
|
|
96
|
+
print(
|
|
97
|
+
f" Trades: {m.n_trades}"
|
|
98
|
+
f" ({GREEN}{m.n_long} long{RESET}, {RED}{m.n_short} short{RESET})"
|
|
99
|
+
f" Win Rate: {m.win_rate:.0%}"
|
|
100
|
+
f" Avg Return: {color_pct(m.avg_return_pct)}"
|
|
101
|
+
)
|
|
102
|
+
print(f" {DIM}{'─' * 70}{RESET}")
|
|
103
|
+
print()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def print_table_header():
|
|
107
|
+
print(
|
|
108
|
+
f" {DIM}{'Date':<12} {'Ticker':<6} {'Action':<6} {'EPS':<5}"
|
|
109
|
+
f" {'Entry':>9} {'Exit':>9} {'Shares':>7}"
|
|
110
|
+
f" {'P&L':>12} {'Return':>8}{RESET}"
|
|
111
|
+
)
|
|
112
|
+
print(f" {DIM}{'─' * 78}{RESET}")
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def print_trade_row(t):
|
|
116
|
+
print(
|
|
117
|
+
f" {t.entry_date:<12} {CYAN}{t.ticker:<6}{RESET}"
|
|
118
|
+
f" {color_direction(t.direction)} {color_eps(t.metadata.get('eps_surprise'))}"
|
|
119
|
+
f" ${t.entry_price:>8.2f} ${t.exit_price:>8.2f} {t.shares:>7.1f}"
|
|
120
|
+
f" {color_pnl(t.pnl)} {color_pct(t.return_pct)}"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def main() -> None:
|
|
125
|
+
import logging
|
|
126
|
+
logging.getLogger("hedge_fund.data.client").setLevel(logging.ERROR)
|
|
127
|
+
|
|
128
|
+
n = len(TICKERS)
|
|
129
|
+
engine = BacktestEngine(capital=CAPITAL, per_trade=PER_TRADE)
|
|
130
|
+
model = PEADModel()
|
|
131
|
+
|
|
132
|
+
# Phase 1: Backtest the PEAD alpha model, ticker by ticker, with progress
|
|
133
|
+
sys.stdout.write(f" Backtesting PEAD alpha... [0/{n}]")
|
|
134
|
+
sys.stdout.flush()
|
|
135
|
+
|
|
136
|
+
trades = []
|
|
137
|
+
with FDClient() as fd:
|
|
138
|
+
for i, ticker in enumerate(TICKERS):
|
|
139
|
+
sys.stdout.write(f"\r Backtesting PEAD alpha... [{i + 1}/{n}] {ticker:<6}")
|
|
140
|
+
sys.stdout.flush()
|
|
141
|
+
r = engine.run_alpha(
|
|
142
|
+
model, [ticker], fd, START_DATE, END_DATE, holding_days=HOLDING_DAYS,
|
|
143
|
+
)
|
|
144
|
+
trades.extend(r.trades)
|
|
145
|
+
|
|
146
|
+
sys.stdout.write(f"\r Backtesting PEAD alpha... {len(trades)} trades" + " " * 20 + "\n")
|
|
147
|
+
sys.stdout.flush()
|
|
148
|
+
time.sleep(0.5)
|
|
149
|
+
|
|
150
|
+
if not trades:
|
|
151
|
+
print(" No trades generated.")
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
trades.sort(key=lambda t: t.entry_date)
|
|
155
|
+
|
|
156
|
+
# Phase 2: Replay trades one by one with running metrics
|
|
157
|
+
clear()
|
|
158
|
+
equity = CAPITAL
|
|
159
|
+
displayed: list = []
|
|
160
|
+
|
|
161
|
+
for trade in trades:
|
|
162
|
+
equity += trade.pnl
|
|
163
|
+
displayed.append(trade)
|
|
164
|
+
|
|
165
|
+
# Rebuild display each trade (like v1's clear-and-reprint)
|
|
166
|
+
clear()
|
|
167
|
+
|
|
168
|
+
# Compute running metrics over the trades shown so far
|
|
169
|
+
running_curve = engine._build_equity_curve(displayed)
|
|
170
|
+
running_metrics = engine._compute_metrics(displayed, running_curve)
|
|
171
|
+
|
|
172
|
+
print_header(running_metrics, equity)
|
|
173
|
+
print_table_header()
|
|
174
|
+
|
|
175
|
+
# Print all trades so far (newest first)
|
|
176
|
+
for t in reversed(displayed):
|
|
177
|
+
print_trade_row(t)
|
|
178
|
+
|
|
179
|
+
print()
|
|
180
|
+
time.sleep(0.4)
|
|
181
|
+
|
|
182
|
+
# Final summary
|
|
183
|
+
time.sleep(0.5)
|
|
184
|
+
print(f" {BOLD}Done.{RESET} {len(trades)} trades executed.\n")
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
if __name__ == "__main__":
|
|
188
|
+
from hedge_fund.tui.keys import apply_credentials
|
|
189
|
+
apply_credentials()
|
|
190
|
+
main()
|