Les mouvements de cotes du tennis, book par book, depuis
Edge / MadMarket. Base : /api
Le flux WebSocket donne le catalogue — tournois, joueurs — et la meilleure cote toutes books confondues.
Le détail par book et la liquidité passent par
/v1/betslips/. ⚠️ L'authentification s'y fait par un en-tête
session:, pas par le cookie root-session où le jeton
est pourtant visible — le cookie seul rend 401. Si cet accès tombe (jeton
expiré), les lignes retombent sur le book
edge-meilleure-offre : pas un nom de book réel, parce qu'on
ne saurait pas lequel des cinq porte ce prix.
/api/sante dit où on en est.
Ce n'est pas un échantillonnage régulier. Si c'en était un,
ecart vaudrait zéro dans la quasi-totalité des lignes et le
dernier vrai mouvement serait noyé.
Conséquence : deux lignes consécutives ne sont pas à intervalle
fixe. C'est pour ça que ecart_secondes est publié plutôt que
supposé.
Edge bride à ~20 secondes par création de betslip, et il en faut deux par match — un par joueur. Un match coûte donc 40 s, et suivre les 230 matchs ouverts demande plus de deux heures par tour. Mesuré : 4,6 lignes par minute, 21 matchs en 17 minutes.
Aucun réglage n'y change rien : le serveur refuse, et il dit lui-même
combien de temps attendre (retry_after). Le seul levier est de
choisir quels matchs suivre — variable TOURNOIS. Vingt
matchs revus toutes les quinze minutes valent mieux que deux cent trente revus
deux fois par jour.
Lis age_secondes avant de jouer un prix. Sans lui, une
cote de vingt minutes ressemble exactement à une cote de l'instant.
null n'est pas 0.
ecart est null au premier relevé d'une sélection,
parce qu'il n'y a rien à quoi comparer. Un 0 affirmerait que le
prix n'a pas bougé.
liquidite est null quand le book n'a pas publié de
mise max. Un 0 se lirait « marché sans profondeur », ce
qui est une affirmation. Le champ liquidite_absente_car dit
pourquoi.
Le flux ne porte aucun drapeau. Un match est tenu pour live si son heure de début est passée, ou si des marchés par set sont proposés. C'est une heuristique.
| route | ce qu'elle répond |
|---|---|
/api | l'index, qui se documente lui-même |
/api/sante | l'état de la source, et pourquoi il n'y a pas de donnée s'il n'y en a pas |
/api/books | les books suivis, y compris ceux qui n'ont rien rendu |
/api/tournois | les tournois, tels qu'Edge les nomme |
/api/joueurs | les joueurs, tels qu'Edge les nomme |
/api/matchs | un match par ligne, les books qui le cotent |
/api/cotes | le grain fin : dernier prix par sélection, avec l'écart et la liquidité |
/api/mouvements | tout l'historique d'un match |
/api/tours | les rotations de collecte |
/health | la sonde, publique |
/api/docs | le Swagger, pour les types exacts |
/api/cotes{
"book": "pin88",
"tournoi": "atp masters 1000 paris",
"tournoi_ecrit_par_le_book": "ATP Masters 1000 Paris",
"joueurs": ["Cameron Norrie", "Matteo Arnaldi"],
"marche": "vainqueur",
"selection": "Cameron Norrie",
"cote": 1.75,
"releve_a": "2026-11-02T14:58:12Z", // le dernier CHANGEMENT
"vu_a": "2026-11-02T15:02:40Z", // le dernier passage, prix identique
"age_secondes": 6, // depuis vu_a
"cote_avant": 1.58,
"ecart": 0.17,
"ecart_pct": 10.759,
"mesure_avant_a": "2026-11-02T14:57:42Z",
"ecart_secondes": 30, // entre les deux prix
"liquidite": 1240.0, // mise max à ce prix, en EUR
"liquidite_devise": "EUR",
"liquidite_absente_car": null,
"cle_match": "atp masters 1000 paris|2026-11-02,10045364,10055688"
}
releve_a et vu_a ne disent pas la même chosereleve_a est l'instant du dernier changement.
vu_a est l'instant du dernier passage où la cote était encore là,
inchangée. Les confondre ferait passer un prix figé depuis deux heures pour un
prix de l'instant.
C'est la mise maximale disponible à ce prix chez ce book. Elle est rafraîchie à chaque passage même quand la cote ne bouge pas : le carnet s'épaissit et s'assèche sans que le prix change, et garder l'ancienne valeur ferait croire qu'on peut encore jouer une taille qui n'est plus là.
En revanche un changement de liquidité n'ouvre pas une nouvelle
ligne : ecart reste l'écart de cote.
Les montants sont convertis dans une devise unique (EUR) via les taux
du flux. Si le taux d'une devise n'est pas encore connu, la liquidité est
null plutôt que convertie au hasard.
/api/cotes| paramètre | effet |
|---|---|
book | pmk | polymarket | molly | bf | pin88 |
tournoi | le libellé Edge plié, ex. atp masters 1000 paris |
joueur | sous-chaîne, cherchée dans les libellés d'origine |
marche | vainqueur |
min_ecart_pct | ne garder que les mouvements d'au moins N % |
min_liquidite | mise max minimale, en EUR. Écarte les lignes sans liquidité connue plutôt que de traiter null comme un grand nombre. |
depuis_minutes | ne garder que ce qui a bougé récemment |
limite | nombre de lignes, 500 par défaut |
/api/sante /api/books /api/tournois /api/matchs /api/cotes?limite=20 min_ecart_pct=3 min_liquidite=500 /api/docs
Aucune traduction, aucun réordonnancement, aucun raccourci.
/api/tournois affiche atp masters 1000 paris parce
que c'est ce que la source envoie — seules la casse et les accents sont
aplanis, pour que la clé d'URL tienne.
Il y avait ici une table de traduction, un réordonnancement des mots de catégorie et un appariement flou des patronymes. Tout a été retiré : ça servait à réconcilier six bookmakers qui écrivaient le même tournoi de cinq façons. Edge est la source unique, et une source unique est cohérente avec elle-même.
Deviner une divergence qui n'existe pas, c'est exactement la façon dont un appariement finit par fondre deux joueurs différents — et un écart de cote entre deux joueurs différents ressemble exactement à une opportunité.
cle_match est construite sur l'event_id du flux,
pas sur les noms. C'est strictement mieux : l'identifiant ne bouge pas
quand un prénom est abrégé, il distingue deux frères sans comparer d'initiales,
et il n'a pas besoin de savoir si « Davidovich Fokina » est un nom ou
deux.
Pour obtenir le prix de chaque book, le service ouvre un betslip par sélection, lit les réponses, et le supprime immédiatement — y compris si une erreur survient en cours de route. Aucun pari n'est jamais placé.