@icgio/icg-exchanges 1.40.50 → 1.41.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.
Files changed (42) hide show
  1. package/lib/exchanges/ascendex.js +1 -3
  2. package/lib/exchanges/biconomy.js +2 -2
  3. package/lib/exchanges/binance.js +1 -0
  4. package/lib/exchanges/bingx.js +2 -1
  5. package/lib/exchanges/bitget.js +1 -0
  6. package/lib/exchanges/bithumb.js +1 -3
  7. package/lib/exchanges/bitkub.js +1 -3
  8. package/lib/exchanges/bitmart.js +49 -32
  9. package/lib/exchanges/bitmex.js +1 -0
  10. package/lib/exchanges/bitrue.js +2 -1
  11. package/lib/exchanges/bitstamp.js +1 -0
  12. package/lib/exchanges/blofin.js +1 -0
  13. package/lib/exchanges/btse.js +1 -3
  14. package/lib/exchanges/bybit.js +48 -35
  15. package/lib/exchanges/coinbase.js +3 -1
  16. package/lib/exchanges/coinstore.js +1 -3
  17. package/lib/exchanges/coinw.js +48 -32
  18. package/lib/exchanges/cryptocom.js +1 -0
  19. package/lib/exchanges/deepcoin.js +1 -0
  20. package/lib/exchanges/digifinex.js +1 -0
  21. package/lib/exchanges/exchange-base.js +12 -46
  22. package/lib/exchanges/fastex.js +1 -3
  23. package/lib/exchanges/gate.js +49 -34
  24. package/lib/exchanges/gemini.js +1 -0
  25. package/lib/exchanges/hashkey.js +1 -0
  26. package/lib/exchanges/hashkeyglobal.js +1 -0
  27. package/lib/exchanges/hitbtc.js +44 -27
  28. package/lib/exchanges/hkbitex.js +1 -3
  29. package/lib/exchanges/htx.js +4 -3
  30. package/lib/exchanges/indodax.js +1 -3
  31. package/lib/exchanges/kraken.js +1 -0
  32. package/lib/exchanges/kucoin.js +2 -1
  33. package/lib/exchanges/lbank.js +2 -1
  34. package/lib/exchanges/mexc.js +2 -1
  35. package/lib/exchanges/okx.js +51 -34
  36. package/lib/exchanges/phemex.js +47 -31
  37. package/lib/exchanges/poloniex.js +43 -27
  38. package/lib/exchanges/swft.js +1 -3
  39. package/lib/exchanges/upbit.js +1 -0
  40. package/lib/exchanges/weex.js +1 -3
  41. package/lib/exchanges/xt.js +2 -1
  42. package/package.json +1 -1
@@ -10,6 +10,7 @@ const ExchangeBase = require('./exchange-base.js')
10
10
 
11
11
  const { error_codes } = require('../error_codes.json')
12
12
  const { utils, rate_limiter: Limiter } = require('@icgio/icg-utils')
13
+ const { fetch_split_windows, sort_history_rows } = ExchangeBase.history_utils
13
14
 
