ccxt 4.4.36 → 4.4.38

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.
Files changed (63) hide show
  1. package/README.md +7 -7
  2. package/dist/ccxt.browser.min.js +2 -2
  3. package/dist/cjs/ccxt.js +6 -6
  4. package/dist/cjs/src/abstract/bitfinex1.js +9 -0
  5. package/dist/cjs/src/base/Exchange.js +10 -1
  6. package/dist/cjs/src/binance.js +0 -2
  7. package/dist/cjs/src/bingx.js +93 -8
  8. package/dist/cjs/src/bitfinex.js +3178 -1161
  9. package/dist/cjs/src/bitfinex1.js +1760 -0
  10. package/dist/cjs/src/bitget.js +0 -2
  11. package/dist/cjs/src/bithumb.js +1 -1
  12. package/dist/cjs/src/bitmart.js +163 -15
  13. package/dist/cjs/src/bybit.js +3 -2
  14. package/dist/cjs/src/coinbase.js +87 -0
  15. package/dist/cjs/src/gate.js +1 -1
  16. package/dist/cjs/src/hyperliquid.js +125 -14
  17. package/dist/cjs/src/kucoin.js +76 -2
  18. package/dist/cjs/src/kucoinfutures.js +93 -5
  19. package/dist/cjs/src/ndax.js +7 -2
  20. package/dist/cjs/src/okx.js +0 -1
  21. package/dist/cjs/src/paradex.js +4 -2
  22. package/dist/cjs/src/pro/bitfinex.js +760 -271
  23. package/dist/cjs/src/pro/bitfinex1.js +675 -0
  24. package/dist/cjs/src/pro/probit.js +1 -0
  25. package/dist/cjs/src/probit.js +3 -1
  26. package/js/ccxt.d.ts +8 -8
  27. package/js/ccxt.js +6 -6
  28. package/js/src/abstract/bingx.d.ts +0 -1
  29. package/js/src/abstract/bitfinex.d.ts +135 -64
  30. package/js/src/abstract/bitfinex1.d.ts +72 -0
  31. package/js/src/base/Exchange.js +10 -1
  32. package/js/src/binance.js +0 -2
  33. package/js/src/bingx.d.ts +14 -1
  34. package/js/src/bingx.js +93 -8
  35. package/js/src/bitfinex.d.ts +316 -106
  36. package/js/src/bitfinex.js +3179 -1162
  37. package/js/src/bitfinex1.d.ts +296 -0
  38. package/js/src/bitfinex1.js +1761 -0
  39. package/js/src/bitget.js +0 -2
  40. package/js/src/bithumb.js +1 -1
  41. package/js/src/bitmart.js +163 -15
  42. package/js/src/bybit.js +3 -2
  43. package/js/src/coinbase.d.ts +33 -0
  44. package/js/src/coinbase.js +87 -0
  45. package/js/src/gate.js +1 -1
  46. package/js/src/hyperliquid.d.ts +6 -1
  47. package/js/src/hyperliquid.js +125 -14
  48. package/js/src/kucoin.js +76 -2
  49. package/js/src/kucoinfutures.js +93 -5
  50. package/js/src/ndax.d.ts +2 -0
  51. package/js/src/ndax.js +7 -2
  52. package/js/src/okx.js +0 -1
  53. package/js/src/paradex.d.ts +2 -0
  54. package/js/src/paradex.js +4 -2
  55. package/js/src/pro/bitfinex.d.ts +42 -10
  56. package/js/src/pro/bitfinex.js +761 -272
  57. package/js/src/pro/bitfinex1.d.ts +67 -0
  58. package/js/src/pro/bitfinex1.js +676 -0
  59. package/js/src/pro/probit.js +1 -0
  60. package/js/src/probit.js +3 -1
  61. package/package.json +1 -1
  62. package/js/src/abstract/bitfinex2.d.ts +0 -143
  63. /package/js/src/abstract/{bitfinex2.js → bitfinex1.js} +0 -0
@@ -6,9 +6,9 @@
6
6
 
7
7
  // ---------------------------------------------------------------------------
8
8
  import bitfinexRest from '../bitfinex.js';
9
- import { ExchangeError, AuthenticationError } from '../base/errors.js';
10
- import { ArrayCache, ArrayCacheBySymbolById } from '../base/ws/Cache.js';
11
9
  import { Precise } from '../base/Precise.js';
10
+ import { ExchangeError, AuthenticationError, ChecksumError } from '../base/errors.js';
11
+ import { ArrayCache, ArrayCacheBySymbolById, ArrayCacheByTimestamp } from '../base/ws/Cache.js';
12
12
  import { sha384 } from '../static_dependencies/noble-hashes/sha512.js';
13
13
  // ---------------------------------------------------------------------------
