> ## Documentation Index
> Fetch the complete documentation index at: https://docs.quantcite.com/llms.txt
> Use this file to discover all available pages before exploring further.

# JavaScript WebSocket Client

> Complete Node.js implementation for connecting to QuantCite's real-time data

## Complete JavaScript Client

Here's a full-featured Node.js WebSocket client for the QuantCite API:

```javascript theme={null}
const WebSocket = require('ws');
const EventEmitter = require('events');

class QuantCiteClient extends EventEmitter {
    constructor(apiKey, baseUrl = 'data.quantcite.com') {
        super();
        this.apiKey = apiKey;
        this.baseUrl = baseUrl;
        this.uri = `wss://${baseUrl}/api/v1/ws`;
        this.ws = null;
        this.authenticated = false;
        this.subscriptions = new Set();
        this.reconnectAttempts = 0;
        this.maxReconnectAttempts = 10;
        this.reconnectDelay = 1000;
        this.pingInterval = null;
        this.running = true;
    }
    
    async connect() {
        return new Promise((resolve, reject) => {
            console.log(`Connecting to ${this.uri}`);
            
            this.ws = new WebSocket(this.uri);
            
            this.ws.on('open', () => {
                console.log('WebSocket connected successfully');
                this.reconnectAttempts = 0;
                this.startPingInterval();
            });
            
            this.ws.on('message', (data) => {
                try {
                    const message = JSON.parse(data.toString());
                    this.handleMessage(message);
                    
                    // Handle authentication response
                    if (message.type === 'authentication_success') {
                        this.authenticated = true;
                        console.log('Authentication successful');
                        console.log(`User ID: ${message.user_id}`);
                        console.log(`Tier: ${message.tier}`);
                        
                        const usage = message.data_usage || {};
                        console.log(`Data usage: ${usage.used_gb || 0}GB / ${usage.limit_gb || 50}GB`);
                        
                        resolve(true);
                    } else if (message.type === 'authentication_error') {
                        console.error(`Authentication failed: ${message.message}`);
                        reject(new Error('Authentication failed'));
                    } else if (message.type === 'welcome') {
                        console.log('Welcome message received, authenticating...');
                        this.authenticate();
                    }
                    
                } catch (error) {
                    console.error('Error parsing message:', error);
                }
            });
            
            this.ws.on('error', (error) => {
                console.error('WebSocket error:', error);
                this.emit('error', error);
                reject(error);
            });
            
            this.ws.on('close', (code, reason) => {
                console.log(`WebSocket closed: ${code} ${reason}`);
                this.authenticated = false;
                this.stopPingInterval();
                
                if (this.running && this.reconnectAttempts < this.maxReconnectAttempts) {
                    this.scheduleReconnect();
                }
                
                this.emit('disconnect', { code, reason });
            });
        });
    }
    
    authenticate() {
        const authMsg = {
            type: 'authenticate',
            api_key: this.apiKey
        };
        
        this.send(authMsg);
        console.log('Authentication message sent');
    }
    
    scheduleReconnect() {
        this.reconnectAttempts++;
        const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
        
        console.log(`Scheduling reconnect attempt ${this.reconnectAttempts} in ${delay}ms`);
        
        setTimeout(() => {
            if (this.running) {
                this.connect().catch(error => {
                    console.error('Reconnection failed:', error);
                });
            }
        }, delay);
    }
    
    startPingInterval() {
        this.pingInterval = setInterval(() => {
            if (this.authenticated) {
                this.ping();
            }
        }, 30000); // Ping every 30 seconds
    }
    
    stopPingInterval() {
        if (this.pingInterval) {
            clearInterval(this.pingInterval);
            this.pingInterval = null;
        }
    }
    
    send(message) {
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
            this.ws.send(JSON.stringify(message));
            return true;
        } else {
            console.error('WebSocket not connected');
            return false;
        }
    }
    
    ping() {
        const pingMsg = { type: 'ping' };
        this.send(pingMsg);
    }
    
    subscribeToSymbol(symbol, exchanges = 'all') {
        if (!this.authenticated) {
            console.error('Not authenticated. Cannot subscribe.');
            return false;
        }
        
        const subscribeMsg = {
            type: 'subscribe_aggregated',
            symbol: symbol,
            exchanges: exchanges
        };
        
        if (this.send(subscribeMsg)) {
            this.subscriptions.add(symbol);
            console.log(`Subscription request sent for ${symbol}`);
            return true;
        }
        
        return false;
    }
    
    unsubscribeFromSymbol(symbol) {
        if (!this.subscriptions.has(symbol)) {
            console.warn(`Not subscribed to ${symbol}`);
            return false;
        }
        
        const unsubscribeMsg = {
            type: 'unsubscribe_aggregated',
            symbol: symbol
        };
        
        if (this.send(unsubscribeMsg)) {
            this.subscriptions.delete(symbol);
            console.log(`Unsubscribed from ${symbol}`);
            return true;
        }
        
        return false;
    }
    
    getCurrentOrderbook(symbol) {
        if (!this.authenticated) {
            console.error('Not authenticated. Cannot request orderbook.');
            return false;
        }
        
        const requestMsg = {
            type: 'get_aggregated_orderbook',
            symbol: symbol
        };
        
        if (this.send(requestMsg)) {
            console.log(`Orderbook snapshot requested for ${symbol}`);
            return true;
        }
        
        return false;
    }
    
    getExchanges() {
        if (!this.authenticated) {
            console.error('Not authenticated. Cannot request exchanges.');
            return false;
        }
        
        const requestMsg = { type: 'get_exchanges' };
        
        if (this.send(requestMsg)) {
            console.log('Exchange list requested');
            return true;
        }
        
        return false;
    }
    
    getTradingPairs(exchange = null) {
        if (!this.authenticated) {
            console.error('Not authenticated. Cannot request trading pairs.');
            return false;
        }
        
        const requestMsg = { type: 'get_pairs' };
        if (exchange) {
            requestMsg.exchange = exchange;
        }
        
        if (this.send(requestMsg)) {
            console.log(`Trading pairs requested for ${exchange || 'all exchanges'}`);
            return true;
        }
        
        return false;
    }
    
    handleMessage(data) {
        const msgType = data.type;
        
        switch (msgType) {
            case 'aggregated_orderbook_update':
                this.handleOrderbookUpdate(data);
                break;
                
            case 'aggregated_subscription_response':
                this.handleSubscriptionResponse(data);
                break;
                
            case 'unsubscribe_response':
                this.handleUnsubscribeResponse(data);
                break;
                
            case 'exchanges_data':
                this.handleExchangesData(data);
                break;
                
            case 'pairs_data':
                this.handlePairsData(data);
                break;
                
            case 'aggregated_orderbook_data':
                this.handleOrderbookSnapshot(data);
                break;
                
            case 'data_limit_warning':
                this.handleDataLimitWarning(data);
                break;
                
            case 'rate_limit_warning':
                this.handleRateLimitWarning(data);
                break;
                
            case 'error':
                this.handleError(data);
                break;
                
            case 'pong':
                this.handlePong(data);
                break;
                
            default:
                console.log(`Received ${msgType}:`, data);
                this.emit('message', data);
        }
    }
    
    handleOrderbookUpdate(data) {
        const symbol = data.symbol;
        const updateNum = data.update_number;
        const orderbook = data.data || {};
        const stats = orderbook.market_stats || {};
        
        console.log(`${symbol} Update #${updateNum}: ` +
                   `Bid: ${stats.best_bid}, Ask: ${stats.best_ask}, Spread: ${stats.spread}`);
        
        // Emit event for external handling
        this.emit('orderbook_update', {
            symbol,
            updateNum,
            orderbook,
            stats
        });
    }
    
    handleOrderbookSnapshot(data) {
        const symbol = data.symbol;
        const timestamp = data.timestamp;
        const orderbook = data.data || {};
        const stats = orderbook.market_stats || {};
        
        console.log(`${symbol} Snapshot at ${timestamp}: ` +
                   `Bid: ${stats.best_bid}, Ask: ${stats.best_ask}`);
        
        this.emit('orderbook_snapshot', {
            symbol,
            timestamp,
            orderbook,
            stats
        });
    }
    
    handleSubscriptionResponse(data) {
        const symbol = data.symbol;
        const success = data.success;
        
        if (success) {
            console.log(`Successfully subscribed to ${symbol}`);
            this.emit('subscribed', { symbol, data });
        } else {
            console.error(`Failed to subscribe to ${symbol}: ${data.message}`);
            this.emit('subscription_error', { symbol, data });
        }
    }
    
    handleUnsubscribeResponse(data) {
        const symbol = data.symbol;
        console.log(`Unsubscribed from ${symbol}`);
        this.emit('unsubscribed', { symbol, data });
    }
    
    handleExchangesData(data) {
        const exchangesInfo = data.data || {};
        const supported = exchangesInfo.supported_exchanges || [];
        const active = exchangesInfo.active_exchanges || [];
        
        console.log(`Supported exchanges: ${supported.length}`);
        console.log(`Active exchanges: ${active.length}`);
        
        this.emit('exchanges_data', exchangesInfo);
    }
    
    handlePairsData(data) {
        const pairsInfo = data.data || {};
        
        if (pairsInfo.exchange) {
            const exchange = pairsInfo.exchange;
            const pairs = pairsInfo.pairs || [];
            console.log(`${exchange}: ${pairs.length} trading pairs`);
        } else {
            const totalExchanges = pairsInfo.total_exchanges || 0;
            const totalPairs = pairsInfo.total_pairs || 0;
            console.log(`Total: ${totalExchanges} exchanges, ${totalPairs} pairs`);
        }
        
        this.emit('pairs_data', pairsInfo);
    }
    
    handleDataLimitWarning(data) {
        console.warn(`Data limit warning: ${data.message}`);
        const usage = data.data_usage || {};
        console.warn(`Usage: ${usage.used_gb}GB / ${usage.limit_gb}GB`);
        
        this.emit('data_limit_warning', data);
    }
    
    handleRateLimitWarning(data) {
        console.warn(`Rate limit warning: ${data.message}`);
        this.emit('rate_limit_warning', data);
    }
    
    handleError(data) {
        console.error(`Error: ${data.error} - ${data.message}`);
        this.emit('api_error', data);
    }
    
    handlePong(data) {
        const latency = data.latency_ms || 0;
        console.log(`Pong received (latency: ${latency}ms)`);
        this.emit('pong', data);
    }
    
    async disconnect() {
        this.running = false;
        this.stopPingInterval();
        
        if (this.ws) {
            this.ws.close();
        }
    }
    
    // Utility methods
    getSubscriptions() {
        return Array.from(this.subscriptions);
    }
    
    isConnected() {
        return this.ws && this.ws.readyState === WebSocket.OPEN;
    }
    
    isAuthenticated() {
        return this.authenticated;
    }
}

