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

# Ticker Data

> Stream comprehensive market stats including last price, funding rates, and open interest in real-time.

## Overview

The ticker channel provides comprehensive real-time market statistics including prices, funding rates, and open interest for all supported trading pairs. This is the most complete market data stream, combining pricing data with market activity metrics.

## Channel information

| Property             | Value                           |
| -------------------- | ------------------------------- |
| **Channel Name**     | `ticker`                        |
| **Authentication**   | Required                        |
| **Update Frequency** | Real-time                       |
| **Data Type**        | Comprehensive market statistics |

## Subscribe to ticker data

<RequestExample>
  ```json Subscribe theme={null}
  {
    "op": "subscribe",
    "args": [
      { "channel": "ticker", "symbol": "US500-BTC" },
      { "channel": "ticker", "symbol": "GOLD-BTC" },
      { "channel": "ticker", "symbol": "USDT-BTC" }
    ]
  }
  ```
</RequestExample>

## Response format

<ResponseExample>
  ```json Ticker Snapshot theme={null}
  {
    "topic": "ticker.US500-BTC",
    "type": "snapshot",
    "createdTime": 1640995200000000000,
    "data": {
      "symbol": "US500-BTC",
      "lastPrice": "0.00432500",
      "indexPrice": "0.00432450",
      "markPrice": "0.00432475",
      "bestBid": "0.00432450",
      "bestAsk": "0.00432500",
      "bestBidQty": "125.50",
      "bestAskQty": "156.75",
      "openInterest": "500.00",
      "openInterestValue": "2.16250000",
      "fundingRate": "0.0001",
      "timestamp": 1640995200000000000,
      "unit": "btc"
    }
  }
  ```
</ResponseExample>

<ResponseField name="data" type="object">
  Comprehensive ticker data

  <Expandable title="Ticker properties">
    <ResponseField name="symbol" type="string">
      Trading pair symbol
    </ResponseField>

    <ResponseField name="lastPrice" type="string">
      Last traded price (null if no trades yet)
    </ResponseField>

    <ResponseField name="indexPrice" type="string">
      Index price for the instrument (null if unavailable)
    </ResponseField>

    <ResponseField name="markPrice" type="string">
      Mark price used for liquidations and unrealized PnL (null if unavailable)
    </ResponseField>

    <ResponseField name="bestBid" type="string">
      Best bid price (null if no bids)
    </ResponseField>

    <ResponseField name="bestAsk" type="string">
      Best ask price (null if no asks)
    </ResponseField>

    <ResponseField name="bestBidQty" type="string">
      Quantity at best bid (null if no bids)
    </ResponseField>

    <ResponseField name="bestAskQty" type="string">
      Quantity at best ask (null if no asks)
    </ResponseField>

    <ResponseField name="openInterest" type="string">
      Total open interest in contracts (null if unavailable)
    </ResponseField>

    <ResponseField name="openInterestValue" type="string">
      Total open interest value in BTC (null if unavailable)
    </ResponseField>

    <ResponseField name="fundingRate" type="string">
      Current funding rate for perpetual contracts (null if unavailable)
    </ResponseField>

    <ResponseField name="timestamp" type="integer">
      Update timestamp in nanoseconds
    </ResponseField>

    <ResponseField name="unit" type="string">
      Unit for price values (btc or sats)
    </ResponseField>
  </Expandable>
</ResponseField>

## Implementation examples

### JavaScript ticker monitor

