hollaex-node-lib 1.1.0 → 2.12.0
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/.drone.yml +59 -0
- package/.editorconfig +10 -0
- package/.eslintrc.json +3 -0
- package/LICENSE +1 -1
- package/README.md +271 -262
- package/example/hollaex.js +7 -16
- package/index.js +7 -446
- package/kit.js +896 -0
- package/package.json +19 -8
- package/utils.js +49 -4
- package/.prettierignore.json +0 -1
- package/.prettierrc.json +0 -8
package/kit.js
ADDED
|
@@ -0,0 +1,896 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const WebSocket = require('ws');
|
|
4
|
+
const moment = require('moment');
|
|
5
|
+
const { createRequest, createSignature, generateHeaders, isDatetime, sanitizeDate } = require('./utils');
|
|
6
|
+
const { setWsHeartbeat } = require('ws-heartbeat/client');
|
|
7
|
+
const { each, union, isNumber, isString, isPlainObject, isBoolean } = require('lodash');
|
|
8
|
+
class HollaExKit {
|
|
9
|
+
constructor(
|
|
10
|
+
opts = {
|
|
11
|
+
apiURL: 'https://api.hollaex.com',
|
|
12
|
+
baseURL: '/v2',
|
|
13
|
+
apiKey: '',
|
|
14
|
+
apiSecret: '',
|
|
15
|
+
apiExpiresAfter: 60
|
|
16
|
+
}
|
|
17
|
+
) {
|
|
18
|
+
this.apiUrl = opts.apiURL || 'https://api.hollaex.com';
|
|
19
|
+
this.baseUrl = opts.baseURL || '/v2';
|
|
20
|
+
this.apiKey = opts.apiKey;
|
|
21
|
+
this.apiSecret = opts.apiSecret;
|
|
22
|
+
this.apiExpiresAfter = opts.apiExpiresAfter || 60;
|
|
23
|
+
this.headers = {
|
|
24
|
+
'content-type': 'application/json',
|
|
25
|
+
Accept: 'application/json',
|
|
26
|
+
'api-key': opts.apiKey
|
|
27
|
+
};
|
|
28
|
+
this.ws = null;
|
|
29
|
+
const [protocol, endpoint] = this.apiUrl.split('://');
|
|
30
|
+
this.wsUrl =
|
|
31
|
+
protocol === 'https'
|
|
32
|
+
? `wss://${endpoint}/stream`
|
|
33
|
+
: `ws://${endpoint}/stream`;
|
|
34
|
+
this.wsEvents = [];
|
|
35
|
+
this.wsReconnect = true;
|
|
36
|
+
this.wsReconnectInterval = 5000;
|
|
37
|
+
this.wsEventListeners = null;
|
|
38
|
+
this.wsConnected = () => this.ws && this.ws.readyState === WebSocket.OPEN;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* Public Endpoints*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get exchange information
|
|
45
|
+
* @return {object} A json object with the exchange information
|
|
46
|
+
*/
|
|
47
|
+
getKit() {
|
|
48
|
+
return createRequest(
|
|
49
|
+
'GET',
|
|
50
|
+
`${this.apiUrl}${this.baseUrl}/kit`,
|
|
51
|
+
this.headers
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Retrieve last, high, low, open and close price and volume within last 24 hours for a symbol
|
|
57
|
+
* @param {string} symbol - The currency pair symbol e.g. 'hex-usdt'
|
|
58
|
+
* @return {object} A JSON object with keys high(number), low(number), open(number), close(number), volume(number), last(number)
|
|
59
|
+
*/
|
|
60
|
+
getTicker(symbol = '') {
|
|
61
|
+
return createRequest(
|
|
62
|
+
'GET',
|
|
63
|
+
`${this.apiUrl}${this.baseUrl}/ticker?symbol=${symbol}`,
|
|
64
|
+
this.headers
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Retrieve last, high, low, open and close price and volume within last 24 hours for all symbols
|
|
70
|
+
* @return {object} A JSON object with symbols as keys which contain high(number), low(number), open(number), close(number), volume(number), last(number)
|
|
71
|
+
*/
|
|
72
|
+
getTickers() {
|
|
73
|
+
return createRequest(
|
|
74
|
+
'GET',
|
|
75
|
+
`${this.apiUrl}${this.baseUrl}/tickers`,
|
|
76
|
+
this.headers
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Retrieve orderbook containing lists of up to the last 20 bids and asks for a symbol
|
|
82
|
+
* @param {string} symbol - The currency pair symbol e.g. 'hex-usdt'
|
|
83
|
+
* @return {object} A JSON object with keys bids(array of active buy orders), asks(array of active sell orders), and timestamp(string)
|
|
84
|
+
*/
|
|
85
|
+
getOrderbook(symbol = '') {
|
|
86
|
+
return createRequest(
|
|
87
|
+
'GET',
|
|
88
|
+
`${this.apiUrl}${this.baseUrl}/orderbook?symbol=${symbol}`,
|
|
89
|
+
this.headers
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Retrieve orderbook containing lists of up to the last 20 bids and asks for all symbols
|
|
95
|
+
* @return {object} A JSON object with the symbol-pairs as keys where the values are objects with keys bids(array of active buy orders), asks(array of active sell orders), and timestamp(string)
|
|
96
|
+
*/
|
|
97
|
+
getOrderbooks() {
|
|
98
|
+
return createRequest(
|
|
99
|
+
'GET',
|
|
100
|
+
`${this.apiUrl}${this.baseUrl}/orderbooks`,
|
|
101
|
+
this.headers
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Retrieve list of up to the last 50 trades
|
|
107
|
+
* @param {object} opts - Optional parameters
|
|
108
|
+
* @param {string} opts.symbol - The currency pair symbol e.g. 'hex-usdt'
|
|
109
|
+
* @return {object} A JSON object with the symbol-pairs as keys where the values are arrays of objects with keys size(number), price(number), side(string), and timestamp(string)
|
|
110
|
+
*/
|
|
111
|
+
getTrades(opts = { symbol: null }) {
|
|
112
|
+
let path = `${this.apiUrl}${this.baseUrl}/trades`;
|
|
113
|
+
|
|
114
|
+
if (isString(opts.symbol)) {
|
|
115
|
+
path += `?symbol=${opts.symbol}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return createRequest('GET', path, this.headers);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Retrieve tick size, min price, max price, min size, and max size of each symbol-pair
|
|
123
|
+
* @return {object} A JSON object with the keys pairs(information on each symbol-pair such as tick_size, min/max price, and min/max size) and currencies(array of all currencies involved in hollaEx)
|
|
124
|
+
*/
|
|
125
|
+
getConstants() {
|
|
126
|
+
return createRequest(
|
|
127
|
+
'GET',
|
|
128
|
+
`${this.apiUrl}${this.baseUrl}/constants`,
|
|
129
|
+
this.headers
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* Private Endpoints*/
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Retrieve user's personal information
|
|
137
|
+
* @return {string} A JSON object showing user's information such as id, email, bank_account, crypto_wallet, balance, etc
|
|
138
|
+
*/
|
|
139
|
+
getUser() {
|
|
140
|
+
const verb = 'GET';
|
|
141
|
+
const path = `${this.baseUrl}/user`;
|
|
142
|
+
const headers = generateHeaders(
|
|
143
|
+
this.headers,
|
|
144
|
+
this.apiSecret,
|
|
145
|
+
verb,
|
|
146
|
+
path,
|
|
147
|
+
this.apiExpiresAfter
|
|
148
|
+
);
|
|
149
|
+
return createRequest(verb, `${this.apiUrl}${path}`, headers);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Retrieve user's wallet balance
|
|
154
|
+
* @return {object} A JSON object with the keys updated_at(string), usdt_balance(number), usdt_pending(number), usdt_available(number), hex_balance, hex_pending, hex_available, eth_balance, eth_pending, eth_available, bch_balance, bch_pending, bch_available
|
|
155
|
+
*/
|
|
156
|
+
getBalance() {
|
|
157
|
+
const verb = 'GET';
|
|
158
|
+
const path = `${this.baseUrl}/user/balance`;
|
|
159
|
+
const headers = generateHeaders(
|
|
160
|
+
this.headers,
|
|
161
|
+
this.apiSecret,
|
|
162
|
+
verb,
|
|
163
|
+
path,
|
|
164
|
+
this.apiExpiresAfter
|
|
165
|
+
);
|
|
166
|
+
return createRequest(verb, `${this.apiUrl}${path}`, headers);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Retrieve list of the user's deposits
|
|
171
|
+
* @param {object} opts - Optional parameters
|
|
172
|
+
* @param {string} opts.currency - The currency to filter by, pass undefined to receive data on all currencies
|
|
173
|
+
* @param {boolean} opts.status - Confirmed status of the deposits to get. Leave blank to get all confirmed and unconfirmed deposits
|
|
174
|
+
* @param {boolean} opts.dismissed - Dismissed status of the deposits to get. Leave blank to get all dismissed and undismissed deposits
|
|
175
|
+
* @param {boolean} opts.rejected - Rejected status of the deposits to get. Leave blank to get all rejected and unrejected deposits
|
|
176
|
+
* @param {boolean} opts.processing - Processing status of the deposits to get. Leave blank to get all processing and unprocessing deposits
|
|
177
|
+
* @param {boolean} opts.waiting - Waiting status of the deposits to get. Leave blank to get all waiting and unwaiting deposits
|
|
178
|
+
* @param {number} opts.limit - Amount of trades per page. Maximum: 50. Default: 50
|
|
179
|
+
* @param {number} opts.page - Page of trades data. Default: 1
|
|
180
|
+
* @param {string} opts.orderBy - The field to order data by e.g. amount, id.
|
|
181
|
+
* @param {string} opts.order - Ascending (asc) or descending (desc).
|
|
182
|
+
* @param {string} opts.startDate - Start date of query in ISO8601 format.
|
|
183
|
+
* @param {string} opts.endDate - End date of query in ISO8601 format.
|
|
184
|
+
* @param {string} opts.transactionId - Deposits with specific transaction ID.
|
|
185
|
+
* @param {string} opts.address - Deposits with specific address.
|
|
186
|
+
* @return {object} A JSON object with the keys count(total number of user's deposits) and data(array of deposits as objects with keys id(number), type(string), amount(number), transaction_id(string), currency(string), created_at(string), status(boolean), fee(number), dismissed(boolean), rejected(boolean), description(string))
|
|
187
|
+
*/
|
|
188
|
+
getDeposits(
|
|
189
|
+
opts = {
|
|
190
|
+
currency: null,
|
|
191
|
+
status: null,
|
|
192
|
+
dismissed: null,
|
|
193
|
+
rejected: null,
|
|
194
|
+
processing: null,
|
|
195
|
+
waiting: null,
|
|
196
|
+
limit: null,
|
|
197
|
+
page: null,
|
|
198
|
+
orderBy: null,
|
|
199
|
+
order: null,
|
|
200
|
+
startDate: null,
|
|
201
|
+
endDate: null,
|
|
202
|
+
transactionId: null,
|
|
203
|
+
address: null
|
|
204
|
+
}
|
|
205
|
+
) {
|
|
206
|
+
const verb = 'GET';
|
|
207
|
+
let path = `${this.baseUrl}/user/deposits?`;
|
|
208
|
+
|
|
209
|
+
if (isString(opts.currency)) {
|
|
210
|
+
path += `¤cy=${opts.currency}`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (isNumber(opts.limit)) {
|
|
214
|
+
path += `&limit=${opts.limit}`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (isNumber(opts.page)) {
|
|
218
|
+
path += `&page=${opts.page}`;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (isString(opts.orderBy)) {
|
|
222
|
+
path += `&order_by=${opts.orderBy}`;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (isString(opts.order)) {
|
|
226
|
+
path += `&order=${opts.order}`;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (isDatetime(opts.startDate)) {
|
|
230
|
+
path += `&start_date=${sanitizeDate(opts.startDate)}`;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (isDatetime(opts.endDate)) {
|
|
234
|
+
path += `&end_date=${sanitizeDate(opts.endDate)}`;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (isString(opts.address)) {
|
|
238
|
+
path += `&address=${opts.address}`;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (isString(opts.transactionId)) {
|
|
242
|
+
path += `&transaction_id=${opts.transactionId}`;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (isBoolean(opts.status)) {
|
|
246
|
+
path += `&status=${opts.status}`;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (isBoolean(opts.dismissed)) {
|
|
250
|
+
path += `&dismissed=${opts.dismissed}`;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (isBoolean(opts.rejected)) {
|
|
254
|
+
path += `&rejected=${opts.rejected}`;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (isBoolean(opts.processing)) {
|
|
258
|
+
path += `&processing=${opts.processing}`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (isBoolean(opts.waiting)) {
|
|
262
|
+
path += `&waiting=${opts.waiting}`;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const headers = generateHeaders(
|
|
266
|
+
this.headers,
|
|
267
|
+
this.apiSecret,
|
|
268
|
+
verb,
|
|
269
|
+
path,
|
|
270
|
+
this.apiExpiresAfter
|
|
271
|
+
);
|
|
272
|
+
return createRequest(verb, `${this.apiUrl}${path}`, headers);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/****** Withdrawals ******/
|
|
276
|
+
/**
|
|
277
|
+
* Retrieve list of the user's withdrawals
|
|
278
|
+
* @param {object} opts - Optional parameters
|
|
279
|
+
* @param {string} opts.currency - The currency to filter by, pass undefined to receive data on all currencies
|
|
280
|
+
* @param {boolean} opts.status - Confirmed status of the withdrawals to get. Leave blank to get all confirmed and unconfirmed withdrawals
|
|
281
|
+
* @param {boolean} opts.dismissed - Dismissed status of the withdrawals to get. Leave blank to get all dismissed and undismissed withdrawals
|
|
282
|
+
* @param {boolean} opts.rejected - Rejected status of the withdrawals to get. Leave blank to get all rejected and unrejected withdrawals
|
|
283
|
+
* @param {boolean} opts.processing - Processing status of the withdrawals to get. Leave blank to get all processing and unprocessing withdrawals
|
|
284
|
+
* @param {boolean} opts.waiting - Waiting status of the withdrawals to get. Leave blank to get all waiting and unwaiting withdrawals
|
|
285
|
+
* @param {number} opts.limit - Amount of trades per page. Maximum: 50. Default: 50
|
|
286
|
+
* @param {number} opts.page - Page of trades data. Default: 1
|
|
287
|
+
* @param {string} opts.orderBy - The field to order data by e.g. amount, id.
|
|
288
|
+
* @param {string} opts.order - Ascending (asc) or descending (desc).
|
|
289
|
+
* @param {string} opts.startDate - Start date of query in ISO8601 format.
|
|
290
|
+
* @param {string} opts.endDate - End date of query in ISO8601 format.
|
|
291
|
+
* @param {string} opts.transactionId - Withdrawals with specific transaction ID.
|
|
292
|
+
* @param {string} opts.address - Withdrawals with specific address.
|
|
293
|
+
* @return {object} A JSON object with the keys count(total number of user's withdrawals) and data(array of withdrawals as objects with keys id(number), type(string), amount(number), transaction_id(string), currency(string), created_at(string), status(boolean), fee(number), dismissed(boolean), rejected(boolean), description(string))
|
|
294
|
+
*/
|
|
295
|
+
getWithdrawals(
|
|
296
|
+
opts = {
|
|
297
|
+
currency: null,
|
|
298
|
+
status: null,
|
|
299
|
+
dismissed: null,
|
|
300
|
+
rejected: null,
|
|
301
|
+
processing: null,
|
|
302
|
+
waiting: null,
|
|
303
|
+
limit: null,
|
|
304
|
+
page: null,
|
|
305
|
+
orderBy: null,
|
|
306
|
+
order: null,
|
|
307
|
+
startDate: null,
|
|
308
|
+
endDate: null,
|
|
309
|
+
transactionId: null,
|
|
310
|
+
address: null
|
|
311
|
+
}
|
|
312
|
+
) {
|
|
313
|
+
const verb = 'GET';
|
|
314
|
+
let path = `${this.baseUrl}/user/withdrawals?`;
|
|
315
|
+
|
|
316
|
+
if (isString(opts.currency)) {
|
|
317
|
+
path += `¤cy=${opts.currency}`;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (isNumber(opts.limit)) {
|
|
321
|
+
path += `&limit=${opts.limit}`;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (isNumber(opts.page)) {
|
|
325
|
+
path += `&page=${opts.page}`;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (isString(opts.orderBy)) {
|
|
329
|
+
path += `&order_by=${opts.orderBy}`;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (isString(opts.order)) {
|
|
333
|
+
path += `&order=${opts.order}`;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (isDatetime(opts.startDate)) {
|
|
337
|
+
path += `&start_date=${sanitizeDate(opts.startDate)}`;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (isDatetime(opts.endDate)) {
|
|
341
|
+
path += `&end_date=${sanitizeDate(opts.endDate)}`;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (isString(opts.address)) {
|
|
345
|
+
path += `&address=${opts.address}`;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (isString(opts.transactionId)) {
|
|
349
|
+
path += `&transaction_id=${opts.transactionId}`;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (isBoolean(opts.status)) {
|
|
353
|
+
path += `&status=${opts.status}`;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (isBoolean(opts.dismissed)) {
|
|
357
|
+
path += `&dismissed=${opts.dismissed}`;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (isBoolean(opts.rejected)) {
|
|
361
|
+
path += `&rejected=${opts.rejected}`;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (isBoolean(opts.processing)) {
|
|
365
|
+
path += `&processing=${opts.processing}`;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (isBoolean(opts.waiting)) {
|
|
369
|
+
path += `&waiting=${opts.waiting}`;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const headers = generateHeaders(
|
|
373
|
+
this.headers,
|
|
374
|
+
this.apiSecret,
|
|
375
|
+
verb,
|
|
376
|
+
path,
|
|
377
|
+
this.apiExpiresAfter
|
|
378
|
+
);
|
|
379
|
+
return createRequest(verb, `${this.apiUrl}${path}`, headers);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Make a withdrawal request
|
|
384
|
+
* @param {string} currency - The currency to withdrawal
|
|
385
|
+
* @param {number} amount - The amount of currency to withdrawal
|
|
386
|
+
* @param {string} address - The recipient's wallet address
|
|
387
|
+
* @param {object} opts - Optional parameters.
|
|
388
|
+
* @param {string} opts.network - Crypto network of currency being withdrawn.
|
|
389
|
+
* @param {string} opts.otpCode - Otp code for user if otp is enabled.
|
|
390
|
+
* @return {object} A JSON object {message:"Success"}
|
|
391
|
+
*/
|
|
392
|
+
requestWithdrawal(currency, amount, address, opts = {
|
|
393
|
+
network: null,
|
|
394
|
+
otpCode: null
|
|
395
|
+
}) {
|
|
396
|
+
const verb = 'POST';
|
|
397
|
+
const path = `${this.baseUrl}/user/request-withdrawal`;
|
|
398
|
+
const data = {
|
|
399
|
+
currency,
|
|
400
|
+
amount,
|
|
401
|
+
address
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
if (opts.network) {
|
|
405
|
+
data.otp_code = opts.otpCode;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (opts.network) {
|
|
409
|
+
data.network = opts.network;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const headers = generateHeaders(
|
|
413
|
+
this.headers,
|
|
414
|
+
this.apiSecret,
|
|
415
|
+
verb,
|
|
416
|
+
path,
|
|
417
|
+
this.apiExpiresAfter,
|
|
418
|
+
data
|
|
419
|
+
);
|
|
420
|
+
return createRequest(verb, `${this.apiUrl}${path}`, headers, { data });
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Retrieve list of the user's completed trades
|
|
425
|
+
* @param {object} opts - Optional parameters
|
|
426
|
+
* @param {string} opts.symbol - The symbol-pair to filter by, pass undefined to receive data on all currencies
|
|
427
|
+
* @param {number} opts.limit - Amount of trades per page
|
|
428
|
+
* @param {number} opts.page - Page of trades data
|
|
429
|
+
* @param {string} opts.orderBy - The field to order data by e.g. amount, id. Default: id
|
|
430
|
+
* @param {string} opts.order - Ascending (asc) or descending (desc). Default: desc
|
|
431
|
+
* @param {string} opts.startDate - Start date of query in ISO8601 format
|
|
432
|
+
* @param {string} opts.endDate - End date of query in ISO8601 format
|
|
433
|
+
* @param {string} opts.format - Custom format of data set. Enum: ['all', 'csv']
|
|
434
|
+
* @return {object} A JSON object with the keys count(total number of user's completed trades) and data(array of up to the user's last 50 completed trades as objects with keys side(string), symbol(string), size(number), price(number), timestamp(string), and fee(number))
|
|
435
|
+
*/
|
|
436
|
+
getUserTrades(
|
|
437
|
+
opts = {
|
|
438
|
+
symbol: null,
|
|
439
|
+
limit: null,
|
|
440
|
+
page: null,
|
|
441
|
+
orderBy: null,
|
|
442
|
+
order: null,
|
|
443
|
+
startDate: null,
|
|
444
|
+
endDate: null,
|
|
445
|
+
format: null
|
|
446
|
+
}
|
|
447
|
+
) {
|
|
448
|
+
const verb = 'GET';
|
|
449
|
+
let path = `${this.baseUrl}/user/trades?`;
|
|
450
|
+
|
|
451
|
+
if (isString(opts.symbol)) {
|
|
452
|
+
path += `&symbol=${opts.symbol}`;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (isNumber(opts.limit)) {
|
|
456
|
+
path += `&limit=${opts.limit}`;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (isNumber(opts.page)) {
|
|
460
|
+
path += `&page=${opts.page}`;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
if (isString(opts.orderBy)) {
|
|
464
|
+
path += `&order_by=${opts.orderBy}`;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (isString(opts.order)) {
|
|
468
|
+
path += `&order=${opts.order}`;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (isDatetime(opts.startDate)) {
|
|
472
|
+
path += `&start_date=${sanitizeDate(opts.startDate)}`;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (isDatetime(opts.endDate)) {
|
|
476
|
+
path += `&end_date=${sanitizeDate(opts.endDate)}`;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (isString(opts.format)) {
|
|
480
|
+
path += `&format=${opts.format}`;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const headers = generateHeaders(
|
|
484
|
+
this.headers,
|
|
485
|
+
this.apiSecret,
|
|
486
|
+
verb,
|
|
487
|
+
path,
|
|
488
|
+
this.apiExpiresAfter
|
|
489
|
+
);
|
|
490
|
+
|
|
491
|
+
return createRequest(verb, `${this.apiUrl}${path}`, headers);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/****** Orders ******/
|
|
495
|
+
/**
|
|
496
|
+
* Retrieve information of a user's specific order
|
|
497
|
+
* @param {string} orderId - The id of the desired order
|
|
498
|
+
* @return {object} The selected order as a JSON object with keys created_at(string), title(string), symbol(string), side(string), size(number), type(string), price(number), id(string), created_by(number), filled(number)
|
|
499
|
+
*/
|
|
500
|
+
getOrder(orderId) {
|
|
501
|
+
const verb = 'GET';
|
|
502
|
+
const path = `${this.baseUrl}/order?order_id=${orderId}`;
|
|
503
|
+
const headers = generateHeaders(
|
|
504
|
+
this.headers,
|
|
505
|
+
this.apiSecret,
|
|
506
|
+
verb,
|
|
507
|
+
path,
|
|
508
|
+
this.apiExpiresAfter
|
|
509
|
+
);
|
|
510
|
+
return createRequest(verb, `${this.apiUrl}${path}`, headers);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Retrieve information of all the user's active orders
|
|
515
|
+
* @param {object} opts - Optional parameters
|
|
516
|
+
* @param {string} opts.symbol - The currency pair symbol to filter by e.g. 'hex-usdt', leave empty to retrieve information of orders of all symbols
|
|
517
|
+
* @param {number} opts.limit - Amount of trades per page. Maximum: 50. Default: 50
|
|
518
|
+
* @param {number} opts.page - Page of trades data. Default: 1
|
|
519
|
+
* @param {string} opts.orderBy - The field to order data by e.g. amount, id.
|
|
520
|
+
* @param {string} opts.order - Ascending (asc) or descending (desc).
|
|
521
|
+
* @param {string} opts.startDate - Start date of query in ISO8601 format.
|
|
522
|
+
* @param {string} opts.endDate - End date of query in ISO8601 format.
|
|
523
|
+
* @return {object} A JSON array of objects containing the user's active orders
|
|
524
|
+
*/
|
|
525
|
+
getOrders(
|
|
526
|
+
opts = {
|
|
527
|
+
symbol: null,
|
|
528
|
+
side: null,
|
|
529
|
+
status: null,
|
|
530
|
+
open: null,
|
|
531
|
+
limit: null,
|
|
532
|
+
page: null,
|
|
533
|
+
orderBy: null,
|
|
534
|
+
order: null,
|
|
535
|
+
startDate: null,
|
|
536
|
+
endDate: null
|
|
537
|
+
}
|
|
538
|
+
) {
|
|
539
|
+
const verb = 'GET';
|
|
540
|
+
let path = `${this.baseUrl}/orders?`;
|
|
541
|
+
|
|
542
|
+
if (isString(opts.symbol)) {
|
|
543
|
+
path += `&symbol=${opts.symbol}`;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (isString(opts.side) && (opts.side.toLowerCase() === 'buy' || opts.side.toLowerCase() === 'sell')) {
|
|
547
|
+
path += `&side=${opts.side}`;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if (isString(opts.status)) {
|
|
551
|
+
path += `&status=${opts.status}`;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
if (isBoolean(opts.open)) {
|
|
555
|
+
path += `&open=${opts.open}`;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
if (isNumber(opts.limit)) {
|
|
559
|
+
path += `&limit=${opts.limit}`;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
if (isNumber(opts.page)) {
|
|
563
|
+
path += `&page=${opts.page}`;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
if (isString(opts.orderBy)) {
|
|
567
|
+
path += `&order_by=${opts.orderBy}`;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (isString(opts.order)) {
|
|
571
|
+
path += `&order=${opts.order}`;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
if (isDatetime(opts.startDate)) {
|
|
575
|
+
path += `&start_date=${sanitizeDate(opts.startDate)}`;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
if (isDatetime(opts.endDate)) {
|
|
579
|
+
path += `&end_date=${sanitizeDate(opts.endDate)}`;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const headers = generateHeaders(
|
|
583
|
+
this.headers,
|
|
584
|
+
this.apiSecret,
|
|
585
|
+
verb,
|
|
586
|
+
path,
|
|
587
|
+
this.apiExpiresAfter
|
|
588
|
+
);
|
|
589
|
+
return createRequest(verb, `${this.apiUrl}${path}`, headers);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Create a new order
|
|
594
|
+
* @param {string} symbol - The currency pair symbol e.g. 'hex-usdt'
|
|
595
|
+
* @param {string} side - The side of the order e.g. 'buy', 'sell'
|
|
596
|
+
* @param {number} size - The amount of currency to order
|
|
597
|
+
* @param {string} type - The type of order to create e.g. 'market', 'limit'
|
|
598
|
+
* @param {number} price - The price at which to order (only required if type is 'limit')
|
|
599
|
+
* @param {object} opts - Optional parameters
|
|
600
|
+
* @param {number} opts.stop - Stop order price
|
|
601
|
+
* @param {object} opts.meta - Additional meta parameters in an object
|
|
602
|
+
* @param {boolean} opts.meta.post_only - Whether or not the order should only be made if market maker.
|
|
603
|
+
* @param {string} opts.meta.note - Additional note to add to order data.
|
|
604
|
+
* @return {object} The new order as a JSON object with keys symbol(string), side(string), size(number), type(string), price(number), id(string), created_by(number), and filled(number)
|
|
605
|
+
*/
|
|
606
|
+
createOrder(
|
|
607
|
+
symbol,
|
|
608
|
+
side,
|
|
609
|
+
size,
|
|
610
|
+
type,
|
|
611
|
+
price = 0,
|
|
612
|
+
opts = {
|
|
613
|
+
stop: null,
|
|
614
|
+
meta: null
|
|
615
|
+
}
|
|
616
|
+
) {
|
|
617
|
+
const verb = 'POST';
|
|
618
|
+
const path = `${this.baseUrl}/order`;
|
|
619
|
+
const data = { symbol, side, size, type, price };
|
|
620
|
+
|
|
621
|
+
if (isPlainObject(opts.meta)) {
|
|
622
|
+
data.meta = opts.meta;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
if (isNumber(opts.stop)) {
|
|
626
|
+
data.stop = opts.stop;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
const headers = generateHeaders(
|
|
630
|
+
this.headers,
|
|
631
|
+
this.apiSecret,
|
|
632
|
+
verb,
|
|
633
|
+
path,
|
|
634
|
+
this.apiExpiresAfter,
|
|
635
|
+
data
|
|
636
|
+
);
|
|
637
|
+
return createRequest(verb, `${this.apiUrl}${path}`, headers, { data });
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Cancel a user's specific order
|
|
642
|
+
* @param {string} orderId - The id of the order to be cancelled
|
|
643
|
+
* @return {object} The cancelled order as a JSON object with keys symbol(string), side(string), size(number), type(string), price(number), id(string), created_by(number), and filled(number)
|
|
644
|
+
*/
|
|
645
|
+
cancelOrder(orderId) {
|
|
646
|
+
const verb = 'DELETE';
|
|
647
|
+
const path = `${this.baseUrl}/order?order_id=${orderId}`;
|
|
648
|
+
const headers = generateHeaders(
|
|
649
|
+
this.headers,
|
|
650
|
+
this.apiSecret,
|
|
651
|
+
verb,
|
|
652
|
+
path,
|
|
653
|
+
this.apiExpiresAfter
|
|
654
|
+
);
|
|
655
|
+
return createRequest(verb, `${this.apiUrl}${path}`, headers);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Cancel all the user's active orders, can filter by currency pair symbol
|
|
660
|
+
* @param {object} opts - Optional parameters
|
|
661
|
+
* @param {string} opts.symbol - The currency pair symbol to filter by e.g. 'hex-usdt', leave empty to cancel orders of all symbols
|
|
662
|
+
* @return {array} A JSON array of objects containing the cancelled orders
|
|
663
|
+
*/
|
|
664
|
+
cancelAllOrders(opts = { symbol: null }) {
|
|
665
|
+
const verb = 'DELETE';
|
|
666
|
+
let path = `${this.baseUrl}/order/all`;
|
|
667
|
+
|
|
668
|
+
if (isString(opts.symbol)) {
|
|
669
|
+
path += `?symbol=${opts.symbol}`;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
const headers = generateHeaders(
|
|
673
|
+
this.headers,
|
|
674
|
+
this.apiSecret,
|
|
675
|
+
verb,
|
|
676
|
+
path,
|
|
677
|
+
this.apiExpiresAfter
|
|
678
|
+
);
|
|
679
|
+
return createRequest(verb, `${this.apiUrl}${path}`, headers);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Connect to hollaEx websocket and listen to an event
|
|
684
|
+
* @param {array} events - The events to listen to
|
|
685
|
+
*/
|
|
686
|
+
connect(events = []) {
|
|
687
|
+
this.wsReconnect = true;
|
|
688
|
+
this.wsEvents = events;
|
|
689
|
+
this.initialConnection = true;
|
|
690
|
+
let url = this.wsUrl;
|
|
691
|
+
if (this.apiKey && this.apiSecret) {
|
|
692
|
+
const apiExpires = moment().unix() + this.apiExpiresAfter;
|
|
693
|
+
const signature = createSignature(
|
|
694
|
+
this.apiSecret,
|
|
695
|
+
'CONNECT',
|
|
696
|
+
'/stream',
|
|
697
|
+
apiExpires
|
|
698
|
+
);
|
|
699
|
+
url = `${url}?api-key=${
|
|
700
|
+
this.apiKey
|
|
701
|
+
}&api-signature=${signature}&api-expires=${apiExpires}`;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
this.ws = new WebSocket(url);
|
|
705
|
+
|
|
706
|
+
if (this.wsEventListeners) {
|
|
707
|
+
this.ws._events = this.wsEventListeners;
|
|
708
|
+
} else {
|
|
709
|
+
this.ws.on('unexpected-response', () => {
|
|
710
|
+
if (this.ws.readyState !== WebSocket.CLOSING) {
|
|
711
|
+
if (this.ws.readyState === WebSocket.OPEN) {
|
|
712
|
+
this.ws.close();
|
|
713
|
+
} else if (this.wsReconnect) {
|
|
714
|
+
this.wsEventListeners = this.ws._events;
|
|
715
|
+
this.ws = null;
|
|
716
|
+
setTimeout(() => {
|
|
717
|
+
this.connect(this.wsEvents);
|
|
718
|
+
}, this.wsReconnectInterval);
|
|
719
|
+
} else {
|
|
720
|
+
this.wsEventListeners = null;
|
|
721
|
+
this.ws = null;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
this.ws.on('error', () => {
|
|
727
|
+
if (this.ws.readyState !== WebSocket.CLOSING) {
|
|
728
|
+
if (this.ws.readyState === WebSocket.OPEN) {
|
|
729
|
+
this.ws.close();
|
|
730
|
+
} else if (this.wsReconnect) {
|
|
731
|
+
this.wsEventListeners = this.ws._events;
|
|
732
|
+
this.ws = null;
|
|
733
|
+
setTimeout(() => {
|
|
734
|
+
this.connect(this.wsEvents);
|
|
735
|
+
}, this.wsReconnectInterval);
|
|
736
|
+
} else {
|
|
737
|
+
this.wsEventListeners = null;
|
|
738
|
+
this.ws = null;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
this.ws.on('close', () => {
|
|
744
|
+
if (this.wsReconnect) {
|
|
745
|
+
this.wsEventListeners = this.ws._events;
|
|
746
|
+
this.ws = null;
|
|
747
|
+
setTimeout(() => {
|
|
748
|
+
this.connect(this.wsEvents);
|
|
749
|
+
}, this.wsReconnectInterval);
|
|
750
|
+
} else {
|
|
751
|
+
this.wsEventListeners = null;
|
|
752
|
+
this.ws = null;
|
|
753
|
+
}
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
this.ws.on('open', () => {
|
|
757
|
+
if (this.wsEvents.length > 0) {
|
|
758
|
+
this.subscribe(this.wsEvents);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
this.initialConnection = false;
|
|
762
|
+
|
|
763
|
+
setWsHeartbeat(this.ws, JSON.stringify({ op: 'ping' }), {
|
|
764
|
+
pingTimeout: 60000,
|
|
765
|
+
pingInterval: 25000
|
|
766
|
+
});
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* Disconnect from hollaEx websocket
|
|
773
|
+
*/
|
|
774
|
+
disconnect() {
|
|
775
|
+
if (this.wsConnected()) {
|
|
776
|
+
this.wsReconnect = false;
|
|
777
|
+
this.ws.close();
|
|
778
|
+
} else {
|
|
779
|
+
throw new Error('Websocket not connected');
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* Subscribe to hollaEx websocket events
|
|
785
|
+
* @param {array} events - The events to listen to
|
|
786
|
+
*/
|
|
787
|
+
subscribe(events = []) {
|
|
788
|
+
if (this.wsConnected()) {
|
|
789
|
+
each(events, (event) => {
|
|
790
|
+
if (!this.wsEvents.includes(event) || this.initialConnection) {
|
|
791
|
+
const [topic, symbol] = event.split(':');
|
|
792
|
+
switch (topic) {
|
|
793
|
+
case 'orderbook':
|
|
794
|
+
case 'trade':
|
|
795
|
+
if (symbol) {
|
|
796
|
+
if (!this.wsEvents.includes(topic)) {
|
|
797
|
+
this.ws.send(
|
|
798
|
+
JSON.stringify({
|
|
799
|
+
op: 'subscribe',
|
|
800
|
+
args: [`${topic}:${symbol}`]
|
|
801
|
+
})
|
|
802
|
+
);
|
|
803
|
+
if (!this.initialConnection) {
|
|
804
|
+
this.wsEvents = union(this.wsEvents, [event]);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
} else {
|
|
808
|
+
this.ws.send(
|
|
809
|
+
JSON.stringify({
|
|
810
|
+
op: 'subscribe',
|
|
811
|
+
args: [topic]
|
|
812
|
+
})
|
|
813
|
+
);
|
|
814
|
+
if (!this.initialConnection) {
|
|
815
|
+
this.wsEvents = this.wsEvents.filter(
|
|
816
|
+
(e) => !e.includes(`${topic}:`)
|
|
817
|
+
);
|
|
818
|
+
this.wsEvents = union(this.wsEvents, [event]);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
break;
|
|
822
|
+
case 'order':
|
|
823
|
+
case 'wallet':
|
|
824
|
+
case 'deposit':
|
|
825
|
+
this.ws.send(
|
|
826
|
+
JSON.stringify({
|
|
827
|
+
op: 'subscribe',
|
|
828
|
+
args: [topic]
|
|
829
|
+
})
|
|
830
|
+
);
|
|
831
|
+
if (!this.initialConnection) {
|
|
832
|
+
this.wsEvents = union(this.wsEvents, [event]);
|
|
833
|
+
}
|
|
834
|
+
break;
|
|
835
|
+
default:
|
|
836
|
+
break;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
} else {
|
|
841
|
+
throw new Error('Websocket not connected');
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* Unsubscribe to hollaEx websocket events
|
|
847
|
+
* @param {array} events - The events to unsub from
|
|
848
|
+
*/
|
|
849
|
+
unsubscribe(events = []) {
|
|
850
|
+
if (this.wsConnected()) {
|
|
851
|
+
each(events, (event) => {
|
|
852
|
+
if (this.wsEvents.includes(event)) {
|
|
853
|
+
const [topic, symbol] = event.split(':');
|
|
854
|
+
switch (topic) {
|
|
855
|
+
case 'orderbook':
|
|
856
|
+
case 'trade':
|
|
857
|
+
if (symbol) {
|
|
858
|
+
this.ws.send(
|
|
859
|
+
JSON.stringify({
|
|
860
|
+
op: 'unsubscribe',
|
|
861
|
+
args: [`${topic}:${symbol}`]
|
|
862
|
+
})
|
|
863
|
+
);
|
|
864
|
+
} else {
|
|
865
|
+
this.ws.send(
|
|
866
|
+
JSON.stringify({
|
|
867
|
+
op: 'unsubscribe',
|
|
868
|
+
args: [topic]
|
|
869
|
+
})
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
this.wsEvents = this.wsEvents.filter((e) => e !== event);
|
|
873
|
+
break;
|
|
874
|
+
case 'order':
|
|
875
|
+
case 'wallet':
|
|
876
|
+
case 'deposit':
|
|
877
|
+
this.ws.send(
|
|
878
|
+
JSON.stringify({
|
|
879
|
+
op: 'unsubscribe',
|
|
880
|
+
args: [topic]
|
|
881
|
+
})
|
|
882
|
+
);
|
|
883
|
+
this.wsEvents = this.wsEvents.filter((e) => e !== event);
|
|
884
|
+
break;
|
|
885
|
+
default:
|
|
886
|
+
break;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
});
|
|
890
|
+
} else {
|
|
891
|
+
throw new Error('Websocket not connected');
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
module.exports = HollaExKit;
|