La vie sans Bloomberg : une API REST COT construite de zéro
Architecture Backend
La CFTC publie chaque vendredi 30 ans de données de positionnement sur les futures — enfouis dans un fichier ZIP hérité avec 300 colonnes et aucune API. Nous en avons construit une. Une API REST qui ingère, normalise et expose les données COT sur 398 contrats futures avec Z-scores, métriques de pression, COT Index et signaux de divergence calculés à la volée. Architecture complète : Django ORM, pipeline de sync incrémental, authentification par clé API, et snapshot Z-score en direct sur tous les contrats mainstream.
Antoine
CEO - CodeMarketLabs
2026-04-21
La vie sans Bloomberg : une API REST COT construite de zéro
Chaque vendredi à 15h30 heure de New York, la CFTC publie le Commitments of Traders. C'est le détail complet des positions ouvertes sur 400+ marchés futures — hedge funds, hedgers commerciaux, swap dealers, petits traders. Qui est long, qui est short, combien de contrats, quel niveau de concentration. C'est l'un des datasets publics les plus actionnables en finance. Et il est livré dans un fichier ZIP contenant un CSV hérité des années 90 avec 300 colonnes et aucune documentation. Cet article documente la construction : comment nous avons parsé, structuré et exposé 30 ans de données COT sous forme d'une API REST propre — avec Z-scores, métriques de pression, COT Index et signaux de divergence calculés à la volée.
Ce que couvre cet article
L'architecture des données : modèles ORM Django pour OpenInterest, Traders, Concentration sur 398 contrats de 1986 à aujourd'hui.
Le pipeline d'ingestion : sync automatisé depuis les ZIPs historiques de la CFTC, mises à jour incrémentales, bulk_create idempotent.
La couche analytique : Z-score, pressure (|z| / max|z|), COT Index (percentile), divergence Commercial vs Non-Commercial, détection de signaux actifs.
Le design de l'API : 25+ endpoints REST, authentification par clé API avec rate limiting journalier, filtre sur les contrats mainstream.
Snapshot Z-score en direct : où se situe chaque contrat futures mainstream en ce moment par rapport à son historique complet.
1. Le problème avec les données COT
La CFTC publie les données COT en deux formats : un tableau HTML sur leur site, et un ZIP annuel compressé contenant un fichier XLS hérité. Ce XLS a 300+ colonnes avec des noms comme NonComm_Postions_Spread_All — notez la faute de frappe dans 'Positions', gravée dans le dataset officiel depuis 1986 et jamais corrigée. Il n'y a pas d'API. Pas de JSON. Aucun moyen d'interroger 'donnez-moi le positionnement net Non-Commercial sur l'Or pour les 5 dernières années' sans télécharger plusieurs fichiers annuels, les concaténer, filtrer par code contrat, et calculer le net vous-même.
cftc-cot-report-text-file
Le problème plus profond : les chiffres bruts sont inutiles sans contexte. Savoir que les Non-Commercials détiennent 180 000 contrats longs sur l'Or ne signifie rien sans connaître la plage historique. Est-ce élevé ? Est-ce près du plus haut historique ? Est-ce en hausse depuis 4 semaines ? Le rapport COT nécessite une normalisation, et la normalisation nécessite de l'historique. La seule façon de faire ça correctement est d'ingérer le dataset complet sur 30 ans et de calculer des métriques relatives au moment de la requête.
2. Architecture des données
La base de données a cinq modèles principaux : Contract (398 marchés, chacun avec un code contrat CFTC, un type de commodity, un exchange et une région), OpenInterest (positions hebdomadaires pour Non-Commercial, Commercial et Non-Reportable), Traders (nombre hebdomadaire de traders déclarants distincts par catégorie), et Concentration (ratios de concentration des 4 et 8 plus grands traders — brut et net). Chaque enregistrement OpenInterest stocke les positions brutes ; les métriques dérivées comme net_non_comm, open_interest et tous les pourcentages sont calculés comme propriétés du modèle à la lecture, pas stockés.
La CFTC publie des fichiers ZIP annuels pour le format Legacy Futures Only depuis 1986. Le pipeline d'ingestion est une commande de gestion Django (sync_cftc) qui télécharge les années pertinentes, parse le XLS, et insère en masse uniquement les enregistrements absents de la base de données. Les choix de conception clés : utiliser report_date comme nom de colonne plutôt que _date (itertuples renomme les colonnes commençant par underscore), filtrer sur reported_date > latest_db_date pour les mises à jour incrémentales, et utiliser bulk_create avec ignore_conflicts=True pour des réexécutions idempotentes.
python
defhandle(self,*args,**options): latest_date = get_latest_db_date() contract_map ={c.contract_code.strip(): c for c in Contract.objects.all()} years_to_fetch =sorted({latest_date.year, CURRENT_YEAR})for year in years_to_fetch: df = download_and_extract(year)# GET dea_fut_xls_{year}.zip df['report_date']= pd.to_datetime(df['Report_Date_as_MM_DD_YYYY']).dt.date
df = df[df['report_date']> latest_date] ingest_df(df, contract_map)# python manage.py sync_cftc # incrémental# python manage.py sync_cftc --year 2012 # année spécifique# python manage.py sync_cftc --full # historique complet depuis 1986
4. La couche analytique
Quatre métriques constituent la valeur analytique de l'API. Le Z-score normalise la position nette Non-Commercial (longs moins shorts) par rapport à sa moyenne et son écart-type sur une fenêtre configurable. Un Z-score de +2 signifie que le positionnement est 2 écarts-types au-dessus de sa moyenne historique. La métrique Pressure normalise le Z-score davantage : |z_current| / max(|z|) sur la période, donnant un score entre 0 et 1 où 1 signifie que le marché est à son positionnement le plus extrême jamais enregistré. Le COT Index est le plus simple : (actuel - min) / (max - min) × 100, un percentile. Au-dessus de 80 signifie le positionnement long le plus extrême dans la fenêtre ; en dessous de 20, le short le plus extrême. La Divergence suit l'écart entre le net Non-Commercial et le net Commercial — quand les spéculateurs sont au maximum long et les hedgers au maximum short, cette tension structurelle est historiquement mean-reverting.
cot-gold-z-score-series
python
vals =[row['non_comm_long_all']- row['non_comm_short_all']for row in qs]mean = statistics.mean(vals)std = statistics.pstdev(vals)or1.0zscores =[(v - mean)/ std for v in vals]# Pressure = à quel point le z actuel est-il extrêmeabs_z =[abs(z)for z in zscores]max_z =max(abs_z)if abs_z else1.0pressure =abs(zscores[-1])/ max_z # 0.0 → 1.0# COT Index = percentile dans la fenêtremin_v, max_v =min(vals),max(vals)cot_index =round((vals[-1]- min_v)/(max_v - min_v)*100,2)if max_v != min_v else50.0
5. Design de l'API
L'API expose 25+ endpoints organisés en quatre couches : données de référence (liste des contrats, types, recherche), données historiques brutes (séries open interest, traders, concentration), analytics par contrat (série Z-score, série pressure, COT index, divergence, summary complet), et analytics cross-univers (classement Z-score sur tous les contrats mainstream, extrêmes au-dessus d'un seuil, détection de signaux actifs). L'authentification utilise un header de clé API personnalisé (X-API-KEY) avec rate limiting journalier suivi en base de données — pas de Redis nécessaire. Le flag mainstream sur chaque Contract filtre l'univers aux ~55 marchés liquides et largement suivis.
bash
# Summary complet pour l'Orcurl-H"X-API-KEY: votre_clé"\ https://api.cotreports.codemarketlabs.com/api/contracts/088691/summary/
# Classement Z-score sur tous les contrats mainstreamcurl-H"X-API-KEY: votre_clé"\"https://api.cotreports.codemarketlabs.com/api/contracts/zscore/ranking/?weeks=156"# Signaux actifs — tous les contrats mainstream, fenêtre 3 anscurl-H"X-API-KEY: votre_clé"\"https://api.cotreports.codemarketlabs.com/api/contracts/signals/?weeks=156"# Extrêmes — contrats FX avec |z-score| > 2curl-H"X-API-KEY: votre_clé"\"https://api.cotreports.codemarketlabs.com/api/contracts/extremes/?type=FX&threshold=2.0"
6. Snapshot Z-Score actuel
Le graphique ci-dessous montre où se situe chaque contrat futures mainstream sur son Z-score — calculé depuis l'inception sur l'historique complet disponible de chaque contrat. Le point rouge indique le minimum historique (positionnement short le plus extrême jamais enregistré), le point vert indique le maximum historique. La barre foncée est le Z-score actuel. La barre claire est le Z-score de la semaine précédente.
cot-gold-z-score-snapshot
7. La suite
L'API actuelle couvre le rapport Legacy Futures Only — Non-Commercial, Commercial, Non-Reportable. Le prochain build ajoute le format Disaggregated (Producteurs/Marchands, Swap Dealers, Managed Money, Other Reportables), qui donne une image plus précise de qui pilote réellement les mouvements de positionnement. Managed Money — la catégorie COT qui correspond le plus précisément aux hedge funds systématiques — est un signal plus propre que le bucket Non-Commercial hérité, qui mélange trend-followers, fonds macro et dealers d'options.
Que mesure réellement le Z-score ?
La position nette Non-Commercial (contrats longs moins shorts) normalisée par sa moyenne et son écart-type sur une fenêtre choisie. Un Z-score de +2 signifie que le positionnement spéculatif est 2 écarts-types au-dessus de sa moyenne historique. La fenêtre par défaut est depuis l'inception. Remplacez avec ?weeks=52 (1 an), ?weeks=156 (3 ans), etc.
Quelle est la différence entre Z-score et COT Index ?
Le Z-score est une mesure statistique relative à la moyenne et à l'écart-type — il peut dépasser 3 ou descendre sous -3 sur les marchés extrêmes. Le COT Index est un percentile borné : (actuel - min) / (max - min) × 100, toujours entre 0 et 100. COT Index au-dessus de 80 = top 20% du positionnement long historique. Les deux sont utiles ; le COT Index est plus intuitif pour les traders, le Z-score est plus utile pour les modèles systématiques.
À quelle fréquence les données sont-elles mises à jour ?
La CFTC publie chaque semaine, tous les vendredis à 15h30 heure de l'Est pour les données du mardi précédent. La commande sync_cftc s'exécute de façon incrémentale — elle ne télécharge et n'insère que les enregistrements plus récents que la dernière date en base.
Qu'est-ce que le filtre mainstream ?
La CFTC suit 398 contrats futures, dont des instruments de basis swap obscurs, des hubs d'électricité régionaux et des micro-contrats avec un open interest minimal. Le filtre mainstream=true restreint l'univers aux ~55 marchés liquides en FX, Taux, Indices Actions, Énergie, Métaux, Agricoles et Crypto. Passez ?mainstream=false pour interroger l'univers complet.
L'historique complet sur 30 ans est-il disponible ?
Oui. Le format Legacy Futures Only de la CFTC remonte à janvier 1986 pour la plupart des contrats. Pour les contrats plus récents (Crypto, futures sur secteurs ETF récents), l'historique est plus court. Le Z-score sur fenêtre inception utilise toujours l'historique complet disponible par contrat — un Z-score sur l'Or utilise 38 ans de données, un Z-score sur le Bitcoin utilise les données depuis 2018.