14
14
  export default class bitfinex extends bitfinexRest {
@@ -21,14 +21,16 @@ export default class bitfinex extends bitfinexRest {
21
21
  'watchOrderBook': true,
22
22
  'watchTrades': true,
23
23
  'watchTradesForSymbols': false,
24
- 'watchBalance': false,
25
- 'watchOHLCV': false, // missing on the exchange side in v1
24
+ 'watchMyTrades': true,
25
+ 'watchBalance': true,
26
+ 'watchOHLCV': true,
27
+ 'watchOrders': true,
26
28
  },
27
29
  'urls': {
28
30
  'api': {
29
31
  'ws': {
30
- 'public': 'wss://api-pub.bitfinex.com/ws/1',
31
- 'private': 'wss://api.bitfinex.com/ws/1',
32
+ 'public': 'wss://api-pub.bitfinex.com/ws/2',
33
+ 'private': 'wss://api.bitfinex.com/ws/2',
32
34
  },
33
35
  },
34
36
  },
@@ -36,6 +38,7 @@ export default class bitfinex extends bitfinexRest {
36
38
  'watchOrderBook': {
37
39
  'prec': 'P0',
38
40
  'freq': 'F0',
41
+ 'checksum': true,
39
42
  },
40
43
  'ordersLimit': 1000,
41
44
  },
@@ -46,21 +49,149 @@ export default class bitfinex extends bitfinexRest {
46
49
  const market = this.market(symbol);
47
50
  const marketId = market['id'];
48
51
  const url = this.urls['api']['ws']['public'];
52
+ const client = this.client(url);
49
53
  const messageHash = channel + ':' + marketId;
50
- // const channel = 'trades';
51
54
  const request = {
52
55
  'event': 'subscribe',
53
56
  'channel': channel,
54
57
  'symbol': marketId,
55
- 'messageHash': messageHash,
56
58
  };
57
- return await this.watch(url, messageHash, this.deepExtend(request, params), messageHash);
59
+ const result = await this.watch(url, messageHash, this.deepExtend(request, params), messageHash, { 'checksum': false });
60
+ const checksum = this.safeBool(this.options, 'checksum', true);
61
+ if (checksum && !client.subscriptions[messageHash]['checksum'] && (channel === 'book')) {
62
+ client.subscriptions[messageHash]['checksum'] = true;
63
+ await client.send({
64
+ 'event': 'conf',
65
+ 'flags': 131072,
66
+ });
67
+ }
68
+ return result;
69
+ }
70
+ async subscribePrivate(messageHash) {
71
+ await this.loadMarkets();
72
+ await this.authenticate();
73
+ const url = this.urls['api']['ws']['private'];
74
+ return await this.watch(url, messageHash, undefined, 1);
75
+ }
76
+ /**
77
+ * @method
78
+ * @name bitfinex#watchOHLCV
79
+ * @description watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
80
+ * @param {string} symbol unified symbol of the market to fetch OHLCV data for
81
+ * @param {string} timeframe the length of time each candle represents
82
+ * @param {int} [since] timestamp in ms of the earliest candle to fetch
83
+ * @param {int} [limit] the maximum amount of candles to fetch
84
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
85
+ * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
86
+ */
87
+ async watchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
88
+ await this.loadMarkets();
89
+ const market = this.market(symbol);
90
+ symbol = market['symbol'];
91
+ const interval = this.safeString(this.timeframes, timeframe, timeframe);
92
+ const channel = 'candles';
93
+ const key = 'trade:' + interval + ':' + market['id'];
94
+ const messageHash = channel + ':' + interval + ':' + market['id'];
95
+ const request = {
96
+ 'event': 'subscribe',
97
+ 'channel': channel,
98
+ 'key': key,
99
+ };
100
+ const url = this.urls['api']['ws']['public'];
101
+ // not using subscribe here because this message has a different format
102
+ const ohlcv = await this.watch(url, messageHash, this.deepExtend(request, params), messageHash);
103
+ if (this.newUpdates) {
104
+ limit = ohlcv.getLimit(symbol, limit);
105
+ }
106
+ return this.filterBySinceLimit(ohlcv, since, limit, 0, true);
107
+ }
108
+ handleOHLCV(client, message, subscription) {
109
+ //
110
+ // initial snapshot
111
+ // [
112
+ // 341527, // channel id
113
+ // [
114
+ // [
115
+ // 1654705860000, // timestamp
116
+ // 1802.6, // open
117
+ // 1800.3, // close
118
+ // 1802.8, // high
119
+ // 1800.3, // low
120
+ // 86.49588236 // volume
121
+ // ],
122
+ // [
123
+ // 1654705800000,
124
+ // 1803.6,
125
+ // 1802.6,
126
+ // 1804.9,
127
+ // 1802.3,
128
+ // 74.6348086
129
+ // ],
130
+ // [
131
+ // 1654705740000,
132
+ // 1802.5,
133
+ // 1803.2,
134
+ // 1804.4,
135
+ // 1802.4,
136
+ // 23.61801085
137
+ // ]
138
+ // ]
139
+ // ]
140
+ //
141
+ // update
142
+ // [
143
+ // 211171,
144
+ // [
145
+ // 1654705680000,
146
+ // 1801,
147
+ // 1802.4,
148
+ // 1802.9,
149
+ // 1800.4,
150
+ // 23.91911091
151
+ // ]
152
+ // ]
153
+ //
154
+ const data = this.safeValue(message, 1, []);
155
+ let ohlcvs = undefined;
156
+ const first = this.safeValue(data, 0);
157
+ if (Array.isArray(first)) {
158
+ // snapshot
159
+ ohlcvs = data;
160
+ }
161
+ else {
162
+ // update
163
+ ohlcvs = [data];
164
+ }
165
+ const channel = this.safeValue(subscription, 'channel');
166
+ const key = this.safeString(subscription, 'key');
167
+ const keyParts = key.split(':');
168
+ const interval = this.safeString(keyParts, 1);
169
+ let marketId = key;
170
+ marketId = marketId.replace('trade:', '');
171
+ marketId = marketId.replace(interval + ':', '');
172
+ const market = this.safeMarket(marketId);
173
+ const timeframe = this.findTimeframe(interval);
174
+ const symbol = market['symbol'];
175
+ const messageHash = channel + ':' + interval + ':' + marketId;
176
+ this.ohlcvs[symbol] = this.safeValue(this.ohlcvs, symbol, {});
177
+ let stored = this.safeValue(this.ohlcvs[symbol], timeframe);
178
+ if (stored === undefined) {
179
+ const limit = this.safeInteger(this.options, 'OHLCVLimit', 1000);
180
+ stored = new ArrayCacheByTimestamp(limit);
181
+ this.ohlcvs[symbol][timeframe] = stored;
182
+ }
183
+ const ohlcvsLength = ohlcvs.length;
184
+ for (let i = 0; i < ohlcvsLength; i++) {
185
+ const ohlcv = ohlcvs[ohlcvsLength - i - 1];
186
+ const parsed = this.parseOHLCV(ohlcv, market);
187
+ stored.append(parsed);
188
+ }
189
+ client.resolve(stored, messageHash);
58
190
  }
59
191
  /**
60
192
  * @method
61
193
  * @name bitfinex#watchTrades
62
194
  * @description get the list of most recent trades for a particular symbol
63
- * @see https://docs.bitfinex.com/v1/reference/ws-public-trades
64
195
  * @param {string} symbol unified symbol of the market to fetch trades for
65
196
  * @param {int} [since] timestamp in ms of the earliest trade to fetch
66
197
  * @param {int} [limit] the maximum amount of trades to fetch
@@ -68,19 +199,39 @@ export default class bitfinex extends bitfinexRest {
68
199
  * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
69
200
  */
70
201
  async watchTrades(symbol, since = undefined, limit = undefined, params = {}) {
71
- await this.loadMarkets();
72
- symbol = this.symbol(symbol);
73
202
  const trades = await this.subscribe('trades', symbol, params);
74
203
  if (this.newUpdates) {
75
204
  limit = trades.getLimit(symbol, limit);
76
205
  }
77
206
  return this.filterBySinceLimit(trades, since, limit, 'timestamp', true);
78
207
  }
