Skip to content
← Blog

Monitorea Kalshi y Polymarket con Python

dino.markets

Kalshi y Polymarket publican catálogos y modelos de precios separados, cada uno con sus propios identificadores. Un monitor útil entre plataformas tiene que establecer que dos listados se refieren a la misma afirmación antes de poner sus precios en una misma fila. Dino se encarga de ese paso de coincidencia y devuelve un objeto Market con los precios de ambas plataformas, así el programa en Python puede concentrarse en filtrar y mostrar los datos.

Este tutorial construye un pequeño monitor de terminal con el paquete oficial dino-markets. Clasifica los mercados emparejados abiertos a partir del snapshot REST retrasado disponible en el plan Free. La sección final agrega el stream en tiempo real para recibir actualizaciones a medida que ocurren.

Instala el cliente y crea una clave

Crea una clave de API gratuita y guárdala en una variable de entorno. Luego instala el paquete de Python:

export DINO_API_KEY=sk_live_...
pip install dino-markets

El cliente lee DINO_API_KEY automáticamente. La primera solicitud obtiene el catálogo de Markets abiertos y en vivo, ordenado por la mayor diferencia de precio observada entre plataformas:

from dino_markets import Dino

client = Dino()

catalog = client.markets(
    status="open,live",
    sort="-spread_pts",
    limit=25,
)

ranked = [
    market
    for market in catalog["markets"]
    if market["spread_pts"] is not None
]
if not ranked:
    raise SystemExit("No priced matched markets are available right now.")

print("as returned by the delayed REST snapshot")
for market in ranked:
    print(
        f'{market["spread_pts"]:>5.1f} pts  '
        f'{market["title"]:<42} '
        f'{market["category"]}/{market["market_type"]}'
    )

Los precios de REST se mantienen alrededor de dos minutos por detrás del precio en vivo en todos los planes. Eso hace que el resultado sea adecuado para explorar el catálogo y para investigación. También puede inicializar una vista local antes de que lleguen las actualizaciones del stream.

Muestra los precios de las dos plataformas en cada resultado

Cada Market mantiene la misma forma de outcomes[] en las categorías deportivas y no deportivas. Cada resultado lleva los precios de ambas plataformas más un campo cheaper. Un precio faltante es None, así que fíltralo explícitamente antes de calcular una diferencia:

def priced_outcomes(market):
    return [
        outcome
        for outcome in market["outcomes"]
        if outcome["kalshi"] is not None
        and outcome["polymarket"] is not None
    ]

for market in ranked[:10]:
    print(f'\n{market["title"]}')
    for outcome in priced_outcomes(market):
        gap = abs(outcome["kalshi"] - outcome["polymarket"]) * 100
        print(
            f'  {outcome["name"]:<24} '
            f'Kalshi {outcome["kalshi"]:.2f}  '
            f'Polymarket {outcome["polymarket"]:.2f}  '
            f'gap {gap:.1f} pts'
        )

spread_pts es una observación sobre los resultados del Market. No significa que exista un arbitraje ejecutable. Las Opportunities confirmadas viven en el recurso separado client.find_arbitrage() y llevan patas seleccionadas, comisiones, tamaño y la vigencia del snapshot.

Revisa la resolución antes de usar una fila

La respuesta de la lista es liviana. Obtén un Market por su id estable cuando el monitor necesite los términos de cada plataforma y los riesgos de resolución:

selected = ranked[0]
detail = client.market(selected["id"])

print(detail["title"], detail["match"], detail["signal"])
for risk in detail.get("settlement", {}).get("risks", []):
    print(risk["severity"], risk["event"], risk["note"])

El id es el identificador estable de Dino y es la clave correcta para las filas en caché. El título mostrado y los identificadores de cada plataforma pueden cambiar cuando un listado se vuelve a analizar. Vuelve a obtener el detalle cuando updated_at cambie, para que el monitor capte una identidad revisada o una nueva divulgación de resolución.

Agrega actualizaciones en tiempo real

Instala la dependencia opcional del stream y usa el helper de reconexión del SDK:

pip install "dino-markets[stream]"
import asyncio
from dino_markets import Dino, watch

client = Dino()

def on_publication(channel, frame):
    if frame.get("type") != "price":
        return
    print(
        channel,
        frame["id"],
        frame.get("spread_pts"),
        frame.get("priced_at"),
    )

asyncio.run(watch(client, on_publication))

El helper genera un ticket nuevo de corta duración en cada intento de conexión y recupera las publicaciones recientes después de una desconexión breve. Una clave Free recibe el canal sample curado. Todos los planes de pago reciben el stream completo de Market.

Mantén el bootstrap de REST en el programa incluso después de agregar las actualizaciones de WebSocket. Le da al monitor un snapshot inicial, mientras los frames del stream aplican los cambios de precio actuales a esos ids de Market en caché. La referencia de WebSocket documenta el comportamiento de recuperación, y la referencia del objeto Market cubre todos los campos usados aquí.

El monitor terminado lee datos emparejados y muestra las diferencias de precio. No coloca órdenes en ninguna de las dos plataformas. Son datos para desarrolladores, no asesoramiento financiero ni de trading. Crea una clave gratuita para ejecutar primero la versión de REST.

Más del blog