// Export the client
module.exports = QuantCiteClient;

// Example usage
async function main() {
    const client = new QuantCiteClient('demo_key_123');
    
    // Set up event listeners
    client.on('orderbook_update', (data) => {
        // Handle real-time orderbook updates
        console.log(`Orderbook update for ${data.symbol}`);
    });
    
    client.on('error', (error) => {
        console.error('Client error:', error);
    });
    
    client.on('disconnect', (info) => {
        console.log('Disconnected:', info);
    });
    
    try {
        // Connect and authenticate
        await client.connect();
        
        // Wait a moment for authentication
        setTimeout(async () => {
            if (client.isAuthenticated()) {
                // Get available exchanges
                client.getExchanges();
                
                // Subscribe to BTC/USDT on specific exchanges
                client.subscribeToSymbol('BTC/USDT', ['binance', 'okx', 'bybit']);
                
                // Get current orderbook snapshot
                setTimeout(() => {
                    client.getCurrentOrderbook('BTC/USDT');
                }, 1000);
                
                // Subscribe to ETH/USDT on all exchanges
                setTimeout(() => {
                    client.subscribeToSymbol('ETH/USDT', 'all');
                }, 2000);
            }
        }, 1000);
        
    } catch (error) {
        console.error('Failed to connect:', error);
    }
    
    // Graceful shutdown
    process.on('SIGINT', async () => {
        console.log('Shutting down...');
        await client.disconnect();
        process.exit(0);
    });
}