14
15
  function pre_process_pair(pair) {
15
16
  let [cur, quote_cur] = utils.parse_pair(pair)
@@ -663,41 +664,55 @@ module.exports = class Gate extends ExchangeBase {
663
664
  }
664
665
  get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
665
666
  let get_history_trades_base = (pair, start_time_in_ms, end_time_in_ms, cb) => {
666
- let params = {
667
- currency_pair: pre_process_pair(pair),
668
- from: parseInt(start_time_in_ms / 1000),
669
- limit: 1000,
670
- to: parseInt(end_time_in_ms / 1000),
671
- }
672
667
  let [api_key, secret_key] = this.api_secret_key
673
- let timestamp = parseInt(Date.now() / 1000)
674
- let path = '/api/v4/spot/my_trades'
675
- let sign = get_signature_gatev4('GET', path, params, secret_key, timestamp)
676
- let options = get_options(api_key, timestamp, sign)
677
- let url = 'https://api.gateio.ws' + path + '?' + qs.stringify(params)
678
- needle.get(url, options, (err, res, body) => {
679
- if (body && Array.isArray(body)) {
680
- let trades = body.map((trade) => {
681
- return {
682
- order_id: trade.order_id,
683
- pair,
684
- type: trade.side,
685
- price: parseFloat(trade.price),
686
- amount: parseFloat(trade.amount),
687
- total: parseFloat(trade.price) * parseFloat(trade.amount),
688
- close_time: new Date(parseInt(trade.create_time_ms)),
689
- fees: {
690
- [post_process_cur(trade.fee_currency)]: parseFloat(trade.fee),
691
- },
692
- }
693
- })
694
-
695
- cb({ success: true, body: trades })
696
- } else if (body) {
697
- cb({ success: false, error: error_codes.other, body })
698
- } else {
699
- cb({ success: false, error: error_codes.timeout })
700
- }
668
+ ;(async () => {
669
+ const rows = await fetch_split_windows({
670
+ start_ms: start_time_in_ms,
671
+ stop_ms: end_time_in_ms,
672
+ initial_window_ms: Math.max(end_time_in_ms - start_time_in_ms, 60 * 1000),
673
+ page_limit: 1000,
674
+ fetch_window: async (window_start_ms, window_stop_ms) =>
675
+ await new Promise((resolve, reject) => {
676
+ let params = {
677
+ currency_pair: pre_process_pair(pair),
678
+ from: parseInt(window_start_ms / 1000),
679
+ limit: 1000,
680
+ to: parseInt(window_stop_ms / 1000),
681
+ }
682
+ let timestamp = parseInt(Date.now() / 1000)
683
+ let path = '/api/v4/spot/my_trades'
684
+ let sign = get_signature_gatev4('GET', path, params, secret_key, timestamp)
685
+ let options = get_options(api_key, timestamp, sign)
686
+ let url = 'https://api.gateio.ws' + path + '?' + qs.stringify(params)
687
+ needle.get(url, options, (err, res, body) => {
688
+ if (body && Array.isArray(body)) {
689
+ resolve(
690
+ body.map((trade) => ({
691
+ order_id: trade.order_id,
692
+ pair,
693
+ type: trade.side,
694
+ price: parseFloat(trade.price),
695
+ amount: parseFloat(trade.amount),
696
+ total: parseFloat(trade.price) * parseFloat(trade.amount),
697
+ close_time: new Date(parseInt(trade.create_time_ms)),
698
+ fees: {
699
+ [post_process_cur(trade.fee_currency)]: parseFloat(trade.fee),
700
+ },
701
+ role: trade.role,
702
+ })),
703
+ )
704
+ } else if (body) {
705
+ reject({ error: error_codes.other, body })
706
+ } else {
707
+ reject({ error: error_codes.timeout })
708
+ }
709
+ })
710
+ }),
711
+ })
712
+ const merged = utils.merge_trades_with_same_id(rows)
713
+ cb({ success: true, body: sort_history_rows(merged) })
714
+ })().catch((error) => {
715
+ cb({ success: false, error: error?.error || error_codes.other, body: error?.body })
701
716
  })
702
717
  }
703
718
  this.private_limiter.process(get_history_trades_base, pair, start_time_in_ms, end_time_in_ms, cb)
@@ -422,6 +422,7 @@ module.exports = class Gemini extends ExchangeBase {
422
422
  await sleep(250)
423
423
  }
424
424
  trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
425
+ trades = utils.merge_trades_with_same_id(trades)
425
426
  cb({ success: true, body: trades })
426
427
  })().catch((error) => cb(error))
427
428
  }
@@ -532,6 +532,7 @@ module.exports = class Hashkey extends ExchangeBase {
532
532
  delay_ms: 250,
533
533
  })
534
534
  trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
535
+ trades = utils.merge_trades_with_same_id(trades)
535
536
  cb({ success: true, body: trades })
536
537
  })().catch((error) => cb(error))
