Polymarket Bot Tutorial · Chapitre 22 sur 32
Bots multi-outcome NegRisk sur Polymarket : mécanismes de sum-to-1, arbitrage de legs lorsque les legs YES ne totalisent pas 1, hedging entre legs, et pièges d'exécution propres aux marchés multi-outcome.
Ce que couvre ce chapitre
Les marchés NegRisk multi-outcome sont mutuellement exclusifs - exactement un seul se résout en YES. Ce chapitre traite de la couche stratégie au-dessus des mécanismes d'exécution du chapitre 11 : comment hedger entre les legs, quand l'arbitrage sum-to-1 est réel, et les bugs que la plupart des bots NegRisk rencontrent lors du premier déploiement.
- Récapitulatif NegRisk vs binary
- Invariant sum-to-1 et arbitrage
- Construction de hedge leg par leg
- Exécution : flag neg_risk dans les orders
- Bugs courants dans les bots NegRisk
- Code : snapshot de tous les legs et détection d'une somme inférieure à 1.00
Récapitulatif NegRisk vs binary
Binary : un marché yes/no, deux tokens, somme de 1.0. NegRisk : N outcomes mutuellement exclusifs, N tokens, tous les legs YES totalisent ~1.0 sur l'événement.
Du point de vue de l'exécution, NegRisk nécessite negRisk: true sur chaque order (chapitre 11) et passe par un smart contract d'exchange distinct. Du point de vue de la stratégie, NegRisk offre deux opportunités uniques que les binaries n'ont pas : l'arb inter-leg lorsque la somme s'écarte de 1.0, et la construction de hedge en achetant plusieurs legs YES.
Coûts propres à NegRisk : plus il y a de legs, plus la spread tax est élevée (chaque leg tradé coûte environ 0.5-1c de spread), et plus les déviations sum-to-1 sont larges sur les événements illiquides (l'arb est plus souvent disponible, mais plus petit).
Invariant sum-to-1 et arbitrage
Le principe de l'arb : si acheter tous les N legs YES coûte moins de 1.00 $, vous verrouillez un profit garanti à la résolution (un leg doit payer 1.00 $ ; les autres tombent à 0).
En pratique, l'écart d'arb est généralement de 0 à 3c, absorbé par la spread + les fees sur chaque leg, et disparaît en quelques minutes après l'ouverture. La capacité est limitée par la liquidité du leg le plus mince.
L'arb est aussi soumis à des modes d'échec de résolution spécifiques : un outcome "none of the above" qui se résout explicitement en YES lorsqu'aucun candidat nommé ne correspond. Si l'événement comporte un tel leg et que vous ne l'avez pas acheté, votre "hedge complet" ne couvre pas le payout réel.
Construction de hedge leg par leg
Si vous détenez une position sur un leg NegRisk, vous pouvez hedger en achetant les YES sur les legs concurrents en proportion. Si vous détenez Trump-YES à 0.50 et voulez vous couvrir contre une perte de Trump, vous achetez un portfolio des autres legs nommés.
Poids de hedge par leg ≈ probabilité implicite actuelle du leg conditionnelle à une défaite de Trump. Approximation : weight_i = price_i / (1 - trump_price).
Le hedge est imparfait parce que les prix utilisés sont pris à un instant donné et que les probabilités conditionnelles évoluent à mesure que les news arrivent. Rééquilibrez le hedge chaque semaine ou lors d'une information majeure. N'allez pas trop loin dans l'ingénierie ; le but du hedge est de réduire la variance, pas de l'éliminer.
Exécution : flag neg_risk dans les orders
Le bug spécifique NegRisk le plus courant : oublier negRisk: true dans le payload de placement d'order. L'order est accepté par l'API mais se settle incorrectement parce qu'il est routé vers l'exchange CTF standard au lieu de l'exchange NegRisk.
// CORRECT for NegRisk markets:
await client.createAndPostOrder(
{ tokenID, price, size, side: Side.BUY },
{ tickSize: '0.01', negRisk: true }, // <-- REQUIRED
OrderType.FOK
);
Source de vérité : market.negRisk depuis l'API Gamma. Lisez-le ; transmettez-le. Ne hardcodez jamais le flag en devinant.
Bugs courants dans les bots NegRisk
À partir des logs de debug de production sur plusieurs bots.
- Flag negRisk manquant : orders acceptés, settlement échoue. Solution : imposer le flag dans chaque wrapper.
- Hedging sans le leg "Other" : dans les événements avec un outcome "None of the above", le portfolio de hedge qui l'exclut est incomplet. Solution : vérifier systématiquement le leg Other lors de la construction des hedges.
- Arb sum-to-1 sous-dimensionné : l'arber repère l'écart de 1c mais trade 5 shares par leg ; le profit total est de 5 cents avant spread, donc négatif net. Solution : dimensionner l'arb pour générer des dollars absolus significatifs, pas pour courir après des pourcentages spectaculaires.
- Prix de leg obsolètes : le bot récupère 3 prix de legs, cela prend 200ms au total, et le prix du dernier leg change pendant la récupération. Solution : récupérer tous les legs en parallèle + traiter le snapshot comme une seule observation.
Code : snapshot de tous les legs et détection d'une somme inférieure à 1.00
Référence : snapshot en parallèle de tous les legs YES d'un événement NegRisk, détection de l'arb.
import asyncio, aiohttp
async def fetch_leg_ask(session, token_id):
async with session.get(f"https://clob.polymarket.com/book?token_id={token_id}") as r:
d = await r.json()
asks = d.get("asks", [])
return float(asks[0]["price"]) if asks else None
async def check_arb(event_slug):
event = await fetch_event(event_slug)
if not event["markets"][0]["negRisk"]: return None
legs = []
for m in event["markets"]:
toks = json.loads(m["clobTokenIds"])
yes_token = toks[0]
legs.append(yes_token)
async with aiohttp.ClientSession() as s:
asks = await asyncio.gather(*[fetch_leg_ask(s, t) for t in legs])
if any(a is None for a in asks): return None
total = sum(asks)
if total < 0.97:
return {"edge": 1 - total, "legs": list(zip(legs, asks))}
return None
L'exécution atomique de tous les legs est le problème plus difficile et nécessite un FOK par leg + rollback en cas de fill partiel (même pattern que le code stat-arb du chapitre 16).