```javascript theme={null}
class TickerMonitor {
  constructor() {
    this.tickers = {};
    this.listeners = [];
  }

  connect() {
    this.ws = new WebSocket('wss://ws.roxom.com/ws');
    
    this.ws.onopen = () => {
      console.log('Connected to Ticker stream');
      this.subscribeToSymbols(['US500-BTC', 'GOLD-BTC', 'USDT-BTC']);
    };

    this.ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      
      if (data.topic && data.topic.startsWith('ticker.')) {
        this.handleTickerUpdate(data);
      }
    };
  }

  subscribeToSymbols(symbols) {
    const args = symbols.map(symbol => ({
      channel: 'ticker',
      symbol: symbol
    }));

    this.ws.send(JSON.stringify({
      op: 'subscribe',
      args: args
    }));
  }

  handleTickerUpdate(message) {
    const symbol = message.data.symbol;
    const ticker = message.data;

    // Update local ticker cache
    this.tickers[symbol] = {
      symbol: symbol,
      lastPrice: parseFloat(ticker.lastPrice || 0),
      indexPrice: parseFloat(ticker.indexPrice || 0),
      markPrice: parseFloat(ticker.markPrice || 0),
      bestBid: parseFloat(ticker.bestBid || 0),
      bestAsk: parseFloat(ticker.bestAsk || 0),
      bestBidQty: parseFloat(ticker.bestBidQty || 0),
      bestAskQty: parseFloat(ticker.bestAskQty || 0),
      openInterest: parseFloat(ticker.openInterest || 0),
      openInterestValue: parseFloat(ticker.openInterestValue || 0),
      fundingRate: parseFloat(ticker.fundingRate || 0),
      timestamp: ticker.timestamp,
      lastUpdated: Date.now()
    };

    // Calculate spread
    if (ticker.bestBid && ticker.bestAsk) {
      const spread = this.tickers[symbol].bestAsk - this.tickers[symbol].bestBid;
      const midPrice = (this.tickers[symbol].bestBid + this.tickers[symbol].bestAsk) / 2;
      this.tickers[symbol].spread = spread;
      this.tickers[symbol].spreadPercent = (spread / midPrice) * 100;
    }

    console.log(`${symbol} Ticker Update:`);
    console.log(`  Last: ${ticker.lastPrice} | Mark: ${ticker.markPrice} | Index: ${ticker.indexPrice}`);
    console.log(`  Bid: ${ticker.bestBid} (${ticker.bestBidQty}) | Ask: ${ticker.bestAsk} (${ticker.bestAskQty})`);
    console.log(`  OI: ${ticker.openInterest} contracts (${ticker.openInterestValue} BTC)`);
    console.log(`  Funding Rate: ${(parseFloat(ticker.fundingRate || 0) * 100).toFixed(4)}%`);

    // Notify listeners
    this.notifyListeners(symbol, this.tickers[symbol]);
  }

  addListener(callback) {
    this.listeners.push(callback);
  }

  notifyListeners(symbol, ticker) {
    this.listeners.forEach(callback => {
      try {
        callback(symbol, ticker);
      } catch (error) {
        console.error('Listener error:', error);
      }
    });
  }

  getTicker(symbol) {
    return this.tickers[symbol] || null;
  }

  getAllTickers() {
    return { ...this.tickers };
  }
}

// Usage
const tickerMonitor = new TickerMonitor();

tickerMonitor.addListener((symbol, ticker) => {
  // Check for high funding rates
  if (Math.abs(ticker.fundingRate) > 0.001) {
    console.log(`⚠️  High funding rate on ${symbol}: ${(ticker.fundingRate * 100).toFixed(4)}%`);
  }
  
  // Check for large open interest
  if (ticker.openInterestValue > 100) {
    console.log(`📊 Large OI on ${symbol}: ${ticker.openInterestValue.toFixed(2)} BTC`);
  }
});

tickerMonitor.connect();
```

### Python ticker tracker

