Schema v1.0

Signals Documentation

Everything you need to submit signals, be graded, and earn revenue on Paradox Alpha. Your first working submission is five lines of JSON.

Quickstart

From zero to graded in four steps. You'll need a Paradox Alpha account — sign up is free and takes under a minute.

  1. 1

    Create an API key

    In your dashboard, visit Signals → Keysand click Create new key. Copy it immediately — we never show it again.

    bash
    export PX_KEY="px_live_sk_4f3c...3a"
  2. 2

    Register a strategy

    Give it a name, a universe, and a horizon. Strategies start in draft status until you publish.

    bash
    curl https://api.paradoxalpha.com/api/v1/signals/strategies \
      -H "Authorization: Bearer $PX_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "slug": "aurora-momentum",
        "name": "Aurora Momentum",
        "description": "Cross-sectional 12h momentum on top-100 perps",
        "universe": "binance_perp_top100",
        "horizon": "8h",
        "submission_cadence": "8h"
      }'
  3. 3

    Submit your first signal

    Scores are unit-free — we z-score them cross-sectionally. Positive = long, negative = short.

    bash
    curl https://api.paradoxalpha.com/api/v1/signals/strategies/aurora-momentum/submissions \
      -H "Authorization: Bearer $PX_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "schema_version": "1.0",
        "submission_id": "run-2026-04-10-1200",
        "as_of": "2026-04-10T12:00:00Z",
        "scores": {
          "BTCUSDT": 0.82,
          "ETHUSDT": 0.64,
          "SOLUSDT": -0.31,
          "BNBUSDT": 0.42,
          "XRPUSDT": -0.55
        }
      }'
  4. 4

    Watch your grade

    Metrics refresh every 15 minutes. After 30 submissions you'll receive a grade from D to S. Above grade A, your strategy becomes eligible for live bot subscriptions.

Signal Format (schema v1.0)

Paradox Signals uses a single canonical format: cross-sectional {symbol: float}scores, with optional per-symbol enrichment. One schema covers every use case.

json
{
  "schema_version": "1.0",
  "submission_id": "run-2026-04-10-1200",
  "as_of": "2026-04-10T12:00:00Z",
  "valid_until": "2026-04-10T20:00:00Z",
  "universe_hint": "binance_perp_top100",
  "scores": {
    "BTCUSDT": 0.82,
    "ETHUSDT": 0.64,
    "SOLUSDT": -0.31,
    "BNBUSDT": 0.42,
    "XRPUSDT": -0.55
  },
  "enrichment": {
    "BTCUSDT": {
      "direction": "long",
      "conviction": 0.9,
      "horizon": "8h",
      "expected_return_pct": 1.8
    }
  },
  "notes": "trend regime, low vol"
}
FieldTypeRequiredNotes
schema_versionstringyesCurrently "1.0".
submission_idstring ≤ 128yesClient-chosen idempotency key. Repeats return the original response.
as_ofISO-8601 UTCyesMust be within 15 min of now. Rejected if in past or future.
valid_untilISO-8601 UTCnoDefaults to as_of + horizon × 2. Signals past this are ignored.
scores{symbol: float}yesCore payload. At least 5 symbols, must not all be identical.
enrichment{symbol: obj}noPer-symbol direction, conviction, horizon, expected return.
notesstring ≤ 500noFree-form label — shown in submission log.

API Reference

All endpoints live under /api/v1/signals/. Authenticate with either a JWT (dashboard) or an API key (programmatic).

POST /strategies

Register a new signal strategy. JWT only.

json
// Body
{
  "slug": "aurora-momentum",
  "name": "Aurora Momentum",
  "description": "Cross-sectional momentum",
  "universe": "binance_perp_top100",
  "horizon": "8h",
  "submission_cadence": "8h",
  "is_public": true,
  "tags": ["momentum", "trend"]
}

POST /strategies/{slug}/submissions

Submit a signal tick. API key or JWT. Returns 202 on accept, 200 on duplicate, 422 on validation failure, 429 on rate limit.

json
// Response (202)
{
  "submission_id": "run-2026-04-10-1200",
  "server_received_at": "2026-04-10T12:00:01.234Z",
  "accepted": true,
  "duplicate": false,
  "coverage": { "symbols": 5, "benchmark_pct": 5.0 },
  "warnings": []
}

GET /strategies/{slug}/metrics

Retrieve rolling evaluation windows (last 30 by default).

GET /leaderboard

Top strategies by composite score. Public, no auth required.

GET /keys · POST /keys · DELETE /keys/{id}

Manage API keys. JWT only. POST returns the raw key once.

Evaluation Methodology

Every live strategy is re-graded every 15 minutes on a rolling 24h window. We reuse the same asymmetric long/short rank-IC engine that powers our in-house Autopilot — no manual review, no politics, just math.

  • Rank IC: Spearman correlation between your scores and realized forward returns.
  • Long / Short IC: split into top and bottom quintile separately.
  • Hit Rate: directional accuracy.
  • Sharpe: annualized Sharpe of a paper portfolio driven by your signal.
  • Coverage: fraction of the benchmark universe your signal hits.
  • Freshness: latency from as_of to server_received_at.

Composite = 0.5 × rank_ic + 0.3 × sharpe_norm + 0.1 × hit_rate_norm + 0.1 × coverage

Grades: S (>0.25, 100+ subs), A (>0.15), B (>0.08), C (>0.03), D (below). UNRATED under 30 submissions.

Look-ahead prevention: forward returns are always measured fromserver_received_at, never from your claimed as_of. Submissions back-dated by >15 min are rejected outright.

Python SDK

A minimal Python SDK is planned for Phase 4. For now, the API is simple enough to wrap in a few lines of requests:

python
import os, requests
from datetime import datetime, timezone

BASE = "https://api.paradoxalpha.com/api/v1/signals"
KEY = os.environ["PX_KEY"]

def submit(slug: str, scores: dict[str, float], submission_id: str):
    return requests.post(
        f"{BASE}/strategies/{slug}/submissions",
        headers={"Authorization": f"Bearer {KEY}"},
        json={
            "schema_version": "1.0",
            "submission_id": submission_id,
            "as_of": datetime.now(timezone.utc).isoformat(),
            "scores": scores,
        },
    ).json()

print(submit("aurora-momentum",
             {"BTCUSDT": 0.82, "ETHUSDT": 0.64, "SOLUSDT": -0.31,
              "BNBUSDT": 0.42, "XRPUSDT": -0.55},
             submission_id="run-" + datetime.now().strftime("%Y%m%d-%H%M")))

Errors & Rate Limits

Errors use a consistent envelope:

json
{
  "detail": {
    "code": "signal_too_old",
    "message": "as_of is 23.4 minutes in the past (max 15)",
    "param": "as_of"
  }
}
CodeHTTPWhen
signal_too_old422as_of > 15 min in the past
signal_in_future422as_of > 2 min ahead of server time
signal_too_sparse422fewer than 5 symbols
signal_degenerate422all scores identical
rate_limited42960/min per provider per strategy

FAQ

For non-technical questions about payouts, grading philosophy, and contributor onboarding, see thesignals landing page.

Need help? Email signals@paradoxalpha.com.