ccxws
Version:
Websocket client for 37 cryptocurrency exchanges
313 lines • 10.8 kB
JavaScript
"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;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BithumbClient = void 0;
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/restrict-plus-operands */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
const moment_1 = __importDefault(require("moment"));
const BasicClient_1 = require("../BasicClient");
const Debounce_1 = require("../flowcontrol/Debounce");
const Throttle_1 = require("../flowcontrol/Throttle");
const Level2Point_1 = require("../Level2Point");
const Level2Snapshots_1 = require("../Level2Snapshots");
const Level2Update_1 = require("../Level2Update");
const Ticker_1 = require("../Ticker");
const Trade_1 = require("../Trade");
const https = __importStar(require("../Https"));
const NotImplementedFn_1 = require("../NotImplementedFn");
class BithumbClient extends BasicClient_1.BasicClient {
constructor({ wssPath = "wss://pubwss.bithumb.com/pub/ws", watcherMs } = {}) {
super(wssPath, "Bithumb", undefined, watcherMs);
this._sendSubTicker = NotImplementedFn_1.NotImplementedFn;
this._sendSubCandles = NotImplementedFn_1.NotImplementedFn;
this._sendUnsubCandles = NotImplementedFn_1.NotImplementedFn;
this._sendSubTrades = NotImplementedFn_1.NotImplementedFn;
this._sendSubLevel2Snapshots = NotImplementedFn_1.NotImplementedFn;
this._sendUnsubLevel2Snapshots = NotImplementedFn_1.NotImplementedFn;
this._sendSubLevel2Updates = NotImplementedFn_1.NotImplementedFn;
this._sendSubLevel3Snapshots = NotImplementedFn_1.NotImplementedFn;
this._sendUnsubLevel3Snapshots = NotImplementedFn_1.NotImplementedFn;
this._sendSubLevel3Updates = NotImplementedFn_1.NotImplementedFn;
this._sendUnsubLevel3Updates = NotImplementedFn_1.NotImplementedFn;
this._restL2SnapshotPath = "https://api.bithumb.com/public/orderbook";
this.hasTickers = true;
this.hasTrades = true;
this.hasLevel2Updates = true;
this.remoteIdMap = new Map();
this.restThrottleMs = 50;
this._requestLevel2Snapshot = (0, Throttle_1.throttle)(this.__requestLevel2Snapshot.bind(this), this.restThrottleMs); // prettier-ignore
this._sendSubTicker = (0, Debounce_1.debounce)(this.__sendSubTicker.bind(this));
this._sendSubTrades = (0, Debounce_1.debounce)(this.__sendSubTrades.bind(this));
this._sendSubLevel2Updates = (0, Debounce_1.debounce)(this.__sendSubLevel2Updates.bind(this));
}
__sendSubTicker() {
const symbols = Array.from(this._tickerSubs.keys());
this._wss.send(JSON.stringify({
type: "ticker",
symbols,
tickTypes: ["24H"],
}));
}
_sendUnsubTicker() {
//
}
__sendSubTrades() {
const symbols = Array.from(this._tradeSubs.keys());
this._wss.send(JSON.stringify({
type: "transaction",
symbols,
}));
}
_sendUnsubTrades() {
//
}
__sendSubLevel2Updates() {
const symbols = Array.from(this._level2UpdateSubs.keys());
for (const symbol of symbols) {
this._requestLevel2Snapshot(this._level2UpdateSubs.get(symbol));
}
this._wss.send(JSON.stringify({
type: "orderbookdepth",
symbols,
}));
}
_sendUnsubLevel2Updates() {
//
}
_onMessage(raw) {
const msg = JSON.parse(raw);
// console.log(raw);
// tickers
if (msg.type === "ticker") {
const remoteId = msg.content.symbol;
const market = this._tickerSubs.get(remoteId);
if (!market)
return;
const ticker = this._constructTicker(msg.content, market);
this.emit("ticker", ticker, market);
return;
}
// trades
if (msg.type === "transaction") {
for (const datum of msg.content.list) {
const remoteId = datum.symbol;
const market = this._tradeSubs.get(remoteId);
if (!market)
return;
const trade = this._constructTrade(datum, market);
this.emit("trade", trade, market);
}
return;
}
// l2pudate
if (msg.type === "orderbookdepth") {
const remoteId = msg.content.list[0].symbol;
const market = this._level2UpdateSubs.get(remoteId);
if (!market)
return;
const update = this._constructL2Update(msg, market);
this.emit("l2update", update, market);
return;
}
}
/**
{
"type":"ticker",
"content":{
"tickType":"24H",
"date":"20200814",
"time":"063809",
"openPrice":"13637000",
"closePrice":"13714000",
"lowPrice":"13360000",
"highPrice":"13779000",
"value":"63252021221.2101",
"volume":"4647.44384349",
"sellVolume":"2372.30829641",
"buyVolume":"2275.03363265",
"prevClosePrice":"13601000",
"chgRate":"0.56",
"chgAmt":"77000",
"volumePower":"95.89",
"symbol":"BTC_KRW"
}
}
*/
_constructTicker(data, market) {
const timestamp = moment_1.default
.parseZone(data.date + data.time + "+09:00", "YYYYMMDDhhmmssZ")
.valueOf();
return new Ticker_1.Ticker({
exchange: this.name,
base: market.base,
quote: market.quote,
timestamp,
last: data.closePrice,
open: data.openPrice,
high: data.highPrice,
low: data.lowPrice,
volume: data.volume,
quoteVolume: data.value,
change: data.chgAmt,
changePercent: data.chgRate,
});
}
/**
{
"type":"transaction",
"content":
{
"list":
[
{
"buySellGb":"1",
"contPrice":"485900",
"contQty":"0.196",
"contAmt":"95236.400",
"contDtm":"2020-08-14 06:28:41.621909",
"updn":"dn",
"symbol":"ETH_KRW"
},
{
"buySellGb":"2",
"contPrice":"486400",
"contQty":"5.4277",
"contAmt":"2640033.2800",
"contDtm":"2020-08-14 06:28:42.453539",
"updn":"up",
"symbol":"ETH_KRW"
}
]
}
}
*/
_constructTrade(datum, market) {
const unix = moment_1.default
.parseZone(datum.contDtm + "+09:00", "YYYY-MM-DD hh:mm:ss.SSSSSS")
.valueOf();
const side = datum.buySellGb == 1 ? "buy" : "sell";
const price = datum.contPrice;
const amount = datum.contQty;
return new Trade_1.Trade({
exchange: this.name,
base: market.base,
quote: market.quote,
side,
unix,
price,
amount,
});
}
/**
{
"type": "orderbookdepth",
"content": {
"list": [
{
"symbol": "BTC_KRW",
"orderType": "ask",
"price": "13811000",
"quantity": "0",
"total": "0"
},
{
"symbol": "BTC_KRW",
"orderType": "ask",
"price": "13733000",
"quantity": "0.0213",
"total": "1"
},
{
"symbol": "BTC_KRW",
"orderType": "bid",
"price": "6558000",
"quantity": "0",
"total": "0"
},
{
"symbol": "BTC_KRW",
"orderType": "bid",
"price": "13728000",
"quantity": "0.0185",
"total": "1"
}
],
"datetime": "1597355189967132"
}
}
*/
_constructL2Update(msg, market) {
const timestampMs = Math.trunc(Number(msg.content.datetime) / 1000);
const asks = [];
const bids = [];
for (const data of msg.content.list) {
const point = new Level2Point_1.Level2Point(data.price, data.quantity, data.total);
if (data.orderType === "bid")
bids.push(point);
else
asks.push(point);
}
return new Level2Update_1.Level2Update({
exchange: this.name,
base: market.base,
quote: market.quote,
timestampMs,
asks,
bids,
datetime: msg.content.datetime,
});
}
async __requestLevel2Snapshot(market) {
let failed = false;
try {
const remote_id = market.id;
const uri = `${this._restL2SnapshotPath}/${remote_id}`;
const raw = (await https.get(uri));
const timestampMs = Number(raw.data.timestamp);
const asks = raw.data.asks.map(p => new Level2Point_1.Level2Point(p.price, p.quantity));
const bids = raw.data.bids.map(p => new Level2Point_1.Level2Point(p.price, p.quantity));
const snapshot = new Level2Snapshots_1.Level2Snapshot({
exchange: this.name,
base: market.base,
quote: market.quote,
timestampMs,
asks,
bids,
});
this.emit("l2snapshot", snapshot, market);
}
catch (ex) {
this.emit("error", ex);
failed = true;
}
finally {
if (failed)
this._requestLevel2Snapshot(market);
}
}
}
exports.BithumbClient = BithumbClient;
//# sourceMappingURL=BithumbClient.js.map