> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bison.markets/llms.txt
> Use this file to discover all available pages before exploring further.

# Orderbook Data

> Real-time orderbook snapshots and deltas for a specific market

## Overview

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

<Info>
  To avoid ambiguity, we denote the smallest possible multiple of USDC (0.000001 USDC) as one `uusdc`,
  which stands for µUSDC (micro-USDC), and denote the smallest possible multiple of a contract (0.01 contract)
  as one `ccontract` (centicontract).

  User-facing USDC balances are specified as fixed-point strings (e.g. `"1.2625"` for USDC). Contract quantities
  in the API and SDK are specified as integer `ccontracts` strings (e.g. `"1050"` for 10.50 contracts at precision 2).
</Info>

## Connection

```
wss://api.bison.markets/ws/kalshi/orderbook/{marketTicker}
```

<ParamField path="marketTicker" type="string" required>
  The Kalshi market ticker (e.g., `KXBTC-100K-DEC31`)
</ParamField>

## Example Connection

```javascript theme={null}
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 -> ccontracts (bigint)
  no: new Map()   // price_uusdc -> ccontracts (bigint)
};

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, BigInt(level.ccontracts));
    });
  }
  
  if (snapshot.no) {
    snapshot.no.forEach(level => {
      orderbook.no.set(level.price_uusdc, BigInt(level.ccontracts));
    });
  }
  
  console.log("Orderbook initialized:", orderbook);
}

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

## Message Types

The Orderbook WebSocket sends two types of messages:

### Snapshot Message

Sent immediately upon connection, providing the complete current orderbook state.

```json theme={null}
{
  "type": "orderbook_snapshot",
  "market_ticker": "KXBTC-100K-DEC31",
  "yes": [
    { "price_uusdc": "750000", "ccontracts": "100" },
    { "price_uusdc": "740000", "ccontracts": "250" },
    { "price_uusdc": "730000", "ccontracts": "500" }
  ],
  "no": [
    { "price_uusdc": "260000", "ccontracts": "150" },
    { "price_uusdc": "270000", "ccontracts": "300" },
    { "price_uusdc": "280000", "ccontracts": "400" }
  ]
}
```

<ResponseField name="type" type="string">
  Always `"orderbook_snapshot"`
</ResponseField>

<ResponseField name="market_ticker" type="string">
  Market ticker for this orderbook
</ResponseField>

<ResponseField name="yes" type="array" optional>
  Array of YES side price levels, ordered by price

  <Expandable title="Level Object">
    <ResponseField name="price_uusdc" type="string">
      Price level in µUSDC (integer string)
    </ResponseField>

    <ResponseField name="ccontracts" type="string">
      Total ccontracts available at this price (integer string; 1 contract = 100 ccontracts)
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="no" type="array" optional>
  Array of NO side price levels, ordered by price

  Structure is the same as `yes` array.
</ResponseField>

### Delta Message

Sent for each orderbook change, providing an incremental update.

```json theme={null}
{
  "type": "orderbook_delta",
  "market_ticker": "KXBTC-100K-DEC31",
  "side": "yes",
  "price_uusdc": "750000",
  "delta": "50"
}
```

<ResponseField name="type" type="string">
  Always `"orderbook_delta"`
</ResponseField>

<ResponseField name="market_ticker" type="string">
  Market ticker for this update
</ResponseField>

<ResponseField name="side" type="string">
  Either `"yes"` or `"no"`
</ResponseField>

<ResponseField name="price_uusdc" type="string">
  Price level being updated (µUSDC integer string)
</ResponseField>

<ResponseField name="delta" type="string">
  Change in ccontracts (integer string; positive for additions, negative for removals)
</ResponseField>

## Maintaining Accurate State

<Note>
  To maintain an accurate orderbook:

  1. Initialize with the snapshot on connection
  2. Apply each delta by adding it to the current ccontracts at that price level
  3. Remove price levels when their ccontracts reaches zero or below
</Note>

## Example: Orderbook Display

```javascript theme={null}
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, BigInt(level.ccontracts));
      });
    }
    
    if (snapshot.no) {
      snapshot.no.forEach(level => {
        this.orderbook.no.set(level.price_uusdc, BigInt(level.ccontracts));
      });
    }
    
    this.render();
  }
  
  applyDelta(delta) {
    const book = delta.side === "yes" ? this.orderbook.yes : this.orderbook.no;
    const currentCcontracts = book.get(delta.price_uusdc) || 0n;
    const newCcontracts = currentCcontracts + BigInt(delta.delta);
    
    if (newCcontracts <= 0n) {
      book.delete(delta.price_uusdc);
    } else {
      book.set(delta.price_uusdc, newCcontracts);
    }
    
    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) => Number(b) - Number(a));
    yesPrices.forEach(price => {
      const ccontracts = this.orderbook.yes.get(price) || 0n;
      const priceUSDC = (Number(price) / 1_000_000).toFixed(2);
      console.log(`  ${priceUSDC}: ${ccontracts.toString()} ccontracts`);
    });
    
    // NO side (sorted descending by price)
    console.log("\nNO:");
    const noPrices = Array.from(this.orderbook.no.keys()).sort((a, b) => Number(b) - Number(a));
    noPrices.forEach(price => {
      const ccontracts = this.orderbook.no.get(price) || 0n;
      const priceUSDC = (Number(price) / 1_000_000).toFixed(2);
      console.log(`  ${priceUSDC}: ${ccontracts.toString()} ccontracts`);
    });
  }
  
  getBestBidAsk() {
    const yesPrices = Array.from(this.orderbook.yes.keys()).map(Number);
    const noPrices = Array.from(this.orderbook.no.keys()).map(Number);
    
    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

<CardGroup cols={2}>
  <Card title="Order Book Depth" icon="layer-group">
    Display full market depth with all price levels
  </Card>

  <Card title="Trading Interfaces" icon="chart-candlestick">
    Build professional trading UIs with real-time order flow
  </Card>

  <Card title="Market Making" icon="robot">
    Monitor liquidity for algorithmic trading strategies
  </Card>

  <Card title="Price Discovery" icon="magnifying-glass">
    Analyze order book imbalances and market sentiment
  </Card>
</CardGroup>

## Best Practices

<Steps>
  <Step title="Initialize with Snapshot">
    Always start by processing the initial snapshot message to establish baseline state.
  </Step>

  <Step title="Apply Deltas Incrementally">
    Add delta values to existing ccontracts rather than replacing them.
  </Step>

  <Step title="Clean Up Zero Ccontracts">
    Remove price levels from your orderbook when their ccontracts reach zero or below.
  </Step>

  <Step title="Sort Price Levels">
    Sort bids descending and asks ascending for proper display.
  </Step>

  <Step title="Handle Reconnection">
    On reconnection, the snapshot will reset your state automatically.
  </Step>
</Steps>