208
+ /**
209
+ * @method
210
+ * @name bitfinex#watchMyTrades
211
+ * @description watches information on multiple trades made by the user
212
+ * @param {string} symbol unified market symbol of the market trades were made in
213
+ * @param {int} [since] the earliest time in ms to fetch trades for
214
+ * @param {int} [limit] the maximum number of trade structures to retrieve
215
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
216
+ * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
217
+ */
218
+ async watchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
219
+ await this.loadMarkets();
220
+ let messageHash = 'myTrade';
221
+ if (symbol !== undefined) {
222
+ const market = this.market(symbol);
223
+ messageHash += ':' + market['id'];
224
+ }
225
+ const trades = await this.subscribePrivate(messageHash);
226
+ if (this.newUpdates) {
227
+ limit = trades.getLimit(symbol, limit);
228
+ }
229
+ return this.filterBySymbolSinceLimit(trades, symbol, since, limit, true);
230
+ }
79
231
  /**
80
232
  * @method
81
233
  * @name bitfinex#watchTicker
82
234
  * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
83
- * @see https://docs.bitfinex.com/v1/reference/ws-public-ticker
84
235
  * @param {string} symbol unified symbol of the market to fetch the ticker for
85
236
  * @param {object} [params] extra parameters specific to the exchange API endpoint
86
237
  * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
@@ -88,100 +239,200 @@ export default class bitfinex extends bitfinexRest {
88
239
  async watchTicker(symbol, params = {}) {
89
240
  return await this.subscribe('ticker', symbol, params);
90
241
  }
91
- handleTrades(client, message, subscription) {
92
- //
93
- // initial snapshot
242
+ handleMyTrade(client, message, subscription = {}) {
94
243
  //
244
+ // trade execution
245
+ // [
246
+ // 0,
247
+ // "te", // or tu
95
248
  // [
96
- // 2,
97
- // [
98
- // [ null, 1580565020, 9374.9, 0.005 ],
99
- // [ null, 1580565004, 9374.9, 0.005 ],
100
- // [ null, 1580565003, 9374.9, 0.005 ],
101
- // ]
249
+ // 1133411090,
250
+ // "tLTCUST",
251
+ // 1655110144598,
252
+ // 97084883506,
253
+ // 0.1,
254
+ // 42.821,
255
+ // "EXCHANGE MARKET",
256
+ // 42.799,
257
+ // -1,
258
+ // null,
259
+ // null,
260
+ // 1655110144596
102
261
  // ]
262
+ // ]
103
263
  //
104
- // when a trade does not have an id yet
264
+ const name = 'myTrade';
265
+ const data = this.safeValue(message, 2);
266
+ const trade = this.parseWsTrade(data);
267
+ const symbol = trade['symbol'];
268
+ const market = this.market(symbol);
269
+ const messageHash = name + ':' + market['id'];
270
+ if (this.myTrades === undefined) {
271
+ const limit = this.safeInteger(this.options, 'tradesLimit', 1000);
272
+ this.myTrades = new ArrayCacheBySymbolById(limit);
273
+ }
274
+ const tradesArray = this.myTrades;
275
+ tradesArray.append(trade);
276
+ this.myTrades = tradesArray;
277
+ // generic subscription
278
+ client.resolve(tradesArray, name);
279
+ // specific subscription
280
+ client.resolve(tradesArray, messageHash);
281
+ }
282
+ handleTrades(client, message, subscription) {
283
+ //
284
+ // initial snapshot
105
285
  //
106
- // // channel id, update type, seq, time, price, amount
107
- // [ 2, "te", "28462857-BTCUSD", 1580565041, 9374.9, 0.005 ],
286
+ // [
287
+ // 188687, // channel id
288
+ // [
289
+ // [ 1128060675, 1654701572690, 0.00217533, 1815.3 ], // id, mts, amount, price
290
+ // [ 1128060665, 1654701551231, -0.00280472, 1814.1 ],
291
+ // [ 1128060664, 1654701550996, -0.00364444, 1814.1 ],
292
+ // [ 1128060656, 1654701527730, -0.00265203, 1814.2 ],
293
+ // [ 1128060647, 1654701505193, 0.00262395, 1815.2 ],
294
+ // [ 1128060642, 1654701484656, -0.13411443, 1816 ],
295
+ // [ 1128060641, 1654701484656, -0.00088557, 1816 ],
296
+ // [ 1128060639, 1654701478326, -0.002, 1816 ],
297
+ // ]
298
+ // ]
299
+ // update
108
300
  //
109
- // when a trade already has an id
301
+ // [
302
+ // 360141,
303
+ // "te",
304
+ // [
305
+ // 1128060969, // id
306
+ // 1654702500098, // mts
307
+ // 0.00325131, // amount positive buy, negative sell
308
+ // 1818.5, // price
309
+ // ],
310
+ // ]
110
311
  //
111
- // // channel id, update type, seq, trade id, time, price, amount
112
- // [ 2, "tu", "28462857-BTCUSD", 413357662, 1580565041, 9374.9, 0.005 ]
113
312
  //
114
313
  const channel = this.safeValue(subscription, 'channel');
115
- const marketId = this.safeString(subscription, 'pair');
314
+ const marketId = this.safeString(subscription, 'symbol');
315
+ const market = this.safeMarket(marketId);
116
316
  const messageHash = channel + ':' + marketId;
117
317
  const tradesLimit = this.safeInteger(this.options, 'tradesLimit', 1000);
118
- const market = this.safeMarket(marketId);
119
318
  const symbol = market['symbol'];
120
- const data = this.safeValue(message, 1);
121
319
  let stored = this.safeValue(this.trades, symbol);
122
320
  if (stored === undefined) {
123
321
  stored = new ArrayCache(tradesLimit);
124
322
  this.trades[symbol] = stored;
125
323
  }
126
- if (Array.isArray(data)) {
127
- const trades = this.parseTrades(data, market);
128
- for (let i = 0; i < trades.length; i++) {
129
- stored.append(trades[i]);
324
+ const messageLength = message.length;
325
+ if (messageLength === 2) {
326
+ // initial snapshot
327
+ const trades = this.safeList(message, 1, []);
328
+ // needs to be reversed to make chronological order
329
+ const length = trades.length;
330
+ for (let i = 0; i < length; i++) {
331
+ const index = length - i - 1;
332
+ const parsed = this.parseWsTrade(trades[index], market);
333
+ stored.append(parsed);
130
334
  }
131
335
  }
132
336
  else {
133
- const second = this.safeString(message, 1);
134
- if (second !== 'tu') {
337
+ // update
338
+ const type = this.safeString(message, 1);
339
+ if (type === 'tu') {
340
+ // don't resolve for a duplicate update
341
+ // since te and tu updates are duplicated on the public stream
135
342
  return;
136
343
  }
137
- const trade = this.parseTrade(message, market);
138
- stored.append(trade);
344
+ const trade = this.safeValue(message, 2, []);
345
+ const parsed = this.parseWsTrade(trade, market);
346
+ stored.append(parsed);
139
347
  }
140
348
  client.resolve(stored, messageHash);
141
349
  }
142
- parseTrade(trade, market = undefined) {
143
- //
144
- // snapshot trade
350
+ parseWsTrade(trade, market = undefined) {
145
351
  //
146
- // // null, time, price, amount
147
- // [ null, 1580565020, 9374.9, 0.005 ],
352
+ // [
353
+ // 1128060969, // id
354
+ // 1654702500098, // mts
355
+ // 0.00325131, // amount positive buy, negative sell
356
+ // 1818.5, // price
357
+ // ]
148
358
  //
149
- // when a trade does not have an id yet
359
+ // trade execution
150
360
  //
151
- // // channel id, update type, seq, time, price, amount
152
- // [ 2, "te", "28462857-BTCUSD", 1580565041, 9374.9, 0.005 ],
361
+ // [
362
+ // 1133411090, // id
363
+ // "tLTCUST", // symbol
364
+ // 1655110144598, // create ms
365
+ // 97084883506, // order id
366
+ // 0.1, // amount
367
+ // 42.821, // price
368
+ // "EXCHANGE MARKET", // order type
369
+ // 42.799, // order price
370
+ // -1, // maker
371
+ // null, // fee
372
+ // null, // fee currency
373
+ // 1655110144596 // cid
374
+ // ]
153
375
  //
154
- // when a trade already has an id
376
+ // trade update
155
377
  //
156
- // // channel id, update type, seq, trade id, time, price, amount
157
- // [ 2, "tu", "28462857-BTCUSD", 413357662, 1580565041, 9374.9, 0.005 ]
378
+ // [
379
+ // 1133411090,
380
+ // "tLTCUST",
381
+ // 1655110144598,
382
+ // 97084883506,
383
+ // 0.1,
384
+ // 42.821,
385
+ // "EXCHANGE MARKET",
386
+ // 42.799,
387
+ // -1,
388
+ // -0.0002,
389
+ // "LTC",
390
+ // 1655110144596
391
+ // ]
158
392
  //
159
- if (!Array.isArray(trade)) {
160
- return super.parseTrade(trade, market);
161
- }
162
- const tradeLength = trade.length;
163
- const event = this.safeString(trade, 1);
164
- let id = undefined;
165
- if (event === 'tu') {
166
- id = this.safeString(trade, tradeLength - 4);
393
+ const numFields = trade.length;
394
+ const isPublic = numFields <= 8;
395
+ let marketId = (!isPublic) ? this.safeString(trade, 1) : undefined;
396
+ market = this.safeMarket(marketId, market);
397
+ const createdKey = isPublic ? 1 : 2;
398
+ const priceKey = isPublic ? 3 : 5;
399
+ const amountKey = isPublic ? 2 : 4;
400
+ marketId = market['id'];
401
+ let type = this.safeString(trade, 6);
402
+ if (type !== undefined) {
403
+ if (type.indexOf('LIMIT') > -1) {
404
+ type = 'limit';
405
+ }
406
+ else if (type.indexOf('MARKET') > -1) {
407
+ type = 'market';
408
+ }
167
409
  }
168
- const timestamp = this.safeTimestamp(trade, tradeLength - 3);
169
- const price = this.safeString(trade, tradeLength - 2);
170
- let amount = this.safeString(trade, tradeLength - 1);
410
+ const orderId = (!isPublic) ? this.safeString(trade, 3) : undefined;
411
+ const id = this.safeString(trade, 0);
412
+ const timestamp = this.safeInteger(trade, createdKey);
413
+ const price = this.safeString(trade, priceKey);
414
+ const amountString = this.safeString(trade, amountKey);
415
+ const amount = this.parseNumber(Precise.stringAbs(amountString));
171
416
  let side = undefined;
172
417
  if (amount !== undefined) {
173
- side = Precise.stringGt(amount, '0') ? 'buy' : 'sell';
174
- amount = Precise.stringAbs(amount);
175
- }
176
- const seq = this.safeString(trade, 2);
177
- const parts = seq.split('-');
178
- let marketId = this.safeString(parts, 1);
179
- if (marketId !== undefined) {
180
- marketId = marketId.replace('t', '');
418
+ side = Precise.stringGt(amountString, '0') ? 'buy' : 'sell';
181
419
  }
182
420
  const symbol = this.safeSymbol(marketId, market);
183
- const takerOrMaker = undefined;
184
- const orderId = undefined;
421
+ const feeValue = this.safeString(trade, 9);
422
+ let fee = undefined;
423
+ if (feeValue !== undefined) {
424
+ const currencyId = this.safeString(trade, 10);
425
+ const code = this.safeCurrencyCode(currencyId);
426
+ fee = {
427
+ 'cost': feeValue,
428
+ 'currency': code,
429
+ };
430
+ }
431
+ const maker = this.safeInteger(trade, 8);
432
+ let takerOrMaker = undefined;
433
+ if (maker !== undefined) {
434
+ takerOrMaker = (maker === -1) ? 'taker' : 'maker';
435
+ }
185
436
  return this.safeTrade({
186
437
  'info': trade,
187
438
  'timestamp': timestamp,
@@ -189,19 +440,20 @@ export default class bitfinex extends bitfinexRest {
189
440
  'symbol': symbol,
190
441
  'id': id,
191
442
  'order': orderId,
192
- 'type': undefined,
443
+ 'type': type,
193
444
  'takerOrMaker': takerOrMaker,
194
445
  'side': side,
195
446
  'price': price,
196
447
  'amount': amount,
197
448
  'cost': undefined,
198
- 'fee': undefined,
199
- });
449
+ 'fee': fee,
450
+ }, market);
200
451
  }
201
452
  handleTicker(client, message, subscription) {
202
453
  //
454
+ // [
455
+ // 340432, // channel ID
203
456
  // [
204
- // 2, // 0 CHANNEL_ID integer Channel ID
205
457
  // 236.62, // 1 BID float Price of last highest bid
206
458
  // 9.0029, // 2 BID_SIZE float Size of the last highest bid
207
459
  // 236.88, // 3 ASK float Price of last lowest ask
@@ -213,47 +465,64 @@ export default class bitfinex extends bitfinexRest {
213
465
  // 250.01, // 9 HIGH float Daily high
214
466
  // 220.05, // 10 LOW float Daily low
215
467
  // ]
468
+ // ]
216
469
  //
217
- const marketId = this.safeString(subscription, 'pair');
470
+ const ticker = this.safeValue(message, 1);
471
+ const marketId = this.safeString(subscription, 'symbol');
472
+ const market = this.safeMarket(marketId);
218
473
  const symbol = this.safeSymbol(marketId);
474
+ const parsed = this.parseWsTicker(ticker, market);
219
475
  const channel = 'ticker';
220
476
  const messageHash = channel + ':' + marketId;
221
- const last = this.safeString(message, 7);
222
- const change = this.safeString(message, 5);
223
- let open = undefined;
224
- if ((last !== undefined) && (change !== undefined)) {
225
- open = Precise.stringSub(last, change);
226
- }
227
- const result = this.safeTicker({
477
+ this.tickers[symbol] = parsed;
478
+ client.resolve(parsed, messageHash);
479
+ }
480
+ parseWsTicker(ticker, market = undefined) {
481
+ //
482
+ // [
483
+ // 236.62, // 1 BID float Price of last highest bid
484
+ // 9.0029, // 2 BID_SIZE float Size of the last highest bid
485
+ // 236.88, // 3 ASK float Price of last lowest ask
486
+ // 7.1138, // 4 ASK_SIZE float Size of the last lowest ask
487
+ // -1.02, // 5 DAILY_CHANGE float Amount that the last price has changed since yesterday
488
+ // 0, // 6 DAILY_CHANGE_PERC float Amount that the price has changed expressed in percentage terms
489
+ // 236.52, // 7 LAST_PRICE float Price of the last trade.
490
+ // 5191.36754297, // 8 VOLUME float Daily volume
491
+ // 250.01, // 9 HIGH float Daily high
492
+ // 220.05, // 10 LOW float Daily low
493
+ // ]
494
+ //
495
+ market = this.safeMarket(undefined, market);
496
+ const symbol = market['symbol'];
497
+ const last = this.safeString(ticker, 6);
498
+ const change = this.safeString(ticker, 4);
499
+ return this.safeTicker({
228
500
  'symbol': symbol,
229
501
  'timestamp': undefined,
230
502
  'datetime': undefined,
231
- 'high': this.safeString(message, 9),
232
- 'low': this.safeString(message, 10),
233
- 'bid': this.safeString(message, 1),
234
- 'bidVolume': undefined,
235
- 'ask': this.safeString(message, 3),
236
- 'askVolume': undefined,
503
+ 'high': this.safeString(ticker, 8),
504
+ 'low': this.safeString(ticker, 9),
505
+ 'bid': this.safeString(ticker, 0),
506
+ 'bidVolume': this.safeString(ticker, 1),
507
+ 'ask': this.safeString(ticker, 2),
508
+ 'askVolume': this.safeString(ticker, 3),
237
509
  'vwap': undefined,
238
- 'open': this.parseNumber(open),
239
- 'close': this.parseNumber(last),
240
- 'last': this.parseNumber(last),
510
+ 'open': undefined,
511
+ 'close': last,
512
+ 'last': last,
241
513
  'previousClose': undefined,
242
- 'change': this.parseNumber(change),
243
- 'percentage': this.safeString(message, 6),
514
+ 'change': change,
515
+ 'percentage': this.safeString(ticker, 5),
244
516
  'average': undefined,
245
- 'baseVolume': this.safeString(message, 8),
517
+ 'baseVolume': this.safeString(ticker, 7),
246
518
  'quoteVolume': undefined,
247
- 'info': message,
248
- });
249
- this.tickers[symbol] = result;
250
- client.resolve(result, messageHash);
519
+ 'info': ticker,
520
+ }, market);
251
521
  }
252
522
  /**
253
523
  * @method
254
524
  * @name bitfinex#watchOrderBook
255
525
  * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
256
- * @see https://docs.bitfinex.com/v1/reference/ws-public-order-books
257
526
  * @param {string} symbol unified symbol of the market to fetch the order book for
258
527
  * @param {int} [limit] the maximum amount of order book entries to return
259
528
  * @param {object} [params] extra parameters specific to the exchange API endpoint
@@ -269,13 +538,12 @@ export default class bitfinex extends bitfinexRest {
269
538
  const prec = this.safeString(options, 'prec', 'P0');
270
539
  const freq = this.safeString(options, 'freq', 'F0');
271
540
  const request = {
272
- // "event": "subscribe", // added in subscribe()
273
- // "channel": channel, // added in subscribe()
274
- // "symbol": marketId, // added in subscribe()
275
541
  'prec': prec,
276
- 'freq': freq,
277
- 'len': limit, // string, number of price points, '25', '100', default = '25'
542
+ 'freq': freq, // string, frequency of updates 'F0' = realtime, 'F1' = 2 seconds, default is 'F0'
278
543
  };
544
+ if (limit !== undefined) {
545
+ request['len'] = limit; // string, number of price points, '25', '100', default = '25'
546
+ }
279
547
  const orderbook = await this.subscribe('book', symbol, this.deepExtend(request, params));
280
548
  return orderbook.limit();
281
549
  }
@@ -298,20 +566,22 @@ export default class bitfinex extends bitfinexRest {
298
566
  // subsequent updates
299
567
  //
300
568
  // [
301
- // 30, // channel id
302
- // 9339.9, // price
303
- // 0, // count
304
- // -1, // size > 0 = bid, size < 0 = ask
569
+ // 358169, // channel id
570
+ // [
571
+ // 1807.1, // price
572
+ // 0, // cound
573
+ // 1 // size
574
+ // ]
305
575
  // ]
306
576
  //
307
- const marketId = this.safeString(subscription, 'pair');
577
+ const marketId = this.safeString(subscription, 'symbol');
308
578
  const symbol = this.safeSymbol(marketId);
309
579
  const channel = 'book';
310
580
  const messageHash = channel + ':' + marketId;
311
581
  const prec = this.safeString(subscription, 'prec', 'P0');
312
582
  const isRaw = (prec === 'R0');
313
583
  // if it is an initial snapshot
314
- if (Array.isArray(message[1])) {
584
+ if (!(symbol in this.orderbooks)) {
315
585
  const limit = this.safeInteger(subscription, 'len');
316
586
  if (isRaw) {
317
587
  // raw order books
@@ -326,65 +596,232 @@ export default class bitfinex extends bitfinexRest {
326
596
  const deltas = message[1];
327
597
  for (let i = 0; i < deltas.length; i++) {
328
598
  const delta = deltas[i];
329
- const id = this.safeString(delta, 0);
330
- const price = this.safeFloat(delta, 1);
331
- const delta2Value = delta[2];
332
- const size = (delta2Value < 0) ? -delta2Value : delta2Value;
333
- const side = (delta2Value < 0) ? 'asks' : 'bids';
599
+ const delta2 = delta[2];
600
+ const size = (delta2 < 0) ? -delta2 : delta2;
601
+ const side = (delta2 < 0) ? 'asks' : 'bids';
334
602
  const bookside = orderbook[side];
335
- bookside.storeArray([price, size, id]);
603
+ const idString = this.safeString(delta, 0);
604
+ const price = this.safeFloat(delta, 1);
605
+ bookside.storeArray([price, size, idString]);
336
606
  }
337
607
  }
338
608
  else {
339
609
  const deltas = message[1];
340
610
  for (let i = 0; i < deltas.length; i++) {
341
611
  const delta = deltas[i];
342
- const delta2 = delta[2];
343
- const size = (delta2 < 0) ? -delta2 : delta2;
344
- const side = (delta2 < 0) ? 'asks' : 'bids';
345
- const countedBookSide = orderbook[side];
346
- countedBookSide.storeArray([delta[0], size, delta[1]]);
612
+ const amount = this.safeNumber(delta, 2);
613
+ const counter = this.safeNumber(delta, 1);
614
+ const price = this.safeNumber(delta, 0);
615
+ const size = (amount < 0) ? -amount : amount;
616
+ const side = (amount < 0) ? 'asks' : 'bids';
617
+ const bookside = orderbook[side];
618
+ bookside.storeArray([price, size, counter]);
347
619
  }
348
620
  }
621
+ orderbook['symbol'] = symbol;
349
622
  client.resolve(orderbook, messageHash);
350
623
  }
351
624
  else {
352
625
  const orderbook = this.orderbooks[symbol];
626
+ const deltas = message[1];
627
+ const orderbookItem = this.orderbooks[symbol];
353
628
  if (isRaw) {
354
- const id = this.safeString(message, 1);
355
- const price = this.safeString(message, 2);
356
- const message3 = message[3];
357
- const size = (message3 < 0) ? -message3 : message3;
358
- const side = (message3 < 0) ? 'asks' : 'bids';
359
- const bookside = orderbook[side];
629
+ const price = this.safeString(deltas, 1);
630
+ const deltas2 = deltas[2];
631
+ const size = (deltas2 < 0) ? -deltas2 : deltas2;
632
+ const side = (deltas2 < 0) ? 'asks' : 'bids';
633
+ const bookside = orderbookItem[side];
360
634
  // price = 0 means that you have to remove the order from your book
361
635
  const amount = Precise.stringGt(price, '0') ? size : '0';
362
- bookside.storeArray([this.parseNumber(price), this.parseNumber(amount), id]);
636
+ const idString = this.safeString(deltas, 0);
637
+ bookside.storeArray([this.parseNumber(price), this.parseNumber(amount), idString]);
363
638
  }
364
639
  else {
365
- const message3Value = message[3];
366
- const size = (message3Value < 0) ? -message3Value : message3Value;
367
- const side = (message3Value < 0) ? 'asks' : 'bids';
368
- const countedBookSide = orderbook[side];
369
- countedBookSide.storeArray([message[1], size, message[2]]);
640
+ const amount = this.safeString(deltas, 2);
641
+ const counter = this.safeString(deltas, 1);
642
+ const price = this.safeString(deltas, 0);
643
+ const size = Precise.stringLt(amount, '0') ? Precise.stringNeg(amount) : amount;
644
+ const side = Precise.stringLt(amount, '0') ? 'asks' : 'bids';
645
+ const bookside = orderbookItem[side];
646
+ bookside.storeArray([this.parseNumber(price), this.parseNumber(size), this.parseNumber(counter)]);
370
647
  }
371
648
  client.resolve(orderbook, messageHash);
372
649
  }
373
650
  }
374
- handleHeartbeat(client, message) {
651
+ handleChecksum(client, message, subscription) {
375
652
  //
376
- // every second (approx) if no other updates are sent
653
+ // [ 173904, "cs", -890884919 ]
377
654
  //
378
- // { "event": "heartbeat" }
655
+ const marketId = this.safeString(subscription, 'symbol');
656
+ const symbol = this.safeSymbol(marketId);
657
+ const channel = 'book';
658
+ const messageHash = channel + ':' + marketId;
659
+ const book = this.safeValue(this.orderbooks, symbol);
660
+ if (book === undefined) {
661
+ return;
662
+ }
663
+ const depth = 25; // covers the first 25 bids and asks
664
+ const stringArray = [];
665
+ const bids = book['bids'];
666
+ const asks = book['asks'];
667
+ const prec = this.safeString(subscription, 'prec', 'P0');
668
+ const isRaw = (prec === 'R0');
669
+ const idToCheck = isRaw ? 2 : 0;
670
+ // pepperoni pizza from bitfinex
671
+ for (let i = 0; i < depth; i++) {
672
+ const bid = this.safeValue(bids, i);
673
+ const ask = this.safeValue(asks, i);
674
+ if (bid !== undefined) {
675
+ stringArray.push(this.numberToString(bids[i][idToCheck]));
676
+ stringArray.push(this.numberToString(bids[i][1]));
677
+ }
678
+ if (ask !== undefined) {
679
+ stringArray.push(this.numberToString(asks[i][idToCheck]));
680
+ const aski1 = asks[i][1];
681
+ stringArray.push(this.numberToString(-aski1));
682
+ }
683
+ }
684
+ const payload = stringArray.join(':');
685
+ const localChecksum = this.crc32(payload, true);
686
+ const responseChecksum = this.safeInteger(message, 2);
687
+ if (responseChecksum !== localChecksum) {
688
+ delete client.subscriptions[messageHash];
689
+ delete this.orderbooks[symbol];
690
+ const checksum = this.handleOption('watchOrderBook', 'checksum', true);
691
+ if (checksum) {
692
+ const error = new ChecksumError(this.id + ' ' + this.orderbookChecksumMessage(symbol));
693
+ client.reject(error, messageHash);
694
+ }
695
+ }
696
+ }
697
+ /**
698
+ * @method
699
+ * @name bitfinex#watchBalance
700
+ * @description watch balance and get the amount of funds available for trading or funds locked in orders
701
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
702
+ * @param {str} [params.type] spot or contract if not provided this.options['defaultType'] is used
703
+ * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
704
+ */
705
+ async watchBalance(params = {}) {
706
+ await this.loadMarkets();
707
+ const balanceType = this.safeString(params, 'wallet', 'exchange'); // exchange, margin
708
+ params = this.omit(params, 'wallet');
709
+ const messageHash = 'balance:' + balanceType;
710
+ return await this.subscribePrivate(messageHash);
711
+ }
712
+ handleBalance(client, message, subscription) {
713
+ //
714
+ // snapshot (exchange + margin together)
715
+ // [
716
+ // 0,
717
+ // "ws",
718
+ // [
719
+ // [
720
+ // "exchange",
721
+ // "LTC",
722
+ // 0.05479727,
723
+ // 0,
724
+ // null,
725
+ // "Trading fees for 0.05 LTC (LTCUST) @ 51.872 on BFX (0.2%)",
726
+ // null,
727
+ // ]
728
+ // [
729
+ // "margin",
730
+ // "USTF0",
731
+ // 11.960650700086292,
732
+ // 0,
733
+ // null,
734
+ // "Trading fees for 0.1 LTCF0 (LTCF0:USTF0) @ 51.844 on BFX (0.065%)",
735
+ // null,
736
+ // ],
737
+ // ],
738
+ // ]
379
739
  //
380
- const event = this.safeString(message, 'event');
381
- client.resolve(message, event);
740
+ // spot
741
+ // [
742
+ // 0,
743
+ // "wu",
744
+ // [
745
+ // "exchange",
746
+ // "LTC", // currency
747
+ // 0.06729727, // wallet balance
748
+ // 0, // unsettled balance
749
+ // 0.06729727, // available balance might be null
750
+ // "Exchange 0.4 LTC for UST @ 65.075",
751
+ // {
752
+ // "reason": "TRADE",
753
+ // "order_id": 96596397973,
754
+ // "order_id_oppo": 96596632735,
755
+ // "trade_price": "65.075",
756
+ // "trade_amount": "-0.4",
757
+ // "order_cid": 1654636218766,
758
+ // "order_gid": null
759
+ // }
760
+ // ]
761
+ // ]
762
+ //
763
+ // margin
764
+ //
765
+ // [
766
+ // "margin",
767
+ // "USTF0",
768
+ // 11.960650700086292, // total
769
+ // 0,
770
+ // 6.776250700086292, // available
771
+ // "Trading fees for 0.1 LTCF0 (LTCF0:USTF0) @ 51.844 on BFX (0.065%)",
772
+ // null
773
+ // ]
774
+ //
775
+ const updateType = this.safeValue(message, 1);
776
+ let data = undefined;
777
+ if (updateType === 'ws') {
778
+ data = this.safeValue(message, 2);
779
+ }
780
+ else {
781
+ data = [this.safeValue(message, 2)];
782
+ }
783
+ const updatedTypes = {};
784
+ for (let i = 0; i < data.length; i++) {
785
+ const rawBalance = data[i];
786
+ const currencyId = this.safeString(rawBalance, 1);
787
+ const code = this.safeCurrencyCode(currencyId);
788
+ const balance = this.parseWsBalance(rawBalance);
789
+ const balanceType = this.safeString(rawBalance, 0);
790
+ const oldBalance = this.safeValue(this.balance, balanceType, {});
791
+ oldBalance[code] = balance;
792
+ oldBalance['info'] = message;
793
+ this.balance[balanceType] = this.safeBalance(oldBalance);
794
+ updatedTypes[balanceType] = true;
795
+ }
796
+ const updatesKeys = Object.keys(updatedTypes);
797
+ for (let i = 0; i < updatesKeys.length; i++) {
798
+ const type = updatesKeys[i];
799
+ const messageHash = 'balance:' + type;
800
+ client.resolve(this.balance[type], messageHash);
801
+ }
382
802
  }
