@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
@@ -479,9 +479,7 @@ module.exports = class Ascendex extends ExchangeBase {
479
479
  }
480
480
  this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
481
481
  }
482
- get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
483
- this._get_history_trades_via_timeframe(pair, start_time_in_ms, end_time_in_ms, cb)
484
- }
482
+ // get_history_trades not implemented, no native absolute-window endpoint
485
483
  static get_min_amount(cb) {
486
484
  let min_cur_amount = {}
487
485
  needle.get(base_url + '/api/pro/v1/cash/products', (err, res, body) => {
@@ -590,7 +590,7 @@ module.exports = class Biconomy extends ExchangeBase {
590
590
  for (const record of records) {
591
591
  const close_time_ms = Math.floor(parseFloat(record.ftime) * 1000)
592
592
  if (close_time_ms < start_time_in_ms) {
593
- cb({ success: true, body: sort_history_rows(filter_history_in_range(rows, start_time_in_ms, end_time_in_ms)) })
593
+ cb({ success: true, body: utils.merge_trades_with_same_id(sort_history_rows(filter_history_in_range(rows, start_time_in_ms, end_time_in_ms))) })
594
594
  return
595
595
  }
596
596
  if (close_time_ms >= end_time_in_ms || parseFloat(record.deal_stock) <= 0) continue
@@ -612,7 +612,7 @@ module.exports = class Biconomy extends ExchangeBase {
612
612
  offset += limit
613
613
  await sleep(50)
614
614
  }
615
- cb({ success: true, body: sort_history_rows(filter_history_in_range(rows, start_time_in_ms, end_time_in_ms)) })
615
+ cb({ success: true, body: utils.merge_trades_with_same_id(sort_history_rows(filter_history_in_range(rows, start_time_in_ms, end_time_in_ms))) })
616
616
  })().catch((error) => {
617
617
  cb({ success: false, error: error?.error || error_codes.other, body: error?.body })
618
618
  })
@@ -715,6 +715,7 @@ module.exports = class Binance extends ExchangeBase {
715
715
  delay_ms: 250,
716
716
  })
717
717
  trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
718
+ trades = utils.merge_trades_with_same_id(trades)
718
719
  cb({ success: true, body: trades })
719
720
  })().catch((error) => cb(error))
720
721
  }
@@ -591,7 +591,8 @@ module.exports = class Bingx extends ExchangeBase {
591
591
  }))
592
592
  },
593
593
  })
594
- cb({ success: true, body: sort_history_rows(rows) })
594
+ const merged = utils.merge_trades_with_same_id(sort_history_rows(rows))
595
+ cb({ success: true, body: merged })
595
596
  })().catch((error) => {
596
597
  cb({ success: false, error: error_codes.other, body: error?.body })
597
598
  })
@@ -571,6 +571,7 @@ module.exports = class Bitget extends ExchangeBase {
571
571
  }
572
572
  }
573
573
  trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
574
+ trades = utils.merge_trades_with_same_id(trades)
574
575
  cb({ success: true, body: trades })
575
576
  })().catch((error) => cb(error))
576
577
  }
