alphaengine 0.2.0__tar.gz → 0.3.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.
- {alphaengine-0.2.0 → alphaengine-0.3.0}/.github/workflows/ci.yml +6 -0
- alphaengine-0.3.0/.gitignore +30 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/PKG-INFO +271 -79
- alphaengine-0.3.0/README.md +457 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/pyproject.toml +15 -0
- alphaengine-0.3.0/scripts/gen_docs.py +138 -0
- alphaengine-0.3.0/src/alphaengine/__main__.py +28 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/src/alphaengine/_version.py +6 -1
- alphaengine-0.3.0/src/alphaengine/agent/__init__.py +40 -0
- alphaengine-0.3.0/src/alphaengine/agent/answer.py +348 -0
- alphaengine-0.3.0/src/alphaengine/auth.py +138 -0
- alphaengine-0.3.0/src/alphaengine/cli.py +3173 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/src/alphaengine/client/__init__.py +4 -0
- alphaengine-0.3.0/src/alphaengine/client/agent.py +262 -0
- alphaengine-0.3.0/src/alphaengine/client/executor.py +748 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/src/alphaengine/client/session.py +210 -9
- alphaengine-0.3.0/src/alphaengine/commands.py +390 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/src/alphaengine/core/__init__.py +21 -0
- alphaengine-0.3.0/src/alphaengine/core/profile.py +282 -0
- alphaengine-0.3.0/src/alphaengine/core/screen.py +286 -0
- alphaengine-0.3.0/src/alphaengine/core/series_shapes.py +71 -0
- alphaengine-0.3.0/src/alphaengine/core/signals.py +283 -0
- alphaengine-0.3.0/src/alphaengine/core/stress.py +226 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/src/alphaengine/core/technical.py +5 -2
- alphaengine-0.2.0/examples/momentum.py → alphaengine-0.3.0/src/alphaengine/demo.py +80 -10
- alphaengine-0.3.0/src/alphaengine/demo_book.py +31 -0
- alphaengine-0.3.0/src/alphaengine/demo_returns.py +18 -0
- alphaengine-0.3.0/src/alphaengine/demo_signal.py +44 -0
- alphaengine-0.3.0/src/alphaengine/demo_universe.py +18 -0
- alphaengine-0.3.0/src/alphaengine/loaders.py +227 -0
- alphaengine-0.3.0/src/alphaengine/model.py +248 -0
- alphaengine-0.3.0/tests/test_agent.py +136 -0
- alphaengine-0.3.0/tests/test_answer.py +263 -0
- alphaengine-0.3.0/tests/test_cli.py +1324 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/tests/test_client.py +305 -0
- alphaengine-0.3.0/tests/test_commands.py +207 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/tests/test_goldens.py +40 -0
- alphaengine-0.3.0/tests/test_loaders.py +231 -0
- alphaengine-0.3.0/tests/test_screen.py +218 -0
- alphaengine-0.3.0/tests/test_signals.py +278 -0
- alphaengine-0.2.0/.gitignore +0 -14
- alphaengine-0.2.0/CHANGELOG.md +0 -198
- alphaengine-0.2.0/README.md +0 -265
- alphaengine-0.2.0/src/alphaengine/agent/__init__.py +0 -16
- alphaengine-0.2.0/src/alphaengine/cli.py +0 -506
- alphaengine-0.2.0/src/alphaengine/client/executor.py +0 -329
- alphaengine-0.2.0/tests/test_cli.py +0 -187
- {alphaengine-0.2.0 → alphaengine-0.3.0}/.github/workflows/publish.yml +0 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/LICENSE +0 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/SECURITY.md +0 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/src/alphaengine/__init__.py +0 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/src/alphaengine/agent/driver.py +0 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/src/alphaengine/core/backtest.py +0 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/src/alphaengine/core/factors.py +0 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/src/alphaengine/core/pairs.py +0 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/src/alphaengine/core/performance.py +0 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/src/alphaengine/core/risk.py +0 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/src/alphaengine/core/validation.py +0 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/src/alphaengine/py.typed +0 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/src/alphaengine/study/__init__.py +0 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/src/alphaengine/study/report.py +0 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/src/alphaengine/study/schema.py +0 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/src/alphaengine/sweep/__init__.py +0 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/src/alphaengine/sweep/runner.py +0 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/tests/test_smoke.py +0 -0
- {alphaengine-0.2.0 → alphaengine-0.3.0}/tests/test_sweep.py +0 -0
|
@@ -45,6 +45,12 @@ jobs:
|
|
|
45
45
|
- run: ruff check src tests
|
|
46
46
|
- run: ruff format --check src tests
|
|
47
47
|
- run: mypy src
|
|
48
|
+
# THE FIFTH COPY GUARD. The command list lived in three hand-maintained
|
|
49
|
+
# places and none of them agreed: `--data` and `--universe` shipped and
|
|
50
|
+
# appeared in one. It is generated from `commands.py` now, and this fails
|
|
51
|
+
# the build when the README falls behind rather than letting a stale list
|
|
52
|
+
# quietly tell somebody a feature does not exist.
|
|
53
|
+
- run: python scripts/gen_docs.py --check
|
|
48
54
|
|
|
49
55
|
# The numbers this library returns are a public contract once published: a
|
|
50
56
|
# study written today has to reproduce in two years. This job exists to make
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
.venv/
|
|
4
|
+
venv/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
*.egg-info/
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
.mypy_cache/
|
|
10
|
+
.ruff_cache/
|
|
11
|
+
.coverage
|
|
12
|
+
htmlcov/
|
|
13
|
+
.DS_Store
|
|
14
|
+
.civenv/
|
|
15
|
+
|
|
16
|
+
# CREDENTIALS. Nothing here was ignored, so a `.env` created while working on
|
|
17
|
+
# this repo would have been committed to a PUBLIC repository with nothing to
|
|
18
|
+
# stop it. Found in a sweep 2026-08-02; no secret was ever actually committed.
|
|
19
|
+
.env
|
|
20
|
+
.env.*
|
|
21
|
+
*.pem
|
|
22
|
+
*.key
|
|
23
|
+
secrets.json
|
|
24
|
+
credentials.json
|
|
25
|
+
|
|
26
|
+
# Artifacts the package itself writes. `r.save()` drops study.json in the
|
|
27
|
+
# working directory by default, so running the example in a clone leaves one
|
|
28
|
+
# staged for the next `git add .`.
|
|
29
|
+
study.json
|
|
30
|
+
*.study.json
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: alphaengine
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Validated research tooling for investment strategies: deflation, overfitting detection, and honest trial counts.
|
|
5
5
|
Project-URL: Homepage, https://github.com/quantOSC/alphaengine
|
|
6
6
|
Project-URL: Documentation, https://github.com/quantOSC/alphaengine#readme
|
|
@@ -41,49 +41,216 @@ Description-Content-Type: text/markdown
|
|
|
41
41
|
|
|
42
42
|
# AlphaEngine
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
**The research loop, on your machine.** Ask what is worth looking at, whether it
|
|
45
|
+
holds up, how much to hold, and whether anything has crossed a line. Your data
|
|
46
|
+
never leaves.
|
|
47
47
|
|
|
48
48
|
```bash
|
|
49
49
|
pip install alphaengine
|
|
50
|
+
alphaengine demo # the whole offline half, no account, no data of your own
|
|
50
51
|
```
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
from alphaengine import sweep
|
|
53
|
+
## What it answers
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
Eight questions, in the order a research week actually asks them. The command
|
|
56
|
+
IS the question; only two of them need code of yours.
|
|
57
|
+
|
|
58
|
+
| Ask | Command | Workflow | Needs |
|
|
59
|
+
|---|---|---|---|
|
|
60
|
+
| Can I trust this data? | `diagnose` | `diagnose_data` | prices |
|
|
61
|
+
| What is worth a look? | `screen` | `screen_universe` | prices |
|
|
62
|
+
| Does this signal carry information? | `signal` | `evaluate_signal` | a signal panel + prices |
|
|
63
|
+
| Is this real, once you count the tries? | `validate` | `validate_study` | prices + your backtest |
|
|
64
|
+
| Where does it break? | `stress` | `stress_study` | a return series |
|
|
65
|
+
| Is it new, or my book again? | `overlap` | `check_overlap` | the candidate + the book |
|
|
66
|
+
| How much should I hold? | `size` | `size_position` | a return series |
|
|
67
|
+
| Still inside the lines? | `monitor` | `monitor_sleeve` | a return series |
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
alphaengine diagnose --data prices.csv # before anything runs on it
|
|
71
|
+
alphaengine screen --universe sp500
|
|
72
|
+
alphaengine size --data returns.csv
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**The refusals are the point.** A screen over a universe that mostly could not
|
|
76
|
+
be measured is refused as a survivorship artifact rather than ranked. A size on
|
|
77
|
+
a record shorter than its own minimum track length is refused outright, because
|
|
78
|
+
a small position is still a claim and the caveat does not travel with it into a
|
|
79
|
+
book. A monitor with no stated tolerances reports `unchecked` — never a green
|
|
80
|
+
light. A stop exits 0: "this did not clear the bar" is the system working.
|
|
56
81
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
82
|
+
Or say it in plain English and let your own model pick:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
alphaengine
|
|
86
|
+
> which of my names are overbought on RSI?
|
|
60
87
|
```
|
|
61
88
|
|
|
62
|
-
|
|
89
|
+
That second path is EXPLORATORY: the model chooses a workflow and then chooses
|
|
90
|
+
each step from what the server permits. Two runs of the same question may
|
|
91
|
+
differ, and the run says so. `run <workflow>` is SCRIPTED and reproducible.
|
|
92
|
+
Both are legitimate; presenting one as the other is not.
|
|
93
|
+
|
|
94
|
+
## The three rungs
|
|
95
|
+
|
|
96
|
+
Each is useful without the one above it. The boot screen shows which are lit.
|
|
63
97
|
|
|
64
|
-
|
|
98
|
+
| Rung | What you need | What you get |
|
|
99
|
+
|---|---|---|
|
|
100
|
+
| **The maths** | nothing | Every statistic, offline, forever. No account. |
|
|
101
|
+
| **Workflows** | a QuantOS `ae_live_` key | The loop end to end, with the record. |
|
|
102
|
+
| **Ask anything** | your OWN model key | Plain English in. Runs under your account, not ours. |
|
|
65
103
|
|
|
66
|
-
|
|
67
|
-
|
|
104
|
+
Nothing here stores a model key: it is read from your environment at call time
|
|
105
|
+
and handed to the provider's own client. There is no field to put one in.
|
|
106
|
+
|
|
107
|
+
## Getting your data in
|
|
108
|
+
|
|
109
|
+
Three doors, and nothing is ever fetched on your behalf.
|
|
68
110
|
|
|
69
111
|
```bash
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
112
|
+
alphaengine run screen_universe --data prices.csv # a local CSV
|
|
113
|
+
alphaengine run screen_universe --universe sp500 # registered in the portal
|
|
114
|
+
alphaengine run validate_study --project research.momentum # a module of yours
|
|
73
115
|
```
|
|
74
116
|
|
|
117
|
+
`--data` reads three shapes, decided by the header and nothing else:
|
|
118
|
+
|
|
75
119
|
```
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
dsr 0.6352
|
|
120
|
+
wide date,AAPL,MSFT,NVDA one column per name
|
|
121
|
+
long date,symbol,close one row per name per day
|
|
122
|
+
series date,close a single series, or one bare column of numbers
|
|
80
123
|
```
|
|
81
124
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
125
|
+
A file it cannot place raises and names all three rather than guessing. A loader
|
|
126
|
+
that guesses eventually guesses wrong on somebody's book, and the failure is not
|
|
127
|
+
an error: it is a screen that ranked returns as prices and produced a shortlist
|
|
128
|
+
nobody can tell is wrong.
|
|
129
|
+
|
|
130
|
+
`--project` is the only door that can carry a **simulator**, because a simulator
|
|
131
|
+
is code. That is why `validate_study` needs it — and `evaluate_signal` needs it
|
|
132
|
+
for the scores it measures — while the rest run on prices alone.
|
|
133
|
+
|
|
134
|
+
`--universe` brings the closes you stored with it in the portal. That is your own
|
|
135
|
+
upload decrypted back to your own account, not us fetching market data, and the
|
|
136
|
+
distinction is the whole of the data boundary below.
|
|
137
|
+
|
|
138
|
+
## Command reference
|
|
139
|
+
|
|
140
|
+
`alphaengine commands` prints this directory in the terminal, and
|
|
141
|
+
`alphaengine commands run` expands any one of them.
|
|
142
|
+
|
|
143
|
+
<!-- COMMANDS:START -->
|
|
144
|
+
<!-- Generated by scripts/gen_docs.py from src/alphaengine/commands.py.
|
|
145
|
+
Do not edit by hand; run `python scripts/gen_docs.py --write`. -->
|
|
146
|
+
|
|
147
|
+
### Get started
|
|
148
|
+
|
|
149
|
+
| Command | Does | Where |
|
|
150
|
+
|---|---|---|
|
|
151
|
+
| `demo` | run the built-in example offline, with no account and no data | shell + session |
|
|
152
|
+
| `workflows` | what the server offers, what each needs, and which reproduce | shell + session |
|
|
153
|
+
| `key [quantos \| anthropic \| openai]` | enter a credential now, or see which rungs are unlocked | session |
|
|
154
|
+
| `commands [verb]` | this directory, or one command in full | shell + session |
|
|
155
|
+
|
|
156
|
+
### Do the work
|
|
157
|
+
|
|
158
|
+
| Command | Does | Where |
|
|
159
|
+
|---|---|---|
|
|
160
|
+
| `diagnose` | can I trust this data? | shell + session |
|
|
161
|
+
| `screen` | what is worth a look? | shell + session |
|
|
162
|
+
| `signal` | does this signal carry information? | shell + session |
|
|
163
|
+
| `validate` | is this real? | shell + session |
|
|
164
|
+
| `stress` | where does it break? | shell + session |
|
|
165
|
+
| `overlap` | is it new, or my book again? | shell + session |
|
|
166
|
+
| `size` | how much? | shell + session |
|
|
167
|
+
| `monitor` | still inside the lines? | shell + session |
|
|
168
|
+
| `run <workflow>` | run one workflow exactly as written | shell + session |
|
|
169
|
+
| `<anything else>` | ask in plain English; your model picks a workflow and drives it | session |
|
|
170
|
+
| `status` | the current run, and what is loaded | session |
|
|
171
|
+
|
|
172
|
+
### Bring your data
|
|
173
|
+
|
|
174
|
+
| Command | Does | Where |
|
|
175
|
+
|---|---|---|
|
|
176
|
+
| `universe <name>` | load a universe you registered in the portal, with its closes | session |
|
|
177
|
+
| `data <file.csv>` | load a local CSV without leaving the session | session |
|
|
178
|
+
| `project <module>` | load `data` and `backtest_fn` from a module of yours | session |
|
|
179
|
+
|
|
180
|
+
### Session
|
|
181
|
+
|
|
182
|
+
| Command | Does | Where |
|
|
183
|
+
|---|---|---|
|
|
184
|
+
| `logout` | remove stored credentials from this machine | shell + session |
|
|
185
|
+
| `version` | print the version | shell |
|
|
186
|
+
| `help` | the short list | session |
|
|
187
|
+
| `quit` | leave the session | session |
|
|
188
|
+
|
|
189
|
+
### Data flags
|
|
190
|
+
|
|
191
|
+
| Flag | What it takes |
|
|
192
|
+
|---|---|
|
|
193
|
+
| `--data FILE` | a local CSV. Wide (date,AAPL,MSFT), long (date,symbol,close), or one series. |
|
|
194
|
+
| `--universe NAME` | a universe you registered in the portal, with the closes you stored. |
|
|
195
|
+
| `--project MODULE` | a Python module. The only door that can carry a simulator. |
|
|
196
|
+
|
|
197
|
+
### Flags
|
|
198
|
+
|
|
199
|
+
| Flag | Does |
|
|
200
|
+
|---|---|
|
|
201
|
+
| `--url URL` | workflow server (default $QUANTOS_API_URL or the public API) |
|
|
202
|
+
| `--key KEY` | portal-issued ae_live_ key (default $QUANTOS_API_KEY) |
|
|
203
|
+
| `--project MODULE` | a module exposing `data` and `backtest_fn` |
|
|
204
|
+
| `--data FILE` | a local CSV: wide, long, or a single series |
|
|
205
|
+
| `--universe NAME` | a universe registered in the portal, with its stored closes |
|
|
206
|
+
| `--symbol TICKER` | one name out of a loaded universe; its closes become the return series |
|
|
207
|
+
| `--sleeve NAME` | the sleeve this run belongs to, and the budget it is bound by |
|
|
208
|
+
| `--thesis ID` | a draft thesis to propose this run's shortlist as a sleeve for |
|
|
209
|
+
| `--label TEXT` | what to call the artifact this run produces |
|
|
210
|
+
| `--input K=V` | a workflow input; repeatable |
|
|
211
|
+
| `--quiet` | only the result, no step narration |
|
|
212
|
+
|
|
213
|
+
### Examples
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
alphaengine diagnose --universe <name>
|
|
217
|
+
alphaengine screen --universe <name>
|
|
218
|
+
alphaengine signal
|
|
219
|
+
alphaengine validate
|
|
220
|
+
alphaengine stress
|
|
221
|
+
alphaengine overlap
|
|
222
|
+
alphaengine size
|
|
223
|
+
alphaengine monitor
|
|
224
|
+
alphaengine demo
|
|
225
|
+
alphaengine workflows
|
|
226
|
+
alphaengine commands
|
|
227
|
+
alphaengine run screen_universe --universe sp500
|
|
228
|
+
alphaengine run size_position --data returns.csv
|
|
229
|
+
alphaengine run validate_study --project research.momentum
|
|
230
|
+
alphaengine logout
|
|
231
|
+
alphaengine version
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
<!-- COMMANDS:END -->
|
|
235
|
+
|
|
236
|
+
## The library, without the CLI
|
|
237
|
+
|
|
238
|
+
The commands above are a wrapper. Everything they compute is importable, runs
|
|
239
|
+
offline, and needs no account:
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
from alphaengine import sweep
|
|
243
|
+
|
|
244
|
+
r = sweep(backtest_fn, {"fast": [5, 10, 20], "slow": [50, 100, 200]}, data=prices)
|
|
245
|
+
|
|
246
|
+
r.surface() # is the result a broad plateau or a single lucky configuration?
|
|
247
|
+
r.verdict() # deflated for the 9 trials that were actually run
|
|
248
|
+
r.save() # study.json, on your disk
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
The trial count is **derived from the grid that ran**, never asserted. Omit it
|
|
252
|
+
and it is recorded as `not_recorded` rather than defaulting to a flattering 1,
|
|
253
|
+
and an unrecorded denominator cannot reach an `edge` verdict at all.
|
|
87
254
|
|
|
88
255
|
## Writing your own `backtest_fn`
|
|
89
256
|
|
|
@@ -102,7 +269,8 @@ correspond. In practice: pick a warm-up long enough for the slowest window in
|
|
|
102
269
|
your grid and start every configuration there.
|
|
103
270
|
|
|
104
271
|
```python
|
|
105
|
-
WARMUP = 200
|
|
272
|
+
WARMUP = 200 # covers the slowest `slow` in the grid
|
|
273
|
+
|
|
106
274
|
|
|
107
275
|
def backtest_fn(*, data, fast, slow):
|
|
108
276
|
close = data["close"]
|
|
@@ -141,53 +309,6 @@ spike surrounded by failures is a result fitted to its own parameters.
|
|
|
141
309
|
back, and a content hash of the data it ran on. Readable in a text editor,
|
|
142
310
|
diffable, and versioned so it still parses in two years.
|
|
143
311
|
|
|
144
|
-
## Running a workflow from the terminal
|
|
145
|
-
|
|
146
|
-
The offline half above is complete on its own. A *workflow* adds a sequence —
|
|
147
|
-
what to run, in what order, and what stops the run — and that sequence lives on
|
|
148
|
-
a QuantOS workflow server, so this part needs an account.
|
|
149
|
-
|
|
150
|
-
```bash
|
|
151
|
-
alphaengine version # no account needed
|
|
152
|
-
alphaengine workflows # what your workspace offers
|
|
153
|
-
alphaengine run validate_study --project examples.momentum
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
The run narrates itself, because a loop you cannot watch is a loop you cannot
|
|
157
|
-
trust. The server names an op, this machine executes it and hands back figures:
|
|
158
|
-
|
|
159
|
-
```
|
|
160
|
-
validate_study · <your workflow server>
|
|
161
|
-
server → compute.sweep
|
|
162
|
-
local · done
|
|
163
|
-
server → compute.deflated_sharpe
|
|
164
|
-
local · done
|
|
165
|
-
server → Stop: the surface is a ridge, not a plateau
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
`--project` names an ordinary module of yours exposing `data` and, if the
|
|
169
|
-
workflow sweeps, `backtest_fn`. Authenticate with a key from the portal:
|
|
170
|
-
|
|
171
|
-
```bash
|
|
172
|
-
export QUANTOS_API_KEY=ae_live_... # QUANTOS_API_URL for self-hosted or VPC
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
**A stop exits 0.** "This did not clear the bar" is the system working, not a
|
|
176
|
-
broken build — a non-zero exit there would make every CI pipeline treat an
|
|
177
|
-
honest refusal as a failure, which is exactly the pressure that gets honesty
|
|
178
|
-
controls switched off. Only a run that could not execute a step exits non-zero.
|
|
179
|
-
|
|
180
|
-
> **Install this into the venv you do research in — not with `pipx` or
|
|
181
|
-
> `uv tool install`.** The compute steps execute in-process against your own
|
|
182
|
-
> DataFrames, so tool isolation, which is normally the right way to install a
|
|
183
|
-
> CLI, is the one thing that cannot work here. You cannot have isolation and
|
|
184
|
-
> in-process data access, and your data not moving is the point.
|
|
185
|
-
|
|
186
|
-
**No LLM dependency and no key field.** `AgentDriver` takes a callable, so you
|
|
187
|
-
bring your own model by passing a function. There is nowhere in this tool to put
|
|
188
|
-
a model key, ours or yours, and that is how "runs under your own account" is
|
|
189
|
-
satisfied structurally rather than promised.
|
|
190
|
-
|
|
191
312
|
## What is in it
|
|
192
313
|
|
|
193
314
|
| Module | Contents |
|
|
@@ -212,10 +333,10 @@ the PM who will act on it, `report()` sends the study — and only the study.
|
|
|
212
333
|
import os
|
|
213
334
|
from alphaengine import Study, sweep
|
|
214
335
|
|
|
215
|
-
os.environ["QUANTOS_API_KEY"] = "ae_live_..."
|
|
336
|
+
os.environ["QUANTOS_API_KEY"] = "ae_live_..." # created in the portal
|
|
216
337
|
|
|
217
338
|
r = sweep(backtest_fn, grid, data=prices)
|
|
218
|
-
r.save()
|
|
339
|
+
r.save() # yours, on your disk, always
|
|
219
340
|
|
|
220
341
|
Study.from_sweep(r, label="momentum, 9 configs").report()
|
|
221
342
|
```
|
|
@@ -293,12 +414,83 @@ While the leading digit is 0 the minor position carries that rule — 0.1 → 0.
|
|
|
293
414
|
what a changed figure costs — so every 0.2.x release produces identical numbers.
|
|
294
415
|
CI fails if a pinned value moves.
|
|
295
416
|
|
|
417
|
+
## Installing: "command not found"
|
|
418
|
+
|
|
419
|
+
The console script lives in your venv's `Scripts/` (Windows) or `bin/` (POSIX),
|
|
420
|
+
which joins your PATH only while that venv is **activated**. A fresh install
|
|
421
|
+
followed by `alphaengine` therefore says *command not found*, which reads as a
|
|
422
|
+
broken install and is not one.
|
|
423
|
+
|
|
424
|
+
```powershell
|
|
425
|
+
.\.venv\Scripts\Activate.ps1 # Windows PowerShell
|
|
426
|
+
source .venv/bin/activate # macOS / Linux
|
|
427
|
+
alphaengine
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Or skip activation entirely — **`python -m alphaengine` always works**, because
|
|
431
|
+
the interpreter that can import the package can always run it:
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
.venv/Scripts/python.exe -m alphaengine # Windows, unactivated
|
|
435
|
+
.venv/bin/python -m alphaengine # POSIX, unactivated
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
This is the price of in-process data access, and it is the one cost we will not
|
|
439
|
+
engineer around: a launcher that worked from anywhere would have to run in its
|
|
440
|
+
own environment, which is exactly the isolation that makes `compute.*` unable to
|
|
441
|
+
see your DataFrames.
|
|
442
|
+
|
|
443
|
+
The run narrates itself, because a loop you cannot watch is a loop you cannot
|
|
444
|
+
trust. The server names an op, this machine executes it and hands back figures:
|
|
445
|
+
|
|
446
|
+
```
|
|
447
|
+
validate_study · <your workflow server>
|
|
448
|
+
server → compute.sweep
|
|
449
|
+
local · done
|
|
450
|
+
server → compute.deflated_sharpe
|
|
451
|
+
local · done
|
|
452
|
+
server → Stop: the surface is a ridge, not a plateau
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
`--project` names an ordinary module of yours exposing `data` and, if the
|
|
456
|
+
workflow sweeps, `backtest_fn`. Authenticate with a key from the portal:
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
export QUANTOS_API_KEY=ae_live_... # QUANTOS_API_URL for self-hosted or VPC
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
**A stop exits 0.** "This did not clear the bar" is the system working, not a
|
|
463
|
+
broken build — a non-zero exit there would make every CI pipeline treat an
|
|
464
|
+
honest refusal as a failure, which is exactly the pressure that gets honesty
|
|
465
|
+
controls switched off. Only a run that could not execute a step exits non-zero.
|
|
466
|
+
|
|
467
|
+
> **Install this into the venv you do research in — not with `pipx` or
|
|
468
|
+
> `uv tool install`.** The compute steps execute in-process against your own
|
|
469
|
+
> DataFrames, so tool isolation, which is normally the right way to install a
|
|
470
|
+
> CLI, is the one thing that cannot work here. You cannot have isolation and
|
|
471
|
+
> in-process data access, and your data not moving is the point.
|
|
472
|
+
|
|
473
|
+
**No LLM dependency and no key field.** `AgentDriver` takes a callable, so you
|
|
474
|
+
bring your own model by passing a function. There is nowhere in this tool to put
|
|
475
|
+
a model key, ours or yours, and that is how "runs under your own account" is
|
|
476
|
+
satisfied structurally rather than promised.
|
|
477
|
+
|
|
296
478
|
## Development
|
|
297
479
|
|
|
298
480
|
```bash
|
|
299
|
-
python -m venv .venv && .venv/bin/pip install -e '.[factors]'
|
|
300
|
-
|
|
301
|
-
|
|
481
|
+
python -m venv .venv && .venv/bin/pip install -e '.[dev,factors]'
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
CI runs five gates. Run all of them before pushing — `ruff check` and
|
|
485
|
+
`ruff format` are different tools with different opinions, and passing one
|
|
486
|
+
says nothing about the other:
|
|
487
|
+
|
|
488
|
+
```bash
|
|
489
|
+
python -m ruff check src tests
|
|
490
|
+
python -m ruff format --check src tests
|
|
491
|
+
python -m mypy src
|
|
492
|
+
python -m pytest
|
|
493
|
+
python -m pytest tests -m golden -q # the frozen figures
|
|
302
494
|
```
|
|
303
495
|
|
|
304
496
|
## Licence
|