Overview

Establish a WebSocket connection to access market data and account streams. All WebSocket connections require authentication using API key headers during the connection handshake.

Connection Setup

JavaScript Example

const ws = new WebSocket('wss://ws.roxom.com/ws', [], {
  headers: {
    'X-API-KEY': 'your-api-key',
    'X-API-TIMESTAMP': Date.now().toString(),
    'X-API-SIGNATURE': 'your-hmac-signature'
  }
});

ws.onopen = function(event) {
  console.log('Connected to Roxom WebSocket API');
  
  // Subscribe to market data streams
  ws.send(JSON.stringify({
    op: 'subscribe',
    args: [
      { channel: 'level1', symbol: 'US500-BTC' },
      { channel: 'trade', symbol: 'GOLD-BTC' }
    ]
  }));
};

ws.onmessage = function(event) {
  const data = JSON.parse(event.data);
  console.log('Received:', data);
};

ws.onclose = function(event) {
  console.log('Connection closed:', event.code, event.reason);
};

ws.onerror = function(error) {
  console.error('WebSocket error:', error);
};

Python Example

import websocket
import json

def on_message(ws, message):
    data = json.loads(message)
    print("Received:", data)

def on_error(ws, error):
    print("Error:", error)

def on_close(ws, close_status_code, close_msg):
    print("Connection closed")

def on_open(ws):
    print("Connected to Roxom WebSocket API")
    
    # Subscribe to market data
    subscribe_msg = {
        "op": "subscribe",
        "args": [
            {"channel": "level1", "symbol": "US500-BTC"},
            {"channel": "trade", "symbol": "GOLD-BTC"}
        ]
    }
    ws.send(json.dumps(subscribe_msg))

ws = websocket.WebSocketApp("wss://ws.roxom.com/ws",
                          on_open=on_open,
                          on_message=on_message,
                          on_error=on_error,
                          on_close=on_close)

ws.run_forever()

Go Example

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "github.com/gorilla/websocket"
)

type SubscribeMessage struct {
    Op   string      `json:"op"`
    Args []SubArgs   `json:"args"`
}

type SubArgs struct {
    Channel string `json:"channel"`
    Symbol  string `json:"symbol"`
}

func main() {
    c, _, err := websocket.DefaultDialer.Dial("wss://ws.roxom.com/ws", nil)
    if err != nil {
        log.Fatal("dial:", err)
    }
    defer c.Close()

    // Subscribe to market data
    subMsg := SubscribeMessage{
        Op: "subscribe",
        Args: []SubArgs{
            {Channel: "level1", Symbol: "US500-BTC"},
            {Channel: "trade", Symbol: "GOLD-BTC"},
        },
    }
    
    if err := c.WriteJSON(subMsg); err != nil {
        log.Println("write:", err)
        return
    }

    // Read messages
    for {
        _, message, err := c.ReadMessage()
        if err != nil {
            log.Println("read:", err)
            return
        }
        fmt.Printf("received: %s\n", message)
    }
}

Connection URLs

wss://ws.roxom.com/ws
Production and sandbox environments are separate. Use sandbox for testing and development.

Subscription Management

Subscribe to Channels

{
  "op": "subscribe",
  "args": [
    { "channel": "level1", "symbol": "US500-BTC" },
    { "channel": "trade", "symbol": "GOLD-BTC" },
    { "channel": "depth", "symbol": "OIL-BTC" }
  ]
}

Unsubscribe from Channels

{
  "op": "unsubscribe",
  "args": [
    { "channel": "trade", "symbol": "GOLD-BTC" }
  ]
}

Available Public Channels

ChannelDescriptionData Type
level1Best bid/ask quotesReal-time quotes
tradeTrade executionsTrade history
depthOrder book depthMarket depth
ticker24hr statisticsPrice statistics

Error Handling

Connection Errors

Best Practices

Complete Example

Here’s a complete example with error handling and reconnection:
class RoxomPublicWebSocket {
  constructor() {
    this.ws = null;
    this.reconnectAttempts = 0;
    this.maxReconnectAttempts = 5;
    this.reconnectDelay = 1000;
  }

  connect() {
    try {
      this.ws = new WebSocket('wss://ws.roxom.com/ws');
      
      this.ws.onopen = () => {
        console.log('✅ Connected to Roxom WebSocket');
        this.reconnectAttempts = 0;
        this.onConnected();
      };

      this.ws.onmessage = (event) => {
        try {
          const data = JSON.parse(event.data);
          this.handleMessage(data);
        } catch (error) {
          console.error('Error parsing message:', error);
        }
      };

      this.ws.onclose = (event) => {
        console.log('❌ Connection closed:', event.code);
        this.handleReconnect();
      };

      this.ws.onerror = (error) => {
        console.error('WebSocket error:', error);
      };

    } catch (error) {
      console.error('Connection error:', error);
      this.handleReconnect();
    }
  }

  onConnected() {
    // Subscribe to market data streams
    this.subscribe([
      { channel: 'level1', symbol: 'US500-BTC' },
      { channel: 'trade', symbol: 'GOLD-BTC' }
    ]);
  }

  subscribe(channels) {
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify({
        op: 'subscribe',
        args: channels
      }));
    }
  }

  handleMessage(data) {
    console.log('Received:', data);
    
    // Route messages to appropriate handlers
    if (data.topic) {
      const [channel, symbol] = data.topic.split('.');
      
      switch (channel) {
        case 'level1':
          this.handleLevel1(symbol, data.data);
          break;
        case 'trade':
          this.handleTrade(symbol, data.data);
          break;
      }
    }
  }

  handleLevel1(symbol, data) {
    console.log(`${symbol} Level 1: Bid ${data.bid[0]} Ask ${data.ask[0]}`);
  }

  handleTrade(symbol, data) {
    console.log(`${symbol} Trade: ${data.takerSide} at ${data.vwap}`);
  }

  handleReconnect() {
    if (this.reconnectAttempts < this.maxReconnectAttempts) {
      this.reconnectAttempts++;
      const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
      
      console.log(`🔄 Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
      
      setTimeout(() => {
        this.connect();
      }, delay);
    } else {
      console.error('❌ Max reconnection attempts reached');
    }
  }

  disconnect() {
    if (this.ws) {
      this.ws.close();
      this.ws = null;
    }
  }
}

// Usage
const client = new RoxomPublicWebSocket();
client.connect();

Next Steps