Skip to main content

Overview

The SDK provides three WebSocket-based event streaming methods:
  • listen - Subscribe to user-specific events (orders, positions, deposits, withdrawals)
  • listenToKalshiEvent - Subscribe to market ticker updates for a specific Kalshi event
  • listenToOrderbook - Subscribe to real-time orderbook snapshots and deltas for a specific market
All methods support automatic reconnection with exponential backoff.

User Events

Subscribe to events for a specific user address.

listen

listen(
  userAddress: string,
  onEvent: (event: BisonEvent) => void,
  options?: {
    onError?: (error: Error) => void;
    onConnect?: () => void;
    onDisconnect?: () => void;
    reconnect?: boolean;
  }
): () => void

Parameters

userAddress
string
required
User’s Ethereum address to listen for events
onEvent
function
required
Callback function that receives events as they occur
options.onError
function
Optional callback for handling WebSocket errors
options.onConnect
function
Optional callback invoked when WebSocket connection is established
options.onDisconnect
function
Optional callback invoked when WebSocket connection closes
options.reconnect
boolean
Enable automatic reconnection (default: true). Uses exponential backoff with max 5 attempts.

Returns

A disconnect function to stop listening and close the WebSocket connection.

Event Types

The callback receives one of the following event types: Order Events:
  • order_placed - New order submitted
  • order_filled - Order matched and executed
  • order_cancelled - Order cancelled
Position Events:
  • position_minted - New position tokens created
  • position_burned - Position tokens destroyed
USDC Events:
  • usdc_deposited - USDC deposited to vault
  • usdc_withdrawn - USDC withdrawn from vault
Market Events:
  • market_opened - Market opened for trading
  • market_closed - Market closed for trading
  • market_settled - Market outcome determined

Example

import { createBisonClient } from 'bison-sdk-ts';

const client = createBisonClient({ 
  baseUrl: 'https://api.bison.markets' 
});

const disconnect = client.listen(
  '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
  (event) => {
    switch (event.type) {
      case 'order_placed':
        console.log(`Order placed: ${event.orderId}`, {
          market: event.marketId,
          action: event.action,
          side: event.side,
          quantity: event.number,
          price: event.priceUusdc / 1_000_000,
        });
        break;
      
      case 'order_filled':
        console.log(`Order filled: ${event.orderId}`);
        break;
      
      case 'usdc_deposited':
        const usdcAmount = event.uusdcAmount / 1_000_000;
        console.log(`Deposited ${usdcAmount} USDC`);
        break;
      
      case 'position_minted':
        console.log(`Minted ${event.number} ${event.side} tokens for ${event.marketId}`);
        break;
      
      case 'market_settled':
        console.log(`Market ${event.marketId} settled:`, event.result);
        break;
      
      default:
        console.log('Received event:', event);
    }
  },
  {
    onConnect: () => console.log('Connected to event stream'),
    onDisconnect: () => console.log('Disconnected from event stream'),
    onError: (error) => console.error('WebSocket error:', error),
    reconnect: true,
  }
);

// Later, when you want to stop listening:
// disconnect();

Market Ticker Updates

Subscribe to real-time price and volume updates for a specific Kalshi event.

listenToKalshiEvent

listenToKalshiEvent(
  eventTicker: string,
  onTicker: (update: KalshiTickerUpdate) => void,
  options?: {
    onError?: (error: Error) => void;
    onConnect?: () => void;
    onDisconnect?: () => void;
    reconnect?: boolean;
  }
): () => void

Parameters

eventTicker
string
required
Kalshi event ticker (e.g., "KXFEDDECISION-26JAN")
onTicker
function
required
Callback function that receives ticker updates for markets in the event
options.onError
function
Optional callback for handling WebSocket errors
options.onConnect
function
Optional callback invoked when WebSocket connection is established
options.onDisconnect
function
Optional callback invoked when WebSocket connection closes
options.reconnect
boolean
Enable automatic reconnection (default: true). Uses exponential backoff with max 5 attempts.

Returns

A disconnect function to stop listening and close the WebSocket connection.

Ticker Update Fields

market_ticker
string
Market ticker identifier
yes_bid_uusdc
number
Current best bid price for YES side in µUSDC
yes_ask_uusdc
number
Current best ask price for YES side in µUSDC
no_bid_uusdc
number
Current best bid price for NO side in µUSDC
no_ask_uusdc
number
Current best ask price for NO side in µUSDC
last_price_uusdc
number
Last traded price in µUSDC
volume
number
Total trading volume
open_interest
number
Current open interest (outstanding contracts)

Example

const disconnect = client.listenToKalshiEvent(
  'KXFEDDECISION-26JAN',
  (update) => {
    console.log(`Market: ${update.market_ticker}`);
    
    if (update.yes_bid_uusdc && update.yes_ask_uusdc) {
      const yesBid = update.yes_bid_uusdc / 1_000_000;
      const yesAsk = update.yes_ask_uusdc / 1_000_000;
      console.log(`YES: $${yesBid} bid / $${yesAsk} ask`);
    }
    
    if (update.no_bid_uusdc && update.no_ask_uusdc) {
      const noBid = update.no_bid_uusdc / 1_000_000;
      const noAsk = update.no_ask_uusdc / 1_000_000;
      console.log(`NO: $${noBid} bid / $${noAsk} ask`);
    }
    
    if (update.last_price_uusdc) {
      console.log(`Last: $${update.last_price_uusdc / 1_000_000}`);
    }
    
    if (update.volume !== undefined) {
      console.log(`Volume: ${update.volume}`);
    }
    
    if (update.open_interest !== undefined) {
      console.log(`Open Interest: ${update.open_interest}`);
    }
  },
  {
    onConnect: () => console.log('Connected to Kalshi event stream'),
    reconnect: true,
  }
);