@@ -425,9 +425,7 @@ 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
+ // get_history_trades not implemented, no native absolute-window endpoint
431
429
  get_trades_id_type(pair, id, type, timeframe_in_ms, cb) {
432
430
  let [cur, quote_cur] = utils.parse_pair(pair)
433
431
  let get_trades_id_type_base = (pair, id, timeframe_in_ms, cb) => {
@@ -314,9 +314,7 @@ 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
+ // get_history_trades not implemented, no native absolute-window endpoint
320
318
  _get_trades_page(pair, page, cb) {
321
319
  let get_trades_base = (pair, page, cb) => {
322
320
  let [api_key, secret_key] = this.api_secret_key
@@ -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 parse_body(body) {
15
16
  if (body) {
@@ -529,38 +530,54 @@ module.exports = class Bitmart extends ExchangeBase {
529
530
  get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
530
531
  let get_history_trades_base = (pair, start_time_in_ms, end_time_in_ms, cb) => {
531
532
  let [api_key, secret_key] = this.api_secret_key
532
- let params = {
533
- endTime: end_time_in_ms,
534
- limit: 200,
535
- orderMode: 'spot',
536
- startTime: start_time_in_ms,
537
- symbol: pre_process_pair(pair),
538
- }
539
- let url = 'https://api-cloud.bitmart.com/spot/v4/query/trades'
540
- let options = get_options(api_key, Date.now(), this.memo, params, secret_key)
541
- needle.post(url, params, options, (err, res, body) => {
542
- body = parse_body(body)
543
- if (body && body.code === 1000 && body.data && Array.isArray(body.data)) {
544
- let trades = body.data.map((t) => ({
545
- order_id: t.orderId.toString(),
546
- pair,
547
- type: t.side,
548
- price: parseFloat(t.price),
549
- amount: parseFloat(t.size),
550
- total: parseFloat(t.notional),
551
- open_time: new Date(t.createTime),
552
- close_time: new Date(t.updateTime),
553
- fees: {
554
- [post_process_cur(t.feeCoinName)]: parseFloat(t.fee),
555
- },
556
- }))
557
- trades = utils.merge_trades_with_same_id(trades)
558
- cb({ success: true, body: trades })
559
- } else if (body) {
560
- cb({ success: false, error: error_codes.other, body })
561
- } else {
562
- cb({ success: false, error: error_codes.timeout })
563
- }
533
+ ;(async () => {
534
+ const rows = await fetch_split_windows({
535
+ start_ms: start_time_in_ms,
536
+ stop_ms: end_time_in_ms,
537
+ initial_window_ms: Math.max(end_time_in_ms - start_time_in_ms, 60 * 1000),
538
+ page_limit: 200,
539
+ fetch_window: async (window_start_ms, window_stop_ms) =>
540
+ await new Promise((resolve, reject) => {
541
+ let params = {
542
+ endTime: window_stop_ms - 1,
543
+ limit: 200,
544
+ orderMode: 'spot',
545
+ startTime: window_start_ms,
546
+ symbol: pre_process_pair(pair),
547
+ }
548
+ let url = 'https://api-cloud.bitmart.com/spot/v4/query/trades'
549
+ let options = get_options(api_key, Date.now(), this.memo, params, secret_key)
550
+ needle.post(url, params, options, (err, res, body) => {
551
+ body = parse_body(body)
552
+ if (body && body.code === 1000 && body.data && Array.isArray(body.data)) {
553
+ resolve(
554
+ body.data.map((t) => ({
555
+ order_id: t.orderId.toString(),
556
+ pair,
557
+ type: t.side,
558
+ price: parseFloat(t.price),
559
+ amount: parseFloat(t.size),
560
+ total: parseFloat(t.notional),
561
+ open_time: new Date(t.createTime),
562
+ close_time: new Date(t.updateTime),
563
+ fees: {
564
+ [post_process_cur(t.feeCoinName)]: parseFloat(t.fee),
565
+ },
566
+ ...(t.tradeRole != null ? { role: t.tradeRole } : {}),
567
+ })),
568
+ )
569
+ } else if (body) {
570
+ reject({ error: error_codes.other, body })
571
+ } else {
572
+ reject({ error: error_codes.timeout })
573
+ }
574
+ })
575
+ }),
576
+ })
577
+ const merged = utils.merge_trades_with_same_id(rows)
578
+ cb({ success: true, body: sort_history_rows(merged) })
579
+ })().catch((error) => {
580
+ cb({ success: false, error: error?.error || error_codes.other, body: error?.body })
564
581
  })
565
582
  }
566
583
  this.private_limiter.process(get_history_trades_base, pair, start_time_in_ms, end_time_in_ms, cb)
@@ -751,6 +751,7 @@ module.exports = class Bitmex extends ExchangeBase {
751
751
  delay_ms: 250,
752
752
  })
753
753
  trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
754
+ trades = utils.merge_trades_with_same_id(trades)
754
755
  cb({ success: true, body: trades })
755
756
  })().catch((error) => cb(error))
756
757
  }
@@ -567,7 +567,8 @@ module.exports = class Bitrue 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(sort_history_rows(rows))
571
+ cb({ success: true, body: merged })
571
572
  })().catch((error) => {
572
573
  cb({ success: false, error: error?.error || error_codes.other, body: error?.body })
573
574
  })
@@ -438,6 +438,7 @@ module.exports = class Bitstamp extends ExchangeBase {
438
438
  await sleep(250)
439
439
  }
440
440
  trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
441
+ trades = utils.merge_trades_with_same_id(trades)
441
442
  cb({ success: true, body: trades })
442
443
  })().catch((error) => cb(error))
443
444
  }
