everestapi 0.2.6__tar.gz → 0.2.8__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.
- {everestapi-0.2.6/src/everestapi.egg-info → everestapi-0.2.8}/PKG-INFO +74 -13
- {everestapi-0.2.6 → everestapi-0.2.8}/README.md +66 -12
- {everestapi-0.2.6 → everestapi-0.2.8}/pyproject.toml +4 -1
- {everestapi-0.2.6 → everestapi-0.2.8}/src/everestapi/__init__.py +12 -1
- {everestapi-0.2.6 → everestapi-0.2.8}/src/everestapi/cli.py +1 -1
- {everestapi-0.2.6 → everestapi-0.2.8}/src/everestapi/client.py +104 -38
- {everestapi-0.2.6 → everestapi-0.2.8}/src/everestapi/mcp/server.py +728 -57
- {everestapi-0.2.6 → everestapi-0.2.8}/src/everestapi/plots.py +4 -5
- everestapi-0.2.8/src/everestapi/scoring.py +205 -0
- {everestapi-0.2.6 → everestapi-0.2.8/src/everestapi.egg-info}/PKG-INFO +74 -13
- {everestapi-0.2.6 → everestapi-0.2.8}/src/everestapi.egg-info/SOURCES.txt +7 -1
- {everestapi-0.2.6 → everestapi-0.2.8}/src/everestapi.egg-info/requires.txt +8 -0
- {everestapi-0.2.6 → everestapi-0.2.8}/tests/test_client.py +87 -5
- everestapi-0.2.8/tests/test_eve953_mcp_progress.py +176 -0
- everestapi-0.2.8/tests/test_eve957_mcp_annotations_resources.py +75 -0
- everestapi-0.2.8/tests/test_eve959_toolsets.py +72 -0
- everestapi-0.2.8/tests/test_eve967_mcp_discoverability.py +40 -0
- everestapi-0.2.8/tests/test_mcp_and_models.py +570 -0
- {everestapi-0.2.6 → everestapi-0.2.8}/tests/test_prediction_range.py +2 -2
- everestapi-0.2.8/tests/test_scoring.py +112 -0
- everestapi-0.2.6/tests/test_mcp_and_models.py +0 -195
- {everestapi-0.2.6 → everestapi-0.2.8}/LICENSE +0 -0
- {everestapi-0.2.6 → everestapi-0.2.8}/setup.cfg +0 -0
- {everestapi-0.2.6 → everestapi-0.2.8}/src/everestapi/__main__.py +0 -0
- {everestapi-0.2.6 → everestapi-0.2.8}/src/everestapi/mcp/__init__.py +0 -0
- {everestapi-0.2.6 → everestapi-0.2.8}/src/everestapi/mcp/__main__.py +0 -0
- {everestapi-0.2.6 → everestapi-0.2.8}/src/everestapi/types.py +0 -0
- {everestapi-0.2.6 → everestapi-0.2.8}/src/everestapi.egg-info/dependency_links.txt +0 -0
- {everestapi-0.2.6 → everestapi-0.2.8}/src/everestapi.egg-info/entry_points.txt +0 -0
- {everestapi-0.2.6 → everestapi-0.2.8}/src/everestapi.egg-info/top_level.txt +0 -0
- {everestapi-0.2.6 → everestapi-0.2.8}/tests/test_cli.py +0 -0
- {everestapi-0.2.6 → everestapi-0.2.8}/tests/test_diagnostics.py +0 -0
- {everestapi-0.2.6 → everestapi-0.2.8}/tests/test_json_or_raise.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: everestapi
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.8
|
|
4
4
|
Summary: Python SDK for the Everesteer prediction tournament platform
|
|
5
5
|
Author-email: Everesteer <support@everesteer.ai>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -24,10 +24,17 @@ Requires-Dist: click>=8.0
|
|
|
24
24
|
Provides-Extra: dev
|
|
25
25
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
26
26
|
Requires-Dist: pytest-httpx>=0.34.0; extra == "dev"
|
|
27
|
+
Requires-Dist: numpy>=1.26; extra == "dev"
|
|
28
|
+
Requires-Dist: pandas>=2.0; extra == "dev"
|
|
29
|
+
Requires-Dist: scipy>=1.10; extra == "dev"
|
|
27
30
|
Provides-Extra: mcp
|
|
28
31
|
Requires-Dist: mcp>=1.0; extra == "mcp"
|
|
29
32
|
Provides-Extra: viz
|
|
30
33
|
Requires-Dist: plotnine>=0.13; extra == "viz"
|
|
34
|
+
Provides-Extra: scoring
|
|
35
|
+
Requires-Dist: numpy>=1.26; extra == "scoring"
|
|
36
|
+
Requires-Dist: pandas>=2.0; extra == "scoring"
|
|
37
|
+
Requires-Dist: scipy>=1.10; extra == "scoring"
|
|
31
38
|
Dynamic: license-file
|
|
32
39
|
|
|
33
40
|
# everestapi
|
|
@@ -89,8 +96,11 @@ api.submit_futures_predictions(model_id="my-fut-model", predictions={...})
|
|
|
89
96
|
Both Parquet and CSV are accepted. **Parquet is recommended** — float precision round-trips cleanly, files compress well, and it matches the format the SDK serves to you (`download_dataset` returns parquet).
|
|
90
97
|
|
|
91
98
|
```python
|
|
99
|
+
# A model slot must exist before you can submit — the platform never auto-creates one.
|
|
100
|
+
api.create_model(name="my-model")
|
|
101
|
+
|
|
92
102
|
api.submit_predictions_file(
|
|
93
|
-
|
|
103
|
+
model_id="my-model",
|
|
94
104
|
file_path="predictions.parquet", # or "predictions.csv"
|
|
95
105
|
tournament="equities",
|
|
96
106
|
)
|
|
@@ -106,16 +116,23 @@ everestapi submit --model my-model --file predictions.parquet
|
|
|
106
116
|
|
|
107
117
|
### Data & diagnostics
|
|
108
118
|
|
|
119
|
+
The hackathon is a display-only diagnostics event. **Tune and self-score offline
|
|
120
|
+
on the labeled validation set** (features + `target_*` columns), then **predict on
|
|
121
|
+
the blind `eiq_live_2026` set and submit** — the leaderboard ranks your
|
|
122
|
+
out-of-sample 2026 CORR on `target_everest_20`. In-sample fit is not rewarded. The
|
|
123
|
+
labeled `eiq_live_2026` answers are held out server-side and are never downloadable.
|
|
124
|
+
|
|
109
125
|
```python
|
|
110
|
-
|
|
111
|
-
api.
|
|
112
|
-
api.get_dataset_info(universe="futures")
|
|
113
|
-
api.get_diagnostics(model_id="my-model")
|
|
126
|
+
# Labeled practice set — tune + self-score offline with everestapi.scoring:
|
|
127
|
+
api.download_dataset(universe="futures", split="validation")
|
|
114
128
|
|
|
115
|
-
#
|
|
116
|
-
#
|
|
117
|
-
api.download_dataset(universe="futures", split="
|
|
129
|
+
# Blind scored set (columns: exped, exped_date, instrument, id — no targets).
|
|
130
|
+
# Predict on it, then submit; it is also the upload id template.
|
|
131
|
+
api.download_dataset(universe="futures", split="live")
|
|
118
132
|
api.submit_validation_diagnostics(model_id="my-model", predictions=df)
|
|
133
|
+
|
|
134
|
+
api.get_dataset_info(universe="futures")
|
|
135
|
+
api.get_diagnostics(model_id="my-model")
|
|
119
136
|
```
|
|
120
137
|
|
|
121
138
|
### Plotting (optional `viz` extra)
|
|
@@ -168,16 +185,59 @@ api.get_stake_balance(model_id="my-model")
|
|
|
168
185
|
api.claim_payout(model_id="my-model", round_id="42")
|
|
169
186
|
```
|
|
170
187
|
|
|
171
|
-
###
|
|
188
|
+
### Score validation predictions offline
|
|
189
|
+
|
|
190
|
+
Reproduce the server's **exact** scoring — CORR20, AIMC20, FNC — *before* you
|
|
191
|
+
submit, so you stop guessing the sign of your signal ("submit raw and negated, let
|
|
192
|
+
the server decide"). The `everestapi.scoring` functions are a verbatim port of the
|
|
193
|
+
platform's scoring engine (verified equal to 1e-12), so your offline number **is**
|
|
194
|
+
the server's number.
|
|
195
|
+
|
|
196
|
+
Install the optional scoring extra (keeps the base SDK light — numpy/pandas/scipy
|
|
197
|
+
are only pulled in here):
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
pip install "everestapi[scoring]"
|
|
201
|
+
```
|
|
172
202
|
|
|
173
203
|
```python
|
|
174
204
|
import pandas as pd
|
|
205
|
+
from everestapi import scoring
|
|
206
|
+
|
|
207
|
+
val = pd.read_parquet("eiq_validation.parquet")
|
|
208
|
+
preds = my_model.predict(val.filter(like="feature_"))
|
|
209
|
+
|
|
210
|
+
# Score per exped (cross-section), then average — matches how the server scores.
|
|
211
|
+
per_exped = [
|
|
212
|
+
scoring.corr20(preds[val.exped == e], val.loc[val.exped == e, "target"])
|
|
213
|
+
for e in val.exped.unique()
|
|
214
|
+
]
|
|
215
|
+
print("mean CORR20:", sum(per_exped) / len(per_exped))
|
|
175
216
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
217
|
+
# Or every metric at once for one exped (ai_model = crowd consensus for that exped):
|
|
218
|
+
scoring.score(preds_e, target_e, ai_model=consensus_e, features=features_e)
|
|
219
|
+
# -> {"corr20", "aimc20", "payout", "fnc", "feature_exposure"}
|
|
179
220
|
```
|
|
180
221
|
|
|
222
|
+
**Sanity-check your pipeline against the example predictions.** The published
|
|
223
|
+
`eiq_validation_example_preds` are a benchmark-grade signal (the Minera ensemble)
|
|
224
|
+
and score a positive mean **CORR20 of ≈ 0.07**. Score that file and reproduce a
|
|
225
|
+
similar number — if you instead get ≈ −0.07, your sign is flipped; if you get ≈ 0,
|
|
226
|
+
your ids/alignment are off:
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
ex = pd.read_parquet("eiq_validation_example_preds.parquet") # column: prediction
|
|
230
|
+
val = pd.read_parquet("eiq_validation.parquet")
|
|
231
|
+
ref = [
|
|
232
|
+
scoring.corr20(ex.loc[val.exped == e, "prediction"], val.loc[val.exped == e, "target"])
|
|
233
|
+
for e in val.exped.unique()
|
|
234
|
+
]
|
|
235
|
+
print(sum(ref) / len(ref)) # ~0.07 -> pipeline + sign are correct
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
A quick convenience for a single overall correlation is also available:
|
|
239
|
+
`EverestAPI.evaluate(predictions, val, target="target_everest_20")`.
|
|
240
|
+
|
|
181
241
|
### CLI
|
|
182
242
|
|
|
183
243
|
```bash
|
|
@@ -207,6 +267,7 @@ with EverestAPI(api_key="...") as api:
|
|
|
207
267
|
|
|
208
268
|
- Python 3.10+
|
|
209
269
|
- httpx >= 0.27
|
|
270
|
+
- Optional `scoring` extra (`pip install "everestapi[scoring]"`): numpy, pandas, scipy — only needed for offline `everestapi.scoring`.
|
|
210
271
|
|
|
211
272
|
## Disclaimers
|
|
212
273
|
|
|
@@ -57,8 +57,11 @@ api.submit_futures_predictions(model_id="my-fut-model", predictions={...})
|
|
|
57
57
|
Both Parquet and CSV are accepted. **Parquet is recommended** — float precision round-trips cleanly, files compress well, and it matches the format the SDK serves to you (`download_dataset` returns parquet).
|
|
58
58
|
|
|
59
59
|
```python
|
|
60
|
+
# A model slot must exist before you can submit — the platform never auto-creates one.
|
|
61
|
+
api.create_model(name="my-model")
|
|
62
|
+
|
|
60
63
|
api.submit_predictions_file(
|
|
61
|
-
|
|
64
|
+
model_id="my-model",
|
|
62
65
|
file_path="predictions.parquet", # or "predictions.csv"
|
|
63
66
|
tournament="equities",
|
|
64
67
|
)
|
|
@@ -74,16 +77,23 @@ everestapi submit --model my-model --file predictions.parquet
|
|
|
74
77
|
|
|
75
78
|
### Data & diagnostics
|
|
76
79
|
|
|
80
|
+
The hackathon is a display-only diagnostics event. **Tune and self-score offline
|
|
81
|
+
on the labeled validation set** (features + `target_*` columns), then **predict on
|
|
82
|
+
the blind `eiq_live_2026` set and submit** — the leaderboard ranks your
|
|
83
|
+
out-of-sample 2026 CORR on `target_everest_20`. In-sample fit is not rewarded. The
|
|
84
|
+
labeled `eiq_live_2026` answers are held out server-side and are never downloadable.
|
|
85
|
+
|
|
77
86
|
```python
|
|
78
|
-
|
|
79
|
-
api.
|
|
80
|
-
api.get_dataset_info(universe="futures")
|
|
81
|
-
api.get_diagnostics(model_id="my-model")
|
|
87
|
+
# Labeled practice set — tune + self-score offline with everestapi.scoring:
|
|
88
|
+
api.download_dataset(universe="futures", split="validation")
|
|
82
89
|
|
|
83
|
-
#
|
|
84
|
-
#
|
|
85
|
-
api.download_dataset(universe="futures", split="
|
|
90
|
+
# Blind scored set (columns: exped, exped_date, instrument, id — no targets).
|
|
91
|
+
# Predict on it, then submit; it is also the upload id template.
|
|
92
|
+
api.download_dataset(universe="futures", split="live")
|
|
86
93
|
api.submit_validation_diagnostics(model_id="my-model", predictions=df)
|
|
94
|
+
|
|
95
|
+
api.get_dataset_info(universe="futures")
|
|
96
|
+
api.get_diagnostics(model_id="my-model")
|
|
87
97
|
```
|
|
88
98
|
|
|
89
99
|
### Plotting (optional `viz` extra)
|
|
@@ -136,16 +146,59 @@ api.get_stake_balance(model_id="my-model")
|
|
|
136
146
|
api.claim_payout(model_id="my-model", round_id="42")
|
|
137
147
|
```
|
|
138
148
|
|
|
139
|
-
###
|
|
149
|
+
### Score validation predictions offline
|
|
150
|
+
|
|
151
|
+
Reproduce the server's **exact** scoring — CORR20, AIMC20, FNC — *before* you
|
|
152
|
+
submit, so you stop guessing the sign of your signal ("submit raw and negated, let
|
|
153
|
+
the server decide"). The `everestapi.scoring` functions are a verbatim port of the
|
|
154
|
+
platform's scoring engine (verified equal to 1e-12), so your offline number **is**
|
|
155
|
+
the server's number.
|
|
156
|
+
|
|
157
|
+
Install the optional scoring extra (keeps the base SDK light — numpy/pandas/scipy
|
|
158
|
+
are only pulled in here):
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
pip install "everestapi[scoring]"
|
|
162
|
+
```
|
|
140
163
|
|
|
141
164
|
```python
|
|
142
165
|
import pandas as pd
|
|
166
|
+
from everestapi import scoring
|
|
167
|
+
|
|
168
|
+
val = pd.read_parquet("eiq_validation.parquet")
|
|
169
|
+
preds = my_model.predict(val.filter(like="feature_"))
|
|
170
|
+
|
|
171
|
+
# Score per exped (cross-section), then average — matches how the server scores.
|
|
172
|
+
per_exped = [
|
|
173
|
+
scoring.corr20(preds[val.exped == e], val.loc[val.exped == e, "target"])
|
|
174
|
+
for e in val.exped.unique()
|
|
175
|
+
]
|
|
176
|
+
print("mean CORR20:", sum(per_exped) / len(per_exped))
|
|
143
177
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
178
|
+
# Or every metric at once for one exped (ai_model = crowd consensus for that exped):
|
|
179
|
+
scoring.score(preds_e, target_e, ai_model=consensus_e, features=features_e)
|
|
180
|
+
# -> {"corr20", "aimc20", "payout", "fnc", "feature_exposure"}
|
|
147
181
|
```
|
|
148
182
|
|
|
183
|
+
**Sanity-check your pipeline against the example predictions.** The published
|
|
184
|
+
`eiq_validation_example_preds` are a benchmark-grade signal (the Minera ensemble)
|
|
185
|
+
and score a positive mean **CORR20 of ≈ 0.07**. Score that file and reproduce a
|
|
186
|
+
similar number — if you instead get ≈ −0.07, your sign is flipped; if you get ≈ 0,
|
|
187
|
+
your ids/alignment are off:
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
ex = pd.read_parquet("eiq_validation_example_preds.parquet") # column: prediction
|
|
191
|
+
val = pd.read_parquet("eiq_validation.parquet")
|
|
192
|
+
ref = [
|
|
193
|
+
scoring.corr20(ex.loc[val.exped == e, "prediction"], val.loc[val.exped == e, "target"])
|
|
194
|
+
for e in val.exped.unique()
|
|
195
|
+
]
|
|
196
|
+
print(sum(ref) / len(ref)) # ~0.07 -> pipeline + sign are correct
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
A quick convenience for a single overall correlation is also available:
|
|
200
|
+
`EverestAPI.evaluate(predictions, val, target="target_everest_20")`.
|
|
201
|
+
|
|
149
202
|
### CLI
|
|
150
203
|
|
|
151
204
|
```bash
|
|
@@ -175,6 +228,7 @@ with EverestAPI(api_key="...") as api:
|
|
|
175
228
|
|
|
176
229
|
- Python 3.10+
|
|
177
230
|
- httpx >= 0.27
|
|
231
|
+
- Optional `scoring` extra (`pip install "everestapi[scoring]"`): numpy, pandas, scipy — only needed for offline `everestapi.scoring`.
|
|
178
232
|
|
|
179
233
|
## Disclaimers
|
|
180
234
|
|
|
@@ -27,13 +27,16 @@ dependencies = [
|
|
|
27
27
|
]
|
|
28
28
|
|
|
29
29
|
[project.optional-dependencies]
|
|
30
|
-
dev = ["pytest>=8.0", "pytest-httpx>=0.34.0"]
|
|
30
|
+
dev = ["pytest>=8.0", "pytest-httpx>=0.34.0", "numpy>=1.26", "pandas>=2.0", "scipy>=1.10"]
|
|
31
31
|
# MCP server (`python -m everestapi.mcp`). Optional — the server falls back to a
|
|
32
32
|
# built-in JSON-RPC stdio loop when the official `mcp` SDK isn't installed.
|
|
33
33
|
mcp = ["mcp>=1.0"]
|
|
34
34
|
# Plotting helpers (`everestapi.plots`). Heavy — pulls matplotlib/pandas/numpy/
|
|
35
35
|
# scipy/statsmodels/mizani. Optional: the SDK and MCP server never import it.
|
|
36
36
|
viz = ["plotnine>=0.13"]
|
|
37
|
+
# Local scoring (`everestapi.scoring`). Optional — keeps the base SDK light
|
|
38
|
+
# (httpx + click only); pull these in to score validation preds offline.
|
|
39
|
+
scoring = ["numpy>=1.26", "pandas>=2.0", "scipy>=1.10"]
|
|
37
40
|
|
|
38
41
|
[project.scripts]
|
|
39
42
|
everestapi = "everestapi.cli:cli"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""EverestAPI — Python SDK for the Everesteer prediction tournament platform."""
|
|
2
2
|
|
|
3
|
-
__version__ = "0.2.
|
|
3
|
+
__version__ = "0.2.8"
|
|
4
4
|
|
|
5
5
|
from everestapi.client import EverestAPI, EverestError
|
|
6
6
|
from everestapi.types import (
|
|
@@ -29,4 +29,15 @@ __all__ = [
|
|
|
29
29
|
"StakeResponse",
|
|
30
30
|
"Submission",
|
|
31
31
|
"UniverseResponse",
|
|
32
|
+
"scoring",
|
|
32
33
|
]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def __getattr__(name):
|
|
37
|
+
# Lazy import so `import everestapi` stays light (httpx + click only) — the
|
|
38
|
+
# numpy/pandas/scipy stack is only loaded if you actually use scoring.
|
|
39
|
+
if name == "scoring":
|
|
40
|
+
import everestapi.scoring as scoring
|
|
41
|
+
|
|
42
|
+
return scoring
|
|
43
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -108,7 +108,7 @@ def submit(model: str, file_path: str, tournament: str, api_key: str | None) ->
|
|
|
108
108
|
try:
|
|
109
109
|
if ext == "parquet":
|
|
110
110
|
result = api.submit_predictions_file(
|
|
111
|
-
|
|
111
|
+
model_id=model,
|
|
112
112
|
file_path=file_path,
|
|
113
113
|
tournament=tournament,
|
|
114
114
|
)
|
|
@@ -133,9 +133,7 @@ def _json_or_raise(resp: httpx.Response) -> dict:
|
|
|
133
133
|
except Exception:
|
|
134
134
|
body = (resp.text or "").strip()
|
|
135
135
|
looks_like_cf = (
|
|
136
|
-
"<html" in body[:200].lower()
|
|
137
|
-
or "cloudflare" in body.lower()
|
|
138
|
-
or "AccessDenied" in body
|
|
136
|
+
"<html" in body[:200].lower() or "cloudflare" in body.lower() or "AccessDenied" in body
|
|
139
137
|
)
|
|
140
138
|
hint = (
|
|
141
139
|
" — this looks like a Cloudflare Access challenge; set "
|
|
@@ -145,14 +143,11 @@ def _json_or_raise(resp: httpx.Response) -> dict:
|
|
|
145
143
|
)
|
|
146
144
|
raise EverestError(
|
|
147
145
|
resp.status_code,
|
|
148
|
-
f"expected a JSON response but received non-JSON content{hint}: "
|
|
149
|
-
f"{body[:200]}",
|
|
146
|
+
f"expected a JSON response but received non-JSON content{hint}: {body[:200]}",
|
|
150
147
|
)
|
|
151
148
|
|
|
152
149
|
|
|
153
|
-
def _check_prediction_range(
|
|
154
|
-
values: dict[str, float], *, lo: float = 0.0, hi: float = 1.0
|
|
155
|
-
) -> None:
|
|
150
|
+
def _check_prediction_range(values: dict[str, float], *, lo: float = 0.0, hi: float = 1.0) -> None:
|
|
156
151
|
"""Raise ``ValueError`` if any prediction is non-finite or outside ``[lo, hi]``.
|
|
157
152
|
|
|
158
153
|
Mirrors the server-side futures bound so a bad submission fails fast
|
|
@@ -173,8 +168,7 @@ def _check_prediction_range(
|
|
|
173
168
|
preview = ", ".join(bad[:5])
|
|
174
169
|
more = f" (+{len(bad) - 5} more)" if len(bad) > 5 else ""
|
|
175
170
|
raise ValueError(
|
|
176
|
-
f"{len(bad)} prediction(s) must be finite and within "
|
|
177
|
-
f"[{lo}, {hi}]: {preview}{more}"
|
|
171
|
+
f"{len(bad)} prediction(s) must be finite and within [{lo}, {hi}]: {preview}{more}"
|
|
178
172
|
)
|
|
179
173
|
|
|
180
174
|
|
|
@@ -187,11 +181,7 @@ def _resolve_default_base_url() -> str:
|
|
|
187
181
|
(CloudFront→S3) that serves no ``/api`` — defaulting there returns an S3
|
|
188
182
|
``AccessDenied`` for every call, so it is deliberately not the fallback.
|
|
189
183
|
"""
|
|
190
|
-
return (
|
|
191
|
-
os.getenv("EIQ_BASE_URL")
|
|
192
|
-
or os.getenv("EVEREST_API_URL")
|
|
193
|
-
or "https://app.everesteer.ai"
|
|
194
|
-
)
|
|
184
|
+
return os.getenv("EIQ_BASE_URL") or os.getenv("EVEREST_API_URL") or "https://app.everesteer.ai"
|
|
195
185
|
|
|
196
186
|
|
|
197
187
|
class EverestAPI:
|
|
@@ -326,29 +316,42 @@ class EverestAPI:
|
|
|
326
316
|
|
|
327
317
|
def submit_predictions_file(
|
|
328
318
|
self,
|
|
329
|
-
|
|
330
|
-
file_path: str,
|
|
319
|
+
model_id: str | None = None,
|
|
320
|
+
file_path: str | None = None,
|
|
331
321
|
tournament: str | None = None,
|
|
322
|
+
*,
|
|
323
|
+
model_name: str | None = None,
|
|
332
324
|
) -> dict:
|
|
333
325
|
"""POST /api/v1/predictions/upload — submit predictions as a CSV or Parquet file.
|
|
334
326
|
|
|
335
327
|
Parameters
|
|
336
328
|
----------
|
|
337
|
-
|
|
338
|
-
Model identifier.
|
|
329
|
+
model_id : str
|
|
330
|
+
Model identifier. (``model_name`` is a deprecated alias.)
|
|
339
331
|
file_path : str
|
|
340
332
|
Path to a ``.csv`` or ``.parquet`` file with columns ``ticker`` (str)
|
|
341
333
|
and ``score`` (float in [-1, 1]).
|
|
342
334
|
tournament : str, optional
|
|
343
335
|
Tournament identifier. Defaults to the client's tournament.
|
|
344
336
|
"""
|
|
337
|
+
if model_id is None:
|
|
338
|
+
if model_name is None:
|
|
339
|
+
raise TypeError("submit_predictions_file() missing required argument: 'model_id'")
|
|
340
|
+
import warnings
|
|
341
|
+
|
|
342
|
+
warnings.warn(
|
|
343
|
+
"model_name= is deprecated; use model_id=", DeprecationWarning, stacklevel=2
|
|
344
|
+
)
|
|
345
|
+
model_id = model_name
|
|
346
|
+
if file_path is None:
|
|
347
|
+
raise TypeError("submit_predictions_file() missing required argument: 'file_path'")
|
|
345
348
|
tourn = tournament or self.tournament
|
|
346
349
|
with open(file_path, "rb") as f:
|
|
347
350
|
fname = file_path.rsplit("/", 1)[-1].rsplit("\\", 1)[-1]
|
|
348
351
|
resp = self._client.post(
|
|
349
352
|
"/api/v1/predictions/upload",
|
|
350
353
|
files={"file": (fname, f, "application/octet-stream")},
|
|
351
|
-
params={"model_id":
|
|
354
|
+
params={"model_id": model_id, "tournament": tourn},
|
|
352
355
|
)
|
|
353
356
|
return _json_or_raise(resp)
|
|
354
357
|
|
|
@@ -363,10 +366,14 @@ class EverestAPI:
|
|
|
363
366
|
poll_interval: float = 2.0,
|
|
364
367
|
timeout: float = 600.0,
|
|
365
368
|
) -> dict:
|
|
366
|
-
"""POST /api/v1/diagnostics/upload (multipart) — score
|
|
369
|
+
"""POST /api/v1/diagnostics/upload (multipart) — score out-of-sample predictions.
|
|
367
370
|
|
|
368
371
|
``predictions`` is a pandas DataFrame (``id`` + ``prediction`` columns) or a
|
|
369
|
-
path to a ``.parquet`` / ``.csv`` file
|
|
372
|
+
path to a ``.parquet`` / ``.csv`` file, generated on the blind eiq_live_2026
|
|
373
|
+
set (``download_dataset(split="live")``). The platform scores it server-side
|
|
374
|
+
against the held-out labeled eiq_live_2026 answers — those answers are never
|
|
375
|
+
downloadable. Tune and self-score offline on the labeled validation set first
|
|
376
|
+
(see :mod:`everestapi.scoring`). Returns the 202 accept dict; with
|
|
370
377
|
``wait=True`` polls ``runs/{upload_id}`` until ``done`` (returns the run) and
|
|
371
378
|
raises :class:`EverestError` on ``failed`` or timeout. Display-only; results
|
|
372
379
|
also surface in the website Validation Diagnostics rail.
|
|
@@ -491,9 +498,7 @@ class EverestAPI:
|
|
|
491
498
|
)
|
|
492
499
|
return "\n".join(lines)
|
|
493
500
|
|
|
494
|
-
def get_validation_panel(
|
|
495
|
-
self, model_id: str, days: int = 365, source: str = "auto"
|
|
496
|
-
) -> dict:
|
|
501
|
+
def get_validation_panel(self, model_id: str, days: int = 365, source: str = "auto") -> dict:
|
|
497
502
|
"""GET /api/v1/models/{model_id}/diagnostics/validation — full validation metrics.
|
|
498
503
|
|
|
499
504
|
``source="auto"`` (default) returns the latest completed validation-diagnostics
|
|
@@ -551,9 +556,19 @@ class EverestAPI:
|
|
|
551
556
|
output_path,
|
|
552
557
|
)
|
|
553
558
|
|
|
554
|
-
def get_scores(self, model_id: str, days: int = 30) -> dict:
|
|
555
|
-
"""GET /api/v1/scores — scoring results for a model.
|
|
556
|
-
|
|
559
|
+
def get_scores(self, model_id: str, days: int = 30, response_format: str = "concise") -> dict:
|
|
560
|
+
"""GET /api/v1/scores — scoring results for a model.
|
|
561
|
+
|
|
562
|
+
``response_format="detailed"`` asks the platform to add a plain-language
|
|
563
|
+
``interpretation`` field that reads the model's own latest numbers and
|
|
564
|
+
state and explains how to improve. ``"concise"`` (default) leaves the
|
|
565
|
+
response unchanged.
|
|
566
|
+
"""
|
|
567
|
+
params = {
|
|
568
|
+
"model_id": model_id,
|
|
569
|
+
"days": str(days),
|
|
570
|
+
"response_format": response_format,
|
|
571
|
+
}
|
|
557
572
|
return self._request("GET", "/api/v1/scores", params=params)
|
|
558
573
|
|
|
559
574
|
def get_leaderboard(self, period: str = "30d") -> dict:
|
|
@@ -579,11 +594,18 @@ class EverestAPI:
|
|
|
579
594
|
404 the current version is resolved and the download retried once.
|
|
580
595
|
|
|
581
596
|
Splits: ``train`` / ``validation`` (features + targets), ``live``
|
|
582
|
-
(
|
|
597
|
+
(features only, no targets). In hackathon mode, ``validation`` is the
|
|
598
|
+
LABELED practice set — features + all ``target_*`` columns — that you tune
|
|
599
|
+
and self-score on offline (see :mod:`everestapi.scoring`), and ``live`` is
|
|
600
|
+
the BLIND eiq_live_2026 out-of-sample set (columns exactly ``exped``,
|
|
601
|
+
``exped_date``, ``instrument``, ``id`` — no targets) that you predict on and
|
|
602
|
+
submit. The labeled eiq_live_2026 answers are held out server-side and never
|
|
603
|
+
downloadable.
|
|
583
604
|
|
|
584
605
|
Futures is served by the futures endpoint (``version`` is a no-op):
|
|
585
|
-
it returns the real bregen tree to full-scope keys and the
|
|
586
|
-
|
|
606
|
+
it returns the real bregen tree to full-scope keys and the hackathon tree
|
|
607
|
+
(labeled ``validation`` practice set + blind ``live`` eiq_live_2026 scored
|
|
608
|
+
set) to hackathon-scoped keys.
|
|
587
609
|
"""
|
|
588
610
|
if output_path is None:
|
|
589
611
|
output_path = f"{universe}_{split}.parquet"
|
|
@@ -632,9 +654,15 @@ class EverestAPI:
|
|
|
632
654
|
params["date"] = date
|
|
633
655
|
return self._request("GET", f"/api/v1/diagnostics/{model_id}", params=params)
|
|
634
656
|
|
|
635
|
-
def get_round_diagnostics(self, model_id: str) -> dict:
|
|
636
|
-
"""GET /api/v1/diagnostics/{model_id}/rounds — per-round scoring breakdown.
|
|
637
|
-
|
|
657
|
+
def get_round_diagnostics(self, model_id: str, response_format: str = "concise") -> dict:
|
|
658
|
+
"""GET /api/v1/diagnostics/{model_id}/rounds — per-round scoring breakdown.
|
|
659
|
+
|
|
660
|
+
``response_format="detailed"`` asks the platform to add a plain-language
|
|
661
|
+
``interpretation`` field grounded in the model's own latest numbers and
|
|
662
|
+
state. ``"concise"`` (default) leaves the response unchanged.
|
|
663
|
+
"""
|
|
664
|
+
params = {"response_format": response_format}
|
|
665
|
+
return self._request("GET", f"/api/v1/diagnostics/{model_id}/rounds", params=params)
|
|
638
666
|
|
|
639
667
|
# -- futures tournament -----------------------------------------------
|
|
640
668
|
|
|
@@ -900,8 +928,8 @@ class EverestAPI:
|
|
|
900
928
|
|
|
901
929
|
# -- rounds -----------------------------------------------------------
|
|
902
930
|
|
|
903
|
-
def get_rounds(self, tournament: str = "
|
|
904
|
-
"""GET /api/v1/rounds — list tournament rounds."""
|
|
931
|
+
def get_rounds(self, tournament: str = "futures", limit: int = 25) -> dict:
|
|
932
|
+
"""GET /api/v1/rounds — list tournament rounds (defaults to 'futures'; equities is unlaunched)."""
|
|
905
933
|
return self._request(
|
|
906
934
|
"GET",
|
|
907
935
|
"/api/v1/rounds",
|
|
@@ -911,8 +939,14 @@ class EverestAPI:
|
|
|
911
939
|
},
|
|
912
940
|
)
|
|
913
941
|
|
|
914
|
-
def get_current_round(self, tournament: str = "
|
|
915
|
-
"""GET /api/v1/rounds/current — current active round.
|
|
942
|
+
def get_current_round(self, tournament: str = "futures") -> dict:
|
|
943
|
+
"""GET /api/v1/rounds/current — current active round (defaults to 'futures'; equities is unlaunched).
|
|
944
|
+
|
|
945
|
+
With a hackathon-scoped key there is no live round — the response is a
|
|
946
|
+
diagnostics-mode payload (mode='diagnostics_hackathon') directing you to
|
|
947
|
+
tune offline on the labeled validation set, then predict on the blind
|
|
948
|
+
eiq_live_2026 set and submit_validation_diagnostics.
|
|
949
|
+
"""
|
|
916
950
|
return self._request(
|
|
917
951
|
"GET",
|
|
918
952
|
"/api/v1/rounds/current",
|
|
@@ -925,6 +959,38 @@ class EverestAPI:
|
|
|
925
959
|
"""GET /api/v1/schedule — round schedule for both tournaments."""
|
|
926
960
|
return self._request("GET", "/api/v1/schedule")
|
|
927
961
|
|
|
962
|
+
def get_started(self) -> dict:
|
|
963
|
+
"""GET /api/v1/get_started — mode-aware orientation.
|
|
964
|
+
|
|
965
|
+
Returns what to do next given this key's scope: the display-only
|
|
966
|
+
diagnostics-hackathon loop (tune and self-score offline on the labeled
|
|
967
|
+
validation set, then predict on the blind eiq_live_2026 set and submit —
|
|
968
|
+
ranked on out-of-sample 2026 CORR on target_everest_20), or the live
|
|
969
|
+
futures tournament flow.
|
|
970
|
+
"""
|
|
971
|
+
return self._request("GET", "/api/v1/get_started")
|
|
972
|
+
|
|
973
|
+
def get_status(self) -> dict:
|
|
974
|
+
"""GET /api/v1/status — state-aware orientation ("where am I").
|
|
975
|
+
|
|
976
|
+
Your whole situational picture in one call: the models you own, each
|
|
977
|
+
one's latest submission and score state, the open round's clock, your
|
|
978
|
+
stake, and a ``next_actions`` list — instead of stitching together
|
|
979
|
+
get_models + get_scores + get_current_round. Display-only.
|
|
980
|
+
"""
|
|
981
|
+
return self._request("GET", "/api/v1/status")
|
|
982
|
+
|
|
983
|
+
def get_capabilities(self) -> dict:
|
|
984
|
+
"""GET / (site root) with ``Accept: application/json`` — capability index.
|
|
985
|
+
|
|
986
|
+
A small machine-readable map of where everything lives: pointers to
|
|
987
|
+
``llms.txt``, the OpenAPI spec, the MCP endpoint, the API base, the SDK,
|
|
988
|
+
and related discovery URLs. Hit before anything else to learn the
|
|
989
|
+
platform's shape without hardcoding paths. The index sits at the host
|
|
990
|
+
root; a leading-slash path resolves there against the API host base URL.
|
|
991
|
+
"""
|
|
992
|
+
return self._request("GET", "/", headers={"Accept": "application/json"})
|
|
993
|
+
|
|
928
994
|
# -- model uploads ----------------------------------------------------
|
|
929
995
|
|
|
930
996
|
def upload_model(self, model_id: str, file_path: str) -> dict:
|