537
538
  }
@@ -520,6 +520,7 @@ module.exports = class Hashkeyglobal extends ExchangeBase {
520
520
  delay_ms: 250,
521
521
  })
522
522
  trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
523
+ trades = utils.merge_trades_with_same_id(trades)
523
524
  cb({ success: true, body: trades })
524
525
  })().catch((error) => cb(error))
525
526
  }
@@ -6,6 +6,7 @@ const needle = require('needle')
6
6
  const ExchangeWs = require('./exchange-ws.js')
7
7
 
8
8
  const ExchangeBase = require('./exchange-base.js')
9
+ const { fetch_split_windows, sort_history_rows } = ExchangeBase.history_utils
9
10
 
10
11
  const { error_codes } = require('../error_codes.json')
11
12
  const { utils, rate_limiter: Limiter } = require('@icgio/icg-utils')
@@ -651,34 +652,50 @@ module.exports = class Hitbtc extends ExchangeBase {
651
652
  })
652
653
  }
653
654
  get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
654
- let from = new Date(start_time_in_ms).toISOString(),
655
- end = new Date(end_time_in_ms).toISOString()
656
- let params = {
657
- from: from,
658
- symbol: pre_process_pair(pair),
659
- till: end,
660
- }
661
- let url = `https://api.hitbtc.com/api/3/spot/history/trade?` + qs.stringify(params)
662
- this.private_limiter.process(needle.get, url, get_signature_hitbtc(this.api_secret_key), (err, res, body) => {
663
- if (body && Array.isArray(body) && body[0]) {
664
- let result = body.map((o) => {
665
- return {
666
- order_id: o.client_order_id.toString(),
667
- pair: post_process_pair(o.symbol),
668
- type: o.side,
669
- price: parseFloat(o.price),
670
- amount: parseFloat(o.quantity),
671
- total: parseFloat(o.price) * parseFloat(o.quantity),
672
- close_time: new Date(o.timestamp),
673
- }
655
+ let get_history_trades_base = (pair, start_time_in_ms, end_time_in_ms, cb) => {
656
+ ;(async () => {
657
+ const rows = await fetch_split_windows({
658
+ start_ms: start_time_in_ms,
659
+ stop_ms: end_time_in_ms,
660
+ initial_window_ms: Math.max(end_time_in_ms - start_time_in_ms, 60 * 1000),
661
+ page_limit: 1000,
662
+ fetch_window: async (window_start_ms, window_stop_ms) =>
663
+ await new Promise((resolve, reject) => {
664
+ let params = {
665
+ from: new Date(window_start_ms).toISOString(),
666
+ symbol: pre_process_pair(pair),
667
+ till: new Date(window_stop_ms).toISOString(),
668
+ limit: 1000,
669
+ }
670
+ let url = `https://api.hitbtc.com/api/3/spot/history/trade?` + qs.stringify(params)
671
+ needle.get(url, get_signature_hitbtc(this.api_secret_key), (err, res, body) => {
672
+ if (body && Array.isArray(body)) {
673
+ resolve(
674
+ body.map((o) => ({
675
+ order_id: o.client_order_id.toString(),
676
+ pair: post_process_pair(o.symbol),
677
+ type: o.side,
678
+ price: parseFloat(o.price),
679
+ amount: parseFloat(o.quantity),
680
+ total: parseFloat(o.price) * parseFloat(o.quantity),
681
+ close_time: new Date(o.timestamp),
682
+ })),
683
+ )
684
+ } else if (body) {
685
+ reject({ error: error_codes.other, body })
686
+ } else {
687
+ reject({ error: error_codes.timeout })
688
+ }
689
+ })
690
+ }),
674
691
  })
675
- cb({ success: true, body: result })
676
- } else if (body) {
677
- cb({ success: false, error: error_codes.other, body })
678
- } else {
679
- cb({ success: false, error: error_codes.timeout })
680
- }
681
- })
692
+ const merged = utils.merge_trades_with_same_id(rows)
693
+ cb({ success: true, body: sort_history_rows(merged) })
694
+ })().catch((error) => {
695
+ cb({ success: false, error: error?.error || error_codes.other, body: error?.body })
696
+ })
697
+ }
698
+ this.private_limiter.process(get_history_trades_base, pair, start_time_in_ms, end_time_in_ms, cb)
682
699
  }
683
700
  // get_all_trades_ws (timeframe_in_ms, cb) {
684
701
  //
@@ -557,9 +557,7 @@ module.exports = class Hkbitex extends ExchangeBase {
557
557
  }
558
558
  this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
559
559
  }
