The Bot Ledger
PT

Início / Guias

Guia da API da Kalshi: Conecte um Bot em 5 Passos

API da Kalshi para um bot, em Python: requisições assinadas, o formato do livro de ofertas, envio e cancelamento de ordens, execuções e histórico gratuito em candlesticks.

Server racks in a data center lit blue
Autenticação
API key id mais uma assinatura RSA sobre timestamp, método e caminho, em cada requisição.
Leitura
Uma API REST para mercados, livros, candlesticks; um websocket para execuções e deltas do livro.
Operação
Ordens limitadas em centavos ou dólares por contrato; o livro é em contratos.
Taxa de taker
0.07 x contracts x p x (1-p), arredondada para cima ao centavo por ordem. Makers não pagam nada em quase todas as séries.
Liquidação
Publicada pela exchange; mercados de cripto liquidam por uma média que a exchange transmite conforme acumula.
Geografia
Contas dos EUA. Programas de recompensa fechados para usuários fora dos EUA.
Docs
docs.kalshi.com

Cinco passos, simplificados de propósito. Os caminhos dos endpoints seguem a referência pública no momento da escrita; confira a documentação antes de depender deles.

  1. Assine cada requisição

    Gere uma API key nas configurações da conta; você recebe um key id e um arquivo de chave privada. Cada requisição carrega o id, um timestamp em milissegundos e uma assinatura RSA-PSS sobre o timestamp, o método HTTP e o caminho.

    import time, base64, requests
    from cryptography.hazmat.primitives import hashes, serialization
    from cryptography.hazmat.primitives.asymmetric import padding
    
    BASE = "https://api.elections.kalshi.com/trade-api/v2"
    key = serialization.load_pem_private_key(open("kalshi.pem", "rb").read(), password=None)
    
    def headers(method, path):
        ts = str(int(time.time() * 1000))
        msg = (ts + method + "/trade-api/v2" + path).encode()
        sig = key.sign(msg, padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.DIGEST_LENGTH), hashes.SHA256())
        return {"KALSHI-ACCESS-KEY": KEY_ID, "KALSHI-ACCESS-SIGNATURE": base64.b64encode(sig).decode(), "KALSHI-ACCESS-TIMESTAMP": ts}
  2. Liste os mercados de uma série

    r = requests.get(f"{BASE}/markets", params={"series_ticker": "KXBTC15M", "status": "open"}, headers=headers("GET", "/markets"))
    markets = r.json()["markets"]
    ticker = markets[0]["ticker"]
  3. Leia o livro de ofertas

    O endpoint do livro mudou de formato: a forma mais nova lista preço em dólares e tamanho por nível. Um parser escrito para os antigos arrays em centavos retorna profundidade zero sem nenhum erro.

    path = f"/markets/{ticker}/orderbook"
    ob = requests.get(BASE + path, headers=headers("GET", path)).json()["orderbook_fp"]
    yes_bids = ob["yes_dollars"]      # [[price_dollars, size], ...]
    no_bids  = ob["no_dollars"]
    best_yes_ask = 1 - float(no_bids[-1][0])   # um ask de YES é o espelho do melhor bid de NO

    A escada pode estar cruzada (melhor bid de YES mais melhor bid de NO acima de 1.00) em uma parcela perceptível dos snapshots. Proteja-se contra isso antes de cotar.

  4. Envie e cancele uma ordem

    order = {"ticker": ticker, "action": "buy", "side": "yes", "type": "limit",
             "count": 20, "yes_price": 85, "client_order_id": "win-1757500800"}
    r = requests.post(f"{BASE}/portfolio/orders", json=order, headers=headers("POST", "/portfolio/orders"))
    order_id = r.json()["order"]["order_id"]
    
    requests.delete(f"{BASE}/portfolio/orders/{order_id}", headers=headers("DELETE", f"/portfolio/orders/{order_id}"))
  5. Leia execuções e liquidações

    fills = requests.get(f"{BASE}/portfolio/fills", headers=headers("GET", "/portfolio/fills")).json()["fills"]
    settled = requests.get(f"{BASE}/portfolio/settlements", headers=headers("GET", "/portfolio/settlements")).json()

    Use o websocket para execuções em um bot ao vivo; consultar o endpoint de ordens por polling tem rate limit.

Histórico gratuito que ninguém menciona

O endpoint de candlesticks retorna o histórico de bid, ask e volume de qualquer mercado no intervalo escolhido, desde a listagem. Para pesquisa, substitui um logger que você ainda não escreveu.

path = f"/series/KXBTC15M/markets/{ticker}/candlesticks"
c = requests.get(BASE + path, params={"start_ts": t0, "end_ts": t1, "period_interval": 1}, headers=headers("GET", path)).json()