everestapi 0.2.1__tar.gz → 0.2.2__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.1/src/everestapi.egg-info → everestapi-0.2.2}/PKG-INFO +5 -5
- {everestapi-0.2.1 → everestapi-0.2.2}/README.md +1 -1
- {everestapi-0.2.1 → everestapi-0.2.2}/pyproject.toml +3 -3
- {everestapi-0.2.1 → everestapi-0.2.2}/src/everestapi/__init__.py +1 -1
- {everestapi-0.2.1 → everestapi-0.2.2}/src/everestapi/cli.py +14 -7
- {everestapi-0.2.1 → everestapi-0.2.2}/src/everestapi/client.py +83 -42
- {everestapi-0.2.1 → everestapi-0.2.2}/src/everestapi/mcp/server.py +108 -32
- {everestapi-0.2.1 → everestapi-0.2.2/src/everestapi.egg-info}/PKG-INFO +5 -5
- {everestapi-0.2.1 → everestapi-0.2.2}/tests/test_client.py +3 -2
- {everestapi-0.2.1 → everestapi-0.2.2}/tests/test_mcp_and_models.py +30 -11
- {everestapi-0.2.1 → everestapi-0.2.2}/LICENSE +0 -0
- {everestapi-0.2.1 → everestapi-0.2.2}/setup.cfg +0 -0
- {everestapi-0.2.1 → everestapi-0.2.2}/src/everestapi/__main__.py +0 -0
- {everestapi-0.2.1 → everestapi-0.2.2}/src/everestapi/mcp/__init__.py +0 -0
- {everestapi-0.2.1 → everestapi-0.2.2}/src/everestapi/mcp/__main__.py +0 -0
- {everestapi-0.2.1 → everestapi-0.2.2}/src/everestapi/types.py +0 -0
- {everestapi-0.2.1 → everestapi-0.2.2}/src/everestapi.egg-info/SOURCES.txt +0 -0
- {everestapi-0.2.1 → everestapi-0.2.2}/src/everestapi.egg-info/dependency_links.txt +0 -0
- {everestapi-0.2.1 → everestapi-0.2.2}/src/everestapi.egg-info/entry_points.txt +0 -0
- {everestapi-0.2.1 → everestapi-0.2.2}/src/everestapi.egg-info/requires.txt +0 -0
- {everestapi-0.2.1 → everestapi-0.2.2}/src/everestapi.egg-info/top_level.txt +0 -0
- {everestapi-0.2.1 → everestapi-0.2.2}/tests/test_cli.py +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: everestapi
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Python SDK for the EverestQuant prediction tournament platform
|
|
5
|
-
Author-email: EverestQuant <
|
|
5
|
+
Author-email: EverestQuant <support@everesteer.ai>
|
|
6
6
|
License-Expression: MIT
|
|
7
|
-
Project-URL: Homepage, https://
|
|
8
|
-
Project-URL: Documentation, https://docs.
|
|
7
|
+
Project-URL: Homepage, https://everesteer.ai
|
|
8
|
+
Project-URL: Documentation, https://docs.everesteer.ai
|
|
9
9
|
Project-URL: Repository, https://github.com/everestquant/everestapi-public
|
|
10
10
|
Keywords: quant,tournament,prediction,staking,machine-learning
|
|
11
11
|
Classifier: Development Status :: 4 - Beta
|
|
@@ -30,7 +30,7 @@ Dynamic: license-file
|
|
|
30
30
|
|
|
31
31
|
# everestapi
|
|
32
32
|
|
|
33
|
-
Python SDK for the [EverestQuant](https://
|
|
33
|
+
Python SDK for the [EverestQuant](https://everesteer.ai) prediction tournament platform.
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
36
|
pip install everestapi
|
|
@@ -9,7 +9,7 @@ description = "Python SDK for the EverestQuant prediction tournament platform"
|
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
11
11
|
requires-python = ">=3.10"
|
|
12
|
-
authors = [{ name = "EverestQuant", email = "
|
|
12
|
+
authors = [{ name = "EverestQuant", email = "support@everesteer.ai" }]
|
|
13
13
|
keywords = ["quant", "tournament", "prediction", "staking", "machine-learning"]
|
|
14
14
|
classifiers = [
|
|
15
15
|
"Development Status :: 4 - Beta",
|
|
@@ -36,8 +36,8 @@ mcp = ["mcp>=1.0"]
|
|
|
36
36
|
everestapi = "everestapi.cli:cli"
|
|
37
37
|
|
|
38
38
|
[project.urls]
|
|
39
|
-
Homepage = "https://
|
|
40
|
-
Documentation = "https://docs.
|
|
39
|
+
Homepage = "https://everesteer.ai"
|
|
40
|
+
Documentation = "https://docs.everesteer.ai"
|
|
41
41
|
Repository = "https://github.com/everestquant/everestapi-public"
|
|
42
42
|
|
|
43
43
|
[tool.setuptools.packages.find]
|
|
@@ -69,11 +69,15 @@ def download_data(universe: str, split: str, output: str | None, api_key: str |
|
|
|
69
69
|
@click.option("--version", "-v", default="v0")
|
|
70
70
|
@click.option("--output", "-o", default=None)
|
|
71
71
|
@click.option("--api-key", envvar=["EIQ_API_KEY", "EVEREST_API_KEY"], default=None)
|
|
72
|
-
def download_benchmark(
|
|
72
|
+
def download_benchmark(
|
|
73
|
+
universe: str, split: str, version: str, output: str | None, api_key: str | None
|
|
74
|
+
) -> None:
|
|
73
75
|
"""Download benchmark model predictions."""
|
|
74
76
|
api = _get_api(api_key)
|
|
75
77
|
try:
|
|
76
|
-
path = api.download_benchmark(
|
|
78
|
+
path = api.download_benchmark(
|
|
79
|
+
universe=universe, split=split, version=version, output_path=output
|
|
80
|
+
)
|
|
77
81
|
click.echo(f"Downloaded to {path}")
|
|
78
82
|
except EverestError as e:
|
|
79
83
|
click.echo(f"Error: {e}", err=True)
|
|
@@ -83,7 +87,9 @@ def download_benchmark(universe: str, split: str, version: str, output: str | No
|
|
|
83
87
|
@cli.command()
|
|
84
88
|
@click.option("--model", "-m", required=True, help="Model identifier")
|
|
85
89
|
@click.option(
|
|
86
|
-
"--file",
|
|
90
|
+
"--file",
|
|
91
|
+
"-f",
|
|
92
|
+
"file_path",
|
|
87
93
|
required=True,
|
|
88
94
|
help="Predictions file (.parquet or .csv). Parquet recommended.",
|
|
89
95
|
)
|
|
@@ -102,7 +108,9 @@ def submit(model: str, file_path: str, tournament: str, api_key: str | None) ->
|
|
|
102
108
|
try:
|
|
103
109
|
if ext == "parquet":
|
|
104
110
|
result = api.submit_predictions_file(
|
|
105
|
-
model_name=model,
|
|
111
|
+
model_name=model,
|
|
112
|
+
file_path=file_path,
|
|
113
|
+
tournament=tournament,
|
|
106
114
|
)
|
|
107
115
|
elif ext == "csv":
|
|
108
116
|
import csv as _csv
|
|
@@ -115,8 +123,7 @@ def submit(model: str, file_path: str, tournament: str, api_key: str | None) ->
|
|
|
115
123
|
result = api.submit_futures_predictions(model_id=model, predictions=preds)
|
|
116
124
|
else:
|
|
117
125
|
preds_list = [
|
|
118
|
-
{"ticker": row["ticker"], "score": float(row["score"])}
|
|
119
|
-
for row in rows
|
|
126
|
+
{"ticker": row["ticker"], "score": float(row["score"])} for row in rows
|
|
120
127
|
]
|
|
121
128
|
result = api.submit_predictions(model_id=model, predictions=preds_list)
|
|
122
129
|
else:
|
|
@@ -227,7 +234,7 @@ def register(name: str, email: str) -> None:
|
|
|
227
234
|
click.echo("Registration successful!")
|
|
228
235
|
click.echo(f" Agent ID: {result['agent_id']}")
|
|
229
236
|
click.echo(f" API Key: {result['api_key']} (save this — shown once)")
|
|
230
|
-
click.echo(
|
|
237
|
+
click.echo("\nSet your API key:")
|
|
231
238
|
click.echo(f" export EIQ_API_KEY={result['api_key']}")
|
|
232
239
|
except EverestError as e:
|
|
233
240
|
click.echo(f"Error: {e}", err=True)
|
|
@@ -86,6 +86,7 @@ def _open_no_symlink(path: str):
|
|
|
86
86
|
st = None
|
|
87
87
|
if st is not None:
|
|
88
88
|
import stat as _stat
|
|
89
|
+
|
|
89
90
|
if _stat.S_ISLNK(st.st_mode):
|
|
90
91
|
raise OSError(
|
|
91
92
|
f"Refusing to write to symlink at {path!r} (symlink-following blocked)",
|
|
@@ -110,7 +111,7 @@ class EverestAPI:
|
|
|
110
111
|
api_key : str, optional
|
|
111
112
|
EverestQuant API key. Falls back to ``EIQ_API_KEY`` (or legacy ``EVEREST_API_KEY``) env var.
|
|
112
113
|
base_url : str, optional
|
|
113
|
-
Base URL for the API. Defaults to ``https://
|
|
114
|
+
Base URL for the API. Defaults to ``https://everesteer.ai`` (apex
|
|
114
115
|
domain), overridable via ``EVEREST_API_URL`` env var.
|
|
115
116
|
timeout : float
|
|
116
117
|
HTTP timeout in seconds.
|
|
@@ -124,7 +125,7 @@ class EverestAPI:
|
|
|
124
125
|
explicitly supplied.
|
|
125
126
|
"""
|
|
126
127
|
|
|
127
|
-
DEFAULT_BASE_URL = os.getenv("EVEREST_API_URL", "https://
|
|
128
|
+
DEFAULT_BASE_URL = os.getenv("EVEREST_API_URL", "https://everesteer.ai")
|
|
128
129
|
|
|
129
130
|
def __init__(
|
|
130
131
|
self,
|
|
@@ -307,7 +308,9 @@ class EverestAPI:
|
|
|
307
308
|
return self.get_validation_panel(model_id=model_id, days=days)
|
|
308
309
|
|
|
309
310
|
def get_model_per_exped_breakdown(
|
|
310
|
-
self,
|
|
311
|
+
self,
|
|
312
|
+
model_id: str,
|
|
313
|
+
days: int = 3650,
|
|
311
314
|
) -> list[float]:
|
|
312
315
|
"""Return the per-exped CORR series for a model (oldest→newest).
|
|
313
316
|
|
|
@@ -317,7 +320,10 @@ class EverestAPI:
|
|
|
317
320
|
return list(resp.get("per_exped_corr", []))
|
|
318
321
|
|
|
319
322
|
def get_job_output(
|
|
320
|
-
self,
|
|
323
|
+
self,
|
|
324
|
+
job_id: str,
|
|
325
|
+
filename: str,
|
|
326
|
+
output_path: str | None = None,
|
|
321
327
|
) -> str:
|
|
322
328
|
"""Download an output file from a completed compute job.
|
|
323
329
|
|
|
@@ -332,7 +338,8 @@ class EverestAPI:
|
|
|
332
338
|
# like "../etc/passwd" can't escape cwd.
|
|
333
339
|
output_path = _safe_output_path(filename, server_supplied=True)
|
|
334
340
|
return self._download(
|
|
335
|
-
f"/api/v1/compute/jobs/{job_id}/output/{filename}",
|
|
341
|
+
f"/api/v1/compute/jobs/{job_id}/output/{filename}",
|
|
342
|
+
output_path,
|
|
336
343
|
)
|
|
337
344
|
|
|
338
345
|
def get_scores(self, model_id: str, days: int = 30) -> dict:
|
|
@@ -443,8 +450,7 @@ class EverestAPI:
|
|
|
443
450
|
"model_id": model_id,
|
|
444
451
|
"exped": exped or "current",
|
|
445
452
|
"predictions": [
|
|
446
|
-
{"instrument_id": iid, "prediction": pred}
|
|
447
|
-
for iid, pred in predictions.items()
|
|
453
|
+
{"instrument_id": iid, "prediction": pred} for iid, pred in predictions.items()
|
|
448
454
|
],
|
|
449
455
|
}
|
|
450
456
|
return self._request("POST", "/api/v1/futures/submit/v2", json=body)
|
|
@@ -460,10 +466,7 @@ class EverestAPI:
|
|
|
460
466
|
.. deprecated::
|
|
461
467
|
Use :meth:`submit_futures_predictions` (v2) instead.
|
|
462
468
|
"""
|
|
463
|
-
preds_list = [
|
|
464
|
-
{"instrument_id": k, "targets": v}
|
|
465
|
-
for k, v in sorted(predictions.items())
|
|
466
|
-
]
|
|
469
|
+
preds_list = [{"instrument_id": k, "targets": v} for k, v in sorted(predictions.items())]
|
|
467
470
|
body = {"model_id": model_id, "exped": exped, "predictions": preds_list}
|
|
468
471
|
return self._request("POST", "/api/v1/futures/submit", json=body)
|
|
469
472
|
|
|
@@ -511,7 +514,8 @@ class EverestAPI:
|
|
|
511
514
|
if version in (None, "latest", "current"):
|
|
512
515
|
version = self._current_dataset_version(universe) or "v0"
|
|
513
516
|
return self._download(
|
|
514
|
-
f"/api/v1/data/{version}/benchmark/{universe}/{split}",
|
|
517
|
+
f"/api/v1/data/{version}/benchmark/{universe}/{split}",
|
|
518
|
+
output_path,
|
|
515
519
|
)
|
|
516
520
|
|
|
517
521
|
def download_ai_model(
|
|
@@ -524,7 +528,8 @@ class EverestAPI:
|
|
|
524
528
|
if output_path is None:
|
|
525
529
|
output_path = f"ai_model_{universe}.parquet"
|
|
526
530
|
return self._download(
|
|
527
|
-
f"/api/v1/data/{version}/ai-model/{universe}",
|
|
531
|
+
f"/api/v1/data/{version}/ai-model/{universe}",
|
|
532
|
+
output_path,
|
|
528
533
|
)
|
|
529
534
|
|
|
530
535
|
# -- compute ----------------------------------------------------------
|
|
@@ -539,7 +544,9 @@ class EverestAPI:
|
|
|
539
544
|
) -> dict:
|
|
540
545
|
"""Submit Tier 1 serverless training job."""
|
|
541
546
|
body: dict = {
|
|
542
|
-
"model": model,
|
|
547
|
+
"model": model,
|
|
548
|
+
"features": features,
|
|
549
|
+
"target": target,
|
|
543
550
|
"universe": universe,
|
|
544
551
|
}
|
|
545
552
|
if params:
|
|
@@ -599,6 +606,7 @@ class EverestAPI:
|
|
|
599
606
|
def wait_for_job(self, job_id: str, timeout: int = 3600) -> dict:
|
|
600
607
|
"""Block until job completes. Exponential backoff polling."""
|
|
601
608
|
import time
|
|
609
|
+
|
|
602
610
|
delay = 2.0
|
|
603
611
|
elapsed = 0.0
|
|
604
612
|
while elapsed < timeout:
|
|
@@ -676,15 +684,24 @@ class EverestAPI:
|
|
|
676
684
|
|
|
677
685
|
def get_rounds(self, tournament: str = "equities", limit: int = 25) -> dict:
|
|
678
686
|
"""GET /api/v1/rounds — list tournament rounds."""
|
|
679
|
-
return self._request(
|
|
680
|
-
"
|
|
681
|
-
|
|
687
|
+
return self._request(
|
|
688
|
+
"GET",
|
|
689
|
+
"/api/v1/rounds",
|
|
690
|
+
params={
|
|
691
|
+
"tournament": tournament,
|
|
692
|
+
"limit": str(limit),
|
|
693
|
+
},
|
|
694
|
+
)
|
|
682
695
|
|
|
683
696
|
def get_current_round(self, tournament: str = "equities") -> dict:
|
|
684
697
|
"""GET /api/v1/rounds/current — current active round."""
|
|
685
|
-
return self._request(
|
|
686
|
-
"
|
|
687
|
-
|
|
698
|
+
return self._request(
|
|
699
|
+
"GET",
|
|
700
|
+
"/api/v1/rounds/current",
|
|
701
|
+
params={
|
|
702
|
+
"tournament": tournament,
|
|
703
|
+
},
|
|
704
|
+
)
|
|
688
705
|
|
|
689
706
|
def get_schedule(self) -> dict:
|
|
690
707
|
"""GET /api/v1/schedule — round schedule for both tournaments."""
|
|
@@ -715,9 +732,14 @@ class EverestAPI:
|
|
|
715
732
|
|
|
716
733
|
def set_multipliers(self, model_id: str, corr_mult: float, aimc_mult: float) -> dict:
|
|
717
734
|
"""PATCH /api/v1/models/{model_id}/multipliers — set payout multipliers."""
|
|
718
|
-
return self._request(
|
|
719
|
-
"
|
|
720
|
-
|
|
735
|
+
return self._request(
|
|
736
|
+
"PATCH",
|
|
737
|
+
f"/api/v1/models/{model_id}/multipliers",
|
|
738
|
+
json={
|
|
739
|
+
"corr_multiplier": corr_mult,
|
|
740
|
+
"aimc_multiplier": aimc_mult,
|
|
741
|
+
},
|
|
742
|
+
)
|
|
721
743
|
|
|
722
744
|
def get_multipliers(self, model_id: str) -> dict:
|
|
723
745
|
"""GET /api/v1/models/{model_id}/multipliers"""
|
|
@@ -741,17 +763,26 @@ class EverestAPI:
|
|
|
741
763
|
|
|
742
764
|
def stake(self, model_id: str, amount_usdc: float, wallet_address: str) -> dict:
|
|
743
765
|
"""Stake USDC on a model."""
|
|
744
|
-
return self._request(
|
|
745
|
-
"
|
|
746
|
-
"
|
|
747
|
-
|
|
748
|
-
|
|
766
|
+
return self._request(
|
|
767
|
+
"POST",
|
|
768
|
+
"/api/v1/staking/stake",
|
|
769
|
+
json={
|
|
770
|
+
"model_id": model_id,
|
|
771
|
+
"amount_usdc": amount_usdc,
|
|
772
|
+
"wallet_address": wallet_address,
|
|
773
|
+
},
|
|
774
|
+
)
|
|
749
775
|
|
|
750
776
|
def confirm_stake(self, stake_id: str, txn_hash: str) -> dict:
|
|
751
777
|
"""Confirm a stake with on-chain transaction hash."""
|
|
752
|
-
return self._request(
|
|
753
|
-
"
|
|
754
|
-
|
|
778
|
+
return self._request(
|
|
779
|
+
"POST",
|
|
780
|
+
"/api/v1/staking/confirm",
|
|
781
|
+
json={
|
|
782
|
+
"stake_id": stake_id,
|
|
783
|
+
"txn_hash": txn_hash,
|
|
784
|
+
},
|
|
785
|
+
)
|
|
755
786
|
|
|
756
787
|
def unstake(self, stake_id: str) -> dict:
|
|
757
788
|
"""Unstake USDC from a model."""
|
|
@@ -763,9 +794,14 @@ class EverestAPI:
|
|
|
763
794
|
|
|
764
795
|
def claim_payout(self, model_id: str, round_id: str) -> dict:
|
|
765
796
|
"""Claim resolved payout for a model and round."""
|
|
766
|
-
return self._request(
|
|
767
|
-
"
|
|
768
|
-
|
|
797
|
+
return self._request(
|
|
798
|
+
"POST",
|
|
799
|
+
"/api/v1/staking/claim",
|
|
800
|
+
json={
|
|
801
|
+
"model_id": model_id,
|
|
802
|
+
"round_id": round_id,
|
|
803
|
+
},
|
|
804
|
+
)
|
|
769
805
|
|
|
770
806
|
def get_staking_history(self, model_id: str) -> dict:
|
|
771
807
|
"""Get stake/unstake/claim history."""
|
|
@@ -787,9 +823,14 @@ class EverestAPI:
|
|
|
787
823
|
|
|
788
824
|
def withdraw_usdc(self, to_address: str, amount_usdc: float) -> dict:
|
|
789
825
|
"""Withdraw USDC to external wallet."""
|
|
790
|
-
return self._request(
|
|
791
|
-
"
|
|
792
|
-
|
|
826
|
+
return self._request(
|
|
827
|
+
"POST",
|
|
828
|
+
"/api/v1/staking/withdraw",
|
|
829
|
+
json={
|
|
830
|
+
"to_address": to_address,
|
|
831
|
+
"amount_usdc": amount_usdc,
|
|
832
|
+
},
|
|
833
|
+
)
|
|
793
834
|
|
|
794
835
|
def get_forwarder_balance(self) -> dict:
|
|
795
836
|
"""GET /api/v1/staking/forwarder-balance"""
|
|
@@ -836,13 +877,13 @@ class EverestAPI:
|
|
|
836
877
|
|
|
837
878
|
@staticmethod
|
|
838
879
|
def evaluate(
|
|
839
|
-
predictions:
|
|
840
|
-
validation_data:
|
|
880
|
+
predictions: np.ndarray | pd.Series,
|
|
881
|
+
validation_data: pd.DataFrame,
|
|
841
882
|
target: str = "target_everest_20",
|
|
842
883
|
) -> dict:
|
|
843
884
|
"""Evaluate predictions against validation set locally."""
|
|
844
885
|
import numpy as np
|
|
845
|
-
from scipy.stats import
|
|
886
|
+
from scipy.stats import rankdata, spearmanr
|
|
846
887
|
|
|
847
888
|
preds = np.asarray(predictions)
|
|
848
889
|
targets = validation_data[target].values
|
|
@@ -864,8 +905,8 @@ class EverestAPI:
|
|
|
864
905
|
pd_dev = pred_ranks - pm
|
|
865
906
|
td_dev = target_ranks - tm
|
|
866
907
|
cov = np.dot(weights, pd_dev * td_dev) / w_sum
|
|
867
|
-
pvar = np.dot(weights, pd_dev
|
|
868
|
-
tvar = np.dot(weights, td_dev
|
|
908
|
+
pvar = np.dot(weights, pd_dev**2) / w_sum
|
|
909
|
+
tvar = np.dot(weights, td_dev**2) / w_sum
|
|
869
910
|
denom = np.sqrt(pvar * tvar)
|
|
870
911
|
result["weighted_corr"] = round(float(cov / denom), 6) if denom > 0 else 0.0
|
|
871
912
|
else:
|
|
@@ -11,7 +11,7 @@ Run::
|
|
|
11
11
|
|
|
12
12
|
Requires environment variables:
|
|
13
13
|
EIQ_API_KEY — your EverestQuant API key (also accepts legacy EVEREST_API_KEY)
|
|
14
|
-
EIQ_BASE_URL — API base URL (default: https://
|
|
14
|
+
EIQ_BASE_URL — API base URL (default: https://everesteer.ai)
|
|
15
15
|
CF_ACCESS_CLIENT_ID / CF_ACCESS_CLIENT_SECRET — Cloudflare Access service
|
|
16
16
|
token, required only when the host sits behind CF Access (e.g. staging)
|
|
17
17
|
"""
|
|
@@ -61,7 +61,7 @@ def _get_client():
|
|
|
61
61
|
base_url = (
|
|
62
62
|
os.environ.get("EIQ_BASE_URL")
|
|
63
63
|
or os.environ.get("EVEREST_API_URL")
|
|
64
|
-
or "https://
|
|
64
|
+
or "https://everesteer.ai"
|
|
65
65
|
)
|
|
66
66
|
if not api_key:
|
|
67
67
|
raise RuntimeError("EIQ_API_KEY environment variable is required")
|
|
@@ -189,7 +189,11 @@ TOOLS = [
|
|
|
189
189
|
"inputSchema": {
|
|
190
190
|
"type": "object",
|
|
191
191
|
"properties": {
|
|
192
|
-
"version": {
|
|
192
|
+
"version": {
|
|
193
|
+
"type": "string",
|
|
194
|
+
"description": "Dataset version (default: bregen)",
|
|
195
|
+
"default": "bregen",
|
|
196
|
+
},
|
|
193
197
|
},
|
|
194
198
|
"required": [],
|
|
195
199
|
},
|
|
@@ -201,7 +205,10 @@ TOOLS = [
|
|
|
201
205
|
"type": "object",
|
|
202
206
|
"properties": {
|
|
203
207
|
"universe": {"type": "string", "description": "Universe: 'futures' or 'equities'"},
|
|
204
|
-
"split": {
|
|
208
|
+
"split": {
|
|
209
|
+
"type": "string",
|
|
210
|
+
"description": "Split: 'train', 'validation', or 'live'",
|
|
211
|
+
},
|
|
205
212
|
"output_path": {"type": "string", "description": "Local path to save (optional)"},
|
|
206
213
|
},
|
|
207
214
|
"required": ["universe", "split"],
|
|
@@ -214,7 +221,11 @@ TOOLS = [
|
|
|
214
221
|
"type": "object",
|
|
215
222
|
"properties": {
|
|
216
223
|
"universe": {"type": "string", "default": "futures"},
|
|
217
|
-
"split": {
|
|
224
|
+
"split": {
|
|
225
|
+
"type": "string",
|
|
226
|
+
"default": "validation",
|
|
227
|
+
"description": "'validation' or 'live'",
|
|
228
|
+
},
|
|
218
229
|
"output_path": {"type": "string", "description": "Local path (optional)"},
|
|
219
230
|
},
|
|
220
231
|
"required": [],
|
|
@@ -227,8 +238,16 @@ TOOLS = [
|
|
|
227
238
|
"type": "object",
|
|
228
239
|
"properties": {
|
|
229
240
|
"model": {"type": "string", "enum": ["lightgbm", "xgboost", "ridge", "mlp"]},
|
|
230
|
-
"features": {
|
|
231
|
-
|
|
241
|
+
"features": {
|
|
242
|
+
"type": "string",
|
|
243
|
+
"enum": ["small", "medium", "all"],
|
|
244
|
+
"default": "small",
|
|
245
|
+
},
|
|
246
|
+
"target": {
|
|
247
|
+
"type": "string",
|
|
248
|
+
"default": "target_everest_20",
|
|
249
|
+
"description": "Target column. Futures payout uses target_everest_20 (20-day forward return). Multiple target types available. See get_dataset_schema for full list.",
|
|
250
|
+
},
|
|
232
251
|
"universe": {"type": "string", "default": "futures"},
|
|
233
252
|
"params": {"type": "object", "description": "Model hyperparameters (optional)"},
|
|
234
253
|
},
|
|
@@ -255,7 +274,10 @@ TOOLS = [
|
|
|
255
274
|
"inputSchema": {
|
|
256
275
|
"type": "object",
|
|
257
276
|
"properties": {
|
|
258
|
-
"job_id": {
|
|
277
|
+
"job_id": {
|
|
278
|
+
"type": "string",
|
|
279
|
+
"description": "Job ID from quick_train or custom_train",
|
|
280
|
+
},
|
|
259
281
|
},
|
|
260
282
|
"required": ["job_id"],
|
|
261
283
|
},
|
|
@@ -266,7 +288,10 @@ TOOLS = [
|
|
|
266
288
|
"inputSchema": {
|
|
267
289
|
"type": "object",
|
|
268
290
|
"properties": {
|
|
269
|
-
"job_id": {
|
|
291
|
+
"job_id": {
|
|
292
|
+
"type": "string",
|
|
293
|
+
"description": "Job ID from quick_train or custom_train",
|
|
294
|
+
},
|
|
270
295
|
},
|
|
271
296
|
"required": ["job_id"],
|
|
272
297
|
},
|
|
@@ -286,8 +311,16 @@ TOOLS = [
|
|
|
286
311
|
"inputSchema": {
|
|
287
312
|
"type": "object",
|
|
288
313
|
"properties": {
|
|
289
|
-
"tournament": {
|
|
290
|
-
|
|
314
|
+
"tournament": {
|
|
315
|
+
"type": "string",
|
|
316
|
+
"description": "Tournament: 'equities' or 'futures'",
|
|
317
|
+
"default": "equities",
|
|
318
|
+
},
|
|
319
|
+
"limit": {
|
|
320
|
+
"type": "integer",
|
|
321
|
+
"description": "Max rounds to return (default 25)",
|
|
322
|
+
"default": 25,
|
|
323
|
+
},
|
|
291
324
|
},
|
|
292
325
|
"required": [],
|
|
293
326
|
},
|
|
@@ -298,7 +331,11 @@ TOOLS = [
|
|
|
298
331
|
"inputSchema": {
|
|
299
332
|
"type": "object",
|
|
300
333
|
"properties": {
|
|
301
|
-
"tournament": {
|
|
334
|
+
"tournament": {
|
|
335
|
+
"type": "string",
|
|
336
|
+
"description": "Tournament: 'equities' or 'futures'",
|
|
337
|
+
"default": "equities",
|
|
338
|
+
},
|
|
302
339
|
},
|
|
303
340
|
"required": [],
|
|
304
341
|
},
|
|
@@ -341,8 +378,14 @@ TOOLS = [
|
|
|
341
378
|
"inputSchema": {
|
|
342
379
|
"type": "object",
|
|
343
380
|
"properties": {
|
|
344
|
-
"name": {
|
|
345
|
-
|
|
381
|
+
"name": {
|
|
382
|
+
"type": "string",
|
|
383
|
+
"description": "Model name (this becomes the model_id for submit/upload).",
|
|
384
|
+
},
|
|
385
|
+
"description": {
|
|
386
|
+
"type": "string",
|
|
387
|
+
"description": "Optional human-readable description.",
|
|
388
|
+
},
|
|
346
389
|
},
|
|
347
390
|
"required": ["name"],
|
|
348
391
|
},
|
|
@@ -383,7 +426,11 @@ TOOLS = [
|
|
|
383
426
|
"type": "object",
|
|
384
427
|
"properties": {
|
|
385
428
|
"model_id": {"type": "string", "description": "Model to run diagnostics on"},
|
|
386
|
-
"days": {
|
|
429
|
+
"days": {
|
|
430
|
+
"type": "integer",
|
|
431
|
+
"description": "Number of days of history (default 365)",
|
|
432
|
+
"default": 365,
|
|
433
|
+
},
|
|
387
434
|
},
|
|
388
435
|
"required": ["model_id"],
|
|
389
436
|
},
|
|
@@ -413,7 +460,11 @@ TOOLS = [
|
|
|
413
460
|
"type": "object",
|
|
414
461
|
"properties": {
|
|
415
462
|
"model_id": {"type": "string", "description": "Model to query"},
|
|
416
|
-
"days": {
|
|
463
|
+
"days": {
|
|
464
|
+
"type": "integer",
|
|
465
|
+
"description": "History window in days (default 3650 = full tournament history)",
|
|
466
|
+
"default": 3650,
|
|
467
|
+
},
|
|
417
468
|
},
|
|
418
469
|
"required": ["model_id"],
|
|
419
470
|
},
|
|
@@ -426,7 +477,10 @@ TOOLS = [
|
|
|
426
477
|
"properties": {
|
|
427
478
|
"model_id": {"type": "string", "description": "Model to stake on."},
|
|
428
479
|
"amount_usdc": {"type": "number", "description": "Amount of USDC to stake."},
|
|
429
|
-
"wallet_address": {
|
|
480
|
+
"wallet_address": {
|
|
481
|
+
"type": "string",
|
|
482
|
+
"description": "On-chain wallet address holding the USDC.",
|
|
483
|
+
},
|
|
430
484
|
},
|
|
431
485
|
"required": ["model_id", "amount_usdc", "wallet_address"],
|
|
432
486
|
},
|
|
@@ -437,7 +491,10 @@ TOOLS = [
|
|
|
437
491
|
"inputSchema": {
|
|
438
492
|
"type": "object",
|
|
439
493
|
"properties": {
|
|
440
|
-
"stake_id": {
|
|
494
|
+
"stake_id": {
|
|
495
|
+
"type": "string",
|
|
496
|
+
"description": "Stake ID returned by stake_on_model.",
|
|
497
|
+
},
|
|
441
498
|
},
|
|
442
499
|
"required": ["stake_id"],
|
|
443
500
|
},
|
|
@@ -460,7 +517,10 @@ TOOLS = [
|
|
|
460
517
|
"type": "object",
|
|
461
518
|
"properties": {
|
|
462
519
|
"model_id": {"type": "string", "description": "Model identifier."},
|
|
463
|
-
"round_id": {
|
|
520
|
+
"round_id": {
|
|
521
|
+
"type": "string",
|
|
522
|
+
"description": "Round identifier for the payout to claim.",
|
|
523
|
+
},
|
|
464
524
|
},
|
|
465
525
|
"required": ["model_id", "round_id"],
|
|
466
526
|
},
|
|
@@ -503,7 +563,10 @@ TOOLS = [
|
|
|
503
563
|
"inputSchema": {
|
|
504
564
|
"type": "object",
|
|
505
565
|
"properties": {
|
|
506
|
-
"round_id": {
|
|
566
|
+
"round_id": {
|
|
567
|
+
"type": "integer",
|
|
568
|
+
"description": "Round identifier for the payout to claim.",
|
|
569
|
+
},
|
|
507
570
|
},
|
|
508
571
|
"required": ["round_id"],
|
|
509
572
|
},
|
|
@@ -729,6 +792,7 @@ def _dispatch(name: str, arguments: dict) -> str:
|
|
|
729
792
|
# MCP-SDK path (preferred)
|
|
730
793
|
# ===================================================================
|
|
731
794
|
|
|
795
|
+
|
|
732
796
|
def _build_mcp_server() -> Server:
|
|
733
797
|
"""Build and return a configured MCP Server instance."""
|
|
734
798
|
server = Server("eiq-mcp")
|
|
@@ -769,6 +833,7 @@ async def _run_mcp() -> None:
|
|
|
769
833
|
# Fallback: minimal JSON-RPC 2.0 over stdin/stdout
|
|
770
834
|
# ===================================================================
|
|
771
835
|
|
|
836
|
+
|
|
772
837
|
def _run_jsonrpc_fallback() -> None:
|
|
773
838
|
"""Minimal JSON-RPC 2.0 loop for environments without the ``mcp`` package."""
|
|
774
839
|
import sys
|
|
@@ -790,11 +855,14 @@ def _run_jsonrpc_fallback() -> None:
|
|
|
790
855
|
params = req.get("params", {})
|
|
791
856
|
|
|
792
857
|
if method == "initialize":
|
|
793
|
-
_jsonrpc_respond(
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
858
|
+
_jsonrpc_respond(
|
|
859
|
+
req_id,
|
|
860
|
+
result={
|
|
861
|
+
"protocolVersion": "2024-11-05",
|
|
862
|
+
"capabilities": {"tools": {"listChanged": False}},
|
|
863
|
+
"serverInfo": {"name": "eiq-mcp", "version": "0.1.0"},
|
|
864
|
+
},
|
|
865
|
+
)
|
|
798
866
|
elif method == "tools/list":
|
|
799
867
|
_jsonrpc_respond(req_id, result={"tools": TOOLS})
|
|
800
868
|
elif method == "tools/call":
|
|
@@ -802,14 +870,20 @@ def _run_jsonrpc_fallback() -> None:
|
|
|
802
870
|
arguments = params.get("arguments", {})
|
|
803
871
|
try:
|
|
804
872
|
text = _dispatch(tool_name, arguments)
|
|
805
|
-
_jsonrpc_respond(
|
|
806
|
-
|
|
807
|
-
|
|
873
|
+
_jsonrpc_respond(
|
|
874
|
+
req_id,
|
|
875
|
+
result={
|
|
876
|
+
"content": [{"type": "text", "text": text}],
|
|
877
|
+
},
|
|
878
|
+
)
|
|
808
879
|
except Exception as exc:
|
|
809
|
-
_jsonrpc_respond(
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
880
|
+
_jsonrpc_respond(
|
|
881
|
+
req_id,
|
|
882
|
+
result={
|
|
883
|
+
"content": [{"type": "text", "text": json.dumps({"error": str(exc)})}],
|
|
884
|
+
"isError": True,
|
|
885
|
+
},
|
|
886
|
+
)
|
|
813
887
|
elif method == "notifications/initialized":
|
|
814
888
|
pass # no response needed for notifications
|
|
815
889
|
else:
|
|
@@ -831,10 +905,12 @@ def _jsonrpc_respond(req_id, *, result=None, error=None) -> None:
|
|
|
831
905
|
# Entry point
|
|
832
906
|
# ===================================================================
|
|
833
907
|
|
|
908
|
+
|
|
834
909
|
def main() -> None:
|
|
835
910
|
"""Run the MCP server over stdio (the transport Claude Code / Cursor use)."""
|
|
836
911
|
if _HAS_MCP:
|
|
837
912
|
import asyncio
|
|
913
|
+
|
|
838
914
|
asyncio.run(_run_mcp())
|
|
839
915
|
else:
|
|
840
916
|
_run_jsonrpc_fallback()
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: everestapi
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Python SDK for the EverestQuant prediction tournament platform
|
|
5
|
-
Author-email: EverestQuant <
|
|
5
|
+
Author-email: EverestQuant <support@everesteer.ai>
|
|
6
6
|
License-Expression: MIT
|
|
7
|
-
Project-URL: Homepage, https://
|
|
8
|
-
Project-URL: Documentation, https://docs.
|
|
7
|
+
Project-URL: Homepage, https://everesteer.ai
|
|
8
|
+
Project-URL: Documentation, https://docs.everesteer.ai
|
|
9
9
|
Project-URL: Repository, https://github.com/everestquant/everestapi-public
|
|
10
10
|
Keywords: quant,tournament,prediction,staking,machine-learning
|
|
11
11
|
Classifier: Development Status :: 4 - Beta
|
|
@@ -30,7 +30,7 @@ Dynamic: license-file
|
|
|
30
30
|
|
|
31
31
|
# everestapi
|
|
32
32
|
|
|
33
|
-
Python SDK for the [EverestQuant](https://
|
|
33
|
+
Python SDK for the [EverestQuant](https://everesteer.ai) prediction tournament platform.
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
36
|
pip install everestapi
|
|
@@ -124,8 +124,9 @@ def test_cf_service_token_kwargs_override_env(monkeypatch):
|
|
|
124
124
|
monkeypatch.setenv("CF_ACCESS_CLIENT_ID", "env.access")
|
|
125
125
|
monkeypatch.setenv("CF_ACCESS_CLIENT_SECRET", "envv")
|
|
126
126
|
kid, ksec = "kw.access", "kwv"
|
|
127
|
-
client = EverestAPI(
|
|
128
|
-
|
|
127
|
+
client = EverestAPI(
|
|
128
|
+
api_key=_K, base_url=_URL, cf_access_client_id=kid, cf_access_client_secret=ksec
|
|
129
|
+
)
|
|
129
130
|
h = client._client.headers
|
|
130
131
|
assert h["cf-access-client-id"] == kid
|
|
131
132
|
assert h["cf-access-client-secret"] == ksec
|
|
@@ -27,30 +27,37 @@ def api(monkeypatch):
|
|
|
27
27
|
|
|
28
28
|
# -- client: model management --------------------------------------------
|
|
29
29
|
|
|
30
|
+
|
|
30
31
|
def test_create_model_posts_body(httpx_mock, api):
|
|
31
32
|
httpx_mock.add_response(
|
|
32
|
-
url="http://test/api/v1/agents/me/models",
|
|
33
|
+
url="http://test/api/v1/agents/me/models",
|
|
34
|
+
method="POST",
|
|
33
35
|
json={"id": "m1", "name": "my-model"},
|
|
34
36
|
)
|
|
35
37
|
out = api.create_model("my-model", description="d")
|
|
36
38
|
assert out["name"] == "my-model"
|
|
37
39
|
assert json.loads(httpx_mock.get_request().content) == {
|
|
38
|
-
"name": "my-model",
|
|
40
|
+
"name": "my-model",
|
|
41
|
+
"description": "d",
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
|
|
42
45
|
def test_create_model_idempotent_on_409(httpx_mock, api):
|
|
43
46
|
httpx_mock.add_response(
|
|
44
|
-
url="http://test/api/v1/agents/me/models",
|
|
45
|
-
|
|
47
|
+
url="http://test/api/v1/agents/me/models",
|
|
48
|
+
method="POST",
|
|
49
|
+
status_code=409,
|
|
50
|
+
json={"detail": "exists"},
|
|
46
51
|
)
|
|
47
52
|
assert api.create_model("dup") == {"name": "dup", "status": "already_exists"}
|
|
48
53
|
|
|
49
54
|
|
|
50
55
|
def test_create_model_reraises_non_409(httpx_mock, api):
|
|
51
56
|
httpx_mock.add_response(
|
|
52
|
-
url="http://test/api/v1/agents/me/models",
|
|
53
|
-
|
|
57
|
+
url="http://test/api/v1/agents/me/models",
|
|
58
|
+
method="POST",
|
|
59
|
+
status_code=400,
|
|
60
|
+
json={"detail": "bad"},
|
|
54
61
|
)
|
|
55
62
|
with pytest.raises(EverestError):
|
|
56
63
|
api.create_model("x")
|
|
@@ -58,7 +65,8 @@ def test_create_model_reraises_non_409(httpx_mock, api):
|
|
|
58
65
|
|
|
59
66
|
def test_get_models(httpx_mock, api):
|
|
60
67
|
httpx_mock.add_response(
|
|
61
|
-
url="http://test/api/v1/agents/me/models",
|
|
68
|
+
url="http://test/api/v1/agents/me/models",
|
|
69
|
+
method="GET",
|
|
62
70
|
json={"agent_id": "a", "models": []},
|
|
63
71
|
)
|
|
64
72
|
assert api.get_models()["models"] == []
|
|
@@ -66,13 +74,16 @@ def test_get_models(httpx_mock, api):
|
|
|
66
74
|
|
|
67
75
|
# -- client: versioned dataset download (the #558 fix) -------------------
|
|
68
76
|
|
|
77
|
+
|
|
69
78
|
def test_download_dataset_uses_versioned_route(httpx_mock, api, tmp_path):
|
|
70
79
|
httpx_mock.add_response(
|
|
71
80
|
url="http://test/api/v1/data/download/bregen/futures/train",
|
|
72
81
|
content=b"PAR1data",
|
|
73
82
|
)
|
|
74
83
|
out = api.download_dataset(
|
|
75
|
-
universe="futures",
|
|
84
|
+
universe="futures",
|
|
85
|
+
split="train",
|
|
86
|
+
version="bregen",
|
|
76
87
|
output_path=str(tmp_path / "t.parquet"),
|
|
77
88
|
)
|
|
78
89
|
assert httpx_mock.get_request().url.path == "/api/v1/data/download/bregen/futures/train"
|
|
@@ -90,7 +101,9 @@ def test_download_dataset_latest_resolves_version(httpx_mock, api, tmp_path):
|
|
|
90
101
|
content=b"PAR1live",
|
|
91
102
|
)
|
|
92
103
|
out = api.download_dataset(
|
|
93
|
-
universe="futures",
|
|
104
|
+
universe="futures",
|
|
105
|
+
split="live",
|
|
106
|
+
version="latest",
|
|
94
107
|
output_path=str(tmp_path / "l.parquet"),
|
|
95
108
|
)
|
|
96
109
|
with open(out, "rb") as f:
|
|
@@ -99,12 +112,18 @@ def test_download_dataset_latest_resolves_version(httpx_mock, api, tmp_path):
|
|
|
99
112
|
|
|
100
113
|
# -- MCP server -----------------------------------------------------------
|
|
101
114
|
|
|
115
|
+
|
|
102
116
|
def test_mcp_tools_include_loop_and_model_tools():
|
|
103
117
|
from everestapi.mcp import server
|
|
118
|
+
|
|
104
119
|
names = {t["name"] for t in server.TOOLS}
|
|
105
120
|
for needed in (
|
|
106
|
-
"download_dataset",
|
|
107
|
-
"
|
|
121
|
+
"download_dataset",
|
|
122
|
+
"create_model",
|
|
123
|
+
"get_models",
|
|
124
|
+
"submit_futures_predictions",
|
|
125
|
+
"get_current_round",
|
|
126
|
+
"get_scores",
|
|
108
127
|
):
|
|
109
128
|
assert needed in names, needed
|
|
110
129
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|