Tutorial de Bot de Polymarket · Capítulo 22 de 32
Bots NegRisk de múltiples resultados en Polymarket: mecánica de suma a 1, arbitraje entre patas cuando las patas YES no suman 1, cobertura entre patas y errores de ejecución específicos de mercados multiresultado.
Qué cubre este capítulo
Los mercados NegRisk de múltiples resultados son mutuamente excluyentes - exactamente uno se resuelve YES. Este capítulo es la capa de estrategia encima de la mecánica de ejecución del capítulo 11: cómo cubrirse entre patas, cuándo el arb de suma a 1 es real, y los bugs que más suelen golpear a los bots NegRisk en su primer despliegue.
- Recapitulación de NegRisk vs binary
- Invariante de suma a 1 y arbitraje
- Construcción de cobertura pata por pata
- Ejecución: flag neg_risk en órdenes
- Bugs comunes en bots NegRisk
- Código: snapshot de todas las patas y detección de suma menor a 1.00
Recapitulación de NegRisk vs binary
Binary: un mercado yes/no, dos tokens, suma 1.0. NegRisk: N resultados mutuamente excluyentes, N tokens, todas las patas YES suman ~1.0 a través del evento.
En ejecución, NegRisk requiere negRisk: true en cada orden (capítulo 11) y enruta mediante un contrato de exchange separado. En estrategia, NegRisk ofrece dos oportunidades únicas que los binary no tienen: arb entre patas cuando la suma se desvía de 1.0, y construcción de cobertura comprando múltiples patas YES.
Costos únicos de NegRisk: más patas = más impuesto por spread (cada pata que operas cuesta ~0.5-1c de spread), desviaciones suma a 1 más amplias en eventos ilíquidos (el arb está más disponible pero suele ser más pequeño).
Invariante de suma a 1 y arbitraje
La premisa del arb: si comprar todas las N patas YES cuesta menos de $1.00, tienes bloqueada una ganancia garantizada al resolver (una pata debe pagar $1.00; las demás van a $0).
En la práctica, la brecha de arb suele ser de 0-3c, consumida por spread + fees en cada pata, y desaparece en minutos tras la apertura. La capacidad está limitada por la liquidez de la pata más delgada.
El arb también está sujeto a modos de falla de resolución específicos: un resultado de "none of the above" que se resuelve explícitamente YES cuando ninguna candidata nombrada califica. Si el evento tiene una pata así y no la compraste, tu "cobertura completa" se queda corta respecto del payout real.
Construcción de cobertura pata por pata
Manteniendo una posición en una pata NegRisk, puedes cubrirte comprando YES en las patas competidoras en proporción. Si tienes Trump-YES a 0.50 y quieres cubrirte contra una derrota de Trump, compras un portafolio de las otras patas nombradas.
El peso de cobertura por pata ≈ probabilidad implícita actual de la pata condicionada a que Trump pierda. Aproximación: weight_i = price_i / (1 - trump_price).
La cobertura es imperfecta porque los precios usados son instantáneos y las probabilidades condicionales cambian a medida que llegan noticias. Rebalancea la cobertura semanalmente o ante noticias importantes. No lo sobreingenierices; el objetivo de la cobertura es reducir varianza, no eliminarla.
Ejecución: flag neg_risk en órdenes
El bug más común específico de NegRisk: olvidar negRisk: true en el payload de colocación de órdenes. La orden es aceptada por la API pero liquida incorrectamente porque se enruta al exchange CTF estándar en lugar del exchange NegRisk.
// CORRECT for NegRisk markets:
await client.createAndPostOrder(
{ tokenID, price, size, side: Side.BUY },
{ tickSize: '0.01', negRisk: true }, // <-- REQUIRED
OrderType.FOK
);
Fuente de verdad: market.negRisk desde Gamma API. Léelo; pásalo tal cual. Nunca hardcodees el flag por intuición.
Bugs comunes en bots NegRisk
Tomado de logs de depuración en producción a través de varios bots.
- Falta el flag negRisk: las órdenes se aceptan, pero la liquidación falla. Solución: imponer el flag en cada wrapper.
- Hedgear sin la pata "Other": en eventos con resultado "None of the above", el portafolio de cobertura que la excluye está incompleto. Solución: revisar siempre la pata Other al construir coberturas.
- Arb suma a 1 subdimensionado: el arber detecta la ventaja de 1c pero opera 5 shares por pata; la ganancia total es de 5 centavos antes de spread, neto negativo. Solución: dimensionar el arb para extraer dólares absolutos relevantes, no perseguir porcentajes llamativos.
- Precio de patas desactualizado: el bot consulta 3 precios de patas, tarda 200ms en total, y el precio de la última pata cambió durante la consulta. Solución: consultar todas las patas en paralelo + tratar el snapshot como una sola observación.
Código: snapshot de todas las patas y detección de suma menor a 1.00
Referencia: snapshot en paralelo de todas las patas YES de un evento NegRisk, detección de 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
La ejecución atómica de todas las patas es el problema más difícil y requiere FOK por pata + rollback ante fill parcial (patrón similar al código de stat-arb del capítulo 16).