560
- get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
561
- this._get_history_trades_via_timeframe(pair, start_time_in_ms, end_time_in_ms, cb)
562
- }
560
+ // get_history_trades not implemented, no native absolute-window endpoint
563
561
  static get_precision(cb) {
564
562
  let price_precision = {},
565
563
  amount_precision = {}
@@ -515,7 +515,7 @@ module.exports = class Htx extends ExchangeBase {
515
515
  price: parseFloat(t.price),
516
516
  amount: parseFloat(t['filled-amount']),
517
517
  total: parseFloat(t['filled-amount']) * parseFloat(t.price),
518
- open_time: new Date(t['created-at']),
518
+ close_time: new Date(t['created-at']),
519
519
  fees: {
520
520
  [post_process_cur(t['fee-currency'])]: parseFloat(t['filled-fees']),
521
521
  },
@@ -525,7 +525,7 @@ module.exports = class Htx extends ExchangeBase {
525
525
  if (trade_id != null) trade.trade_id = trade_id.toString()
526
526
  return trade
527
527
  })
528
- .filter((t) => t.open_time > new Date() - timeframe_in_ms)
528
+ .filter((t) => t.close_time > new Date() - timeframe_in_ms)
529
529
  trades = utils.merge_trades_with_same_id(trades)
530
530
  cb({ success: true, body: trades })
531
531
  } else if (body && body.status === 'error' && body['err-code'] === 'api-signature-not-valid') {
@@ -607,7 +607,8 @@ module.exports = class Htx extends ExchangeBase {
607
607
  })
608
608
  }),
609
609
  })
610
- cb({ success: true, body: sort_history_rows(rows) })
610
+ const merged = utils.merge_trades_with_same_id(rows)
611
+ cb({ success: true, body: sort_history_rows(merged) })
611
612
  })().catch((error) => {
612
613
  cb({ success: false, error: error?.error || error_codes.other, body: error?.body })
613
614
  })
@@ -391,9 +391,7 @@ module.exports = class Indodax extends ExchangeBase {
391
391
  }
392
392
  this.private_limiter.process(get_trades_base, pair, cb)
393
393
  }
394
- get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
395
- this._get_history_trades_via_timeframe(pair, start_time_in_ms, end_time_in_ms, cb)
396
- }
394
+ // get_history_trades not implemented, no native absolute-window endpoint
397
395
  static get_min_amount(cb) {
398
396
  let min_quote_cur_amount = {}
399
397
  needle.get('https://indodax.com/api/pairs', (err, res, body) => {
@@ -393,6 +393,7 @@ module.exports = class Kraken extends ExchangeBase {
393
393
  }
394
394
  trades = trades.filter((trade) => trade.pair === pair)
395
395
  trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
396
+ trades = utils.merge_trades_with_same_id(trades)
396
397
  cb({ success: true, body: trades })
397
398
  })().catch((error) => cb(error))
398
399
  }
@@ -602,7 +602,8 @@ module.exports = class Kucoin extends ExchangeBase {
602
602
  if (current_page < total_pages && trades.length > 0) {
603
603
  fetch_page(current_page + 1, all_trades)
604
604
  } else {
605
- cb({ success: true, body: dedupe_history_trades(all_trades) })
605
+ all_trades = utils.merge_trades_with_same_id(dedupe_history_trades(all_trades))
606
+ cb({ success: true, body: all_trades })
606
607
  }
607
608
  } else if (body && body.code === '429000') {
608
609
  cb({ success: false, error: error_codes.service_unavailable, body })
@@ -670,7 +670,8 @@ module.exports = class Lbank extends ExchangeBase {
670
670
  await sleep(50)
671
671
  }
672
672
  }
