Skip to main content

WebSocket API

GTX DEX provides comprehensive WebSocket support for real-time market data, order updates, and yield tracking. The WebSocket API enables building responsive trading applications with live data feeds.

Connection Details

WebSocket Endpoint

wss://ws.gtxdex.xyz

Connection Parameters

ParameterTypeRequiredDescription
apiKeystringNoAPI key for enhanced rate limits
signaturestringNoHMAC signature for private streams
timestampnumberNoRequest timestamp

Message Format

All WebSocket messages use JSON format:
{
  "method": "SUBSCRIBE",
  "params": ["ticker@ETHUSDC"],
  "id": 1
}

Message Fields

FieldTypeDescription
methodstringAction type: SUBSCRIBE, UNSUBSCRIBE, or stream data
paramsarrayStream names or parameters
idnumberRequest ID for tracking responses

Public Data Streams

Ticker Stream

Real-time 24hr ticker statistics for all symbols.
// Subscribe to specific symbol
ws.send(JSON.stringify({
  method: "SUBSCRIBE",
  params: ["ticker@ETHUSDC"],
  id: 1
}));

// Subscribe to all symbols
ws.send(JSON.stringify({
  method: "SUBSCRIBE", 
  params: ["!ticker@arr"],
  id: 2
}));
Stream Name: ticker@{symbol} or !ticker@arr
{
  "stream": "ticker@ETHUSDC",
  "data": {
    "symbol": "ETHUSDC",
    "priceChange": "120.50",
    "priceChangePercent": "5.12",
    "weightedAvgPrice": "2456.78",
    "lastPrice": "2475.00", 
    "lastQty": "0.5",
    "bidPrice": "2474.50",
    "bidQty": "1.2",
    "askPrice": "2475.50",
    "askQty": "0.8",
    "openPrice": "2354.50",
    "highPrice": "2485.00",
    "lowPrice": "2340.00",
    "volume": "12456.78",
    "quoteVolume": "30789123.45",
    "openTime": 1640995200000,
    "closeTime": 1641081599999,
    "firstId": 28385,
    "lastId": 28460,
    "count": 76
  }
}

Trade Stream

Real-time trade execution data.
// Subscribe to trades for specific symbol
ws.send(JSON.stringify({
  method: "SUBSCRIBE",
  params: ["trade@ETHUSDC"],
  id: 3
}));

// Subscribe to all trades
ws.send(JSON.stringify({
  method: "SUBSCRIBE",
  params: ["!trade@arr"], 
  id: 4
}));
Stream Name: trade@{symbol} or !trade@arr
{
  "stream": "trade@ETHUSDC",
  "data": {
    "eventType": "trade",
    "eventTime": 1641082345123,
    "symbol": "ETHUSDC",
    "tradeId": 12345,
    "price": "2475.00",
    "quantity": "0.5",
    "buyerOrderId": 88,
    "sellerOrderId": 102,
    "tradeTime": 1641082345120,
    "isBuyerMaker": true
  }
}

Order Book Stream

Real-time order book depth updates.
// Partial depth (5 levels)
ws.send(JSON.stringify({
  method: "SUBSCRIBE",
  params: ["depth5@ETHUSDC"],
  id: 5
}));

// Full depth updates
ws.send(JSON.stringify({
  method: "SUBSCRIBE", 
  params: ["depth@ETHUSDC"],
  id: 6
}));

// Diff depth (only changes)
ws.send(JSON.stringify({
  method: "SUBSCRIBE",
  params: ["depthUpdate@ETHUSDC"],
  id: 7
}));
Stream Names:
  • depth@{symbol} - Full order book
  • depth{levels}@{symbol} - Partial depth (5, 10, 20 levels)
  • depthUpdate@{symbol} - Incremental updates
{
  "stream": "depth@ETHUSDC",
  "data": {
    "eventType": "depthUpdate",
    "eventTime": 1641082345123,
    "symbol": "ETHUSDC",
    "firstUpdateId": 157,
    "finalUpdateId": 160,
    "bids": [
      ["2474.50", "1.2"],
      ["2474.00", "0.8"],
      ["2473.50", "2.1"]
    ],
    "asks": [
      ["2475.50", "0.9"],
      ["2476.00", "1.5"],
      ["2476.50", "0.7"]
    ]
  }
}

Candlestick Stream

Real-time OHLCV candlestick data.
// 1-minute candlesticks
ws.send(JSON.stringify({
  method: "SUBSCRIBE",
  params: ["kline_1m@ETHUSDC"],
  id: 8
}));

