autobrain-sim 1.0.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.
- autobrain_sim-1.0.0/LICENSE +19 -0
- autobrain_sim-1.0.0/MANIFEST.in +6 -0
- autobrain_sim-1.0.0/PKG-INFO +194 -0
- autobrain_sim-1.0.0/README.md +181 -0
- autobrain_sim-1.0.0/autobrain_sim.egg-info/PKG-INFO +194 -0
- autobrain_sim-1.0.0/autobrain_sim.egg-info/SOURCES.txt +11 -0
- autobrain_sim-1.0.0/autobrain_sim.egg-info/dependency_links.txt +1 -0
- autobrain_sim-1.0.0/autobrain_sim.egg-info/requires.txt +5 -0
- autobrain_sim-1.0.0/autobrain_sim.egg-info/top_level.txt +1 -0
- autobrain_sim-1.0.0/brain_client.py +282 -0
- autobrain_sim-1.0.0/examples/example.py +40 -0
- autobrain_sim-1.0.0/pyproject.toml +23 -0
- autobrain_sim-1.0.0/setup.cfg +4 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2026
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: autobrain-sim
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Python client for the WorldQuant Brain API
|
|
5
|
+
Requires-Python: >=3.7
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: requests>=2.28
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: pytest; extra == "dev"
|
|
11
|
+
Requires-Dist: pytest-mock; extra == "dev"
|
|
12
|
+
Dynamic: license-file
|
|
13
|
+
|
|
14
|
+
# AutoBrain Sim
|
|
15
|
+
|
|
16
|
+
A lightweight Python client library for interacting with the [WorldQuant Brain](https://platform.worldquantbrain.com) platform API. Supports authentication, alpha simulation submission, and result retrieval.
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- Multiple authentication methods (direct, credentials file, interactive prompt)
|
|
21
|
+
- Submit alpha expressions for simulation
|
|
22
|
+
- Poll for simulation completion automatically
|
|
23
|
+
- Retrieve alpha details and PnL record sets
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install autobrain-sim
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Requirements
|
|
32
|
+
|
|
33
|
+
- Python 3.7+
|
|
34
|
+
- `requests`
|
|
35
|
+
|
|
36
|
+
Install the dependency:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install requests
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Files
|
|
43
|
+
|
|
44
|
+
| File | Description |
|
|
45
|
+
|------|-------------|
|
|
46
|
+
| `brain_client.py` | Core client library (`BrainClient`, `SimulationResult`) |
|
|
47
|
+
| `example.py` | Standalone usage example |
|
|
48
|
+
| `main.ipynb` | Jupyter notebook walkthrough |
|
|
49
|
+
|
|
50
|
+
## Quick Start
|
|
51
|
+
|
|
52
|
+
### Authentication
|
|
53
|
+
|
|
54
|
+
**Method 1 — Direct credentials**
|
|
55
|
+
```python
|
|
56
|
+
from brain_client import BrainClient
|
|
57
|
+
|
|
58
|
+
client = BrainClient(email="your@email.com", password="yourpassword")
|
|
59
|
+
client.authenticate()
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Method 2 — One-liner login**
|
|
63
|
+
```python
|
|
64
|
+
client = BrainClient.login("your@email.com", "yourpassword")
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Method 3 — Interactive prompt** (asks at runtime if no credentials are found)
|
|
68
|
+
```python
|
|
69
|
+
client = BrainClient()
|
|
70
|
+
client.authenticate()
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Method 4 — Credentials file**
|
|
74
|
+
|
|
75
|
+
Create `~/.brain_credentials` (or any path) as a JSON array:
|
|
76
|
+
```json
|
|
77
|
+
["your@email.com", "yourpassword"]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Then load it:
|
|
81
|
+
```python
|
|
82
|
+
client = BrainClient(credentials_file="~/.brain_credentials")
|
|
83
|
+
client.authenticate()
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
> Credentials priority: direct args → `credentials_file` → `~/.brain_credentials` → interactive prompt.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
### Simulate an Alpha
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
sim = client.simulate(
|
|
94
|
+
expression="close / ts_mean(close, 20) - 1",
|
|
95
|
+
settings={
|
|
96
|
+
"region": "USA",
|
|
97
|
+
"universe": "TOP3000",
|
|
98
|
+
"neutralization": "SUBINDUSTRY",
|
|
99
|
+
}
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
result = sim.wait(verbose=True) # blocks until done
|
|
103
|
+
print("Alpha ID:", sim.alpha_id)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Retrieve Results
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
alpha = sim.get_alpha()
|
|
110
|
+
print("Sharpe:", alpha["is"]["sharpe"])
|
|
111
|
+
print("Fitness:", alpha["is"]["fitness"])
|
|
112
|
+
|
|
113
|
+
pnl = sim.get_pnl()
|
|
114
|
+
print(pnl)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## API Reference
|
|
120
|
+
|
|
121
|
+
### `BrainClient`
|
|
122
|
+
|
|
123
|
+
| Method | Description |
|
|
124
|
+
|--------|-------------|
|
|
125
|
+
| `__init__(email, password, credentials_file)` | Initialize client with credentials |
|
|
126
|
+
| `BrainClient.login(email, password)` | Create client and authenticate in one step |
|
|
127
|
+
| `authenticate()` | Sign in and obtain a session token |
|
|
128
|
+
| `simulate(expression, settings, ...)` | Submit an alpha for simulation; returns `SimulationResult` |
|
|
129
|
+
| `get_alpha(alpha_id)` | Fetch alpha details by ID |
|
|
130
|
+
| `get_pnl(alpha_id)` | Fetch PnL record set for an alpha |
|
|
131
|
+
| `get_recordset(alpha_id, record_set_name)` | Fetch any named record set |
|
|
132
|
+
|
|
133
|
+
#### Default Simulation Settings
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
{
|
|
137
|
+
"instrumentType": "EQUITY",
|
|
138
|
+
"region": "USA",
|
|
139
|
+
"universe": "TOP3000",
|
|
140
|
+
"delay": 1,
|
|
141
|
+
"decay": 15,
|
|
142
|
+
"neutralization": "SUBINDUSTRY",
|
|
143
|
+
"truncation": 0.08,
|
|
144
|
+
"maxTrade": "ON",
|
|
145
|
+
"pasteurization": "ON",
|
|
146
|
+
"testPeriod": "P1Y6M",
|
|
147
|
+
"unitHandling": "VERIFY",
|
|
148
|
+
"nanHandling": "OFF",
|
|
149
|
+
"language": "FASTEXPR",
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Any key passed via `settings` overrides the default.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
### `SimulationResult`
|
|
158
|
+
|
|
159
|
+
Returned by `client.simulate()`.
|
|
160
|
+
|
|
161
|
+
| Method | Description |
|
|
162
|
+
|--------|-------------|
|
|
163
|
+
| `wait(verbose=True)` | Poll until simulation completes; returns result JSON |
|
|
164
|
+
| `get_alpha()` | Fetch full alpha details (call after `wait()`) |
|
|
165
|
+
| `get_pnl(poll_interval)` | Fetch PnL record set (call after `wait()`) |
|
|
166
|
+
|
|
167
|
+
| Attribute | Description |
|
|
168
|
+
|-----------|-------------|
|
|
169
|
+
| `alpha_id` | Alpha ID string (available after `wait()`) |
|
|
170
|
+
| `progress_url` | URL used to poll simulation progress |
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Example
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from brain_client import BrainClient
|
|
178
|
+
|
|
179
|
+
client = BrainClient.login() # interactive prompt
|
|
180
|
+
|
|
181
|
+
sim = client.simulate("close / ts_mean(close, 20) - 1")
|
|
182
|
+
result = sim.wait(verbose=True)
|
|
183
|
+
|
|
184
|
+
alpha = sim.get_alpha()
|
|
185
|
+
print(f"Alpha ID : {sim.alpha_id}")
|
|
186
|
+
print(f"Sharpe : {alpha['is']['sharpe']}")
|
|
187
|
+
print(f"Fitness : {alpha['is']['fitness']}")
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Notes
|
|
191
|
+
|
|
192
|
+
- The client uses HTTP Basic Auth to obtain a session token from the `/authentication` endpoint.
|
|
193
|
+
- Polling respects the `Retry-After` response header returned by the Brain API.
|
|
194
|
+
- Never commit your credentials to version control. Use `~/.brain_credentials` or environment variables instead.
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# AutoBrain Sim
|
|
2
|
+
|
|
3
|
+
A lightweight Python client library for interacting with the [WorldQuant Brain](https://platform.worldquantbrain.com) platform API. Supports authentication, alpha simulation submission, and result retrieval.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Multiple authentication methods (direct, credentials file, interactive prompt)
|
|
8
|
+
- Submit alpha expressions for simulation
|
|
9
|
+
- Poll for simulation completion automatically
|
|
10
|
+
- Retrieve alpha details and PnL record sets
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install autobrain-sim
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Requirements
|
|
19
|
+
|
|
20
|
+
- Python 3.7+
|
|
21
|
+
- `requests`
|
|
22
|
+
|
|
23
|
+
Install the dependency:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install requests
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Files
|
|
30
|
+
|
|
31
|
+
| File | Description |
|
|
32
|
+
|------|-------------|
|
|
33
|
+
| `brain_client.py` | Core client library (`BrainClient`, `SimulationResult`) |
|
|
34
|
+
| `example.py` | Standalone usage example |
|
|
35
|
+
| `main.ipynb` | Jupyter notebook walkthrough |
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
### Authentication
|
|
40
|
+
|
|
41
|
+
**Method 1 — Direct credentials**
|
|
42
|
+
```python
|
|
43
|
+
from brain_client import BrainClient
|
|
44
|
+
|
|
45
|
+
client = BrainClient(email="your@email.com", password="yourpassword")
|
|
46
|
+
client.authenticate()
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Method 2 — One-liner login**
|
|
50
|
+
```python
|
|
51
|
+
client = BrainClient.login("your@email.com", "yourpassword")
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Method 3 — Interactive prompt** (asks at runtime if no credentials are found)
|
|
55
|
+
```python
|
|
56
|
+
client = BrainClient()
|
|
57
|
+
client.authenticate()
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Method 4 — Credentials file**
|
|
61
|
+
|
|
62
|
+
Create `~/.brain_credentials` (or any path) as a JSON array:
|
|
63
|
+
```json
|
|
64
|
+
["your@email.com", "yourpassword"]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Then load it:
|
|
68
|
+
```python
|
|
69
|
+
client = BrainClient(credentials_file="~/.brain_credentials")
|
|
70
|
+
client.authenticate()
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
> Credentials priority: direct args → `credentials_file` → `~/.brain_credentials` → interactive prompt.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
### Simulate an Alpha
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
sim = client.simulate(
|
|
81
|
+
expression="close / ts_mean(close, 20) - 1",
|
|
82
|
+
settings={
|
|
83
|
+
"region": "USA",
|
|
84
|
+
"universe": "TOP3000",
|
|
85
|
+
"neutralization": "SUBINDUSTRY",
|
|
86
|
+
}
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
result = sim.wait(verbose=True) # blocks until done
|
|
90
|
+
print("Alpha ID:", sim.alpha_id)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Retrieve Results
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
alpha = sim.get_alpha()
|
|
97
|
+
print("Sharpe:", alpha["is"]["sharpe"])
|
|
98
|
+
print("Fitness:", alpha["is"]["fitness"])
|
|
99
|
+
|
|
100
|
+
pnl = sim.get_pnl()
|
|
101
|
+
print(pnl)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## API Reference
|
|
107
|
+
|
|
108
|
+
### `BrainClient`
|
|
109
|
+
|
|
110
|
+
| Method | Description |
|
|
111
|
+
|--------|-------------|
|
|
112
|
+
| `__init__(email, password, credentials_file)` | Initialize client with credentials |
|
|
113
|
+
| `BrainClient.login(email, password)` | Create client and authenticate in one step |
|
|
114
|
+
| `authenticate()` | Sign in and obtain a session token |
|
|
115
|
+
| `simulate(expression, settings, ...)` | Submit an alpha for simulation; returns `SimulationResult` |
|
|
116
|
+
| `get_alpha(alpha_id)` | Fetch alpha details by ID |
|
|
117
|
+
| `get_pnl(alpha_id)` | Fetch PnL record set for an alpha |
|
|
118
|
+
| `get_recordset(alpha_id, record_set_name)` | Fetch any named record set |
|
|
119
|
+
|
|
120
|
+
#### Default Simulation Settings
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
{
|
|
124
|
+
"instrumentType": "EQUITY",
|
|
125
|
+
"region": "USA",
|
|
126
|
+
"universe": "TOP3000",
|
|
127
|
+
"delay": 1,
|
|
128
|
+
"decay": 15,
|
|
129
|
+
"neutralization": "SUBINDUSTRY",
|
|
130
|
+
"truncation": 0.08,
|
|
131
|
+
"maxTrade": "ON",
|
|
132
|
+
"pasteurization": "ON",
|
|
133
|
+
"testPeriod": "P1Y6M",
|
|
134
|
+
"unitHandling": "VERIFY",
|
|
135
|
+
"nanHandling": "OFF",
|
|
136
|
+
"language": "FASTEXPR",
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Any key passed via `settings` overrides the default.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
### `SimulationResult`
|
|
145
|
+
|
|
146
|
+
Returned by `client.simulate()`.
|
|
147
|
+
|
|
148
|
+
| Method | Description |
|
|
149
|
+
|--------|-------------|
|
|
150
|
+
| `wait(verbose=True)` | Poll until simulation completes; returns result JSON |
|
|
151
|
+
| `get_alpha()` | Fetch full alpha details (call after `wait()`) |
|
|
152
|
+
| `get_pnl(poll_interval)` | Fetch PnL record set (call after `wait()`) |
|
|
153
|
+
|
|
154
|
+
| Attribute | Description |
|
|
155
|
+
|-----------|-------------|
|
|
156
|
+
| `alpha_id` | Alpha ID string (available after `wait()`) |
|
|
157
|
+
| `progress_url` | URL used to poll simulation progress |
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Example
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
from brain_client import BrainClient
|
|
165
|
+
|
|
166
|
+
client = BrainClient.login() # interactive prompt
|
|
167
|
+
|
|
168
|
+
sim = client.simulate("close / ts_mean(close, 20) - 1")
|
|
169
|
+
result = sim.wait(verbose=True)
|
|
170
|
+
|
|
171
|
+
alpha = sim.get_alpha()
|
|
172
|
+
print(f"Alpha ID : {sim.alpha_id}")
|
|
173
|
+
print(f"Sharpe : {alpha['is']['sharpe']}")
|
|
174
|
+
print(f"Fitness : {alpha['is']['fitness']}")
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Notes
|
|
178
|
+
|
|
179
|
+
- The client uses HTTP Basic Auth to obtain a session token from the `/authentication` endpoint.
|
|
180
|
+
- Polling respects the `Retry-After` response header returned by the Brain API.
|
|
181
|
+
- Never commit your credentials to version control. Use `~/.brain_credentials` or environment variables instead.
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: autobrain-sim
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Python client for the WorldQuant Brain API
|
|
5
|
+
Requires-Python: >=3.7
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: requests>=2.28
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: pytest; extra == "dev"
|
|
11
|
+
Requires-Dist: pytest-mock; extra == "dev"
|
|
12
|
+
Dynamic: license-file
|
|
13
|
+
|
|
14
|
+
# AutoBrain Sim
|
|
15
|
+
|
|
16
|
+
A lightweight Python client library for interacting with the [WorldQuant Brain](https://platform.worldquantbrain.com) platform API. Supports authentication, alpha simulation submission, and result retrieval.
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- Multiple authentication methods (direct, credentials file, interactive prompt)
|
|
21
|
+
- Submit alpha expressions for simulation
|
|
22
|
+
- Poll for simulation completion automatically
|
|
23
|
+
- Retrieve alpha details and PnL record sets
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install autobrain-sim
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Requirements
|
|
32
|
+
|
|
33
|
+
- Python 3.7+
|
|
34
|
+
- `requests`
|
|
35
|
+
|
|
36
|
+
Install the dependency:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install requests
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Files
|
|
43
|
+
|
|
44
|
+
| File | Description |
|
|
45
|
+
|------|-------------|
|
|
46
|
+
| `brain_client.py` | Core client library (`BrainClient`, `SimulationResult`) |
|
|
47
|
+
| `example.py` | Standalone usage example |
|
|
48
|
+
| `main.ipynb` | Jupyter notebook walkthrough |
|
|
49
|
+
|
|
50
|
+
## Quick Start
|
|
51
|
+
|
|
52
|
+
### Authentication
|
|
53
|
+
|
|
54
|
+
**Method 1 — Direct credentials**
|
|
55
|
+
```python
|
|
56
|
+
from brain_client import BrainClient
|
|
57
|
+
|
|
58
|
+
client = BrainClient(email="your@email.com", password="yourpassword")
|
|
59
|
+
client.authenticate()
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Method 2 — One-liner login**
|
|
63
|
+
```python
|
|
64
|
+
client = BrainClient.login("your@email.com", "yourpassword")
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Method 3 — Interactive prompt** (asks at runtime if no credentials are found)
|
|
68
|
+
```python
|
|
69
|
+
client = BrainClient()
|
|
70
|
+
client.authenticate()
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Method 4 — Credentials file**
|
|
74
|
+
|
|
75
|
+
Create `~/.brain_credentials` (or any path) as a JSON array:
|
|
76
|
+
```json
|
|
77
|
+
["your@email.com", "yourpassword"]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Then load it:
|
|
81
|
+
```python
|
|
82
|
+
client = BrainClient(credentials_file="~/.brain_credentials")
|
|
83
|
+
client.authenticate()
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
> Credentials priority: direct args → `credentials_file` → `~/.brain_credentials` → interactive prompt.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
### Simulate an Alpha
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
sim = client.simulate(
|
|
94
|
+
expression="close / ts_mean(close, 20) - 1",
|
|
95
|
+
settings={
|
|
96
|
+
"region": "USA",
|
|
97
|
+
"universe": "TOP3000",
|
|
98
|
+
"neutralization": "SUBINDUSTRY",
|
|
99
|
+
}
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
result = sim.wait(verbose=True) # blocks until done
|
|
103
|
+
print("Alpha ID:", sim.alpha_id)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Retrieve Results
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
alpha = sim.get_alpha()
|
|
110
|
+
print("Sharpe:", alpha["is"]["sharpe"])
|
|
111
|
+
print("Fitness:", alpha["is"]["fitness"])
|
|
112
|
+
|
|
113
|
+
pnl = sim.get_pnl()
|
|
114
|
+
print(pnl)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## API Reference
|
|
120
|
+
|
|
121
|
+
### `BrainClient`
|
|
122
|
+
|
|
123
|
+
| Method | Description |
|
|
124
|
+
|--------|-------------|
|
|
125
|
+
| `__init__(email, password, credentials_file)` | Initialize client with credentials |
|
|
126
|
+
| `BrainClient.login(email, password)` | Create client and authenticate in one step |
|
|
127
|
+
| `authenticate()` | Sign in and obtain a session token |
|
|
128
|
+
| `simulate(expression, settings, ...)` | Submit an alpha for simulation; returns `SimulationResult` |
|
|
129
|
+
| `get_alpha(alpha_id)` | Fetch alpha details by ID |
|
|
130
|
+
| `get_pnl(alpha_id)` | Fetch PnL record set for an alpha |
|
|
131
|
+
| `get_recordset(alpha_id, record_set_name)` | Fetch any named record set |
|
|
132
|
+
|
|
133
|
+
#### Default Simulation Settings
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
{
|
|
137
|
+
"instrumentType": "EQUITY",
|
|
138
|
+
"region": "USA",
|
|
139
|
+
"universe": "TOP3000",
|
|
140
|
+
"delay": 1,
|
|
141
|
+
"decay": 15,
|
|
142
|
+
"neutralization": "SUBINDUSTRY",
|
|
143
|
+
"truncation": 0.08,
|
|
144
|
+
"maxTrade": "ON",
|
|
145
|
+
"pasteurization": "ON",
|
|
146
|
+
"testPeriod": "P1Y6M",
|
|
147
|
+
"unitHandling": "VERIFY",
|
|
148
|
+
"nanHandling": "OFF",
|
|
149
|
+
"language": "FASTEXPR",
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Any key passed via `settings` overrides the default.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
### `SimulationResult`
|
|
158
|
+
|
|
159
|
+
Returned by `client.simulate()`.
|
|
160
|
+
|
|
161
|
+
| Method | Description |
|
|
162
|
+
|--------|-------------|
|
|
163
|
+
| `wait(verbose=True)` | Poll until simulation completes; returns result JSON |
|
|
164
|
+
| `get_alpha()` | Fetch full alpha details (call after `wait()`) |
|
|
165
|
+
| `get_pnl(poll_interval)` | Fetch PnL record set (call after `wait()`) |
|
|
166
|
+
|
|
167
|
+
| Attribute | Description |
|
|
168
|
+
|-----------|-------------|
|
|
169
|
+
| `alpha_id` | Alpha ID string (available after `wait()`) |
|
|
170
|
+
| `progress_url` | URL used to poll simulation progress |
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Example
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from brain_client import BrainClient
|
|
178
|
+
|
|
179
|
+
client = BrainClient.login() # interactive prompt
|
|
180
|
+
|
|
181
|
+
sim = client.simulate("close / ts_mean(close, 20) - 1")
|
|
182
|
+
result = sim.wait(verbose=True)
|
|
183
|
+
|
|
184
|
+
alpha = sim.get_alpha()
|
|
185
|
+
print(f"Alpha ID : {sim.alpha_id}")
|
|
186
|
+
print(f"Sharpe : {alpha['is']['sharpe']}")
|
|
187
|
+
print(f"Fitness : {alpha['is']['fitness']}")
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Notes
|
|
191
|
+
|
|
192
|
+
- The client uses HTTP Basic Auth to obtain a session token from the `/authentication` endpoint.
|
|
193
|
+
- Polling respects the `Retry-After` response header returned by the Brain API.
|
|
194
|
+
- Never commit your credentials to version control. Use `~/.brain_credentials` or environment variables instead.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
brain_client.py
|
|
5
|
+
pyproject.toml
|
|
6
|
+
autobrain_sim.egg-info/PKG-INFO
|
|
7
|
+
autobrain_sim.egg-info/SOURCES.txt
|
|
8
|
+
autobrain_sim.egg-info/dependency_links.txt
|
|
9
|
+
autobrain_sim.egg-info/requires.txt
|
|
10
|
+
autobrain_sim.egg-info/top_level.txt
|
|
11
|
+
examples/example.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
brain_client
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"""
|
|
2
|
+
WorldQuant Brain API Client Library
|
|
3
|
+
|
|
4
|
+
A Python client for interacting with the WorldQuant Brain platform API.
|
|
5
|
+
Supports authentication, alpha simulation, and result retrieval.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import getpass
|
|
10
|
+
import requests
|
|
11
|
+
from time import sleep
|
|
12
|
+
from os.path import expanduser
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
BASE_URL = "https://api.worldquantbrain.com"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class BrainClient:
|
|
20
|
+
"""Client for the WorldQuant Brain API."""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
email: Optional[str] = None,
|
|
25
|
+
password: Optional[str] = None,
|
|
26
|
+
credentials_file: Optional[str] = None,
|
|
27
|
+
):
|
|
28
|
+
"""
|
|
29
|
+
Initialize the client. Credentials priority:
|
|
30
|
+
1. email + password passed directly
|
|
31
|
+
2. credentials_file path (JSON: ["email", "password"])
|
|
32
|
+
3. ~/.brain_credentials (if the file exists)
|
|
33
|
+
4. Interactive prompt (asks at runtime)
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
email: Brain platform email.
|
|
37
|
+
password: Brain platform password.
|
|
38
|
+
credentials_file: Path to a JSON credentials file.
|
|
39
|
+
"""
|
|
40
|
+
self._session = requests.Session()
|
|
41
|
+
self._authenticated = False
|
|
42
|
+
|
|
43
|
+
if email and password:
|
|
44
|
+
self._session.auth = (email, password)
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
if credentials_file:
|
|
48
|
+
with open(expanduser(credentials_file), "r") as f:
|
|
49
|
+
self._session.auth = tuple(json.load(f))
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
default_file = expanduser("~/.brain_credentials")
|
|
53
|
+
try:
|
|
54
|
+
with open(default_file, "r") as f:
|
|
55
|
+
self._session.auth = tuple(json.load(f))
|
|
56
|
+
except FileNotFoundError:
|
|
57
|
+
# Fall back to interactive prompt
|
|
58
|
+
email = input("WorldQuant Brain email: ").strip()
|
|
59
|
+
password = getpass.getpass("WorldQuant Brain password: ")
|
|
60
|
+
self._session.auth = (email, password)
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
def login(cls, email: Optional[str] = None, password: Optional[str] = None) -> "BrainClient":
|
|
64
|
+
"""
|
|
65
|
+
Create a BrainClient and authenticate in one step.
|
|
66
|
+
|
|
67
|
+
If email/password are not provided, prompts interactively.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
email: Brain platform email (optional).
|
|
71
|
+
password: Brain platform password (optional).
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
An authenticated BrainClient instance.
|
|
75
|
+
|
|
76
|
+
Example:
|
|
77
|
+
client = BrainClient.login() # interactive prompt
|
|
78
|
+
client = BrainClient.login("me@email.com", "pass") # direct
|
|
79
|
+
"""
|
|
80
|
+
client = cls(email=email, password=password)
|
|
81
|
+
client.authenticate()
|
|
82
|
+
return client
|
|
83
|
+
|
|
84
|
+
# -------------------------------------------------------------------------
|
|
85
|
+
# Authentication
|
|
86
|
+
# -------------------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
def authenticate(self) -> dict:
|
|
89
|
+
"""
|
|
90
|
+
Sign in and obtain a JWT token.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Response JSON from the /authentication endpoint.
|
|
94
|
+
|
|
95
|
+
Raises:
|
|
96
|
+
requests.HTTPError: If authentication fails.
|
|
97
|
+
"""
|
|
98
|
+
response = self._session.post(f"{BASE_URL}/authentication")
|
|
99
|
+
response.raise_for_status()
|
|
100
|
+
self._authenticated = True
|
|
101
|
+
return response.json()
|
|
102
|
+
|
|
103
|
+
# -------------------------------------------------------------------------
|
|
104
|
+
# Simulations
|
|
105
|
+
# -------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
def simulate(self, expression: str, settings: dict = None, alpha_type: str = "REGULAR", regular: str = "close") -> "SimulationResult":
|
|
108
|
+
"""
|
|
109
|
+
Submit an alpha expression for simulation.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
expression: The alpha expression string.
|
|
113
|
+
settings: Simulation settings dict. Defaults to standard equity settings.
|
|
114
|
+
alpha_type: Type of simulation, e.g. "REGULAR".
|
|
115
|
+
regular: Price field, e.g. "close".
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
A SimulationResult object to poll for completion.
|
|
119
|
+
|
|
120
|
+
Raises:
|
|
121
|
+
requests.HTTPError: If the submission fails.
|
|
122
|
+
"""
|
|
123
|
+
default_settings = {
|
|
124
|
+
"instrumentType": "EQUITY",
|
|
125
|
+
"region": "USA",
|
|
126
|
+
"universe": "TOP3000",
|
|
127
|
+
"delay": 1,
|
|
128
|
+
"decay": 15,
|
|
129
|
+
"neutralization": "SUBINDUSTRY",
|
|
130
|
+
"truncation": 0.08,
|
|
131
|
+
"maxTrade": "ON",
|
|
132
|
+
"pasteurization": "ON",
|
|
133
|
+
"testPeriod": "P1Y6M",
|
|
134
|
+
"unitHandling": "VERIFY",
|
|
135
|
+
"nanHandling": "OFF",
|
|
136
|
+
"language": "FASTEXPR",
|
|
137
|
+
"visualization": False,
|
|
138
|
+
}
|
|
139
|
+
if settings:
|
|
140
|
+
default_settings.update(settings)
|
|
141
|
+
|
|
142
|
+
payload = {
|
|
143
|
+
"type": alpha_type,
|
|
144
|
+
"settings": default_settings,
|
|
145
|
+
"regular": expression if regular == "close" else regular,
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
# If expression is provided separately from regular field
|
|
149
|
+
if regular == "close":
|
|
150
|
+
payload["regular"] = expression
|
|
151
|
+
|
|
152
|
+
response = self._session.post(f"{BASE_URL}/simulations", json=payload)
|
|
153
|
+
response.raise_for_status()
|
|
154
|
+
|
|
155
|
+
progress_url = response.headers.get("Location")
|
|
156
|
+
return SimulationResult(self._session, progress_url)
|
|
157
|
+
|
|
158
|
+
# -------------------------------------------------------------------------
|
|
159
|
+
# Alphas
|
|
160
|
+
# -------------------------------------------------------------------------
|
|
161
|
+
|
|
162
|
+
def get_alpha(self, alpha_id: str) -> dict:
|
|
163
|
+
"""
|
|
164
|
+
Retrieve details of a completed alpha by ID.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
alpha_id: The alpha ID string.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Alpha details as a dict.
|
|
171
|
+
"""
|
|
172
|
+
response = self._session.get(f"{BASE_URL}/alphas/{alpha_id}")
|
|
173
|
+
response.raise_for_status()
|
|
174
|
+
return response.json()
|
|
175
|
+
|
|
176
|
+
def get_pnl(self, alpha_id: str, poll_interval: float = 5.0) -> dict:
|
|
177
|
+
"""
|
|
178
|
+
Retrieve PnL record set for an alpha, polling until ready.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
alpha_id: The alpha ID string.
|
|
182
|
+
poll_interval: Fallback seconds to wait between polls if no Retry-After header.
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
PnL record set as a dict.
|
|
186
|
+
"""
|
|
187
|
+
return self._poll_recordset(alpha_id, "pnl", poll_interval)
|
|
188
|
+
|
|
189
|
+
def get_recordset(self, alpha_id: str, record_set_name: str, poll_interval: float = 5.0) -> dict:
|
|
190
|
+
"""
|
|
191
|
+
Retrieve any named record set for an alpha, polling until ready.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
alpha_id: The alpha ID string.
|
|
195
|
+
record_set_name: Name of the record set (e.g. "pnl", "sharpe").
|
|
196
|
+
poll_interval: Fallback seconds between polls if no Retry-After header.
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
Record set data as a dict.
|
|
200
|
+
"""
|
|
201
|
+
return self._poll_recordset(alpha_id, record_set_name, poll_interval)
|
|
202
|
+
|
|
203
|
+
def _poll_recordset(self, alpha_id: str, record_set_name: str, poll_interval: float) -> dict:
|
|
204
|
+
url = f"{BASE_URL}/alphas/{alpha_id}/recordsets/{record_set_name}"
|
|
205
|
+
while True:
|
|
206
|
+
response = self._session.get(url)
|
|
207
|
+
retry_after = float(response.headers.get("Retry-After", 0))
|
|
208
|
+
if retry_after == 0:
|
|
209
|
+
response.raise_for_status()
|
|
210
|
+
return response.json()
|
|
211
|
+
#print(f"Sleeping for {retry_after} seconds...")
|
|
212
|
+
sleep(retry_after)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class SimulationResult:
|
|
216
|
+
"""Represents a pending or completed alpha simulation."""
|
|
217
|
+
|
|
218
|
+
def __init__(self, session: requests.Session, progress_url: str):
|
|
219
|
+
self._session = session
|
|
220
|
+
self.progress_url = progress_url
|
|
221
|
+
self.alpha_id: str = None
|
|
222
|
+
self._result: dict = None
|
|
223
|
+
|
|
224
|
+
def wait(self, verbose: bool = True) -> dict:
|
|
225
|
+
"""
|
|
226
|
+
Poll until the simulation completes.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
verbose: If True, print polling status messages.
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
The completed simulation result JSON containing the alpha id.
|
|
233
|
+
"""
|
|
234
|
+
while True:
|
|
235
|
+
response = self._session.get(self.progress_url)
|
|
236
|
+
retry_after = float(response.headers.get("Retry-After", 0))
|
|
237
|
+
if retry_after == 0:
|
|
238
|
+
response.raise_for_status()
|
|
239
|
+
self._result = response.json()
|
|
240
|
+
self.alpha_id = self._result.get("alpha")
|
|
241
|
+
if verbose:
|
|
242
|
+
print(f"Alpha simulation complete. Alpha ID: {self.alpha_id}")
|
|
243
|
+
return self._result
|
|
244
|
+
if verbose:
|
|
245
|
+
print(f"Simulating... sleeping for {retry_after} seconds")
|
|
246
|
+
sleep(retry_after)
|
|
247
|
+
|
|
248
|
+
def get_alpha(self) -> dict:
|
|
249
|
+
"""
|
|
250
|
+
Fetch full alpha details after simulation completes.
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
Alpha details dict.
|
|
254
|
+
|
|
255
|
+
Raises:
|
|
256
|
+
RuntimeError: If wait() has not been called yet.
|
|
257
|
+
"""
|
|
258
|
+
if not self.alpha_id:
|
|
259
|
+
raise RuntimeError("Simulation not complete. Call wait() first.")
|
|
260
|
+
response = self._session.get(f"{BASE_URL}/alphas/{self.alpha_id}")
|
|
261
|
+
response.raise_for_status()
|
|
262
|
+
return response.json()
|
|
263
|
+
|
|
264
|
+
def get_pnl(self, poll_interval: float = 5.0) -> dict:
|
|
265
|
+
"""
|
|
266
|
+
Retrieve PnL record set after simulation completes.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
poll_interval: Fallback seconds between polls if no Retry-After header.
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
PnL record set dict.
|
|
273
|
+
|
|
274
|
+
Raises:
|
|
275
|
+
RuntimeError: If wait() has not been called yet.
|
|
276
|
+
"""
|
|
277
|
+
if not self.alpha_id:
|
|
278
|
+
raise RuntimeError("Simulation not complete. Call wait() first.")
|
|
279
|
+
client = BrainClient.__new__(BrainClient)
|
|
280
|
+
client._session = self._session
|
|
281
|
+
client._authenticated = True
|
|
282
|
+
return client.get_pnl(self.alpha_id, poll_interval)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example usage of the WorldQuant Brain API client library.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from brain_client import BrainClient
|
|
6
|
+
|
|
7
|
+
# ── วิธีที่ 1: ใส่ email/password โดยตรง ────────────────────────────────────
|
|
8
|
+
client = BrainClient(email="your@email.com", password="yourpassword")
|
|
9
|
+
client.authenticate()
|
|
10
|
+
|
|
11
|
+
# ── วิธีที่ 2: Interactive prompt (ถามตอนรัน) ────────────────────────────────
|
|
12
|
+
# client = BrainClient() # ถ้าไม่มี ~/.brain_credentials จะ prompt อัตโนมัติ
|
|
13
|
+
# client.authenticate()
|
|
14
|
+
|
|
15
|
+
# ── วิธีที่ 3: login() — สั้นสุด สร้าง + auth ในบรรทัดเดียว ─────────────────
|
|
16
|
+
# client = BrainClient.login() # interactive prompt
|
|
17
|
+
# client = BrainClient.login("your@email.com", "pass") # หรือใส่ตรงๆ
|
|
18
|
+
|
|
19
|
+
# ── วิธีที่ 4: Credentials file ──────────────────────────────────────────────
|
|
20
|
+
# client = BrainClient(credentials_file="~/my_creds.json")
|
|
21
|
+
# client.authenticate()
|
|
22
|
+
|
|
23
|
+
print("Authenticated!")
|
|
24
|
+
|
|
25
|
+
# Submit simulation
|
|
26
|
+
sim = client.simulate(
|
|
27
|
+
expression="close / ts_mean(close, 20) - 1",
|
|
28
|
+
settings={
|
|
29
|
+
"region": "USA",
|
|
30
|
+
"universe": "TOP3000",
|
|
31
|
+
"neutralization": "SUBINDUSTRY",
|
|
32
|
+
}
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
result = sim.wait(verbose=True)
|
|
36
|
+
print("Alpha ID:", sim.alpha_id)
|
|
37
|
+
|
|
38
|
+
alpha = sim.get_alpha()
|
|
39
|
+
print("Sharpe:", alpha["is"]["sharpe"])
|
|
40
|
+
print("Fitness:", alpha["is"]["fitness"])
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "autobrain-sim"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Python client for the WorldQuant Brain API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.7"
|
|
11
|
+
|
|
12
|
+
dependencies = [
|
|
13
|
+
"requests>=2.28",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[project.optional-dependencies]
|
|
17
|
+
dev = [
|
|
18
|
+
"pytest",
|
|
19
|
+
"pytest-mock",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[tool.setuptools]
|
|
23
|
+
py-modules = ["brain_client"]
|