UNPKG

ccxws

Version:

Websocket client for 37 cryptocurrency exchanges

525 lines 18.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (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 (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.LedgerXClient = void 0; /* eslint-disable @typescript-eslint/member-ordering */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-floating-promises */ /* eslint-disable @typescript-eslint/no-empty-function */ const BasicClient_1 = require("../BasicClient"); const Level3Point_1 = require("../Level3Point"); const Level3Snapshot_1 = require("../Level3Snapshot"); const https = __importStar(require("../Https")); const Trade_1 = require("../Trade"); const Level3Update_1 = require("../Level3Update"); const NotImplementedFn_1 = require("../NotImplementedFn"); /** * LedgerX is defined in https://docs.ledgerx.com/reference#connecting * This socket uses a unified stream for ALL market data. So we will leverage * subscription filtering to only reply with values that of are of interest. */ class LedgerXClient extends BasicClient_1.BasicClient { constructor({ wssPath = "wss://api.ledgerx.com/ws?token=", apiKey, watcherMs, } = {}) { super(wssPath + apiKey, "LedgerX", undefined, watcherMs); this._sendSubTicker = NotImplementedFn_1.NotImplementedFn; this._sendSubCandles = NotImplementedFn_1.NotImplementedFn; this._sendUnsubCandles = NotImplementedFn_1.NotImplementedFn; this._sendUnsubTicker = NotImplementedFn_1.NotImplementedFn; this._sendSubLevel2Snapshots = NotImplementedFn_1.NotImplementedFn; this._sendUnsubLevel2Snapshots = NotImplementedFn_1.NotImplementedFn; this._sendSubLevel2Updates = NotImplementedFn_1.NotImplementedFn; this._sendUnsubLevel2Updates = NotImplementedFn_1.NotImplementedFn; this._sendSubLevel3Snapshots = NotImplementedFn_1.NotImplementedFn; this._sendUnsubLevel3Snapshots = NotImplementedFn_1.NotImplementedFn; this._sendUnsubLevel3Updates = NotImplementedFn_1.NotImplementedFn; this.hasTrades = true; this.hasLevel3Updates = true; this.runId = 0; this.apiKey = apiKey; } _sendSubTrades() { } _sendUnsubTrades() { } _sendSubLevel3Updates(remote_id, market) { this._requestLevel3Snapshot(market); } _sendUnSubLevel3Updates() { } _onMessage(msg) { this.emit("raw", msg); const json = JSON.parse(msg); if (json.type === "auth_success") { return; } if (json.type === "book_top") { return; } if (json.positions !== undefined) { return; } if (json.collateral !== undefined) { return; } if (json.type === "exposure_reports") { return; } if (json.type === "heartbeat") { this._watcher.markAlive(); // update the run_id if it's changed if (this.runId !== json.run_id) { this.runId = json.run_id; } return; } if (json.type === "action_report") { // insert event if (json.status_type === 200) { const market = this._level3UpdateSubs.get(json.contract_id) || this._level3UpdateSubs.get(json.contract_id.toString()); if (!market) return; const update = this._constructL3Insert(json, market); this.emit("l3update", update, market, json); return; } // trade event, filled either partial or fully if (json.status_type === 201) { // check for trade subscription let market = this._tradeSubs.get(json.contract_id) || this._tradeSubs.get(json.contract_id.toString()); // prettier-ignore if (market) { const trade = this._constructTrade(json, market); this.emit("trade", trade, market, json); } // check for l3 subscription market = this._level3UpdateSubs.get(json.contract_id) || this._level3UpdateSubs.get(json.contract_id.toString()); if (market) { const update = this._constructL3Trade(json, market); this.emit("l3update", update, market, json); } return; } // cancel event if (json.status_type === 203) { const market = this._level3UpdateSubs.get(json.contract_id) || this._level3UpdateSubs.get(json.contract_id.toString()); if (!market) return; const update = this._constructL3Cancel(json, market); this.emit("l3update", update, market, json); return; } // cancelled and replaced event if (json.status_type === 204) { const market = this._level3UpdateSubs.get(json.contract_id) || this._level3UpdateSubs.get(json.contract_id.toString()); if (!market) return; const update = this._constructL3Replace(json, market); this.emit("l3update", update, market, json); return; } } } /** * Obtains the orderbook via REST */ async _requestLevel3Snapshot(market) { try { const uri = `https://trade.ledgerx.com/api/book-states/${market.id}?token=${this.apiKey}`; const { data } = await https.get(uri); const sequenceId = data.clock; const asks = []; const bids = []; for (const row of data.book_states) { const orderId = row.mid; const price = row.price.toFixed(2); const size = row.size.toFixed(); const point = new Level3Point_1.Level3Point(orderId, price, size); if (row.is_ask) asks.push(point); else bids.push(point); } const snapshot = new Level3Snapshot_1.Level3Snapshot({ exchange: this.name, base: market.base, quote: market.quote, sequenceId, asks, bids, }); this.emit("l3snapshot", snapshot, market); } catch (ex) { // TODO handle this properly this.emit("error", ex); } } /** { mid: 'f4c34b09de0b4064a33b7b46f8180022', filled_size: 5, size: 0, inserted_price: 0, updated_time: 1597173352257155800, inserted_size: 0, timestamp: 1597173352257176800, ticks: 78678024531551, price: 0, original_price: 16000, status_type: 201, order_type: 'customer_limit_order', status_reason: 52, filled_price: 16000, is_volatile: false, clock: 24823, vwap: 16000, is_ask: false, inserted_time: 1597173352257155800, type: 'action_report', original_size: 5, contract_id: 22204639 } { mid: '885be81549974faf88e4430f6046513d', filled_size: 5, size: 0, inserted_price: 0, updated_time: 1597164994095326700, inserted_size: 0, timestamp: 1597173352258250800, ticks: 78678025605522, price: 0, original_price: 16000, status_type: 201, order_type: 'customer_limit_order', status_reason: 0, filled_price: 16000, is_volatile: false, clock: 24824, vwap: 16000, is_ask: true, inserted_time: 1597164994095326700, type: 'action_report', original_size: 10, contract_id: 22204639 } */ _constructTrade(msg, market) { let buyOrderId; let sellOrderId; if (msg.is_ask) sellOrderId = msg.mid; else buyOrderId = msg.mid; return new Trade_1.Trade({ exchange: this.name, base: market.base, quote: market.quote, tradeId: undefined, unix: Math.floor(msg.timestamp / 1e6), side: msg.is_ask ? "sell" : "buy", price: msg.filled_price.toFixed(8), amount: msg.filled_size.toFixed(8), buyOrderId, sellOrderId, open_interest: msg.open_interest, }); } /** * 200 - A resting limit order of size inserted_size @ price * inserted_price was inserted into book depth. { inserted_time: 1597176131501325800, timestamp: 1597176131501343700, filled_size: 0, ticks: 81457268698527, size: 1000, contract_id: 22202469, filled_price: 0, inserted_price: 165100, inserted_size: 1000, vwap: 0, is_volatile: true, mid: 'eecd8297c1dc42f1985f67c909540631', original_price: 165100, order_type: 'customer_limit_order', updated_time: 1597176131501325800, original_size: 1000, status_type: 200, status_reason: 0, type: 'action_report', price: 165100, clock: 260, is_ask: false } */ _constructL3Insert(msg, market) { const price = msg.price.toFixed(8); const size = msg.inserted_size.toFixed(8); const point = new Level3Point_1.Level3Point(msg.mid, price, size, { order_type: msg.order_type, status_type: msg.status_type, status_reason: msg.status_reason, is_volatile: msg.is_volatile, timestamp: msg.timestamp, ticks: msg.ticks, inserted_time: msg.inserted_time, updated_time: msg.updated_time, original_price: msg.original_price, original_size: msg.original_size, inserted_price: msg.inserted_price, inserted_size: msg.inserted_size, filled_price: msg.filled_price, filled_size: msg.filled_size, price: msg.price, size: msg.size, vwap: msg.vwap, }); const asks = []; const bids = []; if (msg.is_ask) asks.push(point); else bids.push(point); return new Level3Update_1.Level3Update({ exchange: this.name, base: market.base, quote: market.quote, sequenceId: msg.clock, timestampMs: Math.floor(msg.inserted_time / 1e6), runId: this.runId, asks, bids, }); } /** * 201 - A cross of size filled_size @ price filled_price occurred. * Subtract filled_size from the resting size for this order. { mid: '885be81549974faf88e4430f6046513d', filled_size: 5, size: 0, inserted_price: 0, updated_time: 1597164994095326700, inserted_size: 0, timestamp: 1597173352258250800, ticks: 78678025605522, price: 0, original_price: 16000, status_type: 201, order_type: 'customer_limit_order', status_reason: 0, filled_price: 16000, is_volatile: false, clock: 24824, vwap: 16000, is_ask: true, inserted_time: 1597164994095326700, type: 'action_report', original_size: 10, contract_id: 22204639 } */ _constructL3Trade(msg, market) { const price = msg.original_price.toFixed(8); const size = (msg.original_size - msg.filled_size).toFixed(8); const point = new Level3Point_1.Level3Point(msg.mid, price, size, { order_type: msg.order_type, status_type: msg.status_type, status_reason: msg.status_reason, is_volatile: msg.is_volatile, timestamp: msg.timestamp, ticks: msg.ticks, inserted_time: msg.inserted_time, updated_time: msg.updated_time, original_price: msg.original_price, original_size: msg.original_size, inserted_price: msg.inserted_price, inserted_size: msg.inserted_size, filled_price: msg.filled_price, filled_size: msg.filled_size, price: msg.price, size: msg.size, vwap: msg.vwap, open_interest: msg.open_interest, }); const asks = []; const bids = []; if (msg.is_ask) asks.push(point); else bids.push(point); return new Level3Update_1.Level3Update({ exchange: this.name, base: market.base, quote: market.quote, sequenceId: msg.clock, timestampMs: Math.floor(msg.inserted_time / 1e6), runId: this.runId, asks, bids, }); } /** * 203 - An order was cancelled. Remove this order from book depth. { inserted_time: 1597176853952381700, timestamp: 1597176857137740800, filled_size: 0, ticks: 82182905095242, size: 0, contract_id: 22204631, filled_price: 0, inserted_price: 0, inserted_size: 0, vwap: 0, is_volatile: true, mid: 'b623fdd6fae14fcbbcb9ab3b6b9b3771', original_price: 51300, order_type: 'customer_limit_order', updated_time: 1597176853952381700, original_size: 1, status_type: 203, status_reason: 0, type: 'action_report', price: 0, clock: 506, is_ask: false } */ _constructL3Cancel(msg, market) { const price = msg.original_price.toFixed(8); const size = (0).toFixed(8); const point = new Level3Point_1.Level3Point(msg.mid, price, size, { order_type: msg.order_type, status_type: msg.status_type, status_reason: msg.status_reason, is_volatile: msg.is_volatile, timestamp: msg.timestamp, ticks: msg.ticks, inserted_time: msg.inserted_time, updated_time: msg.updated_time, original_price: msg.original_price, original_size: msg.original_size, inserted_price: msg.inserted_price, inserted_size: msg.inserted_size, filled_price: msg.filled_price, filled_size: msg.filled_size, price: msg.price, size: msg.size, vwap: msg.vwap, open_interest: msg.open_interest, }); const asks = []; const bids = []; if (msg.is_ask) asks.push(point); else bids.push(point); return new Level3Update_1.Level3Update({ exchange: this.name, base: market.base, quote: market.quote, sequenceId: msg.clock, timestampMs: Math.floor(msg.inserted_time / 1e6), runId: this.runId, asks, bids, }); } /** * 204 - An order was cancelled and replaced. The new order retains the * existing mid, and can only reflect an update in size and not price. * Overwrite the resting order size with inserted_size. * { "status_type": 204, "inserted_size": 12, "original_price": 59000, "open_interest": 121, "filled_size": 0, "updated_time": 1623074768372895949, "clock": 40011, "size": 12, "timestamp": 1623074768372897897, "status_reason": 0, "vwap": 0, "inserted_time": 1623074764668677182, "price": 59000, "type": "action_report", "is_ask": true, "original_size": 12, "order_type": "customer_limit_order", "is_volatile": true, "ticks": 25980094140252686, "filled_price": 0, "mid": "c071baaa458a411db184cb6874e86d69", "inserted_price": 59000, "contract_id": 22216779 } */ _constructL3Replace(msg, market) { const price = msg.original_price.toFixed(8); const size = msg.inserted_size.toFixed(8); const point = new Level3Point_1.Level3Point(msg.mid, price, size, { order_type: msg.order_type, status_type: msg.status_type, status_reason: msg.status_reason, is_volatile: msg.is_volatile, timestamp: msg.timestamp, ticks: msg.ticks, inserted_time: msg.inserted_time, updated_time: msg.updated_time, original_price: msg.original_price, original_size: msg.original_size, inserted_price: msg.inserted_price, inserted_size: msg.inserted_size, filled_price: msg.filled_price, filled_size: msg.filled_size, price: msg.price, size: msg.size, vwap: msg.vwap, open_interest: msg.open_interest, }); const asks = []; const bids = []; if (msg.is_ask) asks.push(point); else bids.push(point); return new Level3Update_1.Level3Update({ exchange: this.name, base: market.base, quote: market.quote, sequenceId: msg.clock, timestampMs: Math.floor(msg.inserted_time / 1e6), runId: this.runId, asks, bids, }); } } exports.LedgerXClient = LedgerXClient; //# sourceMappingURL=LedgerXClient.js.map