// 1-hour candlesticks
ws.send(JSON.stringify({
  method: "SUBSCRIBE",
  params: ["kline_1h@ETHUSDC"], 
  id: 9
}));
Intervals: 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M
{
  "stream": "kline_1m@ETHUSDC",
  "data": {
    "eventType": "kline",
    "eventTime": 1641082345123,
    "symbol": "ETHUSDC",
    "kline": {
      "startTime": 1641082260000,
      "closeTime": 1641082319999,
      "symbol": "ETHUSDC",
      "interval": "1m",
      "firstTradeId": 100,
      "lastTradeId": 200,
      "open": "2470.00",
      "close": "2475.00",
      "high": "2478.00",
      "low": "2469.50",
      "volume": "25.5",
      "numberOfTrades": 15,
      "isClosed": false,
      "quoteVolume": "63047.25",
      "takerBuyBaseVolume": "12.8",
      "takerBuyQuoteVolume": "31658.40"
    }
  }
}

Private Data Streams

User Order Updates

Real-time updates for user orders.
// Subscribe to order updates
ws.send(JSON.stringify({
  method: "SUBSCRIBE",
  params: ["executionReport"],
  id: 10
}));
{
  "stream": "executionReport",
  "data": {
    "eventType": "executionReport",
    "eventTime": 1641082345123,
    "symbol": "ETHUSDC",
    "clientOrderId": "user123_order456",
    "side": "BUY",
    "orderType": "LIMIT",
    "timeInForce": "GTC",
    "orderQuantity": "1.0",
    "orderPrice": "2470.00",
    "stopPrice": "0.00",
    "icebergQuantity": "0.00",
    "orderListId": -1,
    "originalClientOrderId": "",
    "currentExecutionType": "NEW",
    "currentOrderStatus": "NEW",
    "orderRejectReason": "NONE",
    "orderId": 4,
    "lastExecutedQuantity": "0.00",
    "cumulativeFilledQuantity": "0.00",
    "lastExecutedPrice": "0.00",
    "commissionAmount": "0",
    "commissionAsset": null,
    "transactionTime": 1641082345120,
    "tradeId": 0,
    "isMaker": false
  }
}

Account Updates

Real-time balance and position updates.
// Subscribe to account updates
ws.send(JSON.stringify({
  method: "SUBSCRIBE", 
  params: ["outboundAccountPosition"],
  id: 11
}));
{
  "stream": "outboundAccountPosition",
  "data": {
    "eventType": "outboundAccountPosition",
    "eventTime": 1641082345123,
    "lastAccountUpdateTime": 1641082345120,
    "balances": [
      {
        "asset": "ETH",
        "free": "1.50000000",
        "locked": "0.25000000"
      },
      {
        "asset": "USDC", 
        "free": "4750.00000000",
        "locked": "250.00000000"
      }
    ]
  }
}

Yield Updates

Real-time yield generation and rewards updates.
// Subscribe to yield updates
ws.send(JSON.stringify({
  method: "SUBSCRIBE",
  params: ["yieldUpdate"],
  id: 12
}));
{
  "stream": "yieldUpdate",
  "data": {
    "eventType": "yieldUpdate",
    "eventTime": 1641082345123,
    "userId": "user123",
    "totalYieldEarned": "15.75",
    "dailyYield": "0.85",
    "currentAPY": "6.25",
    "gtxRewards": "2.5",
    "positions": [
      {
        "symbol": "ETHUSDC",
        "positionValue": "2475.00",
        "yieldRate": "0.06",
        "accruedYield": "8.25"
      }
    ]
  }
}

Implementation Examples

TypeScript WebSocket Client

import WebSocket from 'ws';

class GTXWebSocketClient {
  private ws: WebSocket | null = null;
  private subscriptions: Set<string> = new Set();
  private messageHandlers: Map<string, Function[]> = new Map();
  private reconnectInterval: number = 5000;
  private isConnected: boolean = false;

  constructor(
    private url: string = 'wss://ws.gtxdex.xyz',
    private apiKey?: string,
    private apiSecret?: string
  ) {}

