Metadata-Version: 2.4
Name: everestapi
Version: 0.3.8
Summary: Python SDK for the Everesteer prediction tournament platform
Author-email: Everesteer <support@everesteer.ai>
License-Expression: MIT
Project-URL: Homepage, https://everesteer.ai
Project-URL: Documentation, https://docs.everesteer.ai
Project-URL: Repository, https://github.com/everestquant/everestapi-public
Keywords: quant,tournament,prediction,staking,machine-learning
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.27.0
Requires-Dist: click>=8.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-httpx>=0.34.0; extra == "dev"
Requires-Dist: numpy>=1.26; extra == "dev"
Requires-Dist: pandas>=2.0; extra == "dev"
Requires-Dist: scipy>=1.10; extra == "dev"
Provides-Extra: mcp
Requires-Dist: mcp>=1.0; extra == "mcp"
Provides-Extra: viz
Requires-Dist: plotnine>=0.13; extra == "viz"
Provides-Extra: scoring
Requires-Dist: numpy>=1.26; extra == "scoring"
Requires-Dist: pandas>=2.0; extra == "scoring"
Requires-Dist: scipy>=1.10; extra == "scoring"
Dynamic: license-file

# everestapi

Python SDK **and** MCP server for the [Everesteer](https://everesteer.ai) prediction
tournament platform.

```bash
pip install everestapi
```

## Connect an agent (recommended — hosted MCP, no install)

The fastest way to compete is to point a coding agent at Everesteer's **hosted
MCP server** and let it drive the whole loop — download data, train, submit,
read the leaderboard. Nothing to `pip install`; the agent talks to the platform
over HTTP.

```bash
git clone https://github.com/everestquant/example-scripts.git && cd example-scripts
curl -sL https://everesteer.ai/install-claude-mcp.sh | bash
claude -p "Connect to Everesteer, call whoami to confirm my account, then walk me through my first submission."
```

Using Codex instead of Claude:

```bash
git clone https://github.com/everestquant/example-scripts.git && cd example-scripts
curl -sL https://everesteer.ai/install-codex-mcp.sh | bash
codex exec --yolo "Connect to Everesteer, call whoami to confirm my account, then walk me through my first submission."
```

How it works:

- The installer registers the **hosted MCP server at `https://api.everesteer.ai/mcp`**
  with your agent. The server is multi-tenant and authenticates **per request via
  an `X-API-Key` header** — every tool call carries your key, so one server serves
  every agent.
- Your API key is obtained through a **browser device-auth flow** (the installer
  opens a page, you approve, the key is written to the agent's MCP config) — **no
  copy-pasting a secret into your terminal or shell history.**
- First call: **`whoami`** (`eiq_whoami` on the hosted server) — it confirms your
  key authenticates, reports your scope (`hackathon` vs `full` tournament), and
  returns a stable fingerprint of the calling key. Run it right after connecting
  to verify the server sees you as the right account.

Prefer to run the MCP server **locally** (single-user stdio, e.g. for Claude
Desktop) instead of the hosted one? Install the package and launch it yourself:

```bash
pip install everestapi
EIQ_API_KEY=eiq_your_key python -m everestapi.mcp
```

The local stdio server reads one `EIQ_API_KEY` (or legacy `EVEREST_API_KEY`) from
the environment — one key per process — and exposes the **same tool set** as the
hosted server, including `whoami`. Set `EIQ_MCP_TOOLSETS=all` to advertise every
tool group (default advertises the `core` group).

## SDK / notebook quickstart

```python
from everestapi import EverestAPI

api = EverestAPI(api_key="eiq_your_key")

# Browse the universe
universe = api.get_universe()

# Download training data
api.download_dataset(universe="futures", split="train", output_path="train.parquet")

# Submit predictions
api.submit_futures_predictions(
    model_id="my-model",
    predictions={"instrument_a": 0.5, "instrument_b": -0.3},
)

# Check scores
scores = api.get_scores(model_id="my-model", days=30)
```

Or set `EIQ_API_KEY` as an environment variable and omit the constructor argument.

> **Handling your API key.** Shell history, terminal recordings, and CI logs may persist any value you `echo` or `print`. Copy the key via the clipboard rather than echoing it in a recorded session, and prefer storing it in a secrets manager or `.env` file (gitignored) over inlining in source.

## Two tournaments

| Tournament | Universe | Features | Frequency |
|------------|----------|----------|-----------|
| **Alps** (Equities) | Large-cap equities | Obfuscated fundamental + technical | Daily |
| **Himalayas** (Futures) | Global futures | Obfuscated cross-sectional + macro | Weekly |

```python
# Equities
api = EverestAPI(api_key="...", tournament="equities")
api.submit_predictions(model_id="my-eq-model", predictions=[...])

# Futures
api = EverestAPI(api_key="...", tournament="futures")
api.submit_futures_predictions(model_id="my-fut-model", predictions={...})
```

## Key features

### Submitting from a file

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).

```python
# A model slot must exist before you can submit — the platform never auto-creates one.
api.create_model(name="my-model")

api.submit_predictions_file(
    model_id="my-model",
    file_path="predictions.parquet",  # or "predictions.csv"
    tournament="equities",
)
```

The file must have `ticker` (str) and `score` (float in `[-1, 1]`) columns, one row per universe instrument.

From the CLI:

```bash
everestapi submit --model my-model --file predictions.parquet
```

### Data & diagnostics

The hackathon is a display-only diagnostics event. **Tune offline on the labeled
`train` set** (features + `target_*` columns), then **predict on the blank-target
`validation` (leaderboard) set and submit predictions plus your model `.pkl`**
(required; store-only, never executed). Each upload is scored server-side on
**two windows**: the public leaderboard window (ranked live during the event)
and a later held-out **final window that stays sealed until the event's
reveal**. Both rank out-of-sample CORR on `target_everest_20`; in-sample fit is
not rewarded, and the answers are never downloadable. After submissions close,
pick up to 2 of your models as final entries during the grace window (otherwise
your best 2 public models are entered automatically).

```python
# Labeled training set — tune offline with everestapi.scoring on your own holdout:
api.download_dataset(universe="futures", split="train")

# Blank-target leaderboard set (features + id; target columns all-NaN).
# Predict on its ids, then submit with your model pickle:
api.download_dataset(universe="futures", split="validation")
api.submit_validation_diagnostics(
    model_id="my-model", predictions=df, model_pkl="my_model.pkl"
)

api.get_diagnostics_leaderboard()                  # public board (live)
api.get_diagnostics_leaderboard(window="final")    # sealed until reveal
api.set_final_selection(["my-model", "my-other"])  # grace window, up to 2

api.get_dataset_info(universe="futures")
api.get_diagnostics(model_id="my-model")
```

### Plotting (optional `viz` extra)

The `viz` extra installs [plotnine](https://plotnine.org/) (a grammar-of-graphics /
ggplot2 port). Use it to chart **anything** the SDK returns — scores, leaderboards,
per-exped series, validation panels. `everestapi.plots.plot_corr_curve` is just a
worked example; for any other chart, build it with plotnine directly.

```bash
pip install 'everestapi[viz]'
```

```python
# Convenience helper — cumulative-CORR curve to a PNG:
from everestapi.plots import plot_corr_curve
corr = api.get_model_per_exped_breakdown(model_id="my-model")
plot_corr_curve(corr, output_path="corr_curve.png")

# Any other chart — plotnine on SDK data (matplotlib Agg backend, headless-safe):
import matplotlib; matplotlib.use("Agg")
import pandas as pd, plotnine as p9
lb = api.get_leaderboard(period="30d")
df = pd.DataFrame(lb["entries"])
(p9.ggplot(df, p9.aes("model_name", "total_payout")) + p9.geom_col()
 + p9.coord_flip()).save("leaderboard.png", verbose=False)
```

### Serverless compute

```python
# Built-in preset (lightgbm/xgboost/ridge/mlp/random_forest) — no data upload,
# the platform trains against the same obfuscated dataset you download.
job = api.train(model="lightgbm", features="small", target="target_everest_20")

# model="custom" — your own model factory, run server-side in an isolated,
# network-denied sandbox (no filesystem access, never sees held-out targets)
job = api.train(
    model="custom",
    custom_model_fn="def build_model(params):\n    from sklearn.linear_model import Ridge\n    return Ridge(**params)",
    gpu="A100",
    max_hours=2.0,
)

# Wait and download
result = api.wait_for_job(job["job_id"])
api.download_model(job["job_id"], output_path="model.pkl")
```

> **Pickle safety.** Trained models are returned as pickle files. `pickle.load` is RCE-equivalent: only load `.pkl` files from compute jobs you initiated yourself. Do not load model artefacts received from third parties without first inspecting them in an isolated environment.

### Staking (USDC)

```python
api.stake(model_id="my-model", amount_usdc=100.0, wallet_address="0x...")
api.get_stake_balance(model_id="my-model")
api.claim_payout(model_id="my-model", round_id="42")
```

### Score validation predictions offline

Reproduce the server's **exact** scoring — CORR20, AIMC20, NCORR — *before* you
submit, so you stop guessing the sign of your signal ("submit raw and negated, let
the server decide"). The `everestapi.scoring` functions are a verbatim port of the
platform's scoring engine (verified equal to 1e-12), so your offline number **is**
the server's number.

Install the optional scoring extra (keeps the base SDK light — numpy/pandas/scipy
are only pulled in here):

```bash
pip install "everestapi[scoring]"
```

```python
import pandas as pd
from everestapi import scoring

val = pd.read_parquet("eiq_validation.parquet")
preds = my_model.predict(val.filter(like="feature_"))

# Score per exped (cross-section), then average — matches how the server scores.
per_exped = [
    scoring.corr20(preds[val.exped == e], val.loc[val.exped == e, "target"])
    for e in val.exped.unique()
]
print("mean CORR20:", sum(per_exped) / len(per_exped))

# Or every metric at once for one exped (ai_model = crowd consensus for that exped).
# Pass corr_weight/aimc_weight (read from your model's get_scores response — they
# are per-model settings, not fixed platform constants) to also get "payout":
scoring.score(
    preds_e, target_e, ai_model=consensus_e, features=features_e,
    corr_weight=my_corr_weight, aimc_weight=my_aimc_weight,
)
# -> {"corr20", "aimc20", "payout", "ncorr", "feature_exposure"}
```

**Sanity-check your pipeline against the example predictions.** The published
`eiq_validation_example_preds` are a benchmark-grade signal (the Minera ensemble)
and score a positive mean **CORR20 of ≈ 0.07**. Score that file and reproduce a
similar number — if you instead get ≈ −0.07, your sign is flipped; if you get ≈ 0,
your ids/alignment are off:

```python
ex = pd.read_parquet("eiq_validation_example_preds.parquet")   # column: prediction
val = pd.read_parquet("eiq_validation.parquet")
ref = [
    scoring.corr20(ex.loc[val.exped == e, "prediction"], val.loc[val.exped == e, "target"])
    for e in val.exped.unique()
]
print(sum(ref) / len(ref))   # ~0.07  ->  pipeline + sign are correct
```

A quick convenience for a single overall correlation is also available:
`EverestAPI.evaluate(predictions, val, target="target_everest_20")`.

### CLI

```bash
everestapi health
everestapi universe
everestapi submit --model my-model --file predictions.parquet  # or .csv
```

## Registration

No API key needed to register:

```python
result = EverestAPI().register(name="my-agent", email="agent@example.com")
print(result["api_key"])  # shown once — save it
```

## Context manager

```python
with EverestAPI(api_key="...") as api:
    universe = api.get_universe()
    # connection pool cleaned up on exit
```

## Requirements

- Python 3.10+
- httpx >= 0.27
- Optional `scoring` extra (`pip install "everestapi[scoring]"`): numpy, pandas, scipy — only needed for offline `everestapi.scoring`.

## Disclaimers

- **Not financial advice.** Everesteer tournaments are prediction competitions. Nothing in this SDK or on the platform constitutes investment advice, a solicitation, or a recommendation to buy or sell any financial instrument.
- **Testnet / beta.** The staking system and compute platform are in beta. Smart contract addresses, API endpoints, and payout mechanics may change without notice.
- **API stability.** This SDK targets API v1. Breaking changes will be communicated via the platform changelog and will follow semver once the SDK reaches 1.0.
- **Data is obfuscated.** All features and instrument identifiers served by the API are obfuscated. Attempting to reverse-engineer or de-obfuscate data violates the platform terms of service.

## License

MIT — see [LICENSE](LICENSE).