@@ -653,6 +653,7 @@ module.exports = class Blofin extends ExchangeBase {
653
653
  delay_ms: 250,
654
654
  })
655
655
  trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
656
+ trades = utils.merge_trades_with_same_id(trades)
656
657
  cb({ success: true, body: trades })
657
658
  })().catch((error) => cb(error))
658
659
  }
@@ -489,9 +489,7 @@ module.exports = class Btse extends ExchangeBase {
489
489
  }
490
490
  this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
491
491
  }
492
- get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
493
- this._get_history_trades_via_timeframe(pair, start_time_in_ms, end_time_in_ms, cb)
494
- }
492
+ // get_history_trades not implemented, no native absolute-window endpoint
495
493
  get_all_deposits(timeframe_in_ms, cb) {
496
494
  let get_all_deposits_base = (timeframe_in_ms, cb) => {
497
495
  let [api_key, secret_key] = this.api_secret_key
@@ -10,6 +10,7 @@ const ExchangeWs = require('./exchange-ws.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 get_options(api_key, sign, timestamp) {
15
16
  let options = {
@@ -540,41 +541,53 @@ module.exports = class Bybit extends ExchangeBase {
540
541
  get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
541
542
  let get_history_trades_base = (pair, start_time_in_ms, end_time_in_ms, cb) => {
542
543
  let [api_key, secret_key] = this.api_secret_key
543
- let params = {
544
- category: 'spot',
545
- symbol: pre_process_pair(pair),
546
- startTime: start_time_in_ms,
547
- endTime: end_time_in_ms,
548
- limit: 100,
549
- }
550
- const timestamp = Date.now().toString()
551
- let sign = get_signature_bybitv5('GET', api_key, secret_key, params, timestamp)
552
- let options = get_options(api_key, sign, timestamp)
553
- let url = 'https://api.bybit.com/v5/execution/list?' + qs.stringify(params)
554
- needle.get(url, options, (err, res, body) => {
555
- if (body && body.retCode === 0 && Array.isArray(body.result.list)) {
556
- let res = body.result.list.map((t) => {
557
- const trade = {
558
- order_id: t.orderId,
559
- pair,
560
- type: t.side.toLowerCase(),
561
- price: parseFloat(t.execPrice),
562
- amount: parseFloat(t.execQty),
563
- total: parseFloat(t.execPrice) * parseFloat(t.execQty),
564
- fees: {
565
- [post_process_cur(t.feeCurrency)]: parseFloat(t.execFee),
566
- },
567
- close_time: new Date(parseInt(t.execTime)),
568
- }
569
- return trade
570
- })
571
- res = utils.merge_trades_with_same_id(res)
572
- cb({ success: true, body: res })
573
- } else if (body) {
574
- cb({ success: false, error: error_codes.other, body })
575
- } else {
576
- cb({ success: false, error: error_codes.timeout })
577
- }
544
+ ;(async () => {
545
+ const rows = await fetch_split_windows({
546
+ start_ms: start_time_in_ms,
547
+ stop_ms: end_time_in_ms,
548
+ initial_window_ms: Math.max(end_time_in_ms - start_time_in_ms, 60 * 1000),
549
+ page_limit: 100,
550
+ fetch_window: async (window_start_ms, window_stop_ms) =>
551
+ await new Promise((resolve, reject) => {
552
+ let params = {
553
+ category: 'spot',
554
+ symbol: pre_process_pair(pair),
555
+ startTime: window_start_ms,
556
+ endTime: window_stop_ms,
557
+ limit: 100,
558
+ }
559
+ const timestamp = Date.now().toString()
560
+ let sign = get_signature_bybitv5('GET', api_key, secret_key, params, timestamp)
561
+ let options = get_options(api_key, sign, timestamp)
562
+ let url = 'https://api.bybit.com/v5/execution/list?' + qs.stringify(params)
563
+ needle.get(url, options, (err, res, body) => {
564
+ if (body && body.retCode === 0 && Array.isArray(body.result.list)) {
565
+ resolve(
566
+ body.result.list.map((t) => ({
567
+ order_id: t.orderId,
568
+ pair,
569
+ type: t.side.toLowerCase(),
570
+ price: parseFloat(t.execPrice),
571
+ amount: parseFloat(t.execQty),
572
+ total: parseFloat(t.execPrice) * parseFloat(t.execQty),
573
+ fees: {
574
+ [post_process_cur(t.feeCurrency)]: parseFloat(t.execFee),
575
+ },
576
+ close_time: new Date(parseInt(t.execTime)),
577
+ })),
578
+ )
579
+ } else if (body) {
580
+ reject({ error: error_codes.other, body })
581
+ } else {
582
+ reject({ error: error_codes.timeout })
583
+ }
584
+ })
585
+ }),
586
+ })
587
+ const merged = utils.merge_trades_with_same_id(rows)
588
+ cb({ success: true, body: sort_history_rows(merged) })
589
+ })().catch((error) => {
590
+ cb({ success: false, error: error?.error || error_codes.other, body: error?.body })
578
591
  })
579
592
  }
580
593
  this.private_limiter.process(get_history_trades_base, pair, start_time_in_ms, end_time_in_ms, cb)
@@ -1994,7 +1994,9 @@ module.exports = class Coinbase extends ExchangeBase {
1994
1994
  await sleep(50)
1995
1995
  }
1996
1996
  }
1997
- cb({ success: true, body: sort_history_rows(filter_history_in_range(results, start_time_in_ms, end_time_in_ms)) })
1997
+ let trades = sort_history_rows(filter_history_in_range(results, start_time_in_ms, end_time_in_ms))
1998
+ trades = utils.merge_trades_with_same_id(trades)
1999
+ cb({ success: true, body: trades })
1998
2000
  } catch (error) {
1999
2001
  cb({ success: false, error: error?.error || error_codes.other, body: error?.body })
2000
2002
  }
@@ -477,9 +477,7 @@ module.exports = class Coinstore extends ExchangeBase {
477
477
  }
478
478
  this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
479
479
  }
480
- get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
481
- this._get_history_trades_via_timeframe(pair, start_time_in_ms, end_time_in_ms, cb)
482
- }
480
+ // get_history_trades not implemented, no native absolute-window endpoint
483
481
  static get_precision(cb) {
484
482
  const price_precision = {}
485
483
  const amount_precision = {}
@@ -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
  const { utils, rate_limiter: Limiter } = require('@icgio/icg-utils')
@@ -506,38 +507,53 @@ module.exports = class Coinw extends ExchangeBase {
506
507
  get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
507
508
  const [api_key, secret_key] = this.api_secret_key
508
509
  const get_trades_base = (pair, start_time_in_ms, end_time_in_ms, cb) => {
509
- const params = {
510
- api_key,
511
- currencyPair: pre_process_pair(pair),
512
- endAt: end_time_in_ms,
513
- startAt: start_time_in_ms,
514
- }
515
- params.sign = get_signature_coinw(secret_key, params)
516
- needle.post('https://api.coinw.com/api/v1/private?command=returnUTradeHistory', params, options, (_err, _res, body) => {
517
- body = parse_body(body)
518
- if (body && body.code === '200') {
519
- if (!body.data) {
520
- cb({ success: true, body: [] })
521
- } else {
522
- let trades = body.data
523
- .filter((t) => t.status === '2' || t.status === '3')
524
- .map((trade) => ({
525
- order_id: trade.tradeID.toString(),
526
- pair,
527
- type: trade.type,
528
- price: parseFloat(trade.prize), // exchange typo
529
- amount: parseFloat(trade.success_count),
530
- total: parseFloat(trade.success_amount),
531
- close_time: new Date(parseInt(trade.date)),
532
- }))
533
- trades = utils.merge_trades_with_same_id(trades)
534
- cb({ success: true, body: trades })
535
- }
536
- } else if (body) {
537
- cb({ success: false, error: error_codes.other, body })
538
- } else {
539
- cb({ success: false, error: error_codes.timeout })
540
- }
510
+ ;(async () => {
511
+ const rows = await fetch_split_windows({
512
+ start_ms: start_time_in_ms,
513
+ stop_ms: end_time_in_ms,
514
+ initial_window_ms: Math.max(end_time_in_ms - start_time_in_ms, 60 * 1000),
515
+ page_limit: 100,
516
+ fetch_window: async (window_start_ms, window_stop_ms) =>
517
+ await new Promise((resolve, reject) => {
518
+ const params = {
519
+ api_key,
520
+ currencyPair: pre_process_pair(pair),
521
+ endAt: window_stop_ms,
522
+ startAt: window_start_ms,
523
+ }
524
+ params.sign = get_signature_coinw(secret_key, params)
525
+ needle.post('https://api.coinw.com/api/v1/private?command=returnUTradeHistory', params, options, (_err, _res, body) => {
526
+ body = parse_body(body)
527
+ if (body && body.code === '200') {
528
+ if (!body.data) {
529
+ resolve([])
530
+ } else {
531
+ resolve(
532
+ body.data
533
+ .filter((t) => t.status === '2' || t.status === '3')
534
+ .map((trade) => ({
535
+ order_id: trade.tradeID.toString(),
536
+ pair,
537
+ type: trade.type,
538
+ price: parseFloat(trade.prize), // exchange typo
539
+ amount: parseFloat(trade.success_count),
540
+ total: parseFloat(trade.success_amount),
541
+ close_time: new Date(parseInt(trade.date)),
542
+ })),
543
+ )
544
+ }
545
+ } else if (body) {
546
+ reject({ error: error_codes.other, body })
547
+ } else {
548
+ reject({ error: error_codes.timeout })
549
+ }
550
+ })
551
+ }),
552
+ })
553
+ const merged = utils.merge_trades_with_same_id(rows)
554
+ cb({ success: true, body: sort_history_rows(merged) })
555
+ })().catch((error) => {
556
+ cb({ success: false, error: error?.error || error_codes.other, body: error?.body })
541
557
  })
542
558
  }
543
559
  this.private_limiter.process(get_trades_base, pair, start_time_in_ms, end_time_in_ms, cb)
@@ -558,6 +558,7 @@ module.exports = class Cryptocom extends ExchangeBase {
558
558
  delay_ms: 250,
559
559
  })
560
560
  trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
561
+ trades = utils.merge_trades_with_same_id(trades)
561
562
  cb({ success: true, body: trades })
562
563
  })().catch((error) => cb(error))
563
564
  }
@@ -528,6 +528,7 @@ module.exports = class Deepcoin extends ExchangeBase {
528
528
  delay_ms: 250,
529
529
  })
530
530
  trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
531
+ trades = utils.merge_trades_with_same_id(trades)
531
532
  cb({ success: true, body: trades })
532
533
  })().catch((error) => cb(error))
