diff --git a/js/gemini.js b/js/gemini.js index 7fe6229d8125..0041137a75c2 100644 --- a/js/gemini.js +++ b/js/gemini.js @@ -233,6 +233,11 @@ module.exports = class gemini extends Exchange { }, 'options': { 'fetchMarketsMethod': 'fetch_markets_from_web', + 'fetchMarketFromWebRetries': 10, + 'fetchMarketsFromAPI': { + 'fetchDetailsForAllSymbols': false, + 'fetchDetailsForMarketIds': [], + }, 'fetchTickerMethod': 'fetchTickerV1', // fetchTickerV1, fetchTickerV2, fetchTickerV1AndV2 'networkIds': { 'bitcoin': 'BTC', @@ -271,7 +276,21 @@ module.exports = class gemini extends Exchange { } async fetchMarketsFromWeb (params = {}) { - const response = await this.webGetRestApi (params); + // This endpoint so we retry + const maxRetries = this.safeInteger (this.options, 'fetchMarketFromWebRetries', 10); + let response = undefined; + let retry = 0; + while (retry < maxRetries) { + try { + response = await this.webGetRestApi (params); + break; + } catch (e) { + retry = retry + 1; + if (retry === maxRetries) { + throw e; + } + } + } const sections = response.split ('

Symbols and minimums

'); const numSections = sections.length; const error = this.id + ' fetchMarketsFromWeb() the ' + this.name + ' API doc HTML markup has changed, breaking the parser of order limits and precision info for ' + this.name + ' markets.'; @@ -371,6 +390,17 @@ module.exports = class gemini extends Exchange { return result; } + parseMarketActive (status) { + const statuses = { + 'open': true, + 'closed': false, + 'cancel_only': true, + 'post_only': true, + 'limit_only': true, + }; + return this.safeValue (statuses, status, true); + } + async fetchMarketsFromAPI (params = {}) { const response = await this.publicGetV1Symbols (params); // @@ -380,16 +410,86 @@ module.exports = class gemini extends Exchange { // ... // ] // - const result = []; + const result = {}; for (let i = 0; i < response.length; i++) { const marketId = response[i]; - const market = marketId; const idLength = marketId.length - 0; - const baseId = marketId.slice (0, idLength - 3); + const baseId = marketId.slice (0, idLength - 3); // Not true for all markets const quoteId = marketId.slice (idLength - 3, idLength); const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); - result.push ({ + result[marketId] = { + 'id': marketId, + 'symbol': base + '/' + quote, + 'swap': false, + 'future': false, + 'option': false, + 'active': undefined, + 'contract': false, + 'linear': undefined, + 'inverse': undefined, + 'strike': undefined, + 'optionType': undefined, + 'precision': { + 'price': undefined, + 'amount': undefined, + }, + 'limits': { + 'leverage': { + 'min': undefined, + 'max': undefined, + }, + 'amount': { + 'min': undefined, + 'max': undefined, + }, + 'price': { + 'max': undefined, + }, + }, + 'info': marketId, + }; + } + const options = this.safeValue (this.options, 'fetchMarketsFromAPI', {}); + const fetchDetailsForAllSymbols = this.safeValue (options, 'fetchDetailsForAllSymbols', false); + const fetchDetailsForMarketIds = this.safeValue (options, 'fetchDetailsForMarketIds', []); + let promises = []; + let marketIds = []; + if (fetchDetailsForAllSymbols) { + marketIds = response; + } else { + marketIds = fetchDetailsForMarketIds; + } + for (let i = 0; i < marketIds.length; i++) { + const marketId = marketIds[i]; + const method = 'publicGetV1SymbolsDetailsSymbol'; + const request = { + 'symbol': marketId, + }; + promises.push (this[method] (this.extend (request, params))); + // + // { + // "symbol": "BTCUSD", + // "base_currency": "BTC", + // "quote_currency": "USD", + // "tick_size": 1E-8, + // "quote_increment": 0.01, + // "min_order_size": "0.00001", + // "status": "open", + // "wrap_enabled": false + // } + // + } + promises = await Promise.all (promises); + for (let i = 0; i < promises.length; i++) { + const response = promises[i]; + const marketId = this.safeStringLower (response, 'symbol'); + const baseId = this.safeString (response, 'base_currency'); + const base = this.safeCurrencyCode (baseId); + const quoteId = this.safeString (response, 'quote_currency'); + const quote = this.safeCurrencyCode (quoteId); + const status = this.safeString (response, 'status'); + result[marketId] = ({ 'id': marketId, 'symbol': base + '/' + quote, 'base': base, @@ -404,7 +504,7 @@ module.exports = class gemini extends Exchange { 'swap': false, 'future': false, 'option': false, - 'active': undefined, + 'active': this.parseMarketActive (status), 'contract': false, 'linear': undefined, 'inverse': undefined, @@ -414,8 +514,8 @@ module.exports = class gemini extends Exchange { 'strike': undefined, 'optionType': undefined, 'precision': { - 'price': undefined, - 'amount': undefined, + 'price': this.safeNumber (response, 'quote_increment'), + 'amount': this.safeNumber (response, 'tick_size'), }, 'limits': { 'leverage': { @@ -423,7 +523,7 @@ module.exports = class gemini extends Exchange { 'max': undefined, }, 'amount': { - 'min': undefined, + 'min': this.safeNumber (response, 'min_order_size'), 'max': undefined, }, 'price': { @@ -435,10 +535,10 @@ module.exports = class gemini extends Exchange { 'max': undefined, }, }, - 'info': market, + 'info': response, }); } - return result; + return this.toArray (result); } async fetchOrderBook (symbol, limit = undefined, params = {}) {