@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.
@@ -370,6 +370,77 @@ module.exports = class Bitstamp extends ExchangeBase {
370
370
  }
371
371
  this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
372
372
  }
373
+ get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
374
+ let [api_key, secret_key] = this.api_secret_key
375
+ let { filter_history_in_range, sort_history_rows, sleep } = ExchangeBase.history_utils
376
+ let [base_cur, quote_cur] = utils.parse_pair(pair)
377
+ let base_key = base_cur.toLowerCase()
378
+ let quote_key = quote_cur.toLowerCase()
379
+ let price_key = base_key + '_' + quote_key
380
+ let parse_trade_time = (datetime) => {
381
+ let iso = datetime.replace(' ', 'T').replace(/(\.\d{3})\d+$/, '$1')
382
+ return new Date(iso + 'Z')
383
+ }
384
+ let fetch_page = (offset) =>
385
+ new Promise((resolve, reject) => {
386
+ let url = 'https://www.bitstamp.net/api/v2/user_transactions/' + pre_process_pair(pair) + '/'
387
+ let params = {
388
+ key: api_key,
389
+ nonce: Date.now(),
390
+ signature: get_signature_bitstamp(api_key, secret_key, this.account_id),
391
+ limit: 1000,
392
+ offset,
393
+ sort: 'desc',
394
+ }
395
+ needle.post(url, params, (err, res, body) => {
396
+ if (Array.isArray(body)) {
397
+ resolve(body)
398
+ } else if (body) {
399
+ reject({ success: false, error: error_codes.other, body })
400
+ } else {
401
+ reject({ success: false, error: error_codes.timeout })
402
+ }
403
+ })
404
+ })
405
+ ;(async () => {
406
+ let trades = []
407
+ let offset = 0
408
+ while (offset <= 200000) {
409
+ let rows = await fetch_page(offset)
410
+ if (rows.length === 0) break
411
+ trades.push(
412
+ ...rows
413
+ .filter((trade) => String(trade.type) === '2')
414
+ .map((trade) => {
415
+ let close_time = parse_trade_time(trade.datetime)
416
+ let price = parseFloat(trade[price_key])
417
+ let amount = Math.abs(parseFloat(trade[base_key]))
418
+ let total = Math.abs(parseFloat(trade[quote_key]))
419
+ return {
420
+ order_id: trade.order_id != null ? trade.order_id.toString() : '',
421
+ trade_id: (trade.id || trade.tid || '').toString(),
422
+ pair,
423
+ type: parseFloat(trade[base_key]) < 0 ? 'sell' : 'buy',
424
+ price,
425
+ amount,
426
+ total: Number.isFinite(total) ? total : price * amount,
427
+ close_time,
428
+ }
429
+ })
430
+ )
431
+ let oldest_seen_ms = Math.min(
432
+ ...rows
433
+ .map((trade) => parse_trade_time(trade.datetime).getTime())
434
+ .filter((time) => Number.isFinite(time))
435
+ )
436
+ if (rows.length < 1000 || (Number.isFinite(oldest_seen_ms) && oldest_seen_ms < start_time_in_ms)) break
437
+ offset += rows.length
438
+ await sleep(250)
439
+ }
440
+ trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
441
+ cb({ success: true, body: trades })
442
+ })().catch((error) => cb(error))
443
+ }
373
444
  static get_min_amount(cb) {
374
445
  let min_quote_cur_amount = {}
375
446
  needle.get('https://www.bitstamp.net/api/v2/trading-pairs-info/', (err, res, body) => {
@@ -583,6 +583,78 @@ module.exports = class Blofin extends ExchangeBase {
583
583
  }
584
584
  this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
585
585
  }
586
+ get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
587
+ let [api_key, secret_key] = this.api_secret_key
588
+ let { fetch_split_windows, filter_history_in_range, sort_history_rows } = ExchangeBase.history_utils
589
+ let [base_cur, quote_cur] = utils.parse_pair(pair)
590
+ let path = '/api/v1/spot/trade/fills-history'
591
+ let fetch_window = (window_start_ms, window_stop_ms) =>
592
+ new Promise((resolve, reject) => {
593
+ let params = {
594
+ instType: 'SPOT',
595
+ instId: pre_process_pair(pair),
596
+ begin: window_start_ms.toString(),
597
+ end: (window_stop_ms - 1).toString(),
598
+ limit: '100',
599
+ }
600
+ let query = qs.stringify(params)
601
+ let request_path = path + '?' + query
602
+ let timestamp = Date.now()
603
+ let nonce = generate_nonce()
604
+ let signature = get_signature_blofin(secret_key, 'GET', timestamp, request_path, nonce)
605
+ let options = get_options(api_key, signature, timestamp, nonce, this.passphrase)
606
+ let url = 'https://openapi.blofin.com' + request_path
607
+ needle.get(url, options, (err, res, body) => {
608
+ if (body && _.includes(['0', 0, 200], body.code) && Array.isArray(body.data)) {
609
+ resolve(
610
+ body.data.map((trade) => {
611
+ let price = parseFloat(trade.fillPrice || trade.trade_price)
612
+ let amount = parseFloat(trade.fillSize || trade.trade_quantity)
613
+ let total = price * amount
614
+ let fee = Math.abs(parseFloat(trade.fee || trade.trade_fee))
615
+ let fee_currency = trade.feeCurrency || trade.fee_currency
616
+ if (fee_currency === 'base_currency') fee_currency = base_cur
617
+ if (fee_currency === 'quote_currency') fee_currency = quote_cur
618
+ let mapped_trade = {
619
+ order_id: (trade.orderId || trade.order_id || '').toString(),
620
+ trade_id: (trade.tradeId || trade.trade_id || '').toString(),
621
+ pair,
622
+ type: (trade.side || trade.order_side || '').toLowerCase(),
623
+ price,
624
+ amount,
625
+ total,
626
+ close_time: new Date(parseInt(trade.ts || trade.create_time)),
627
+ }
628
+ if (Number.isFinite(fee) && fee_currency) {
629
+ mapped_trade.fees = {
630
+ [post_process_cur(fee_currency)]: fee,
631
+ }
632
+ }
633
+ return mapped_trade
634
+ })
635
+ )
636
+ } else if (body && body.toString().startsWith('<html>')) {
637
+ reject({ success: false, error: error_codes.service_unavailable, body })
638
+ } else if (body) {
639
+ reject({ success: false, error: error_codes.other, body })
640
+ } else {
641
+ reject({ success: false, error: error_codes.timeout })
642
+ }
643
+ })
644
+ })
645
+ ;(async () => {
646
+ let trades = await fetch_split_windows({
647
+ start_ms: start_time_in_ms,
648
+ stop_ms: end_time_in_ms,
649
+ initial_window_ms: 30 * 24 * 60 * 60 * 1000,
650
+ page_limit: 100,
651
+ fetch_window,
652
+ delay_ms: 250,
653
+ })
654
+ trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
655
+ cb({ success: true, body: trades })
656
+ })().catch((error) => cb(error))
657
+ }
586
658
  get_all_deposits(timeframe_in_ms, cb) {
587
659
  let get_all_deposits_base = (timeframe_in_ms, cb) => {
588
660
  let [api_key, secret_key] = this.api_secret_key
@@ -488,6 +488,9 @@ module.exports = class Btse extends ExchangeBase {
488
488
  }
489
489
  this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
490
490
  }
491
+ get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
492
+ this._get_history_trades_via_timeframe(pair, start_time_in_ms, end_time_in_ms, cb)
493
+ }
491
494
  get_all_deposits(timeframe_in_ms, cb) {
492
495
  let get_all_deposits_base = (timeframe_in_ms, cb) => {
493
496
  let [api_key, secret_key] = this.api_secret_key
@@ -7,6 +7,7 @@ const needle = require('needle')
7
7
 
8
8
  const ExchangeWs = require('./exchange-ws.js')
9
9
  const ExchangeBase = require('./exchange-base.js')
10
+ const { filter_history_in_range, history_time_ms, sleep, sort_history_rows } = ExchangeBase.history_utils
10
11
 
11
12
  const { sorted_book } = require('@icgio/icg-utils')
12
13
 
@@ -1572,6 +1573,107 @@ module.exports = class Coinbase extends ExchangeBase {
1572
1573
  cb({ success: true, body: [] })
1573
1574
  }
1574
1575
  }
1576
+ get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
1577
+ const get_history_trades_base = async (pair, start_time_in_ms, end_time_in_ms, cb) => {
1578
+ try {
1579
+ const results = []
1580
+ if (this.advanced) {
1581
+ let cursor = null
1582
+ while (true) {
1583
+ let path = '/api/v3/brokerage/orders/historical/fills'
1584
+ let params = { product_id: pre_process_pair(pair) }
1585
+ if (cursor) params.cursor = cursor
1586
+ let options = this._get_auth('GET', path, params)
1587
+ let url = this._base_url() + path + '?' + qs.stringify(params)
1588
+ const response = await new Promise((resolve, reject) => {
1589
+ needle.get(url, options, (err, res, body) => {
1590
+ if (body && Array.isArray(body.fills)) {
1591
+ resolve(body)
1592
+ } else if (body) {
1593
+ reject({ error: error_codes.other, body })
1594
+ } else {
1595
+ reject({ error: error_codes.timeout })
1596
+ }
1597
+ })
1598
+ })
1599
+ if (response.fills.length === 0) break
1600
+ let oldest_ms = Infinity
1601
+ for (const fill of response.fills) {
1602
+ const close_time_ms = history_time_ms(fill.trade_time)
1603
+ if (close_time_ms < oldest_ms) oldest_ms = close_time_ms
1604
+ if (close_time_ms < start_time_in_ms || close_time_ms >= end_time_in_ms) continue
1605
+ let quote_cur = (fill.product_id || '').split('-')[1] || ''
1606
+ let trade = {
1607
+ order_id: fill.order_id != null ? fill.order_id.toString() : '',
1608
+ pair,
1609
+ type: String(fill.side || '').toLowerCase(),
1610
+ price: parseFloat(fill.price),
1611
+ amount: parseFloat(fill.size),
1612
+ total: parseFloat(fill.price) * parseFloat(fill.size),
1613
+ close_time: new Date(close_time_ms),
1614
+ fees: quote_cur && fill.commission != null ? { [post_process_cur(quote_cur)]: parseFloat(fill.commission) } : {},
1615
+ role: fill.liquidity_indicator === 'MAKER' ? 'maker' : 'taker',
1616
+ }
1617
+ if (fill.trade_id != null) trade.trade_id = fill.trade_id.toString()
1618
+ else if (fill.entry_id != null) trade.trade_id = fill.entry_id.toString()
1619
+ results.push(trade)
1620
+ }
1621
+ if (!response.has_next || !response.cursor || oldest_ms < start_time_in_ms) break
1622
+ cursor = response.cursor
1623
+ await sleep(50)
1624
+ }
1625
+ } else {
1626
+ let after = null
1627
+ while (true) {
1628
+ let path = '/fills'
1629
+ let params = { product_id: pre_process_pair(pair) }
1630
+ if (after) params.after = after
1631
+ let options = this._get_auth('GET', path, params)
1632
+ let url = this._base_url() + path + '?' + qs.stringify(params)
1633
+ const response = await new Promise((resolve, reject) => {
1634
+ needle.get(url, options, (err, res, body) => {
1635
+ if (body && Array.isArray(body)) {
1636
+ resolve({ res, body })
1637
+ } else if (body) {
1638
+ reject({ error: error_codes.other, body })
1639
+ } else {
1640
+ reject({ error: error_codes.timeout })
1641
+ }
1642
+ })
1643
+ })
1644
+ if (response.body.length === 0) break
1645
+ let oldest_ms = Infinity
1646
+ for (const fill of response.body) {
1647
+ const close_time_ms = history_time_ms(fill.created_at)
1648
+ if (close_time_ms < oldest_ms) oldest_ms = close_time_ms
1649
+ if (close_time_ms < start_time_in_ms || close_time_ms >= end_time_in_ms) continue
1650
+ let trade = {
1651
+ order_id: fill.order_id != null ? fill.order_id.toString() : '',
1652
+ pair,
1653
+ type: String(fill.side || '').toLowerCase(),
1654
+ price: parseFloat(fill.price),
1655
+ amount: parseFloat(fill.size),
1656
+ total: parseFloat(fill.price) * parseFloat(fill.size),
1657
+ close_time: new Date(close_time_ms),
1658
+ fees: fill.funding_currency && fill.fee != null ? { [post_process_cur(fill.funding_currency)]: parseFloat(fill.fee) } : {},
1659
+ role: fill.liquidity === 'M' ? 'maker' : 'taker',
1660
+ }
1661
+ if (fill.trade_id != null) trade.trade_id = fill.trade_id.toString()
1662
+ results.push(trade)
1663
+ }
1664
+ const next_after = response.res?.headers?.['cb-after']
1665
+ if (!next_after || oldest_ms < start_time_in_ms) break
1666
+ after = next_after
1667
+ await sleep(50)
1668
+ }
1669
+ }
1670
+ cb({ success: true, body: sort_history_rows(filter_history_in_range(results, start_time_in_ms, end_time_in_ms)) })
1671
+ } catch (error) {
1672
+ cb({ success: false, error: error?.error || error_codes.other, body: error?.body })
1673
+ }
1674
+ }
1675
+ this.private_limiter.process(get_history_trades_base, pair, start_time_in_ms, end_time_in_ms, cb)
1676
+ }
1575
1677
  _convert(from_cur, to_cur, amount, cb) {
1576
1678
  if (this.advanced) {
1577
1679
  cb({ success: false, error: error_codes.other, body: { message: 'Convert not supported on Advanced API' } })
@@ -476,6 +476,9 @@ module.exports = class Coinstore extends ExchangeBase {
476
476
  }
477
477
  this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
478
478
  }
479
+ get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
480
+ this._get_history_trades_via_timeframe(pair, start_time_in_ms, end_time_in_ms, cb)
481
+ }
479
482
  static get_precision(cb) {
480
483
  const price_precision = {}
481
484
  const amount_precision = {}
@@ -493,6 +493,72 @@ module.exports = class Cryptocom extends ExchangeBase {
493
493
  }
494
494
  })