// Run example if this file is executed directly
if (require.main === module) {
    main();
}
```

## Package Configuration

Create a `package.json` file:

```json theme={null}
{
  "name": "quantcite-client",
  "version": "1.0.0",
  "description": "QuantCite WebSocket API client",
  "main": "quantcite-client.js",
  "scripts": {
    "start": "node quantcite-client.js",
    "test": "node test.js"
  },
  "dependencies": {
    "ws": "^8.14.2"
  },
  "keywords": ["cryptocurrency", "websocket", "api", "trading", "orderbook"],
  "author": "Your Name",
  "license": "MIT"
}
```

Install dependencies:

```bash theme={null}
npm install
```

## Usage Examples

### Basic Usage

```javascript theme={null}
const QuantCiteClient = require('./quantcite-client');

async function basicExample() {
    const client = new QuantCiteClient('your_api_key_here');
    
    // Set up event listener
    client.on('orderbook_update', (data) => {
        console.log(`${data.symbol}: ${data.stats.best_bid} / ${data.stats.best_ask}`);
    });
    
    try {
        await client.connect();
        
        // Subscribe to Bitcoin data
        setTimeout(() => {
            client.subscribeToSymbol('BTC/USDT', ['binance', 'okx']);
        }, 1000);
        
    } catch (error) {
        console.error('Connection failed:', error);
    }
}