  async connect(): Promise<void> {
    return new Promise((resolve, reject) => {
      let connectionUrl = this.url;
      
      // Connect to WebSocket
      
      this.ws = new WebSocket(connectionUrl);
      
      this.ws.on('open', () => {
        console.log('🔗 WebSocket connected');
        this.isConnected = true;
        
        // Resubscribe to all streams
        this.subscriptions.forEach(stream => {
          this.subscribe(stream);
        });
        
        resolve();
      });
      
      this.ws.on('message', (data: string) => {
        this.handleMessage(JSON.parse(data));
      });
      
      this.ws.on('close', () => {
        console.log('❌ WebSocket disconnected');
        this.isConnected = false;
        this.reconnect();
      });
      
      this.ws.on('error', (error) => {
        console.error('WebSocket error:', error);
        reject(error);
      });
    });
  }

  private createSignature(timestamp: number): string {
    const crypto = require('crypto');
    const message = `timestamp=${timestamp}`;
    return crypto
      .createHmac('sha256', this.apiSecret)
      .update(message)
      .digest('hex');
  }

  private handleMessage(message: any) {
    if (message.stream) {
      // Stream data message
      const handlers = this.messageHandlers.get(message.stream) || [];
      handlers.forEach(handler => handler(message.data));
    } else if (message.id) {
      // Response to subscription/unsubscription
      console.log('Subscription response:', message);
    }
  }

  private reconnect() {
    setTimeout(() => {
      console.log('🔄 Reconnecting...');
      this.connect().catch(console.error);
    }, this.reconnectInterval);
  }

  subscribe(stream: string, handler?: Function): void {
    this.subscriptions.add(stream);
    
    if (handler) {
      if (!this.messageHandlers.has(stream)) {
        this.messageHandlers.set(stream, []);
      }
      this.messageHandlers.get(stream)!.push(handler);
    }

    if (this.isConnected && this.ws) {
      this.ws.send(JSON.stringify({
        method: 'SUBSCRIBE',
        params: [stream],
        id: Date.now()
      }));
    }
  }

  unsubscribe(stream: string): void {
    this.subscriptions.delete(stream);
    this.messageHandlers.delete(stream);

    if (this.isConnected && this.ws) {
      this.ws.send(JSON.stringify({
        method: 'UNSUBSCRIBE',
        params: [stream],
        id: Date.now()
      }));
    }
  }

  // Convenience methods for common streams
  subscribeTicker(symbol: string, handler: (data: any) => void): void {
    this.subscribe(`ticker@${symbol}`, handler);
  }

  subscribeTrades(symbol: string, handler: (data: any) => void): void {
    this.subscribe(`trade@${symbol}`, handler);
  }

  subscribeOrderBook(symbol: string, levels: number = 20, handler?: (data: any) => void): void {
    const stream = levels ? `depth${levels}@${symbol}` : `depth@${symbol}`;
    this.subscribe(stream, handler);
  }

  subscribeKlines(symbol: string, interval: string, handler: (data: any) => void): void {
    this.subscribe(`kline_${interval}@${symbol}`, handler);
  }

  // Private streams
  subscribeUserOrders(handler: (data: any) => void): void {
    this.subscribe('executionReport', handler);
  }

  subscribeAccountUpdates(handler: (data: any) => void): void {
    this.subscribe('outboundAccountPosition', handler);
  }

  subscribeYieldUpdates(handler: (data: any) => void): void {
    this.subscribe('yieldUpdate', handler);
  }

  disconnect(): void {
    if (this.ws) {
      this.ws.close();
    }
  }
}

// Usage
const wsClient = new GTXWebSocketClient();

wsClient.connect().then(() => {
  // Subscribe to price updates
  wsClient.subscribeTicker('ETHUSDC', (ticker) => {
    console.log(`ETH price: $${ticker.lastPrice} (${ticker.priceChangePercent}%)`);
  });

  // Subscribe to trades
  wsClient.subscribeTrades('ETHUSDC', (trade) => {
    console.log(`Trade: ${trade.quantity} ETH at $${trade.price}`);
  });

  // Subscribe to order book
  wsClient.subscribeOrderBook('ETHUSDC', 5, (depth) => {
    console.log(`Best bid: $${depth.bids[0][0]}, Best ask: $${depth.asks[0][0]}`);
  });
});

Advanced Features

Rate Limiting

WebSocket connections are subject to rate limits:
Connection TypeLimitNotes
Public Streams5 connections per IPMarket data only
Private Streams3 connections per API keyUser data streams
Messages/Second100 per connectionSubscription requests

Connection Management

class ConnectionManager {
  private lastPingTime: number = 0;
  private pingInterval: NodeJS.Timeout | null = null;
  
