Skip to main content

Overview

Connect to receive real-time orderbook data for a specific market, including initial snapshots and incremental updates.

Connection

wss://api.bison.markets/ws/kalshi/orderbook/{marketTicker}
marketTicker
string
required
The Kalshi market ticker (e.g., KXBTC-100K-DEC31)

Example Connection

const marketTicker = "KXBTC-100K-DEC31";
const ws = new WebSocket(
  `wss://api.bison.markets/ws/kalshi/orderbook/${marketTicker}`
);

// Track orderbook state
let orderbook = {
  yes: new Map(), // price_uusdc -> quantity
  no: new Map()   // price_uusdc -> quantity
};

ws.onopen = () => {
  console.log(`Connected to ${marketTicker} orderbook`);
  
  // Send heartbeat every 30 seconds
  setInterval(() => {
    if (ws.readyState === WebSocket.OPEN) {
      ws.send(JSON.stringify({ type: "ping" }));
    }
  }, 30000);
};

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  
  if (data.type === "orderbook_snapshot") {
    handleSnapshot(data);
  } else if (data.type === "orderbook_delta") {
    handleDelta(data);
  }
};

function handleSnapshot(snapshot) {
  // Reset orderbook with full snapshot
  orderbook.yes.clear();
  orderbook.no.clear();
  
  if (snapshot.yes) {
    snapshot.yes.forEach(level => {
      orderbook.yes.set(level.price_uusdc, level.quantity);
    });
  }
  
  if (snapshot.no) {
    snapshot.no.forEach(level => {
      orderbook.no.set(level.price_uusdc, level.quantity);
    });
  }
  
  console.log("Orderbook initialized:", orderbook);
}

function handleDelta(delta) {
  // Apply incremental update
  const book = delta.side === "yes" ? orderbook.yes : orderbook.no;
  const currentQty = book.get(delta.price_uusdc) || 0;
  const newQty = currentQty + delta.delta;
  
  if (newQty <= 0) {
    book.delete(delta.price_uusdc);
  } else {
    book.set(delta.price_uusdc, newQty);
  }
  
  console.log(`Orderbook updated: ${delta.side} @ ${delta.price_uusdc}: ${newQty}`);
}

Message Types

The Orderbook WebSocket sends two types of messages:

Snapshot Message

Sent immediately upon connection, providing the complete current orderbook state.
{
  "type": "orderbook_snapshot",
  "market_ticker": "KXBTC-100K-DEC31",
  "yes": [
    { "price_uusdc": 750000, "quantity": 100 },
    { "price_uusdc": 740000, "quantity": 250 },
    { "price_uusdc": 730000, "quantity": 500 }
  ],
  "no": [
    { "price_uusdc": 260000, "quantity": 150 },
    { "price_uusdc": 270000, "quantity": 300 },
    { "price_uusdc": 280000, "quantity": 400 }
  ]
}
type
string
Always "orderbook_snapshot"
market_ticker
string
Market ticker for this orderbook
yes
array
Array of YES side price levels, ordered by price
no
array
Array of NO side price levels, ordered by priceStructure is the same as yes array.

Delta Message

Sent for each orderbook change, providing an incremental update.
{
  "type": "orderbook_delta",
  "market_ticker": "KXBTC-100K-DEC31",
  "side": "yes",
  "price_uusdc": 750000,
  "delta": 50
}
type
string
Always "orderbook_delta"
market_ticker
string
Market ticker for this update
side
string
Either "yes" or "no"
price_uusdc
number
Price level being updated
delta
number
Change in quantity (positive for additions, negative for removals)

Maintaining Accurate State

To maintain an accurate orderbook:
  1. Initialize with the snapshot on connection
  2. Apply each delta by adding it to the current quantity at that price level
  3. Remove price levels when their quantity reaches zero or below

Example: Orderbook Display

class OrderbookManager {
  constructor(marketTicker) {
    this.marketTicker = marketTicker;
    this.orderbook = {
      yes: new Map(),
      no: new Map()
    };
    this.ws = null;
  }
  
