SPB Git forge

spb/hfmarketdata

Public

Open high-frequency market data platform — FirstRate full-history downloader, DuckDB/Parquet lake, open REST API and React docs platform (www.hfmarketdata.io)

127commits 1branches 0releases
24.7 MBsize
maindefault branch
11 days agolast push
JavaScript 53.7% Python 38.3% CSS 4.6% TypeScript 3.1%
14.0 KB

# HF Market Data — Upgrade majeur (v2) : plan d'exécution

Date : 2026-09-04 · Auteur : Simon-Pierre Boucher · Statut : livrable 1 (schéma · plan de doc · maquettes), puis implémentation chantier par chantier.

Contraintes retenues : aucun breaking change (/v1/* existants inchangés, erreurs enrichies mais detail conservé), UTC/ISO 8601, erreurs JSON uniformes {"error":{"code","message","docs"}}, p95 < 300 ms pour 10 000 barres, clés hashées, rate limit aussi sur l'inscription.

# 0. Architecture cible

text
hfmarketdata/
  api/                      FastAPI (Python 3.14, DuckDB sur le lac Parquet, SQLite métadonnées, Redis quotas)
    main.py                 app + endpoints historiques (inchangés) + montage des routeurs v2
    core/config.py          env (HFMD_*) · core/errors.py (format uniforme) · core/db.py (SQLite/SQLAlchemy)
    futures/                chantier 1 : symbols.py (parsing), specs.py (référentiel racines), rolls.py, backfill.py, routes.py
    accounts/               chantier 2 : models.py, security.py (hash/PAT), routes_auth.py, routes_me.py, routes_admin.py, mailer.py, cli.py
    ratelimit/              chantier 2 : redis_limiter.py (Lua), middleware.py, tiers.py
    openapi.py              spec 3.1 enrichie (tags, exemples, codes d'erreur) = source unique de la doc
  web/                      Vite + React 18 + react-router · dark par défaut · SPA code-splittée (pré-rendu des pages statiques au build)
    src/app/                shell, nav, thème, auth context
    src/pages/{home,docs,playground,integrations,pricing,status,auth,dashboard,admin}
    src/docs/               générateur 3 colonnes depuis /openapi.json + guides MDX (content/guides/*.mdx) + changelog
    src/playground/         constructeur de requêtes, exécution, tableau, mini-graphique, export de code, headers quota
  mcp/                      chantier 5 : serveur MCP `hfmarketdata-mcp` (TypeScript, stdio) + README vitrine
  skills/                   chantier 5 : pack de skills (SKILL.md + scripts) → web/public/downloads/hfmarketdata-skills.zip
  tests/                    pytest (unit + intégration TestClient + Redis) · web/e2e (Playwright)
  scripts/backfill_contracts.py  chantier 1 : backfill idempotent des échéances (2010 →)
frd_downloader.py           inchangé

Stockage : SQLite (HFMD_STATE_DB, défaut ~/firstratedata/state/hfmd.db) pour comptes, clés, tiers, usage journalier et futures_contracts ; Redis (HFMD_REDIS_URL) pour les fenêtres glissantes de quotas (requêtes + lignes), scripts Lua atomiques.

# 1. Schéma de base de données

# 1.1 futures_roots (référentiel des produits)

colonne type note
root TEXT PK ex. ES, CL, 6E (FirstRate : E6 → alias 6E accepté)
name TEXT « E-mini S&P 500 »
exchange TEXT CME, CBOT, NYMEX, COMEX, ICE, EUREX, …
asset_class TEXT equity_index, energy, metals, rates, ags, fx, crypto, volatility, softs, livestock
currency TEXT USD, EUR, …
contract_size REAL / TEXT « 50 × index », 1 000 bbl… (valeur numérique + contract_size_unit)
tick_size REAL
tick_value REAL
settlement_type TEXT cash / physical
expiry_rule TEXT clé de règle (ex. third_friday, cl_rule, last_business_day, data)
month_cycle TEXT ex. HMUZ, FGHJKMNQUVXZ
first_data_date, last_data_date DATE dérivés du lac (min/max sur tous les contrats)
contracts_count INT
source TEXT reference (table statique) / derived

# 1.2 futures_contracts

colonne type note
symbol TEXT PK forme courte ESZ25 ; forme longue ESZ2025 acceptée en entrée
root TEXT FK
month_code CHAR(1) F G H J K M N Q U V X Z
contract_month INT (1-12) · contract_year INT
expiration_date DATE règle par racine, sinon last_data_date ; expiration_source = rule / data
last_trading_date DATE
first_notice_date DATE NULL si cash-settled
settlement_type, contract_size, tick_size, tick_value, currency, exchange copiés de la racine
first_data_date, last_data_date DATE plage réelle (1day)
status TEXT active / expired (expired si last_data_date < aujourd'hui − 7 j ET mois d'échéance passé)
volume_avg_daily REAL 20 dernières séances
open_interest_last REAL dernier OI non nul
timeframes TEXT JSON des granularités disponibles
files TEXT JSON {tf: [archive_path, update_path]}
updated_at TIMESTAMP
Index : (root, expiration_date), (status), (root, contract_year, contract_month).

# 1.3 futures_contract_gaps — symbol, timeframe, gap_start, gap_end, bars_missing (gaps > 3 jours ouvrés, calculés au backfill).

# 1.4 Comptes / clés / quotas

table colonnes
users id PK, email UNIQUE, name, password_hash (argon2), email_verified_at, role (user/admin), tier (free/high_usage), status (invited/active/disabled), created_at, last_login_at
email_tokens id, user_id FK, kind (verify/reset/invite), token_hash, expires_at, used_at
api_keys id PK, user_id FK, name, prefix (8 car. affichés : hfmd_live_ab12cd34), key_hash (sha256 + sel serveur), tier_override (nullable), status (active/revoked), created_at, last_used_at, revoked_at
usage_daily day, principal (key:<id> ou ip:<hash>), requests, rows, rows_parquet, bytes, status_2xx, status_429 — PK (day, principal) — agrégé depuis Redis toutes les minutes
usage_minute minute, principal, requests, rows — 7 j glissants (graphes 24 h / 7 j)
audit_log ts, actor, action, target, meta

Format d'une clé : hfmd_live_<32 car. base62> ; on n'en stocke que le hash et le préfixe ; affichée une seule fois à la création ; révocable ; « régénérer » = révoque + crée.

# 1.5 Tiers (constantes ratelimit/tiers.py)

tier fenêtre requêtes lignes lignes max / requête
keyless (IP) 1 h 30 100 000 5 000
free (compte + clé) 1 min 120 1 000 000 50 000
high_usage (sur demande à contact@spboucher.ai) 1 min 600 10 000 000 200 000

Redis : clé rl:{principal}:{req|rows} — fenêtre glissante par buckets de 1 s (hash) évaluée en Lua : EVALSHA limiter <key> <now_ms> <window_ms> <cost> <limit> → {allowed, remaining, reset_ms} ; deux appels dans un même script pour requêtes et lignes. Parquet = coût lignes ÷ 2 ; /v1/bulk/* et réponses 304 = 0 ligne. Headers X-RateLimit-* sur toute réponse ; 429 Retry-After + error.type.

# 2. Contrat d'API — nouveautés (toutes additives)

Chantier 1 · GET /v1/futures/roots · GET /v1/futures/{root}/contracts · GET /v1/futures/contract/{symbol}/bars · GET /v1/futures/contract/{symbol}/coverage · GET /v1/futures/{root}/chain?as_of= · GET /v1/futures/{root}/continuous?roll=&adjust=&depth= · GET /v1/futures/{root}/term-structure?as_of=. Paramètres communs : interval (1m|5m|30m|1h|1d, alias de timeframe), from/to, session=rth|eth|all, cursor/limit, format=json|csv|parquet. Réponses : {"data":[...], "meta":{"symbol","interval","count","next_cursor","roll_dates":[...]}}. Erreurs : 400 INVALID_CONTRACT_SYMBOL, 404 CONTRACT_NOT_FOUND, 404 ROOT_NOT_FOUND, 400 INVALID_PARAMETER, 429 RATE_LIMIT_EXCEEDED, 401 INVALID_API_KEY.

Chantier 2 · /v1/auth/{signup,verify,login,logout,forgot,reset,accept-invite} · /v1/me · /v1/me/keys (POST/GET/DELETE/POST …/rotate) · /v1/me/usage?range=24h|7d|30d · /v1/admin/users (CRUD, tier, invitation) · /v1/admin/usage · /v1/limits (public : tiers + limites courantes du principal).

# 3. Plan de la documentation (/docs)

Génération : /openapi.json (3.1, exemples réels capturés au build) → référence ; content/guides/*.mdx → guides ; content/changelog.mdx. Layout 3 colonnes (nav · texte · code curl/Python/JS/R avec langue persistante), Cmd+K, « Try it » → /playground?ep=…&params…, lien « Edit / Report issue » (mailto contact@spboucher.ai + lien repo), dark par défaut, mobile : nav en tiroir, colonne code repliable.

text
Getting started        Guides                              Reference (OpenAPI)         More
  Quickstart (30 s)      Futures individual contracts        Meta & status               Changelog
  Authentication         Options chains & Greeks             Bars (stocks/ETF/crypto/…)  Versioning & deprecation
  Rate limits            Time zones & sessions (RTH/ETH)     Futures v2 (7 endpoints)    Limits & pricing
  Data formats           Bulk downloads                      Options                     Integrations (MCP, skills)
                         Recipes: pandas backtest ·          Accounts & keys · Usage     Status
                         custom continuous · CL term structure Errors

# 4. Maquettes

# 4.1 Playground (/playground, aussi intégré au dashboard avec clé injectée)

text
┌ HF Market Data ─ Docs · Playground · Integrations · Pricing · Status ─────────────── [Sign in] ┐
│ ⓘ Keyless mode: 30 req/h. Create a free account for 120 req/min → [Create free account]       │
├──────────────────────────────┬─────────────────────────────────────────────────────────────────┤
│ Request type ▾               │ GET https://www.hfmarketdata.io/v1/futures/ES/continuous?roll=… │ [Copy]
│  ○ Stock/ETF bars            │ ──────────────────────────────────────────────────────────────── │
│  ○ Crypto · FX               │ [Run ▶]   200 OK · 184 ms · 2 512 rows                          │
│  ● Futures continuous        │ ┌ Table ─┬ JSON ─┬ Chart ──────────────────────────────────────┐ │
│  ○ Individual contract       │ │ ▂▃▅▆▇█▇▆▅ candlesticks (lightweight-charts) roll markers ▲   │ │
│  ○ Contract chain            │ └─────────────────────────────────────────────────────────────┘ │
│  ○ Term structure            │ Rate limit: requests 29/30 · rows 97 488/100 000 · reset 41 min │
│  ○ Options chain (Greeks)    │ ┌ curl ─┬ Python (requests+pandas) ─┬ JavaScript (fetch) ─────┐ │
│  ○ Symbols / roots           │ │ import requests, pandas as pd …                             │ │
│ ─ Form (dynamic) ─           │ └─────────────────────────────────────────────────────────────┘ │
│ root  [ES  ▾ autocomplete]   │ Examples: AAPL 1-min today · CL chain · ES continuous back-adj  │
│ roll  [volume ▾] adjust [back_adjusted ▾] depth [1 ▾]         2015-2025 · NG term structure    │
│ from [2015-01-01] to [2025-12-31]   interval [1d ▾]  format [json ▾]  session [all ▾]          │
└──────────────────────────────┴─────────────────────────────────────────────────────────────────┘

# 4.2 Dashboard (/dashboard)

text
┌ Sidebar: Overview · API keys · Usage · Playground · Account          Tier: Free  ·  Need more? contact@spboucher.ai ┐
│ Overview  ─ Requests today 1 240 / (120/min) · Rows today 3.1 M · Last request 2 min ago                            │
│ API keys  ─ ┌ name ─ prefix ─ created ─ last used ─ status ─ actions ┐  [+ Create key]                              │
│             │ default  hfmd_live_ab12cd34…  2026-09-04  just now  active  [Rotate] [Revoke] │  (clé montrée 1 fois) │
│ Usage     ─ [24h] [7d] [30d]  ▁▂▃▅▆▇█ requests / min   ▁▁▂▃▅▆ rows / min   · table par jour · export CSV            │
│ Playground─ même composant qu'en public, clé injectée automatiquement, bandeau « authenticated as … »               │
│ Admin (rôle admin) ─ users (créer · inviter · tier · désactiver) · consommation globale · top principals            │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

# 4.3 Access & limits (/limits) — tout est gratuit

Tableau 3 colonnes (Keyless · Free account · High usage) avec les 6 chiffres, CTA « Create free account » et « Request high usage → contact@spboucher.ai », encart « incitations » (Parquet = ½ coût, bulk hors quota, ETag/304 gratuits).

# 4.4 Homepage

Hero (« Open high-frequency market data. 1-minute to daily, since 2010. ») · bloc « Get an API key » en 3 étapes (keyless → free → high usage) · aperçu live du playground (requête ES continu) · Integrations (Claude Code · Cursor · Codex · MCP) · datasets live (compteurs /v1/status).

# 5. Ordre d'implémentation

  1. Socle : config, erreurs uniformes, SQLite, OpenAPI 3.1 enrichie, tests de non-régression des endpoints v1.
  2. Chantier 1 (futures) + backfill + tests unitaires symboles/rolls.
  3. Chantier 2 (Redis limiter, clés, comptes, dashboard API, CLI hfmd users add, seed des 3 utilisateurs).
  4. Chantier 6 + 3 + 4 (site, docs 3 colonnes, playground) — frontend.
  5. Chantier 5 (MCP + skills + page Integrations).
  6. Tests E2E Playwright, déploiement via mld sur M3U96b (Redis + backfill dans le manifeste).