ccxt 4.2.68 → 4.2.70

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.
@@ -88,6 +88,7 @@ export default class bitstamp extends Exchange {
88
88
  'setLeverage': false,
89
89
  'setMarginMode': false,
90
90
  'setPositionMode': false,
91
+ 'transfer': true,
91
92
  'withdraw': true,
92
93
  },
93
94
  'urls': {
@@ -2122,6 +2123,73 @@ export default class bitstamp extends Exchange {
2122
2123
  const response = await this[method](this.extend(request, params));
2123
2124
  return this.parseTransaction(response, currency);
2124
2125
  }
2126
+ async transfer(code, amount, fromAccount, toAccount, params = {}) {
2127
+ /**
2128
+ * @method
2129
+ * @name bitstamp#transfer
2130
+ * @description transfer currency internally between wallets on the same account
2131
+ * @see https://www.bitstamp.net/api/#tag/Sub-account/operation/TransferFromMainToSub
2132
+ * @see https://www.bitstamp.net/api/#tag/Sub-account/operation/TransferFromSubToMain
2133
+ * @param {string} code unified currency code
2134
+ * @param {float} amount amount to transfer
2135
+ * @param {string} fromAccount account to transfer from
2136
+ * @param {string} toAccount account to transfer to
2137
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
2138
+ * @returns {object} a [transfer structure]{@link https://docs.ccxt.com/#/?id=transfer-structure}
2139
+ */
2140
+ await this.loadMarkets();
2141
+ const currency = this.currency(code);
2142
+ amount = this.currencyToPrecision(code, amount);
2143
+ amount = this.parseToNumeric(amount);
2144
+ const request = {
2145
+ 'amount': amount,
2146
+ 'currency': currency['id'].toUpperCase(),
2147
+ };
2148
+ let response = undefined;
2149
+ if (fromAccount === 'main') {
2150
+ request['subAccount'] = toAccount;
2151
+ response = await this.privatePostTransferFromMain(this.extend(request, params));
2152
+ }
2153
+ else if (toAccount === 'main') {
2154
+ request['subAccount'] = fromAccount;
2155
+ response = await this.privatePostTransferToMain(this.extend(request, params));
2156
+ }
2157
+ else {
2158
+ throw new BadRequest(this.id + ' transfer() only supports from or to main');
2159
+ }
2160
+ //
2161
+ // { status: 'ok' }
2162
+ //
2163
+ const transfer = this.parseTransfer(response, currency);
2164
+ transfer['amount'] = amount;
2165
+ transfer['fromAccount'] = fromAccount;
2166
+ transfer['toAccount'] = toAccount;
2167
+ return transfer;
2168
+ }
2169
+ parseTransfer(transfer, currency = undefined) {
2170
+ //
2171
+ // { status: 'ok' }
2172
+ //
2173
+ const status = this.safeString(transfer, 'status');
2174
+ return {
2175
+ 'info': transfer,
2176
+ 'id': undefined,
2177
+ 'timestamp': undefined,
2178
+ 'datetime': undefined,
2179
+ 'currency': currency['code'],
2180
+ 'amount': undefined,
2181
+ 'fromAccount': undefined,
2182
+ 'toAccount': undefined,
2183
+ 'status': this.parseTransferStatus(status),
2184
+ };
2185
+ }
2186
+ parseTransferStatus(status) {
2187
+ const statuses = {
2188
+ 'ok': 'ok',
2189
+ 'error': 'failed',
2190
+ };
2191
+ return this.safeString(statuses, status, status);
2192
+ }
2125
2193
  nonce() {
2126
2194
  return this.milliseconds();
2127
2195
  }
package/js/src/gate.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import Exchange from './abstract/gate.js';
2
- import type { Int, OrderSide, OrderType, OHLCV, Trade, FundingRateHistory, OpenInterest, Order, Balances, OrderRequest, FundingHistory, Str, Transaction, Ticker, OrderBook, Tickers, Greeks, Strings, Market, Currency, MarketInterface, TransferEntry } from './base/types.js';
2
+ import type { Int, OrderSide, OrderType, OHLCV, Trade, FundingRateHistory, OpenInterest, Order, Balances, OrderRequest, FundingHistory, Str, Transaction, Ticker, OrderBook, Tickers, Greeks, Strings, Market, Currency, MarketInterface, TransferEntry, Leverage, Leverages } from './base/types.js';
3
3
  /**
4
4
  * @class gate
5
5
  * @augments Exchange
@@ -361,5 +361,8 @@ export default class gate extends Exchange {
361
361
  info: any;
362
362
  };
363
363
  closePosition(symbol: string, side?: OrderSide, params?: {}): Promise<Order>;
364
+ fetchLeverage(symbol: string, params?: {}): Promise<Leverage>;
365
+ fetchLeverages(symbols?: string[], params?: {}): Promise<Leverages>;
366
+ parseLeverage(leverage: any, market?: any): Leverage;
364
367
  handleErrors(code: any, reason: any, url: any, method: any, headers: any, body: any, response: any, requestHeaders: any, requestBody: any): any;
365
368
  }
package/js/src/gate.js CHANGED
@@ -118,7 +118,8 @@ export default class gate extends Exchange {
118
118
  'fetchIsolatedBorrowRate': false,
119
119
  'fetchIsolatedBorrowRates': false,
120
120
  'fetchLedger': true,
121
- 'fetchLeverage': false,
121
+ 'fetchLeverage': true,
122
+ 'fetchLeverages': true,
122
123
  'fetchLeverageTiers': true,
123
124
  'fetchLiquidations': true,
124
125
  'fetchMarginMode': false,
@@ -4483,7 +4484,10 @@ export default class gate extends Exchange {
4483
4484
  if (lastTradeTimestamp === undefined) {
4484
4485
  lastTradeTimestamp = this.safeTimestamp2(order, 'update_time', 'finish_time');
4485
4486
  }
4486
- const marketType = ('currency_pair' in order) ? 'spot' : 'contract';
4487
+ let marketType = 'contract';
4488
+ if (('currency_pair' in order) || ('market' in order)) {
4489
+ marketType = 'spot';
4490
+ }
4487
4491
  const exchangeSymbol = this.safeString2(order, 'currency_pair', 'market', contract);
4488
4492
  const symbol = this.safeSymbol(exchangeSymbol, market, '_', marketType);
4489
4493
  // Everything below this(above return) is related to fees
@@ -7015,6 +7019,178 @@ export default class gate extends Exchange {
7015
7019
  }
7016
7020
  return await this.createOrder(symbol, 'market', side, 0, undefined, params);
7017
7021
  }
7022
+ async fetchLeverage(symbol, params = {}) {
7023
+ /**
7024
+ * @method
7025
+ * @name gate#fetchLeverage
7026
+ * @description fetch the set leverage for a market
7027
+ * @see https://www.gate.io/docs/developers/apiv4/en/#get-unified-account-information
7028
+ * @see https://www.gate.io/docs/developers/apiv4/en/#get-detail-of-lending-market
7029
+ * @see https://www.gate.io/docs/developers/apiv4/en/#query-one-single-margin-currency-pair-deprecated
7030
+ * @param {string} symbol unified market symbol
7031
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
7032
+ * @param {boolean} [params.unified] default false, set to true for fetching the unified accounts leverage
7033
+ * @returns {object} a [leverage structure]{@link https://docs.ccxt.com/#/?id=leverage-structure}
7034
+ */
7035
+ await this.loadMarkets();
7036
+ let market = undefined;
7037
+ if (symbol !== undefined) {
7038
+ // unified account does not require a symbol
7039
+ market = this.market(symbol);
7040
+ }
7041
+ const request = {};
7042
+ let response = undefined;
7043
+ const isUnified = this.safeBool(params, 'unified');
7044
+ params = this.omit(params, 'unified');
7045
+ if (market['spot']) {
7046
+ request['currency_pair'] = market['id'];
7047
+ if (isUnified) {
7048
+ response = await this.publicMarginGetUniCurrencyPairsCurrencyPair(this.extend(request, params));
7049
+ //
7050
+ // {
7051
+ // "currency_pair": "BTC_USDT",
7052
+ // "base_min_borrow_amount": "0.0001",
7053
+ // "quote_min_borrow_amount": "1",
7054
+ // "leverage": "10"
7055
+ // }
7056
+ //
7057
+ }
7058
+ else {
7059
+ response = await this.publicMarginGetCurrencyPairsCurrencyPair(this.extend(request, params));
7060
+ //
7061
+ // {
7062
+ // "id": "BTC_USDT",
7063
+ // "base": "BTC",
7064
+ // "quote": "USDT",
7065
+ // "leverage": 10,
7066
+ // "min_base_amount": "0.0001",
7067
+ // "min_quote_amount": "1",
7068
+ // "max_quote_amount": "40000000",
7069
+ // "status": 1
7070
+ // }
7071
+ //
7072
+ }
7073
+ }
7074
+ else if (isUnified) {
7075
+ response = await this.privateUnifiedGetAccounts(this.extend(request, params));
7076
+ //
7077
+ // {
7078
+ // "user_id": 10001,
7079
+ // "locked": false,
7080
+ // "balances": {
7081
+ // "ETH": {
7082
+ // "available": "0",
7083
+ // "freeze": "0",
7084
+ // "borrowed": "0.075393666654",
7085
+ // "negative_liab": "0",
7086
+ // "futures_pos_liab": "0",
7087
+ // "equity": "1016.1",
7088
+ // "total_freeze": "0",
7089
+ // "total_liab": "0"
7090
+ // },
7091
+ // "POINT": {
7092
+ // "available": "9999999999.017023138734",
7093
+ // "freeze": "0",
7094
+ // "borrowed": "0",
7095
+ // "negative_liab": "0",
7096
+ // "futures_pos_liab": "0",
7097
+ // "equity": "12016.1",
7098
+ // "total_freeze": "0",
7099
+ // "total_liab": "0"
7100
+ // },
7101
+ // "USDT": {
7102
+ // "available": "0.00000062023",
7103
+ // "freeze": "0",
7104
+ // "borrowed": "0",
7105
+ // "negative_liab": "0",
7106
+ // "futures_pos_liab": "0",
7107
+ // "equity": "16.1",
7108
+ // "total_freeze": "0",
7109
+ // "total_liab": "0"
7110
+ // }
7111
+ // },
7112
+ // "total": "230.94621713",
7113
+ // "borrowed": "161.66395521",
7114
+ // "total_initial_margin": "1025.0524665088",
7115
+ // "total_margin_balance": "3382495.944473949183",
7116
+ // "total_maintenance_margin": "205.01049330176",
7117
+ // "total_initial_margin_rate": "3299.827135672679",
7118
+ // "total_maintenance_margin_rate": "16499.135678363399",
7119
+ // "total_available_margin": "3381470.892007440383",
7120
+ // "unified_account_total": "3381470.892007440383",
7121
+ // "unified_account_total_liab": "0",
7122
+ // "unified_account_total_equity": "100016.1",
7123
+ // "leverage": "2"
7124
+ // }
7125
+ //
7126
+ }
7127
+ else {
7128
+ throw new NotSupported(this.id + ' fetchLeverage() does not support ' + market['type'] + ' markets');
7129
+ }
7130
+ return this.parseLeverage(response, market);
7131
+ }
7132
+ async fetchLeverages(symbols = undefined, params = {}) {
7133
+ /**
7134
+ * @method
7135
+ * @name gate#fetchLeverages
7136
+ * @description fetch the set leverage for all leverage markets, only spot margin is supported on gate
7137
+ * @see https://www.gate.io/docs/developers/apiv4/en/#list-lending-markets
7138
+ * @see https://www.gate.io/docs/developers/apiv4/en/#list-all-supported-currency-pairs-supported-in-margin-trading-deprecated
7139
+ * @param {string[]} symbols a list of unified market symbols
7140
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
7141
+ * @param {boolean} [params.unified] default false, set to true for fetching unified account leverages
7142
+ * @returns {object} a list of [leverage structures]{@link https://docs.ccxt.com/#/?id=leverage-structure}
7143
+ */
7144
+ await this.loadMarkets();
7145
+ symbols = this.marketSymbols(symbols);
7146
+ let response = undefined;
7147
+ const isUnified = this.safeBool(params, 'unified');
7148
+ params = this.omit(params, 'unified');
7149
+ let marketIdRequest = 'id';
7150
+ if (isUnified) {
7151
+ marketIdRequest = 'currency_pair';
7152
+ response = await this.publicMarginGetUniCurrencyPairs(params);
7153
+ //
7154
+ // [
7155
+ // {
7156
+ // "currency_pair": "1INCH_USDT",
7157
+ // "base_min_borrow_amount": "8",
7158
+ // "quote_min_borrow_amount": "1",
7159
+ // "leverage": "3"
7160
+ // },
7161
+ // ]
7162
+ //
7163
+ }
7164
+ else {
7165
+ response = await this.publicMarginGetCurrencyPairs(params);
7166
+ //
7167
+ // [
7168
+ // {
7169
+ // "id": "1CAT_USDT",
7170
+ // "base": "1CAT",
7171
+ // "quote": "USDT",
7172
+ // "leverage": 3,
7173
+ // "min_base_amount": "71",
7174
+ // "min_quote_amount": "1",
7175
+ // "max_quote_amount": "10000",
7176
+ // "status": 1
7177
+ // },
7178
+ // ]
7179
+ //
7180
+ }
7181
+ return this.parseLeverages(response, symbols, marketIdRequest, 'spot');
7182
+ }
7183
+ parseLeverage(leverage, market = undefined) {
7184
+ const marketId = this.safeString2(leverage, 'currency_pair', 'id');
7185
+ const leverageValue = this.safeInteger(leverage, 'leverage');
7186
+ return {
7187
+ 'info': leverage,
7188
+ 'symbol': this.safeSymbol(marketId, market, '_', 'spot'),
7189
+ 'marginMode': undefined,
7190
+ 'longLeverage': leverageValue,
7191
+ 'shortLeverage': leverageValue,
7192
+ };
7193
+ }
7018
7194
  handleErrors(code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
7019
7195
  if (response === undefined) {
7020
7196
  return undefined;
@@ -12,7 +12,7 @@ export default class gemini extends Exchange {
12
12
  fetchMarketsFromWeb(params?: {}): Promise<any[]>;
13
13
  parseMarketActive(status: any): boolean;
14
14
  fetchUSDTMarkets(params?: {}): Promise<any[]>;
15
- fetchMarketsFromAPI(params?: {}): Promise<unknown[]>;
15
+ fetchMarketsFromAPI(params?: {}): Promise<any[]>;
16
16
  parseMarket(response: any): Market;
17
17
  fetchOrderBook(symbol: string, limit?: Int, params?: {}): Promise<OrderBook>;
18
18
  fetchTickerV1(symbol: string, params?: {}): Promise<Ticker>;
package/js/src/gemini.js CHANGED
@@ -30,7 +30,7 @@ export default class gemini extends Exchange {
30
30
  'CORS': undefined,
31
31
  'spot': true,
32
32
  'margin': false,
33
- 'swap': false,
33
+ 'swap': true,
34
34
  'future': false,
35
35
  'option': false,
36
36
  'addMargin': false,
@@ -255,11 +255,11 @@ export default class gemini extends Exchange {
255
255
  },
256
256
  },
257
257
  'options': {
258
- 'fetchMarketsMethod': 'fetch_markets_from_web',
258
+ 'fetchMarketsMethod': 'fetch_markets_from_api',
259
259
  'fetchMarketFromWebRetries': 10,
260
260
  'fetchMarketsFromAPI': {
261
261
  'fetchDetailsForAllSymbols': false,
262
- 'fetchDetailsForMarketIds': [],
262
+ 'quoteCurrencies': ['USDT', 'GUSD', 'USD', 'DAI', 'EUR', 'GBP', 'SGD', 'BTC', 'ETH', 'LTC', 'BCH'],
263
263
  },
264
264
  'fetchMarkets': {
265
265
  'webApiEnable': true,
@@ -315,10 +315,7 @@ export default class gemini extends Exchange {
315
315
  }
316
316
  //
317
317
  // {
318
- // "tradingPairs": [
319
- // [ "BTCAUD", 2, 8, "0.00001", 10, true ],
320
- // ...
321
- // ],
318
+ // "tradingPairs": [ [ 'BTCUSD', 2, 8, '0.00001', 10, true ], ... ],
322
319
  // "currencies": [
323
320
  // [ "ORCA", "Orca", 204, 6, 0, 6, 8, false, null, "solana" ], // as confirmed, precisions seem to be the 5th index
324
321
  // [ "ATOM", "Cosmos", 44, 6, 0, 6, 8, false, null, "cosmos" ],
@@ -337,6 +334,7 @@ export default class gemini extends Exchange {
337
334
  // }
338
335
  //
339
336
  const result = {};
337
+ this.options['tradingPairs'] = this.safeList(data, 'tradingPairs');
340
338
  const currenciesArray = this.safeValue(data, 'currencies', []);
341
339
  for (let i = 0; i < currenciesArray.length; i++) {
342
340
  const currency = currenciesArray[i];
@@ -546,7 +544,7 @@ export default class gemini extends Exchange {
546
544
  return result;
547
545
  }
548
546
  async fetchMarketsFromAPI(params = {}) {
549
- const response = await this.publicGetV1Symbols(params);
547
+ const marketIdsRaw = await this.publicGetV1Symbols(params);
550
548
  //
551
549
  // [
552
550
  // "btcusd",
@@ -554,93 +552,185 @@ export default class gemini extends Exchange {
554
552
  // ...
555
553
  // ]
556
554
  //
557
- const result = {};
558
- for (let i = 0; i < response.length; i++) {
559
- const marketId = response[i];
560
- const market = {
561
- 'symbol': marketId,
562
- };
563
- result[marketId] = this.parseMarket(market);
555
+ const result = [];
556
+ const options = this.safeDict(this.options, 'fetchMarketsFromAPI', {});
557
+ const bugSymbol = 'efilfil'; // we skip this inexistent test symbol, which bugs other functions
558
+ const marketIds = [];
559
+ for (let i = 0; i < marketIdsRaw.length; i++) {
560
+ if (marketIdsRaw[i] !== bugSymbol) {
561
+ marketIds.push(marketIdsRaw[i]);
562
+ }
564
563
  }
565
- const options = this.safeValue(this.options, 'fetchMarketsFromAPI', {});
566
- const fetchDetailsForAllSymbols = this.safeBool(options, 'fetchDetailsForAllSymbols', false);
567
- const fetchDetailsForMarketIds = this.safeValue(options, 'fetchDetailsForMarketIds', []);
568
- let promises = [];
569
- let marketIds = [];
570
- if (fetchDetailsForAllSymbols) {
571
- marketIds = response;
564
+ if (this.safeBool(options, 'fetchDetailsForAllSymbols', false)) {
565
+ const promises = [];
566
+ for (let i = 0; i < marketIds.length; i++) {
567
+ const marketId = marketIds[i];
568
+ const request = {
569
+ 'symbol': marketId,
570
+ };
571
+ promises.push(this.publicGetV1SymbolsDetailsSymbol(this.extend(request, params)));
572
+ //
573
+ // {
574
+ // "symbol": "BTCUSD",
575
+ // "base_currency": "BTC",
576
+ // "quote_currency": "USD",
577
+ // "tick_size": 1E-8,
578
+ // "quote_increment": 0.01,
579
+ // "min_order_size": "0.00001",
580
+ // "status": "open",
581
+ // "wrap_enabled": false
582
+ // }
583
+ //
584
+ }
585
+ const responses = await Promise.all(promises);
586
+ for (let i = 0; i < responses.length; i++) {
587
+ result.push(this.parseMarket(responses[i]));
588
+ }
572
589
  }
573
590
  else {
574
- marketIds = fetchDetailsForMarketIds;
575
- }
576
- for (let i = 0; i < marketIds.length; i++) {
577
- const marketId = marketIds[i];
578
- const request = {
579
- 'symbol': marketId,
580
- };
581
- promises.push(this.publicGetV1SymbolsDetailsSymbol(this.extend(request, params)));
582
- //
583
- // {
584
- // "symbol": "BTCUSD",
585
- // "base_currency": "BTC",
586
- // "quote_currency": "USD",
587
- // "tick_size": 1E-8,
588
- // "quote_increment": 0.01,
589
- // "min_order_size": "0.00001",
590
- // "status": "open",
591
- // "wrap_enabled": false
592
- // }
593
- //
594
- }
595
- promises = await Promise.all(promises);
596
- for (let i = 0; i < promises.length; i++) {
597
- const responseInner = promises[i];
598
- const marketId = this.safeStringLower(responseInner, 'symbol');
599
- result[marketId] = this.parseMarket(responseInner);
591
+ // use trading-pairs info, if it was fetched
592
+ const tradingPairs = this.safeList(this.options, 'tradingPairs');
593
+ if (tradingPairs !== undefined) {
594
+ const indexedTradingPairs = this.indexBy(tradingPairs, 0);
595
+ for (let i = 0; i < marketIds.length; i++) {
596
+ const marketId = marketIds[i];
597
+ const tradingPair = this.safeList(indexedTradingPairs, marketId.toUpperCase());
598
+ if (tradingPair !== undefined) {
599
+ result.push(this.parseMarket(tradingPair));
600
+ }
601
+ }
602
+ }
603
+ else {
604
+ for (let i = 0; i < marketIds.length; i++) {
605
+ result.push(this.parseMarket(marketIds[i]));
606
+ }
607
+ }
600
608
  }
601
- return this.toArray(result);
609
+ return result;
602
610
  }
603
611
  parseMarket(response) {
604
- const marketId = this.safeStringLower(response, 'symbol');
605
- let baseId = this.safeString(response, 'base_currency');
606
- let quoteId = this.safeString(response, 'quote_currency');
607
- if (baseId === undefined) {
608
- const idLength = marketId.length - 0;
609
- const isUSDT = marketId.indexOf('usdt') >= 0;
610
- const quoteSize = isUSDT ? 4 : 3;
611
- baseId = marketId.slice(0, idLength - quoteSize); // Not true for all markets
612
- quoteId = marketId.slice(idLength - quoteSize, idLength);
612
+ //
613
+ // response might be:
614
+ //
615
+ // btcusd
616
+ //
617
+ // or
618
+ //
619
+ // [
620
+ // 'BTCUSD', // symbol
621
+ // 2, // priceTickDecimalPlaces
622
+ // 8, // quantityTickDecimalPlaces
623
+ // '0.00001', // quantityMinimum
624
+ // 10, // quantityRoundDecimalPlaces
625
+ // true // minimumsAreInclusive
626
+ // ],
627
+ //
628
+ // or
629
+ //
630
+ // {
631
+ // "symbol": "BTCUSD", // perpetuals have 'PERP' suffix, i.e. DOGEUSDPERP
632
+ // "base_currency": "BTC",
633
+ // "quote_currency": "USD",
634
+ // "tick_size": 1E-8,
635
+ // "quote_increment": 0.01,
636
+ // "min_order_size": "0.00001",
637
+ // "status": "open",
638
+ // "wrap_enabled": false
639
+ // "product_type": "swap", // only in perps
640
+ // "contract_type": "linear", // only in perps
641
+ // "contract_price_currency": "GUSD" // only in perps
642
+ // }
643
+ //
644
+ let marketId = undefined;
645
+ let baseId = undefined;
646
+ let quoteId = undefined;
647
+ let settleId = undefined;
648
+ let tickSize = undefined;
649
+ let increment = undefined;
650
+ let minSize = undefined;
651
+ let status = undefined;
652
+ let swap = false;
653
+ let contractSize = undefined;
654
+ let linear = undefined;
655
+ let inverse = undefined;
656
+ const isString = (typeof response === 'string');
657
+ const isArray = (Array.isArray(response));
658
+ if (!isString && !isArray) {
659
+ marketId = this.safeStringLower(response, 'symbol');
660
+ minSize = this.safeNumber(response, 'min_order_size');
661
+ tickSize = this.safeNumber(response, 'tick_size');
662
+ increment = this.safeNumber(response, 'quote_increment');
663
+ status = this.parseMarketActive(this.safeString(response, 'status'));
664
+ baseId = this.safeString(response, 'base_currency');
665
+ quoteId = this.safeString(response, 'quote_currency');
666
+ settleId = this.safeString(response, 'contract_price_currency');
667
+ }
668
+ else {
669
+ // if no detailed API was called, then parse either string or array
670
+ if (isString) {
671
+ marketId = response;
672
+ }
673
+ else {
674
+ marketId = this.safeStringLower(response, 0);
675
+ minSize = this.safeNumber(response, 3);
676
+ tickSize = this.parseNumber(this.parsePrecision(this.safeString(response, 1)));
677
+ increment = this.parseNumber(this.parsePrecision(this.safeString(response, 2)));
678
+ }
679
+ const marketIdUpper = marketId.toUpperCase();
680
+ const isPerp = (marketIdUpper.indexOf('PERP') >= 0);
681
+ const marketIdWithoutPerp = marketIdUpper.replace('PERP', '');
682
+ const quoteQurrencies = this.handleOption('fetchMarketsFromAPI', 'quoteCurrencies', []);
683
+ for (let i = 0; i < quoteQurrencies.length; i++) {
684
+ const quoteCurrency = quoteQurrencies[i];
685
+ if (marketIdWithoutPerp.endsWith(quoteCurrency)) {
686
+ baseId = marketIdWithoutPerp.replace(quoteCurrency, '');
687
+ quoteId = quoteCurrency;
688
+ if (isPerp) {
689
+ settleId = quoteCurrency; // always same
690
+ }
691
+ break;
692
+ }
693
+ }
613
694
  }
614
695
  const base = this.safeCurrencyCode(baseId);
615
696
  const quote = this.safeCurrencyCode(quoteId);
616
- const status = this.safeString(response, 'status');
697
+ const settle = this.safeCurrencyCode(settleId);
698
+ let symbol = base + '/' + quote;
699
+ if (settleId !== undefined) {
700
+ symbol = symbol + ':' + settle;
701
+ swap = true;
702
+ contractSize = tickSize; // always same
703
+ linear = true; // always linear
704
+ inverse = false;
705
+ }
706
+ const type = swap ? 'swap' : 'spot';
617
707
  return {
618
708
  'id': marketId,
619
- 'symbol': base + '/' + quote,
709
+ 'symbol': symbol,
620
710
  'base': base,
621
711
  'quote': quote,
622
- 'settle': undefined,
712
+ 'settle': settle,
623
713
  'baseId': baseId,
624
714
  'quoteId': quoteId,
625
- 'settleId': undefined,
626
- 'type': 'spot',
627
- 'spot': true,
715
+ 'settleId': settleId,
716
+ 'type': type,
717
+ 'spot': !swap,
628
718
  'margin': false,
629
- 'swap': false,
719
+ 'swap': swap,
630
720
  'future': false,
631
721
  'option': false,
632
- 'active': this.parseMarketActive(status),
633
- 'contract': false,
634
- 'linear': undefined,
635
- 'inverse': undefined,
636
- 'contractSize': undefined,
722
+ 'active': status,
723
+ 'contract': swap,
724
+ 'linear': linear,
725
+ 'inverse': inverse,
726
+ 'contractSize': contractSize,
637
727
  'expiry': undefined,
638
728
  'expiryDatetime': undefined,
639
729
  'strike': undefined,
640
730
  'optionType': undefined,
641
731
  'precision': {
642
- 'price': this.safeNumber(response, 'quote_increment'),
643
- 'amount': this.safeNumber(response, 'tick_size'),
732
+ 'price': increment,
733
+ 'amount': tickSize,
644
734
  },
645
735
  'limits': {
646
736
  'leverage': {
@@ -648,7 +738,7 @@ export default class gemini extends Exchange {
648
738
  'max': undefined,
649
739
  },
650
740
  'amount': {
651
- 'min': this.safeNumber(response, 'min_order_size'),
741
+ 'min': minSize,
652
742
  'max': undefined,
653
743
  },
654
744
  'price': {
package/js/src/hitbtc.js CHANGED
@@ -318,6 +318,7 @@ export default class hitbtc extends Exchange {
318
318
  '2012': BadRequest,
319
319
  '2020': BadRequest,
320
320
  '2022': BadRequest,
321
+ '2024': InvalidOrder,
321
322
  '10001': BadRequest,
322
323
  '10021': AccountSuspended,
323
324
  '10022': BadRequest,
@@ -335,6 +336,7 @@ export default class hitbtc extends Exchange {
335
336
  '20012': ExchangeError,
336
337
  '20014': ExchangeError,
337
338
  '20016': ExchangeError,
339
+ '20018': ExchangeError,
338
340
  '20031': ExchangeError,
339
341
  '20032': ExchangeError,
340
342
  '20033': ExchangeError,
@@ -345,10 +347,15 @@ export default class hitbtc extends Exchange {
345
347
  '20043': ExchangeError,
346
348
  '20044': PermissionDenied,
347
349
  '20045': InvalidOrder,
350
+ '20047': InvalidOrder,
351
+ '20048': InvalidOrder,
352
+ '20049': InvalidOrder,
348
353
  '20080': ExchangeError,
349
354
  '21001': ExchangeError,
350
355
  '21003': AccountSuspended,
351
356
  '21004': AccountSuspended,
357
+ '22004': ExchangeError,
358
+ '22008': ExchangeError, // Gateway timeout exceeded.
352
359
  },
353
360
  'broad': {},
354
361
  },
package/js/src/htx.js CHANGED
@@ -470,6 +470,7 @@ export default class htx extends Exchange {
470
470
  'v2/sub-user/api-key-modification': 1,
471
471
  'v2/sub-user/api-key-deletion': 1,
472
472
  'v1/subuser/transfer': 10,
473
+ 'v1/trust/user/active/credit': 10,
473
474
  // Trading
474
475
  'v1/order/orders/place': 0.2,
475
476
  'v1/order/batch-orders': 0.4,