  startHealthCheck(ws: WebSocket): void {
    this.pingInterval = setInterval(() => {
      if (ws.readyState === WebSocket.OPEN) {
        this.lastPingTime = Date.now();
        ws.ping();
      }
    }, 30000); // Ping every 30 seconds
    
    ws.on('pong', () => {
      const latency = Date.now() - this.lastPingTime;
      console.log(`📡 Ping: ${latency}ms`);
    });
  }
  
  stopHealthCheck(): void {
    if (this.pingInterval) {
      clearInterval(this.pingInterval);
    }
  }
}

Stream Aggregation

Combine multiple streams for complex analysis:
class StreamAggregator {
  private tickerData: Map<string, any> = new Map();
  private tradeData: Map<string, any[]> = new Map();
  
  constructor(private wsClient: GTXWebSocketClient) {}
  
  startAggregation(symbols: string[]): void {
    symbols.forEach(symbol => {
      // Subscribe to ticker
      this.wsClient.subscribeTicker(symbol, (ticker) => {
        this.tickerData.set(symbol, ticker);
        this.updateAnalysis(symbol);
      });
      
      // Subscribe to trades
      this.wsClient.subscribeTrades(symbol, (trade) => {
        if (!this.tradeData.has(symbol)) {
          this.tradeData.set(symbol, []);
        }
        
        const trades = this.tradeData.get(symbol)!;
        trades.push(trade);
        
        // Keep only last 100 trades
        if (trades.length > 100) {
          trades.shift();
        }
        
        this.updateAnalysis(symbol);
      });
    });
  }
  
  private updateAnalysis(symbol: string): void {
    const ticker = this.tickerData.get(symbol);
    const trades = this.tradeData.get(symbol) || [];
    
    if (ticker && trades.length > 0) {
      // Calculate volume-weighted average price
      const vwap = this.calculateVWAP(trades);
      
      // Detect large orders
      const largeOrders = trades.filter(t => parseFloat(t.quantity) > 1.0);
      
      console.log(`${symbol} Analysis:`, {
        currentPrice: ticker.lastPrice,
        vwap: vwap.toFixed(2),
        largeOrdersCount: largeOrders.length,
        momentum: this.calculateMomentum(trades)
      });
    }
  }
  
  private calculateVWAP(trades: any[]): number {
    const totalValue = trades.reduce((sum, trade) => 
      sum + (parseFloat(trade.price) * parseFloat(trade.quantity)), 0
    );
    const totalVolume = trades.reduce((sum, trade) => 
      sum + parseFloat(trade.quantity), 0
    );
    
    return totalVolume > 0 ? totalValue / totalVolume : 0;
  }
  
  private calculateMomentum(trades: any[]): string {
    if (trades.length < 10) return 'neutral';
    
    const recent = trades.slice(-10);
    const buyVolume = recent
      .filter(t => !t.isBuyerMaker)
      .reduce((sum, t) => sum + parseFloat(t.quantity), 0);
    const sellVolume = recent
      .filter(t => t.isBuyerMaker)
      .reduce((sum, t) => sum + parseFloat(t.quantity), 0);
    
    const ratio = buyVolume / (buyVolume + sellVolume);
    
    if (ratio > 0.6) return 'bullish';
    if (ratio < 0.4) return 'bearish';
    return 'neutral';
  }
}

Error Handling

Common Error Codes

CodeMessageSolution
1000Normal closureConnection closed normally
1006Abnormal closureNetwork issue, reconnect
4001Invalid message formatCheck JSON format
4002Invalid stream nameVerify stream syntax
4003Connection failedCheck connection parameters
4004Rate limit exceededReduce request frequency

Robust Error Handling

class RobustWebSocketClient extends GTXWebSocketClient {
  private maxReconnectAttempts: number = 10;
  private reconnectAttempts: number = 0;
  
  protected reconnect(): void {
    if (this.reconnectAttempts >= this.maxReconnectAttempts) {
      console.error('❌ Max reconnection attempts reached');
      return;
    }
    
    this.reconnectAttempts++;
    const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
    
    console.log(`🔄 Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
    
    setTimeout(() => {
      this.connect().then(() => {
        this.reconnectAttempts = 0; // Reset on successful connection
      }).catch(console.error);
    }, delay);
  }
}

The WebSocket API provides real-time access to all GTX DEX data streams. Combine with yield tracking to build applications that monitor both trading and earning opportunities in real-time.