533
534
  }
@@ -440,6 +440,7 @@ module.exports = class Digifinex extends ExchangeBase {
440
440
  delay_ms: 250,
441
441
  })
442
442
  trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
443
+ trades = utils.merge_trades_with_same_id(trades)
443
444
  cb({ success: true, body: trades })
444
445
  })().catch((error) => cb(error))
445
446
  }
@@ -450,9 +450,14 @@ class ExchangeBase {
450
450
  // ============================================
451
451
 
452
452
  /**
453
- * Get trade history for a trading pair
453
+ * Get trade history for a trading pair within a relative time window.
454
+ *
455
+ * Implementations should call utils.merge_trades_with_same_id() before returning,
456
+ * which groups fills by order_id, aggregates amount/total/fees, recalculates price,
457
+ * and populates fill_history[] on every returned trade (even single-fill trades).
458
+ *
454
459
  * @param {string} pair - Trading pair
455
- * @param {number} timeframe_in_ms - Time range in milliseconds
460
+ * @param {number} timeframe_in_ms - Time range in milliseconds (now - timeframe_in_ms .. now)
456
461
  * @param {function(ApiResponse<TradeResult[]>): void} cb - Callback with trades
457
462
  */
458
463
  get_trades(pair, timeframe_in_ms, cb) {
@@ -461,7 +466,11 @@ class ExchangeBase {
461
466
 
462
467
  /**
463
468
  * Get trade history for a trading pair in an absolute time window.
464
- * Implementations should return individual fills filtered to [start_time_in_ms, end_time_in_ms).
469
+ *
470
+ * Implementations should call utils.merge_trades_with_same_id() before
471
+ * returning, which groups fills by order_id, aggregates amount/total/fees,
472
+ * recalculates price, and populates fill_history[] on every returned trade.
473
+ *
465
474
  * @param {string} pair - Trading pair
466
475
  * @param {number} start_time_in_ms - Inclusive start timestamp in milliseconds
467
476
  * @param {number} end_time_in_ms - Exclusive end timestamp in milliseconds
@@ -471,49 +480,6 @@ class ExchangeBase {
471
480
  cb({ success: false, error: `${this.name}: get_history_trades not implemented` })
472
481
  }
473
482
 
474
- /**
475
- * Best-effort absolute-window history built from the exchange's existing recent trade endpoint.
476
- * @param {string} pair
477
- * @param {number} start_time_in_ms
478
- * @param {number} end_time_in_ms
479
- * @param {function(ApiResponse<TradeResult[]>): void} cb
480
- * @param {{use_all_trades?: boolean}} options
481
- */
482
- _get_history_trades_via_timeframe(pair, start_time_in_ms, end_time_in_ms, cb, options = {}) {
483
- if (!(start_time_in_ms < end_time_in_ms)) {
484
- cb({ success: true, body: [] })
485
- return
486
- }
487
-
488
- const use_all_trades = options.use_all_trades === true
489
- const timeframe_in_ms = Math.max(end_time_in_ms - start_time_in_ms, Date.now() - start_time_in_ms, 0)
490
- const handle = (res) => {
491
- if (!res || !res.success) {
492
- cb(res)
493
- return
494
- }
495
- let trades = Array.isArray(res.body) ? res.body : []
496
- if (pair) {
497
- trades = trades.filter((trade) => !trade?.pair || trade.pair === pair)
498
- }
499
- cb({
500
- success: true,
501
- body: ExchangeBase.history_utils.sort_history_rows(ExchangeBase.history_utils.filter_history_in_range(trades, start_time_in_ms, end_time_in_ms)),
502
- })
503
- }
504
-
505
- if (use_all_trades) {
506
- if (typeof this.get_all_trades !== 'function') {
507
- cb({ success: false, error: `${this.name}: get_all_trades not implemented` })
508
- return
509
- }
510
- this.get_all_trades(timeframe_in_ms, handle)
511
- return
512
- }
513
-
514
- this.get_trades(pair, timeframe_in_ms, handle)
515
- }
516
-
517
483
  // ============================================
518
484
  // Deposit/Withdrawal Methods
519
485
  // ============================================
@@ -316,9 +316,7 @@ module.exports = class Fastex extends ExchangeBase {
316
316
  }
317
317
  this.private_limiter.process(get_trades_base, pair, cb)
318
318
  }
319
- get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
320
- this._get_history_trades_via_timeframe(pair, start_time_in_ms, end_time_in_ms, cb)
321
- }
319
+ // get_history_trades not implemented, no native absolute-window endpoint
322
320
  static get_min_amount(cb) {
323
321
  let min_quote_cur_amount = {}
324
322
  needle.get('https://exchange.fastex.com/api/v1/pair/list?items_per_page=100&page=1', (err, res, body) => {