495
495
  }
496
+ get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
497
+ let [api_key, secret_key] = this.api_secret_key
498
+ let { fetch_split_windows, filter_history_in_range, sort_history_rows } = ExchangeBase.history_utils
499
+ let fetch_window = (window_start_ms, window_stop_ms) =>
500
+ new Promise((resolve, reject) => {
501
+ let req = {
502
+ id: 11,
503
+ method: 'private/get-trades',
504
+ params: {
505
+ instrument_name: pre_process_pair(pair),
506
+ start_time: window_start_ms,
507
+ end_time: window_stop_ms - 1,
508
+ limit: 100,
509
+ },
510
+ api_key: api_key,
511
+ nonce: Date.now(),
512
+ }
513
+ req.sig = get_signature_crypto(req, secret_key)
514
+ needle.post('https://api.crypto.com/v2/private/get-trades', req, options, (err, res, body) => {
515
+ body = parse_body(body)
516
+ let rows = body?.result?.data || body?.result?.trade_list
517
+ if (body && body.code === 0 && Array.isArray(rows)) {
518
+ resolve(
519
+ rows.map((trade) => {
520
+ let price = parseFloat(trade.traded_price)
521
+ let amount = parseFloat(trade.traded_quantity)
522
+ let fee = Math.abs(parseFloat(trade.fees || trade.fee))
523
+ let mapped_trade = {
524
+ order_id: (trade.order_id || '').toString(),
525
+ trade_id: (trade.trade_id || trade.trade_match_id || '').toString(),
526
+ pair,
527
+ type: (trade.side || '').toLowerCase(),
528
+ price,
529
+ amount,
530
+ total: price * amount,
531
+ close_time: new Date(parseInt(trade.create_time)),
532
+ }
533
+ if (trade.taker_side) mapped_trade.role = trade.taker_side.toLowerCase()
534
+ if (Number.isFinite(fee) && trade.fee_instrument_name) {
535
+ mapped_trade.fees = {
536
+ [post_process_cur(trade.fee_instrument_name)]: fee,
537
+ }
538
+ }
539
+ return mapped_trade
540
+ })
541
+ )
542
+ } else if (body) {
543
+ reject({ success: false, error: error_codes.other, body })
544
+ } else {
545
+ reject({ success: false, error: error_codes.timeout })
546
+ }
547
+ })
548
+ })
549
+ ;(async () => {
550
+ let trades = await fetch_split_windows({
551
+ start_ms: start_time_in_ms,
552
+ stop_ms: end_time_in_ms,
553
+ initial_window_ms: 7 * 24 * 60 * 60 * 1000,
554
+ page_limit: 100,
555
+ fetch_window,
556
+ delay_ms: 250,
557
+ })
558
+ trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
559
+ cb({ success: true, body: trades })
560
+ })().catch((error) => cb(error))
561
+ }
496
562
  get_all_deposits(timeframe_in_ms, cb) {
497
563
  let [api_key, secret_key] = this.api_secret_key
498
564
  let req = {
@@ -467,6 +467,70 @@ module.exports = class Deepcoin extends ExchangeBase {
467
467
  }
468
468
  this.private_limiter.process(get_all_trades_base, timeframe_in_ms, cb, 2)
469
469
  }
470
+ get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
471
+ let [api_key, secret_key] = this.api_secret_key
472
+ let { fetch_split_windows, filter_history_in_range, sort_history_rows } = ExchangeBase.history_utils
473
+ let path = '/deepcoin/trade/fills'
474
+ let fetch_window = (window_start_ms, window_stop_ms) =>
475
+ new Promise((resolve, reject) => {
476
+ let params = {
477
+ instType: 'SPOT',
478
+ instId: pre_process_pair(pair),
479
+ begin: window_start_ms,
480
+ end: window_stop_ms - 1,
481
+ limit: 100,
482
+ }
483
+ let query = qs.stringify(params)
484
+ let options = get_options(api_key, secret_key, this.passphrase, 'GET', path + '?' + query, '')
485
+ needle.get(base_url + path + '?' + query, options, (err, res, body) => {
486
+ body = parse_body(body)
487
+ if (body && body.code === '0' && Array.isArray(body.data)) {
488
+ resolve(
489
+ body.data.map((trade) => {
490
+ let price = parseFloat(trade.fillPx)
491
+ let amount = parseFloat(trade.fillSz)
492
+ let mapped_trade = {
493
+ order_id: (trade.ordId || '').toString(),
494
+ trade_id: (trade.tradeId || trade.billId || '').toString(),
495
+ pair: post_process_pair(trade.instId),
496
+ type: trade.side,
497
+ price,
498
+ amount,
499
+ total: price * amount,
500
+ close_time: new Date(parseInt(trade.ts)),
501
+ }
502
+ if (trade.execType === 'M') mapped_trade.role = 'maker'
503
+ if (trade.execType === 'T') mapped_trade.role = 'taker'
504
+ if (trade.feeCcy && Number.isFinite(parseFloat(trade.fee))) {
505
+ mapped_trade.fees = {
506
+ [post_process_cur(trade.feeCcy)]: Math.abs(parseFloat(trade.fee)),
507
+ }
508
+ }
509
+ return mapped_trade
510
+ })
511
+ )
512
+ } else if (body && (body.code === '50026' || body.code === '50001')) {
513
+ reject({ success: false, error: error_codes.service_unavailable, body })
514
+ } else if (body) {
515
+ reject({ success: false, error: error_codes.other, body })
516
+ } else {
517
+ reject({ success: false, error: error_codes.timeout })
518
+ }
519
+ })
520
+ })
521
+ ;(async () => {
522
+ let trades = await fetch_split_windows({
523
+ start_ms: start_time_in_ms,
524
+ stop_ms: end_time_in_ms,
525
+ initial_window_ms: 30 * 24 * 60 * 60 * 1000,
526
+ page_limit: 100,
527
+ fetch_window,
528
+ delay_ms: 250,
529
+ })
530
+ trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
531
+ cb({ success: true, body: trades })
532
+ })().catch((error) => cb(error))
533
+ }
470
534
  static get_min_amount(cb) {
471
535
  let min_quote_cur_amount = {}
472
536
  needle.get('https://api.deepcoin.com/deepcoin/market/instruments?instType=SPOT', (err, res, body) => {
@@ -381,6 +381,68 @@ module.exports = class Digifinex extends ExchangeBase {
381
381
  }
382
382
  this.private_limiter.process(get_trades_base, pair, timeframe_in_ms, cb)
383
383
  }
384
+ get_history_trades(pair, start_time_in_ms, end_time_in_ms, cb) {
385
+ let [api_key, secret_key] = this.api_secret_key
386
+ let { fetch_split_windows, filter_history_in_range, sort_history_rows } = ExchangeBase.history_utils
387
+ let fetch_window = (window_start_ms, window_stop_ms) =>
388
+ new Promise((resolve, reject) => {
389
+ let params = {
390
+ symbol: pre_process_pair(pair),
391
+ limit: 500,
392
+ start_time: Math.floor(window_start_ms / 1000),
393
+ end_time: Math.floor((window_stop_ms - 1) / 1000),
394
+ }
395
+ let signature = get_signature_digifinex(secret_key, params)
396
+ let options = get_options(api_key, signature)
397
+ let url = 'https://openapi.digifinex.com/v3/spot/mytrades?' + qs.stringify(params)
398
+ needle.get(url, options, (err, res, body) => {
399
+ if (body && body.code === 0 && Array.isArray(body.list)) {
400
+ resolve(
401
+ body.list.map((trade) => {
402
+ let price = parseFloat(trade.price)
403
+ let amount = parseFloat(trade.amount)
404
+ let is_maker = trade.is_maker === true || trade.is_maker === 1 || trade.is_maker === '1' || trade.is_maker === 'true'
405
+ let mapped_trade = {
406
+ order_id: (trade.order_id || '').toString(),
407
+ trade_id: (trade.id || trade.tid || '').toString(),
408
+ pair,
409
+ type: (trade.side || '').split('_')[0],
410
+ price,
411
+ amount,
412
+ total: price * amount,
413
+ close_time: new Date(parseInt(trade.timestamp) * 1000),
414
+ role: is_maker ? 'maker' : 'taker',
415
+ }
416
+ if (trade.fee_currency && Number.isFinite(parseFloat(trade.fee))) {
417
+ mapped_trade.fees = {
418
+ [post_process_cur(trade.fee_currency)]: Math.abs(parseFloat(trade.fee)),
419
+ }
420
+ }
421
+ return mapped_trade
422
+ })
423
+ )
424
+ } else if (body && body.toString().startsWith('<html>')) {
425
+ reject({ success: false, error: error_codes.service_unavailable, body })
426
+ } else if (body) {
427
+ reject({ success: false, error: error_codes.other, body })
428
+ } else {
429
+ reject({ success: false, error: error_codes.timeout })
430
+ }
431
+ })
432
+ })
433
+ ;(async () => {
434
+ let trades = await fetch_split_windows({
435
+ start_ms: start_time_in_ms,
436
+ stop_ms: end_time_in_ms,
437
+ initial_window_ms: 30 * 24 * 60 * 60 * 1000,
438
+ page_limit: 500,
439
+ fetch_window,
440
+ delay_ms: 250,
441
+ })
442
+ trades = sort_history_rows(filter_history_in_range(trades, start_time_in_ms, end_time_in_ms))
443
+ cb({ success: true, body: trades })
444
+ })().catch((error) => cb(error))
445
+ }
384
446
  get_all_deposits(timeframe_in_ms, cb) {
385
447
  let get_all_deposits_base = (timeframe_in_ms, cb) => {
386
448
  let [api_key, secret_key] = this.api_secret_key