// Stop listening when done
// disconnect();

Orderbook Updates

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

listenToOrderbook

listenToOrderbook(
  marketTicker: string,
  onUpdate: (update: OrderbookUpdate) => void,
  options?: {
    onError?: (error: Error) => void;
    onConnect?: () => void;
    onDisconnect?: () => void;
    reconnect?: boolean;
  }
): () => void

Parameters

marketTicker
string
required
Kalshi market ticker (e.g., "KXBTC-100K-DEC31")
onUpdate
function
required
Callback function that receives orderbook updates (snapshots and deltas)
options.onError
function
Optional callback for handling WebSocket errors
options.onConnect
function
Optional callback invoked when WebSocket connection is established
options.onDisconnect
function
Optional callback invoked when WebSocket connection closes
options.reconnect
boolean
Enable automatic reconnection (default: true). Uses exponential backoff with max 5 attempts.

Returns

A disconnect function to stop listening and close the WebSocket connection.

Update Types

The callback receives one of two update types: Snapshot Update (OrderbookSnapshot):
  • Sent immediately upon connection
  • Contains the complete current orderbook state
  • Fields: type, market_ticker, yes, no
Delta Update (OrderbookDelta):
  • Sent for each orderbook change
  • Contains incremental updates to specific price levels
  • Fields: type, market_ticker, side, price_uusdc, delta

Example

import { createBisonClient } from 'bison-sdk-ts';

const client = createBisonClient({ 
  baseUrl: 'https://api.bison.markets' 
});

// Track orderbook state
const orderbook = {
  yes: new Map<number, number>(),  // price -> quantity
  no: new Map<number, number>()     // price -> quantity
};

const disconnect = client.listenToOrderbook(
  'KXBTC-100K-DEC31',
  (update) => {
    if (update.type === 'orderbook_snapshot') {
      // Initialize orderbook with full snapshot
      orderbook.yes.clear();
      orderbook.no.clear();
      
      update.yes?.forEach(level => {
        orderbook.yes.set(level.price_uusdc, level.quantity);
      });
      
      update.no?.forEach(level => {
        orderbook.no.set(level.price_uusdc, level.quantity);
      });
      
      console.log('Orderbook initialized');
      console.log(`YES levels: ${orderbook.yes.size}`);
      console.log(`NO levels: ${orderbook.no.size}`);
      
    } else if (update.type === 'orderbook_delta') {
      // Apply incremental update
      const book = update.side === 'yes' ? orderbook.yes : orderbook.no;
      const currentQty = book.get(update.price_uusdc) || 0;
      const newQty = currentQty + update.delta;
      
      if (newQty <= 0) {
        book.delete(update.price_uusdc);
      } else {
        book.set(update.price_uusdc, newQty);
      }
      
      const priceUSDC = (update.price_uusdc / 1_000_000).toFixed(2);
      console.log(`${update.side.toUpperCase()} @ $${priceUSDC}: ${newQty} contracts`);
    }
  },
  {
    onConnect: () => console.log('Connected to orderbook'),
    onError: (error) => console.error('Orderbook error:', error),
    reconnect: true,
  }
);

// Helper function to get best bid/ask
function getBestPrices() {
  const yesPrices = Array.from(orderbook.yes.keys());
  const noPrices = Array.from(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,
  };
}

// Use after receiving updates
setTimeout(() => {
  const prices = getBestPrices();
  console.log('Best prices:', {
    yes: prices.yesBid && prices.yesAsk 
      ? `${prices.yesBid / 1_000_000} / ${prices.yesAsk / 1_000_000}`
      : 'N/A',
    no: prices.noBid && prices.noAsk 
      ? `${prices.noBid / 1_000_000} / ${prices.noAsk / 1_000_000}`
      : 'N/A',
  });
}, 1000);

// Stop listening when done
// disconnect();

Connection Management

All three methods return a disconnect function that should be called to properly clean up the WebSocket connection:
const disconnectUser = client.listen(userAddress, onEvent);
const disconnectEvent = client.listenToKalshiEvent(eventTicker, onTicker);
const disconnectOrderbook = client.listenToOrderbook(marketTicker, onUpdate);

// Later, when component unmounts or user logs out:
disconnectUser();
disconnectEvent();
disconnectOrderbook();

Automatic Reconnection

The SDK automatically handles connection failures with exponential backoff:
  • Initial reconnect delay: 1 second
  • Maximum reconnect delay: 30 seconds
  • Maximum reconnect attempts: 5
  • Heartbeat interval: 30 seconds
When the maximum number of reconnect attempts is reached, the connection will stop trying to reconnect. You can manually reconnect by calling the listen method again.

Error Handling

const disconnect = client.listen(
  userAddress,
  (event) => {
    console.log('Event:', event);
  },
  {
    onError: (error) => {
      // Handle WebSocket errors
      console.error('Connection error:', error.message);
      
      // Optionally notify user or trigger fallback behavior
      showErrorNotification('Lost connection to event stream');
    },
    onDisconnect: () => {
      // Connection closed
      console.log('Disconnected. Will attempt to reconnect...');
    }
  }
);

Important Notes

  • WebSocket connections are automatically kept alive with periodic heartbeat messages
  • Events are delivered in real-time as they occur on the blockchain or Kalshi platform
  • Multiple subscriptions can be active simultaneously (user events, market tickers, orderbooks)
  • Reconnection is enabled by default but can be disabled by setting reconnect: false
  • Always call the disconnect function to avoid memory leaks when cleaning up
  • For orderbook subscriptions, always process the initial snapshot before applying deltas
  • Orderbook delta updates are cumulative - add them to existing quantities, don’t replace