basicExample();
```

### Event-Driven Processing

```javascript theme={null}
const client = new QuantCiteClient('your_api_key_here');

// Handle different event types
client.on('orderbook_update', (data) => {
    // Process real-time orderbook updates
    const { symbol, stats } = data;
    console.log(`${symbol}: Spread = ${stats.spread}`);
});

client.on('data_limit_warning', (data) => {
    // Handle data usage warnings
    console.warn('Approaching data limit!');
});

client.on('subscribed', (data) => {
    // Confirmation of successful subscription
    console.log(`Now receiving data for ${data.symbol}`);
});

client.on('api_error', (data) => {
    // Handle API errors
    console.error(`API Error: ${data.error}`);
});

// Connect and start
client.connect();
```

### Multiple Symbol Management

```javascript theme={null}
class TradingBot {
    constructor(apiKey) {
        this.client = new QuantCiteClient(apiKey);
        this.symbols = ['BTC/USDT', 'ETH/USDT', 'BNB/USDT'];
        this.orderbooks = new Map();
    }
    
    async start() {
        this.client.on('orderbook_update', (data) => {
            this.orderbooks.set(data.symbol, data.orderbook);
            this.analyzeSpread(data.symbol, data.stats);
        });
        
        await this.client.connect();
        
        // Subscribe to multiple symbols
        setTimeout(() => {
            this.symbols.forEach(symbol => {
                this.client.subscribeToSymbol(symbol, 'all');
            });
        }, 1000);
    }
    
    analyzeSpread(symbol, stats) {
        const spread = stats.spread_percent;
        if (spread > 0.1) { // 0.1% spread threshold
            console.log(`High spread detected for ${symbol}: ${spread.toFixed(4)}%`);
        }
    }
}

const bot = new TradingBot('your_api_key_here');
bot.start();
```

## Key Features

<CardGroup cols={2}>
  <Card title="Event-Driven Architecture" icon="bolt">
    Built with Node.js EventEmitter for efficient real-time data processing and custom event handling.
  </Card>

  <Card title="Automatic Reconnection" icon="rotate">
    Handles connection drops with exponential backoff retry logic and maintains subscriptions.
  </Card>

  <Card title="Comprehensive Error Handling" icon="shield">
    Proper error handling for all message types with detailed logging and event emission.
  </Card>

  <Card title="Health Monitoring" icon="heart-pulse">
    Built-in ping/pong mechanism with connection health monitoring and latency tracking.
  </Card>
</CardGroup>

## Browser Compatibility

For browser usage, create a simplified version without Node.js dependencies:

```javascript theme={null}
class QuantCiteBrowserClient {
    constructor(apiKey, baseUrl = 'data.quantcite.com') {
        this.apiKey = apiKey;
        this.uri = `wss://${baseUrl}/api/v1/ws`;
        this.ws = null;
        this.authenticated = false;
        this.callbacks = {};
    }
    
    on(event, callback) {
        if (!this.callbacks[event]) {
            this.callbacks[event] = [];
        }
        this.callbacks[event].push(callback);
    }
    
    emit(event, data) {
        if (this.callbacks[event]) {
            this.callbacks[event].forEach(callback => callback(data));
        }
    }
    
    connect() {
        return new Promise((resolve, reject) => {
            this.ws = new WebSocket(this.uri);
            
            this.ws.onopen = () => {
                console.log('Connected to QuantCite');
            };
            
            this.ws.onmessage = (event) => {
                const data = JSON.parse(event.data);
                this.handleMessage(data);
                
                if (data.type === 'authentication_success') {
                    this.authenticated = true;
                    resolve(true);
                }
            };
            
            this.ws.onerror = (error) => {
                reject(error);
            };
        });
    }
    
    // Add other methods as needed...
}
```

<Note>
  Remember to replace `"demo_key_123"` with your actual API key. The client includes automatic reconnection and comprehensive error handling for production use.
</Note>
