ccxt 4.0.91 → 4.0.94

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,8 @@
2
2
 
3
3
  var hitbtc$1 = require('../hitbtc.js');
4
4
  var Cache = require('../base/ws/Cache.js');
5
+ var sha256 = require('../static_dependencies/noble-hashes/sha256.js');
6
+ var errors = require('../base/errors.js');
5
7
 
6
8
  // ---------------------------------------------------------------------------
7
9
  // ---------------------------------------------------------------------------
@@ -11,145 +13,245 @@ class hitbtc extends hitbtc$1 {
11
13
  'has': {
12
14
  'ws': true,
13
15
  'watchTicker': true,
14
- 'watchTickers': false,
16
+ 'watchTickers': true,
15
17
  'watchTrades': true,
16
18
  'watchOrderBook': true,
17
- 'watchBalance': false,
19
+ 'watchBalance': true,
20
+ 'watchOrders': true,
18
21
  'watchOHLCV': true,
22
+ 'watchMyTrades': false,
19
23
  },
20
24
  'urls': {
21
25
  'api': {
22
- 'ws': 'wss://api.hitbtc.com/api/2/ws',
26
+ 'ws': {
27
+ 'public': 'wss://api.hitbtc.com/api/3/ws/public',
28
+ 'private': 'wss://api.hitbtc.com/api/3/ws/trading',
29
+ },
23
30
  },
24
31
  },
25
32
  'options': {
26
33
  'tradesLimit': 1000,
27
- 'methods': {
28
- 'orderbook': 'subscribeOrderbook',
29
- 'ticker': 'subscribeTicker',
30
- 'trades': 'subscribeTrades',
31
- 'ohlcv': 'subscribeCandles',
34
+ 'watchTicker': {
35
+ 'method': 'ticker/{speed}', // 'ticker/{speed}' or 'ticker/price/{speed}'
32
36
  },
37
+ 'watchTickers': {
38
+ 'method': 'ticker/{speed}', // 'ticker/{speed}','ticker/price/{speed}', 'ticker/{speed}/batch', or 'ticker/{speed}/price/batch''
39
+ },
40
+ 'watchOrderBook': {
41
+ 'method': 'orderbook/full', // 'orderbook/full', 'orderbook/{depth}/{speed}', 'orderbook/{depth}/{speed}/batch', 'orderbook/top/{speed}', or 'orderbook/top/{speed}/batch'
42
+ },
43
+ },
44
+ 'timeframes': {
45
+ '1m': 'M1',
46
+ '3m': 'M3',
47
+ '5m': 'M5',
48
+ '15m': 'M15',
49
+ '30m': 'M30',
50
+ '1h': 'H1',
51
+ '4h': 'H4',
52
+ '1d': 'D1',
53
+ '1w': 'D7',
54
+ '1M': '1M',
55
+ },
56
+ 'streaming': {
57
+ 'keepAlive': 4000,
33
58
  },
34
59
  });
35
60
  }