383
- handleSystemStatus(client, message) {
803
+ parseWsBalance(balance) {
384
804
  //
385
- // todo: answer the question whether handleSystemStatus should be renamed
386
- // and unified as handleStatus for any usage pattern that
387
- // involves system status and maintenance updates
805
+ // [
806
+ // "exchange",
807
+ // "LTC",
808
+ // 0.05479727, // balance
809
+ // 0,
810
+ // null, // available null if not calculated yet
811
+ // "Trading fees for 0.05 LTC (LTCUST) @ 51.872 on BFX (0.2%)",
812
+ // null,
813
+ // ]
814
+ //
815
+ const totalBalance = this.safeString(balance, 2);
816
+ const availableBalance = this.safeString(balance, 4);
817
+ const account = this.account();
818
+ if (availableBalance !== undefined) {
819
+ account['free'] = availableBalance;
820
+ }
821
+ account['total'] = totalBalance;
822
+ return account;
823
+ }
824
+ handleSystemStatus(client, message) {
388
825
  //
389
826
  // {
390
827
  // "event": "info",
@@ -415,57 +852,47 @@ export default class bitfinex extends bitfinexRest {
415
852
  async authenticate(params = {}) {
416
853
  const url = this.urls['api']['ws']['private'];
417
854
  const client = this.client(url);
418
- const future = client.future('authenticated');
419
- const method = 'auth';
420
- const authenticated = this.safeValue(client.subscriptions, method);
855
+ const messageHash = 'authenticated';
856
+ const future = client.future(messageHash);
857
+ const authenticated = this.safeValue(client.subscriptions, messageHash);
421
858
  if (authenticated === undefined) {
422
859
  const nonce = this.milliseconds();
423
860
  const payload = 'AUTH' + nonce.toString();
424
861
  const signature = this.hmac(this.encode(payload), this.encode(this.secret), sha384, 'hex');
862
+ const event = 'auth';
425
863
  const request = {
426
864
  'apiKey': this.apiKey,
427
865
  'authSig': signature,
428
866
  'authNonce': nonce,
429
867
  'authPayload': payload,
430
- 'event': method,
431
- 'filter': [
432
- 'trading',
433
- 'wallet',
434
- ],
868
+ 'event': event,
435
869
  };
436
- this.spawn(this.watch, url, method, request, 1);
870
+ const message = this.extend(request, params);
871
+ this.watch(url, messageHash, message, messageHash);
437
872
  }
438
873
  return await future;
439
874
  }
440
875
  handleAuthenticationMessage(client, message) {
876
+ const messageHash = 'authenticated';
441
877
  const status = this.safeString(message, 'status');
442
878
  if (status === 'OK') {
443
879
  // we resolve the future here permanently so authentication only happens once
444
- const future = this.safeValue(client.futures, 'authenticated');
880
+ const future = this.safeValue(client.futures, messageHash);
445
881
  future.resolve(true);
446
882
  }
447
883
  else {
448
884
  const error = new AuthenticationError(this.json(message));
449
- client.reject(error, 'authenticated');
885
+ client.reject(error, messageHash);
450
886
  // allows further authentication attempts
451
- const method = this.safeString(message, 'event');
452
- if (method in client.subscriptions) {
453
- delete client.subscriptions[method];
887
+ if (messageHash in client.subscriptions) {
888
+ delete client.subscriptions[messageHash];
454
889
  }
455
890
  }
456
891
  }
457
- async watchOrder(id, symbol = undefined, params = {}) {
458
- await this.loadMarkets();
459
- const url = this.urls['api']['ws']['private'];
460
- await this.authenticate();
461
- return await this.watch(url, id, undefined, 1);
462
- }
463
892
  /**
464
893
  * @method
465
894
  * @name bitfinex#watchOrders
466
895
  * @description watches information on multiple orders made by the user
467
- * @see https://docs.bitfinex.com/v1/reference/ws-auth-order-updates
468
- * @see https://docs.bitfinex.com/v1/reference/ws-auth-order-snapshots
469
896
  * @param {string} symbol unified market symbol of the market orders were made in
470
897
  * @param {int} [since] the earliest time in ms to fetch orders for
471
898
  * @param {int} [limit] the maximum number of order structures to retrieve
@@ -474,12 +901,12 @@ export default class bitfinex extends bitfinexRest {
474
901
  */
475
902
  async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
476
903
  await this.loadMarkets();
477
- await this.authenticate();
904
+ let messageHash = 'orders';
478
905
  if (symbol !== undefined) {
479
- symbol = this.symbol(symbol);
906
+ const market = this.market(symbol);
907
+ messageHash += ':' + market['id'];
480
908
  }
481
- const url = this.urls['api']['ws']['private'];
482
- const orders = await this.watch(url, 'os', undefined, 1);
909
+ const orders = await this.subscribePrivate(messageHash);
483
910
  if (this.newUpdates) {
484
911
  limit = orders.getLimit(symbol, limit);
485
912
  }
@@ -487,111 +914,159 @@ export default class bitfinex extends bitfinexRest {
487
914
  }
488
915
  handleOrders(client, message, subscription) {
489
916
  //
490
- // order snapshot
491
- //
492
- // [
493
- // 0,
494
- // "os",
495
- // [
496
- // [
497
- // 45287766631,
498
- // "ETHUST",
499
- // -0.07,
500
- // -0.07,
501
- // "EXCHANGE LIMIT",
502
- // "ACTIVE",
503
- // 210,
504
- // 0,
505
- // "2020-05-16T13:17:46Z",
506
- // 0,
507
- // 0,
508
- // 0
509
- // ]
510
- // ]
511
- // ]
512
- //
513
- // order cancel
514
- //
515
- // [
516
- // 0,
517
- // "oc",
518
- // [
519
- // 45287766631,
520
- // "ETHUST",
521
- // -0.07,
522
- // -0.07,
523
- // "EXCHANGE LIMIT",
524
- // "CANCELED",
525
- // 210,
526
- // 0,
527
- // "2020-05-16T13:17:46Z",
528
- // 0,
529
- // 0,
530
- // 0,
531
- // ]
532
- // ]
917
+ // limit order
918
+ // [
919
+ // 0,
920
+ // "on", // ou or oc
921
+ // [
922
+ // 96923856256, // order id
923
+ // null, // gid
924
+ // 1655029337026, // cid
925
+ // "tLTCUST", // symbol
926
+ // 1655029337027, // created timestamp
927
+ // 1655029337029, // updated timestamp
928
+ // 0.1, // amount
929
+ // 0.1, // amount_orig
930
+ // "EXCHANGE LIMIT", // order type
931
+ // null, // type_prev
932
+ // null, // mts_tif
933
+ // null, // placeholder
934
+ // 0, // flags
935
+ // "ACTIVE", // status
936
+ // null,
937
+ // null,
938
+ // 30, // price
939
+ // 0, // price average
940
+ // 0, // price_trailling
941
+ // 0, // price_aux_limit
942
+ // null,
943
+ // null,
944
+ // null,
945
+ // 0, // notify
946
+ // 0,
947
+ // null,
948
+ // null,
949
+ // null,
950
+ // "BFX",
951
+ // null,
952
+ // null,
953
+ // ]
954
+ // ]
533
955
  //
534
956
  const data = this.safeValue(message, 2, []);
535
957
  const messageType = this.safeString(message, 1);
958
+ if (this.orders === undefined) {
959
+ const limit = this.safeInteger(this.options, 'ordersLimit', 1000);
960
+ this.orders = new ArrayCacheBySymbolById(limit);
961
+ }
962
+ const orders = this.orders;
963
+ const symbolIds = {};
536
964
  if (messageType === 'os') {
965
+ const snapshotLength = data.length;
966
+ if (snapshotLength === 0) {
967
+ return;
968
+ }
537
969
  for (let i = 0; i < data.length; i++) {
538
970
  const value = data[i];
539
- this.handleOrder(client, value);
971
+ const parsed = this.parseWsOrder(value);
972
+ const symbol = parsed['symbol'];
973
+ symbolIds[symbol] = true;
974
+ orders.append(parsed);
540
975
  }
541
976
  }
542
977
  else {
543
- this.handleOrder(client, data);
978
+ const parsed = this.parseWsOrder(data);
979
+ orders.append(parsed);
980
+ const symbol = parsed['symbol'];
981
+ symbolIds[symbol] = true;
544
982
  }
545
- if (this.orders !== undefined) {
546
- client.resolve(this.orders, 'os');
983
+ const name = 'orders';
984
+ client.resolve(this.orders, name);
985
+ const keys = Object.keys(symbolIds);
986
+ for (let i = 0; i < keys.length; i++) {
987
+ const symbol = keys[i];
988
+ const market = this.market(symbol);
989
+ const messageHash = name + ':' + market['id'];
990
+ client.resolve(this.orders, messageHash);
547
991
  }
548
992
  }
549
993
  parseWsOrderStatus(status) {
550
994
  const statuses = {
551
995
  'ACTIVE': 'open',
552
996
  'CANCELED': 'canceled',
997
+ 'EXECUTED': 'closed',
998
+ 'PARTIALLY': 'open',
553
999
  };
554
1000
  return this.safeString(statuses, status, status);
555
1001
  }
556
- handleOrder(client, order) {
557
- // [ 45287766631,
558
- // "ETHUST",
559
- // -0.07,
560
- // -0.07,
561
- // "EXCHANGE LIMIT",
562
- // "CANCELED",
563
- // 210,
564
- // 0,
565
- // "2020-05-16T13:17:46Z",
566
- // 0,
567
- // 0,
568
- // 0 ]
1002
+ parseWsOrder(order, market = undefined) {
1003
+ //
1004
+ // [
1005
+ // 97084883506, // order id
1006
+ // null,
1007
+ // 1655110144596, // clientOrderId
1008
+ // "tLTCUST", // symbol
1009
+ // 1655110144596, // created timestamp
1010
+ // 1655110144598, // updated timestamp
1011
+ // 0, // amount
1012
+ // 0.1, // amount_orig negative if sell order
1013
+ // "EXCHANGE MARKET", // type
1014
+ // null,
1015
+ // null,
1016
+ // null,
1017
+ // 0,
1018
+ // "EXECUTED @ 42.821(0.1)", // status
1019
+ // null,
1020
+ // null,
1021
+ // 42.799, // price
1022
+ // 42.821, // price average
1023
+ // 0, // price trailling
1024
+ // 0, // price_aux_limit
1025
+ // null,
1026
+ // null,
1027
+ // null,
1028
+ // 0,
1029
+ // 0,
1030
+ // null,
1031
+ // null,
1032
+ // null,
1033
+ // "BFX",
1034
+ // null,
1035
+ // null,
1036
+ // {}
1037
+ // ]
1038
+ //
569
1039
  const id = this.safeString(order, 0);
570
- const marketId = this.safeString(order, 1);
1040
+ const clientOrderId = this.safeString(order, 1);
1041
+ const marketId = this.safeString(order, 3);
571
1042
  const symbol = this.safeSymbol(marketId);
572
- let amount = this.safeString(order, 2);
573
- let remaining = this.safeString(order, 3);
1043
+ market = this.safeMarket(symbol);
1044
+ let amount = this.safeString(order, 7);
574
1045
  let side = 'buy';
575
1046
  if (Precise.stringLt(amount, '0')) {
576
1047
  amount = Precise.stringAbs(amount);
577
- remaining = Precise.stringAbs(remaining);
578
1048
  side = 'sell';
579
1049
  }
580
- let type = this.safeString(order, 4);
1050
+ const remaining = Precise.stringAbs(this.safeString(order, 6));
1051
+ let type = this.safeString(order, 8);
581
1052
  if (type.indexOf('LIMIT') > -1) {
582
1053
  type = 'limit';
583
1054
  }
584
1055
  else if (type.indexOf('MARKET') > -1) {
585
1056
  type = 'market';
586
1057
  }
587
- const status = this.parseWsOrderStatus(this.safeString(order, 5));
588
- const price = this.safeString(order, 6);
589
- const rawDatetime = this.safeString(order, 8);
590
- const timestamp = this.parse8601(rawDatetime);
591
- const parsed = this.safeOrder({
1058
+ const rawState = this.safeString(order, 13);
1059
+ const stateParts = rawState.split(' ');
1060
+ const trimmedStatus = this.safeString(stateParts, 0);
1061
+ const status = this.parseWsOrderStatus(trimmedStatus);
1062
+ const price = this.safeString(order, 16);
1063
+ const timestamp = this.safeInteger2(order, 5, 4);
1064
+ const average = this.safeString(order, 17);
1065
+ const stopPrice = this.omitZero(this.safeString(order, 18));
1066
+ return this.safeOrder({
592
1067
  'info': order,
593
1068
  'id': id,
594
- 'clientOrderId': undefined,
1069
+ 'clientOrderId': clientOrderId,
595
1070
  'timestamp': timestamp,
596
1071
  'datetime': this.iso8601(timestamp),
597
1072
  'lastTradeTimestamp': undefined,
@@ -599,9 +1074,9 @@ export default class bitfinex extends bitfinexRest {
599
1074
  'type': type,
600
1075
  'side': side,
601
1076
  'price': price,
602
- 'stopPrice': undefined,
603
- 'triggerPrice': undefined,
604
- 'average': undefined,
1077
+ 'stopPrice': stopPrice,
1078
+ 'triggerPrice': stopPrice,
1079
+ 'average': average,
605
1080
  'amount': amount,
606
1081
  'remaining': remaining,
607
1082
  'filled': undefined,
@@ -609,60 +1084,74 @@ export default class bitfinex extends bitfinexRest {
609
1084
  'fee': undefined,
610
1085
  'cost': undefined,
611
1086
  'trades': undefined,
612
- });
613
- if (this.orders === undefined) {
614
- const limit = this.safeInteger(this.options, 'ordersLimit', 1000);
615
- this.orders = new ArrayCacheBySymbolById(limit);
616
- }
617
- const orders = this.orders;
618
- orders.append(parsed);
619
- client.resolve(parsed, id);
620
- return parsed;
1087
+ }, market);
621
1088
  }
622
1089
  handleMessage(client, message) {
1090
+ const channelId = this.safeString(message, 0);
1091
+ //
1092
+ // [
1093
+ // 1231,
1094
+ // "hb",
1095
+ // ]
1096
+ //
1097
+ // auth message
1098
+ // {
1099
+ // "event": "auth",
1100
+ // "status": "OK",
1101
+ // "chanId": 0,
1102
+ // "userId": 3159883,
1103
+ // "auth_id": "ac7108e7-2f26-424d-9982-c24700dc02ca",
1104
+ // "caps": {
1105
+ // "orders": { read: 1, write: 1 },
1106
+ // "account": { read: 1, write: 1 },
1107
+ // "funding": { read: 1, write: 1 },
1108
+ // "history": { read: 1, write: 0 },
1109
+ // "wallets": { read: 1, write: 1 },
1110
+ // "withdraw": { read: 0, write: 1 },
1111
+ // "positions": { read: 1, write: 1 },
1112
+ // "ui_withdraw": { read: 0, write: 0 }
1113
+ // }
1114
+ // }
1115
+ //
623
1116
  if (Array.isArray(message)) {
624
- const channelId = this.safeString(message, 0);
625
- //
626
- // [
627
- // 1231,
628
- // "hb",
629
- // ]
630
- //
631
1117
  if (message[1] === 'hb') {
632
1118
  return; // skip heartbeats within subscription channels for now
633
1119
  }
634
1120
  const subscription = this.safeValue(client.subscriptions, channelId, {});
635
1121
  const channel = this.safeString(subscription, 'channel');
636
1122
  const name = this.safeString(message, 1);
637
- const methods = {
1123
+ const publicMethods = {
638
1124
  'book': this.handleOrderBook,
639
- // 'ohlc': this.handleOHLCV,
1125
+ 'cs': this.handleChecksum,
1126
+ 'candles': this.handleOHLCV,
640
1127
  'ticker': this.handleTicker,
641
1128
  'trades': this.handleTrades,
1129
+ };
1130
+ const privateMethods = {
642
1131
  'os': this.handleOrders,
1132
+ 'ou': this.handleOrders,
643
1133
  'on': this.handleOrders,
644
1134
  'oc': this.handleOrders,
1135
+ 'wu': this.handleBalance,
1136
+ 'ws': this.handleBalance,
1137
+ 'tu': this.handleMyTrade,
645
1138
  };
646
- const method = this.safeValue2(methods, channel, name);
1139
+ let method = undefined;
1140
+ if (channelId === '0') {
1141
+ method = this.safeValue(privateMethods, name);
1142
+ }
1143
+ else {
1144
+ method = this.safeValue2(publicMethods, name, channel);
1145
+ }
647
1146
  if (method !== undefined) {
648
1147
  method.call(this, client, message, subscription);
649
1148
  }
650
1149
  }
651
1150
  else {
652
- // todo add bitfinex handleErrorMessage
653
- //
654
- // {
655
- // "event": "info",
656
- // "version": 2,
657
- // "serverId": "e293377e-7bb7-427e-b28c-5db045b2c1d1",
658
- // "platform": { status: 1 }, // 1 for operative, 0 for maintenance
659
- // }
660
- //
661
1151
  const event = this.safeString(message, 'event');
662
1152
  if (event !== undefined) {
663
1153
  const methods = {
664
1154
  'info': this.handleSystemStatus,
665
- // 'book': 'handleOrderBook',
666
1155
  'subscribed': this.handleSubscriptionStatus,
667
1156
  'auth': this.handleAuthenticationMessage,
668
1157
  };