Roxom.com

Ticker Data

Copy page

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

PropertyValue
Channel Nameticker
AuthenticationRequired
Update FrequencyReal-time
Data TypeComprehensive market statistics

Subscribe to ticker data

Request

Subscribe
{
  "op": "subscribe",
  "args": [
    { "channel": "ticker", "symbol": "GOLD-BTC" },
    { "channel": "ticker", "symbol": "USDT-BTC" }
  ]
}

Response format

Response

Ticker Snapshot
{
  "topic": "ticker.GOLD-BTC",
  "type": "snapshot",
  "createdTime": 1640995200000000000,
  "data": {
    "symbol": "GOLD-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"
  }
}
dataobject

Comprehensive ticker data

Implementation examples

JavaScript ticker monitor

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(['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

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(['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

Market Dashboard

Display comprehensive market statistics in trading dashboards with real-time updates for all key metrics.

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)}%`;
}
Funding Rate Monitoring

Monitor funding rates across symbols to identify arbitrage opportunities or funding rate trading strategies.

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`);
    }
  }
}
Open Interest Tracking

Track open interest changes to gauge market sentiment and potential price movements.

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;
}
Price Deviation Alerts

Monitor differences between mark price, index price, and last price to identify market inefficiencies.

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)}`);
  }
}

Performance considerations

Update Frequency

Ticker updates can be frequent during volatile market conditions. Consider throttling UI updates:

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 = {};
  }
}
Null Value Handling

Ticker data fields can be null if data is not yet available. Always handle null values:

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}`);
}

Data field availability

Error handling

Common ticker stream errors and solutions:

Invalid Symbol

Error: Subscription rejected for unknown trading pair

Solution: Verify symbol format matches exactly: GOLD-BTC, USDT-BTC

Null Values

Error: Unexpected null values in ticker data

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

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}`);
}
Stale Data

Error: Ticker data not updating

Solution: Check connection health and implement staleness detection:

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;
}

Next steps

Last modified on