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
Copy
Ask AI
{
"op": "subscribe",
"args": [
{ "channel": "ticker", "symbol": "US500-BTC" },
{ "channel": "ticker", "symbol": "GOLD-BTC" }
]
}
Response Format
Copy
Ask AI
{
"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"
}
}
Comprehensive ticker data
Show Ticker properties
Show Ticker properties
Trading pair symbol
Last traded price (null if no trades yet)
Index price for the instrument (null if unavailable)
Mark price used for liquidations and unrealized PnL (null if unavailable)
Best bid price (null if no bids)
Best ask price (null if no asks)
Quantity at best bid (null if no bids)
Quantity at best ask (null if no asks)
Total open interest in contracts (null if unavailable)
Total open interest value in BTC (null if unavailable)
Current funding rate for perpetual contracts (null if unavailable)
Update timestamp in nanoseconds
Unit for price values (btc or sats)
Implementation Examples
JavaScript Ticker Monitor
Copy
Ask AI
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', 'OIL-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
Copy
Ask AI
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', 'OIL-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
Market Dashboard
Display comprehensive market statistics in trading dashboards with real-time updates for all key metrics.
Copy
Ask AI
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
Funding Rate Monitoring
Monitor funding rates across symbols to identify arbitrage opportunities or funding rate trading strategies.
Copy
Ask AI
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
Open Interest Tracking
Track open interest changes to gauge market sentiment and potential price movements.
Copy
Ask AI
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
Price Deviation Alerts
Monitor differences between mark price, index price, and last price to identify market inefficiencies.
Copy
Ask AI
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
Update Frequency
Ticker updates can be frequent during volatile market conditions. Consider throttling UI updates:
Copy
Ask AI
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
Null Value Handling
Ticker data fields can be null if data is not yet available. Always handle null values:
Copy
Ask AI
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 Fields Availability
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 tradeindexPrice: Available from price feedmarkPrice: Available from price feedbestBid/bestAsk: Available when orderbook has quotesopenInterest: Available from position trackingfundingRate: Available for perpetual contracts
Error Handling
Common ticker stream errors and solutions:Invalid Symbol
Invalid Symbol
Error: Subscription rejected for unknown trading pairSolution: Verify symbol format matches exactly:
US500-BTC, GOLD-BTC, OIL-BTC, US100-BTCNull Values
Null Values
Error: Unexpected null values in ticker dataSolution: Always handle null values gracefully as they indicate data not yet available
Copy
Ask AI
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
Stale Data
Error: Ticker data not updatingSolution: Check connection health and implement staleness detection:
Copy
Ask AI
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
- Learn about Level 1 Quotes for simple bid/ask updates
- Explore Order Book Depth for detailed orderbook data
- Review Connection Management for reliable streaming