hfmarketdata — conventions de travail (lues par tous les agents)
Plateforme : www.hfmarketdata.io — API FastAPI + DuckDB sur un lac Parquet (350 Go, FirstRate Data), site React/Vite servi par l'API. Plan complet : docs/UPGRADE-PLAN.md (lire en premier). Prod (depuis le 2026-09-14) : serveur OVH BHS128 (ssh BHS128, ubuntu@51.161.112.69, Ubuntu 24.04, 12 threads / 128 Go) — ~/hfmarketdata (venv Python 3.14 via uv, versions épinglées dans requirements-prod-freeze.txt), lac ~/firstratedata (359 Go Parquet + state/hfmd.db + edgar/), Redis natif, PM2 sous systemd (pm2-ubuntu) avec ~/apps/pm2.hfmarketdata.config.cjs généré depuis le manifeste mld par ~/apps/.manifests/gen-pm2.py : hfmarketdata-api (uvicorn :8090, 2 workers), edgar-incremental, crons frd-refresh (lun 03:00), contracts-backfill (04:30), edgar-reconcile (dim 06:00) ; edgar-backfill et rowgroups à la main (pm2 start … --only). Route publique : BHS64 Caddy → wg1 10.67.0.60:8090 (mlt add www.hfmarketdata.io BHS128:8090), ufw ouvert sur wg1 depuis 10.67.0.1. Hors mld (retirée du registre ; l'ancienne copie M3U96b ~/hfmarketdata + ~/firstratedata + Redis a été effacée le 2026-09-14 — BHS128 détient la seule copie du lac et de state/hfmd.db : prévoir une sauvegarde hors serveur ; manifeste mld sauvegardé sur le laptop ~/Desktop/Cluster/secrets/mld-manifests-retired-20260914/hfmarketdata.json). Release : rsync -a --exclude venv --exclude .git --exclude 'hfmarketdata/web/node_modules' --exclude 'hfmarketdata/web/dist' --exclude mcp/node_modules . BHS128:hfmarketdata/ puis ssh BHS128 'cd hfmarketdata && ~/.local/bin/uv pip install --python venv/bin/python -r hfmarketdata/requirements.txt && (cd hfmarketdata/web && npm ci && npm run build) && pm2 restart hfmarketdata-api hfmarketdata-edgar-incremental && pm2 save'. Dépôt de vérité : spbgit gitsrv:srv/git/hfmarketdata.git (branche main).
Règles absolues
- Aucun breaking change : les endpoints
/v1/*existants (main.py) gardent paths, paramètres, formes de réponse ({"count","data"}) et codes. Tout le neuf est additif sous/v1/. - Erreurs : lever
core.errors.ApiError(status, CODE, message, type=…, details=…); les codes vivent danscore/errors.py::CODES(en ajouter si besoin, jamais de code inconnu). Jamais deHTTPExceptiondans le code v2. - Réponses v2 :
core.responses.frame_response(df, fmt, meta=…, request=…)pour les tableaux (json/csv/parquet, enveloppe{"data","meta"},X-Row-Count),json_response(...)pour le reste,clamp_limit(...)pourlimit, curseurs viaencode_cursor/decode_cursor. Timestamps UTC ISO 8601. - Données : DuckDB via
core.duck.con()(connexion par thread) +core.duck.cached(key, builder)pour les scans de répertoires.settings.parquet= racine du lac. Jamais de valeur inventée : absence →null+ raison danscoverage. - Métadonnées : SQLite via
core.db(Base,session(),get_sessiondépendance FastAPI,create_all()idempotent au démarrage du module). Un module = un fichiermodels.py. - Config : uniquement
core.config.settings(variablesHFMD_*). Secrets jamais en dur, jamais dans les exemples publics. - Structure :
hfmarketdata/api/<module>/{routes.py,models.py,service.py,…};routes.pyexposerouter(APIRouter avecprefix="/v1/…",tags=[…]) OUinstall(app)(middleware).main.pycharge les modules listés dansV2_MODULES— ne pas éditer main.py au-delà de cette liste. Imports absolus depuishfmarketdata/api(ex.from core.errors import ApiError). - Quota lignes : toujours poser
X-Row-Count(fait parframe_response). Endpoints coûteux :request.state.request_cost = 2(screener, frames). Bulk :request.state.quota_exempt = True. - Style : Python 3.12+ typé, docstrings en anglais (le produit est en anglais), commentaires courts. Frontend : React 18 + Vite, dark par défaut, pas de framework CSS lourd (CSS modules / variables), composants dans
hfmarketdata/web/src. - Tests : pytest dans
tests/(pytest.inimethfmarketdata/apisur le path) ; lac synthétiquetests/fixtures/make_fixtures.py(l'étendre si un module a besoin d'autres fichiers) ; Redis =fakeredisquandHFMD_REDIS_URL=fakeredis://. Chaque module livre ses tests unitaires + intégration (TestClient)../.venv/bin/python -m pytestdoit rester vert. - OpenAPI 3.1 : chaque route a
summary,description(markdown),response_modelouresponses={…}avec exemples réels, et déclare ses erreurs possibles viaopenapi_extra={"x-errors": ["CONTRACT_NOT_FOUND", …]}. La doc du site est générée depuis/openapi.json: la qualité des descriptions EST la doc. - Git : commits atomiques en français, préfixe du chantier (
futures:,accounts:,ratelimit:,fundamentals:,web:,mcp:,docs:). Ne pas committer.venv,node_modules,dist, données.
Données réelles (référence, pas pour les tests)
Layout : parquet/{stock|etf|crypto|index|fx}/{1min|5min|30min|1hour|1day}/{adj}/{TICKER}_{tf}.parquet · parquet/futures/{tf}/{contin_UNadj|contin_adj_ratio|contin_adj_absolute}/{ROOT}_{tf}.parquet · parquet/futures_contracts/{tf}/{archive|update}/{ROOT}_{MonthCode}{YY}_{tf}.parquet (142 racines, ~15 000 contrats/tf, la colonne ticker = racine ; archive ≤ 2025, update ≥ 2025 avec chevauchement → dédupliquer sur datetime, priorité update) · parquet/options/{yyyy}_{qN}/{TICKER}_month_option_chain.parquet · meta/futures/futures.csv (Ticker, Name, First Date, Last Date). Colonnes barres : ticker, datetime (TIMESTAMP naïf, heure US/Eastern pour l'intraday), open, high, low, close, volume, open_interest (futures 1day). API publique pour vérifier : https://www.hfmarketdata.io/v1/status. Accès au nœud si indispensable : ssh M3U96b (lecture seule sur ~/firstratedata).