673
- cb({ success: true, body: sort_history_rows(filter_history_in_range(rows, start_time_in_ms, end_time_in_ms)) })
673
+ const merged = utils.merge_trades_with_same_id(rows)
674
+ cb({ success: true, body: sort_history_rows(filter_history_in_range(merged, start_time_in_ms, end_time_in_ms)) })
674
675
  })().catch((error) => {
675
676
  cb({ success: false, error: error?.error || error_codes.other, body: error?.body })
676
677
  })
@@ -567,7 +567,8 @@ module.exports = class Mexc extends ExchangeBase {
567
567
  })
568
568
  }),
569
569
  })
570
- cb({ success: true, body: sort_history_rows(rows) })
570
+ const merged = utils.merge_trades_with_same_id(rows)
571
+ cb({ success: true, body: sort_history_rows(merged) })
571
572
  })().catch((error) => {
572
573
  cb({ success: false, error: error?.error || error_codes.other, body: error?.body })
573
574
  })
@@ -11,6 +11,7 @@ const ExchangeWs = require('./exchange-ws.js')
11
11
  const { error_codes } = require('../error_codes.json')
12
12
 
13
13
  const { utils, rate_limiter: Limiter } = require('@icgio/icg-utils')
14
+ const { fetch_split_windows, sort_history_rows } = ExchangeBase.history_utils
14
15
 
15
16
  let base_url = 'https://www.okx.com'
16
17
 
@@ -652,41 +653,57 @@ module.exports = class Okx extends ExchangeBase {
652
653
  this.private_limiter.process(get_all_trades_base, timeframe_in_ms, cb, 2)
653
654
  }
654
655
  get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
