ccxt 4.1.84 → 4.1.85
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.
- package/README.md +5 -4
- package/dist/ccxt.browser.js +1136 -339
- package/dist/ccxt.browser.min.js +2 -2
- package/dist/cjs/ccxt.js +1 -1
- package/dist/cjs/src/base/Exchange.js +3 -0
- package/dist/cjs/src/bingx.js +10 -8
- package/dist/cjs/src/bithumb.js +2 -2
- package/dist/cjs/src/bitmart.js +90 -73
- package/dist/cjs/src/bitopro.js +2 -2
- package/dist/cjs/src/coinex.js +1 -0
- package/dist/cjs/src/kucoinfutures.js +0 -3
- package/dist/cjs/src/pro/bitmart.js +1026 -250
- package/js/ccxt.d.ts +1 -1
- package/js/ccxt.js +1 -1
- package/js/src/base/Exchange.d.ts +1 -0
- package/js/src/base/Exchange.js +3 -0
- package/js/src/bingx.js +10 -8
- package/js/src/bithumb.js +2 -2
- package/js/src/bitmart.js +90 -73
- package/js/src/bitopro.js +2 -2
- package/js/src/coinex.js +1 -0
- package/js/src/kucoinfutures.d.ts +0 -1
- package/js/src/kucoinfutures.js +0 -3
- package/js/src/pro/bitmart.d.ts +17 -6
- package/js/src/pro/bitmart.js +1028 -252
- package/package.json +1 -1
package/js/src/pro/bitmart.js
CHANGED
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
// ---------------------------------------------------------------------------
|
|
8
8
|
import bitmartRest from '../bitmart.js';
|
|
9
|
-
import { ArgumentsRequired, AuthenticationError } from '../base/errors.js';
|
|
10
|
-
import { ArrayCache, ArrayCacheByTimestamp, ArrayCacheBySymbolById } from '../base/ws/Cache.js';
|
|
9
|
+
import { ArgumentsRequired, AuthenticationError, ExchangeError, NotSupported } from '../base/errors.js';
|
|
10
|
+
import { ArrayCache, ArrayCacheByTimestamp, ArrayCacheBySymbolById, ArrayCacheBySymbolBySide } from '../base/ws/Cache.js';
|
|
11
11
|
import { sha256 } from '../static_dependencies/noble-hashes/sha256.js';
|
|
12
|
+
import { Asks, Bids } from '../base/ws/OrderBookSide.js';
|
|
12
13
|
// ---------------------------------------------------------------------------
|
|
13
14
|
export default class bitmart extends bitmartRest {
|
|
14
15
|
describe() {
|
|
@@ -22,24 +23,38 @@ export default class bitmart extends bitmartRest {
|
|
|
22
23
|
'cancelOrdersWs': false,
|
|
23
24
|
'cancelAllOrdersWs': false,
|
|
24
25
|
'ws': true,
|
|
26
|
+
'watchBalance': true,
|
|
25
27
|
'watchTicker': true,
|
|
28
|
+
'watchTickers': true,
|
|
26
29
|
'watchOrderBook': true,
|
|
27
30
|
'watchOrders': true,
|
|
28
31
|
'watchTrades': true,
|
|
29
32
|
'watchOHLCV': true,
|
|
33
|
+
'watchPosition': 'emulated',
|
|
34
|
+
'watchPositions': true,
|
|
30
35
|
},
|
|
31
36
|
'urls': {
|
|
32
37
|
'api': {
|
|
33
38
|
'ws': {
|
|
34
|
-
'
|
|
35
|
-
|
|
39
|
+
'spot': {
|
|
40
|
+
'public': 'wss://ws-manager-compress.{hostname}/api?protocol=1.1',
|
|
41
|
+
'private': 'wss://ws-manager-compress.{hostname}/user?protocol=1.1',
|
|
42
|
+
},
|
|
43
|
+
'swap': {
|
|
44
|
+
'public': 'wss://openapi-ws.{hostname}/api?protocol=1.1',
|
|
45
|
+
'private': 'wss://openapi-ws.{hostname}/user?protocol=1.1',
|
|
46
|
+
},
|
|
36
47
|
},
|
|
37
48
|
},
|
|
38
49
|
},
|
|
39
50
|
'options': {
|
|
40
51
|
'defaultType': 'spot',
|
|
52
|
+
'watchBalance': {
|
|
53
|
+
'fetchBalanceSnapshot': true,
|
|
54
|
+
'awaitBalanceSnapshot': true, // whether to wait for the balance snapshot before providing updates
|
|
55
|
+
},
|
|
41
56
|
'watchOrderBook': {
|
|
42
|
-
'depth': '
|
|
57
|
+
'depth': 'depth50', // depth5, depth20, depth50
|
|
43
58
|
},
|
|
44
59
|
'ws': {
|
|
45
60
|
'inflate': true,
|
|
@@ -65,33 +80,166 @@ export default class bitmart extends bitmartRest {
|
|
|
65
80
|
},
|
|
66
81
|
});
|
|
67
82
|
}
|
|
68
|
-
async subscribe(channel, symbol, params = {}) {
|
|
69
|
-
await this.loadMarkets();
|
|
83
|
+
async subscribe(channel, symbol, type, params = {}) {
|
|
70
84
|
const market = this.market(symbol);
|
|
71
|
-
const url = this.implodeHostname(this.urls['api']['ws']['public']);
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
'
|
|
76
|
-
|
|
85
|
+
const url = this.implodeHostname(this.urls['api']['ws'][type]['public']);
|
|
86
|
+
let request = {};
|
|
87
|
+
let messageHash = undefined;
|
|
88
|
+
if (type === 'spot') {
|
|
89
|
+
messageHash = 'spot/' + channel + ':' + market['id'];
|
|
90
|
+
request = {
|
|
91
|
+
'op': 'subscribe',
|
|
92
|
+
'args': [messageHash],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
messageHash = 'futures/' + channel + ':' + market['id'];
|
|
97
|
+
request = {
|
|
98
|
+
'action': 'subscribe',
|
|
99
|
+
'args': [messageHash],
|
|
100
|
+
};
|
|
101
|
+
}
|
|
77
102
|
return await this.watch(url, messageHash, this.deepExtend(request, params), messageHash);
|
|
78
103
|
}
|
|
79
|
-
async
|
|
104
|
+
async watchBalance(params = {}) {
|
|
105
|
+
/**
|
|
106
|
+
* @method
|
|
107
|
+
* @name bitmart#watchBalance
|
|
108
|
+
* @see https://developer-pro.bitmart.com/en/spot/#private-balance-change
|
|
109
|
+
* @see https://developer-pro.bitmart.com/en/futures/#private-assets-channel
|
|
110
|
+
* @description watch balance and get the amount of funds available for trading or funds locked in orders
|
|
111
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
112
|
+
* @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
|
|
113
|
+
*/
|
|
80
114
|
await this.loadMarkets();
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
115
|
+
let type = 'spot';
|
|
116
|
+
[type, params] = this.handleMarketTypeAndParams('watchBalance', undefined, params);
|
|
117
|
+
await this.authenticate(type, params);
|
|
118
|
+
let request = {};
|
|
119
|
+
if (type === 'spot') {
|
|
120
|
+
request = {
|
|
121
|
+
'op': 'subscribe',
|
|
122
|
+
'args': ['spot/user/balance:BALANCE_UPDATE'],
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
request = {
|
|
127
|
+
'action': 'subscribe',
|
|
128
|
+
'args': ['futures/asset:USDT', 'futures/asset:BTC', 'futures/asset:ETH'],
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const messageHash = 'balance:' + type;
|
|
132
|
+
const url = this.implodeHostname(this.urls['api']['ws'][type]['private']);
|
|
133
|
+
const client = this.client(url);
|
|
134
|
+
this.setBalanceCache(client, type);
|
|
135
|
+
const fetchBalanceSnapshot = this.handleOptionAndParams(this.options, 'watchBalance', 'fetchBalanceSnapshot', true);
|
|
136
|
+
const awaitBalanceSnapshot = this.handleOptionAndParams(this.options, 'watchBalance', 'awaitBalanceSnapshot', false);
|
|
137
|
+
if (fetchBalanceSnapshot && awaitBalanceSnapshot) {
|
|
138
|
+
await client.future(type + ':fetchBalanceSnapshot');
|
|
139
|
+
}
|
|
89
140
|
return await this.watch(url, messageHash, this.deepExtend(request, params), messageHash);
|
|
90
141
|
}
|
|
142
|
+
setBalanceCache(client, type) {
|
|
143
|
+
if (type in client.subscriptions) {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
const options = this.safeValue(this.options, 'watchBalance');
|
|
147
|
+
const fetchBalanceSnapshot = this.handleOptionAndParams(options, 'watchBalance', 'fetchBalanceSnapshot', true);
|
|
148
|
+
if (fetchBalanceSnapshot) {
|
|
149
|
+
const messageHash = type + ':fetchBalanceSnapshot';
|
|
150
|
+
if (!(messageHash in client.futures)) {
|
|
151
|
+
client.future(messageHash);
|
|
152
|
+
this.spawn(this.loadBalanceSnapshot, client, messageHash, type);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
this.balance[type] = {};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
async loadBalanceSnapshot(client, messageHash, type) {
|
|
160
|
+
const response = await this.fetchBalance({ 'type': type });
|
|
161
|
+
this.balance[type] = this.extend(response, this.safeValue(this.balance, type, {}));
|
|
162
|
+
// don't remove the future from the .futures cache
|
|
163
|
+
const future = client.futures[messageHash];
|
|
164
|
+
future.resolve();
|
|
165
|
+
client.resolve(this.balance[type], 'balance:' + type);
|
|
166
|
+
}
|
|
167
|
+
handleBalance(client, message) {
|
|
168
|
+
//
|
|
169
|
+
// spot
|
|
170
|
+
// {
|
|
171
|
+
// "data":[
|
|
172
|
+
// {
|
|
173
|
+
// "balance_details":[
|
|
174
|
+
// {
|
|
175
|
+
// "av_bal":"0.206000000000000000000000000000",
|
|
176
|
+
// "ccy":"LTC",
|
|
177
|
+
// "fz_bal":"0.100000000000000000000000000000"
|
|
178
|
+
// }
|
|
179
|
+
// ],
|
|
180
|
+
// "event_time":"1701632345415",
|
|
181
|
+
// "event_type":"TRANSACTION_COMPLETED"
|
|
182
|
+
// }
|
|
183
|
+
// ],
|
|
184
|
+
// "table":"spot/user/balance"
|
|
185
|
+
// }
|
|
186
|
+
// swap
|
|
187
|
+
// {
|
|
188
|
+
// group: 'futures/asset:USDT',
|
|
189
|
+
// data: {
|
|
190
|
+
// currency: 'USDT',
|
|
191
|
+
// available_balance: '37.19688649135',
|
|
192
|
+
// position_deposit: '0.788687546',
|
|
193
|
+
// frozen_balance: '0'
|
|
194
|
+
// }
|
|
195
|
+
// }
|
|
196
|
+
//
|
|
197
|
+
const channel = this.safeString2(message, 'table', 'group');
|
|
198
|
+
const data = this.safeValue(message, 'data');
|
|
199
|
+
if (data === undefined) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const isSpot = (channel.indexOf('spot') >= 0);
|
|
203
|
+
const type = isSpot ? 'spot' : 'swap';
|
|
204
|
+
this.balance['info'] = message;
|
|
205
|
+
if (isSpot) {
|
|
206
|
+
if (!Array.isArray(data)) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
for (let i = 0; i < data.length; i++) {
|
|
210
|
+
const timestamp = this.safeInteger(message, 'event_time');
|
|
211
|
+
this.balance['timestamp'] = timestamp;
|
|
212
|
+
this.balance['datetime'] = this.iso8601(timestamp);
|
|
213
|
+
const balanceDetails = this.safeValue(data[i], 'balance_details', []);
|
|
214
|
+
for (let ii = 0; ii < balanceDetails.length; ii++) {
|
|
215
|
+
const rawBalance = balanceDetails[i];
|
|
216
|
+
const account = this.account();
|
|
217
|
+
const currencyId = this.safeString(rawBalance, 'ccy');
|
|
218
|
+
const code = this.safeCurrencyCode(currencyId);
|
|
219
|
+
account['free'] = this.safeString(rawBalance, 'av_bal');
|
|
220
|
+
account['total'] = this.safeString(rawBalance, 'fz_bal');
|
|
221
|
+
this.balance[code] = account;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
const currencyId = this.safeString(data, 'currency');
|
|
227
|
+
const code = this.safeCurrencyCode(currencyId);
|
|
228
|
+
const account = this.account();
|
|
229
|
+
account['free'] = this.safeString(data, 'available_balance');
|
|
230
|
+
account['used'] = this.safeString(data, 'frozen_balance');
|
|
231
|
+
this.balance[type][code] = account;
|
|
232
|
+
}
|
|
233
|
+
this.balance[type] = this.safeBalance(this.balance[type]);
|
|
234
|
+
const messageHash = 'balance:' + type;
|
|
235
|
+
client.resolve(this.balance[type], messageHash);
|
|
236
|
+
}
|
|
91
237
|
async watchTrades(symbol, since = undefined, limit = undefined, params = {}) {
|
|
92
238
|
/**
|
|
93
239
|
* @method
|
|
94
240
|
* @name bitmart#watchTrades
|
|
241
|
+
* @see https://developer-pro.bitmart.com/en/spot/#public-trade-channel
|
|
242
|
+
* @see https://developer-pro.bitmart.com/en/futures/#public-trade-channel
|
|
95
243
|
* @description get the list of most recent trades for a particular symbol
|
|
96
244
|
* @param {string} symbol unified symbol of the market to fetch trades for
|
|
97
245
|
* @param {int} [since] timestamp in ms of the earliest trade to fetch
|
|
@@ -101,7 +249,10 @@ export default class bitmart extends bitmartRest {
|
|
|
101
249
|
*/
|
|
102
250
|
await this.loadMarkets();
|
|
103
251
|
symbol = this.symbol(symbol);
|
|
104
|
-
const
|
|
252
|
+
const market = this.market(symbol);
|
|
253
|
+
let type = 'spot';
|
|
254
|
+
[type, params] = this.handleMarketTypeAndParams('watchTrades', market, params);
|
|
255
|
+
const trades = await this.subscribe('trade', symbol, type, params);
|
|
105
256
|
if (this.newUpdates) {
|
|
106
257
|
limit = trades.getLimit(symbol, limit);
|
|
107
258
|
}
|
|
@@ -111,17 +262,64 @@ export default class bitmart extends bitmartRest {
|
|
|
111
262
|
/**
|
|
112
263
|
* @method
|
|
113
264
|
* @name bitmart#watchTicker
|
|
265
|
+
* @see https://developer-pro.bitmart.com/en/spot/#public-ticker-channel
|
|
114
266
|
* @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
|
|
115
267
|
* @param {string} symbol unified symbol of the market to fetch the ticker for
|
|
116
268
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
117
269
|
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
|
|
118
270
|
*/
|
|
119
|
-
|
|
271
|
+
await this.loadMarkets();
|
|
272
|
+
symbol = this.symbol(symbol);
|
|
273
|
+
const market = this.market(symbol);
|
|
274
|
+
let type = 'spot';
|
|
275
|
+
[type, params] = this.handleMarketTypeAndParams('watchTicker', market, params);
|
|
276
|
+
if (type === 'swap') {
|
|
277
|
+
throw new NotSupported(this.id + ' watchTicker() does not support ' + type + ' markets. Use watchTickers() instead');
|
|
278
|
+
}
|
|
279
|
+
return await this.subscribe('ticker', symbol, type, params);
|
|
280
|
+
}
|
|
281
|
+
async watchTickers(symbols = undefined, params = {}) {
|
|
282
|
+
/**
|
|
283
|
+
* @method
|
|
284
|
+
* @name bitmart#watchTickers
|
|
285
|
+
* @see https://developer-pro.bitmart.com/en/futures/#overview
|
|
286
|
+
* @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list
|
|
287
|
+
* @param {string[]} symbols unified symbol of the market to fetch the ticker for
|
|
288
|
+
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
289
|
+
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
|
|
290
|
+
*/
|
|
291
|
+
await this.loadMarkets();
|
|
292
|
+
const market = this.getMarketFromSymbols(symbols);
|
|
293
|
+
let type = 'spot';
|
|
294
|
+
[type, params] = this.handleMarketTypeAndParams('watchTickers', market, params);
|
|
295
|
+
symbols = this.marketSymbols(symbols);
|
|
296
|
+
if (type === 'spot') {
|
|
297
|
+
throw new NotSupported(this.id + ' watchTickers() does not support ' + type + ' markets. Use watchTicker() instead');
|
|
298
|
+
}
|
|
299
|
+
const url = this.implodeHostname(this.urls['api']['ws'][type]['public']);
|
|
300
|
+
if (type === 'swap') {
|
|
301
|
+
type = 'futures';
|
|
302
|
+
}
|
|
303
|
+
let messageHash = 'tickers';
|
|
304
|
+
if (symbols !== undefined) {
|
|
305
|
+
messageHash += '::' + symbols.join(',');
|
|
306
|
+
}
|
|
307
|
+
const request = {
|
|
308
|
+
'action': 'subscribe',
|
|
309
|
+
'args': ['futures/ticker'],
|
|
310
|
+
};
|
|
311
|
+
const newTickers = await this.watch(url, messageHash, this.deepExtend(request, params), messageHash);
|
|
312
|
+
if (this.newUpdates) {
|
|
313
|
+
return newTickers;
|
|
314
|
+
}
|
|
315
|
+
return this.filterByArray(this.tickers, 'symbol', symbols);
|
|
120
316
|
}
|
|
121
317
|
async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) {
|
|
122
318
|
/**
|
|
123
319
|
* @method
|
|
124
320
|
* @name bitmart#watchOrders
|
|
321
|
+
* @see https://developer-pro.bitmart.com/en/spot/#private-order-channel
|
|
322
|
+
* @see https://developer-pro.bitmart.com/en/futures/#private-order-channel
|
|
125
323
|
* @description watches information on multiple orders made by the user
|
|
126
324
|
* @param {string} symbol unified market symbol of the market orders were made in
|
|
127
325
|
* @param {int} [since] the earliest time in ms to fetch orders for
|
|
@@ -129,196 +327,626 @@ export default class bitmart extends bitmartRest {
|
|
|
129
327
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
130
328
|
* @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
131
329
|
*/
|
|
132
|
-
if (symbol === undefined) {
|
|
133
|
-
throw new ArgumentsRequired(this.id + ' watchOrders() requires a symbol argument');
|
|
134
|
-
}
|
|
135
330
|
await this.loadMarkets();
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (
|
|
139
|
-
|
|
331
|
+
let market = undefined;
|
|
332
|
+
let messageHash = 'orders';
|
|
333
|
+
if (symbol !== undefined) {
|
|
334
|
+
symbol = this.symbol(symbol);
|
|
335
|
+
market = this.market(symbol);
|
|
336
|
+
messageHash = 'orders::' + symbol;
|
|
337
|
+
}
|
|
338
|
+
let type = 'spot';
|
|
339
|
+
[type, params] = this.handleMarketTypeAndParams('watchOrders', market, params);
|
|
340
|
+
await this.authenticate(type, params);
|
|
341
|
+
let request = undefined;
|
|
342
|
+
if (type === 'spot') {
|
|
343
|
+
if (symbol === undefined) {
|
|
344
|
+
throw new ArgumentsRequired(this.id + ' watchOrders() requires a symbol argument for spot markets');
|
|
345
|
+
}
|
|
346
|
+
request = {
|
|
347
|
+
'op': 'subscribe',
|
|
348
|
+
'args': ['spot/user/order:' + market['id']],
|
|
349
|
+
};
|
|
140
350
|
}
|
|
141
|
-
|
|
142
|
-
|
|
351
|
+
else {
|
|
352
|
+
request = {
|
|
353
|
+
'action': 'subscribe',
|
|
354
|
+
'args': ['futures/order'],
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
const url = this.implodeHostname(this.urls['api']['ws'][type]['private']);
|
|
358
|
+
const newOrders = await this.watch(url, messageHash, this.deepExtend(request, params), messageHash);
|
|
143
359
|
if (this.newUpdates) {
|
|
144
|
-
|
|
360
|
+
return newOrders;
|
|
145
361
|
}
|
|
146
|
-
return this.filterBySymbolSinceLimit(orders, symbol, since, limit, true);
|
|
362
|
+
return this.filterBySymbolSinceLimit(this.orders, symbol, since, limit, true);
|
|
147
363
|
}
|
|
148
364
|
handleOrders(client, message) {
|
|
149
365
|
//
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
//
|
|
366
|
+
// spot
|
|
367
|
+
// {
|
|
368
|
+
// "data":[
|
|
369
|
+
// {
|
|
370
|
+
// "symbol": "LTC_USDT",
|
|
371
|
+
// "notional": '',
|
|
372
|
+
// "side": "buy",
|
|
373
|
+
// "last_fill_time": "0",
|
|
374
|
+
// "ms_t": "1646216634000",
|
|
375
|
+
// "type": "limit",
|
|
376
|
+
// "filled_notional": "0.000000000000000000000000000000",
|
|
377
|
+
// "last_fill_price": "0",
|
|
378
|
+
// "size": "0.500000000000000000000000000000",
|
|
379
|
+
// "price": "50.000000000000000000000000000000",
|
|
380
|
+
// "last_fill_count": "0",
|
|
381
|
+
// "filled_size": "0.000000000000000000000000000000",
|
|
382
|
+
// "margin_trading": "0",
|
|
383
|
+
// "state": "8",
|
|
384
|
+
// "order_id": "24807076628",
|
|
385
|
+
// "order_type": "0"
|
|
386
|
+
// }
|
|
387
|
+
// ],
|
|
388
|
+
// "table":"spot/user/order"
|
|
389
|
+
// }
|
|
390
|
+
// swap
|
|
391
|
+
// {
|
|
392
|
+
// "group":"futures/order",
|
|
393
|
+
// "data":[
|
|
394
|
+
// {
|
|
395
|
+
// "action":2,
|
|
396
|
+
// "order":{
|
|
397
|
+
// "order_id":"2312045036986775",
|
|
398
|
+
// "client_order_id":"",
|
|
399
|
+
// "price":"71.61707928",
|
|
400
|
+
// "size":"1",
|
|
401
|
+
// "symbol":"LTCUSDT",
|
|
402
|
+
// "state":1,
|
|
403
|
+
// "side":4,
|
|
404
|
+
// "type":"market",
|
|
405
|
+
// "leverage":"1",
|
|
406
|
+
// "open_type":"cross",
|
|
407
|
+
// "deal_avg_price":"0",
|
|
408
|
+
// "deal_size":"0",
|
|
409
|
+
// "create_time":1701625324646,
|
|
410
|
+
// "update_time":1701625324640,
|
|
411
|
+
// "plan_order_id":"",
|
|
412
|
+
// "last_trade":null
|
|
413
|
+
// }
|
|
169
414
|
// }
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
// }
|
|
415
|
+
// ]
|
|
416
|
+
// }
|
|
173
417
|
//
|
|
174
|
-
const
|
|
175
|
-
|
|
418
|
+
const orders = this.safeValue(message, 'data');
|
|
419
|
+
if (orders === undefined) {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
176
422
|
const ordersLength = orders.length;
|
|
423
|
+
const newOrders = [];
|
|
424
|
+
const symbols = {};
|
|
177
425
|
if (ordersLength > 0) {
|
|
178
426
|
const limit = this.safeInteger(this.options, 'ordersLimit', 1000);
|
|
179
427
|
if (this.orders === undefined) {
|
|
180
428
|
this.orders = new ArrayCacheBySymbolById(limit);
|
|
181
429
|
}
|
|
182
430
|
const stored = this.orders;
|
|
183
|
-
const marketIds = [];
|
|
184
431
|
for (let i = 0; i < orders.length; i++) {
|
|
185
432
|
const order = this.parseWsOrder(orders[i]);
|
|
186
433
|
stored.append(order);
|
|
434
|
+
newOrders.push(order);
|
|
187
435
|
const symbol = order['symbol'];
|
|
188
|
-
|
|
189
|
-
marketIds.push(market['id']);
|
|
190
|
-
}
|
|
191
|
-
for (let i = 0; i < marketIds.length; i++) {
|
|
192
|
-
const messageHash = channel + ':' + marketIds[i];
|
|
193
|
-
client.resolve(this.orders, messageHash);
|
|
436
|
+
symbols[symbol] = true;
|
|
194
437
|
}
|
|
195
438
|
}
|
|
439
|
+
const newOrderSymbols = Object.keys(symbols);
|
|
440
|
+
for (let i = 0; i < newOrderSymbols.length; i++) {
|
|
441
|
+
const symbol = newOrderSymbols[i];
|
|
442
|
+
this.resolvePromiseIfMessagehashMatches(client, 'orders::', symbol, newOrders);
|
|
443
|
+
}
|
|
444
|
+
client.resolve(newOrders, 'orders');
|
|
196
445
|
}
|
|
197
446
|
parseWsOrder(order, market = undefined) {
|
|
198
447
|
//
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
//
|
|
203
|
-
//
|
|
204
|
-
//
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
//
|
|
208
|
-
//
|
|
209
|
-
//
|
|
210
|
-
//
|
|
211
|
-
//
|
|
212
|
-
//
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
448
|
+
// spot
|
|
449
|
+
// {
|
|
450
|
+
// "symbol": "LTC_USDT",
|
|
451
|
+
// "notional": '',
|
|
452
|
+
// "side": "buy",
|
|
453
|
+
// "last_fill_time": "0",
|
|
454
|
+
// "ms_t": "1646216634000",
|
|
455
|
+
// "type": "limit",
|
|
456
|
+
// "filled_notional": "0.000000000000000000000000000000",
|
|
457
|
+
// "last_fill_price": "0",
|
|
458
|
+
// "size": "0.500000000000000000000000000000",
|
|
459
|
+
// "price": "50.000000000000000000000000000000",
|
|
460
|
+
// "last_fill_count": "0",
|
|
461
|
+
// "filled_size": "0.000000000000000000000000000000",
|
|
462
|
+
// "margin_trading": "0",
|
|
463
|
+
// "state": "8",
|
|
464
|
+
// "order_id": "24807076628",
|
|
465
|
+
// "order_type": "0"
|
|
466
|
+
// }
|
|
467
|
+
// swap
|
|
468
|
+
// {
|
|
469
|
+
// "action":2,
|
|
470
|
+
// "order":{
|
|
471
|
+
// "order_id":"2312045036986775",
|
|
472
|
+
// "client_order_id":"",
|
|
473
|
+
// "price":"71.61707928",
|
|
474
|
+
// "size":"1",
|
|
475
|
+
// "symbol":"LTCUSDT",
|
|
476
|
+
// "state":1,
|
|
477
|
+
// "side":4,
|
|
478
|
+
// "type":"market",
|
|
479
|
+
// "leverage":"1",
|
|
480
|
+
// "open_type":"cross",
|
|
481
|
+
// "deal_avg_price":"0",
|
|
482
|
+
// "deal_size":"0",
|
|
483
|
+
// "create_time":1701625324646,
|
|
484
|
+
// "update_time":1701625324640,
|
|
485
|
+
// "plan_order_id":"",
|
|
486
|
+
// "last_trade":null
|
|
487
|
+
// }
|
|
488
|
+
// }
|
|
489
|
+
//
|
|
490
|
+
const action = this.safeNumber(order, 'action');
|
|
491
|
+
const isSpot = (action === undefined);
|
|
492
|
+
if (isSpot) {
|
|
493
|
+
const marketId = this.safeString(order, 'symbol');
|
|
494
|
+
market = this.safeMarket(marketId, market, '_', 'spot');
|
|
495
|
+
const id = this.safeString(order, 'order_id');
|
|
496
|
+
const clientOrderId = this.safeString(order, 'clientOid');
|
|
497
|
+
const price = this.safeString(order, 'price');
|
|
498
|
+
const filled = this.safeString(order, 'filled_size');
|
|
499
|
+
const amount = this.safeString(order, 'size');
|
|
500
|
+
const type = this.safeString(order, 'type');
|
|
501
|
+
const rawState = this.safeString(order, 'state');
|
|
502
|
+
const status = this.parseOrderStatusByType(market['type'], rawState);
|
|
503
|
+
const timestamp = this.safeInteger(order, 'ms_t');
|
|
504
|
+
const symbol = market['symbol'];
|
|
505
|
+
const side = this.safeStringLower(order, 'side');
|
|
506
|
+
return this.safeOrder({
|
|
507
|
+
'info': order,
|
|
508
|
+
'symbol': symbol,
|
|
509
|
+
'id': id,
|
|
510
|
+
'clientOrderId': clientOrderId,
|
|
511
|
+
'timestamp': undefined,
|
|
512
|
+
'datetime': undefined,
|
|
513
|
+
'lastTradeTimestamp': timestamp,
|
|
514
|
+
'type': type,
|
|
515
|
+
'timeInForce': undefined,
|
|
516
|
+
'postOnly': undefined,
|
|
517
|
+
'side': side,
|
|
518
|
+
'price': price,
|
|
519
|
+
'stopPrice': undefined,
|
|
520
|
+
'triggerPrice': undefined,
|
|
521
|
+
'amount': amount,
|
|
522
|
+
'cost': undefined,
|
|
523
|
+
'average': undefined,
|
|
524
|
+
'filled': filled,
|
|
525
|
+
'remaining': undefined,
|
|
526
|
+
'status': status,
|
|
527
|
+
'fee': undefined,
|
|
528
|
+
'trades': undefined,
|
|
529
|
+
}, market);
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
const orderInfo = this.safeValue(order, 'order');
|
|
533
|
+
const marketId = this.safeString(orderInfo, 'symbol');
|
|
534
|
+
const symbol = this.safeSymbol(marketId, market, '', 'swap');
|
|
535
|
+
const orderId = this.safeString(orderInfo, 'order_id');
|
|
536
|
+
const timestamp = this.safeInteger(orderInfo, 'create_time');
|
|
537
|
+
const updatedTimestamp = this.safeInteger(orderInfo, 'update_time');
|
|
538
|
+
const lastTrade = this.safeValue(orderInfo, 'last_trade');
|
|
539
|
+
const cachedOrders = this.orders;
|
|
540
|
+
const orders = this.safeValue(cachedOrders.hashmap, symbol, {});
|
|
541
|
+
const cachedOrder = this.safeValue(orders, orderId);
|
|
542
|
+
let trades = undefined;
|
|
543
|
+
if (cachedOrder !== undefined) {
|
|
544
|
+
trades = this.safeValue(order, 'trades');
|
|
545
|
+
}
|
|
546
|
+
if (lastTrade !== undefined) {
|
|
547
|
+
if (trades === undefined) {
|
|
548
|
+
trades = [];
|
|
549
|
+
}
|
|
550
|
+
trades.push(lastTrade);
|
|
551
|
+
}
|
|
552
|
+
return this.safeOrder({
|
|
553
|
+
'info': order,
|
|
554
|
+
'symbol': symbol,
|
|
555
|
+
'id': orderId,
|
|
556
|
+
'clientOrderId': this.safeString(orderInfo, 'client_order_id'),
|
|
557
|
+
'timestamp': timestamp,
|
|
558
|
+
'datetime': this.iso8601(timestamp),
|
|
559
|
+
'lastTradeTimestamp': updatedTimestamp,
|
|
560
|
+
'type': this.safeString(orderInfo, 'type'),
|
|
561
|
+
'timeInForce': undefined,
|
|
562
|
+
'postOnly': undefined,
|
|
563
|
+
'side': this.parseWsOrderSide(this.safeString(orderInfo, 'side')),
|
|
564
|
+
'price': this.safeString(orderInfo, 'price'),
|
|
565
|
+
'stopPrice': undefined,
|
|
566
|
+
'triggerPrice': undefined,
|
|
567
|
+
'amount': this.safeString(orderInfo, 'size'),
|
|
568
|
+
'cost': undefined,
|
|
569
|
+
'average': this.safeString(orderInfo, 'deal_avg_price'),
|
|
570
|
+
'filled': this.safeString(orderInfo, 'deal_size'),
|
|
571
|
+
'remaining': undefined,
|
|
572
|
+
'status': this.parseWsOrderStatus(this.safeString(order, 'action')),
|
|
573
|
+
'fee': undefined,
|
|
574
|
+
'trades': trades,
|
|
575
|
+
}, market);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
parseWsOrderStatus(statusId) {
|
|
579
|
+
const statuses = {
|
|
580
|
+
'1': 'closed',
|
|
581
|
+
'2': 'open',
|
|
582
|
+
'3': 'canceled',
|
|
583
|
+
'4': 'closed',
|
|
584
|
+
'5': 'canceled',
|
|
585
|
+
'6': 'open',
|
|
586
|
+
'7': 'open',
|
|
587
|
+
'8': 'closed',
|
|
588
|
+
'9': 'closed', // active adl match deal
|
|
589
|
+
};
|
|
590
|
+
return this.safeString(statuses, statusId, statusId);
|
|
591
|
+
}
|
|
592
|
+
parseWsOrderSide(sideId) {
|
|
593
|
+
const sides = {
|
|
594
|
+
'1': 'buy',
|
|
595
|
+
'2': 'buy',
|
|
596
|
+
'3': 'sell',
|
|
597
|
+
'4': 'sell', // sell_open_short
|
|
598
|
+
};
|
|
599
|
+
return this.safeString(sides, sideId, sideId);
|
|
600
|
+
}
|
|
601
|
+
async watchPositions(symbols = undefined, since = undefined, limit = undefined, params = {}) {
|
|
602
|
+
/**
|
|
603
|
+
* @method
|
|
604
|
+
* @name bitmart#watchPositions
|
|
605
|
+
* @see https://developer-pro.bitmart.com/en/futures/#private-position-channel
|
|
606
|
+
* @description watch all open positions
|
|
607
|
+
* @param {string[]|undefined} symbols list of unified market symbols
|
|
608
|
+
* @param {object} params extra parameters specific to the exchange API endpoint
|
|
609
|
+
* @returns {object[]} a list of [position structure]{@link https://docs.ccxt.com/en/latest/manual.html#position-structure}
|
|
610
|
+
*/
|
|
611
|
+
await this.loadMarkets();
|
|
612
|
+
const type = 'swap';
|
|
613
|
+
await this.authenticate(type, params);
|
|
614
|
+
symbols = this.marketSymbols(symbols, 'swap', true, true, false);
|
|
615
|
+
let messageHash = 'positions';
|
|
616
|
+
if (symbols !== undefined) {
|
|
617
|
+
messageHash += '::' + symbols.join(',');
|
|
618
|
+
}
|
|
619
|
+
const subscriptionHash = 'futures/position';
|
|
620
|
+
const request = {
|
|
621
|
+
'action': 'subscribe',
|
|
622
|
+
'args': ['futures/position'],
|
|
623
|
+
};
|
|
624
|
+
const url = this.implodeHostname(this.urls['api']['ws'][type]['private']);
|
|
625
|
+
const newPositions = await this.watch(url, messageHash, this.deepExtend(request, params), subscriptionHash);
|
|
626
|
+
if (this.newUpdates) {
|
|
627
|
+
return newPositions;
|
|
628
|
+
}
|
|
629
|
+
return this.filterBySymbolsSinceLimit(this.positions, symbols, since, limit);
|
|
630
|
+
}
|
|
631
|
+
handlePositions(client, message) {
|
|
632
|
+
//
|
|
633
|
+
// {
|
|
634
|
+
// "group":"futures/position",
|
|
635
|
+
// "data":[
|
|
636
|
+
// {
|
|
637
|
+
// "symbol":"LTCUSDT",
|
|
638
|
+
// "hold_volume":"5",
|
|
639
|
+
// "position_type":2,
|
|
640
|
+
// "open_type":2,
|
|
641
|
+
// "frozen_volume":"0",
|
|
642
|
+
// "close_volume":"0",
|
|
643
|
+
// "hold_avg_price":"71.582",
|
|
644
|
+
// "close_avg_price":"0",
|
|
645
|
+
// "open_avg_price":"71.582",
|
|
646
|
+
// "liquidate_price":"0",
|
|
647
|
+
// "create_time":1701623327513,
|
|
648
|
+
// "update_time":1701627620439
|
|
649
|
+
// },
|
|
650
|
+
// {
|
|
651
|
+
// "symbol":"LTCUSDT",
|
|
652
|
+
// "hold_volume":"6",
|
|
653
|
+
// "position_type":1,
|
|
654
|
+
// "open_type":2,
|
|
655
|
+
// "frozen_volume":"0",
|
|
656
|
+
// "close_volume":"0",
|
|
657
|
+
// "hold_avg_price":"71.681666666666666667",
|
|
658
|
+
// "close_avg_price":"0",
|
|
659
|
+
// "open_avg_price":"71.681666666666666667",
|
|
660
|
+
// "liquidate_price":"0",
|
|
661
|
+
// "create_time":1701621167225,
|
|
662
|
+
// "update_time":1701628152614
|
|
663
|
+
// }
|
|
664
|
+
// ]
|
|
665
|
+
// }
|
|
666
|
+
//
|
|
667
|
+
const data = this.safeValue(message, 'data', []);
|
|
668
|
+
const cache = this.positions;
|
|
669
|
+
if (this.positions === undefined) {
|
|
670
|
+
this.positions = new ArrayCacheBySymbolBySide();
|
|
671
|
+
}
|
|
672
|
+
const newPositions = [];
|
|
673
|
+
for (let i = 0; i < data.length; i++) {
|
|
674
|
+
const rawPosition = data[i];
|
|
675
|
+
const position = this.parseWsPosition(rawPosition);
|
|
676
|
+
newPositions.push(position);
|
|
677
|
+
cache.append(position);
|
|
678
|
+
}
|
|
679
|
+
const messageHashes = this.findMessageHashes(client, 'positions::');
|
|
680
|
+
for (let i = 0; i < messageHashes.length; i++) {
|
|
681
|
+
const messageHash = messageHashes[i];
|
|
682
|
+
const parts = messageHash.split('::');
|
|
683
|
+
const symbolsString = parts[1];
|
|
684
|
+
const symbols = symbolsString.split(',');
|
|
685
|
+
const positions = this.filterByArray(newPositions, 'symbol', symbols, false);
|
|
686
|
+
if (!this.isEmpty(positions)) {
|
|
687
|
+
client.resolve(positions, messageHash);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
client.resolve(newPositions, 'positions');
|
|
691
|
+
}
|
|
692
|
+
parseWsPosition(position, market = undefined) {
|
|
693
|
+
//
|
|
694
|
+
// {
|
|
695
|
+
// "symbol":"LTCUSDT",
|
|
696
|
+
// "hold_volume":"6",
|
|
697
|
+
// "position_type":1,
|
|
698
|
+
// "open_type":2,
|
|
699
|
+
// "frozen_volume":"0",
|
|
700
|
+
// "close_volume":"0",
|
|
701
|
+
// "hold_avg_price":"71.681666666666666667",
|
|
702
|
+
// "close_avg_price":"0",
|
|
703
|
+
// "open_avg_price":"71.681666666666666667",
|
|
704
|
+
// "liquidate_price":"0",
|
|
705
|
+
// "create_time":1701621167225,
|
|
706
|
+
// "update_time":1701628152614
|
|
707
|
+
// }
|
|
708
|
+
//
|
|
709
|
+
const marketId = this.safeString(position, 'symbol');
|
|
710
|
+
market = this.safeMarket(marketId, market, '', 'swap');
|
|
229
711
|
const symbol = market['symbol'];
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
712
|
+
const openTimestamp = this.safeInteger(position, 'create_time');
|
|
713
|
+
const timestamp = this.safeInteger(position, 'update_time');
|
|
714
|
+
const side = this.safeNumber(position, 'position_type');
|
|
715
|
+
const marginModeId = this.safeNumber(position, 'open_type');
|
|
716
|
+
return this.safePosition({
|
|
717
|
+
'info': position,
|
|
718
|
+
'id': undefined,
|
|
233
719
|
'symbol': symbol,
|
|
234
|
-
'
|
|
235
|
-
'
|
|
236
|
-
'
|
|
237
|
-
'
|
|
238
|
-
'
|
|
239
|
-
'
|
|
240
|
-
'
|
|
241
|
-
'
|
|
242
|
-
'
|
|
243
|
-
'
|
|
244
|
-
'
|
|
245
|
-
'
|
|
246
|
-
'
|
|
247
|
-
'
|
|
248
|
-
'
|
|
249
|
-
'
|
|
250
|
-
'
|
|
251
|
-
'
|
|
252
|
-
'
|
|
253
|
-
'
|
|
254
|
-
|
|
720
|
+
'timestamp': openTimestamp,
|
|
721
|
+
'datetime': this.iso8601(openTimestamp),
|
|
722
|
+
'lastUpdateTimestamp': timestamp,
|
|
723
|
+
'hedged': undefined,
|
|
724
|
+
'side': (side === 1) ? 'long' : 'short',
|
|
725
|
+
'contracts': this.safeNumber(position, 'hold_volume'),
|
|
726
|
+
'contractSize': this.safeNumber(market, 'contractSize'),
|
|
727
|
+
'entryPrice': this.safeNumber(position, 'open_avg_price'),
|
|
728
|
+
'markPrice': this.safeNumber(position, 'hold_avg_price'),
|
|
729
|
+
'lastPrice': undefined,
|
|
730
|
+
'notional': undefined,
|
|
731
|
+
'leverage': undefined,
|
|
732
|
+
'collateral': undefined,
|
|
733
|
+
'initialMargin': undefined,
|
|
734
|
+
'initialMarginPercentage': undefined,
|
|
735
|
+
'maintenanceMargin': undefined,
|
|
736
|
+
'maintenanceMarginPercentage': undefined,
|
|
737
|
+
'unrealizedPnl': undefined,
|
|
738
|
+
'realizedPnl': undefined,
|
|
739
|
+
'liquidationPrice': this.safeNumber(position, 'liquidate_price'),
|
|
740
|
+
'marginMode': (marginModeId === 1) ? 'isolated' : 'cross',
|
|
741
|
+
'percentage': undefined,
|
|
742
|
+
'marginRatio': undefined,
|
|
743
|
+
'stopLossPrice': undefined,
|
|
744
|
+
'takeProfitPrice': undefined,
|
|
745
|
+
});
|
|
255
746
|
}
|
|
256
747
|
handleTrade(client, message) {
|
|
257
748
|
//
|
|
258
|
-
//
|
|
259
|
-
//
|
|
260
|
-
//
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
//
|
|
265
|
-
//
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
//
|
|
269
|
-
//
|
|
749
|
+
// spot
|
|
750
|
+
// {
|
|
751
|
+
// "table": "spot/trade",
|
|
752
|
+
// "data": [
|
|
753
|
+
// {
|
|
754
|
+
// "price": "52700.50",
|
|
755
|
+
// "s_t": 1630982050,
|
|
756
|
+
// "side": "buy",
|
|
757
|
+
// "size": "0.00112",
|
|
758
|
+
// "symbol": "BTC_USDT"
|
|
759
|
+
// },
|
|
760
|
+
// ]
|
|
761
|
+
// }
|
|
270
762
|
//
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
763
|
+
// swap
|
|
764
|
+
// {
|
|
765
|
+
// "group":"futures/trade:BTCUSDT",
|
|
766
|
+
// "data":[
|
|
767
|
+
// {
|
|
768
|
+
// "trade_id":6798697637,
|
|
769
|
+
// "contract_id":1,
|
|
770
|
+
// "symbol":"BTCUSDT",
|
|
771
|
+
// "deal_price":"39735.8",
|
|
772
|
+
// "deal_vol":"2",
|
|
773
|
+
// "type":0,
|
|
774
|
+
// "way":1,
|
|
775
|
+
// "create_time":1701618503,
|
|
776
|
+
// "create_time_mill":1701618503517,
|
|
777
|
+
// "created_at":"2023-12-03T15:48:23.517518538Z"
|
|
778
|
+
// }
|
|
779
|
+
// ]
|
|
780
|
+
// }
|
|
781
|
+
//
|
|
782
|
+
const channel = this.safeString2(message, 'table', 'group');
|
|
783
|
+
const isSpot = (channel.indexOf('spot') >= 0);
|
|
784
|
+
const data = this.safeValue(message, 'data');
|
|
785
|
+
if (data === undefined) {
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
let stored = undefined;
|
|
274
789
|
for (let i = 0; i < data.length; i++) {
|
|
275
|
-
const trade = this.
|
|
790
|
+
const trade = this.parseWsTrade(data[i]);
|
|
276
791
|
const symbol = trade['symbol'];
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
let stored = this.safeValue(this.trades, symbol);
|
|
792
|
+
const tradesLimit = this.safeInteger(this.options, 'tradesLimit', 1000);
|
|
793
|
+
stored = this.safeValue(this.trades, symbol);
|
|
280
794
|
if (stored === undefined) {
|
|
281
795
|
stored = new ArrayCache(tradesLimit);
|
|
282
796
|
this.trades[symbol] = stored;
|
|
283
797
|
}
|
|
284
798
|
stored.append(trade);
|
|
285
|
-
client.resolve(stored, messageHash);
|
|
286
799
|
}
|
|
800
|
+
let messageHash = channel;
|
|
801
|
+
if (isSpot) {
|
|
802
|
+
messageHash += ':' + this.safeString(data[0], 'symbol');
|
|
803
|
+
}
|
|
804
|
+
client.resolve(stored, messageHash);
|
|
287
805
|
return message;
|
|
288
806
|
}
|
|
807
|
+
parseWsTrade(trade, market = undefined) {
|
|
808
|
+
// spot
|
|
809
|
+
// {
|
|
810
|
+
// "price": "52700.50",
|
|
811
|
+
// "s_t": 1630982050,
|
|
812
|
+
// "side": "buy",
|
|
813
|
+
// "size": "0.00112",
|
|
814
|
+
// "symbol": "BTC_USDT"
|
|
815
|
+
// }
|
|
816
|
+
// swap
|
|
817
|
+
// {
|
|
818
|
+
// "trade_id":6798697637,
|
|
819
|
+
// "contract_id":1,
|
|
820
|
+
// "symbol":"BTCUSDT",
|
|
821
|
+
// "deal_price":"39735.8",
|
|
822
|
+
// "deal_vol":"2",
|
|
823
|
+
// "type":0,
|
|
824
|
+
// "way":1,
|
|
825
|
+
// "create_time":1701618503,
|
|
826
|
+
// "create_time_mill":1701618503517,
|
|
827
|
+
// "created_at":"2023-12-03T15:48:23.517518538Z"
|
|
828
|
+
// }
|
|
829
|
+
//
|
|
830
|
+
const contractId = this.safeString(trade, 'contract_id');
|
|
831
|
+
const marketType = (contractId === undefined) ? 'spot' : 'swap';
|
|
832
|
+
const marketDelimiter = (marketType === 'spot') ? '_' : '';
|
|
833
|
+
const timestamp = this.safeInteger(trade, 'create_time_mill', this.safeTimestamp(trade, 's_t'));
|
|
834
|
+
const marketId = this.safeString(trade, 'symbol');
|
|
835
|
+
return this.safeTrade({
|
|
836
|
+
'info': trade,
|
|
837
|
+
'id': this.safeString(trade, 'trade_id'),
|
|
838
|
+
'order': undefined,
|
|
839
|
+
'timestamp': timestamp,
|
|
840
|
+
'datetime': this.iso8601(timestamp),
|
|
841
|
+
'symbol': this.safeSymbol(marketId, market, marketDelimiter, marketType),
|
|
842
|
+
'type': undefined,
|
|
843
|
+
'side': this.safeString(trade, 'side'),
|
|
844
|
+
'price': this.safeString2(trade, 'price', 'deal_price'),
|
|
845
|
+
'amount': this.safeString2(trade, 'size', 'deal_vol'),
|
|
846
|
+
'cost': undefined,
|
|
847
|
+
'takerOrMaker': undefined,
|
|
848
|
+
'fee': undefined,
|
|
849
|
+
}, market);
|
|
850
|
+
}
|
|
289
851
|
handleTicker(client, message) {
|
|
290
852
|
//
|
|
291
|
-
//
|
|
292
|
-
//
|
|
293
|
-
//
|
|
294
|
-
//
|
|
295
|
-
//
|
|
296
|
-
//
|
|
297
|
-
//
|
|
298
|
-
//
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
-
//
|
|
302
|
-
//
|
|
303
|
-
//
|
|
304
|
-
//
|
|
853
|
+
// {
|
|
854
|
+
// "data": [
|
|
855
|
+
// {
|
|
856
|
+
// "base_volume_24h": "78615593.81",
|
|
857
|
+
// "high_24h": "52756.97",
|
|
858
|
+
// "last_price": "52638.31",
|
|
859
|
+
// "low_24h": "50991.35",
|
|
860
|
+
// "open_24h": "51692.03",
|
|
861
|
+
// "s_t": 1630981727,
|
|
862
|
+
// "symbol": "BTC_USDT"
|
|
863
|
+
// }
|
|
864
|
+
// ],
|
|
865
|
+
// "table": "spot/ticker"
|
|
866
|
+
// }
|
|
867
|
+
// {
|
|
868
|
+
// "group":"futures/ticker",
|
|
869
|
+
// "data":{
|
|
870
|
+
// "symbol":"BTCUSDT",
|
|
871
|
+
// "volume_24":"117387.58",
|
|
872
|
+
// "fair_price":"146.24",
|
|
873
|
+
// "last_price":"146.24",
|
|
874
|
+
// "range":"147.17",
|
|
875
|
+
// "ask_price": "147.11",
|
|
876
|
+
// "ask_vol": "1",
|
|
877
|
+
// "bid_price": "142.11",
|
|
878
|
+
// "bid_vol": "1"
|
|
879
|
+
// }
|
|
880
|
+
// }
|
|
305
881
|
//
|
|
306
882
|
const table = this.safeString(message, 'table');
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
883
|
+
const isSpot = (table !== undefined);
|
|
884
|
+
const data = this.safeValue(message, 'data');
|
|
885
|
+
if (data === undefined) {
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
if (isSpot) {
|
|
889
|
+
for (let i = 0; i < data.length; i++) {
|
|
890
|
+
const ticker = this.parseTicker(data[i]);
|
|
891
|
+
const symbol = ticker['symbol'];
|
|
892
|
+
const marketId = this.safeString(ticker['info'], 'symbol');
|
|
893
|
+
const messageHash = table + ':' + marketId;
|
|
894
|
+
this.tickers[symbol] = ticker;
|
|
895
|
+
client.resolve(ticker, messageHash);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
else {
|
|
899
|
+
const ticker = this.parseWsSwapTicker(data);
|
|
900
|
+
const symbol = this.safeString(ticker, 'symbol');
|
|
313
901
|
this.tickers[symbol] = ticker;
|
|
314
|
-
client.resolve(ticker,
|
|
902
|
+
client.resolve(ticker, 'tickers');
|
|
903
|
+
this.resolvePromiseIfMessagehashMatches(client, 'tickers::', symbol, ticker);
|
|
315
904
|
}
|
|
316
905
|
return message;
|
|
317
906
|
}
|
|
907
|
+
parseWsSwapTicker(ticker, market = undefined) {
|
|
908
|
+
//
|
|
909
|
+
// {
|
|
910
|
+
// "symbol":"BTCUSDT",
|
|
911
|
+
// "volume_24":"117387.58",
|
|
912
|
+
// "fair_price":"146.24",
|
|
913
|
+
// "last_price":"146.24",
|
|
914
|
+
// "range":"147.17",
|
|
915
|
+
// "ask_price": "147.11",
|
|
916
|
+
// "ask_vol": "1",
|
|
917
|
+
// "bid_price": "142.11",
|
|
918
|
+
// "bid_vol": "1"
|
|
919
|
+
// }
|
|
920
|
+
const marketId = this.safeString(ticker, 'symbol');
|
|
921
|
+
return this.safeTicker({
|
|
922
|
+
'symbol': this.safeSymbol(marketId, market, '', 'swap'),
|
|
923
|
+
'timestamp': undefined,
|
|
924
|
+
'datetime': undefined,
|
|
925
|
+
'high': undefined,
|
|
926
|
+
'low': undefined,
|
|
927
|
+
'bid': this.safeString(ticker, 'bid_price'),
|
|
928
|
+
'bidVolume': this.safeString(ticker, 'bid_vol'),
|
|
929
|
+
'ask': this.safeString(ticker, 'ask_price'),
|
|
930
|
+
'askVolume': this.safeString(ticker, 'ask_vol'),
|
|
931
|
+
'vwap': undefined,
|
|
932
|
+
'open': undefined,
|
|
933
|
+
'close': undefined,
|
|
934
|
+
'last': this.safeString(ticker, 'last_price'),
|
|
935
|
+
'previousClose': undefined,
|
|
936
|
+
'change': undefined,
|
|
937
|
+
'percentage': undefined,
|
|
938
|
+
'average': this.safeString(ticker, 'fair_price'),
|
|
939
|
+
'baseVolume': undefined,
|
|
940
|
+
'quoteVolume': this.safeString(ticker, 'volume_24'),
|
|
941
|
+
'info': ticker,
|
|
942
|
+
}, market);
|
|
943
|
+
}
|
|
318
944
|
async watchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
|
|
319
945
|
/**
|
|
320
946
|
* @method
|
|
321
947
|
* @name bitmart#watchOHLCV
|
|
948
|
+
* @see https://developer-pro.bitmart.com/en/spot/#public-kline-channel
|
|
949
|
+
* @see https://developer-pro.bitmart.com/en/futures/#public-klinebin-channel
|
|
322
950
|
* @description watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
|
323
951
|
* @param {string} symbol unified symbol of the market to fetch OHLCV data for
|
|
324
952
|
* @param {string} timeframe the length of time each candle represents
|
|
@@ -329,10 +957,19 @@ export default class bitmart extends bitmartRest {
|
|
|
329
957
|
*/
|
|
330
958
|
await this.loadMarkets();
|
|
331
959
|
symbol = this.symbol(symbol);
|
|
960
|
+
const market = this.market(symbol);
|
|
961
|
+
let type = 'spot';
|
|
962
|
+
[type, params] = this.handleMarketTypeAndParams('watchOrderBook', market, params);
|
|
332
963
|
const timeframes = this.safeValue(this.options, 'timeframes', {});
|
|
333
964
|
const interval = this.safeString(timeframes, timeframe);
|
|
334
|
-
|
|
335
|
-
|
|
965
|
+
let name = undefined;
|
|
966
|
+
if (type === 'spot') {
|
|
967
|
+
name = 'kline' + interval;
|
|
968
|
+
}
|
|
969
|
+
else {
|
|
970
|
+
name = 'klineBin' + interval;
|
|
971
|
+
}
|
|
972
|
+
const ohlcv = await this.subscribe(name, symbol, type, params);
|
|
336
973
|
if (this.newUpdates) {
|
|
337
974
|
limit = ohlcv.getLimit(symbol, limit);
|
|
338
975
|
}
|
|
@@ -340,40 +977,82 @@ export default class bitmart extends bitmartRest {
|
|
|
340
977
|
}
|
|
341
978
|
handleOHLCV(client, message) {
|
|
342
979
|
//
|
|
343
|
-
//
|
|
344
|
-
//
|
|
345
|
-
//
|
|
346
|
-
//
|
|
347
|
-
//
|
|
348
|
-
//
|
|
349
|
-
//
|
|
350
|
-
//
|
|
351
|
-
//
|
|
352
|
-
//
|
|
353
|
-
//
|
|
354
|
-
//
|
|
355
|
-
//
|
|
356
|
-
//
|
|
357
|
-
//
|
|
358
|
-
//
|
|
980
|
+
// {
|
|
981
|
+
// "data": [
|
|
982
|
+
// {
|
|
983
|
+
// "candle": [
|
|
984
|
+
// 1631056350,
|
|
985
|
+
// "46532.83",
|
|
986
|
+
// "46555.71",
|
|
987
|
+
// "46511.41",
|
|
988
|
+
// "46555.71",
|
|
989
|
+
// "0.25"
|
|
990
|
+
// ],
|
|
991
|
+
// "symbol": "BTC_USDT"
|
|
992
|
+
// }
|
|
993
|
+
// ],
|
|
994
|
+
// "table": "spot/kline1m"
|
|
995
|
+
// }
|
|
996
|
+
// swap
|
|
997
|
+
// {
|
|
998
|
+
// "group":"futures/klineBin1m:BTCUSDT",
|
|
999
|
+
// "data":{
|
|
1000
|
+
// "symbol":"BTCUSDT",
|
|
1001
|
+
// "items":[
|
|
1002
|
+
// {
|
|
1003
|
+
// "o":"39635.8",
|
|
1004
|
+
// "h":"39636",
|
|
1005
|
+
// "l":"39614.4",
|
|
1006
|
+
// "c":"39629.7",
|
|
1007
|
+
// "v":"31852",
|
|
1008
|
+
// "ts":1701617761
|
|
1009
|
+
// }
|
|
1010
|
+
// ]
|
|
1011
|
+
// }
|
|
1012
|
+
// }
|
|
359
1013
|
//
|
|
360
|
-
const
|
|
361
|
-
const
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
1014
|
+
const channel = this.safeString2(message, 'table', 'group');
|
|
1015
|
+
const isSpot = (channel.indexOf('spot') >= 0);
|
|
1016
|
+
const data = this.safeValue(message, 'data');
|
|
1017
|
+
if (data === undefined) {
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
const parts = channel.split('/');
|
|
1021
|
+
const part1 = this.safeString(parts, 1, '');
|
|
1022
|
+
let interval = part1.replace('kline', '');
|
|
1023
|
+
interval = interval.replace('Bin', '');
|
|
1024
|
+
const intervalParts = interval.split(':');
|
|
1025
|
+
interval = this.safeString(intervalParts, 0);
|
|
365
1026
|
// use a reverse lookup in a static map instead
|
|
366
1027
|
const timeframes = this.safeValue(this.options, 'timeframes', {});
|
|
367
1028
|
const timeframe = this.findTimeframe(interval, timeframes);
|
|
368
1029
|
const duration = this.parseTimeframe(timeframe);
|
|
369
1030
|
const durationInMs = duration * 1000;
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
1031
|
+
if (isSpot) {
|
|
1032
|
+
for (let i = 0; i < data.length; i++) {
|
|
1033
|
+
const marketId = this.safeString(data[i], 'symbol');
|
|
1034
|
+
const market = this.safeMarket(marketId);
|
|
1035
|
+
const symbol = market['symbol'];
|
|
1036
|
+
const rawOHLCV = this.safeValue(data[i], 'candle');
|
|
1037
|
+
const parsed = this.parseOHLCV(rawOHLCV, market);
|
|
1038
|
+
parsed[0] = this.parseToInt(parsed[0] / durationInMs) * durationInMs;
|
|
1039
|
+
this.ohlcvs[symbol] = this.safeValue(this.ohlcvs, symbol, {});
|
|
1040
|
+
let stored = this.safeValue(this.ohlcvs[symbol], timeframe);
|
|
1041
|
+
if (stored === undefined) {
|
|
1042
|
+
const limit = this.safeInteger(this.options, 'OHLCVLimit', 1000);
|
|
1043
|
+
stored = new ArrayCacheByTimestamp(limit);
|
|
1044
|
+
this.ohlcvs[symbol][timeframe] = stored;
|
|
1045
|
+
}
|
|
1046
|
+
stored.append(parsed);
|
|
1047
|
+
const messageHash = channel + ':' + marketId;
|
|
1048
|
+
client.resolve(stored, messageHash);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
else {
|
|
1052
|
+
const marketId = this.safeString(data, 'symbol');
|
|
1053
|
+
const market = this.safeMarket(marketId, undefined, '', 'swap');
|
|
374
1054
|
const symbol = market['symbol'];
|
|
375
|
-
const
|
|
376
|
-
parsed[0] = this.parseToInt(parsed[0] / durationInMs) * durationInMs;
|
|
1055
|
+
const items = this.safeValue(data, 'items', []);
|
|
377
1056
|
this.ohlcvs[symbol] = this.safeValue(this.ohlcvs, symbol, {});
|
|
378
1057
|
let stored = this.safeValue(this.ohlcvs[symbol], timeframe);
|
|
379
1058
|
if (stored === undefined) {
|
|
@@ -381,24 +1060,34 @@ export default class bitmart extends bitmartRest {
|
|
|
381
1060
|
stored = new ArrayCacheByTimestamp(limit);
|
|
382
1061
|
this.ohlcvs[symbol][timeframe] = stored;
|
|
383
1062
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
1063
|
+
for (let i = 0; i < items.length; i++) {
|
|
1064
|
+
const candle = items[i];
|
|
1065
|
+
const parsed = this.parseOHLCV(candle, market);
|
|
1066
|
+
stored.append(parsed);
|
|
1067
|
+
}
|
|
1068
|
+
client.resolve(stored, channel);
|
|
387
1069
|
}
|
|
388
1070
|
}
|
|
389
1071
|
async watchOrderBook(symbol, limit = undefined, params = {}) {
|
|
390
1072
|
/**
|
|
391
1073
|
* @method
|
|
392
1074
|
* @name bitmart#watchOrderBook
|
|
1075
|
+
* @see https://developer-pro.bitmart.com/en/spot/#public-depth-all-channel
|
|
1076
|
+
* @see https://developer-pro.bitmart.com/en/futures/#public-depth-channel
|
|
393
1077
|
* @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
|
|
394
1078
|
* @param {string} symbol unified symbol of the market to fetch the order book for
|
|
395
1079
|
* @param {int} [limit] the maximum amount of order book entries to return
|
|
396
1080
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
397
1081
|
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
|
|
398
1082
|
*/
|
|
1083
|
+
await this.loadMarkets();
|
|
399
1084
|
const options = this.safeValue(this.options, 'watchOrderBook', {});
|
|
400
1085
|
const depth = this.safeString(options, 'depth', 'depth50');
|
|
401
|
-
|
|
1086
|
+
symbol = this.symbol(symbol);
|
|
1087
|
+
const market = this.market(symbol);
|
|
1088
|
+
let type = 'spot';
|
|
1089
|
+
[type, params] = this.handleMarketTypeAndParams('watchOrderBook', market, params);
|
|
1090
|
+
const orderbook = await this.subscribe(depth, symbol, type, params);
|
|
402
1091
|
return orderbook.limit();
|
|
403
1092
|
}
|
|
404
1093
|
handleDelta(bookside, delta) {
|
|
@@ -446,22 +1135,19 @@ export default class bitmart extends bitmartRest {
|
|
|
446
1135
|
}
|
|
447
1136
|
handleOrderBook(client, message) {
|
|
448
1137
|
//
|
|
1138
|
+
// spot
|
|
449
1139
|
// {
|
|
450
1140
|
// "data": [
|
|
451
1141
|
// {
|
|
452
1142
|
// "asks": [
|
|
453
1143
|
// [ '46828.38', "0.21847" ],
|
|
454
1144
|
// [ '46830.68', "0.08232" ],
|
|
455
|
-
//
|
|
456
|
-
// [ '46837.82', "0.02028" ],
|
|
457
|
-
// [ '46839.43', "0.15068" ]
|
|
1145
|
+
// ...
|
|
458
1146
|
// ],
|
|
459
1147
|
// "bids": [
|
|
460
1148
|
// [ '46820.78', "0.00444" ],
|
|
461
1149
|
// [ '46814.33', "0.00234" ],
|
|
462
|
-
//
|
|
463
|
-
// [ '46808.14', "0.00217" ],
|
|
464
|
-
// [ '46808.04', "0.00013" ]
|
|
1150
|
+
// ...
|
|
465
1151
|
// ],
|
|
466
1152
|
// "ms_t": 1631044962431,
|
|
467
1153
|
// "symbol": "BTC_USDT"
|
|
@@ -469,32 +1155,99 @@ export default class bitmart extends bitmartRest {
|
|
|
469
1155
|
// ],
|
|
470
1156
|
// "table": "spot/depth5"
|
|
471
1157
|
// }
|
|
1158
|
+
// swap
|
|
1159
|
+
// {
|
|
1160
|
+
// "group":"futures/depth50:BTCUSDT",
|
|
1161
|
+
// "data":{
|
|
1162
|
+
// "symbol":"BTCUSDT",
|
|
1163
|
+
// "way":1,
|
|
1164
|
+
// "depths":[
|
|
1165
|
+
// {
|
|
1166
|
+
// "price":"39509.8",
|
|
1167
|
+
// "vol":"2379"
|
|
1168
|
+
// },
|
|
1169
|
+
// {
|
|
1170
|
+
// "price":"39509.6",
|
|
1171
|
+
// "vol":"6815"
|
|
1172
|
+
// },
|
|
1173
|
+
// ...
|
|
1174
|
+
// ],
|
|
1175
|
+
// "ms_t":1701566021194
|
|
1176
|
+
// }
|
|
1177
|
+
// }
|
|
472
1178
|
//
|
|
473
|
-
const data = this.safeValue(message, 'data'
|
|
474
|
-
|
|
1179
|
+
const data = this.safeValue(message, 'data');
|
|
1180
|
+
if (data === undefined) {
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
const depths = this.safeValue(data, 'depths');
|
|
1184
|
+
const isSpot = (depths === undefined);
|
|
1185
|
+
const table = this.safeString2(message, 'table', 'group');
|
|
475
1186
|
const parts = table.split('/');
|
|
476
1187
|
const lastPart = this.safeString(parts, 1);
|
|
477
|
-
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
1188
|
+
let limitString = lastPart.replace('depth', '');
|
|
1189
|
+
const dotsIndex = limitString.indexOf(':');
|
|
1190
|
+
limitString = limitString.slice(0, dotsIndex);
|
|
1191
|
+
const limit = this.parseToInt(limitString);
|
|
1192
|
+
if (isSpot) {
|
|
1193
|
+
for (let i = 0; i < data.length; i++) {
|
|
1194
|
+
const update = data[i];
|
|
1195
|
+
const marketId = this.safeString(update, 'symbol');
|
|
1196
|
+
const symbol = this.safeSymbol(marketId);
|
|
1197
|
+
let orderbook = this.safeValue(this.orderbooks, symbol);
|
|
1198
|
+
if (orderbook === undefined) {
|
|
1199
|
+
orderbook = this.orderBook({}, limit);
|
|
1200
|
+
orderbook['symbol'] = symbol;
|
|
1201
|
+
this.orderbooks[symbol] = orderbook;
|
|
1202
|
+
}
|
|
1203
|
+
orderbook.reset({});
|
|
1204
|
+
this.handleOrderBookMessage(client, update, orderbook);
|
|
1205
|
+
const timestamp = this.safeInteger(update, 'ms_t');
|
|
1206
|
+
orderbook['timestamp'] = timestamp;
|
|
1207
|
+
orderbook['datetime'] = this.iso8601(timestamp);
|
|
1208
|
+
const messageHash = table + ':' + marketId;
|
|
1209
|
+
client.resolve(orderbook, messageHash);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
else {
|
|
1213
|
+
const marketId = this.safeString(data, 'symbol');
|
|
482
1214
|
const symbol = this.safeSymbol(marketId);
|
|
483
1215
|
let orderbook = this.safeValue(this.orderbooks, symbol);
|
|
484
1216
|
if (orderbook === undefined) {
|
|
485
1217
|
orderbook = this.orderBook({}, limit);
|
|
1218
|
+
orderbook['symbol'] = symbol;
|
|
486
1219
|
this.orderbooks[symbol] = orderbook;
|
|
487
1220
|
}
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
1221
|
+
const way = this.safeNumber(data, 'way');
|
|
1222
|
+
const side = (way === 1) ? 'bids' : 'asks';
|
|
1223
|
+
if (way === 1) {
|
|
1224
|
+
orderbook[side] = new Bids([], limit);
|
|
1225
|
+
}
|
|
1226
|
+
else {
|
|
1227
|
+
orderbook[side] = new Asks([], limit);
|
|
1228
|
+
}
|
|
1229
|
+
for (let i = 0; i < depths.length; i++) {
|
|
1230
|
+
const depth = depths[i];
|
|
1231
|
+
const price = this.safeNumber(depth, 'price');
|
|
1232
|
+
const amount = this.safeNumber(depth, 'vol');
|
|
1233
|
+
const orderbookSide = this.safeValue(orderbook, side);
|
|
1234
|
+
orderbookSide.store(price, amount);
|
|
1235
|
+
}
|
|
1236
|
+
const bidsLength = orderbook['bids'].length;
|
|
1237
|
+
const asksLength = orderbook['asks'].length;
|
|
1238
|
+
if ((bidsLength === 0) || (asksLength === 0)) {
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
1241
|
+
const timestamp = this.safeInteger(data, 'ms_t');
|
|
1242
|
+
orderbook['timestamp'] = timestamp;
|
|
1243
|
+
orderbook['datetime'] = this.iso8601(timestamp);
|
|
1244
|
+
const messageHash = table;
|
|
491
1245
|
client.resolve(orderbook, messageHash);
|
|
492
1246
|
}
|
|
493
|
-
return message;
|
|
494
1247
|
}
|
|
495
|
-
async authenticate(params = {}) {
|
|
1248
|
+
async authenticate(type, params = {}) {
|
|
496
1249
|
this.checkRequiredCredentials();
|
|
497
|
-
const url = this.implodeHostname(this.urls['api']['ws']['private']);
|
|
1250
|
+
const url = this.implodeHostname(this.urls['api']['ws'][type]['private']);
|
|
498
1251
|
const messageHash = 'authenticated';
|
|
499
1252
|
const client = this.client(url);
|
|
500
1253
|
const future = client.future(messageHash);
|
|
@@ -505,15 +1258,28 @@ export default class bitmart extends bitmartRest {
|
|
|
505
1258
|
const path = 'bitmart.WebSocket';
|
|
506
1259
|
const auth = timestamp + '#' + memo + '#' + path;
|
|
507
1260
|
const signature = this.hmac(this.encode(auth), this.encode(this.secret), sha256);
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
1261
|
+
let request = undefined;
|
|
1262
|
+
if (type === 'spot') {
|
|
1263
|
+
request = {
|
|
1264
|
+
'op': 'login',
|
|
1265
|
+
'args': [
|
|
1266
|
+
this.apiKey,
|
|
1267
|
+
timestamp,
|
|
1268
|
+
signature,
|
|
1269
|
+
],
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1272
|
+
else {
|
|
1273
|
+
request = {
|
|
1274
|
+
'action': 'access',
|
|
1275
|
+
'args': [
|
|
1276
|
+
this.apiKey,
|
|
1277
|
+
timestamp,
|
|
1278
|
+
signature,
|
|
1279
|
+
'web',
|
|
1280
|
+
],
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
517
1283
|
const message = this.extend(request, params);
|
|
518
1284
|
this.watch(url, messageHash, message, messageHash);
|
|
519
1285
|
}
|
|
@@ -521,13 +1287,16 @@ export default class bitmart extends bitmartRest {
|
|
|
521
1287
|
}
|
|
522
1288
|
handleSubscriptionStatus(client, message) {
|
|
523
1289
|
//
|
|
524
|
-
//
|
|
1290
|
+
// {"event":"subscribe","channel":"spot/depth:BTC-USDT"}
|
|
525
1291
|
//
|
|
526
1292
|
return message;
|
|
527
1293
|
}
|
|
528
1294
|
handleAuthenticate(client, message) {
|
|
529
1295
|
//
|
|
530
|
-
//
|
|
1296
|
+
// spot
|
|
1297
|
+
// { event: "login" }
|
|
1298
|
+
// swap
|
|
1299
|
+
// { action: 'access', success: true }
|
|
531
1300
|
//
|
|
532
1301
|
const messageHash = 'authenticated';
|
|
533
1302
|
const future = this.safeValue(client.futures, messageHash);
|
|
@@ -535,29 +1304,41 @@ export default class bitmart extends bitmartRest {
|
|
|
535
1304
|
}
|
|
536
1305
|
handleErrorMessage(client, message) {
|
|
537
1306
|
//
|
|
538
|
-
//
|
|
539
|
-
//
|
|
1307
|
+
// { event: "error", message: "Invalid sign", errorCode: 30013 }
|
|
1308
|
+
// {"event":"error","message":"Unrecognized request: {\"event\":\"subscribe\",\"channel\":\"spot/depth:BTC-USDT\"}","errorCode":30039}
|
|
1309
|
+
// {
|
|
1310
|
+
// action: '',
|
|
1311
|
+
// group: 'futures/trade:BTCUSDT',
|
|
1312
|
+
// success: false,
|
|
1313
|
+
// request: { action: '', args: [ 'futures/trade:BTCUSDT' ] },
|
|
1314
|
+
// error: 'Invalid action [] for group [futures/trade:BTCUSDT]'
|
|
1315
|
+
// }
|
|
540
1316
|
//
|
|
541
1317
|
const errorCode = this.safeString(message, 'errorCode');
|
|
1318
|
+
const error = this.safeString(message, 'error');
|
|
542
1319
|
try {
|
|
543
|
-
if (errorCode !== undefined) {
|
|
1320
|
+
if (errorCode !== undefined || error !== undefined) {
|
|
544
1321
|
const feedback = this.id + ' ' + this.json(message);
|
|
545
1322
|
this.throwExactlyMatchedException(this.exceptions['exact'], errorCode, feedback);
|
|
546
|
-
const messageString = this.safeValue(message, 'message');
|
|
547
|
-
|
|
548
|
-
|
|
1323
|
+
const messageString = this.safeValue(message, 'message', error);
|
|
1324
|
+
this.throwBroadlyMatchedException(this.exceptions['broad'], messageString, feedback);
|
|
1325
|
+
const action = this.safeString(message, 'action');
|
|
1326
|
+
if (action === 'access') {
|
|
1327
|
+
throw new AuthenticationError(feedback);
|
|
549
1328
|
}
|
|
1329
|
+
throw new ExchangeError(feedback);
|
|
550
1330
|
}
|
|
551
1331
|
return false;
|
|
552
1332
|
}
|
|
553
1333
|
catch (e) {
|
|
554
|
-
if (e instanceof AuthenticationError) {
|
|
1334
|
+
if ((e instanceof AuthenticationError)) {
|
|
555
1335
|
const messageHash = 'authenticated';
|
|
556
1336
|
client.reject(e, messageHash);
|
|
557
1337
|
if (messageHash in client.subscriptions) {
|
|
558
1338
|
delete client.subscriptions[messageHash];
|
|
559
1339
|
}
|
|
560
1340
|
}
|
|
1341
|
+
client.reject(e);
|
|
561
1342
|
return true;
|
|
562
1343
|
}
|
|
563
1344
|
}
|
|
@@ -590,14 +1371,14 @@ export default class bitmart extends bitmartRest {
|
|
|
590
1371
|
//
|
|
591
1372
|
// { data: '', table: "spot/user/order" }
|
|
592
1373
|
//
|
|
593
|
-
const
|
|
594
|
-
if (
|
|
595
|
-
const event = this.
|
|
1374
|
+
const channel = this.safeString2(message, 'table', 'group');
|
|
1375
|
+
if (channel === undefined) {
|
|
1376
|
+
const event = this.safeString2(message, 'event', 'action');
|
|
596
1377
|
if (event !== undefined) {
|
|
597
1378
|
const methods = {
|
|
598
1379
|
// 'info': this.handleSystemStatus,
|
|
599
|
-
// 'book': 'handleOrderBook',
|
|
600
1380
|
'login': this.handleAuthenticate,
|
|
1381
|
+
'access': this.handleAuthenticate,
|
|
601
1382
|
'subscribe': this.handleSubscriptionStatus,
|
|
602
1383
|
};
|
|
603
1384
|
const method = this.safeValue(methods, event);
|
|
@@ -610,30 +1391,25 @@ export default class bitmart extends bitmartRest {
|
|
|
610
1391
|
}
|
|
611
1392
|
}
|
|
612
1393
|
else {
|
|
613
|
-
const parts = table.split('/');
|
|
614
|
-
const name = this.safeString(parts, 1);
|
|
615
1394
|
const methods = {
|
|
616
|
-
'depth': this.handleOrderBook,
|
|
617
1395
|
'depth5': this.handleOrderBook,
|
|
618
1396
|
'depth20': this.handleOrderBook,
|
|
619
1397
|
'depth50': this.handleOrderBook,
|
|
620
1398
|
'ticker': this.handleTicker,
|
|
621
1399
|
'trade': this.handleTrade,
|
|
622
|
-
|
|
1400
|
+
'kline': this.handleOHLCV,
|
|
1401
|
+
'order': this.handleOrders,
|
|
1402
|
+
'position': this.handlePositions,
|
|
1403
|
+
'balance': this.handleBalance,
|
|
1404
|
+
'asset': this.handleBalance,
|
|
623
1405
|
};
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
}
|
|
632
|
-
if (method === undefined) {
|
|
633
|
-
return message;
|
|
634
|
-
}
|
|
635
|
-
else {
|
|
636
|
-
return method.call(this, client, message);
|
|
1406
|
+
const keys = Object.keys(methods);
|
|
1407
|
+
for (let i = 0; i < keys.length; i++) {
|
|
1408
|
+
const key = keys[i];
|
|
1409
|
+
if (channel.indexOf(key) >= 0) {
|
|
1410
|
+
const method = this.safeValue(methods, key);
|
|
1411
|
+
return method.call(this, client, message);
|
|
1412
|
+
}
|
|
637
1413
|
}
|
|
638
1414
|
}
|
|
639
1415
|
}
|