UNPKG

tlab-trading-toolkit

Version:

A trading toolkit for building advanced trading bots on the GDAX platform

206 lines (205 loc) 9.12 kB
'use strict'; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); /*************************************************************************************************************************** * @license * * Copyright 2017 Coinbase, Inc. * * * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * * with the License. You may obtain a copy of the License at * * * * http://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * * License for the specific language governing permissions and limitations under the License. * ***************************************************************************************************************************/ const ExchangeFeed_1 = require("../ExchangeFeed"); const BitmexCommon_1 = require("./BitmexCommon"); const types_1 = require("../../lib/types"); const Orderbook_1 = require("../../lib/Orderbook"); const ProductMap_1 = require("../ProductMap"); class BitmexMarketFeed extends ExchangeFeed_1.ExchangeFeed { static product(genericProduct) { return ProductMap_1.ProductMap.ExchangeMap.get('Bitmex').getExchangeProduct(genericProduct) || genericProduct; } static genericProduct(exchangeProduct) { return ProductMap_1.ProductMap.ExchangeMap.get('Bitmex').getGenericProduct(exchangeProduct) || exchangeProduct; } static getMarket(genericProduct) { return ProductMap_1.ProductMap.ExchangeMap.get('Bitmex').getMarket(genericProduct); } static getMarketForExchangeProduct(exchangeProduct) { return ProductMap_1.ProductMap.ExchangeMap.get('Bitmex').getMarket(BitmexMarketFeed.genericProduct(exchangeProduct)); } constructor(config) { if (!config.wsUrl) { config.wsUrl = BitmexCommon_1.BITMEX_WS_FEED; } super(config); this.owner = 'BitMEX'; this.feedUrl = config.wsUrl; this.seq = 0; this.connect(); setInterval(() => { this.ping(); }, 25 * 1000); } subscribe(productIds) { return __awaiter(this, void 0, void 0, function* () { this.productSequences = {}; this.logger.log('debug', `Subscribing to the following symbols: ${JSON.stringify(productIds)}`); productIds.forEach((productId) => { this.productSequences[productId] = -1; const subscribeMessage = { op: 'subscribe', args: [`orderBookL2:${productId}`, `trade:${productId}`], }; this.send(JSON.stringify(subscribeMessage)); }); return true; }); } onOpen() { // Nothing for now } handleMessage(rawMsg) { const msg = JSON.parse(rawMsg); if (msg.error) { const errMsg = { message: `Error while subscribing to symbols`, }; this.push(errMsg); } else if (msg.table === 'trade') { // trade message const tradeMsg = msg; this.handleTrade(tradeMsg); } else if (msg.action) { if (msg.action === 'partial') { // orderbook snapshot const snapshotMsg = msg; this.handleSnapshot(snapshotMsg); } else { // orderbook update const updateMsg = msg; this.handleOrderbookUpdate(updateMsg); } } else if (msg.success !== undefined) { // subscription response const subscriptionResMSg = msg; this.handleSubscriptionSuccess(subscriptionResMSg); } else if (msg.info) { // welcome message this.logger.log('debug', 'Received welcome message from BitMEX WS feed.'); } else { // unhandled/unexpected message const unkMsg = { type: 'unknown', time: new Date(), origin: msg, }; this.push(unkMsg); } } /** * Gets the next sequence number, incrementing it for the next time it's called. */ getSeq() { this.seq += 1; return this.seq; } handleSnapshot(snapshot) { // (re)initialize our order id map const newIdMap = snapshot.data.reduce((acc, { id, price }) => (Object.assign({}, acc, { [id]: price })), {}); var orderIdMap = new Map(); var exchangeSymbol = snapshot.data[0].symbol; this.orderIdMap = orderIdMap.set(exchangeSymbol, newIdMap); const mapLevelUpdates = ({ id, price, size, side }) => Orderbook_1.PriceLevelFactory(price, size, side.toLowerCase()); const asks = snapshot.data .filter(({ side }) => side === 'Sell') .map(mapLevelUpdates); const bids = snapshot.data .filter(({ side }) => side === 'Buy') .map(mapLevelUpdates); const priceDataToLvl3 = ({ price, size, side, id }) => ({ price: types_1.Big(price), size: types_1.Big(size), side: side.toLowerCase(), id: id.toString(), }); const orderPool = snapshot.data.reduce((acc, pd) => (Object.assign({}, acc, { [pd.id.toString()]: priceDataToLvl3(pd) })), {}); this.productSequences[exchangeSymbol] = 0; const snapshotMsg = { time: new Date(), sequence: 0, type: 'snapshot', productId: BitmexMarketFeed.genericProduct(exchangeSymbol), asks, bids, orderPool, }; this.push(snapshotMsg); } nextSequence(exchangeSymbol) { this.productSequences[exchangeSymbol] = this.productSequences[exchangeSymbol] + 1; return this.productSequences[exchangeSymbol]; } handleOrderbookUpdate(updates) { updates.data.forEach((update) => { var orderMap = this.orderIdMap.get(update.symbol); if (!orderMap) return; const price = orderMap[update.id]; if (update.price) { // insert this.orderIdMap.get(update.symbol)[update.id] = update.price; } else if (!update.size) { // delete delete this.orderIdMap.get(update.symbol)[update.id]; } const message = { time: new Date(), sequence: this.nextSequence(update.symbol), type: 'level', productId: BitmexMarketFeed.genericProduct(update.symbol), price: (price ? price : update.price).toString(), size: update.size ? update.size.toString() : '0', side: update.side.toLowerCase(), count: 1, }; this.push(message); }); } handleTrade(trades) { trades.data.forEach((trade) => { const message = { type: 'trade', productId: BitmexMarketFeed.genericProduct(trade.symbol), time: new Date(trade.timestamp), tradeId: trade.trdMatchID, price: trade.price.toString(), size: trade.size.toString(), side: trade.side.toLowerCase(), }; this.push(message); }); } handleSubscriptionSuccess(successMsg) { // TODO } } exports.BitmexMarketFeed = BitmexMarketFeed;