Skip to main content

Overview

Receive real-time updates for your account activity through authenticated WebSocket connections. After connecting with valid API credentials, subscribe to the private channels you need (orders, balance, positions).
Account update streams require authentication and explicit subscription. Include valid API credentials in the WebSocket connection headers (X-API-Key and X-API-Signature).

Available Private Streams

Stream Types Overview

ChannelDescriptionAuto-EnabledAuthentication
ordersOrder status changes and fills❌ No (subscribe)Required
balanceAccount balance updates❌ No (subscribe)Required
positionsPosition changes and PnL❌ No (subscribe)Required
accountAccount-level notifications❌ No (subscribe)Required
Important Account Event Behavior: When authenticated, account-related events (orders, positions, balance) are delivered to all WebSocket connections for your account regardless of individual channel subscriptions.For example, if you subscribe only to orders but place a trade that affects your position and balance, you will receive:
  • Order update events (subscribed)
  • Position update events (auto-delivered)
  • Balance update events (auto-delivered)
This ensures you don’t miss critical account changes. Filter events client-side using the "type" field if you need specific event types only.Example: Subscribing only to orders but placing a trade will generate:
// Order event (subscribed)
{"type": "order", "data": {"orderId": "...", "status": "filled", ...}}

// Position event (auto-delivered) 
{"type": "position", "data": [{"id": "...", "size": "22.00", ...}]}

// Balance event (auto-delivered)
{"type": "balance", "data": {"amount": "-0.00004905", ...}}

Quick Start

Authenticated Connection Setup

// Generate RSA signature for WebSocket connection
// Payload for WebSocket connection is always: "GET:/ws"
const signature = generateRSASignature(privateKey, 'GET:/ws');

const ws = new WebSocket('wss://ws.roxom.com/ws', [], {
  headers: {
    'X-API-Key': 'your-api-key',
    'X-API-Signature': signature
  }
});

ws.onopen = () => {
  ws.send(JSON.stringify({
    op: 'subscribe',
    args: [ { channel: 'orders' }, { channel: 'positions' }, { channel: 'balance' } ]
  }));
};

ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
  
  if (data.event === 'subscribe') return; // subscription ack
  if (data.data && data.data.type === 'order') handleOrderUpdate(data.data);
  if (data.data && data.data.type === 'position') handlePositionUpdate(data.data);
  if (data.data && data.data.type === 'balance') handleBalanceUpdate(data.data);
};

RSA Signature Generation

The WebSocket connection requires an RSA signature of the payload GET:/ws using your private key:
function generateRSASignature(privateKey, payload = 'GET:/ws') {
  const crypto = require('crypto');
  const sign = crypto.createSign('SHA256');
  sign.update(payload);
  return sign.sign(privateKey, 'base64');
}

// Example usage
const fs = require('fs');
const privateKey = fs.readFileSync('path/to/your/private-key.pem', 'utf8');
const signature = generateRSASignature(privateKey);

Python RSA Signature Generation

from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
import base64

def generate_rsa_signature(private_key_path: str, payload: str = 'GET:/ws') -> str:
    # Load private key
    with open(private_key_path, 'rb') as key_file:
        private_key = serialization.load_pem_private_key(
            key_file.read(),
            password=None
        )
    
    # Sign the payload
    signature = private_key.sign(
        payload.encode('utf-8'),
        padding.PKCS1v15(),
        hashes.SHA256()
    )
    
    # Return base64 encoded signature
    return base64.b64encode(signature).decode('utf-8')

# Example usage
signature = generate_rsa_signature('path/to/your/private-key.pem')

Go RSA Signature Generation

package main

import (
    "crypto"
    "crypto/rand"
    "crypto/rsa"
    "crypto/sha256"
    "crypto/x509"
    "encoding/base64"
    "encoding/pem"
    "io/ioutil"
)

func generateRSASignature(privateKeyPath string, payload string) (string, error) {
    // Default payload for WebSocket connection
    if payload == "" {
        payload = "GET:/ws"
    }
    
    // Load private key
    keyData, err := ioutil.ReadFile(privateKeyPath)
    if err != nil {
        return "", err
    }
    
    block, _ := pem.Decode(keyData)
    privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
    if err != nil {
        return "", err
    }
    
    // Hash the payload
    hash := sha256.Sum256([]byte(payload))
    
    // Sign the hash
    signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hash[:])
    if err != nil {
        return "", err
    }
    
    // Return base64 encoded signature
    return base64.StdEncoding.EncodeToString(signature), nil
}

Message Format

All account update messages follow a consistent structure:
{
  "type": "order|balance|position|account",
  "data": {
    "accountId": "01234567-89ab-7def-8123-456789abcdef",
    // Type-specific data fields
    "timestamp": 1640995200000000000
  }
}

Common Use Cases

Monitor order fills, cancellations, and rejections in real-time to maintain accurate order state and trigger appropriate business logic.
Track position changes and unrealized P&L to implement stop-loss mechanisms and position size limits.
Real-time balance updates enable dynamic portfolio rebalancing and cash management strategies.
Use order and position updates to trigger next steps in multi-leg trading strategies and complex order workflows.

Error Handling

Cause: Invalid API credentials or signatureSolution:
  • Verify API key is correct and active
  • Check RSA signature generation using correct payload (“GET:/ws”)
  • Ensure private key matches the public key associated with your API key
Cause: Network or server connectivity issuesSolution:
  • Implement reconnection logic with exponential backoff
  • Monitor connection health with heartbeat
  • Handle reconnection state recovery
Cause: Too many connection attemptsSolution:
  • Use single connection per application
  • Implement connection pooling
  • Add delays between reconnection attempts

Best Practices

  • Maintain local copies of order and position state
  • Handle message ordering and potential duplicates
  • Implement proper state reconciliation on reconnection
  • Use message timestamps for event sequencing
  • Process updates asynchronously to avoid blocking
  • Batch UI updates for high-frequency streams
  • Implement efficient data structures for tracking
  • Monitor memory usage for long-running connections
  • Implement comprehensive error handling
  • Log all account events for audit trails
  • Use heartbeat monitoring for connection health
  • Plan for graceful degradation during outages

Authentication Requirements

To access private account streams, you need:
  1. Valid API Credentials: Active API key with appropriate permissions
  2. Proper Signature: RSA signature of the payload “GET:/ws” using your private key
  3. Correct Headers: Both X-API-Key and X-API-Signature headers included
See the Authentication Guide for detailed setup instructions.

Next Steps

I