```python theme={null}
import websocket
import json
from typing import Dict, Any, Callable, Optional
from datetime import datetime

class TickerTracker:
    def __init__(self):
        self.tickers: Dict[str, Dict] = {}
        self.listeners: List[Callable] = []
        self.ws = None
        
    def connect(self):
        self.ws = websocket.WebSocketApp(
            "wss://ws.roxom.com/ws",
            on_open=self.on_open,
            on_message=self.on_message,
            on_error=self.on_error,
            on_close=self.on_close
        )
        
        self.ws.run_forever()
    
    def on_open(self, ws):
        print("📡 Connected to Ticker stream")
        self.subscribe_to_symbols(['US500-BTC', 'GOLD-BTC', 'USDT-BTC'])
    
    def subscribe_to_symbols(self, symbols):
        subscribe_msg = {
            "op": "subscribe",
            "args": [{"channel": "ticker", "symbol": symbol} for symbol in symbols]
        }
        self.ws.send(json.dumps(subscribe_msg))
    
    def on_message(self, ws, message):
        try:
            data = json.loads(message)
            
            if data.get('topic', '').startswith('ticker.'):
                self.handle_ticker_update(data)
        except json.JSONDecodeError as e:
            print(f"❌ JSON decode error: {e}")
    
    def handle_ticker_update(self, message):
        ticker_data = message['data']
        symbol = ticker_data['symbol']
        
        # Parse ticker data with null handling
        last_price = float(ticker_data['lastPrice']) if ticker_data.get('lastPrice') else None
        index_price = float(ticker_data['indexPrice']) if ticker_data.get('indexPrice') else None
        mark_price = float(ticker_data['markPrice']) if ticker_data.get('markPrice') else None
        best_bid = float(ticker_data['bestBid']) if ticker_data.get('bestBid') else None
        best_ask = float(ticker_data['bestAsk']) if ticker_data.get('bestAsk') else None
        best_bid_qty = float(ticker_data['bestBidQty']) if ticker_data.get('bestBidQty') else None
        best_ask_qty = float(ticker_data['bestAskQty']) if ticker_data.get('bestAskQty') else None
        open_interest = float(ticker_data['openInterest']) if ticker_data.get('openInterest') else None
        open_interest_value = float(ticker_data['openInterestValue']) if ticker_data.get('openInterestValue') else None
        funding_rate = float(ticker_data['fundingRate']) if ticker_data.get('fundingRate') else None
        
        # Calculate derived values
        spread = None
        spread_percent = None
        if best_bid and best_ask:
            spread = best_ask - best_bid
            mid_price = (best_bid + best_ask) / 2
            spread_percent = (spread / mid_price) * 100 if mid_price > 0 else 0
        
        # Update ticker cache
        self.tickers[symbol] = {
            'symbol': symbol,
            'last_price': last_price,
            'index_price': index_price,
            'mark_price': mark_price,
            'best_bid': best_bid,
            'best_ask': best_ask,
            'best_bid_qty': best_bid_qty,
            'best_ask_qty': best_ask_qty,
            'open_interest': open_interest,
            'open_interest_value': open_interest_value,
            'funding_rate': funding_rate,
            'spread': spread,
            'spread_percent': spread_percent,
            'timestamp': ticker_data['timestamp'],
            'last_updated': datetime.now()
        }
        
        # Display ticker update
        print(f"\n{symbol} Ticker Update:")
        print(f"  Last: {last_price:.8f} | Mark: {mark_price:.8f} | Index: {index_price:.8f}")
        print(f"  Bid: {best_bid:.8f} ({best_bid_qty}) | Ask: {best_ask:.8f} ({best_ask_qty})")
        print(f"  OI: {open_interest} contracts ({open_interest_value:.8f} BTC)")
        print(f"  Funding Rate: {(funding_rate * 100):.4f}%")
        
        # Notify listeners
        self.notify_listeners(symbol, self.tickers[symbol])
    
    def add_listener(self, callback):
        self.listeners.append(callback)
    
    def notify_listeners(self, symbol, ticker):
        for callback in self.listeners:
            try:
                callback(symbol, ticker)
            except Exception as e:
                print(f"❌ Listener error: {e}")
    
    def get_ticker(self, symbol) -> Optional[Dict]:
        return self.tickers.get(symbol)
    
    def get_all_tickers(self) -> Dict:
        return self.tickers.copy()
    
    def on_error(self, ws, error):
        print(f"🚨 WebSocket error: {error}")
    
    def on_close(self, ws, close_status_code, close_msg):
        print("❌ Ticker connection closed")

# Usage
tracker = TickerTracker()

# Add custom ticker handler
def ticker_handler(symbol, ticker):
    # Alert on high funding rates
    if ticker['funding_rate'] and abs(ticker['funding_rate']) > 0.001:
        print(f"⚠️  High funding rate on {symbol}: {(ticker['funding_rate'] * 100):.4f}%")
    
    # Alert on large open interest
    if ticker['open_interest_value'] and ticker['open_interest_value'] > 100:
        print(f"📊 Large OI on {symbol}: {ticker['open_interest_value']:.2f} BTC")

tracker.add_listener(ticker_handler)
tracker.connect()
```

## Use cases