  connect() {
    this.ws = new WebSocket(
      `wss://api.bison.markets/ws/kalshi/orderbook/${this.marketTicker}`
    );
    
    this.ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      
      if (data.type === "orderbook_snapshot") {
        this.applySnapshot(data);
      } else if (data.type === "orderbook_delta") {
        this.applyDelta(data);
      }
    };
    
    // Heartbeat
    setInterval(() => {
      if (this.ws.readyState === WebSocket.OPEN) {
        this.ws.send(JSON.stringify({ type: "ping" }));
      }
    }, 30000);
  }
  
  applySnapshot(snapshot) {
    this.orderbook.yes.clear();
    this.orderbook.no.clear();
    
    if (snapshot.yes) {
      snapshot.yes.forEach(level => {
        this.orderbook.yes.set(level.price_uusdc, level.quantity);
      });
    }
    
    if (snapshot.no) {
      snapshot.no.forEach(level => {
        this.orderbook.no.set(level.price_uusdc, level.quantity);
      });
    }
    
    this.render();
  }
  
  applyDelta(delta) {
    const book = delta.side === "yes" ? this.orderbook.yes : this.orderbook.no;
    const currentQty = book.get(delta.price_uusdc) || 0;
    const newQty = currentQty + delta.delta;
    
    if (newQty <= 0) {
      book.delete(delta.price_uusdc);
    } else {
      book.set(delta.price_uusdc, newQty);
    }
    
    this.render();
  }
  
  render() {
    console.log(`\n=== ${this.marketTicker} Orderbook ===`);
    
    // YES side (sorted descending by price)
    console.log("\nYES:");
    const yesPrices = Array.from(this.orderbook.yes.keys()).sort((a, b) => b - a);
    yesPrices.forEach(price => {
      const qty = this.orderbook.yes.get(price);
      const priceUSDC = (price / 1_000_000).toFixed(2);
      console.log(`  ${priceUSDC}: ${qty} contracts`);
    });
    
    // NO side (sorted descending by price)
    console.log("\nNO:");
    const noPrices = Array.from(this.orderbook.no.keys()).sort((a, b) => b - a);
    noPrices.forEach(price => {
      const qty = this.orderbook.no.get(price);
      const priceUSDC = (price / 1_000_000).toFixed(2);
      console.log(`  ${priceUSDC}: ${qty} contracts`);
    });
  }
  
  getBestBidAsk() {
    const yesPrices = Array.from(this.orderbook.yes.keys());
    const noPrices = Array.from(this.orderbook.no.keys());
    
    return {
      yesBid: yesPrices.length > 0 ? Math.max(...yesPrices) : null,
      yesAsk: yesPrices.length > 0 ? Math.min(...yesPrices) : null,
      noBid: noPrices.length > 0 ? Math.max(...noPrices) : null,
      noAsk: noPrices.length > 0 ? Math.min(...noPrices) : null,
    };
  }
  
  disconnect() {
    if (this.ws) {
      this.ws.close();
    }
  }
}

// Usage
const manager = new OrderbookManager("KXBTC-100K-DEC31");
manager.connect();

// Later, get best bid/ask
const { yesBid, yesAsk, noBid, noAsk } = manager.getBestBidAsk();
console.log(`Best YES: ${yesBid} / ${yesAsk}`);
console.log(`Best NO: ${noBid} / ${noAsk}`);

Use Cases

Order Book Depth

Display full market depth with all price levels

Trading Interfaces

Build professional trading UIs with real-time order flow

Market Making

Monitor liquidity for algorithmic trading strategies

Price Discovery

Analyze order book imbalances and market sentiment

Best Practices

1

Initialize with Snapshot

Always start by processing the initial snapshot message to establish baseline state.
2

Apply Deltas Incrementally

Add delta values to existing quantities rather than replacing them.
3

Clean Up Zero Quantities

Remove price levels from your orderbook when their quantity reaches zero or below.
4

Sort Price Levels

Sort bids descending and asks ascending for proper display.
5

Handle Reconnection

On reconnection, the snapshot will reset your state automatically.