@icgio/icg-exchanges-wrapper 1.8.6
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 +1 -0
- package/functions/get-exchanges-balance.js +32 -0
- package/functions/get-exchanges-open-orders.js +0 -0
- package/functions/get-exchanges-rates-market-history.js +113 -0
- package/functions/get-exchanges-trades.js +0 -0
- package/index.js +16 -0
- package/middleware/account.js +102 -0
- package/middleware/orders.js +1024 -0
- package/middleware/websocket.js +69 -0
- package/package.json +29 -0
|
@@ -0,0 +1,1024 @@
|
|
|
1
|
+
const _ = require('lodash')
|
|
2
|
+
|
|
3
|
+
const uuidv4 = require('uuid/v4')
|
|
4
|
+
const Influx = require('influx')
|
|
5
|
+
|
|
6
|
+
const icg_exchanges = require('@icgio/icg-exchanges')
|
|
7
|
+
const utils = require('@icgio/icg-utils').utils
|
|
8
|
+
const get_coinmarketcap_rates = require('@icgio/icg-utils').coinmarketcap
|
|
9
|
+
|
|
10
|
+
const { round_price, round_amount } = require('@icgio/icg-exchanges-data')
|
|
11
|
+
|
|
12
|
+
const Account = require('./account.js')
|
|
13
|
+
|
|
14
|
+
const limits = {
|
|
15
|
+
public_max_concurrent: 10,
|
|
16
|
+
public_interval_concurrent: 1000,
|
|
17
|
+
private_max_concurrent: 5,
|
|
18
|
+
private_interval_concurrent: 1000
|
|
19
|
+
}
|
|
20
|
+
const small_limits = {
|
|
21
|
+
public_max_concurrent: 5,
|
|
22
|
+
public_interval_concurrent: 1000,
|
|
23
|
+
private_max_concurrent: 3,
|
|
24
|
+
private_interval_concurrent: 1000
|
|
25
|
+
}
|
|
26
|
+
const mimimum_limits = {
|
|
27
|
+
public_max_concurrent: 2,
|
|
28
|
+
public_interval_concurrent: 1000,
|
|
29
|
+
private_max_concurrent: 1,
|
|
30
|
+
private_interval_concurrent: 3000
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const rates_length = 10
|
|
34
|
+
|
|
35
|
+
module.exports = class Orders {
|
|
36
|
+
constructor (exchange_basecur_curs, cd, settings) {
|
|
37
|
+
this.exchange_basecur_curs = exchange_basecur_curs
|
|
38
|
+
let { exchange_pairs, exchange_curs, pair_exchanges } = utils.exchange_basecur_curs_converter(exchange_basecur_curs)
|
|
39
|
+
this.exchange_pairs = exchange_pairs
|
|
40
|
+
this.pair_exchanges = pair_exchanges
|
|
41
|
+
this.Exchanges = {}
|
|
42
|
+
this.settings = settings || {}
|
|
43
|
+
this.ws_enabled = settings.ws_enabled || false
|
|
44
|
+
this.interval_dict = settings.interval_dict || {}
|
|
45
|
+
this.influx_url_check_volume = settings.influx_url_check_volume
|
|
46
|
+
this.check_init_order_opened = settings.check_init_order_opened || true
|
|
47
|
+
this.check_init_order_opened_timeout = settings.check_init_order_opened_timeout || 5 * 1000
|
|
48
|
+
this.cancel_previous_orders = settings.cancel_previous_orders || false
|
|
49
|
+
this.cancel_range_orders = settings.cancel_range_orders || false
|
|
50
|
+
this.delete_finished_orders_timeout = settings.delete_finished_orders_timeout || 30 * 1000 // CANCELED or F-FILLED
|
|
51
|
+
this.interval_dict = settings.interval_dict || {}
|
|
52
|
+
this.major_exchanges = settings.major_exchanges || []
|
|
53
|
+
this.no_cancel_range_exchanges = settings.no_cancel_range_exchanges || []
|
|
54
|
+
this.no_trades_function_exchanges = settings.no_trades_function_exchanges || []
|
|
55
|
+
this.no_trades_function_f_filled_timeout = settings.no_trades_function_f_filled_timeout || 30 * 1000
|
|
56
|
+
this.bad_trades_function_exchanges = settings.bad_trades_function_exchanges || []
|
|
57
|
+
this.buy_sell_limits_threshold = settings.buy_sell_limits_threshold || 0.95
|
|
58
|
+
this.p_filled_threshold = settings.p_filled_threshold || 0
|
|
59
|
+
this.f_filled_threshold = settings.f_filled_threshold || 0.95
|
|
60
|
+
this.previous_orders_canceled = false
|
|
61
|
+
this.previous_orders_canceled_exchange_pair = {}
|
|
62
|
+
this.init_account_from_first_balances = settings.init_account_from_first_balances || true
|
|
63
|
+
this.account_init = false
|
|
64
|
+
for (let exchange in exchange_pairs) {
|
|
65
|
+
if (exchange === 'bitfinex') {
|
|
66
|
+
this.Exchanges[exchange] = new icg_exchanges[_.upperFirst(exchange)](cd[exchange]['api_keys'], cd[exchange]['secret_keys'], _.assign(small_limits, cd[exchange]))
|
|
67
|
+
} else if (exchange === 'okex' || exchange === 'bgogo') {
|
|
68
|
+
this.Exchanges[exchange] = new icg_exchanges[_.upperFirst(exchange)](cd[exchange]['api_key'], cd[exchange]['secret_key'], _.assign(small_limits, cd[exchange]))
|
|
69
|
+
} else if (exchange === 'bitforex' || exchange === 'bitkub' || exchange === 'coinbene') {
|
|
70
|
+
this.Exchanges[exchange] = new icg_exchanges[_.upperFirst(exchange)](cd[exchange]['api_key'], cd[exchange]['secret_key'], _.assign(mimimum_limits, cd[exchange]))
|
|
71
|
+
} else if (cd[exchange]['api_keys'] || exchange === 'kucoin') {
|
|
72
|
+
this.Exchanges[exchange] = new icg_exchanges[_.upperFirst(exchange)](cd[exchange]['api_keys'], cd[exchange]['secret_keys'], _.assign(small_limits, cd[exchange]))
|
|
73
|
+
} else if (cd[exchange]['api_keys']) {
|
|
74
|
+
this.Exchanges[exchange] = new icg_exchanges[_.upperFirst(exchange)](cd[exchange]['api_keys'], cd[exchange]['secret_keys'], _.assign(limits, cd[exchange]))
|
|
75
|
+
} else {
|
|
76
|
+
this.Exchanges[exchange] = new icg_exchanges[_.upperFirst(exchange)](cd[exchange]['api_key'], cd[exchange]['secret_key'], _.assign(limits, cd[exchange]))
|
|
77
|
+
}
|
|
78
|
+
if (!this.interval_dict || !this.interval_dict[exchange]) {
|
|
79
|
+
this.interval_dict[exchange] = {
|
|
80
|
+
'balances': 10 * 1000,
|
|
81
|
+
'positions': 10 * 1000,
|
|
82
|
+
'open_orders': 10 * 1000,
|
|
83
|
+
'trades': 10 * 1000,
|
|
84
|
+
'rates': 1 * 1000,
|
|
85
|
+
'market_history': 2 * 1000,
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
this.interval_dict[exchange].balances = this.interval_dict[exchange].balances || 10 * 1000
|
|
89
|
+
this.interval_dict[exchange].positions = this.interval_dict[exchange].positions || 10 * 1000
|
|
90
|
+
this.interval_dict[exchange].open_orders = this.interval_dict[exchange].open_orders || 10 * 1000
|
|
91
|
+
this.interval_dict[exchange].trades = this.interval_dict[exchange].trades || 10 * 1000
|
|
92
|
+
this.interval_dict[exchange].rates = this.interval_dict[exchange].rates || 1 * 1000
|
|
93
|
+
this.interval_dict[exchange].market_history = this.interval_dict[exchange].market_history || 2 * 1000
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
this.balances = {}, this.open_orders = {}, this.trades = {}, this.rates = {}, this.market_history = {}, this.volumes = {}, this.cmc_rates = {}
|
|
97
|
+
this.rates_combined_all = {}, this.rates_combined_all_with_buy_sell_limits = {}, this.rates_combined_major = {}, this.rates_combined_good_with_buy_sell_limits = {}
|
|
98
|
+
this.buy_sell_limits = {}
|
|
99
|
+
this.order_info = {}
|
|
100
|
+
this.order_info_status = {}
|
|
101
|
+
this.enabled = settings.enabled || {
|
|
102
|
+
'info_log': true,
|
|
103
|
+
'error_log': true,
|
|
104
|
+
'balances_check': true,
|
|
105
|
+
'positions_check': false,
|
|
106
|
+
'open_orders_check': true,
|
|
107
|
+
'trades_check': true,
|
|
108
|
+
'rates_check': true,
|
|
109
|
+
'market_history_check': true,
|
|
110
|
+
'volumes_check': false,
|
|
111
|
+
'cmc_rates_check': true
|
|
112
|
+
}
|
|
113
|
+
this.info_log = (...args) => {
|
|
114
|
+
if (this.enabled.info_log)
|
|
115
|
+
console.log(...args)
|
|
116
|
+
}
|
|
117
|
+
this.error_log = (...args) => {
|
|
118
|
+
if (this.enabled.info_log)
|
|
119
|
+
console.error(...args)
|
|
120
|
+
}
|
|
121
|
+
if (this.enabled.balances_check) {
|
|
122
|
+
this.check_balances()
|
|
123
|
+
}
|
|
124
|
+
if (this.enabled.positions_check) {
|
|
125
|
+
this.check_positions()
|
|
126
|
+
}
|
|
127
|
+
if (this.enabled.open_orders_check) {
|
|
128
|
+
this.check_open_orders()
|
|
129
|
+
}
|
|
130
|
+
if (this.enabled.trades_check) {
|
|
131
|
+
this.trades_amount_past_hour = {}
|
|
132
|
+
this.check_trades()
|
|
133
|
+
}
|
|
134
|
+
if (this.enabled.rates_check) {
|
|
135
|
+
this.check_rates()
|
|
136
|
+
}
|
|
137
|
+
if (this.enabled.market_history_check) {
|
|
138
|
+
this.check_market_history()
|
|
139
|
+
}
|
|
140
|
+
if (this.enabled.volumes_check) {
|
|
141
|
+
this.check_volumes()
|
|
142
|
+
}
|
|
143
|
+
if (this.enabled.cmc_rates_check) {
|
|
144
|
+
this.check_cmc_rates()
|
|
145
|
+
}
|
|
146
|
+
if (this.enabled.balances_check && this.enabled.open_orders_check && this.enabled.trades_check) {
|
|
147
|
+
setInterval(() => {
|
|
148
|
+
this.process_bot(this.balances, this.open_orders, this.trades)
|
|
149
|
+
}, 1000)
|
|
150
|
+
}
|
|
151
|
+
this.ws_market_first_time = {}
|
|
152
|
+
}
|
|
153
|
+
init_account (initial_balances) {
|
|
154
|
+
this.account = new Account(initial_balances)
|
|
155
|
+
}
|
|
156
|
+
set_exchange_assets_from_balances (balances) {
|
|
157
|
+
this.account.set_exchange_assets_from_balances(balances)
|
|
158
|
+
}
|
|
159
|
+
limit_trade (exchange, pair, method, price, amount, id=uuidv4().slice(0, 13), order_type='', limiter_on=true) {
|
|
160
|
+
price = round_price(_.upperFirst(exchange), pair, price)
|
|
161
|
+
amount = round_amount(_.upperFirst(exchange), pair, amount)
|
|
162
|
+
this.info_log('%s %s %s %s %s %s at %s', limiter_on, order_type, exchange, pair, method, amount, price)
|
|
163
|
+
this.update_order_info(exchange, pair, method, id, {
|
|
164
|
+
order_type,
|
|
165
|
+
status: 'INIT',
|
|
166
|
+
id,
|
|
167
|
+
order_id: id,
|
|
168
|
+
type: method,
|
|
169
|
+
price,
|
|
170
|
+
amount,
|
|
171
|
+
f_amount: 0,
|
|
172
|
+
f_percentage: 0,
|
|
173
|
+
t_amount: amount,
|
|
174
|
+
open_time: new Date(),
|
|
175
|
+
close_time: null
|
|
176
|
+
}, order_type + ' init')
|
|
177
|
+
let no_callback_timeout = setTimeout(() => {
|
|
178
|
+
if (_.get(this.order_info, [exchange, pair, method, id, 'status']) === 'INIT') {
|
|
179
|
+
console.log('no callback: %s %s %s %s %s', exchange, pair, method, id, order_type)
|
|
180
|
+
this.update_order_info(exchange, pair, method, id, {
|
|
181
|
+
status: 'N-OPENED-TIMEOUT'
|
|
182
|
+
}, order_type + ' n-opened-timeout-init')
|
|
183
|
+
}
|
|
184
|
+
}, 60 * 1000)
|
|
185
|
+
let limit_trade = (cb) => {
|
|
186
|
+
if (limiter_on) {
|
|
187
|
+
this.Exchanges[exchange].limit_trade(pair, method, price, amount, cb)
|
|
188
|
+
} else {
|
|
189
|
+
this.Exchanges[exchange].limit_trade_base(pair, method, price, amount, cb)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
limit_trade((res) => {
|
|
193
|
+
if (res.success) {
|
|
194
|
+
let order_id = res.body
|
|
195
|
+
this.delete_order_info(exchange, pair, method, id, 'limit-trade-success')
|
|
196
|
+
this.update_order_info(exchange, pair, method, order_id, {
|
|
197
|
+
order_type,
|
|
198
|
+
status: 'N-FILLED',
|
|
199
|
+
id,
|
|
200
|
+
order_id,
|
|
201
|
+
type: method,
|
|
202
|
+
price,
|
|
203
|
+
amount,
|
|
204
|
+
f_amount: 0,
|
|
205
|
+
f_percentage: 0,
|
|
206
|
+
t_amount: amount,
|
|
207
|
+
open_time: new Date(),
|
|
208
|
+
close_time: null
|
|
209
|
+
}, order_type + ' n-filled')
|
|
210
|
+
} else if (res.error === 'amount-too-small' || res.error === 'insufficient-funds') {
|
|
211
|
+
this.delete_order_info(exchange, pair, method, id, 'limit-trade-' + res.error)
|
|
212
|
+
} else {
|
|
213
|
+
if (order_type.startsWith('self') || order_type.startsWith('maker_2')) {
|
|
214
|
+
this.delete_order_info(exchange, pair, method, id, 'limit-trade-timeout-' + order_type)
|
|
215
|
+
}
|
|
216
|
+
// else if (_.get(this.order_info, [exchange, pair, method, id, 'status']) === 'INIT') { // make sure no_callback_timeout is NOT running zero
|
|
217
|
+
// this.update_order_info(exchange, pair, method, id, {
|
|
218
|
+
// status: 'N-OPENED-TIMEOUT'
|
|
219
|
+
// }, order_type + ' n-opened-timeout-callback')
|
|
220
|
+
// }
|
|
221
|
+
this.error_log('limit trade error: %s %s %s %s %s %s %s %j', order_type, id, exchange, pair, method, price, amount, res)
|
|
222
|
+
}
|
|
223
|
+
clearTimeout(no_callback_timeout)
|
|
224
|
+
})
|
|
225
|
+
}
|
|
226
|
+
limit_trade_all (pair, method, price, total) {
|
|
227
|
+
if (!this.volumes[pair]) {
|
|
228
|
+
this.error_log('%s volumes do not exist')
|
|
229
|
+
return
|
|
230
|
+
}
|
|
231
|
+
let id = uuidv4().slice(0, 13)
|
|
232
|
+
let sum = _.sum(_.values(this.volumes[pair]))
|
|
233
|
+
for (let exchange of this.pair_exchanges[pair]) {
|
|
234
|
+
let amount = this.volumes[pair][exchange] / sum * total
|
|
235
|
+
this.limit_trade(exchange, pair, method, price, amount, id)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
market_trade (exchange, pair, method, amount, id=uuidv4().slice(0, 13)) {
|
|
239
|
+
if (!this.rates[exchange][pair]) {
|
|
240
|
+
this.info_log('%s %s rates do not exist', exchange, pair)
|
|
241
|
+
return
|
|
242
|
+
}
|
|
243
|
+
let input_array = (method === 'sell') ? this.rates[exchange][pair].bids : this.rates[exchange][pair].asks
|
|
244
|
+
let { price } = utils.vol_position(input_array, amount)
|
|
245
|
+
this.limit_trade(exchange, pair, method, price, amount, id)
|
|
246
|
+
}
|
|
247
|
+
market_trade_all (pair, method, total) {
|
|
248
|
+
if (!this.rates_combined_all[pair]) {
|
|
249
|
+
this.error_log('%s rates combined do not exist', pair)
|
|
250
|
+
return
|
|
251
|
+
}
|
|
252
|
+
let type = method === 'buy' ? 'asks' : 'bids' // reverse because it is market order
|
|
253
|
+
let { price_exchange, vol_exchange } = utils.vol_position_combined(this.rates_combined_all[pair][type], total)
|
|
254
|
+
let id = uuidv4().slice(0, 13)
|
|
255
|
+
for (let exchange of this.pair_exchanges[pair]) {
|
|
256
|
+
if (price_exchange[exchange] && vol_exchange[exchange])
|
|
257
|
+
this.limit_trade(exchange, pair, method, price_exchange[exchange], vol_exchange[exchange], id)
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
cancel_order (exchange, pair, order) {
|
|
261
|
+
let original_status = order.status
|
|
262
|
+
this.update_order_info(exchange, pair, order.type, order.order_id, {
|
|
263
|
+
status: 'CANCELING'
|
|
264
|
+
}, order.order_type + ' canceling')
|
|
265
|
+
order.exchange = exchange, order.pair = pair
|
|
266
|
+
this.Exchanges[exchange].cancel_order_by_order(order, (res) => {
|
|
267
|
+
if (res.success || (!res.success && res.error === 'order-not-open' && original_status === 'CANCELING')) {
|
|
268
|
+
this.update_order_info(exchange, pair, order.type, order.order_id, {
|
|
269
|
+
status: 'CANCELED',
|
|
270
|
+
close_time: new Date()
|
|
271
|
+
}, order.order_type + ' canceled')
|
|
272
|
+
setTimeout(() => {
|
|
273
|
+
this.delete_order_info(exchange, pair, order.type, order.order_id, 'cancel-order-success')
|
|
274
|
+
}, this.delete_finished_orders_timeout)
|
|
275
|
+
} else if (!res.success && res.error === 'order-not-open' && original_status !== 'CANCELED') {
|
|
276
|
+
// this.info_log('%s ORDER CANNOT BE CANCELED: %j', original_status, order)
|
|
277
|
+
this.update_order_info(exchange, pair, order.type, order.order_id, {
|
|
278
|
+
status: 'F-FILLED',
|
|
279
|
+
amount: 0,
|
|
280
|
+
f_amount: order.amount,
|
|
281
|
+
f_percentage: 1,
|
|
282
|
+
close_time: new Date()
|
|
283
|
+
}, order.order_type + ' cannot-be-canceled')
|
|
284
|
+
setTimeout(() => {
|
|
285
|
+
this.delete_order_info(exchange, pair, order.type, order.order_id, 'cannot-be-canceled')
|
|
286
|
+
}, this.delete_finished_orders_timeout)
|
|
287
|
+
} else {
|
|
288
|
+
console.error('cancel order:', exchange, pair, order, res)
|
|
289
|
+
}
|
|
290
|
+
})
|
|
291
|
+
}
|
|
292
|
+
get_order_info_all () {
|
|
293
|
+
return this.order_info
|
|
294
|
+
}
|
|
295
|
+
get_order_info_all_stats () {
|
|
296
|
+
let info_all = this.get_order_info_all(), info_all_stats = {}
|
|
297
|
+
for (let exchange in info_all) {
|
|
298
|
+
for (let pair in info_all[exchange]) {
|
|
299
|
+
for (let type in info_all[exchange][pair]) {
|
|
300
|
+
_.set(info_all_stats, [exchange, pair, type], _.keys(info_all[exchange][pair][type]).length)
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return info_all_stats
|
|
305
|
+
}
|
|
306
|
+
get_order_info_by_order_type (order_type) {
|
|
307
|
+
let order_info_temp = {}
|
|
308
|
+
for (let exchange in this.order_info) {
|
|
309
|
+
for (let pair in this.order_info[exchange]) {
|
|
310
|
+
for (let type in this.order_info[exchange][pair]) {
|
|
311
|
+
for (let order_id in this.order_info[exchange][pair][type]) {
|
|
312
|
+
if (_.get(this.order_info, [exchange, pair, type, order_id, 'order_type']) === order_type) {
|
|
313
|
+
_.setWith(order_info_temp, `${exchange}.${pair}.${type}.${order_id}`, _.get(this.order_info, `${exchange}.${pair}.${type}.${order_id}`), Object)
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return order_info_temp
|
|
320
|
+
}
|
|
321
|
+
get_order_info_status_all (exchange, pair, type, status) {
|
|
322
|
+
if (type === 'both') {
|
|
323
|
+
return this.get_order_info_status_all(exchange, pair, 'sell', status).concat(this.get_order_info_status_all(exchange, pair, 'buy', status))
|
|
324
|
+
}
|
|
325
|
+
_.set(this.order_info_status, [exchange, pair, type, status], [])
|
|
326
|
+
for (let order_id in _.get(this.order_info, [exchange, pair, type], {})) {
|
|
327
|
+
let order_status = _.get(this.order_info, [exchange, pair, type, order_id, 'status'])
|
|
328
|
+
if (order_status === status || status.indexOf(order_status) > -1) {
|
|
329
|
+
this.order_info_status[exchange][pair][type][status].push(this.order_info[exchange][pair][type][order_id])
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return this.order_info_status[exchange][pair][type][status]
|
|
333
|
+
}
|
|
334
|
+
get_order_info_status_by_order_type (exchange, pair, type, status, order_type) {
|
|
335
|
+
return this.get_order_info_status_all(exchange, pair, type, status).filter(o => o.order_type === order_type)
|
|
336
|
+
}
|
|
337
|
+
process_bot (balances, open_orders, trades) {
|
|
338
|
+
for (let exchange in trades) {
|
|
339
|
+
for (let pair in trades[exchange]) {
|
|
340
|
+
trades[exchange][pair].trades.map(trade => {
|
|
341
|
+
if (_.get(this.order_info, [exchange, pair, trade.type, trade.order_id])) {
|
|
342
|
+
let order_info = this.order_info[exchange][pair][trade.type][trade.order_id]
|
|
343
|
+
if (order_info.status === 'CANCELED' || order_info.status === 'CANCELING') {
|
|
344
|
+
let type = trade.type, order_id = trade.order_id
|
|
345
|
+
// this.info_log('TRADES: %s ORDER FILLED', order_info.status, trade, order_info)
|
|
346
|
+
this.update_order_info(exchange, pair, type, order_id, {
|
|
347
|
+
status: 'F-FILLED',
|
|
348
|
+
price: trade.price,
|
|
349
|
+
amount: 0,
|
|
350
|
+
f_amount: trade.amount,
|
|
351
|
+
t_amount: trade.amount,
|
|
352
|
+
f_percentage: 1,
|
|
353
|
+
close_time: trade.close_time || new Date()
|
|
354
|
+
}, order_info.order_type + ' canceled-order-filled')
|
|
355
|
+
setTimeout(() => {
|
|
356
|
+
this.delete_order_info(exchange, pair, type, order_id, 'canceled-order-filled')
|
|
357
|
+
}, this.delete_finished_orders_timeout)
|
|
358
|
+
} else if (order_info.status === 'F-FILLED' && trade.amount > order_info.f_amount) {
|
|
359
|
+
// this.info_log('TRADES: FILLED ORDER FILLED MORE', trade, order_info)
|
|
360
|
+
this.update_order_info(exchange, pair, order_info.type, order_info.order_id, {
|
|
361
|
+
f_amount: trade.amount,
|
|
362
|
+
t_amount: trade.amount,
|
|
363
|
+
f_percentage: 1
|
|
364
|
+
}, order_info.order_type + ' filled-order-filled-more')
|
|
365
|
+
} else if (_.includes(['N-FILLED', 'P-FILLED'], order_info.status) && trade.amount > order_info.f_amount && trade.amount / order_info.t_amount > this.f_filled_threshold) {
|
|
366
|
+
// this.info_log('TRADES: %s %s ORDER F-FILLED', exchange, pair, trade)
|
|
367
|
+
this.update_order_info(exchange, pair, order_info.type, order_info.order_id, {
|
|
368
|
+
status: 'F-FILLED',
|
|
369
|
+
price: trade.price,
|
|
370
|
+
amount: order_info.t_amount - trade.amount,
|
|
371
|
+
f_amount: trade.amount,
|
|
372
|
+
f_percentage: trade.amount / order_info.t_amount,
|
|
373
|
+
close_time: trade.close_time || new Date()
|
|
374
|
+
}, order_info.order_type + ' order-f-filled-1')
|
|
375
|
+
setTimeout(() => {
|
|
376
|
+
this.delete_order_info(exchange, pair, order_info.type, order_info.order_id, 'f-filled-trades')
|
|
377
|
+
}, this.delete_finished_orders_timeout)
|
|
378
|
+
} else if (_.includes(['N-FILLED', 'P-FILLED'], order_info.status) && trade.amount > order_info.f_amount && trade.amount / order_info.t_amount > this.p_filled_threshold) {
|
|
379
|
+
// this.info_log('TRADES: %s %s ORDER P-FILLED', exchange, pair, trade)
|
|
380
|
+
this.update_order_info(exchange, pair, order_info.type, order_info.order_id, {
|
|
381
|
+
status: 'P-FILLED',
|
|
382
|
+
amount: order_info.t_amount - trade.amount,
|
|
383
|
+
f_amount: trade.amount,
|
|
384
|
+
f_percentage: trade.amount / order_info.t_amount
|
|
385
|
+
}, order_info.order_type + ' order-p-filled-1')
|
|
386
|
+
} else if (_.includes(['F-FILLED', 'P-FILLED'], order_info.status)) {
|
|
387
|
+
|
|
388
|
+
} else {
|
|
389
|
+
this.info_log('TRADES: UNKNOWN %j', trade, this.order_info[exchange][pair])
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
})
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
for (let exchange of this.no_trades_function_exchanges) {
|
|
396
|
+
for (let pair in _.get(this.order_info, [exchange], {})) {
|
|
397
|
+
for (let type in _.get(this.order_info, [exchange, pair], {})) {
|
|
398
|
+
for (let order_id in _.get(this.order_info, [exchange, pair, type], {})) {
|
|
399
|
+
let order_info_order = this.order_info[exchange][pair][type][order_id]
|
|
400
|
+
if ((order_info_order.status === 'N-FILLED' || order_info_order.status === 'P-FILLED') && order_info_order.open_time < new Date() - this.no_trades_function_f_filled_timeout) {
|
|
401
|
+
let exist = false
|
|
402
|
+
open_orders[exchange][pair].open_orders.map(o => {
|
|
403
|
+
if (o.order_id === order_info_order.order_id) {
|
|
404
|
+
exist = true
|
|
405
|
+
}
|
|
406
|
+
})
|
|
407
|
+
if (!exist) {
|
|
408
|
+
// this.info_log('ORDERS: %s %s ORDER F-FILLED NO TRADES FUNCTION EXCHANGES', exchange, pair)
|
|
409
|
+
this.update_order_info(exchange, pair, order_info_order.type, order_info_order.order_id, {
|
|
410
|
+
status: 'F-FILLED',
|
|
411
|
+
amount: 0,
|
|
412
|
+
f_amount: order_info_order.t_amount,
|
|
413
|
+
f_percentage: 1,
|
|
414
|
+
close_time: new Date()
|
|
415
|
+
}, order_info_order.order_type + ' order-f-filled-2')
|
|
416
|
+
setTimeout(() => {
|
|
417
|
+
this.delete_order_info(exchange, pair, order_info_order.type, order_info_order.order_id, 'f-filled-no-trades-function')
|
|
418
|
+
}, this.delete_finished_orders_timeout)
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
for (let exchange in open_orders) {
|
|
426
|
+
for (let pair in open_orders[exchange]) {
|
|
427
|
+
open_orders[exchange][pair].open_orders.map(order => {
|
|
428
|
+
if (_.get(this.order_info, [exchange, pair, order.type, order.order_id])) {
|
|
429
|
+
let order_info = this.order_info[exchange][pair][order.type][order.order_id]
|
|
430
|
+
if (_.includes(['N-FILLED', 'P-FILLED'], order_info.status) && order.f_amount > order_info.f_amount && order.f_amount / order_info.t_amount > this.f_filled_threshold) {
|
|
431
|
+
// this.info_log('ORDERS: %s %s ORDER F-FILLED', exchange, pair)
|
|
432
|
+
this.update_order_info(exchange, pair, order_info.type, order_info.order_id, {
|
|
433
|
+
status: 'F-FILLED',
|
|
434
|
+
amount: order.amount,
|
|
435
|
+
f_amount: order.f_amount,
|
|
436
|
+
f_percentage: order.f_amount / order_info.t_amount,
|
|
437
|
+
close_time: order.close_time || new Date()
|
|
438
|
+
}, order_info.order_type + ' order-f-filled-3')
|
|
439
|
+
setTimeout(() => {
|
|
440
|
+
this.delete_order_info(exchange, pair, order_info.type, order_info.order_id, 'f-filled-open-orders')
|
|
441
|
+
}, this.delete_finished_orders_timeout)
|
|
442
|
+
} else if (_.includes(['N-FILLED', 'P-FILLED'], order_info.status) && order.f_amount > order_info.f_amount && order.f_amount / order_info.t_amount > this.p_filled_threshold) {
|
|
443
|
+
// this.info_log('ORDERS: %s %s ORDER P-FILLED', exchange, pair)
|
|
444
|
+
this.update_order_info(exchange, pair, order_info.type, order_info.order_id, {
|
|
445
|
+
status: 'P-FILLED',
|
|
446
|
+
amount: order.amount,
|
|
447
|
+
f_amount: order.f_amount,
|
|
448
|
+
f_percentage: order.f_amount / order_info.t_amount
|
|
449
|
+
}, order_info.order_type + ' order-p-filled-2')
|
|
450
|
+
}
|
|
451
|
+
} else if (this.get_order_info_status_all(exchange, pair, 'both', 'INIT') && this.check_init_order_opened) {
|
|
452
|
+
this.get_order_info_status_all(exchange, pair, 'both', 'INIT').map(init_order => {
|
|
453
|
+
let init_order_price = round_price(exchange, pair, init_order.price)
|
|
454
|
+
let order_price = round_price(exchange, pair, order.price)
|
|
455
|
+
let init_order_time = init_order.open_time.getTime()
|
|
456
|
+
let order_time = order.open_time.getTime()
|
|
457
|
+
if (init_order.type === order.type && init_order_price === order_price && order_time - init_order_time > 0 && order_time - init_order_time < this.check_init_order_opened_timeout) {
|
|
458
|
+
// this.info_log('ORDERS: %s %s %s %s INIT ORDER OPENED', init_order.order_id, order.order_id, exchange, pair)
|
|
459
|
+
this.delete_order_info(exchange, pair, init_order.type, init_order.order_id, 'init-order-opened')
|
|
460
|
+
this.update_order_info(exchange, pair, order.type, order.order_id, {
|
|
461
|
+
status: 'N-FILLED',
|
|
462
|
+
order_id: order.order_id,
|
|
463
|
+
type: order.type,
|
|
464
|
+
price: order.price,
|
|
465
|
+
amount: order.amount,
|
|
466
|
+
f_amount: 0,
|
|
467
|
+
t_amount: order.amount,
|
|
468
|
+
open_time: order.open_time || new Date(),
|
|
469
|
+
close_time: null
|
|
470
|
+
}, init_order.order_type + ' init-order-opened')
|
|
471
|
+
}
|
|
472
|
+
})
|
|
473
|
+
}
|
|
474
|
+
})
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
update_order_info (exchange, pair, type, order_id, dict, note) {
|
|
479
|
+
if (!order_id) {
|
|
480
|
+
this.error_log('UPDATE %s: order_id invalid %s %s %s %s %j', note, exchange, pair, type, order_id, dict)
|
|
481
|
+
return
|
|
482
|
+
}
|
|
483
|
+
for (let key in dict) {
|
|
484
|
+
if (key === 'f_percentage') {
|
|
485
|
+
dict[key] = Math.round(dict[key] * 100) / 100
|
|
486
|
+
}
|
|
487
|
+
_.setWith(this.order_info, `${exchange}.${pair}.${type}.${order_id}.${key}`, dict[key], Object)
|
|
488
|
+
}
|
|
489
|
+
this.info_log('UPDATE %s: %s %s %s %s %j', note, exchange, pair, type, order_id, dict)
|
|
490
|
+
}
|
|
491
|
+
delete_order_info (exchange, pair, type, order_id, note) {
|
|
492
|
+
this.order_info[exchange][pair][type] = _.omit(this.order_info[exchange][pair][type], [order_id])
|
|
493
|
+
this.info_log('DELETE %s: %s %s %s %s', note, exchange, pair, type, order_id)
|
|
494
|
+
}
|
|
495
|
+
check_balances () {
|
|
496
|
+
let balances_handler = (exchange, res) => {
|
|
497
|
+
if (res.success) {
|
|
498
|
+
_.set(this.balances, [exchange], Object.assign(res.body, { update_time: new Date() }))
|
|
499
|
+
for (let exchange in this.exchange_basecur_curs) {
|
|
500
|
+
for (let base_cur in this.exchange_basecur_curs[exchange]) {
|
|
501
|
+
this.exchange_basecur_curs[exchange][base_cur].map(cur => {
|
|
502
|
+
let cur_balance = _.get(this.balances, [exchange, 'available', cur], 0)
|
|
503
|
+
let base_cur_balance = _.get(this.balances, [exchange, 'available', base_cur], 0)
|
|
504
|
+
let pair = cur + base_cur
|
|
505
|
+
_.set(this.buy_sell_limits, [exchange, pair], {
|
|
506
|
+
sell: cur_balance * this.buy_sell_limits_threshold,
|
|
507
|
+
buy: base_cur_balance * this.buy_sell_limits_threshold
|
|
508
|
+
})
|
|
509
|
+
})
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
if (!this.account_init && _.keys(this.balances).length === _.keys(this.exchange_pairs).length) {
|
|
513
|
+
this.account_init = true
|
|
514
|
+
this.init_account(this.balances)
|
|
515
|
+
}
|
|
516
|
+
} else if (_.get(this.balances, [exchange, 'update_time']) < new Date() - this.interval_dict[exchange].balances) {
|
|
517
|
+
this.error_log('%s balances expired: %j', exchange, res)
|
|
518
|
+
this.balances = _.omit(this.balances, [exchange])
|
|
519
|
+
this.buy_sell_limits = _.omit(this.buy_sell_limits, [exchange])
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
function get_balances (Exchanges, interval_dict) {
|
|
523
|
+
for (let exchange in Exchanges) {
|
|
524
|
+
if (Exchanges[exchange].balance_ws) {
|
|
525
|
+
setInterval(() => {
|
|
526
|
+
if (Exchanges[exchange].ws_status().trading === 'n-opened')
|
|
527
|
+
Exchanges[exchange].ws_account()
|
|
528
|
+
Exchanges[exchange].balance_ws((res) => { balances_handler (exchange, res) })
|
|
529
|
+
}, interval_dict[exchange].balances)
|
|
530
|
+
} else {
|
|
531
|
+
setInterval(() => {
|
|
532
|
+
Exchanges[exchange].balance((res) => { balances_handler (exchange, res) })
|
|
533
|
+
}, interval_dict[exchange].balances)
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
get_balances(this.Exchanges, this.interval_dict)
|
|
538
|
+
}
|
|
539
|
+
check_positions () {
|
|
540
|
+
let position_handler = (exchange, pair, res) => {
|
|
541
|
+
|
|
542
|
+
}
|
|
543
|
+
let position_all_handler = (exchange, res) => {
|
|
544
|
+
if (res.success) {
|
|
545
|
+
_.set(this.positions, [exchange], Object.assign(res.body, { update_time: new Date() }))
|
|
546
|
+
for (let exchange in this.exchange_pairs) {
|
|
547
|
+
this.exchange_pairs[exchange].map(pair => {
|
|
548
|
+
_.set(this.buy_sell_limits, [exchange, pair], {
|
|
549
|
+
sell: 1000 + _.get(res.body, ['total', pair], 0),
|
|
550
|
+
buy: 1000 - _.get(res.body, ['total', pair], 0)
|
|
551
|
+
})
|
|
552
|
+
})
|
|
553
|
+
}
|
|
554
|
+
} else if (_.get(this.positions, [exchange, 'update_time']) < new Date() - this.interval_dict[exchange].positions) {
|
|
555
|
+
this.error_log('%s positions expired: %j', exchange, res)
|
|
556
|
+
this.positions = _.omit(this.positions, [exchange])
|
|
557
|
+
this.buy_sell_limits = _.omit(this.buy_sell_limits, [exchange])
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
function get_positions (Exchanges, interval_dict) {
|
|
561
|
+
for (let exchange in Exchanges) {
|
|
562
|
+
if (Exchanges[exchange].position_all_ws) {
|
|
563
|
+
if (Exchanges[exchange].ws_status().trading === 'n-opened')
|
|
564
|
+
Exchanges[exchange].ws_account()
|
|
565
|
+
setInterval(() => {
|
|
566
|
+
Exchanges[exchange].position_all_ws((res) => { position_all_handler (exchange, res) })
|
|
567
|
+
}, interval_dict[exchange].positions)
|
|
568
|
+
} else if (Exchanges[exchange].position_ws) {
|
|
569
|
+
if (Exchanges[exchange].ws_status().trading === 'n-opened')
|
|
570
|
+
Exchanges[exchange].ws_account()
|
|
571
|
+
setInterval(() => {
|
|
572
|
+
for (let pair of this.exchange_pairs[exchange]) {
|
|
573
|
+
Exchanges[exchange].position_ws(pair, (res) => { position_handler (exchange, pair, res) })
|
|
574
|
+
}
|
|
575
|
+
}, interval_dict[exchange].positions)
|
|
576
|
+
} else if (Exchanges[exchange].position_all) {
|
|
577
|
+
setInterval(() => {
|
|
578
|
+
Exchanges[exchange].position_all((res) => { position_all_handler (exchange, res) })
|
|
579
|
+
}, interval_dict[exchange].positions)
|
|
580
|
+
} else if (Exchanges[exchange].position) {
|
|
581
|
+
setInterval(() => {
|
|
582
|
+
for (let pair of this.exchange_pairs[exchange]) {
|
|
583
|
+
Exchanges[exchange].position_ws(pair, (res) => { position_handler (exchange, pair, res) })
|
|
584
|
+
}
|
|
585
|
+
}, interval_dict[exchange].positions)
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
get_positions(this.Exchanges, this.interval_dict)
|
|
590
|
+
}
|
|
591
|
+
check_open_orders () {
|
|
592
|
+
const open_orders_timeframe = 12 * 60 * 60 * 1000
|
|
593
|
+
let previous_orders_processer = (exchange, pair, orders) => {
|
|
594
|
+
if (this.cancel_previous_orders && !this.previous_orders_canceled) {
|
|
595
|
+
orders.map(order => {
|
|
596
|
+
this.info_log('cancel original order: %s %s %j', exchange, pair, order)
|
|
597
|
+
this.Exchanges[exchange].cancel_order_by_order(order, (res) => {})
|
|
598
|
+
})
|
|
599
|
+
if (orders.length === 0) {
|
|
600
|
+
_.set(this.previous_orders_canceled_exchange_pair, [exchange, pair], true)
|
|
601
|
+
}
|
|
602
|
+
if (!this.previous_orders_canceled) {
|
|
603
|
+
let previous_orders_canceled = true
|
|
604
|
+
for (let exchange in this.exchange_pairs) {
|
|
605
|
+
this.exchange_pairs[exchange].map(pair => {
|
|
606
|
+
if (!_.get(this.previous_orders_canceled_exchange_pair, [exchange, pair])) {
|
|
607
|
+
previous_orders_canceled = false
|
|
608
|
+
}
|
|
609
|
+
})
|
|
610
|
+
}
|
|
611
|
+
if (previous_orders_canceled)
|
|
612
|
+
this.info_log('original order canceled')
|
|
613
|
+
this.previous_orders_canceled = previous_orders_canceled
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
let rate_major = _.get(this.rates_combined_major, [pair])
|
|
617
|
+
if (this.cancel_range_orders && this.major_exchanges[pair].indexOf(exchange) === -1 && this.no_cancel_range_exchanges.indexOf(exchange) === -1 && rate_major) {
|
|
618
|
+
// console.log(exchange, pair, rate_major, orders)
|
|
619
|
+
orders.map(order => {
|
|
620
|
+
if (order.type === 'sell' && order.price < rate_major.asks[0][0] && order.order_status !== 'F-FILLED' && order.order_type !== 'arb') {
|
|
621
|
+
this.info_log('cancel range order: %s %s %j', exchange, pair, order)
|
|
622
|
+
order.order_type = 'range-order'
|
|
623
|
+
this.cancel_order(exchange, pair, order)
|
|
624
|
+
}
|
|
625
|
+
if (order.type === 'buy' && order.price > rate_major.bids[0][0] && order.order_status !== 'F-FILLED' && order.order_type !== 'arb') {
|
|
626
|
+
this.info_log('cancel range order: %s %s %j', exchange, pair, order)
|
|
627
|
+
order.order_type = 'range-order'
|
|
628
|
+
this.cancel_order(exchange, pair, order)
|
|
629
|
+
}
|
|
630
|
+
})
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
let open_orders_handler = (exchange, pair, res) => {
|
|
634
|
+
if (res.success) {
|
|
635
|
+
_.set(this.open_orders, [exchange, pair], { open_orders: res.body, update_time: new Date() })
|
|
636
|
+
previous_orders_processer(exchange, pair, res.body)
|
|
637
|
+
} else if (_.get(this.open_orders, [exchange, pair, 'update_time']) < new Date() - this.interval_dict[exchange].open_orders) {
|
|
638
|
+
this.error_log('%s %s open orders expired: %j', exchange, pair, res)
|
|
639
|
+
this.open_orders[exchange] = _.omit(this.open_orders[exchange], [pair])
|
|
640
|
+
} else if (res.error !== 'opening') {
|
|
641
|
+
this.error_log('%s %s open orders: %j', exchange, pair, res)
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
let all_open_orders_handler = (exchange, res) => {
|
|
645
|
+
if (res.success) {
|
|
646
|
+
for (let pair of this.exchange_pairs[exchange]) {
|
|
647
|
+
let open_orders = res.body.filter(o => o.pair === pair)
|
|
648
|
+
_.set(this.open_orders, [exchange, pair], { open_orders: open_orders, update_time: new Date() })
|
|
649
|
+
previous_orders_processer(exchange, pair, open_orders)
|
|
650
|
+
}
|
|
651
|
+
} else {
|
|
652
|
+
let error_logged = false
|
|
653
|
+
for (let pair of this.exchange_pairs[exchange]) {
|
|
654
|
+
if (_.get(this.open_orders, [exchange, pair, 'update_time']) < new Date() - this.interval_dict[exchange].open_orders) {
|
|
655
|
+
if (!error_logged) {
|
|
656
|
+
this.error_log('%s all open orders expired: %j', exchange, res)
|
|
657
|
+
error_logged = true
|
|
658
|
+
}
|
|
659
|
+
this.open_orders[exchange] = _.omit(this.open_orders[exchange], [pair])
|
|
660
|
+
} else if (res.error !== 'opening' && !error_logged) {
|
|
661
|
+
this.error_log('%s all open orders: %j', exchange, res)
|
|
662
|
+
error_logged = true
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
let get_open_orders_all = (Exchanges, exchange_pairs, interval_dict) => {
|
|
668
|
+
function get_all_open_orders_ws (exchange) {
|
|
669
|
+
if (Exchanges[exchange].ws_status().trading === 'n-opened')
|
|
670
|
+
Exchanges[exchange].ws_account()
|
|
671
|
+
Exchanges[exchange].get_all_open_orders_ws((res) => {
|
|
672
|
+
all_open_orders_handler(exchange, res)
|
|
673
|
+
setTimeout(() => {
|
|
674
|
+
get_all_open_orders_ws(exchange)
|
|
675
|
+
}, interval_dict[exchange].open_orders)
|
|
676
|
+
})
|
|
677
|
+
}
|
|
678
|
+
function get_open_orders_ws (exchange, pair) {
|
|
679
|
+
Exchanges[exchange].get_open_orders_ws(pair, (res) => {
|
|
680
|
+
open_orders_handler(exchange, pair, res)
|
|
681
|
+
setTimeout(() => {
|
|
682
|
+
get_open_orders_ws(exchange, pair)
|
|
683
|
+
}, interval_dict[exchange].open_orders)
|
|
684
|
+
})
|
|
685
|
+
}
|
|
686
|
+
function get_all_open_orders (exchange) {
|
|
687
|
+
Exchanges[exchange].get_all_open_orders((res) => {
|
|
688
|
+
all_open_orders_handler(exchange, res)
|
|
689
|
+
setTimeout(() => {
|
|
690
|
+
get_all_open_orders(exchange)
|
|
691
|
+
}, interval_dict[exchange].open_orders)
|
|
692
|
+
})
|
|
693
|
+
}
|
|
694
|
+
function get_open_orders (exchange, pair) {
|
|
695
|
+
Exchanges[exchange].get_open_orders(pair, (res) => {
|
|
696
|
+
open_orders_handler(exchange, pair, res)
|
|
697
|
+
setTimeout(() => {
|
|
698
|
+
get_open_orders(exchange, pair)
|
|
699
|
+
}, interval_dict[exchange].open_orders)
|
|
700
|
+
})
|
|
701
|
+
}
|
|
702
|
+
for (let exchange in Exchanges) {
|
|
703
|
+
if (Exchanges[exchange].get_all_open_orders_ws) {
|
|
704
|
+
if (Exchanges[exchange].ws_status().trading === 'n-opened')
|
|
705
|
+
Exchanges[exchange].ws_account()
|
|
706
|
+
get_all_open_orders_ws(exchange)
|
|
707
|
+
} else if (Exchanges[exchange].get_open_orders_ws) {
|
|
708
|
+
if (Exchanges[exchange].ws_status().trading === 'n-opened')
|
|
709
|
+
Exchanges[exchange].ws_account()
|
|
710
|
+
for (let pair of exchange_pairs[exchange]) {
|
|
711
|
+
get_open_orders_ws(exchange, pair)
|
|
712
|
+
}
|
|
713
|
+
} else if (Exchanges[exchange].get_all_open_orders) {
|
|
714
|
+
get_all_open_orders(exchange)
|
|
715
|
+
} else if (Exchanges[exchange].get_open_orders) {
|
|
716
|
+
for (let pair of exchange_pairs[exchange]) {
|
|
717
|
+
get_open_orders(exchange, pair)
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
get_open_orders_all(this.Exchanges, this.exchange_pairs, this.interval_dict)
|
|
723
|
+
}
|
|
724
|
+
check_trades () {
|
|
725
|
+
const trades_timeframe = 12 * 60 * 60 * 1000
|
|
726
|
+
let trades_handler = (exchange, pair, res) => {
|
|
727
|
+
if (res.success) {
|
|
728
|
+
_.set(this.trades, [exchange, pair], { trades: res.body, update_time: new Date() })
|
|
729
|
+
_.set(this.trades_amount_past_hour, [exchange, pair], _.sumBy(res.body.filter(t => t.close_time > new Date() - 60 * 60 * 1000 || t.open_time > new Date() - 60 * 60 * 1000), t => t.amount) || 0)
|
|
730
|
+
} else if (_.get(this.trades, [exchange, pair, 'update_time']) < new Date() - this.interval_dict[exchange].trades) {
|
|
731
|
+
this.error_log('%s %s trades expired: %j', exchange, pair, res)
|
|
732
|
+
this.trades[exchange] = _.omit(this.trades[exchange], [pair])
|
|
733
|
+
} else if (res.error !== 'opening') {
|
|
734
|
+
this.error_log('%s %s trades: %j', exchange, pair, res)
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
let all_trades_handler = (exchange, res) => {
|
|
738
|
+
if (res.success) {
|
|
739
|
+
for (let pair of this.exchange_pairs[exchange]) {
|
|
740
|
+
let trades = res.body.filter(o => o.pair === pair)
|
|
741
|
+
_.set(this.trades, [exchange, pair], { trades: trades, update_time: new Date() })
|
|
742
|
+
_.set(this.trades_amount_past_hour, [exchange, pair], _.sumBy(trades.filter(t => t.close_time > new Date() - 60 * 60 * 1000 || t.open_time > new Date() - 60 * 60 * 1000), t => t.amount) || 0)
|
|
743
|
+
}
|
|
744
|
+
} else if (res.error !== 'opening') {
|
|
745
|
+
let error_logged = false
|
|
746
|
+
for (let pair of this.exchange_pairs[exchange]) {
|
|
747
|
+
if (_.get(this.trades, [exchange, pair, 'update_time']) < new Date() - this.interval_dict[exchange].trades) {
|
|
748
|
+
if (!error_logged) {
|
|
749
|
+
this.error_log('%s all trades expired: %j', exchange, res)
|
|
750
|
+
error_logged = true
|
|
751
|
+
}
|
|
752
|
+
this.trades[exchange] = _.omit(this.trades[exchange], [pair])
|
|
753
|
+
} else if (res.error !== 'opening' && !error_logged) {
|
|
754
|
+
this.error_log('%s all trades: %j', exchange, res)
|
|
755
|
+
error_logged = true
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
let get_trades_all = (Exchanges, exchange_pairs, interval_dict) => {
|
|
761
|
+
let get_all_trades_ws = (exchange) => {
|
|
762
|
+
if (Exchanges[exchange].ws_status().trading === 'n-opened')
|
|
763
|
+
Exchanges[exchange].ws_account()
|
|
764
|
+
Exchanges[exchange].get_all_trades_ws(trades_timeframe, (res) => {
|
|
765
|
+
all_trades_handler(exchange, res)
|
|
766
|
+
setTimeout(() => {
|
|
767
|
+
get_all_trades_ws(exchange)
|
|
768
|
+
}, interval_dict[exchange].trades)
|
|
769
|
+
})
|
|
770
|
+
}
|
|
771
|
+
let get_trades_ws = (exchange, pair) => {
|
|
772
|
+
Exchanges[exchange].get_trades_ws(pair, trades_timeframe, (res) => {
|
|
773
|
+
trades_handler(exchange, pair, res)
|
|
774
|
+
setTimeout(() => {
|
|
775
|
+
get_trades_ws(exchange, pair)
|
|
776
|
+
}, interval_dict[exchange].trades)
|
|
777
|
+
})
|
|
778
|
+
}
|
|
779
|
+
let get_all_trades = (exchange) => {
|
|
780
|
+
Exchanges[exchange].get_all_trades(trades_timeframe, (res) => {
|
|
781
|
+
all_trades_handler(exchange, res)
|
|
782
|
+
setTimeout(() => {
|
|
783
|
+
get_all_trades(exchange)
|
|
784
|
+
}, interval_dict[exchange].trades)
|
|
785
|
+
})
|
|
786
|
+
}
|
|
787
|
+
let get_trades = (exchange, pair) => {
|
|
788
|
+
Exchanges[exchange].get_trades(pair, trades_timeframe, (res) => {
|
|
789
|
+
trades_handler(exchange, pair, res)
|
|
790
|
+
setTimeout(() => {
|
|
791
|
+
get_trades(exchange, pair)
|
|
792
|
+
}, interval_dict[exchange].trades)
|
|
793
|
+
})
|
|
794
|
+
}
|
|
795
|
+
let get_trades_id = (exchange, pair) => {
|
|
796
|
+
let order_info = _.get(this.order_info, [exchange, pair], {})
|
|
797
|
+
let timeout = setTimeout(() => {
|
|
798
|
+
get_trades_id(exchange, pair)
|
|
799
|
+
}, interval_dict[exchange].trades)
|
|
800
|
+
let order_number = _.keys(order_info['sell']).length + _.keys(order_info['buy']).length, count = 0, result = []
|
|
801
|
+
for (let type in order_info) {
|
|
802
|
+
for (let order_id in order_info[type]) {
|
|
803
|
+
Exchanges[exchange].get_trades_id(pair, order_id, trades_timeframe, (res) => {
|
|
804
|
+
count++
|
|
805
|
+
if (res.success) {
|
|
806
|
+
result = res.body.concat(result)
|
|
807
|
+
}
|
|
808
|
+
if (count === order_number) {
|
|
809
|
+
trades_handler(exchange, pair, { success: true, body: result })
|
|
810
|
+
clearTimeout(timeout)
|
|
811
|
+
setTimeout(() => {
|
|
812
|
+
get_trades_id(exchange, pair)
|
|
813
|
+
}, interval_dict[exchange].trades)
|
|
814
|
+
}
|
|
815
|
+
})
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
for (let exchange in Exchanges) {
|
|
820
|
+
if (this.no_trades_function_exchanges.indexOf(exchange) > -1) {
|
|
821
|
+
|
|
822
|
+
} else if (Exchanges[exchange].get_all_trades_ws) {
|
|
823
|
+
if (Exchanges[exchange].ws_status().trading === 'n-opened')
|
|
824
|
+
Exchanges[exchange].ws_account()
|
|
825
|
+
get_all_trades_ws(exchange)
|
|
826
|
+
} else if (Exchanges[exchange].get_trades_ws) {
|
|
827
|
+
if (Exchanges[exchange].ws_status().trading === 'n-opened')
|
|
828
|
+
Exchanges[exchange].ws_account()
|
|
829
|
+
for (let pair of exchange_pairs[exchange]) {
|
|
830
|
+
get_trades_ws(exchange, pair)
|
|
831
|
+
}
|
|
832
|
+
} else if (Exchanges[exchange].get_all_trades) {
|
|
833
|
+
get_all_trades(exchange)
|
|
834
|
+
} else if (Exchanges[exchange].get_trades) {
|
|
835
|
+
for (let pair of exchange_pairs[exchange]) {
|
|
836
|
+
get_trades(exchange, pair)
|
|
837
|
+
}
|
|
838
|
+
} else if (Exchanges[exchange].get_trades_id) {
|
|
839
|
+
for (let pair of exchange_pairs[exchange]) {
|
|
840
|
+
get_trades_id(exchange, pair)
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
get_trades_all (this.Exchanges, this.exchange_pairs, this.interval_dict)
|
|
846
|
+
}
|
|
847
|
+
check_rates () {
|
|
848
|
+
let rates_handler = (exchange, pair, res) => {
|
|
849
|
+
if (res.success) {
|
|
850
|
+
res.body.asks = res.body.asks.slice(0, rates_length)
|
|
851
|
+
res.body.bids = res.body.bids.slice(0, rates_length)
|
|
852
|
+
_.set(this.rates, [exchange, pair], Object.assign(res.body, { update_time: new Date() }))
|
|
853
|
+
for (let pair in this.pair_exchanges) {
|
|
854
|
+
let orderbook_all = {}, orderbook_all_with_buy_sell_limits = {},
|
|
855
|
+
orderbook_major = {}, orderbook_major_temp = {},
|
|
856
|
+
orderbook_good_with_buy_sell_limits = {}, orderbook_good_temp = {}
|
|
857
|
+
for (let exchange of this.pair_exchanges[pair]) {
|
|
858
|
+
|
|
859
|
+
if (_.get(this.rates, [exchange, pair, 'asks'], []).length > 0 && _.get(this.rates, [exchange, pair, 'bids'], []).length > 0) {
|
|
860
|
+
orderbook_all[exchange] = this.rates[exchange][pair]
|
|
861
|
+
} else {
|
|
862
|
+
orderbook_all = _.omit(orderbook_all, [exchange])
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
if (_.get(this.rates, [exchange, pair, 'asks'], []).length > 0
|
|
866
|
+
&& _.get(this.rates, [exchange, pair, 'bids'], []).length > 0
|
|
867
|
+
&& _.get(this.buy_sell_limits, [exchange, pair, 'buy'])
|
|
868
|
+
&& _.get(this.buy_sell_limits, [exchange, pair, 'sell'])
|
|
869
|
+
) {
|
|
870
|
+
let rates = JSON.parse(JSON.stringify(this.rates[exchange][pair]))
|
|
871
|
+
let rates_with_buy_sell_limits = {
|
|
872
|
+
asks: utils.sum_position_orderbook(rates.asks, this.buy_sell_limits[exchange][pair].buy),
|
|
873
|
+
bids: utils.vol_position_orderbook(rates.bids, this.buy_sell_limits[exchange][pair].sell)
|
|
874
|
+
}
|
|
875
|
+
orderbook_all_with_buy_sell_limits[exchange] = rates_with_buy_sell_limits
|
|
876
|
+
if (this.major_exchanges[pair] && this.major_exchanges[pair].indexOf(exchange) > -1) {
|
|
877
|
+
orderbook_major_temp[exchange] = rates
|
|
878
|
+
if (_.keys(orderbook_major_temp).length === this.major_exchanges[pair].length) {
|
|
879
|
+
orderbook_major = orderbook_major_temp
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
if (this.bad_trades_function_exchanges && this.bad_trades_function_exchanges.indexOf(exchange) === -1) {
|
|
883
|
+
orderbook_good_temp[exchange] = rates_with_buy_sell_limits
|
|
884
|
+
if (_.keys(orderbook_good_temp).length === this.pair_exchanges[pair].length - this.bad_trades_function_exchanges.length) {
|
|
885
|
+
orderbook_good_with_buy_sell_limits = orderbook_good_temp
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
} else {
|
|
889
|
+
orderbook_all_with_buy_sell_limits = _.omit(orderbook_all_with_buy_sell_limits, [exchange])
|
|
890
|
+
orderbook_major = _.omit(orderbook_major, [exchange])
|
|
891
|
+
orderbook_major_temp = _.omit(orderbook_major_temp, [exchange])
|
|
892
|
+
orderbook_good_with_buy_sell_limits = _.omit(orderbook_good_with_buy_sell_limits, [exchange])
|
|
893
|
+
orderbook_good_temp = _.omit(orderbook_good_temp, [exchange])
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
if (_.keys(orderbook_all).length > 0) {
|
|
897
|
+
let o = utils.combine_orderbooks(orderbook_all, 100)
|
|
898
|
+
if (o.asks.length > 0 && o.bids.length > 0) {
|
|
899
|
+
_.set(this.rates_combined_all, [pair], o)
|
|
900
|
+
} else {
|
|
901
|
+
this.rates_combined_all = _.omit(this.rates_combined_all, [pair])
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
if (_.keys(orderbook_all_with_buy_sell_limits).length > 0) {
|
|
905
|
+
let o = utils.combine_orderbooks(orderbook_all_with_buy_sell_limits, 100)
|
|
906
|
+
if (o.asks.length > 0 && o.bids.length > 0) {
|
|
907
|
+
_.set(this.rates_combined_all_with_buy_sell_limits, [pair], o)
|
|
908
|
+
} else {
|
|
909
|
+
this.rates_combined_all_with_buy_sell_limits = _.omit(this.rates_combined_all_with_buy_sell_limits, [pair])
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
if (_.keys(orderbook_major).length > 0) {
|
|
913
|
+
let o = utils.combine_orderbooks(orderbook_major, 100)
|
|
914
|
+
if (o.asks.length > 0 && o.bids.length > 0) {
|
|
915
|
+
_.set(this.rates_combined_major, [pair], o)
|
|
916
|
+
} else {
|
|
917
|
+
this.rates_combined_major = _.omit(this.rates_combined_major, [pair])
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
if (_.keys(orderbook_good_with_buy_sell_limits).length > 0) {
|
|
921
|
+
let o = utils.combine_orderbooks(orderbook_good_with_buy_sell_limits, 100)
|
|
922
|
+
if (o.asks.length > 0 && o.bids.length > 0) {
|
|
923
|
+
_.set(this.rates_combined_good_with_buy_sell_limits, [pair], o)
|
|
924
|
+
} else {
|
|
925
|
+
this.rates_combined_good_with_buy_sell_limits = _.omit(this.rates_combined_good_with_buy_sell_limits, [pair])
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
} else if (_.get(this.rates, [exchange, pair, 'update_time']) < new Date() - this.interval_dict[exchange].rates) {
|
|
930
|
+
this.error_log('%s %s rates expired: %j', exchange, pair, res)
|
|
931
|
+
this.rates[exchange] = _.omit(this.rates[exchange], [pair])
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
let get_rates = (Exchanges, exchange_pairs, interval_dict) => {
|
|
935
|
+
for (let exchange in Exchanges) {
|
|
936
|
+
if (Exchanges[exchange].ws_market) {
|
|
937
|
+
setInterval(() => {
|
|
938
|
+
if (!this.ws_market_first_time[exchange]) {
|
|
939
|
+
Exchanges[exchange].ws_market(exchange_pairs[exchange])
|
|
940
|
+
this.ws_market_first_time[exchange] = true
|
|
941
|
+
} else {
|
|
942
|
+
for (let pair of exchange_pairs[exchange]) {
|
|
943
|
+
Exchanges[exchange].rate_ws(pair, (res) => { rates_handler(exchange, pair, res) })
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
}, interval_dict[exchange].rates)
|
|
947
|
+
} else {
|
|
948
|
+
for (let pair of exchange_pairs[exchange]) {
|
|
949
|
+
setInterval(() => {
|
|
950
|
+
Exchanges[exchange].rate(pair, 30, (res) => { rates_handler(exchange, pair, res) })
|
|
951
|
+
}, interval_dict[exchange].rates)
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
get_rates (this.Exchanges, this.exchange_pairs, this.interval_dict)
|
|
957
|
+
}
|
|
958
|
+
check_market_history () {
|
|
959
|
+
let market_history_handler = (exchange, pair, res) => {
|
|
960
|
+
if (res.success) {
|
|
961
|
+
_.set(this.market_history, [exchange, pair], Object.assign(res.body, { update_time: new Date() }))
|
|
962
|
+
} else if (_.get(this.market_history, [exchange, pair, 'update_time']) < new Date() - this.interval_dict[exchange].market_history) {
|
|
963
|
+
this.error_log('%s %s market_history expired: %j', exchange, pair, res)
|
|
964
|
+
this.market_history[exchange] = _.omit(this.market_history[exchange], [pair])
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
let get_market_history = (Exchanges, exchange_pairs, interval_dict) => {
|
|
968
|
+
for (let exchange in Exchanges) {
|
|
969
|
+
const market_history_timeframe = (exchange === 'coinbene') ? 30 * 24 * 60 * 60 * 1000 : 10 * 60 * 1000
|
|
970
|
+
if (Exchanges[exchange].ws_market) {
|
|
971
|
+
setInterval(() => {
|
|
972
|
+
if (!this.ws_market_first_time[exchange]) {
|
|
973
|
+
Exchanges[exchange].ws_market(exchange_pairs[exchange])
|
|
974
|
+
this.ws_market_first_time[exchange] = true
|
|
975
|
+
} else {
|
|
976
|
+
for (let pair of exchange_pairs[exchange]) {
|
|
977
|
+
Exchanges[exchange].market_history_ws(pair, market_history_timeframe, (res) => { market_history_handler(exchange, pair, res) })
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}, interval_dict[exchange].market_history)
|
|
981
|
+
} else {
|
|
982
|
+
for (let pair of exchange_pairs[exchange]) {
|
|
983
|
+
setInterval(() => {
|
|
984
|
+
Exchanges[exchange].market_history(pair, market_history_timeframe, (res) => { market_history_handler(exchange, pair, res) })
|
|
985
|
+
}, interval_dict[exchange].market_history)
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
get_market_history (this.Exchanges, this.exchange_pairs, this.interval_dict)
|
|
991
|
+
}
|
|
992
|
+
check_volumes () {
|
|
993
|
+
const influx_client = new Influx.InfluxDB(this.influx_url_check_volume)
|
|
994
|
+
let check_volumes_base = () => {
|
|
995
|
+
for (let pair in this.pair_exchanges) {
|
|
996
|
+
for (let exchange of this.pair_exchanges[pair]) {
|
|
997
|
+
let query_string = `SELECT sum("vol") FROM "${pair}" WHERE ("exchange" = '${_.upperFirst(exchange)}') AND time > now() - 12h`
|
|
998
|
+
influx_client.query(query_string).then(res => {
|
|
999
|
+
res.map(e => {
|
|
1000
|
+
if (e.time && e.sum) {
|
|
1001
|
+
_.set(this.volumes, [pair, exchange], e.sum)
|
|
1002
|
+
}
|
|
1003
|
+
})
|
|
1004
|
+
})
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
check_volumes_base()
|
|
1009
|
+
setInterval(() => {
|
|
1010
|
+
check_volumes_base()
|
|
1011
|
+
}, 15 * 60 * 1000)
|
|
1012
|
+
}
|
|
1013
|
+
check_cmc_rates () {
|
|
1014
|
+
let check_cmc_rates_base = () => {
|
|
1015
|
+
get_coinmarketcap_rates(rates => {
|
|
1016
|
+
this.cmc_rates = rates
|
|
1017
|
+
})
|
|
1018
|
+
}
|
|
1019
|
+
check_cmc_rates_base()
|
|
1020
|
+
setInterval(() => {
|
|
1021
|
+
check_cmc_rates_base()
|
|
1022
|
+
}, 15 * 60 * 1000)
|
|
1023
|
+
}
|
|
1024
|
+
}
|