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