<AccordionGroup>
  <Accordion title="Market Dashboard">
    Display comprehensive market statistics in trading dashboards with real-time updates for all key metrics.

    ```javascript theme={null}
    function updateMarketDashboard(symbol, ticker) {
      // Update price displays
      document.getElementById(`${symbol}-last`).textContent = ticker.lastPrice.toFixed(8);
      document.getElementById(`${symbol}-mark`).textContent = ticker.markPrice.toFixed(8);
      document.getElementById(`${symbol}-index`).textContent = ticker.indexPrice.toFixed(8);
      
      // Update orderbook top
      document.getElementById(`${symbol}-bid`).textContent = 
        `${ticker.bestBid.toFixed(8)} (${ticker.bestBidQty})`;
      document.getElementById(`${symbol}-ask`).textContent = 
        `${ticker.bestAsk.toFixed(8)} (${ticker.bestAskQty})`;
      
      // Update market stats
      document.getElementById(`${symbol}-oi`).textContent = ticker.openInterest;
      document.getElementById(`${symbol}-oi-value`).textContent = 
        `${ticker.openInterestValue.toFixed(4)} BTC`;
      document.getElementById(`${symbol}-funding`).textContent = 
        `${(ticker.fundingRate * 100).toFixed(4)}%`;
    }
    ```
  </Accordion>

  <Accordion title="Funding Rate Monitoring">
    Monitor funding rates across symbols to identify arbitrage opportunities or funding rate trading strategies.

    ```javascript theme={null}
    function monitorFundingRates(symbol, ticker) {
      const fundingRatePercent = ticker.fundingRate * 100;
      
      if (Math.abs(fundingRatePercent) > 0.1) {
        console.log(`⚠️  High funding rate: ${symbol} - ${fundingRatePercent.toFixed(4)}%`);
        
        // Potential arbitrage opportunity
        if (fundingRatePercent > 0.1) {
          console.log(`💡 Consider shorting ${symbol} to earn funding`);
        } else if (fundingRatePercent < -0.1) {
          console.log(`💡 Consider longing ${symbol} to earn funding`);
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="Open Interest Tracking">
    Track open interest changes to gauge market sentiment and potential price movements.

    ```javascript theme={null}
    function trackOpenInterest(symbol, ticker) {
      const previousOI = this.previousOI[symbol] || 0;
      const currentOI = ticker.openInterestValue;
      const oiChange = currentOI - previousOI;
      const oiChangePercent = (oiChange / previousOI) * 100;
      
      if (Math.abs(oiChangePercent) > 5) {
        console.log(`📊 Significant OI change on ${symbol}:`);
        console.log(`   Previous: ${previousOI.toFixed(2)} BTC`);
        console.log(`   Current: ${currentOI.toFixed(2)} BTC`);
        console.log(`   Change: ${oiChangePercent.toFixed(2)}%`);
        
        if (oiChange > 0) {
          console.log(`   💹 Increasing OI - New positions being opened`);
        } else {
          console.log(`   📉 Decreasing OI - Positions being closed`);
        }
      }
      
      this.previousOI[symbol] = currentOI;
    }
    ```
  </Accordion>

  <Accordion title="Price Deviation Alerts">
    Monitor differences between mark price, index price, and last price to identify market inefficiencies.

    ```javascript theme={null}
    function checkPriceDeviations(symbol, ticker) {
      if (!ticker.markPrice || !ticker.indexPrice || !ticker.lastPrice) return;
      
      const markIndexDiff = Math.abs(ticker.markPrice - ticker.indexPrice);
      const markIndexDiffPercent = (markIndexDiff / ticker.indexPrice) * 100;
      
      const lastMarkDiff = Math.abs(ticker.lastPrice - ticker.markPrice);
      const lastMarkDiffPercent = (lastMarkDiff / ticker.markPrice) * 100;
      
      if (markIndexDiffPercent > 0.5) {
        console.log(`⚠️  Large mark-index deviation on ${symbol}: ${markIndexDiffPercent.toFixed(3)}%`);
        console.log(`   Mark: ${ticker.markPrice.toFixed(8)}`);
        console.log(`   Index: ${ticker.indexPrice.toFixed(8)}`);
      }
      
      if (lastMarkDiffPercent > 1.0) {
        console.log(`⚠️  Large last-mark deviation on ${symbol}: ${lastMarkDiffPercent.toFixed(3)}%`);
        console.log(`   Last: ${ticker.lastPrice.toFixed(8)}`);
        console.log(`   Mark: ${ticker.markPrice.toFixed(8)}`);
      }
    }
    ```
  </Accordion>
</AccordionGroup>

## Performance considerations

<AccordionGroup>
  <Accordion title="Update Frequency">
    Ticker updates can be frequent during volatile market conditions. Consider throttling UI updates:

    ```javascript theme={null}
    class ThrottledTickerHandler {
      constructor(throttleMs = 200) {
        this.throttleMs = throttleMs;
        this.pendingUpdates = {};
        this.updateTimer = null;
      }
      
      handleTickerUpdate(message) {
        const symbol = message.data.symbol;
        this.pendingUpdates[symbol] = message;
        
        if (!this.updateTimer) {
          this.updateTimer = setTimeout(() => {
            this.processPendingUpdates();
            this.updateTimer = null;
          }, this.throttleMs);
        }
      }
      
      processPendingUpdates() {
        Object.values(this.pendingUpdates).forEach(message => {
          this.processUpdate(message);
        });
        this.pendingUpdates = {};
      }
    }
    ```
  </Accordion>

  <Accordion title="Null Value Handling">
    Ticker data fields can be null if data is not yet available. Always handle null values:

    ```javascript theme={null}
    function safeParseTickerValue(value, defaultValue = 0) {
      if (value === null || value === undefined) {
        return defaultValue;
      }
      return parseFloat(value);
    }

    function handleTicker(ticker) {
      const lastPrice = safeParseTickerValue(ticker.lastPrice);
      const markPrice = safeParseTickerValue(ticker.markPrice);
      const fundingRate = safeParseTickerValue(ticker.fundingRate);
      
      // Use safely parsed values
      console.log(`Last: ${lastPrice}, Mark: ${markPrice}, Funding: ${fundingRate}`);
    }
    ```
  </Accordion>
</AccordionGroup>

## Data field availability

<Note>
  **Initial Connection**: When first subscribing to a ticker channel, some fields may be null until the relevant data becomes available. This is normal behavior:

  * `lastPrice`: Available after the first trade
  * `indexPrice`: Available from price feed
  * `markPrice`: Available from price feed
  * `bestBid` and `bestAsk`: Available when orderbook has quotes
  * `openInterest`: Available from position tracking
  * `fundingRate`: Available for perpetual contracts

  The ticker will send updates as new data becomes available for each field.
</Note>

## Error handling

Common ticker stream errors and solutions:

<AccordionGroup>
  <Accordion title="Invalid Symbol">
    **Error**: Subscription rejected for unknown trading pair

    **Solution**: Verify symbol format matches exactly: `US500-BTC`, `GOLD-BTC`, `USDT-BTC`
  </Accordion>

  <Accordion title="Null Values">
    **Error**: Unexpected null values in ticker data

    **Solution**: Always handle null values gracefully as they indicate data not yet available

    ```javascript theme={null}
    function displayTicker(ticker) {
      const lastPrice = ticker.lastPrice || 'N/A';
      const fundingRate = ticker.fundingRate ? 
        (parseFloat(ticker.fundingRate) * 100).toFixed(4) + '%' : 'N/A';
      
      console.log(`Last: ${lastPrice}, Funding: ${fundingRate}`);
    }
    ```
  </Accordion>

  <Accordion title="Stale Data">
    **Error**: Ticker data not updating

    **Solution**: Check connection health and implement staleness detection:

    ```javascript theme={null}
    function checkTickerStaleness(ticker, maxAgeMs = 10000) {
      const age = Date.now() - ticker.lastUpdated;
      if (age > maxAgeMs) {
        console.warn(`Stale ticker data for ${ticker.symbol}: ${age}ms old`);
        return true;
      }
      return false;
    }
    ```
  </Accordion>
</AccordionGroup>

## Next steps

* Learn about [Level 1 Quotes](/websockets/market-data/level1) for simple bid/ask updates
* Explore [Order Book Depth](/websockets/market-data/depth) for detailed orderbook data
* Review [Connection Management](/websockets/connection) for reliable streaming
