UNPKG

@itick/browser-sdk

Version:

Official iTick API SDK for browser. Real-time & historical data for global Stocks, Forex, Crypto, Indices, Futures, Funds, Precious Metals. REST (OHLCV/K-line) + low-latency WebSocket. Promise-based, TypeScript-ready. For quant trading & fintech

212 lines 7.35 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const index_1 = require("./index"); class SocketClient { constructor(token, options) { // WebSocket connection related properties this.socket = null; this.isRunning = false; this.reconnectAttempts = 0; this.pingInterval = null; this.reconnectTimeout = null; // Callback functions this.messageHandlers = []; this.errorHandlers = []; this.openHandlers = []; this.closeHandlers = []; // Subscription information storage this.lastSubscription = null; this.wssURL = options.wssURL ?? 'wss://api.itick.org'; this.wsPath = options.wsPath; this.token = token; this.RECONNECT_INTERVAL = options.reconnectInterval ?? 5000; this.MAX_RECONNECT_ATTEMPTS = options.maxReconnectTimes ?? 0; this.PING_INTERVAL = options.pingInterval ?? 30000; this.lastSubscription = options.subscribeData ?? null; this._connectWebSocket(); } /** * WebSocket Message Handler */ onSocketMessage(handler) { this.messageHandlers.push(handler); } /** * WebSocket Error Handler */ onSocketError(handler) { this.errorHandlers.push(handler); } /** * WebSocket Connection Open Handler */ onSocketOpen(handler) { this.openHandlers.push(handler); } /** * WebSocket Connection Close Handler */ onSocketClose(handler) { this.closeHandlers.push(handler); } /** * Internal WebSocket Connection Method */ async _connectWebSocket() { const url = (0, index_1.buildWebSocketUrl)(this.wssURL, this.wsPath || ''); try { this.isRunning = true; this.socket = await (0, index_1.createWebSocket)(url, this.token); this.socket.onopen = () => { this.reconnectAttempts = 0; this._startPing(); this.openHandlers.forEach(handler => handler()); // Subscribe to data after successful connection this._resubscribeLast(); }; this.socket.onmessage = (event) => { const messageStr = typeof event.data === 'string' ? event.data : event.data.toString(); this.messageHandlers.forEach(handler => handler(JSON.parse(messageStr))); }; this.socket.onclose = (event) => { this._stopPing(); this.closeHandlers.forEach(handler => handler(event)); if (this.isRunning) { this._scheduleReconnect(); } }; this.socket.onerror = (event) => { this.errorHandlers.forEach(handler => handler(event)); }; } catch (error) { this.errorHandlers.forEach(handler => handler(error)); if (this.isRunning) { this._scheduleReconnect(); } } } /** * Start heartbeat ping */ _startPing() { this._stopPing(); this.pingInterval = setInterval(() => { if (this.socket && this.socket.readyState === WebSocket.OPEN) { this.socket.send(JSON.stringify({ ac: "ping", params: Date.now() })); } }, this.PING_INTERVAL); } /** * Stop heartbeat ping */ _stopPing() { if (this.pingInterval) { clearInterval(this.pingInterval); this.pingInterval = null; } } /** * Schedule reconnection */ _scheduleReconnect() { // When MAX_RECONNECT_ATTEMPTS is 0, it means unlimited reconnection if (this.MAX_RECONNECT_ATTEMPTS > 0 && this.reconnectAttempts >= this.MAX_RECONNECT_ATTEMPTS) { this.errorHandlers.forEach(handler => handler(new Error('Maximum reconnect attempts reached'))); return; } this.reconnectAttempts++; this.reconnectTimeout = setTimeout(() => { if (this.isRunning) { this._connectWebSocket().catch((error) => { }); } }, this.RECONNECT_INTERVAL); } /** * WebSocket Subscription Method * @description * message object must contain ac, types and params fields * - ac: operation type, must be "subscribe" or "unsubscribe" * - types: subscription types, can be comma-separated string or string array, supports "quote", "depth", "tick", "kline@1m" etc. kline@1m can also be written as kline@1, SDK will automatically convert to correct format * - params: subscription parameters, can be comma-separated string or string array, format is "code$region" (e.g., "AAPL$US") SDK will automatically handle formatting * - String array format is recommended for clearer code and fewer errors * @example * Example 1: Subscribe to AAPL and TSA real-time quotes/order book/trades/1-minute K-line data * ```json * { * "ac": "subscribe", * "types": "quote,depth,tick,kline@1", * "params": "AAPL$US,TSA$US" * } * ``` * Example 2: Subscribe to AAPL and TSA real-time quotes/order book/trades/1-minute K-line data * ```json * { * "ac": "subscribe", * "types": ["quote","depth","tick","kline@1m"], * "params": ["AAPL$US","TSA$US"] * } * ``` * * Both methods can correctly subscribe, SDK will automatically handle type conversion and parameter formatting * */ subscribeSocket(data) { if (!this.socket || this.socket.readyState !== WebSocket.OPEN) { throw new Error('WebSocket not connected'); } try { let { ac, types, codes } = data; const type = (0, index_1.parseeSubscribeType)(types); const symbol = (0, index_1.parseSymbols)(codes); this.socket.send(JSON.stringify({ ac, types: type, params: symbol })); } catch (err) { console.error(err); } } /** * Restore subscription after reconnection */ _resubscribeLast() { if (!this.lastSubscription) return; try { let { types, codes } = this.lastSubscription; const type = (0, index_1.parseeSubscribeType)(types); const symbol = (0, index_1.parseSymbols)(codes); this.socket?.send(JSON.stringify({ ac: 'subscribe', types: type, params: symbol })); } catch (err) { console.error(err); } } /** * Close WebSocket */ closeWebSocket() { this.isRunning = false; this._stopPing(); if (this.reconnectTimeout) { clearTimeout(this.reconnectTimeout); } if (this.socket) { this.socket.close(); } // Clear last subscription information when closing this.lastSubscription = null; } /** * Check if WebSocket is connected */ checkSocketConnected() { if (!this.socket) return false; return this.socket.readyState === WebSocket.OPEN; } disconnectSocket() { this.closeWebSocket(); } } exports.default = SocketClient; //# sourceMappingURL=SocketClient.js.map