@icgio/icg-exchanges 1.40.42 → 1.40.44
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/API_DOCS.md +6 -6
- package/lib/exchanges/ascendex.js +3 -0
- package/lib/exchanges/biconomy.js +62 -0
- package/lib/exchanges/binance.js +61 -0
- package/lib/exchanges/bingx.js +70 -0
- package/lib/exchanges/bitfinex.js +76 -0
- package/lib/exchanges/bitget.js +82 -0
- package/lib/exchanges/bithumb.js +3 -0
- package/lib/exchanges/bitkub.js +3 -0
- package/lib/exchanges/bitmex.js +69 -0
- package/lib/exchanges/bitrue.js +56 -0
- package/lib/exchanges/bitstamp.js +71 -0
- package/lib/exchanges/blofin.js +72 -0
- package/lib/exchanges/btse.js +3 -0
- package/lib/exchanges/coinbase.js +102 -0
- package/lib/exchanges/coinstore.js +3 -0
- package/lib/exchanges/cryptocom.js +66 -0
- package/lib/exchanges/deepcoin.js +64 -0
- package/lib/exchanges/digifinex.js +62 -0
- package/lib/exchanges/exchange-base.js +167 -1
- package/lib/exchanges/fastex.js +3 -0
- package/lib/exchanges/gemini.js +70 -0
- package/lib/exchanges/hashkey.js +67 -0
- package/lib/exchanges/hashkeyglobal.js +67 -0
- package/lib/exchanges/hkbitex.js +3 -0
- package/lib/exchanges/htx.js +74 -0
- package/lib/exchanges/indodax.js +3 -0
- package/lib/exchanges/kraken.js +63 -0
- package/lib/exchanges/lbank.js +86 -51
- package/lib/exchanges/mexc.js +58 -0
- package/lib/exchanges/swft.js +3 -0
- package/lib/exchanges/upbit.js +57 -0
- package/lib/exchanges/weex.js +3 -0
- package/lib/exchanges/xt.js +61 -0
- package/package.json +1 -1
package/API_DOCS.md
CHANGED
|
@@ -8,9 +8,9 @@ Quick reference for official API documentation of all supported exchanges.
|
|
|
8
8
|
|----------|-------------------|
|
|
9
9
|
| Ascendex | https://ascendex.github.io/ascendex-pro-api/ |
|
|
10
10
|
| Biconomy | https://github.com/BiconomyOfficial/APIDocs |
|
|
11
|
-
| Binance | https://binance
|
|
11
|
+
| Binance | https://developers.binance.com/docs/binance-spot-api-docs/README |
|
|
12
12
|
| Bingx | https://bingx-api.github.io/docs/ |
|
|
13
|
-
| Bitfinex | https://docs.bitfinex.com/
|
|
13
|
+
| Bitfinex | https://docs.bitfinex.com/reference |
|
|
14
14
|
| Bitget | https://www.bitget.com/api-doc/spot/intro |
|
|
15
15
|
| Bithumb | https://apidocs.bithumb.com/ |
|
|
16
16
|
| Bitkub | https://github.com/bitkub/bitkub-official-api-docs |
|
|
@@ -31,13 +31,13 @@ Quick reference for official API documentation of all supported exchanges.
|
|
|
31
31
|
| Digifinex | https://docs.digifinex.com/ |
|
|
32
32
|
| Gate | https://www.gate.io/docs/developers/apiv4/ |
|
|
33
33
|
| Gemini | https://docs.gemini.com/ |
|
|
34
|
-
| Hashkey | https://
|
|
35
|
-
| HashkeyGlobal | https://
|
|
34
|
+
| Hashkey | https://docs.hashkey.com/uae/en/ |
|
|
35
|
+
| HashkeyGlobal | https://docs.hashkey.com/glb |
|
|
36
36
|
| Hitbtc | https://api.hitbtc.com/ |
|
|
37
37
|
| Hkbitex | https://www.hkbitex.com.hk |
|
|
38
38
|
| Htx | https://huobiapi.github.io/docs/spot/v1/en/ |
|
|
39
39
|
| Indodax | https://github.com/btcid/indodax-official-api-docs |
|
|
40
|
-
| Kraken | https://docs.kraken.com/
|
|
40
|
+
| Kraken | https://docs.kraken.com/api/ |
|
|
41
41
|
| Kucoin | https://docs.kucoin.com/ |
|
|
42
42
|
| Lbank | https://www.lbank.com/en-US/docs/index.html |
|
|
43
43
|
| Mexc | https://mexcdevelop.github.io/apidocs/ |
|
|
@@ -46,6 +46,6 @@ Quick reference for official API documentation of all supported exchanges.
|
|
|
46
46
|
| Phemex | https://phemex-docs.github.io/ |
|
|
47
47
|
| Poloniex | https://docs.poloniex.com/ |
|
|
48
48
|
| Swft | https://www.swft.pro |
|
|
49
|
-
| Upbit | https://docs.upbit.com/ |
|
|
49
|
+
| Upbit | https://global-docs.upbit.com/ |
|
|
50
50
|
| Weex | https://www.weex.com/api-doc |
|
|
51
51
|
| Xt | https://doc.xt.com/ |
|
|
@@ -478,6 +478,9 @@ module.exports = class Ascendex extends ExchangeBase {
|
|
|
478
478
|
}
|
|
479
479
|
this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
|
|
480
480
|
}
|
|
481
|
+
get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
|
|
482
|
+
this._get_history_trades_via_timeframe(pair, start_time_in_ms, end_time_in_ms, cb)
|
|
483
|
+
}
|
|
481
484
|
static get_min_amount(cb) {
|
|
482
485
|
let min_cur_amount = {}
|
|
483
486
|
needle.get(base_url + '/api/pro/v1/cash/products', (err, res, body) => {
|
|
@@ -7,6 +7,7 @@ const needle = require('needle')
|
|
|
7
7
|
|
|
8
8
|
const ExchangeBase = require('./exchange-base.js')
|
|
9
9
|
const ExchangeWs = require('./exchange-ws.js')
|
|
10
|
+
const { filter_history_in_range, sleep, sort_history_rows } = ExchangeBase.history_utils
|
|
10
11
|
|
|
11
12
|
const { error_codes } = require('../error_codes.json')
|
|
12
13
|
|
|
@@ -551,6 +552,67 @@ module.exports = class Biconomy extends ExchangeBase {
|
|
|
551
552
|
}
|
|
552
553
|
this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
|
|
553
554
|
}
|
|
555
|
+
get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
|
|
556
|
+
const get_history_trades_base = (pair, start_time_in_ms, end_time_in_ms, cb) => {
|
|
557
|
+
let [api_key, secret_key] = this.api_secret_key
|
|
558
|
+
;(async () => {
|
|
559
|
+
const rows = []
|
|
560
|
+
const [, quote_cur] = utils.parse_pair(pair)
|
|
561
|
+
let offset = 0
|
|
562
|
+
const limit = 100
|
|
563
|
+
while (true) {
|
|
564
|
+
let params = {
|
|
565
|
+
api_key: api_key,
|
|
566
|
+
limit,
|
|
567
|
+
market: pre_process_pair(pair),
|
|
568
|
+
offset,
|
|
569
|
+
}
|
|
570
|
+
params.sign = get_signature_biconomy(secret_key, params)
|
|
571
|
+
const body = await new Promise((resolve, reject) => {
|
|
572
|
+
needle.post('https://api.biconomy.com/api/v1/private/order/finished', params, options, (err, res, body) => {
|
|
573
|
+
if (body && body.code === 0 && body.result && Array.isArray(body.result.records)) {
|
|
574
|
+
resolve(body)
|
|
575
|
+
} else if (body) {
|
|
576
|
+
reject({ error: error_codes.other, body })
|
|
577
|
+
} else {
|
|
578
|
+
reject({ error: error_codes.timeout })
|
|
579
|
+
}
|
|
580
|
+
})
|
|
581
|
+
})
|
|
582
|
+
const records = body.result.records
|
|
583
|
+
if (records.length === 0) break
|
|
584
|
+
for (const record of records) {
|
|
585
|
+
const close_time_ms = Math.floor(parseFloat(record.ftime) * 1000)
|
|
586
|
+
if (close_time_ms < start_time_in_ms) {
|
|
587
|
+
cb({ success: true, body: sort_history_rows(filter_history_in_range(rows, start_time_in_ms, end_time_in_ms)) })
|
|
588
|
+
return
|
|
589
|
+
}
|
|
590
|
+
if (close_time_ms >= end_time_in_ms || parseFloat(record.deal_stock) <= 0) continue
|
|
591
|
+
rows.push({
|
|
592
|
+
order_id: record.id != null ? record.id.toString() : '',
|
|
593
|
+
pair,
|
|
594
|
+
type: parseFloat(record.side) === 2 ? 'buy' : 'sell',
|
|
595
|
+
price: parseFloat(record.price),
|
|
596
|
+
amount: parseFloat(record.deal_stock),
|
|
597
|
+
total: parseFloat(record.deal_money),
|
|
598
|
+
close_time: new Date(close_time_ms),
|
|
599
|
+
fees: {
|
|
600
|
+
[quote_cur]: parseFloat(record.maker_fee) > 0 ? parseFloat(record.maker_fee) : parseFloat(record.taker_fee),
|
|
601
|
+
},
|
|
602
|
+
role: 'maker',
|
|
603
|
+
})
|
|
604
|
+
}
|
|
605
|
+
if (records.length < limit) break
|
|
606
|
+
offset += limit
|
|
607
|
+
await sleep(50)
|
|
608
|
+
}
|
|
609
|
+
cb({ success: true, body: sort_history_rows(filter_history_in_range(rows, start_time_in_ms, end_time_in_ms)) })
|
|
610
|
+
})().catch((error) => {
|
|
611
|
+
cb({ success: false, error: error?.error || error_codes.other, body: error?.body })
|
|
612
|
+
})
|
|
613
|
+
}
|
|
614
|
+
this.private_limiter.process(get_history_trades_base, pair, start_time_in_ms, end_time_in_ms, cb)
|
|
615
|
+
}
|
|
554
616
|
// get_all_trades (timeframe_in_ms, cb) {
|
|
555
617
|
//
|
|
556
618
|
// }
|
package/lib/exchanges/binance.js
CHANGED
|
@@ -656,6 +656,67 @@ module.exports = class Binance extends ExchangeBase {
|
|
|
656
656
|
}
|
|
657
657
|
this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
|
|
658
658
|
}
|
|
659
|
+
get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
|
|
660
|
+
let [api_key, secret_key] = this.api_secret_key
|
|
661
|
+
let { fetch_split_windows, filter_history_in_range, sort_history_rows } = ExchangeBase.history_utils
|
|
662
|
+
let request_options = {
|
|
663
|
+
headers: {
|
|
664
|
+
...options.headers,
|
|
665
|
+
'X-MBX-APIKEY': api_key,
|
|
666
|
+
},
|
|
667
|
+
}
|
|
668
|
+
let path = '/api/v3/myTrades'
|
|
669
|
+
let fetch_window = (window_start_ms, window_stop_ms) =>
|
|
670
|
+
new Promise((resolve, reject) => {
|
|
671
|
+
let params = {
|
|
672
|
+
symbol: pre_process_pair(pair),
|
|
673
|
+
startTime: window_start_ms,
|
|
674
|
+
endTime: window_stop_ms - 1,
|
|
675
|
+
limit: 1000,
|
|
676
|
+
timestamp: Date.now(),
|
|
677
|
+
}
|
|
678
|
+
params.signature = get_signature_binance(secret_key, params)
|
|
679
|
+
let url = 'https://api.binance.com' + path + '?' + qs.stringify(params)
|
|
680
|
+
this.private_limiter.process(needle.get, url, request_options, (err, res, body) => {
|
|
681
|
+
if (body && Array.isArray(body)) {
|
|
682
|
+
resolve(
|
|
683
|
+
body.map((trade) => ({
|
|
684
|
+
order_id: trade.orderId.toString(),
|
|
685
|
+
trade_id: trade.id.toString(),
|
|
686
|
+
pair,
|
|
687
|
+
type: trade.isBuyer ? 'buy' : 'sell',
|
|
688
|
+
price: parseFloat(trade.price),
|
|
689
|
+
amount: parseFloat(trade.qty),
|
|
690
|
+
total: parseFloat(trade.quoteQty || parseFloat(trade.price) * parseFloat(trade.qty)),
|
|
691
|
+
close_time: new Date(trade.time),
|
|
692
|
+
fees: {
|
|
693
|
+
[post_process_cur(trade.commissionAsset)]: parseFloat(trade.commission),
|
|
694
|
+
},
|
|
695
|
+
role: trade.isMaker ? 'maker' : 'taker',
|
|
696
|
+
}))
|
|
697
|
+
)
|
|
698
|
+
} else if (body && body.code === -1021 && body.msg.startsWith('Timestamp for this request')) {
|
|
699
|
+
reject({ success: false, error: error_codes.request_error, body })
|
|
700
|
+
} else if (body) {
|
|
701
|
+
reject({ success: false, error: error_codes.other, body })
|
|
702
|
+
} else {
|
|
703
|
+
reject({ success: false, error: error_codes.timeout })
|
|
704
|
+
}
|
|
705
|
+
})
|
|
706
|
+
})
|
|
707
|
+
;(async () => {
|
|
708
|
+
let trades = await fetch_split_windows({
|
|
709
|
+
start_ms: start_time_in_ms,
|
|
710
|
+
stop_ms: end_time_in_ms,
|
|
711
|
+
initial_window_ms: 7 * 24 * 60 * 60 * 1000,
|
|
712
|
+
page_limit: 1000,
|
|
713
|
+
fetch_window,
|
|
714
|
+
delay_ms: 250,
|
|
715
|
+
})
|
|
716
|
+
trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
|
|
717
|
+
cb({ success: true, body: trades })
|
|
718
|
+
})().catch((error) => cb(error))
|
|
719
|
+
}
|
|
659
720
|
// get_trades_ws (pair, timeframe_in_ms, cb) {
|
|
660
721
|
// if (this.ws.trading.status === 'opened') {
|
|
661
722
|
// if (!this.ws.trading.first_trades[pair]) {
|
package/lib/exchanges/bingx.js
CHANGED
|
@@ -8,6 +8,7 @@ const zlib = require('zlib')
|
|
|
8
8
|
|
|
9
9
|
const ExchangeBase = require('./exchange-base.js')
|
|
10
10
|
const ExchangeWs = require('./exchange-ws.js')
|
|
11
|
+
const { fetch_split_windows, sort_history_rows, with_retry } = ExchangeBase.history_utils
|
|
11
12
|
|
|
12
13
|
const { error_codes } = require('../error_codes.json')
|
|
13
14
|
|
|
@@ -527,6 +528,75 @@ module.exports = class Bingx extends ExchangeBase {
|
|
|
527
528
|
}
|
|
528
529
|
this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
|
|
529
530
|
}
|
|
531
|
+
get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
|
|
532
|
+
const get_history_trades_base = (pair, start_time_in_ms, end_time_in_ms, cb) => {
|
|
533
|
+
let [api_key, secret_key] = this.api_secret_key
|
|
534
|
+
const options = get_options(api_key)
|
|
535
|
+
;(async () => {
|
|
536
|
+
const rows = await fetch_split_windows({
|
|
537
|
+
start_ms: start_time_in_ms,
|
|
538
|
+
stop_ms: end_time_in_ms,
|
|
539
|
+
initial_window_ms: 60 * 60 * 1000,
|
|
540
|
+
page_limit: 1000,
|
|
541
|
+
fetch_window: async (window_start_ms, window_stop_ms) => {
|
|
542
|
+
const body = await with_retry(
|
|
543
|
+
async () =>
|
|
544
|
+
await new Promise((resolve, reject) => {
|
|
545
|
+
let params = {
|
|
546
|
+
limit: 1000,
|
|
547
|
+
startTime: window_start_ms,
|
|
548
|
+
endTime: window_stop_ms - 1,
|
|
549
|
+
symbol: pre_process_pair(pair),
|
|
550
|
+
timestamp: new Date().getTime(),
|
|
551
|
+
}
|
|
552
|
+
let signature = get_signature_bingx(secret_key, params)
|
|
553
|
+
let url = 'https://open-api.bingx.com/openApi/spot/v1/trade/myTrades?' + qs.stringify(params) + '&signature=' + signature
|
|
554
|
+
needle.get(url, options, (err, res, body) => {
|
|
555
|
+
body = parse_body(body)
|
|
556
|
+
if (body && body.code === 0 && body.data && Array.isArray(body.data.fills)) {
|
|
557
|
+
resolve(body)
|
|
558
|
+
} else {
|
|
559
|
+
const error = new Error(`bingx myTrades failed: ${JSON.stringify(body).slice(0, 300)}`)
|
|
560
|
+
error.body = body
|
|
561
|
+
error.code = body?.code
|
|
562
|
+
reject(error)
|
|
563
|
+
}
|
|
564
|
+
})
|
|
565
|
+
}),
|
|
566
|
+
{
|
|
567
|
+
retries: 5,
|
|
568
|
+
should_retry: (error) => error?.code === 100410 || /rate limited/i.test(String(error?.message || '')),
|
|
569
|
+
backoff_ms: (attempt, error) => {
|
|
570
|
+
const unblock_match = String(error?.message || '').match(/unblocked after (\d+)/i)
|
|
571
|
+
if (unblock_match) {
|
|
572
|
+
return Math.max(parseInt(unblock_match[1], 10) - Date.now(), 0) + 1000
|
|
573
|
+
}
|
|
574
|
+
return attempt * 3000
|
|
575
|
+
},
|
|
576
|
+
},
|
|
577
|
+
)
|
|
578
|
+
return body.data.fills.map((trade) => ({
|
|
579
|
+
order_id: trade.orderId != null ? trade.orderId.toString() : '',
|
|
580
|
+
pair,
|
|
581
|
+
type: trade.isBuyer ? 'buy' : 'sell',
|
|
582
|
+
price: parseFloat(trade.price),
|
|
583
|
+
amount: parseFloat(trade.qty),
|
|
584
|
+
total: parseFloat(trade.quoteQty || parseFloat(trade.price) * parseFloat(trade.qty)),
|
|
585
|
+
close_time: new Date(trade.time),
|
|
586
|
+
fees: {
|
|
587
|
+
[post_process_cur(trade.commissionAsset)]: parseFloat(trade.commission),
|
|
588
|
+
},
|
|
589
|
+
role: trade.isMaker ? 'maker' : 'taker',
|
|
590
|
+
}))
|
|
591
|
+
},
|
|
592
|
+
})
|
|
593
|
+
cb({ success: true, body: sort_history_rows(rows) })
|
|
594
|
+
})().catch((error) => {
|
|
595
|
+
cb({ success: false, error: error_codes.other, body: error?.body })
|
|
596
|
+
})
|
|
597
|
+
}
|
|
598
|
+
this.private_limiter.process(get_history_trades_base, pair, start_time_in_ms, end_time_in_ms, cb)
|
|
599
|
+
}
|
|
530
600
|
get_all_deposits(timeframe_in_ms, cb) {
|
|
531
601
|
let get_all_deposits_base = (timeframe_in_ms, cb) => {
|
|
532
602
|
let [api_key, secret_key] = this.api_secret_key
|
|
@@ -80,6 +80,17 @@ function get_options(request, api_secret_key, url) {
|
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
function get_v2_headers_bitfinex(path, body, api_secret_key) {
|
|
84
|
+
let nonce = (Date.now() * 1000).toString()
|
|
85
|
+
let auth = '/api' + path + nonce + body
|
|
86
|
+
return {
|
|
87
|
+
'bfx-nonce': nonce,
|
|
88
|
+
'bfx-apikey': api_secret_key[0],
|
|
89
|
+
'bfx-signature': get_signature_bitfinex(auth, api_secret_key[1]),
|
|
90
|
+
'Content-Type': 'application/json',
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
83
94
|
module.exports = class Bitfinex extends ExchangeBase {
|
|
84
95
|
constructor(api_key, secret_key, settings) {
|
|
85
96
|
super('bitfinex', settings.id, api_key, secret_key)
|
|
@@ -774,6 +785,71 @@ module.exports = class Bitfinex extends ExchangeBase {
|
|
|
774
785
|
}
|
|
775
786
|
this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
|
|
776
787
|
}
|
|
788
|
+
get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
|
|
789
|
+
let { fetch_split_windows, filter_history_in_range, sort_history_rows } = ExchangeBase.history_utils
|
|
790
|
+
let path = '/v2/auth/r/trades/' + pre_process_pair(pair, true) + '/hist'
|
|
791
|
+
let api_secret_key = this.api_secret_key
|
|
792
|
+
let fetch_window = (window_start_ms, window_stop_ms) =>
|
|
793
|
+
new Promise((resolve, reject) => {
|
|
794
|
+
let params = {
|
|
795
|
+
start: window_start_ms,
|
|
796
|
+
end: window_stop_ms - 1,
|
|
797
|
+
limit: 1000,
|
|
798
|
+
sort: 1,
|
|
799
|
+
}
|
|
800
|
+
let body = JSON.stringify(params)
|
|
801
|
+
let options = {
|
|
802
|
+
headers: get_v2_headers_bitfinex(path, body, api_secret_key),
|
|
803
|
+
}
|
|
804
|
+
this.private_limiter.process(needle.post, 'https://api.bitfinex.com' + path, body, options, (err, res, response_body) => {
|
|
805
|
+
if (typeof response_body === 'string') {
|
|
806
|
+
try {
|
|
807
|
+
response_body = JSON.parse(response_body)
|
|
808
|
+
} catch (e) {}
|
|
809
|
+
}
|
|
810
|
+
if (Array.isArray(response_body)) {
|
|
811
|
+
resolve(
|
|
812
|
+
response_body.map((trade) => {
|
|
813
|
+
let amount = Math.abs(parseFloat(trade[4]))
|
|
814
|
+
let mapped_trade = {
|
|
815
|
+
order_id: (trade[3] || '').toString(),
|
|
816
|
+
trade_id: trade[0].toString(),
|
|
817
|
+
pair: post_process_pair(trade[1]),
|
|
818
|
+
type: parseFloat(trade[4]) >= 0 ? 'buy' : 'sell',
|
|
819
|
+
price: parseFloat(trade[5]),
|
|
820
|
+
amount,
|
|
821
|
+
total: amount * parseFloat(trade[5]),
|
|
822
|
+
close_time: new Date(trade[2]),
|
|
823
|
+
role: trade[8] ? 'maker' : 'taker',
|
|
824
|
+
}
|
|
825
|
+
if (trade[10] && trade[9] !== undefined) {
|
|
826
|
+
mapped_trade.fees = {
|
|
827
|
+
[post_process_cur(trade[10])]: Math.abs(parseFloat(trade[9])),
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
return mapped_trade
|
|
831
|
+
})
|
|
832
|
+
)
|
|
833
|
+
} else if (response_body) {
|
|
834
|
+
reject({ success: false, error: error_codes.other, body: response_body })
|
|
835
|
+
} else {
|
|
836
|
+
reject({ success: false, error: error_codes.timeout })
|
|
837
|
+
}
|
|
838
|
+
})
|
|
839
|
+
})
|
|
840
|
+
;(async () => {
|
|
841
|
+
let trades = await fetch_split_windows({
|
|
842
|
+
start_ms: start_time_in_ms,
|
|
843
|
+
stop_ms: end_time_in_ms,
|
|
844
|
+
initial_window_ms: 30 * 24 * 60 * 60 * 1000,
|
|
845
|
+
page_limit: 1000,
|
|
846
|
+
fetch_window,
|
|
847
|
+
delay_ms: 250,
|
|
848
|
+
})
|
|
849
|
+
trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
|
|
850
|
+
cb({ success: true, body: trades })
|
|
851
|
+
})().catch((error) => cb(error))
|
|
852
|
+
}
|
|
777
853
|
// get_trades_ws (pair, timeframe_in_ms, cb) {
|
|
778
854
|
// if (this.ws.trading.status === 'opened') {
|
|
779
855
|
// if (this.ws.trading.trades_dict[pair]) {
|
package/lib/exchanges/bitget.js
CHANGED
|
@@ -56,6 +56,24 @@ function post_process_cur(cur) {
|
|
|
56
56
|
return cur.toUpperCase()
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
function get_trade_fees_bitget(fee_detail) {
|
|
60
|
+
let detail = fee_detail
|
|
61
|
+
if (typeof detail === 'string') {
|
|
62
|
+
try {
|
|
63
|
+
detail = JSON.parse(detail)
|
|
64
|
+
} catch (e) {
|
|
65
|
+
detail = null
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (!detail || typeof detail !== 'object') return {}
|
|
69
|
+
let fee_coin = detail.feeCoin || detail.feeCoinName || detail.coin
|
|
70
|
+
let fee = parseFloat(detail.totalFee ?? detail.fee ?? detail.feeAmount)
|
|
71
|
+
if (!fee_coin || !Number.isFinite(fee)) return {}
|
|
72
|
+
return {
|
|
73
|
+
[post_process_cur(fee_coin)]: Math.abs(fee),
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
59
77
|
module.exports = class Bitget extends ExchangeBase {
|
|
60
78
|
constructor(api_key, secret_key, settings) {
|
|
61
79
|
super('bitget', settings.id, api_key, secret_key)
|
|
@@ -491,6 +509,70 @@ module.exports = class Bitget extends ExchangeBase {
|
|
|
491
509
|
}
|
|
492
510
|
this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
|
|
493
511
|
}
|
|
512
|
+
get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
|
|
513
|
+
let [api_key, secret_key] = this.api_secret_key
|
|
514
|
+
let { filter_history_in_range, sort_history_rows, split_windows, sleep } = ExchangeBase.history_utils
|
|
515
|
+
let path = '/api/v2/spot/trade/fills'
|
|
516
|
+
let fetch_page = (params) =>
|
|
517
|
+
new Promise((resolve, reject) => {
|
|
518
|
+
let options = get_options(api_key, secret_key, this.passphrase, 'GET', path, params)
|
|
519
|
+
let url = base_url + path + '?' + qs.stringify(params)
|
|
520
|
+
this.private_limiter.process(needle.get, url, options, (err, res, body) => {
|
|
521
|
+
if (body && body.code === '00000' && Array.isArray(body.data)) {
|
|
522
|
+
resolve(body.data)
|
|
523
|
+
} else if (body) {
|
|
524
|
+
reject({ success: false, error: error_codes.other, body })
|
|
525
|
+
} else {
|
|
526
|
+
reject({ success: false, error: error_codes.timeout })
|
|
527
|
+
}
|
|
528
|
+
})
|
|
529
|
+
})
|
|
530
|
+
;(async () => {
|
|
531
|
+
let trades = []
|
|
532
|
+
let windows = split_windows(start_time_in_ms, end_time_in_ms, 90 * 24 * 60 * 60 * 1000)
|
|
533
|
+
for (let [window_start_ms, window_stop_ms] of windows) {
|
|
534
|
+
let cursor
|
|
535
|
+
while (true) {
|
|
536
|
+
let params = {
|
|
537
|
+
symbol: pre_process_pair(pair),
|
|
538
|
+
startTime: window_start_ms.toString(),
|
|
539
|
+
endTime: (window_stop_ms - 1).toString(),
|
|
540
|
+
limit: '100',
|
|
541
|
+
}
|
|
542
|
+
if (cursor) params.idLessThan = cursor
|
|
543
|
+
let rows = await fetch_page(params)
|
|
544
|
+
trades.push(
|
|
545
|
+
...rows.map((trade) => {
|
|
546
|
+
let price = parseFloat(trade.priceAvg || trade.price)
|
|
547
|
+
let amount = parseFloat(trade.size)
|
|
548
|
+
let total = parseFloat(trade.amount)
|
|
549
|
+
let mapped_trade = {
|
|
550
|
+
order_id: trade.orderId.toString(),
|
|
551
|
+
trade_id: (trade.tradeId || trade.id || '').toString(),
|
|
552
|
+
pair: post_process_pair(trade.symbol),
|
|
553
|
+
type: trade.side,
|
|
554
|
+
price,
|
|
555
|
+
amount,
|
|
556
|
+
total: Number.isFinite(total) ? total : price * amount,
|
|
557
|
+
open_time: new Date(parseInt(trade.cTime)),
|
|
558
|
+
close_time: new Date(parseInt(trade.uTime || trade.cTime)),
|
|
559
|
+
role: trade.tradeScope,
|
|
560
|
+
}
|
|
561
|
+
let fees = get_trade_fees_bitget(trade.feeDetail)
|
|
562
|
+
if (_.keys(fees).length > 0) mapped_trade.fees = fees
|
|
563
|
+
return mapped_trade
|
|
564
|
+
})
|
|
565
|
+
)
|
|
566
|
+
if (rows.length < 100) break
|
|
567
|
+
cursor = rows[rows.length - 1].id || rows[rows.length - 1].tradeId
|
|
568
|
+
if (!cursor) break
|
|
569
|
+
await sleep(250)
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
|
|
573
|
+
cb({ success: true, body: trades })
|
|
574
|
+
})().catch((error) => cb(error))
|
|
575
|
+
}
|
|
494
576
|
get_deposits(cur, timeframe_in_ms, cb) {
|
|
495
577
|
let [api_key, secret_key] = this.api_secret_key
|
|
496
578
|
let current = new Date().getTime()
|
package/lib/exchanges/bithumb.js
CHANGED
|
@@ -425,6 +425,9 @@ module.exports = class Bithumb extends ExchangeBase {
|
|
|
425
425
|
}
|
|
426
426
|
this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
|
|
427
427
|
}
|
|
428
|
+
get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
|
|
429
|
+
this._get_history_trades_via_timeframe(pair, start_time_in_ms, end_time_in_ms, cb)
|
|
430
|
+
}
|
|
428
431
|
get_trades_id_type(pair, id, type, timeframe_in_ms, cb) {
|
|
429
432
|
let [cur, quote_cur] = utils.parse_pair(pair)
|
|
430
433
|
let get_trades_id_type_base = (pair, id, timeframe_in_ms, cb) => {
|
package/lib/exchanges/bitkub.js
CHANGED
|
@@ -314,6 +314,9 @@ module.exports = class Bitkub extends ExchangeBase {
|
|
|
314
314
|
}
|
|
315
315
|
this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
|
|
316
316
|
}
|
|
317
|
+
get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
|
|
318
|
+
this._get_history_trades_via_timeframe(pair, start_time_in_ms, end_time_in_ms, cb)
|
|
319
|
+
}
|
|
317
320
|
_get_trades_page(pair, page, cb) {
|
|
318
321
|
let get_trades_base = (pair, page, cb) => {
|
|
319
322
|
let [api_key, secret_key] = this.api_secret_key
|
package/lib/exchanges/bitmex.js
CHANGED
|
@@ -685,6 +685,75 @@ module.exports = class Bitmex extends ExchangeBase {
|
|
|
685
685
|
}
|
|
686
686
|
this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
|
|
687
687
|
}
|
|
688
|
+
get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
|
|
689
|
+
let [api_key, secret_key] = this.api_secret_key
|
|
690
|
+
let { fetch_split_windows, filter_history_in_range, sort_history_rows } = ExchangeBase.history_utils
|
|
691
|
+
let verb = 'GET'
|
|
692
|
+
let endpoint = '/api/v1/execution/tradeHistory'
|
|
693
|
+
let fetch_window = (window_start_ms, window_stop_ms) =>
|
|
694
|
+
new Promise((resolve, reject) => {
|
|
695
|
+
let params = {
|
|
696
|
+
count: 500,
|
|
697
|
+
symbol: pre_process_pair(pair),
|
|
698
|
+
startTime: new Date(window_start_ms).toISOString(),
|
|
699
|
+
endTime: new Date(window_stop_ms - 1).toISOString(),
|
|
700
|
+
}
|
|
701
|
+
let query = qs.stringify(params)
|
|
702
|
+
let nonce = Date.now() + 60 * 1000
|
|
703
|
+
let signature = get_signature_bitmex(verb, endpoint + '?' + query, nonce, null, secret_key)
|
|
704
|
+
let options = get_options(api_key, nonce, signature)
|
|
705
|
+
needle.get(base_url + endpoint + '?' + query, options, (err, res, body) => {
|
|
706
|
+
if (Array.isArray(body)) {
|
|
707
|
+
resolve(
|
|
708
|
+
body
|
|
709
|
+
.filter((trade) => trade.execType === 'Trade')
|
|
710
|
+
.map((trade) => {
|
|
711
|
+
let price = parseFloat(trade.lastPx || trade.avgPx || trade.price)
|
|
712
|
+
let amount = Math.abs(parseFloat(trade.homeNotional))
|
|
713
|
+
if (!Number.isFinite(amount)) {
|
|
714
|
+
let quantity = parseFloat(trade.lastQty || trade.cumQty || trade.foreignNotional)
|
|
715
|
+
amount = Number.isFinite(quantity) && Number.isFinite(price) && price !== 0 ? Math.abs(quantity) / price : 0
|
|
716
|
+
}
|
|
717
|
+
let total = Math.abs(parseFloat(trade.foreignNotional))
|
|
718
|
+
if (!Number.isFinite(total)) total = Math.abs(parseFloat(trade.lastQty || trade.cumQty || 0))
|
|
719
|
+
let mapped_trade = {
|
|
720
|
+
order_id: (trade.orderID || '').toString(),
|
|
721
|
+
trade_id: (trade.execID || trade.trdMatchID || '').toString(),
|
|
722
|
+
pair,
|
|
723
|
+
type: (trade.side || '').toLowerCase(),
|
|
724
|
+
price,
|
|
725
|
+
amount,
|
|
726
|
+
size: total,
|
|
727
|
+
total,
|
|
728
|
+
close_time: new Date(trade.transactTime || trade.timestamp),
|
|
729
|
+
}
|
|
730
|
+
if (trade.lastLiquidityInd === 'AddedLiquidity') mapped_trade.role = 'maker'
|
|
731
|
+
if (trade.lastLiquidityInd === 'RemovedLiquidity') mapped_trade.role = 'taker'
|
|
732
|
+
return mapped_trade
|
|
733
|
+
})
|
|
734
|
+
)
|
|
735
|
+
} else if (body && body.error && body.error.message === 'The system is currently overloaded. Please try again later.') {
|
|
736
|
+
reject({ success: false, error: error_codes.system_overloaded })
|
|
737
|
+
} else if (body) {
|
|
738
|
+
reject({ success: false, error: error_codes.other, body })
|
|
739
|
+
} else {
|
|
740
|
+
reject({ success: false, error: error_codes.timeout })
|
|
741
|
+
}
|
|
742
|
+
})
|
|
743
|
+
})
|
|
744
|
+
;(async () => {
|
|
745
|
+
let trades = await fetch_split_windows({
|
|
746
|
+
start_ms: start_time_in_ms,
|
|
747
|
+
stop_ms: end_time_in_ms,
|
|
748
|
+
initial_window_ms: 30 * 24 * 60 * 60 * 1000,
|
|
749
|
+
page_limit: 500,
|
|
750
|
+
fetch_window,
|
|
751
|
+
delay_ms: 250,
|
|
752
|
+
})
|
|
753
|
+
trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
|
|
754
|
+
cb({ success: true, body: trades })
|
|
755
|
+
})().catch((error) => cb(error))
|
|
756
|
+
}
|
|
688
757
|
static get_precision(cb) {
|
|
689
758
|
let price_precision = {},
|
|
690
759
|
amount_precision = {}
|
package/lib/exchanges/bitrue.js
CHANGED
|
@@ -8,6 +8,7 @@ const zlib = require('zlib')
|
|
|
8
8
|
const ExchangeBase = require('./exchange-base.js')
|
|
9
9
|
const ExchangeWs = require('./exchange-ws.js')
|
|
10
10
|
const JSONbig = require('json-bigint')
|
|
11
|
+
const { fetch_split_windows, sort_history_rows } = ExchangeBase.history_utils
|
|
11
12
|
|
|
12
13
|
const { error_codes } = require('../error_codes.json')
|
|
13
14
|
|
|
@@ -512,6 +513,61 @@ module.exports = class Bitrue extends ExchangeBase {
|
|
|
512
513
|
}
|
|
513
514
|
this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
|
|
514
515
|
}
|
|
516
|
+
get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
|
|
517
|
+
const get_history_trades_base = (pair, start_time_in_ms, end_time_in_ms, cb) => {
|
|
518
|
+
let [api_key, secret_key] = this.api_secret_key
|
|
519
|
+
const options = get_options(api_key, false)
|
|
520
|
+
;(async () => {
|
|
521
|
+
const rows = await fetch_split_windows({
|
|
522
|
+
start_ms: start_time_in_ms,
|
|
523
|
+
stop_ms: end_time_in_ms,
|
|
524
|
+
initial_window_ms: 60 * 60 * 1000,
|
|
525
|
+
page_limit: 1000,
|
|
526
|
+
fetch_window: async (window_start_ms, window_stop_ms) =>
|
|
527
|
+
await new Promise((resolve, reject) => {
|
|
528
|
+
let params = {
|
|
529
|
+
symbol: pre_process_pair(pair),
|
|
530
|
+
startTime: window_start_ms,
|
|
531
|
+
endTime: window_stop_ms - 1,
|
|
532
|
+
limit: 1000,
|
|
533
|
+
timestamp: parseInt(Date.now()),
|
|
534
|
+
}
|
|
535
|
+
let query = qs.stringify(params)
|
|
536
|
+
let sign = get_signature_bitrue(secret_key, query)
|
|
537
|
+
let url = 'https://openapi.bitrue.com/api/v2/myTrades?' + query + `&signature=${sign}`
|
|
538
|
+
needle.get(url, options, (err, res, body) => {
|
|
539
|
+
let parsed_body = typeof body === 'string' ? JSONbig.parse(body) : body
|
|
540
|
+
if (parsed_body && Array.isArray(parsed_body)) {
|
|
541
|
+
resolve(
|
|
542
|
+
parsed_body.map((trade) => ({
|
|
543
|
+
order_id: trade.orderId != null ? trade.orderId.toString() : '',
|
|
544
|
+
pair,
|
|
545
|
+
type: trade.isBuyer ? 'buy' : 'sell',
|
|
546
|
+
price: parseFloat(trade.price),
|
|
547
|
+
amount: parseFloat(trade.qty),
|
|
548
|
+
total: parseFloat(trade.price) * parseFloat(trade.qty),
|
|
549
|
+
close_time: new Date(trade.time),
|
|
550
|
+
fees: {
|
|
551
|
+
[post_process_cur(trade.commissionAsset)]: parseFloat(trade.commission),
|
|
552
|
+
},
|
|
553
|
+
role: trade.isMaker ? 'maker' : 'taker',
|
|
554
|
+
})),
|
|
555
|
+
)
|
|
556
|
+
} else if (parsed_body) {
|
|
557
|
+
reject({ error: error_codes.other, body: parsed_body })
|
|
558
|
+
} else {
|
|
559
|
+
reject({ error: error_codes.timeout })
|
|
560
|
+
}
|
|
561
|
+
})
|
|
562
|
+
}),
|
|
563
|
+
})
|
|
564
|
+
cb({ success: true, body: sort_history_rows(rows) })
|
|
565
|
+
})().catch((error) => {
|
|
566
|
+
cb({ success: false, error: error?.error || error_codes.other, body: error?.body })
|
|
567
|
+
})
|
|
568
|
+
}
|
|
569
|
+
this.private_limiter.process(get_history_trades_base, pair, start_time_in_ms, end_time_in_ms, cb)
|
|
570
|
+
}
|
|
515
571
|
get_deposits(cur, timeframe_in_ms, cb) {
|
|
516
572
|
let get_deposits_base = (timeframe_in_ms, cb) => {
|
|
517
573
|
let [api_key, secret_key] = this.api_secret_key
|