@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.
@@ -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 { filter_history_in_range, sleep, sort_history_rows, split_windows } = 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')
@@ -69,11 +70,29 @@ const uuid38 = () => uuid16() + uuid22()
69
70
  function get_signature_lbank(secret_key, request, timestamp, echostr) {
70
71
  request = _.extend(request, { timestamp, echostr, signature_method: 'HmacSHA256' })
71
72
  request = _.fromPairs(_.sortBy(_.toPairs(request), [0]))
72
- let message = qs.stringify(request)
73
+ let message = qs.stringify(request, { encode: false })
73
74
  message = crypto.createHash('md5').update(message).digest('hex').toUpperCase()
74
75
  return crypto.createHmac('sha256', secret_key).update(message).digest('hex')
75
76
  }
76
77
 
78
+ function format_history_time_utc8(ms) {
79
+ const date = new Date(ms + 8 * 60 * 60 * 1000)
80
+ const pad = (value) => String(value).padStart(2, '0')
81
+ return (
82
+ date.getUTCFullYear() +
83
+ '-' +
84
+ pad(date.getUTCMonth() + 1) +
85
+ '-' +
86
+ pad(date.getUTCDate()) +
87
+ ' ' +
88
+ pad(date.getUTCHours()) +
89
+ ':' +
90
+ pad(date.getUTCMinutes()) +
91
+ ':' +
92
+ pad(date.getUTCSeconds())
93
+ )
94
+ }
95
+
77
96
  function parse_lbank_ws_timestamp(value) {
78
97
  if (value instanceof Date) return value
79
98
  if (typeof value === 'number') return new Date(value)
@@ -580,61 +599,77 @@ module.exports = class Lbank extends ExchangeBase {
580
599
  this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
581
600
  }
582
601
  get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
583
- let [api_key, secret_key] = this.api_secret_key
584
- const PAGE_SIZE = 100
585
- let all_results = []
586
-
587
- const fetch_page = (page_start) => {
588
- let timestamp = new Date().getTime().toString()
589
- let echostr = uuid38()
590
- let params = {
591
- api_key: api_key,
592
- direct: 'next',
593
- endTime: end_time_in_ms.toString(),
594
- size: PAGE_SIZE,
595
- startTime: page_start.toString(),
596
- symbol: pre_process_pair(pair),
597
- }
598
- params.sign = get_signature_lbank(secret_key, params, timestamp, echostr)
599
- let options = get_options(timestamp, echostr)
600
- needle.post('https://api.lbank.info/v2/transaction_history.do', params, options, (err, res, body) => {
601
- body = parse_body(body)
602
- if (body && body.result && body.error_code === 0 && !body.data) {
603
- cb({ success: true, body: all_results })
604
- } else if (body && body.result && Array.isArray(body.data)) {
605
- let result = body.data.map((order) => {
606
- return {
607
- order_id: order.orderUuid.toString(),
608
- pair,
609
- type: order.tradeType,
610
- price: parseFloat(order.dealPrice),
611
- amount: parseFloat(order.dealQuantity),
612
- total: parseFloat(order.dealVolumePrice),
613
- close_time: new Date(order.dealTime),
614
- fees: {
615
- [order.tradeType === 'buy' ? pair_to_base_cur(pair) : pair_to_quote_cur(pair)]: parseFloat(order.tradeFee),
616
- },
617
- }
618
- })
619
- all_results = all_results.concat(result)
620
- // direct=next returns oldest first — advance startTime past the newest to get next page
621
- if (result.length >= PAGE_SIZE) {
622
- const newest_time = Math.max(...result.map(t => t.close_time.getTime()))
623
- if (newest_time > page_start && newest_time < end_time_in_ms) {
624
- fetch_page(newest_time + 1)
625
- return
602
+ const get_history_trades_base = (pair, start_time_in_ms, end_time_in_ms, cb) => {
603
+ let [api_key, secret_key] = this.api_secret_key
604
+ ;(async () => {
605
+ const rows = []
606
+ const max_window_ms = 2 * 24 * 60 * 60 * 1000
607
+ const windows = split_windows(start_time_in_ms, end_time_in_ms, Math.min(max_window_ms, end_time_in_ms - start_time_in_ms))
608
+ for (const [window_start_ms, window_stop_ms] of windows) {
609
+ let last_trade_id = null
610
+ while (true) {
611
+ const raw_items = await new Promise((resolve, reject) => {
612
+ let timestamp = new Date().getTime().toString()
613
+ let echostr = uuid38()
614
+ let params = {
615
+ api_key: api_key,
616
+ endTime: format_history_time_utc8(window_stop_ms),
617
+ limit: '100',
618
+ startTime: format_history_time_utc8(window_start_ms),
619
+ symbol: pre_process_pair(pair),
620
+ }
621
+ if (last_trade_id) params.fromId = last_trade_id
622
+ params.sign = get_signature_lbank(secret_key, params, timestamp, echostr)
623
+ let options = get_options(timestamp, echostr)
624
+ needle.post('https://api.lbank.info/v2/supplement/transaction_history.do', params, options, (err, res, body) => {
625
+ body = parse_body(body)
626
+ if (body && body.result && body.error_code === 0 && !body.data) {
627
+ resolve([])
628
+ } else if (body && body.result && Array.isArray(body.data)) {
629
+ resolve(
630
+ body.data.map((trade) => {
631
+ let result = {
632
+ order_id: trade.orderId != null ? trade.orderId.toString() : '',
633
+ pair,
634
+ type: trade.isBuyer ? 'buy' : 'sell',
635
+ price: parseFloat(trade.price),
636
+ amount: parseFloat(trade.qty),
637
+ total: parseFloat(trade.quoteQty),
638
+ close_time: new Date(trade.time),
639
+ fees: {
640
+ [trade.isBuyer ? pair_to_base_cur(pair) : pair_to_quote_cur(pair)]: parseFloat(trade.commission),
641
+ },
642
+ role: trade.isMaker ? 'maker' : 'taker',
643
+ }
644
+ if (trade.id != null) result.trade_id = trade.id.toString()
645
+ return result
646
+ }),
647
+ )
648
+ } else if (body) {
649
+ reject({ error: error_codes.other, body })
650
+ } else {
651
+ reject({ error: error_codes.timeout })
652
+ }
653
+ })
654
+ })
655
+ if (raw_items.length === 0) break
656
+ let items = raw_items
657
+ if (last_trade_id && items[0] && items[0].trade_id === last_trade_id) {
658
+ items = items.slice(1)
626
659
  }
660
+ if (items.length === 0) break
661
+ rows.push(...items)
662
+ last_trade_id = raw_items[raw_items.length - 1]?.trade_id || null
663
+ if (raw_items.length < 100 || !last_trade_id) break
664
+ await sleep(50)
627
665
  }
628
- cb({ success: true, body: all_results })
629
- } else if (body) {
630
- cb({ success: false, error: error_codes.other, body })
631
- } else {
632
- cb({ success: false, error: error_codes.timeout })
633
666
  }
667
+ cb({ success: true, body: sort_history_rows(filter_history_in_range(rows, start_time_in_ms, end_time_in_ms)) })
668
+ })().catch((error) => {
669
+ cb({ success: false, error: error?.error || error_codes.other, body: error?.body })
634
670
  })
635
671
  }
636
-
637
- this.private_limiter.process(fetch_page, start_time_in_ms)
672
+ this.private_limiter.process(get_history_trades_base, pair, start_time_in_ms, end_time_in_ms, cb)
638
673
  }
639
674
  get_all_deposits(timeframe_in_ms, cb) {
640
675
  const get_all_deposits_base = (timeframe_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 { utils, rate_limiter: Limiter } = require('@icgio/icg-utils')
12
13
 
@@ -515,6 +516,63 @@ module.exports = class Mexc extends ExchangeBase {
515
516
  }
516
517
  this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
517
518
  }
519
+ get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
520
+ const get_history_trades_base = (pair, start_time_in_ms, end_time_in_ms, cb) => {
521
+ let [api_key, secret_key] = this.api_secret_key
522
+ const options = get_options(api_key)
523
+ ;(async () => {
524
+ const rows = await fetch_split_windows({
525
+ start_ms: start_time_in_ms,
526
+ stop_ms: end_time_in_ms,
527
+ initial_window_ms: Math.max(end_time_in_ms - start_time_in_ms, 60 * 1000),
528
+ page_limit: 1000,
529
+ fetch_window: async (window_start_ms, window_stop_ms) =>
530
+ await new Promise((resolve, reject) => {
531
+ let params = {
532
+ symbol: pre_process_pair(pair),
533
+ startTime: window_start_ms,
534
+ endTime: window_stop_ms - 1,
535
+ limit: 1000,
536
+ timestamp: parseInt(Date.now()),
537
+ }
538
+ let signature = get_signature_mexc(secret_key, params)
539
+ let url = 'https://api.mexc.com/api/v3/myTrades?' + qs.stringify(params) + '&signature=' + signature
540
+ needle.get(url, options, (err, res, body) => {
541
+ if (body && Array.isArray(body)) {
542
+ resolve(
543
+ body.map((trade) => {
544
+ let result = {
545
+ order_id: trade.orderId != null ? trade.orderId.toString() : '',
546
+ pair,
547
+ type: trade.isBuyer ? 'buy' : 'sell',
548
+ price: parseFloat(trade.price),
549
+ amount: parseFloat(trade.qty),
550
+ total: parseFloat(trade.quoteQty),
551
+ close_time: new Date(parseInt(trade.time)),
552
+ fees: {
553
+ [post_process_cur(trade.commissionAsset)]: parseFloat(trade.commission),
554
+ },
555
+ role: trade.isMaker ? 'maker' : 'taker',
556
+ }
557
+ if (trade.id != null) result.trade_id = trade.id.toString()
558
+ return result
559
+ }),
560
+ )
561
+ } else if (body) {
562
+ reject({ error: error_codes.other, body })
563
+ } else {
564
+ reject({ error: error_codes.timeout })
565
+ }
566
+ })
567
+ }),
568
+ })
569
+ cb({ success: true, body: sort_history_rows(rows) })
570
+ })().catch((error) => {
571
+ cb({ success: false, error: error?.error || error_codes.other, body: error?.body })
572
+ })
573
+ }
574
+ this.private_limiter.process(get_history_trades_base, pair, start_time_in_ms, end_time_in_ms, cb)
575
+ }
518
576
  get_all_deposits(timeframe_in_ms, cb) {
519
577
  let get_all_deposits_base = (timeframe_in_ms, cb) => {
520
578
  let [api_key, secret_key] = this.api_secret_key
@@ -416,6 +416,9 @@ 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
422
  // get_all_deposits(timeframe_in_ms, cb) {
420
423
  //
421
424
  // }
@@ -374,6 +374,63 @@ module.exports = class Upbit extends ExchangeBase {
374
374
  }
375
375
  this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
376
376
  }
377
+ get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
378
+ let [api_key, secret_key] = this.api_secret_key
379
+ let { filter_history_in_range, sort_history_rows, split_windows, sleep } = ExchangeBase.history_utils
380
+ let fetch_page = (params) =>
381
+ new Promise((resolve, reject) => {
382
+ let options = get_options(api_key, secret_key, params)
383
+ let url = 'https://api.upbit.com/v1/orders/closed?' + qs.stringify(params)
384
+ this.private_limiter.process(needle.get, url, options, (err, res, body) => {
385
+ if (body && Array.isArray(body)) {
386
+ resolve(body)
387
+ } else if (body) {
388
+ reject({ success: false, error: error_codes.other, body })
389
+ } else {
390
+ reject({ success: false, error: error_codes.timeout })
391
+ }
392
+ })
393
+ })
394
+ ;(async () => {
395
+ let trades = []
396
+ let windows = split_windows(start_time_in_ms, end_time_in_ms, 7 * 24 * 60 * 60 * 1000)
397
+ for (let [window_start_ms, window_stop_ms] of windows) {
398
+ let page = 1
399
+ while (true) {
400
+ let params = {
401
+ market: pre_process_pair(pair),
402
+ state: 'done',
403
+ page,
404
+ limit: 100,
405
+ order_by: 'asc',
406
+ start_time: window_start_ms,
407
+ end_time: window_stop_ms - 1,
408
+ }
409
+ let rows = await fetch_page(params)
410
+ trades.push(
411
+ ...rows.map((order) => {
412
+ let price = parseFloat(order.avg_price) > 0 ? parseFloat(order.avg_price) : parseFloat(order.price)
413
+ let amount = parseFloat(order.executed_volume)
414
+ return {
415
+ order_id: order.uuid,
416
+ pair,
417
+ type: order.side === 'bid' ? 'buy' : 'sell',
418
+ price,
419
+ amount,
420
+ total: Number.isFinite(parseFloat(order.executed_funds)) ? parseFloat(order.executed_funds) : price * amount,
421
+ close_time: new Date(order.done_at || order.created_at),
422
+ }
423
+ })
424
+ )
425
+ if (rows.length < 100) break
426
+ page++
427
+ await sleep(250)
428
+ }
429
+ }
430
+ trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
431
+ cb({ success: true, body: trades })
432
+ })().catch((error) => cb(error))
433
+ }
377
434
  get_trades_id(pair, order_id, timeframe_in_ms, cb) {
378
435
  let get_trades_id_base = (pair, order_id, timeframe_in_ms, cb) => {
379
436
  let params = {
@@ -393,6 +393,9 @@ 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
399
  get_all_deposits(timeframe_in_ms, cb) {
397
400
  let get_all_deposits_base = (cb) => {
398
401
  let [api_key, secret_key] = this.api_secret_key
@@ -7,6 +7,7 @@ const websocket = require('ws')
7
7
 
8
8
  const ExchangeBase = require('./exchange-base.js')
9
9
  const ExchangeWs = require('./exchange-ws.js')
10
+ const { fetch_split_windows, sort_query_params, 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')
@@ -516,6 +517,66 @@ module.exports = class Xt extends ExchangeBase {
516
517
  }
517
518
  this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
518
519
  }
520
+ get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
521
+ const get_history_trades_base = (pair, start_time_in_ms, end_time_in_ms, cb) => {
522
+ let [api_key, secret_key] = this.api_secret_key
523
+ ;(async () => {
524
+ const rows = await fetch_split_windows({
525
+ start_ms: start_time_in_ms,
526
+ stop_ms: end_time_in_ms,
527
+ initial_window_ms: 4 * 60 * 60 * 1000,
528
+ page_limit: 100,
529
+ fetch_window: async (window_start_ms, window_stop_ms) =>
530
+ await new Promise((resolve, reject) => {
531
+ const path = '/v4/trade'
532
+ const params = sort_query_params({
533
+ bizType: 'SPOT',
534
+ endTime: window_stop_ms - 1,
535
+ limit: 100,
536
+ startTime: window_start_ms,
537
+ symbol: pre_process_pair(pair),
538
+ })
539
+ const url = 'https://sapi.xt.com' + path + '?' + qs.stringify(params)
540
+ const timestamp = new Date().getTime()
541
+ const sign = get_signature_xt(api_key, secret_key, 'GET', path, params, '', timestamp)
542
+ const options = get_options(api_key, timestamp, sign)
543
+ needle.get(url, options, (err, res, body) => {
544
+ body = parse_body(body)
545
+ if (body && body.mc === 'SUCCESS' && body.result && Array.isArray(body.result.items)) {
546
+ resolve(
547
+ body.result.items.map((item) => {
548
+ let trade = {
549
+ order_id: item.orderId != null ? item.orderId.toString() : '',
550
+ pair,
551
+ type: String(item.orderSide || '').toLowerCase(),
552
+ price: parseFloat(item.price),
553
+ amount: parseFloat(item.quantity),
554
+ total: parseFloat(item.quoteQty),
555
+ close_time: new Date(item.time),
556
+ fees: {
557
+ [post_process_cur(item.feeCurrency)]: parseFloat(item.fee),
558
+ },
559
+ role: String(item.takerMaker || '').toLowerCase(),
560
+ }
561
+ if (item.tradeId != null) trade.trade_id = item.tradeId.toString()
562
+ return trade
563
+ }),
564
+ )
565
+ } else if (body) {
566
+ reject({ error: error_codes.other, body })
567
+ } else {
568
+ reject({ error: error_codes.timeout })
569
+ }
570
+ })
571
+ }),
572
+ })
573
+ cb({ success: true, body: sort_history_rows(rows) })
574
+ })().catch((error) => {
575
+ cb({ success: false, error: error?.error || error_codes.other, body: error?.body })
576
+ })
577
+ }
578
+ this.private_limiter.process(get_history_trades_base, pair, start_time_in_ms, end_time_in_ms, cb)
579
+ }
519
580
  get_deposits(cur, timeframe_in_ms, cb) {
520
581
  const chains = ['Tron', 'BNB Smart Chain', 'Ethereum']
521
582
  let get_deposits_base = (timeframe_in_ms, cb) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@icgio/icg-exchanges",
3
- "version": "1.40.42",
3
+ "version": "1.40.44",
4
4
  "description": "icgio exchanges package",
5
5
  "main": "./exchanges.js",
6
6
  "scripts": {