655
- let [api_key, secret_key] = this.api_secret_key
656
656
  let get_history_trades_base = (pair, start_time_in_ms, end_time_in_ms, cb) => {
657
- let params = {
658
- instId: pre_process_pair(pair),
659
- instType: 'SPOT',
660
- begin: start_time_in_ms,
661
- end: end_time_in_ms,
662
- }
663
- let path = '/api/v5/trade/fills-history?' + qs.stringify(params)
664
- let options = get_options(api_key, secret_key, this.passphrase, 'GET', path, '')
665
- needle.get(base_url + path, options, (err, res, body) => {
666
- body = parse_body(body)
667
- if (body && body.code === '0' && body.data) {
668
- let trades = body.data.map((t) => ({
669
- order_id: t.ordId.toString(),
670
- ...(t.billId != null ? { trade_id: t.billId.toString() } : {}),
671
- pair: post_process_pair(t.instId),
672
- type: t.side,
673
- price: parseFloat(t.fillPx),
674
- amount: parseFloat(t.fillSz),
675
- total: parseFloat(t.fillPx) * parseFloat(t.fillSz),
676
- close_time: new Date(parseInt(t.ts)),
677
- fees: {
678
- [post_process_cur(t.feeCcy)]: -parseFloat(t.fee),
679
- },
680
- }))
681
- trades = utils.merge_trades_with_same_id(trades)
682
- cb({ success: true, body: trades })
683
- } else if (body && (body.code === '50026' || body.code === '50001')) {
684
- cb({ success: false, error: error_codes.service_unavailable, body })
685
- } else if (body) {
686
- cb({ success: false, error: error_codes.other, body })
687
- } else {
688
- cb({ success: false, error: error_codes.timeout })
689
- }
657
+ let [api_key, secret_key] = this.api_secret_key
658
+ ;(async () => {
659
+ const rows = await fetch_split_windows({
660
+ start_ms: start_time_in_ms,
661
+ stop_ms: end_time_in_ms,
662
+ initial_window_ms: Math.max(end_time_in_ms - start_time_in_ms, 60 * 1000),
663
+ page_limit: 100,
664
+ fetch_window: async (window_start_ms, window_stop_ms) =>
665
+ await new Promise((resolve, reject) => {
666
+ let params = {
667
+ instId: pre_process_pair(pair),
668
+ instType: 'SPOT',
669
+ begin: window_start_ms,
670
+ end: window_stop_ms,
671
+ }
672
+ let path = '/api/v5/trade/fills-history?' + qs.stringify(params)
673
+ let options = get_options(api_key, secret_key, this.passphrase, 'GET', path, '')
674
+ needle.get(base_url + path, options, (err, res, body) => {
675
+ body = parse_body(body)
676
+ if (body && body.code === '0' && body.data) {
677
+ resolve(
678
+ body.data.map((t) => ({
679
+ order_id: t.ordId.toString(),
680
+ ...(t.billId != null ? { trade_id: t.billId.toString() } : {}),
681
+ pair: post_process_pair(t.instId),
682
+ type: t.side,
683
+ price: parseFloat(t.fillPx),
684
+ amount: parseFloat(t.fillSz),
685
+ total: parseFloat(t.fillPx) * parseFloat(t.fillSz),
686
+ close_time: new Date(parseInt(t.ts)),
687
+ fees: {
688
+ [post_process_cur(t.feeCcy)]: -parseFloat(t.fee),
689
+ },
690
+ role: t.execType === 'M' ? 'maker' : 'taker',
691
+ })),
692
+ )
693
+ } else if (body && (body.code === '50026' || body.code === '50001')) {
694
+ reject({ error: error_codes.service_unavailable, body })
695
+ } else if (body) {
696
+ reject({ error: error_codes.other, body })
697
+ } else {
698
+ reject({ error: error_codes.timeout })
699
+ }
700
+ })
701
+ }),
702
+ })
703
+ const merged = utils.merge_trades_with_same_id(rows)
704
+ cb({ success: true, body: sort_history_rows(merged) })
705
+ })().catch((error) => {
706
+ cb({ success: false, error: error?.error || error_codes.other, body: error?.body })
690
707
  })
691
708
  }
692
709
  this.private_limiter.process(get_history_trades_base, pair, start_time_in_ms, end_time_in_ms, cb)
@@ -7,6 +7,7 @@ const needle = require('needle')
7
7
  const ExchangeWs = require('./exchange-ws.js')
8
8
 
9
9
  const ExchangeBase = require('./exchange-base.js')
10
+ const { fetch_split_windows, sort_history_rows } = ExchangeBase.history_utils
10
11
 
11
12
  const { error_codes } = require('../error_codes.json')
12
13
 
@@ -1169,37 +1170,52 @@ module.exports = class Phemex extends ExchangeBase {
1169
1170
  get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
1170
1171
  let get_history_trades_base = (pair, start_time_in_ms, end_time_in_ms, cb) => {
1171
1172
  let [api_key, secret_key] = this.api_secret_key
1172
- let params = {
1173
- symbol: pre_process_pair(pair),
1174
- limit: 200,
1175
- start: start_time_in_ms,
1176
- end: end_time_in_ms,
1177
- }
1178
- let path = '/api-data/spots/trades'
1179
- let options = get_options(api_key, secret_key, path, params, '')
1180
- let url = base_url + path + '?' + qs.stringify(params)
1181
- needle.get(url, options, (err, res, body) => {
1182
- body = parse_body(body)
1183
- if (body && body.code === 0 && body.data && Array.isArray(body.data.rows)) {
1184
- let trades = body.data.rows.map((t) => ({
1185
- order_id: t.orderID.toString(),
1186
- pair,
1187
- type: t.side.toLowerCase(),
1188
- price: t.execPriceEp / price_scale_factor(pair),
1189
- amount: t.execBaseQtyEv / amount_scale_factor(pair),
1190
- total: t.execQuoteQtyEv / price_scale_factor(pair),
1191
- close_time: new Date(t.transactTimeNs / 1e6),
1192
- fees: {
1193
- [post_process_cur(t.quoteCurrency)]: parseFloat(t.execFeeEv) / price_scale_factor(pair),
1194
- },
1195
- }))
1196
- trades = utils.merge_trades_with_same_id(trades)
1197
- cb({ success: true, body: trades })
1198
- } else if (body) {
1199
- cb({ success: false, error: error_codes.other, body })
1200
- } else {
1201
- cb({ success: false, error: error_codes.timeout })
1202
- }
1173
+ ;(async () => {
1174
+ const rows = await fetch_split_windows({
1175
+ start_ms: start_time_in_ms,
1176
+ stop_ms: end_time_in_ms,
1177
+ initial_window_ms: Math.max(end_time_in_ms - start_time_in_ms, 60 * 1000),
1178
+ page_limit: 200,
1179
+ fetch_window: async (window_start_ms, window_stop_ms) =>
1180
+ await new Promise((resolve, reject) => {
1181
+ let params = {
1182
+ symbol: pre_process_pair(pair),
1183
+ limit: 200,
1184
+ start: window_start_ms,
1185
+ end: window_stop_ms,
1186
+ }
1187
+ let path = '/api-data/spots/trades'
1188
+ let options = get_options(api_key, secret_key, path, params, '')
1189
+ let url = base_url + path + '?' + qs.stringify(params)
1190
+ needle.get(url, options, (err, res, body) => {
1191
+ body = parse_body(body)
1192
+ if (body && body.code === 0 && body.data && Array.isArray(body.data.rows)) {
1193
+ resolve(
1194
+ body.data.rows.map((t) => ({
1195
+ order_id: t.orderID.toString(),
1196
+ pair,
1197
+ type: t.side.toLowerCase(),
1198
+ price: t.execPriceEp / price_scale_factor(pair),
1199
+ amount: t.execBaseQtyEv / amount_scale_factor(pair),
1200
+ total: t.execQuoteQtyEv / price_scale_factor(pair),
1201
+ close_time: new Date(t.transactTimeNs / 1e6),
1202
+ fees: {
1203
+ [post_process_cur(t.quoteCurrency)]: parseFloat(t.execFeeEv) / price_scale_factor(pair),
1204
+ },
1205
+ })),
1206
+ )
1207
+ } else if (body) {
1208
+ reject({ error: error_codes.other, body })
1209
+ } else {
1210
+ reject({ error: error_codes.timeout })
1211
+ }
1212
+ })
1213
+ }),
1214
+ })
1215
+ const merged = utils.merge_trades_with_same_id(rows)
1216
+ cb({ success: true, body: sort_history_rows(merged) })
1217
+ })().catch((error) => {
1218
+ cb({ success: false, error: error?.error || error_codes.other, body: error?.body })
1203
1219
  })
1204
1220
  }
1205
1221
  this.private_limiter.process(get_history_trades_base, pair, start_time_in_ms, end_time_in_ms, cb)
@@ -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 { fetch_split_windows, sort_history_rows } = ExchangeBase.history_utils
10
11
 
11
12
  const { error_codes } = require('../error_codes.json')
12
13
  const { utils, rate_limiter: Limiter } = require('@icgio/icg-utils')
@@ -442,33 +443,48 @@ module.exports = class Poloniex extends ExchangeBase {
442
443
  }
443
444
  get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
444
445
  let get_history_trades_base = (pair, start_time_in_ms, end_time_in_ms, cb) => {
445
- let path = '/trades',
446
- params = {
447
- startTime: start_time_in_ms,
448
- symbols: pre_process_pair(pair),
449
- endTime: end_time_in_ms,
450
- }
451
- let options = get_options('GET', path, params, this.api_secret_key)
452
- let url = base_url + path + '?' + qs.stringify(params)
453
- needle.get(url, options, (err, res, body) => {
454
- if (body && Array.isArray(body)) {
455
- let result = (body = body.map((trade) => {
456
- return {
457
- order_id: trade.orderId,
458
- pair: post_process_pair(trade.symbol),
459
- type: trade.side.toLowerCase(),
460
- price: parseFloat(trade.price),
461
- amount: parseFloat(trade.quantity),
462
- total: parseFloat(trade.price) * parseFloat(trade.quantity),
463
- close_time: new Date(trade.createTime),
464
- }
465
- }))
466
- cb({ success: true, body: result })
467
- } else if (body) {
468
- cb({ success: false, error: error_codes.other, body })
469
- } else {
470
- cb({ success: false, error: error_codes.timeout })
471
- }
446
+ ;(async () => {
447
+ const rows = await fetch_split_windows({
448
+ start_ms: start_time_in_ms,
449
+ stop_ms: end_time_in_ms,
450
+ initial_window_ms: Math.max(end_time_in_ms - start_time_in_ms, 60 * 1000),
451
+ page_limit: 500,
452
+ fetch_window: async (window_start_ms, window_stop_ms) =>
453
+ await new Promise((resolve, reject) => {
454
+ let path = '/trades',
455
+ params = {
456
+ startTime: window_start_ms,
457
+ symbols: pre_process_pair(pair),
458
+ endTime: window_stop_ms,
459
+ limit: 500,
460
+ }
461
+ let options = get_options('GET', path, params, this.api_secret_key)
462
+ let url = base_url + path + '?' + qs.stringify(params)
463
+ needle.get(url, options, (err, res, body) => {
464
+ if (body && Array.isArray(body)) {
465
+ resolve(
466
+ body.map((trade) => ({
467
+ order_id: trade.orderId,
468
+ pair: post_process_pair(trade.symbol),
469
+ type: trade.side.toLowerCase(),
470
+ price: parseFloat(trade.price),
471
+ amount: parseFloat(trade.quantity),
472
+ total: parseFloat(trade.price) * parseFloat(trade.quantity),
473
+ close_time: new Date(trade.createTime),
474
+ })),
475
+ )
476
+ } else if (body) {
477
+ reject({ error: error_codes.other, body })
478
+ } else {
479
+ reject({ error: error_codes.timeout })
480
+ }
481
+ })
482
+ }),
483
+ })
484
+ const merged = utils.merge_trades_with_same_id(rows)
485
+ cb({ success: true, body: sort_history_rows(merged) })
486
+ })().catch((error) => {
487
+ cb({ success: false, error: error?.error || error_codes.other, body: error?.body })
472
488
  })
473
489
  }
474
490
  this.private_limiter.process(get_history_trades_base, pair, start_time_in_ms, end_time_in_ms, cb)
@@ -416,9 +416,7 @@ module.exports = class Swft extends ExchangeBase {
416
416
  }
417
417
  this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
418
418
  }
419
- get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
420
- this._get_history_trades_via_timeframe(pair, start_time_in_ms, end_time_in_ms, cb)
421
- }
419
+ // get_history_trades not implemented, no native absolute-window endpoint
422
420
  // get_all_deposits(timeframe_in_ms, cb) {
423
421
  //
424
422
  // }
@@ -429,6 +429,7 @@ module.exports = class Upbit extends ExchangeBase {
429
429
  }
430
430
  }
431
431
  trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
432
+ trades = utils.merge_trades_with_same_id(trades)
432
433
  cb({ success: true, body: trades })
433
434
  })().catch((error) => cb(error))
434
435
  }
@@ -393,9 +393,7 @@ module.exports = class Weex extends ExchangeBase {
393
393
  }
394
394
  this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
395
395
  }
396
- get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
397
- this._get_history_trades_via_timeframe(pair, start_time_in_ms, end_time_in_ms, cb)
398
- }
396
+ // get_history_trades not implemented, no native absolute-window endpoint
399
397
  get_all_deposits(timeframe_in_ms, cb) {
400
398
  let get_all_deposits_base = (cb) => {
401
399
  let [api_key, secret_key] = this.api_secret_key