36
- async watchPublic(symbol, channel, timeframe = undefined, params = {}) {
61
+ async authenticate() {
62
+ /**
63
+ * @ignore
64
+ * @method
65
+ * @description authenticates the user to access private web socket channels
66
+ * @see https://api.hitbtc.com/#socket-authentication
67
+ * @returns {object} response from exchange
68
+ */
69
+ this.checkRequiredCredentials();
70
+ const url = this.urls['api']['ws']['private'];
71
+ const messageHash = 'authenticated';
72
+ const client = this.client(url);
73
+ const future = client.future(messageHash);
74
+ const authenticated = this.safeValue(client.subscriptions, messageHash);
75
+ if (authenticated === undefined) {
76
+ const timestamp = this.milliseconds();
77
+ const signature = this.hmac(this.encode(this.numberToString(timestamp)), this.encode(this.secret), sha256.sha256, 'hex');
78
+ const request = {
79
+ 'method': 'login',
80
+ 'params': {
81
+ 'type': 'HS256',
82
+ 'api_key': this.apiKey,
83
+ 'timestamp': timestamp,
84
+ 'signature': signature,
85
+ },
86
+ };
87
+ this.watch(url, messageHash, request, messageHash);
88
+ //
89
+ // {
90
+ // jsonrpc: '2.0',
91
+ // result: true
92
+ // }
93
+ //
94
+ // # Failure to return results
95
+ //
96
+ // {
97
+ // jsonrpc: '2.0',
98
+ // error: {
99
+ // code: 1002,
100
+ // message: 'Authorization is required or has been failed',
101
+ // description: 'invalid signature format'
102
+ // }
103
+ // }
104
+ //
105
+ }
106
+ return future;
107
+ }
108
+ async subscribePublic(name, symbols = undefined, params = {}) {
109
+ /**
110
+ * @ignore
111
+ * @method
112
+ * @param {string} name websocket endpoint name
113
+ * @param {[string]} [symbols] unified CCXT symbol(s)
114
+ * @param {object} [params] extra parameters specific to the hitbtc api
115
+ * @returns
116
+ */
37
117
  await this.loadMarkets();
38
- const marketId = this.marketId(symbol);
39
- const url = this.urls['api']['ws'];
40
- let messageHash = channel + ':' + marketId;
41
- if (timeframe !== undefined) {
42
- messageHash += ':' + timeframe;
118
+ const url = this.urls['api']['ws']['public'];
119
+ let messageHash = name;
120
+ if (symbols !== undefined) {
121
+ messageHash = messageHash + '::' + symbols.join(',');
43
122
  }
44
- const methods = this.safeValue(this.options, 'methods', {});
45
- const method = this.safeString(methods, channel, channel);
46
- const requestId = this.nonce();
47
123
  const subscribe = {
48
- 'method': method,
49
- 'params': {
50
- 'symbol': marketId,
51
- },
52
- 'id': requestId,
124
+ 'method': 'subscribe',
125
+ 'id': this.nonce(),
126
+ 'ch': name,
53
127
  };
54
- const request = this.deepExtend(subscribe, params);
128
+ const request = this.extend(subscribe, params);
55
129
  return await this.watch(url, messageHash, request, messageHash);
56
130
  }
131
+ async subscribePrivate(name, symbol = undefined, params = {}) {
132
+ /**
133
+ * @ignore
134
+ * @method
135
+ * @param {string} name websocket endpoint name
136
+ * @param {string} [symbol] unified CCXT symbol
137
+ * @param {object} [params] extra parameters specific to the hitbtc api
138
+ * @returns
139
+ */
140
+ await this.loadMarkets();
141
+ await this.authenticate();
142
+ const url = this.urls['api']['ws']['private'];
143
+ const splitName = name.split('_subscribe');
144
+ let messageHash = this.safeString(splitName, 0);
145
+ if (symbol !== undefined) {
146
+ messageHash = messageHash + '::' + symbol;
147
+ }
148
+ const subscribe = {
149
+ 'method': name,
150
+ 'params': params,
151
+ 'id': this.nonce(),
152
+ };
153
+ return await this.watch(url, messageHash, subscribe, messageHash);
154
+ }
57
155
  async watchOrderBook(symbol, limit = undefined, params = {}) {
58
156
  /**
59
157
  * @method
60
158
  * @name hitbtc#watchOrderBook
61
159
  * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
160
+ * @see https://api.hitbtc.com/#subscribe-to-full-order-book
161
+ * @see https://api.hitbtc.com/#subscribe-to-partial-order-book
162
+ * @see https://api.hitbtc.com/#subscribe-to-partial-order-book-in-batches
163
+ * @see https://api.hitbtc.com/#subscribe-to-top-of-book
164
+ * @see https://api.hitbtc.com/#subscribe-to-top-of-book-in-batches
62
165
  * @param {string} symbol unified symbol of the market to fetch the order book for
63
166
  * @param {int} [limit] the maximum amount of order book entries to return
64
167
  * @param {object} [params] extra parameters specific to the hitbtc api endpoint
65
- * @returns {object} A dictionary of [order book structures]{@link https://github.com/ccxt/ccxt/wiki/Manual#order-book-structure} indexed by market symbols
168
+ * @param {string} [params.method] 'orderbook/full', 'orderbook/{depth}/{speed}', 'orderbook/{depth}/{speed}/batch', 'orderbook/top/{speed}', or 'orderbook/top/{speed}/batch'
169
+ * @param {int} [params.depth] 5 , 10, or 20 (default)
170
+ * @param {int} [params.speed] 100 (default), 500, or 1000
171
+ * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
66
172
  */
67
- const orderbook = await this.watchPublic(symbol, 'orderbook', undefined, params);
68
- return orderbook.limit();
69
- }
70
- handleOrderBookSnapshot(client, message) {
71
- //
72
- // {
73
- // jsonrpc: "2.0",
74
- // method: "snapshotOrderbook",
75
- // params: {
76
- // ask: [
77
- // { price: "6927.75", size: "0.11991" },
78
- // { price: "6927.76", size: "0.06200" },
79
- // { price: "6927.85", size: "0.01000" },
80
- // ],
81
- // bid: [
82
- // { price: "6926.18", size: "0.16898" },
83
- // { price: "6926.17", size: "0.06200" },
84
- // { price: "6925.97", size: "0.00125" },
85
- // ],
86
- // symbol: "BTCUSD",
87
- // sequence: 494854,
88
- // timestamp: "2020-04-03T08:58:53.460Z"
89
- // }
90
- // }
91
- //
92
- const params = this.safeValue(message, 'params', {});
93
- const marketId = this.safeString(params, 'symbol');
94
- const market = this.safeMarket(marketId);
95
- const symbol = market['symbol'];
96
- const timestamp = this.parse8601(this.safeString(params, 'timestamp'));
97
- const nonce = this.safeInteger(params, 'sequence');
98
- if (symbol in this.orderbooks) {
99
- delete this.orderbooks[symbol];
173
+ const options = this.safeValue(this.options, 'watchOrderBook');
174
+ const defaultMethod = this.safeString(options, 'method', 'orderbook/full');
175
+ let name = this.safeString2(params, 'method', 'defaultMethod', defaultMethod);
176
+ const depth = this.safeString(params, 'depth', '20');
177
+ const speed = this.safeString(params, 'depth', '100');
178
+ if (name === 'orderbook/{depth}/{speed}') {
179
+ name = 'orderbook/D' + depth + '/' + speed + 'ms';
180
+ }
181
+ else if (name === 'orderbook/{depth}/{speed}/batch') {
182
+ name = 'orderbook/D' + depth + '/' + speed + 'ms/batch';
183
+ }
184
+ else if (name === 'orderbook/top/{speed}') {
185
+ name = 'orderbook/top/' + speed + 'ms';
100
186
  }
101
- const snapshot = this.parseOrderBook(params, symbol, timestamp, 'bid', 'ask', 'price', 'size');
102
- const orderbook = this.orderBook(snapshot);
103
- orderbook['nonce'] = nonce;
104
- this.orderbooks[symbol] = orderbook;
105
- const messageHash = 'orderbook:' + marketId;
106
- client.resolve(orderbook, messageHash);
187
+ else if (name === 'orderbook/top/{speed}/batch') {
188
+ name = 'orderbook/top/' + speed + 'ms/batch';
189
+ }
190
+ const market = this.market(symbol);
191
+ const request = {
192
+ 'params': {
193
+ 'symbols': [market['id']],
194
+ },
195
+ };
196
+ const orderbook = await this.subscribePublic(name, [symbol], this.deepExtend(request, params));
197
+ return orderbook.limit();
107
198
  }
108
- handleOrderBookUpdate(client, message) {
109
- //
110
- // {
111
- // jsonrpc: "2.0",
112
- // method: "updateOrderbook",
113
- // params: {
114
- // ask: [
115
- // { price: "6940.65", size: "0.00000" },
116
- // { price: "6940.66", size: "6.00000" },
117
- // { price: "6943.52", size: "0.04707" },
118
- // ],
119
- // bid: [
120
- // { price: "6938.40", size: "0.11991" },
121
- // { price: "6938.39", size: "0.00073" },
122
- // { price: "6936.65", size: "0.00000" },
123
- // ],
124
- // symbol: "BTCUSD",
125
- // sequence: 497872,
126
- // timestamp: "2020-04-03T09:03:56.685Z"
127
- // }
128
- // }
129
- //
130
- const params = this.safeValue(message, 'params', {});
131
- const marketId = this.safeString(params, 'symbol');
132
- const market = this.safeMarket(marketId);
133
- const symbol = market['symbol'];
134
- if (symbol in this.orderbooks) {
135
- const timestamp = this.parse8601(this.safeString(params, 'timestamp'));
136
- const nonce = this.safeInteger(params, 'sequence');
199
+ handleOrderBook(client, message) {
200
+ //
201
+ // {
202
+ // "ch": "orderbook/full", // Channel
203
+ // "snapshot": {
204
+ // "ETHBTC": {
205
+ // "t": 1626866578796, // Timestamp in milliseconds
206
+ // "s": 27617207, // Sequence number
207
+ // "a": [ // Asks
208
+ // ["0.060506", "0"],
209
+ // ["0.060549", "12.6431"],
210
+ // ["0.060570", "0"],
211
+ // ["0.060612", "0"]
212
+ // ],
213
+ // "b": [ // Bids
214
+ // ["0.060439", "4.4095"],
215
+ // ["0.060414", "0"],
216
+ // ["0.060407", "7.3349"],
217
+ // ["0.060390", "0"]
218
+ // ]
219
+ // }
220
+ // }
221
+ // }
222
+ //
223
+ const data = this.safeValue2(message, 'snapshot', 'update', {});
224
+ const marketIds = Object.keys(data);
225
+ const channel = this.safeString(message, 'ch');
226
+ for (let i = 0; i < marketIds.length; i++) {
227
+ const marketId = marketIds[i];
228
+ const market = this.safeMarket(marketId);
229
+ const symbol = market['symbol'];
230
+ const item = data[marketId];
231
+ const messageHash = channel + '::' + symbol;
232
+ if (!(symbol in this.orderbooks)) {
233
+ const subscription = this.safeValue(client.subscriptions, messageHash, {});
234
+ const limit = this.safeInteger(subscription, 'limit');
235
+ this.orderbooks[symbol] = this.orderBook({}, limit);
236
+ }
237
+ const timestamp = this.safeInteger(item, 't');
238
+ const nonce = this.safeInteger(item, 's');
137
239
  const orderbook = this.orderbooks[symbol];
138
- const asks = this.safeValue(params, 'ask', []);
139
- const bids = this.safeValue(params, 'bid', []);
240
+ const asks = this.safeValue(item, 'a', []);
241
+ const bids = this.safeValue(item, 'b', []);
140
242
  this.handleDeltas(orderbook['asks'], asks);
141
243
  this.handleDeltas(orderbook['bids'], bids);
142
244
  orderbook['timestamp'] = timestamp;
143
245
  orderbook['datetime'] = this.iso8601(timestamp);
144
246
  orderbook['nonce'] = nonce;
247
+ orderbook['symbol'] = symbol;
145
248
  this.orderbooks[symbol] = orderbook;
146
- const messageHash = 'orderbook:' + marketId;
147
249
  client.resolve(orderbook, messageHash);
148
250
  }
149
251
  }
150
252
  handleDelta(bookside, delta) {
151
- const price = this.safeFloat(delta, 'price');
152
- const amount = this.safeFloat(delta, 'size');
253
+ const price = this.safeNumber(delta, 0);
254
+ const amount = this.safeNumber(delta, 1);
153
255
  bookside.store(price, amount);
154
256
  }
155
257
  handleDeltas(bookside, deltas) {
@@ -162,180 +264,394 @@ class hitbtc extends hitbtc$1 {
162
264
  * @method
163
265
  * @name hitbtc#watchTicker
164
266
  * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
267
+ * @see https://api.hitbtc.com/#subscribe-to-ticker
268
+ * @see https://api.hitbtc.com/#subscribe-to-ticker-in-batches
269
+ * @see https://api.hitbtc.com/#subscribe-to-mini-ticker
270
+ * @see https://api.hitbtc.com/#subscribe-to-mini-ticker-in-batches
165
271
  * @param {string} symbol unified symbol of the market to fetch the ticker for
166
272
  * @param {object} [params] extra parameters specific to the hitbtc api endpoint
167
- * @returns {object} a [ticker structure]{@link https://github.com/ccxt/ccxt/wiki/Manual#ticker-structure}
273
+ * @param {string} [params.method] 'ticker/{speed}' (default), or 'ticker/price/{speed}'
274
+ * @param {string} [params.speed] '1s' (default), or '3s'
275
+ * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
276
+ */
277
+ const options = this.safeValue(this.options, 'watchTicker');
278
+ const defaultMethod = this.safeString(options, 'method', 'ticker/{speed}');
279
+ const method = this.safeString2(params, 'method', 'defaultMethod', defaultMethod);
280
+ const speed = this.safeString(params, 'speed', '1s');
281
+ const name = this.implodeParams(method, { 'speed': speed });
282
+ params = this.omit(params, ['method', 'speed']);
283
+ const market = this.market(symbol);
284
+ const request = {
285
+ 'params': {
286
+ 'symbols': [market['id']],
287
+ },
288
+ };
289
+ return await this.subscribePublic(name, [symbol], this.deepExtend(request, params));
290
+ }
291
+ async watchTickers(symbols = undefined, params = {}) {
292
+ /**
293
+ * @method
294
+ * @name hitbtc#watchTicker
295
+ * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
296
+ * @param {string} symbol unified symbol of the market to fetch the ticker for
297
+ * @param {object} params extra parameters specific to the hitbtc api endpoint
298
+ * @param {string} params.method 'ticker/{speed}' (default),'ticker/price/{speed}', 'ticker/{speed}/batch', or 'ticker/{speed}/price/batch''
299
+ * @param {string} params.speed '1s' (default), or '3s'
300
+ * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure}
168
301
  */
169
- return await this.watchPublic(symbol, 'ticker', undefined, params);
302
+ await this.loadMarkets();
303
+ const options = this.safeValue(this.options, 'watchTicker');
304
+ const defaultMethod = this.safeString(options, 'method', 'ticker/{speed}');
305
+ const method = this.safeString2(params, 'method', 'defaultMethod', defaultMethod);
306
+ const speed = this.safeString(params, 'speed', '1s');
307
+ const name = this.implodeParams(method, { 'speed': speed });
308
+ params = this.omit(params, ['method', 'speed']);
309
+ const marketIds = [];
310
+ if (symbols === undefined) {
311
+ marketIds.push('*');
312
+ }
313
+ else {
314
+ for (let i = 0; i < symbols.length; i++) {
315
+ const marketId = this.marketId(symbols[i]);
316
+ marketIds.push(marketId);
317
+ }
318
+ }
319
+ const request = {
320
+ 'params': {
321
+ 'symbols': marketIds,
322
+ },
323
+ };
324
+ const tickers = await this.subscribePublic(name, symbols, this.deepExtend(request, params));
325
+ if (this.newUpdates) {
326
+ return tickers;
327
+ }
328
+ return this.filterByArray(this.tickers, 'symbol', symbols);
170
329
  }
171
330
  handleTicker(client, message) {
172
331
  //
173
- // {
174
- // jsonrpc: '2.0',
175
- // method: 'ticker',
176
- // params: {
177
- // ask: '6983.22',
178
- // bid: '6980.77',
179
- // last: '6980.77',
180
- // open: '6650.05',
181
- // low: '6606.45',
182
- // high: '7223.11',
183
- // volume: '79264.33941',
184
- // volumeQuote: '540183372.5134832',
185
- // timestamp: '2020-04-03T10:02:18.943Z',
186
- // symbol: 'BTCUSD'
187
- // }
188
- // }
332
+ // {
333
+ // "ch": "ticker/1s",
334
+ // "data": {
335
+ // "ETHBTC": {
336
+ // "t": 1614815872000, // Timestamp in milliseconds
337
+ // "a": "0.031175", // Best ask
338
+ // "A": "0.03329", // Best ask quantity
339
+ // "b": "0.031148", // Best bid
340
+ // "B": "0.10565", // Best bid quantity
341
+ // "c": "0.031210", // Last price
342
+ // "o": "0.030781", // Open price
343
+ // "h": "0.031788", // High price
344
+ // "l": "0.030733", // Low price
345
+ // "v": "62.587", // Base asset volume
346
+ // "q": "1.951420577", // Quote asset volume
347
+ // "p": "0.000429", // Price change
348
+ // "P": "1.39", // Price change percent
349
+ // "L": 1182694927 // Last trade identifier
350
+ // }
351
+ // }
352
+ // }
189
353
  //
190
- const params = this.safeValue(message, 'params');
191
- const marketId = this.safeValue(params, 'symbol');
192
- const market = this.safeMarket(marketId);
193
- const symbol = market['symbol'];
194
- const result = this.parseTicker(params, market);
195
- this.tickers[symbol] = result;
196
- const method = this.safeValue(message, 'method');
197
- const messageHash = method + ':' + marketId;
198
- client.resolve(result, messageHash);
354
+ // {
355
+ // "ch": "ticker/price/1s",
356
+ // "data": {
357
+ // "BTCUSDT": {
358
+ // "t": 1614815872030,
359
+ // "o": "32636.79",
360
+ // "c": "32085.51",
361
+ // "h": "33379.92",
362
+ // "l": "30683.28",
363
+ // "v": "11.90667",
364
+ // "q": "384081.1955629"
365
+ // }
366
+ // }
367
+ // }
368
+ //
369
+ const data = this.safeValue(message, 'data', {});
370
+ const marketIds = Object.keys(data);
371
+ const channel = this.safeString(message, 'ch');
372
+ const newTickers = [];
373
+ for (let i = 0; i < marketIds.length; i++) {
374
+ const marketId = marketIds[i];
375
+ const market = this.safeMarket(marketId);
376
+ const symbol = market['symbol'];
377
+ const ticker = this.parseWsTicker(data[marketId], market);
378
+ this.tickers[symbol] = ticker;
379
+ newTickers.push(ticker);
380
+ const messageHash = channel + '::' + symbol;
381
+ client.resolve(this.tickers[symbol], messageHash);
382
+ }
383
+ const messageHashes = this.findMessageHashes(client, channel + '::');
384
+ for (let i = 0; i < messageHashes.length; i++) {
385
+ const messageHash = messageHashes[i];
386
+ const parts = messageHash.split('::');
387
+ const symbolsString = parts[1];
388
+ const symbols = symbolsString.split(',');
389
+ const tickers = this.filterByArray(newTickers, 'symbol', symbols);
390
+ const tickersSymbols = Object.keys(tickers);
391
+ const numTickers = tickersSymbols.length;
392
+ if (numTickers > 0) {
393
+ client.resolve(tickers, messageHash);
394
+ }
395
+ }
396
+ client.resolve(this.tickers, channel);
397
+ return message;
398
+ }
399
+ parseWsTicker(ticker, market = undefined) {
400
+ //
401
+ // {
402
+ // "t": 1614815872000, // Timestamp in milliseconds
403
+ // "a": "0.031175", // Best ask
404
+ // "A": "0.03329", // Best ask quantity
405
+ // "b": "0.031148", // Best bid
406
+ // "B": "0.10565", // Best bid quantity
407
+ // "c": "0.031210", // Last price
408
+ // "o": "0.030781", // Open price
409
+ // "h": "0.031788", // High price
410
+ // "l": "0.030733", // Low price
411
+ // "v": "62.587", // Base asset volume
412
+ // "q": "1.951420577", // Quote asset volume
413
+ // "p": "0.000429", // Price change
414
+ // "P": "1.39", // Price change percent
415
+ // "L": 1182694927 // Last trade identifier
416
+ // }
417
+ //
418
+ // {
419
+ // "t": 1614815872030,
420
+ // "o": "32636.79",
421
+ // "c": "32085.51",
422
+ // "h": "33379.92",
423
+ // "l": "30683.28",
424
+ // "v": "11.90667",
425
+ // "q": "384081.1955629"
426
+ // }
427
+ //
428
+ const timestamp = this.safeInteger(ticker, 't');
429
+ const symbol = this.safeSymbol(undefined, market);
430
+ const last = this.safeString(ticker, 'c');
431
+ return this.safeTicker({
432
+ 'symbol': symbol,
433
+ 'timestamp': timestamp,
434
+ 'datetime': this.iso8601(timestamp),
435
+ 'high': this.safeString(ticker, 'h'),
436
+ 'low': this.safeString(ticker, 'l'),
437
+ 'bid': this.safeString(ticker, 'b'),
438
+ 'bidVolume': this.safeString(ticker, 'B'),
439
+ 'ask': this.safeString(ticker, 'a'),
440
+ 'askVolume': this.safeString(ticker, 'A'),
441
+ 'vwap': undefined,
442
+ 'open': this.safeString(ticker, 'o'),
443
+ 'close': last,
444
+ 'last': last,
445
+ 'previousClose': undefined,
446
+ 'change': undefined,
447
+ 'percentage': undefined,
448
+ 'average': undefined,
449
+ 'baseVolume': this.safeString(ticker, 'v'),
450
+ 'quoteVolume': this.safeString(ticker, 'q'),
451
+ 'info': ticker,
452
+ }, market);
199
453
  }
200
454
  async watchTrades(symbol, since = undefined, limit = undefined, params = {}) {
201
455
  /**
202
456
  * @method
203
457
  * @name hitbtc#watchTrades
204
458
  * @description get the list of most recent trades for a particular symbol
459
+ * @see https://api.hitbtc.com/#subscribe-to-trades
205
460
  * @param {string} symbol unified symbol of the market to fetch trades for
206
461
  * @param {int} [since] timestamp in ms of the earliest trade to fetch
207
462
  * @param {int} [limit] the maximum amount of trades to fetch
208
463
  * @param {object} [params] extra parameters specific to the hitbtc api endpoint
209
464
  * @returns {object[]} a list of [trade structures]{@link https://github.com/ccxt/ccxt/wiki/Manual#public-trades}
210
465
  */
211
- const trades = await this.watchPublic(symbol, 'trades', undefined, params);
466
+ await this.loadMarkets();
467
+ const market = this.market(symbol);
468
+ const request = {
469
+ 'params': {
470
+ 'symbols': [market['id']],
471
+ },
472
+ };
473
+ if (limit !== undefined) {
474
+ request['limit'] = limit;
475
+ }
476
+ const trades = await this.subscribePublic('trades', [symbol], this.deepExtend(request, params));
212
477
  if (this.newUpdates) {
213
478
  limit = trades.getLimit(symbol, limit);
214
479
  }
215
- return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
480
+ return this.filterBySinceLimit(trades, since, limit, 'timestamp');
216
481
  }
217
482
  handleTrades(client, message) {
218
483
  //
219
- // {
220
- // jsonrpc: '2.0',
221
- // method: 'snapshotTrades', // updateTrades
222
- // params: {
223
- // data: [
224
- // {
225
- // id: 814145791,
226
- // price: '6957.20',
227
- // quantity: '0.02779',
228
- // side: 'buy',
229
- // timestamp: '2020-04-03T10:28:20.032Z'
230
- // },
231
- // {
232
- // id: 814145792,
233
- // price: '6957.20',
234
- // quantity: '0.12918',
235
- // side: 'buy',
236
- // timestamp: '2020-04-03T10:28:20.039Z'
237
- // },
238
- // ],
239
- // symbol: 'BTCUSD'
240
- // }
241
- // }
242
- //
243
- const params = this.safeValue(message, 'params', {});
244
- const data = this.safeValue(params, 'data', []);
245
- const marketId = this.safeString(params, 'symbol');
246
- const market = this.safeMarket(marketId);
247
- const symbol = market['symbol'];
248
- const messageHash = 'trades:' + marketId;
249
- const tradesLimit = this.safeInteger(this.options, 'tradesLimit', 1000);
250
- let stored = this.safeValue(this.trades, symbol);
251
- if (stored === undefined) {
252
- stored = new Cache.ArrayCache(tradesLimit);
253
- this.trades[symbol] = stored;
254
- }
255
- if (Array.isArray(data)) {
256
- const trades = this.parseTrades(data, market);
484
+ // {
485
+ // "result": {
486
+ // "ch": "trades", // Channel
487
+ // "subscriptions": ["ETHBTC", "BTCUSDT"]
488
+ // },
489
+ // "id": 123
490
+ // }
491
+ //
492
+ // Notification snapshot
493
+ //
494
+ // {
495
+ // "ch": "trades", // Channel
496
+ // "snapshot": {
497
+ // "BTCUSDT": [{
498
+ // "t": 1626861109494, // Timestamp in milliseconds
499
+ // "i": 1555634969, // Trade identifier
500
+ // "p": "30881.96", // Price
501
+ // "q": "12.66828", // Quantity
502
+ // "s": "buy" // Side
503
+ // }]
504
+ // }
505
+ // }
506
+ //
507
+ // Notification update
508
+ //
509
+ // {
510
+ // "ch": "trades",
511
+ // "update": {
512
+ // "BTCUSDT": [{
513
+ // "t": 1626861123552,
514
+ // "i": 1555634969,
515
+ // "p": "30877.68",
516
+ // "q": "0.00006",
517
+ // "s": "sell"
518
+ // }]
519
+ // }
520
+ // }
521
+ //
522
+ const data = this.safeValue2(message, 'snapshot', 'update', {});
523
+ const marketIds = Object.keys(data);
524
+ for (let i = 0; i < marketIds.length; i++) {
525
+ const marketId = marketIds[i];
526
+ const market = this.safeMarket(marketId);
527
+ const tradesLimit = this.safeInteger(this.options, 'tradesLimit', 1000);
528
+ const symbol = market['symbol'];
529
+ let stored = this.safeValue(this.trades, symbol);
530
+ if (stored === undefined) {
531
+ stored = new Cache.ArrayCache(tradesLimit);
532
+ this.trades[symbol] = stored;
533
+ }
534
+ const trades = this.parseWsTrades(data[marketId], market);
257
535
  for (let i = 0; i < trades.length; i++) {
258
536
  stored.append(trades[i]);
259
537
  }
538
+ const messageHash = 'trades::' + symbol;
539
+ client.resolve(stored, messageHash);
260
540
  }
261
- else {
262
- const trade = this.parseTrade(message, market);
263
- stored.append(trade);
264
- }
265
- client.resolve(stored, messageHash);
266
541
  return message;
267
542
  }
543
+ parseWsTrades(trades, market = undefined, since = undefined, limit = undefined, params = {}) {
544
+ trades = this.toArray(trades);
545
+ let result = [];
546
+ for (let i = 0; i < trades.length; i++) {
547
+ const trade = this.extend(this.parseWsTrade(trades[i], market), params);
548
+ result.push(trade);
549
+ }
550
+ result = this.sortBy2(result, 'timestamp', 'id');
551
+ const symbol = this.safeString(market, 'symbol');
552
+ return this.filterBySymbolSinceLimit(result, symbol, since, limit);
553
+ }
554
+ parseWsTrade(trade, market = undefined) {
555
+ //
556
+ // {
557
+ // "t": 1626861123552, // Timestamp in milliseconds
558
+ // "i": 1555634969, // Trade identifier
559
+ // "p": "30877.68", // Price
560
+ // "q": "0.00006", // Quantity
561
+ // "s": "sell" // Side
562
+ // }
563
+ //
564
+ const timestamp = this.safeInteger(trade, 't');
565
+ return this.safeTrade({
566
+ 'info': trade,
567
+ 'id': this.safeString(trade, 'i'),
568
+ 'order': undefined,
569
+ 'timestamp': timestamp,
570
+ 'datetime': this.iso8601(timestamp),
571
+ 'symbol': this.safeString(market, 'symbol'),
572
+ 'type': undefined,
573
+ 'side': this.safeString(trade, 's'),
574
+ 'takerOrMaker': undefined,
575
+ 'price': this.safeString(trade, 'p'),
576
+ 'amount': this.safeString(trade, 'q'),
577
+ 'cost': undefined,
578
+ 'fee': undefined,
579
+ }, market);
580
+ }
268
581
  async watchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
269
582
  /**
270
583
  * @method
271
584
  * @name hitbtc#watchOHLCV
272
585
  * @description watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
586
+ * @see https://api.hitbtc.com/#subscribe-to-candles
273
587
  * @param {string} symbol unified symbol of the market to fetch OHLCV data for
274
- * @param {string} timeframe the length of time each candle represents
275
- * @param {int} [since] timestamp in ms of the earliest candle to fetch
276
- * @param {int} [limit] the maximum amount of candles to fetch
588
+ * @param {string} [timeframe] the length of time each candle represents
589
+ * @param {int} [since] not used by hitbtc watchOHLCV
590
+ * @param {int} [limit] 0 1000, default value = 0 (no history returned)
277
591
  * @param {object} [params] extra parameters specific to the hitbtc api endpoint
278
- * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
592
+ * @returns {[[int]]} A list of candles ordered as timestamp, open, high, low, close, volume
279
593
  */
280
- // if (limit === undefined) {
281
- // limit = 100;
282
- // }
283
594
  const period = this.safeString(this.timeframes, timeframe, timeframe);
595
+ const name = 'candles/' + period;
596
+ const market = this.market(symbol);
284
597
  const request = {
285
598
  'params': {
286
- 'period': period,
287
- // 'limit': limit,
599
+ 'symbols': [market['id']],
288
600
  },
289
601
  };
290
- const requestParams = this.deepExtend(request, params);
291
- const ohlcv = await this.watchPublic(symbol, 'ohlcv', period, requestParams);
602
+ if (limit !== undefined) {
603
+ request['params']['limit'] = limit;
604
+ }
605
+ const ohlcv = await this.subscribePublic(name, [symbol], this.deepExtend(request, params));
292
606
  if (this.newUpdates) {
293
607
  limit = ohlcv.getLimit(symbol, limit);
294
608
  }
295
- return this.filterBySinceLimit(ohlcv, since, limit, 0, true);
609
+ return this.filterBySinceLimit(ohlcv, since, limit, 0);
296
610
  }
297
611
  handleOHLCV(client, message) {
298
612
  //
299
- // {
300
- // jsonrpc: '2.0',
301
- // method: 'snapshotCandles', // updateCandles
302
- // params: {
303
- // data: [
304
- // {
305
- // timestamp: '2020-04-05T00:06:00.000Z',
306
- // open: '6869.40',
307
- // close: '6867.16',
308
- // min: '6863.17',
309
- // max: '6869.4',
310
- // volume: '0.08947',
311
- // volumeQuote: '614.4195442'
312
- // },
313
- // {
314
- // timestamp: '2020-04-05T00:07:00.000Z',
315
- // open: '6867.54',
316
- // close: '6859.26',
317
- // min: '6858.85',
318
- // max: '6867.54',
319
- // volume: '1.7766',
320
- // volumeQuote: '12191.5880395'
321
- // },
322
- // ],
323
- // symbol: 'BTCUSD',
324
- // period: 'M1'
325
- // }
326
- // }
327
- //
328
- const params = this.safeValue(message, 'params', {});
329
- const data = this.safeValue(params, 'data', []);
330
- const marketId = this.safeString(params, 'symbol');
331
- const market = this.safeMarket(marketId);
332
- const symbol = market['symbol'];
333
- const period = this.safeString(params, 'period');
613
+ // {
614
+ // "ch": "candles/M1", // Channel
615
+ // "snapshot": {
616
+ // "BTCUSDT": [{
617
+ // "t": 1626860340000, // Message timestamp
618
+ // "o": "30881.95", // Open price
619
+ // "c": "30890.96", // Last price
620
+ // "h": "30900.8", // High price
621
+ // "l": "30861.27", // Low price
622
+ // "v": "1.27852", // Base asset volume
623
+ // "q": "39493.9021811" // Quote asset volume
624
+ // }
625
+ // ...
626
+ // ]
627
+ // }
628
+ // }
629
+ //
630
+ // {
631
+ // "ch": "candles/M1",
632
+ // "update": {
633
+ // "ETHBTC": [{
634
+ // "t": 1626860880000,
635
+ // "o": "0.060711",
636
+ // "c": "0.060749",
637
+ // "h": "0.060749",
638
+ // "l": "0.060711",
639
+ // "v": "12.2800",
640
+ // "q": "0.7455339675"
641
+ // }]
642
+ // }
643
+ // }
644
+ //
645
+ const data = this.safeValue2(message, 'snapshot', 'update', {});
646
+ const marketIds = Object.keys(data);
647
+ const channel = this.safeString(message, 'ch');
648
+ const splitChannel = channel.split('/');
649
+ const period = this.safeString(splitChannel, 1);
334
650
  const timeframe = this.findTimeframe(period);
335
- const messageHash = 'ohlcv:' + marketId + ':' + period;
336
- for (let i = 0; i < data.length; i++) {
337
- const candle = data[i];
338
- const parsed = this.parseOHLCV(candle, market);
651
+ for (let i = 0; i < marketIds.length; i++) {
652
+ const marketId = marketIds[i];
653
+ const market = this.safeMarket(marketId);
654
+ const symbol = market['symbol'];
339
655
  this.ohlcvs[symbol] = this.safeValue(this.ohlcvs, symbol, {});
340
656
  let stored = this.safeValue(this.ohlcvs[symbol], timeframe);
341
657
  if (stored === undefined) {
@@ -343,11 +659,319 @@ class hitbtc extends hitbtc$1 {
343
659
  stored = new Cache.ArrayCacheByTimestamp(limit);
344
660
  this.ohlcvs[symbol][timeframe] = stored;
345
661
  }
346
- stored.append(parsed);
662
+ const ohlcvs = this.parseWsOHLCVs(data[marketId], market);
663
+ for (let i = 0; i < ohlcvs.length; i++) {
664
+ stored.append(ohlcvs[i]);
665
+ }
666
+ const messageHash = channel + '::' + symbol;
347
667
  client.resolve(stored, messageHash);
348
668
  }
349
669
  return message;
350
670
  }
671
+ parseWsOHLCV(ohlcv, market = undefined) {
672
+ //
673
+ // {
674
+ // "t": 1626860340000, // Message timestamp
675
+ // "o": "30881.95", // Open price
676
+ // "c": "30890.96", // Last price
677
+ // "h": "30900.8", // High price
678
+ // "l": "30861.27", // Low price
679
+ // "v": "1.27852", // Base asset volume
680
+ // "q": "39493.9021811" // Quote asset volume
681
+ // }
682
+ //
683
+ return [
684
+ this.safeInteger(ohlcv, 't'),
685
+ this.safeNumber(ohlcv, 'o'),
686
+ this.safeNumber(ohlcv, 'h'),
687
+ this.safeNumber(ohlcv, 'l'),
688
+ this.safeNumber(ohlcv, 'c'),
689
+ this.safeNumber(ohlcv, 'v'),
690
+ ];
691
+ }
692
+ async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
693
+ /**
694
+ * @method
695
+ * @name hitbtc#watchOrders
696
+ * @description watches information on multiple orders made by the user
697
+ * @see https://api.hitbtc.com/#subscribe-to-reports
698
+ * @see https://api.hitbtc.com/#subscribe-to-reports-2
699
+ * @see https://api.hitbtc.com/#subscribe-to-reports-3
700
+ * @param {string} [symbol] unified CCXT market symbol
701
+ * @param {int} [since] timestamp in ms of the earliest order to fetch
702
+ * @param {int} [limit] the maximum amount of orders to fetch
703
+ * @param {object} [params] extra parameters specific to the hitbtc api endpoint
704
+ * @returns {[object]} a list of [order structures]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure}
705
+ */
706
+ await this.loadMarkets();
707
+ let marketType = undefined;
708
+ let market = undefined;
709
+ if (symbol !== undefined) {
710
+ market = this.market(symbol);
711
+ }
712
+ [marketType, params] = this.handleMarketTypeAndParams('watchOrders', market, params);
713
+ const name = this.getSupportedMapping(marketType, {
714
+ 'spot': 'spot_subscribe',
715
+ 'margin': 'margin_subscribe',
716
+ 'swap': 'futures_subscribe',
717
+ 'future': 'futures_subscribe',
718
+ });
719
+ const orders = await this.subscribePrivate(name, symbol, params);
720
+ if (this.newUpdates) {
721
+ limit = orders.getLimit(symbol, limit);
722
+ }
723
+ return this.filterBySinceLimit(orders, since, limit, 'timestamp');
724
+ }
725
+ handleOrder(client, message) {
726
+ //
727
+ // {
728
+ // "jsonrpc": "2.0",
729
+ // "method": "spot_order", // "margin_order", "future_order"
730
+ // "params": {
731
+ // "id": 584244931496,
732
+ // "client_order_id": "b5acd79c0a854b01b558665bcf379456",
733
+ // "symbol": "BTCUSDT",
734
+ // "side": "buy",
735
+ // "status": "new",
736
+ // "type": "limit",
737
+ // "time_in_force": "GTC",
738
+ // "quantity": "0.01000",
739
+ // "quantity_cumulative": "0",
740
+ // "price": "0.01", // only updates and snapshots
741
+ // "post_only": false,
742
+ // "reduce_only": false, // only margin and contract
743
+ // "display_quantity": "0", // only updates and snapshot
744
+ // "created_at": "2021-07-02T22:52:32.864Z",
745
+ // "updated_at": "2021-07-02T22:52:32.864Z",
746
+ // "trade_id": 1361977606, // only trades
747
+ // "trade_quantity": "0.00001", // only trades
748
+ // "trade_price": "49595.04", // only trades
749
+ // "trade_fee": "0.001239876000", // only trades
750
+ // "trade_taker": true, // only trades, only spot
751
+ // "trade_position_id": 485308, // only trades, only margin
752
+ // "report_type": "new" // "trade", "status" (snapshot)
753
+ // }
754
+ // }
755
+ //
756
+ // {
757
+ // "jsonrpc": "2.0",
758
+ // "method": "spot_orders", // "margin_orders", "future_orders"
759
+ // "params": [
760
+ // {
761
+ // "id": 584244931496,
762
+ // "client_order_id": "b5acd79c0a854b01b558665bcf379456",
763
+ // "symbol": "BTCUSDT",
764
+ // "side": "buy",
765
+ // "status": "new",
766
+ // "type": "limit",
767
+ // "time_in_force": "GTC",
768
+ // "quantity": "0.01000",
769
+ // "quantity_cumulative": "0",
770
+ // "price": "0.01", // only updates and snapshots
771
+ // "post_only": false,
772
+ // "reduce_only": false, // only margin and contract
773
+ // "display_quantity": "0", // only updates and snapshot
774
+ // "created_at": "2021-07-02T22:52:32.864Z",
775
+ // "updated_at": "2021-07-02T22:52:32.864Z",
776
+ // "trade_id": 1361977606, // only trades
777
+ // "trade_quantity": "0.00001", // only trades
778
+ // "trade_price": "49595.04", // only trades
779
+ // "trade_fee": "0.001239876000", // only trades
780
+ // "trade_taker": true, // only trades, only spot
781
+ // "trade_position_id": 485308, // only trades, only margin
782
+ // "report_type": "new" // "trade", "status" (snapshot)
783
+ // }
784
+ // ]
785
+ // }
786
+ //
787
+ if (this.orders === undefined) {
788
+ const limit = this.safeInteger(this.options, 'ordersLimit');
789
+ this.orders = new Cache.ArrayCacheBySymbolById(limit);
790
+ }
791
+ const data = this.safeValue(message, 'params', []);
792
+ if (Array.isArray(data)) {
793
+ for (let i = 0; i < data.length; i++) {
794
+ const order = data[i];
795
+ this.handleOrderHelper(client, message, order);
796
+ }
797
+ }
798
+ else {
799
+ this.handleOrderHelper(client, message, data);
800
+ }
801
+ return message;
802
+ }
803
+ handleOrderHelper(client, message, order) {
804
+ const orders = this.orders;
805
+ const marketId = this.safeStringLower2(order, 'instrument', 'symbol');
806
+ const method = this.safeString(message, 'method');
807
+ const splitMethod = method.split('_order');
808
+ const messageHash = this.safeString(splitMethod, 0);
809
+ const symbol = this.safeSymbol(marketId);
810
+ const parsed = this.parseOrder(order);
811
+ orders.append(parsed);
812
+ client.resolve(orders, messageHash);
813
+ client.resolve(orders, messageHash + '::' + symbol);
814
+ }
815
+ parseWsOrderTrade(trade, market = undefined) {
816
+ //
817
+ // {
818
+ // "id": 584244931496,
819
+ // "client_order_id": "b5acd79c0a854b01b558665bcf379456",
820
+ // "symbol": "BTCUSDT",
821
+ // "side": "buy",
822
+ // "status": "new",
823
+ // "type": "limit",
824
+ // "time_in_force": "GTC",
825
+ // "quantity": "0.01000",
826
+ // "quantity_cumulative": "0",
827
+ // "price": "0.01", // only updates and snapshots
828
+ // "post_only": false,
829
+ // "reduce_only": false, // only margin and contract
830
+ // "display_quantity": "0", // only updates and snapshot
831
+ // "created_at": "2021-07-02T22:52:32.864Z",
832
+ // "updated_at": "2021-07-02T22:52:32.864Z",
833
+ // "trade_id": 1361977606, // only trades
834
+ // "trade_quantity": "0.00001", // only trades
835
+ // "trade_price": "49595.04", // only trades
836
+ // "trade_fee": "0.001239876000", // only trades
837
+ // "trade_taker": true, // only trades, only spot
838
+ // "trade_position_id": 485308, // only trades, only margin
839
+ // "report_type": "new" // "trade", "status" (snapshot)
840
+ // }
841
+ //
842
+ const timestamp = this.safeInteger(trade, 'created_at');
843
+ const marketId = this.safeString(trade, 'symbol');
844
+ return this.safeTrade({
845
+ 'info': trade,
846
+ 'id': this.safeString(trade, 'trade_id'),
847
+ 'order': this.safeString(trade, 'id'),
848
+ 'timestamp': timestamp,
849
+ 'datetime': this.iso8601(timestamp),
850
+ 'symbol': this.safeMarket(marketId, market),
851
+ 'type': undefined,
852
+ 'side': this.safeString(trade, 'side'),
853
+ 'takerOrMaker': this.safeString(trade, 'trade_taker'),
854
+ 'price': this.safeString(trade, 'trade_price'),
855
+ 'amount': this.safeString(trade, 'trade_quantity'),
856
+ 'cost': undefined,
857
+ 'fee': {
858
+ 'cost': this.safeString(trade, 'trade_fee'),
859
+ 'currency': undefined,
860
+ 'rate': undefined,
861
+ },
862
+ }, market);
863
+ }
864
+ parseWsOrder(order, market = undefined) {
865
+ //
866
+ // {
867
+ // "id": 584244931496,
868
+ // "client_order_id": "b5acd79c0a854b01b558665bcf379456",
869
+ // "symbol": "BTCUSDT",
870
+ // "side": "buy",
871
+ // "status": "new",
872
+ // "type": "limit",
873
+ // "time_in_force": "GTC",
874
+ // "quantity": "0.01000",
875
+ // "quantity_cumulative": "0",
876
+ // "price": "0.01", // only updates and snapshots
877
+ // "post_only": false,
878
+ // "reduce_only": false, // only margin and contract
879
+ // "display_quantity": "0", // only updates and snapshot
880
+ // "created_at": "2021-07-02T22:52:32.864Z",
881
+ // "updated_at": "2021-07-02T22:52:32.864Z",
882
+ // "trade_id": 1361977606, // only trades
883
+ // "trade_quantity": "0.00001", // only trades
884
+ // "trade_price": "49595.04", // only trades
885
+ // "trade_fee": "0.001239876000", // only trades
886
+ // "trade_taker": true, // only trades, only spot
887
+ // "trade_position_id": 485308, // only trades, only margin
888
+ // "report_type": "new" // "trade", "status" (snapshot)
889
+ // }
890
+ //
891
+ const timestamp = this.safeString(order, 'created_at');
892
+ const marketId = this.safeSymbol(order, 'symbol');
893
+ market = this.safeMarket(marketId, market);
894
+ const tradeId = this.safeString(order, 'trade_id');
895
+ let trades = undefined;
896
+ if (tradeId !== undefined) {
897
+ const trade = this.parseWsOrderTrade(order, market);
898
+ trades = [trade];
899
+ }
900
+ return this.safeOrder({
901
+ 'info': order,
902
+ 'id': this.safeString(order, 'id'),
903
+ 'clientOrderId': this.safeString(order, 'client_order_id'),
904
+ 'timestamp': timestamp,
905
+ 'datetime': this.iso8601(timestamp),
906
+ 'lastTradeTimestamp': undefined,
907
+ 'symbol': market['symbol'],
908
+ 'price': this.safeString(order, 'price'),
909
+ 'amount': this.safeString(order, 'quantity'),
910
+ 'type': this.safeString(order, 'type'),
911
+ 'side': this.safeStringUpper(order, 'side'),
912
+ 'timeInForce': this.safeString(order, 'time_in_force'),
913
+ 'postOnly': this.safeString(order, 'post_only'),
914
+ 'reduceOnly': this.safeValue(order, 'reduce_only'),
915
+ 'filled': undefined,
916
+ 'remaining': undefined,
917
+ 'cost': undefined,
918
+ 'status': this.parseOrderStatus(this.safeString(order, 'status')),
919
+ 'average': undefined,
920
+ 'trades': trades,
921
+ 'fee': undefined,
922
+ }, market);
923
+ }
924
+ async watchBalance(params = {}) {
925
+ /**
926
+ * @method
927
+ * @name hitbtc#watchBalance
928
+ * @description watches balance updates, cannot subscribe to margin account balances
929
+ * @see https://api.hitbtc.com/#subscribe-to-spot-balances
930
+ * @see https://api.hitbtc.com/#subscribe-to-futures-balances
931
+ * @param {object} [params] extra parameters specific to the hitbtc api endpoint
932
+ * @param {string} [params.type] 'spot', 'swap', or 'future'
933
+ *
934
+ * EXCHANGE SPECIFIC PARAMETERS
935
+ * @param {string} [params.mode] 'updates' or 'batches' (default), 'updates' = messages arrive after balance updates, 'batches' = messages arrive at equal intervals if there were any updates
936
+ * @returns {[object]} a list of [balance structures]{@link https://docs.ccxt.com/#/?id=balance-structure}
937
+ */
938
+ await this.loadMarkets();
939
+ let type = undefined;
940
+ [type, params] = this.handleMarketTypeAndParams('watchBalance', undefined, params);
941
+ const name = this.getSupportedMapping(type, {
942
+ 'spot': 'spot_balance_subscribe',
943
+ 'swap': 'futures_balance_subscribe',
944
+ 'future': 'futures_balance_subscribe',
945
+ });
946
+ const mode = this.safeString(params, 'mode', 'batches');
947
+ params = this.omit(params, 'mode');
948
+ const request = {
949
+ 'mode': mode,
950
+ };
951
+ return await this.subscribePrivate(name, undefined, this.extend(request, params));
952
+ }
953
+ handleBalance(client, message) {
954
+ //
955
+ // {
956
+ // "jsonrpc": "2.0",
957
+ // "method": "futures_balance",
958
+ // "params": [
959
+ // {
960
+ // "currency": "BCN",
961
+ // "available": "100.000000000000",
962
+ // "reserved": "0",
963
+ // "reserved_margin": "0"
964
+ // },
965
+ // ...
966
+ // ]
967
+ // }
968
+ //
969
+ const messageHash = this.safeString(message, 'method');
970
+ const params = this.safeValue(message, 'params');
971
+ const balance = this.parseBalance(params);
972
+ this.balance = this.deepExtend(this.balance, balance);
973
+ client.resolve(this.balance, messageHash);
974
+ }
351
975
  handleNotification(client, message) {
352
976
  //
353
977
  // { jsonrpc: '2.0', result: true, id: null }
@@ -355,23 +979,57 @@ class hitbtc extends hitbtc$1 {
355
979
  return message;
356
980
  }
357
981
  handleMessage(client, message) {
358
- const methods = {
359
- 'snapshotOrderbook': this.handleOrderBookSnapshot,
360
- 'updateOrderbook': this.handleOrderBookUpdate,
361
- 'ticker': this.handleTicker,
362
- 'snapshotTrades': this.handleTrades,
363
- 'updateTrades': this.handleTrades,
364
- 'snapshotCandles': this.handleOHLCV,
365
- 'updateCandles': this.handleOHLCV,
366
- };
367
- const event = this.safeString(message, 'method');
368
- const method = this.safeValue(methods, event);
369
- if (method === undefined) {
370
- this.handleNotification(client, message);
982
+ let channel = this.safeString2(message, 'ch', 'method');
983
+ if (channel !== undefined) {
984
+ const splitChannel = channel.split('/');
985
+ channel = this.safeString(splitChannel, 0);
986
+ const methods = {
987
+ 'candles': this.handleOHLCV,
988
+ 'ticker': this.handleTicker,
989
+ 'trades': this.handleTrades,
990
+ 'orderbook': this.handleOrderBook,
991
+ 'spot_order': this.handleOrder,
992
+ 'spot_orders': this.handleOrder,
993
+ 'margin_order': this.handleOrder,
994
+ 'margin_orders': this.handleOrder,
995
+ 'futures_order': this.handleOrder,
996
+ 'futures_orders': this.handleOrder,
997
+ 'spot_balance': this.handleBalance,
998
+ 'futures_balance': this.handleBalance,
999
+ };
1000
+ const method = this.safeValue(methods, channel);
1001
+ if (method !== undefined) {
1002
+ method.call(this, client, message);
1003
+ }
371
1004
  }
372
1005
  else {
373
- method.call(this, client, message);
1006
+ const success = this.safeValue(message, 'result');
1007
+ if ((success === true) && !('id' in message)) {
1008
+ this.handleAuthenticate(client, message);
1009
+ }
1010
+ }
1011
+ }
1012
+ handleAuthenticate(client, message) {
1013
+ //
1014
+ // {
1015
+ // jsonrpc: '2.0',
1016
+ // result: true
1017
+ // }
1018
+ //
1019
+ const success = this.safeValue(message, 'result');
1020
+ const messageHash = 'authenticated';
1021
+ if (success) {
1022
+ const future = this.safeValue(client.futures, messageHash);
1023
+ future.resolve(true);
374
1024
  }
1025
+ else {
1026
+ const error = new errors.AuthenticationError(this.id + ' ' + this.json(message));
1027
+ client.reject(error, messageHash);
1028
+ if (messageHash in client.subscriptions) {
1029
+ delete client.subscriptions[messageHash];
1030
+ }
1031
+ }
1032
+ return message;
375
1033
  }
376
1034
  }
377
1035