UNPKG

betfair-exchange-api

Version:

A TypeScript client for the Betfair Exchange API

229 lines (228 loc) 8.52 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.BetfairStreamClient = exports.SegmentType = exports.ChangeType = exports.StreamOperation = void 0; const tls = __importStar(require("tls")); const events_1 = require("events"); // ============================================================================ // Stream API Types // ============================================================================ var StreamOperation; (function (StreamOperation) { StreamOperation["AUTHENTICATION"] = "authentication"; StreamOperation["MARKET_SUBSCRIPTION"] = "marketSubscription"; StreamOperation["ORDER_SUBSCRIPTION"] = "orderSubscription"; StreamOperation["HEARTBEAT"] = "heartbeat"; })(StreamOperation || (exports.StreamOperation = StreamOperation = {})); var ChangeType; (function (ChangeType) { ChangeType["SUB_IMAGE"] = "SUB_IMAGE"; ChangeType["RESUB_DELTA"] = "RESUB_DELTA"; ChangeType["HEARTBEAT"] = "HEARTBEAT"; })(ChangeType || (exports.ChangeType = ChangeType = {})); var SegmentType; (function (SegmentType) { SegmentType["SEG_START"] = "SEG_START"; SegmentType["SEG"] = "SEG"; SegmentType["SEG_END"] = "SEG_END"; })(SegmentType || (exports.SegmentType = SegmentType = {})); class BetfairStreamClient extends events_1.EventEmitter { constructor(appKey, sessionToken, config) { super(); this.host = 'stream-api.betfair.com'; this.port = 443; this.socket = null; this.buffer = ''; this.connectionId = null; this.authenticated = false; this.requestIdCounter = 1; this.appKey = appKey; this.sessionToken = sessionToken; } connect() { if (this.socket) { this.socket.destroy(); this.socket = null; } this.socket = tls.connect({ host: this.host, port: this.port, }, () => { this.emit('connected'); this.authenticate(); }); this.socket.on('data', (data) => this.handleData(data)); this.socket.on('close', () => { this.authenticated = false; this.connectionId = null; this.emit('disconnected'); }); this.socket.on('error', (error) => this.emit('error', error)); } disconnect() { if (this.socket) { this.socket.destroy(); this.socket = null; } } authenticate() { const message = { op: StreamOperation.AUTHENTICATION, id: this.getNextRequestId(), appKey: this.appKey, session: this.sessionToken, }; this.sendMessage(message); } subscribeToMarkets(marketFilter = {}, marketDataFilter = {}, conflateMs, heartbeatMs, segmentationEnabled = true) { if (!this.authenticated) { throw new Error('Client not authenticated. Call authenticate() first.'); } this.conflateMs = conflateMs; this.heartbeatMs = heartbeatMs; this.segmentationEnabled = segmentationEnabled; const message = { op: StreamOperation.MARKET_SUBSCRIPTION, id: this.getNextRequestId(), marketFilter, marketDataFilter, conflateMs, heartbeatMs, segmentationEnabled, initialClk: this.marketSubscriptionInitialClk, clk: this.marketSubscriptionClk, }; this.sendMessage(message); } subscribeToOrders(orderFilter = {}, conflateMs, heartbeatMs, segmentationEnabled = true) { if (!this.authenticated) { throw new Error('Client not authenticated. Call authenticate() first.'); } this.conflateMs = conflateMs; this.heartbeatMs = heartbeatMs; this.segmentationEnabled = segmentationEnabled; const message = { op: StreamOperation.ORDER_SUBSCRIPTION, id: this.getNextRequestId(), orderFilter, conflateMs, heartbeatMs, segmentationEnabled, initialClk: this.orderSubscriptionInitialClk, clk: this.orderSubscriptionClk, }; this.sendMessage(message); } sendHeartbeat() { if (!this.authenticated) { throw new Error('Client not authenticated. Call authenticate() first.'); } const message = { op: StreamOperation.HEARTBEAT, id: this.getNextRequestId(), }; this.sendMessage(message); } handleData(data) { this.buffer += data.toString(); let newlineIndex; while ((newlineIndex = this.buffer.indexOf('\r\n')) !== -1) { const message = this.buffer.substring(0, newlineIndex); this.buffer = this.buffer.substring(newlineIndex + 2); try { if (message.trim().length > 0) { this.processMessage(JSON.parse(message)); } } catch (error) { this.emit('error', new Error(`Failed to parse message: ${error instanceof Error ? error.message : 'Unknown error'}`)); } } } processMessage(message) { switch (message.op) { case 'connection': this.connectionId = message.connectionId; this.emit('connection', message); break; case 'status': if (message.id === 1 && message.statusCode === 'SUCCESS') { this.authenticated = true; } else if (message.errorCode) { this.emit('error', new Error(`Status error: ${message.errorCode} - ${message.errorMessage}`)); } this.emit('status', message); break; case 'mcm': if (message.initialClk) { this.marketSubscriptionInitialClk = message.initialClk; } if (message.clk) { this.marketSubscriptionClk = message.clk; } this.emit('marketChange', message); break; case 'ocm': if (message.initialClk) { this.orderSubscriptionInitialClk = message.initialClk; } if (message.clk) { this.orderSubscriptionClk = message.clk; } this.emit('orderChange', message); break; default: this.emit('error', new Error(`Unknown message operation: ${message.op}`)); } } sendMessage(message) { if (!this.socket) { throw new Error('Not connected. Call connect() first.'); } const jsonMessage = JSON.stringify(message); this.socket.write(`${jsonMessage}\r\n`); } getNextRequestId() { return this.requestIdCounter++; } getConnectionId() { return this.connectionId; } isAuthenticated() { return this.authenticated; } } exports.BetfairStreamClient = BetfairStreamClient;