/// <summary> /// Get exchange symbols including available metadata such as min trade size and whether the market is active /// </summary> /// <returns>Collection of ExchangeMarkets</returns> public override IEnumerable <ExchangeMarket> GetSymbolsMetadata() { var markets = new List <ExchangeMarket>(); JObject obj = MakeJsonRequest <JObject>("/public/getmarkets"); JToken result = CheckError(obj); if (result is JArray array) { foreach (JToken token in array) { var market = new ExchangeMarket { MarketName = token["MarketName"].ToStringUpperInvariant(), IsActive = token["IsActive"].ConvertInvariant <bool>(), MinTradeSize = token["MinTradeSize"].ConvertInvariant <decimal>(), BaseCurrency = token["BaseCurrency"].ToStringUpperInvariant(), MarketCurrency = token["MarketCurrency"].ToStringUpperInvariant() }; markets.Add(market); } } return(markets); }
/// <summary> /// Get exchange symbols including available metadata such as min trade size and whether the market is active /// </summary> /// <returns>Collection of ExchangeMarkets</returns> protected override async Task <IEnumerable <ExchangeMarket> > OnGetSymbolsMetadataAsync() { var markets = new List <ExchangeMarket>(); JToken array = await MakeJsonRequestAsync <JToken>("/public/getmarkets"); // StepSize is 8 decimal places for both price and amount on everything at Bittrex const decimal StepSize = 0.00000001m; foreach (JToken token in array) { var market = new ExchangeMarket { BaseCurrency = token["BaseCurrency"].ToStringUpperInvariant(), IsActive = token["IsActive"].ConvertInvariant <bool>(), MarketCurrency = token["MarketCurrency"].ToStringUpperInvariant(), MarketName = token["MarketName"].ToStringUpperInvariant(), MinTradeSize = token["MinTradeSize"].ConvertInvariant <decimal>(), MinPrice = StepSize, PriceStepSize = StepSize, QuantityStepSize = StepSize }; markets.Add(market); } return(markets); }
/// <summary> /// Get exchange symbols including available metadata such as min trade size and whether the market is active /// </summary> /// <returns>Collection of ExchangeMarkets</returns> protected internal override async Task <IEnumerable <ExchangeMarket> > OnGetMarketSymbolsMetadataAsync() { var markets = new List <ExchangeMarket>(); JToken array = await MakeJsonRequestAsync <JToken>("/markets"); // StepSize is 8 decimal places for both price and amount on everything at Bittrex const decimal StepSize = 0.00000001m; foreach (JToken token in array) { var market = new ExchangeMarket { //NOTE: Bittrex is weird in that they call the QuoteCurrency the "BaseCurrency" and the BaseCurrency the "MarketCurrency". QuoteCurrency = token["quoteCurrencySymbol"].ToStringUpperInvariant(), IsActive = token["status"].ToStringLowerInvariant() == "online" ? true : false, BaseCurrency = token["baseCurrencySymbol"].ToStringUpperInvariant(), //NOTE: They also reverse the order of the currencies in the MarketName MarketSymbol = token["symbol"].ToStringUpperInvariant(), MinTradeSize = token["minTradeSize"].ConvertInvariant <decimal>(), MinPrice = StepSize, PriceStepSize = StepSize, QuantityStepSize = StepSize }; markets.Add(market); } return(markets); }
/// <summary> /// Get currencies from exchange market symbol. This method will call GetMarketSymbolsMetadataAsync if no MarketSymbolSeparator is defined for the exchange. /// </summary> /// <param name="marketSymbol">Market symbol</param> /// <returns>Base and quote currency</returns> public virtual (string baseCurrency, string quoteCurrency) ExchangeMarketSymbolToCurrencies(string marketSymbol) { marketSymbol.ThrowIfNullOrWhitespace(nameof(marketSymbol)); // no separator logic... if (string.IsNullOrWhiteSpace(MarketSymbolSeparator)) { try { // we must look it up via metadata, most often this call will be cached and fast ExchangeMarket marketSymbolMetadata = GetExchangeMarketFromCacheAsync(marketSymbol).Sync(); if (marketSymbolMetadata == null) { throw new InvalidDataException($"No market symbol metadata returned or unable to find symbol metadata for {marketSymbol}"); } return(marketSymbolMetadata.BaseCurrency, marketSymbolMetadata.QuoteCurrency); } catch (Exception e) { throw new InvalidOperationException($"Failed to retrieve symbol metadata for exchange {Name}.", e); } } // default behavior with separator return(OnSplitMarketSymbolToCurrencies(marketSymbol)); }
/// <summary> /// Gets the exchange market from this exchange's SymbolsMetadata cache. This will make a network request if needed to retrieve fresh markets from the exchange using GetSymbolsMetadataAsync(). /// Please note that sending a symbol that is not found over and over will result in many network requests. Only send symbols that you are confident exist on the exchange. /// </summary> /// <param name="marketSymbol">The market symbol. Ex. ADA/BTC. This is assumed to be normalized and already correct for the exchange.</param> /// <returns>The ExchangeMarket or null if it doesn't exist in the cache or there was an error</returns> public virtual async Task <ExchangeMarket> GetExchangeMarketFromCacheAsync(string marketSymbol) { try { // *NOTE*: custom caching, do not wrap in CacheMethodCall... // not sure if this is needed, but adding it just in case await new SynchronizationContextRemover(); ExchangeMarket[] markets = (await GetMarketSymbolsMetadataAsync()).ToArray(); ExchangeMarket market = markets.FirstOrDefault(m => m.MarketSymbol == marketSymbol); if (market == null) { // try again with a fresh request, every symbol *should* be in the response from PopulateExchangeMarketsAsync Cache.Remove(nameof(GetMarketSymbolsMetadataAsync)); markets = (await GetMarketSymbolsMetadataAsync()).ToArray(); // try and find the market again, this time if not found we give up and just return null market = markets.FirstOrDefault(m => m.MarketSymbol == marketSymbol); } return(market); } catch { // TODO: Report the error somehow, for now a failed network request will just return null symbol which will force the caller to use default handling } return(null); }
public override async Task <IEnumerable <ExchangeMarket> > GetMarketSymbolsMetadataAsync() { // [{"base_asset_symbol":"AERGO-46B","list_price":"0.00350000","lot_size":"1.00000000","quote_asset_symbol":"BNB","tick_size":"0.00000001"}, ... var markets = new List <ExchangeMarket>(); JToken allSymbols = await MakeJsonRequestAsync <JToken>("/markets"); foreach (JToken marketSymbolToken in allSymbols) { var QuoteCurrency = marketSymbolToken["quote_asset_symbol"].ToStringUpperInvariant(); var BaseCurrency = marketSymbolToken["base_asset_symbol"].ToStringUpperInvariant(); var market = new ExchangeMarket { MarketSymbol = BaseCurrency + '_' + QuoteCurrency, IsActive = true, BaseCurrency = BaseCurrency, QuoteCurrency = QuoteCurrency, }; market.MinTradeSize = marketSymbolToken["lot_size"].ConvertInvariant <decimal>(); market.QuantityStepSize = marketSymbolToken["lot_size"].ConvertInvariant <decimal>(); market.MinPrice = marketSymbolToken["tick_size"].ConvertInvariant <decimal>(); market.PriceStepSize = marketSymbolToken["tick_size"].ConvertInvariant <decimal>(); markets.Add(market); } return(markets); }
private async Task <List <string> > GetMarketSymbolList(string[] marketSymbols) { await PopulateLookupTables(); // prime cache Task <string>[] marketSymbolsArray = marketSymbols.Select(async(m) => { ExchangeMarket market = await GetExchangeMarketFromCacheAsync(m); if (market == null) { return(null); } return(market.AltMarketSymbol2); }).ToArray(); List <string> marketSymbolList = new List <string>(); foreach (Task <string> ms in marketSymbolsArray) { string result = await ms; if (result != null) { marketSymbolList.Add(result); } } return(marketSymbolList); }
protected override async Task <IEnumerable <ExchangeMarket> > OnGetSymbolsMetadataAsync() { /* * { * "status" * : * "ok", "data" * : * [{ * "base-currency": "btc", * "quote-currency": "usdt", * "price-precision": 2, * "amount-precision": 4, * "symbol-partition": "main" * }, { * "base-currency": "bch", * "quote-currency": "usdt", * "price-precision": 2, * "amount-precision": 4, * "symbol-partition": "main" * }, * */ if (ReadCache("GetSymbolsMetadata", out List <ExchangeMarket> markets)) { return(markets); } markets = new List <ExchangeMarket>(); JToken allSymbols = await MakeJsonRequestAsync <JToken>("/common/symbols", BaseUrlV1, null); foreach (var symbol in allSymbols) { var marketCurrency = symbol["base-currency"].ToStringLowerInvariant(); var baseCurrency = symbol["quote-currency"].ToStringLowerInvariant(); var price_precision = symbol["price-precision"].ConvertInvariant <double>(); var priceStepSize = Math.Pow(10, -price_precision); var amount_precision = symbol["amount-precision"].ConvertInvariant <double>(); var quantityStepSize = Math.Pow(10, -amount_precision); var market = new ExchangeMarket() { MarketCurrency = marketCurrency, BaseCurrency = baseCurrency, MarketName = marketCurrency + baseCurrency, IsActive = true, }; market.PriceStepSize = priceStepSize.ConvertInvariant <decimal>(); market.QuantityStepSize = quantityStepSize.ConvertInvariant <decimal>(); market.MinPrice = market.PriceStepSize.Value; market.MinTradeSize = market.QuantityStepSize.Value; markets.Add(market); } WriteCache("GetSymbolsMetadata", TimeSpan.FromMinutes(60.0), markets); return(markets); }
protected override async Task <IEnumerable <ExchangeMarket> > OnGetMarketSymbolsMetadataAsync() { List <ExchangeMarket> markets = new List <ExchangeMarket>(); // [{"symbol":"REQ-ETH","quoteMaxSize":"99999999","enableTrading":true,"priceIncrement":"0.0000001","baseMaxSize":"1000000","baseCurrency":"REQ","quoteCurrency":"ETH","market":"ETH","quoteIncrement":"0.0000001","baseMinSize":"1","quoteMinSize":"0.00001","name":"REQ-ETH","baseIncrement":"0.0001"}, ... ] JToken marketSymbolTokens = await MakeJsonRequestAsync <JToken>("/symbols"); foreach (JToken marketSymbolToken in marketSymbolTokens) { ExchangeMarket market = new ExchangeMarket() { MarketSymbol = marketSymbolToken["symbol"].ToStringInvariant(), BaseCurrency = marketSymbolToken["baseCurrency"].ToStringInvariant(), QuoteCurrency = marketSymbolToken["quoteCurrency"].ToStringInvariant(), MinTradeSize = marketSymbolToken["baseMinSize"].ConvertInvariant <decimal>(), MinTradeSizeInQuoteCurrency = marketSymbolToken["quoteMinSize"].ConvertInvariant <decimal>(), MaxTradeSize = marketSymbolToken["baseMaxSize"].ConvertInvariant <decimal>(), MaxTradeSizeInQuoteCurrency = marketSymbolToken["quoteMaxSize"].ConvertInvariant <decimal>(), QuantityStepSize = marketSymbolToken["baseIncrement"].ConvertInvariant <decimal>(), PriceStepSize = marketSymbolToken["priceIncrement"].ConvertInvariant <decimal>(), IsActive = marketSymbolToken["enableTrading"].ConvertInvariant <bool>(), }; markets.Add(market); } return(markets); }
public override async Task <(string baseCurrency, string quoteCurrency)> ExchangeMarketSymbolToCurrenciesAsync(string marketSymbol) { ExchangeMarket market = await GetExchangeMarketFromCacheAsync(marketSymbol); if (market == null) { throw new ArgumentException("Unable to get currencies for market symbol " + marketSymbol); } return(market.BaseCurrency, market.QuoteCurrency); }
/// <inheritdoc /> protected async internal override Task <IEnumerable <ExchangeMarket> > OnGetMarketSymbolsMetadataAsync() { //{ // "name": "BTC-0628", // "baseCurrency": null, // "quoteCurrency": null, // "quoteVolume24h": 28914.76, // "change1h": 0.012, // "change24h": 0.0299, // "changeBod": 0.0156, // "highLeverageFeeExempt": false, // "minProvideSize": 0.001, // "type": "future", // "underlying": "BTC", // "enabled": true, // "ask": 3949.25, // "bid": 3949, // "last": 10579.52, // "postOnly": false, // "price": 10579.52, // "priceIncrement": 0.25, // "sizeIncrement": 0.0001, // "restricted": false, // "volumeUsd24h": 28914.76 //} var markets = new List <ExchangeMarket>(); JToken result = await MakeJsonRequestAsync <JToken>("/markets"); foreach (JToken token in result.Children()) { var symbol = token["name"].ToStringInvariant(); if (!Regex.Match(symbol, @"[\w\d]*\/[[\w\d]]*").Success) { continue; } var market = new ExchangeMarket() { MarketSymbol = symbol, BaseCurrency = token["baseCurrency"].ToStringInvariant(), QuoteCurrency = token["quoteCurrency"].ToStringInvariant(), PriceStepSize = token["priceIncrement"].ConvertInvariant <decimal>(), QuantityStepSize = token["sizeIncrement"].ConvertInvariant <decimal>(), MinTradeSize = token["minProvideSize"].ConvertInvariant <decimal>(), IsActive = token["enabled"].ConvertInvariant <bool>(), }; markets.Add(market); } return(markets); }
protected override async Task <IEnumerable <ExchangeMarket> > OnGetMarketSymbolsMetadataAsync() { /* * {"code":0,"data":[{"baseCurrency":1,"collect":"0","isMarginOpen":false,"listDisplay": 0, * "marginRiskPreRatio": 0, * "marginRiskRatio": 0, * "marketFrom": 103, * "maxMarginLeverage": 0, * "maxPriceDigit": 8, * "maxSizeDigit": 6, * "minTradeSize": 0.00100000, * "online": 1, * "productId": 12, * "quoteCurrency": 0, * "quoteIncrement": 1E-8, * "quotePrecision": 4, * "sort": 10013, * "symbol": "ltc_btc" * }, */ List <ExchangeMarket> markets = new List <ExchangeMarket>(); JToken allMarketSymbolTokens = await MakeJsonRequestAsync <JToken>("/markets/products", BaseUrlV2); foreach (JToken marketSymbolToken in allMarketSymbolTokens) { var marketName = marketSymbolToken["symbol"].ToStringInvariant(); string[] pieces = marketName.ToStringUpperInvariant().Split('_'); var market = new ExchangeMarket { MarketSymbol = marketName, IsActive = marketSymbolToken["online"].ConvertInvariant <bool>(), QuoteCurrency = pieces[1], BaseCurrency = pieces[0], MarginEnabled = marketSymbolToken["isMarginOpen"].ConvertInvariant(false) }; var quotePrecision = marketSymbolToken["quotePrecision"].ConvertInvariant <double>(); var quantityStepSize = Math.Pow(10, -quotePrecision); market.QuantityStepSize = quantityStepSize.ConvertInvariant <decimal>(); var maxSizeDigit = marketSymbolToken["maxSizeDigit"].ConvertInvariant <double>(); var maxTradeSize = Math.Pow(10, maxSizeDigit); market.MaxTradeSize = maxTradeSize.ConvertInvariant <decimal>() - 1.0m; market.MinTradeSize = marketSymbolToken["minTradeSize"].ConvertInvariant <decimal>(); market.PriceStepSize = marketSymbolToken["quoteIncrement"].ConvertInvariant <decimal>(); market.MinPrice = market.PriceStepSize.Value; var maxPriceDigit = marketSymbolToken["maxPriceDigit"].ConvertInvariant <double>(); var maxPrice = Math.Pow(10, maxPriceDigit); market.MaxPrice = maxPrice.ConvertInvariant <decimal>() - 1.0m; markets.Add(market); } return(markets); }
protected override async Task <IEnumerable <ExchangeMarket> > OnGetMarketSymbolsMetadataAsync() { /* * { * "status" * : * "ok", "data" * : * [{ * "base-currency": "btc", * "quote-currency": "usdt", * "price-precision": 2, * "amount-precision": 4, * "symbol-partition": "main" * }, { * "base-currency": "bch", * "quote-currency": "usdt", * "price-precision": 2, * "amount-precision": 4, * "symbol-partition": "main" * }, * */ List <ExchangeMarket> markets = new List <ExchangeMarket>(); JToken allMarketSymbols = await MakeJsonRequestAsync <JToken>("/common/symbols", BaseUrlV1, null); foreach (var marketSymbol in allMarketSymbols) { var baseCurrency = marketSymbol["base-currency"].ToStringLowerInvariant(); var quoteCurrency = marketSymbol["quote-currency"].ToStringLowerInvariant(); var pricePrecision = marketSymbol["price-precision"].ConvertInvariant <double>(); var priceStepSize = Math.Pow(10, -pricePrecision).ConvertInvariant <decimal>(); var amountPrecision = marketSymbol["amount-precision"].ConvertInvariant <double>(); var quantityStepSize = Math.Pow(10, -amountPrecision).ConvertInvariant <decimal>(); var market = new ExchangeMarket { BaseCurrency = baseCurrency, QuoteCurrency = quoteCurrency, MarketSymbol = baseCurrency + quoteCurrency, IsActive = true, PriceStepSize = priceStepSize, QuantityStepSize = quantityStepSize, MinPrice = priceStepSize, MinTradeSize = quantityStepSize, }; markets.Add(market); } return(markets); }
protected override async Task <IEnumerable <ExchangeMarket> > OnGetSymbolsMetadataAsync() { if (ReadCache("GetSymbols", out List <ExchangeMarket> cachedMarkets)) { return(cachedMarkets); } var markets = new List <ExchangeMarket>(); JToken allPairs = await MakeJsonRequestAsync <JToken>("/symbols_details", BaseUrlV1); Match m; foreach (JToken pair in allPairs) { var market = new ExchangeMarket { IsActive = true, MarketName = NormalizeSymbol(pair["pair"].ToStringInvariant()), MinTradeSize = pair["minimum_order_size"].ConvertInvariant <decimal>() }; m = Regex.Match(market.MarketName, "^(BTC|USD|ETH|GBP|JPY|EUR|EOS)"); if (m.Success) { market.MarketCurrency = m.Value; market.BaseCurrency = market.MarketName.Substring(m.Length); } else { m = Regex.Match(market.MarketName, "(BTC|USD|ETH|GBP|JPY|EUR|EOS)$"); if (m.Success) { market.MarketCurrency = market.MarketName.Substring(0, m.Index); market.BaseCurrency = m.Value; } else { // TODO: Figure out a nicer way to handle newly added pairs market.MarketCurrency = market.MarketName.Substring(0, 3); market.BaseCurrency = market.MarketName.Substring(3); } } int pricePrecision = pair["price_precision"].ConvertInvariant <int>(); market.PriceStepSize = (decimal)Math.Pow(0.1, pricePrecision); markets.Add(market); } WriteCache("GetSymbols", TimeSpan.FromMinutes(60.0), markets); return(markets); }
protected override async Task <IEnumerable <ExchangeMarket> > OnGetMarketSymbolsMetadataAsync() { //https://poloniex.com/public?command=returnOrderBook¤cyPair=all&depth=0 /* * "BTC_CLAM": { * "asks": [], * "bids": [], * "isFrozen": "0", * "seq": 37268918 * }, ... */ var markets = new List <ExchangeMarket>(); Dictionary <string, JToken> lookup = await MakeJsonRequestAsync <Dictionary <string, JToken> >("/public?command=returnOrderBook¤cyPair=all&depth=0"); // StepSize is 8 decimal places for both price and amount on everything at Polo const decimal StepSize = 0.00000001m; const decimal minTradeSize = 0.0001m; foreach (var kvp in lookup) { var market = new ExchangeMarket { MarketSymbol = kvp.Key, IsActive = false }; string isFrozen = kvp.Value["isFrozen"].ToStringInvariant(); if (string.Equals(isFrozen, "0")) { market.IsActive = true; } string[] pairs = kvp.Key.Split('_'); if (pairs.Length == 2) { market.QuoteCurrency = pairs[0]; market.BaseCurrency = pairs[1]; market.PriceStepSize = StepSize; market.QuantityStepSize = StepSize; market.MinPrice = StepSize; market.MinTradeSize = minTradeSize; } markets.Add(market); } return(markets); }
public override IEnumerable <ExchangeMarket> GetSymbolsMetadata() { if (ReadCache("GetSymbols", out List <ExchangeMarket> cachedMarkets)) { return(cachedMarkets); } var markets = new List <ExchangeMarket>(); JToken allPairs = MakeJsonRequest <JToken>("/symbols_details", BaseUrlV1); Match m; foreach (JToken pair in allPairs) { var market = new ExchangeMarket { IsActive = true, MarketName = NormalizeSymbol(pair["pair"].ToStringInvariant()), MinTradeSize = pair["minimum_order_size"].ConvertInvariant <decimal>() }; m = Regex.Match(market.MarketName, "^(BTC|USD|ETH|GBP|JPY|EUR)"); if (m.Success) { market.MarketCurrency = m.Value; market.BaseCurrency = market.MarketName.Substring(m.Length); } else { m = Regex.Match(market.MarketName, "(BTC|USD|ETH|GBP|JPY|EUR)$"); if (m.Success) { market.MarketCurrency = market.MarketName.Substring(0, m.Index); market.BaseCurrency = m.Value; } else { throw new System.IO.InvalidDataException("Unexpected market name: " + market.MarketName); } } int pricePrecision = pair["price_precision"].ConvertInvariant <int>(); market.PriceStepSize = (decimal)Math.Pow(0.1, pricePrecision); markets.Add(market); } WriteCache("GetSymbols", TimeSpan.FromMinutes(60.0), markets); return(markets); }
protected internal override async Task <IEnumerable <ExchangeMarket> > OnGetMarketSymbolsMetadataAsync() { /* * { * "id": "ETH_USDT", * "base": "ETH", * "quote": "USDT", * "fee": "0.2", * "min_base_amount": "0.001", * "min_quote_amount": "1.0", * "amount_precision": 3, * "precision": 6, * "trade_status": "tradable", * "sell_start": 1516378650, * "buy_start": 1516378650 * } */ var markets = new List <ExchangeMarket>(); JToken obj = await MakeJsonRequestAsync <JToken>("/spot/currency_pairs"); if (!(obj is null)) { foreach (JToken marketSymbolToken in obj) { var market = new ExchangeMarket { MarketSymbol = marketSymbolToken["id"].ToStringUpperInvariant(), IsActive = marketSymbolToken["trade_status"].ToStringUpperInvariant() == "tradable", QuoteCurrency = marketSymbolToken["quote"].ToStringUpperInvariant(), BaseCurrency = marketSymbolToken["base"].ToStringUpperInvariant(), }; int pricePrecision = marketSymbolToken["precision"].ConvertInvariant <int>(); market.PriceStepSize = (decimal)Math.Pow(0.1, pricePrecision); int quantityPrecision = marketSymbolToken["amount_precision"].ConvertInvariant <int>(); market.QuantityStepSize = (decimal)Math.Pow(0.1, quantityPrecision); market.MinTradeSizeInQuoteCurrency = marketSymbolToken["min_quote_amount"].ConvertInvariant <decimal>(); market.MinTradeSize = marketSymbolToken["min_base_amount"].ConvertInvariant <decimal>(); markets.Add(market); } } return(markets); }
protected override async Task <IEnumerable <ExchangeMarket> > OnGetMarketSymbolsMetadataAsync() { var markets = new List <ExchangeMarket>(); JToken allPairs = await MakeJsonRequestAsync <JToken>("/symbols_details", BaseUrlV1); Match m; foreach (JToken pair in allPairs) { var market = new ExchangeMarket { IsActive = true, MarketSymbol = pair["pair"].ToStringInvariant(), MinTradeSize = pair["minimum_order_size"].ConvertInvariant <decimal>(), MaxTradeSize = pair["maximum_order_size"].ConvertInvariant <decimal>(), MarginEnabled = pair["margin"].ConvertInvariant <bool>(false) }; var pairPropertyVal = pair["pair"].ToStringUpperInvariant(); m = Regex.Match(pairPropertyVal, "^(BTC|USD|ETH|GBP|JPY|EUR|EOS)"); if (m.Success) { market.BaseCurrency = m.Value; market.QuoteCurrency = pairPropertyVal.Substring(m.Length); } else { m = Regex.Match(pairPropertyVal, "(BTC|USD|ETH|GBP|JPY|EUR|EOS)$"); if (m.Success) { market.BaseCurrency = pairPropertyVal.Substring(0, m.Index); market.QuoteCurrency = m.Value; } else { // TODO: Figure out a nicer way to handle newly added pairs market.BaseCurrency = pairPropertyVal.Substring(0, 3); market.QuoteCurrency = pairPropertyVal.Substring(3); } } int pricePrecision = pair["price_precision"].ConvertInvariant <int>(); market.PriceStepSize = (decimal)Math.Pow(0.1, pricePrecision); markets.Add(market); } return(markets); }
public override IEnumerable <ExchangeMarket> GetSymbolsMetadata() { var markets = new List <ExchangeMarket>(); JToken products = MakeJsonRequest <JToken>("/products"); foreach (JToken product in products) { var market = new ExchangeMarket(); market.MarketName = product["id"].ToStringUpperInvariant(); market.BaseCurrency = product["quote_currency"].ToStringUpperInvariant(); market.MarketCurrency = product["base_currency"].ToStringUpperInvariant(); market.IsActive = string.Equals(product["status"].ToStringInvariant(), "online", StringComparison.OrdinalIgnoreCase); market.MinTradeSize = product["base_min_size"].ConvertInvariant <decimal>(); market.PriceStepSize = product["quote_increment"].ConvertInvariant <decimal>(); markets.Add(market); } return(markets); }
protected override async Task <IEnumerable <ExchangeMarket> > OnGetMarketSymbolsMetadataAsync() { List <ExchangeMarket> markets = new List <ExchangeMarket>(); // [ { "coinType": "ETH", "trading": true, "symbol": "ETH-BTC", "lastDealPrice": 0.03169122, "buy": 0.03165041, "sell": 0.03168714, "change": -0.00004678, "coinTypePair": "BTC", "sort": 100, "feeRate": 0.001, "volValue": 121.99939218, "plus": true, "high": 0.03203444, "datetime": 1539730948000, "vol": 3847.9028281, "low": 0.03153312, "changeRate": -0.0015 }, ... ] JToken marketSymbolTokens = await MakeJsonRequestAsync <JToken>("/market/open/symbols"); foreach (JToken marketSymbolToken in marketSymbolTokens) { ExchangeMarket market = new ExchangeMarket() { IsActive = marketSymbolToken["trading"].ConvertInvariant <bool>(), BaseCurrency = marketSymbolToken["coinType"].ToStringInvariant(), QuoteCurrency = marketSymbolToken["coinTypePair"].ToStringInvariant(), MarketSymbol = marketSymbolToken["symbol"].ToStringInvariant() }; markets.Add(market); } return(markets); }
protected override async Task <IEnumerable <ExchangeMarket> > OnGetSymbolsMetadataAsync() { List <ExchangeMarket> markets = new List <ExchangeMarket>(); // [ { "coinType": "KCS", "trading": true, "lastDealPrice": 4500,"buy": 4120, "sell": 4500, "coinTypePair": "BTC", "sort": 0,"feeRate": 0.001,"volValue": 324866889, "high": 6890, "datetime": 1506051488000, "vol": 5363831663913, "low": 4500, "changeRate": -0.3431 }, ... ] JToken token = await MakeJsonRequestAsync <JToken>("/market/open/symbols"); foreach (JToken symbol in token) { ExchangeMarket market = new ExchangeMarket() { IsActive = symbol["trading"].ConvertInvariant <bool>(), MarketCurrency = symbol["coinType"].ToStringInvariant(), BaseCurrency = symbol["coinTypePair"].ToStringInvariant(), }; market.MarketName = market.MarketCurrency + "-" + market.BaseCurrency; markets.Add(market); } return(markets); }
public virtual async Task <(string baseCurrency, string quoteCurrency)> ExchangeMarketSymbolToCurrenciesAsync(string marketSymbol) { marketSymbol.ThrowIfNullOrWhitespace(nameof(marketSymbol)); // no separator logic... if (string.IsNullOrWhiteSpace(MarketSymbolSeparator)) { // we must look it up via metadata, most often this call will be cached and fast ExchangeMarket marketSymbolMetadata = await GetExchangeMarketFromCacheAsync(marketSymbol); if (marketSymbolMetadata == null) { throw new InvalidDataException($"No market symbol metadata returned or unable to find symbol metadata for {marketSymbol}"); } return(marketSymbolMetadata.BaseCurrency, marketSymbolMetadata.QuoteCurrency); } // default behavior with separator return(OnSplitMarketSymbolToCurrencies(marketSymbol)); }
public override IEnumerable <ExchangeMarket> GetSymbolsMetadata() { //https://poloniex.com/public?command=returnOrderBook¤cyPair=all&depth=0 /* * "BTC_CLAM": { * "asks": [], * "bids": [], * "isFrozen": "0", * "seq": 37268918 * }, ... */ var markets = new List <ExchangeMarket>(); Dictionary <string, JToken> lookup = MakeJsonRequest <Dictionary <string, JToken> >("/public?command=returnOrderBook¤cyPair=all&depth=0"); foreach (var kvp in lookup) { var market = new ExchangeMarket { MarketName = kvp.Key, IsActive = false }; string isFrozen = kvp.Value["isFrozen"].ToStringInvariant(); if (string.Equals(isFrozen, "0")) { market.IsActive = true; } string[] pairs = kvp.Key.Split('_'); if (pairs.Length == 2) { market.BaseCurrency = pairs[0]; market.MarketCurrency = pairs[1]; } // TODO: Not sure how to find min order amount markets.Add(market); } return(markets); }
protected override async Task <IEnumerable <ExchangeMarket> > OnGetSymbolsMetadataAsync() { var markets = new List <ExchangeMarket>(); JToken products = await MakeJsonRequestAsync <JToken>("/products"); foreach (JToken product in products) { var market = new ExchangeMarket { MarketName = product["id"].ToStringUpperInvariant(), BaseCurrency = product["quote_currency"].ToStringUpperInvariant(), MarketCurrency = product["base_currency"].ToStringUpperInvariant(), IsActive = string.Equals(product["status"].ToStringInvariant(), "online", StringComparison.OrdinalIgnoreCase), MinTradeSize = product["base_min_size"].ConvertInvariant <decimal>(), PriceStepSize = product["quote_increment"].ConvertInvariant <decimal>() }; markets.Add(market); } return(markets); }
/// <summary> /// Convert an exchange symbol into a global symbol, which will be the same for all exchanges. /// Global symbols are always uppercase and separate the currency pair with a hyphen (-). /// Global symbols list the base currency first (i.e. BTC) and conversion currency /// second (i.e. ETH). Example BTC-ETH, read as x BTC is worth y ETH. /// BTC is always first, then ETH, etc. Fiat pair is always first in global symbol too. /// </summary> /// <param name="marketSymbol">Exchange symbol</param> /// <returns>Global symbol</returns> public virtual string ExchangeMarketSymbolToGlobalMarketSymbol(string marketSymbol) { string modifiedMarketSymbol = marketSymbol; char separator; // if no separator, we must query metadata and build the pair if (string.IsNullOrWhiteSpace(MarketSymbolSeparator)) { // we must look it up via metadata, most often this call will be cached and fast ExchangeMarket marketSymbolMetadata = GetExchangeMarketFromCacheAsync(marketSymbol).Sync(); if (marketSymbolMetadata == null) { throw new InvalidDataException($"No market symbol metadata returned or unable to find symbol metadata for {marketSymbol}"); } modifiedMarketSymbol = marketSymbolMetadata.BaseCurrency + GlobalMarketSymbolSeparatorString + marketSymbolMetadata.QuoteCurrency; separator = GlobalMarketSymbolSeparator; } else { separator = MarketSymbolSeparator[0]; } return(ExchangeMarketSymbolToGlobalMarketSymbolWithSeparator(modifiedMarketSymbol, separator)); }
protected override async Task <IEnumerable <ExchangeMarket> > OnGetSymbolsMetadataAsync() { List <ExchangeMarket> markets = new List <ExchangeMarket>(); // {"success": true,"minBtcVolume": 0.0005,"restrictions": [{"currencyPair": "BTC/USD","priceScale": 5}, ... ]} JToken token = await MakeJsonRequestAsync <JToken>("/exchange/restrictions"); foreach (JToken market in token["restrictions"]) { var split = market["currencyPair"].ToStringInvariant().Split('/'); var exchangeMarket = new ExchangeMarket { MarketName = market["currencyPair"].ToStringInvariant(), BaseCurrency = split[1], MarketCurrency = split[0], IsActive = true, MinTradeSize = (decimal)market["minLimitQuantity"], PriceStepSize = (decimal?)(1 / Math.Pow(10, (int)market["priceScale"])) }; markets.Add(exchangeMarket); } return(markets); }
protected override async Task <IEnumerable <ExchangeMarket> > OnGetMarketSymbolsMetadataAsync() { /* * {{ * "symbol": ".XRPXBT", * "rootSymbol": "XRP", * "state": "Unlisted", * "typ": "MRCXXX", * "listing": null, * "front": null, * "expiry": null, * "settle": null, * "relistInterval": null, * "inverseLeg": "", * "sellLeg": "", * "buyLeg": "", * "optionStrikePcnt": null, * "optionStrikeRound": null, * "optionStrikePrice": null, * "optionMultiplier": null, * "positionCurrency": "", * "underlying": "XRP", * "quoteCurrency": "XBT", * "underlyingSymbol": "XRPXBT=", * "reference": "PLNX", * "referenceSymbol": "BTC_XRP", * "calcInterval": null, * "publishInterval": "2000-01-01T00:01:00Z", * "publishTime": null, * "maxOrderQty": null, * "maxPrice": null, * "lotSize": null, * "tickSize": 1E-08, * "multiplier": null, * "settlCurrency": "", * "underlyingToPositionMultiplier": null, * "underlyingToSettleMultiplier": null, * "quoteToSettleMultiplier": null, * "isQuanto": false, * "isInverse": false, * "initMargin": null, * "maintMargin": null, * "riskLimit": null, * "riskStep": null, * "limit": null, * "capped": false, * "taxed": false, * "deleverage": false, * "makerFee": null, * "takerFee": null, * "settlementFee": null, * "insuranceFee": null, * "fundingBaseSymbol": "", * "fundingQuoteSymbol": "", * "fundingPremiumSymbol": "", * "fundingTimestamp": null, * "fundingInterval": null, * "fundingRate": null, * "indicativeFundingRate": null, * "rebalanceTimestamp": null, * "rebalanceInterval": null, * "openingTimestamp": null, * "closingTimestamp": null, * "sessionInterval": null, * "prevClosePrice": null, * "limitDownPrice": null, * "limitUpPrice": null, * "bankruptLimitDownPrice": null, * "bankruptLimitUpPrice": null, * "prevTotalVolume": null, * "totalVolume": null, * "volume": null, * "volume24h": null, * "prevTotalTurnover": null, * "totalTurnover": null, * "turnover": null, * "turnover24h": null, * "prevPrice24h": 7.425E-05, * "vwap": null, * "highPrice": null, * "lowPrice": null, * "lastPrice": 7.364E-05, * "lastPriceProtected": null, * "lastTickDirection": "MinusTick", * "lastChangePcnt": -0.0082, * "bidPrice": null, * "midPrice": null, * "askPrice": null, * "impactBidPrice": null, * "impactMidPrice": null, * "impactAskPrice": null, * "hasLiquidity": false, * "openInterest": 0, * "openValue": 0, * "fairMethod": "", * "fairBasisRate": null, * "fairBasis": null, * "fairPrice": null, * "markMethod": "LastPrice", * "markPrice": 7.364E-05, * "indicativeTaxRate": null, * "indicativeSettlePrice": null, * "optionUnderlyingPrice": null, * "settledPrice": null, * "timestamp": "2018-07-05T13:27:15Z" * }} */ List <ExchangeMarket> markets = new List <ExchangeMarket>(); JToken allSymbols = await MakeJsonRequestAsync <JToken>("/instrument?count=500&reverse=false"); foreach (JToken marketSymbolToken in allSymbols) { var market = new ExchangeMarket { MarketSymbol = marketSymbolToken["symbol"].ToStringUpperInvariant(), IsActive = marketSymbolToken["state"].ToStringInvariant().EqualsWithOption("Open"), QuoteCurrency = marketSymbolToken["quoteCurrency"].ToStringUpperInvariant(), BaseCurrency = marketSymbolToken["underlying"].ToStringUpperInvariant(), }; try { market.PriceStepSize = marketSymbolToken["tickSize"].ConvertInvariant <decimal>(); market.MaxPrice = marketSymbolToken["maxPrice"].ConvertInvariant <decimal>(); //market.MinPrice = symbol["minPrice"].ConvertInvariant<decimal>(); market.MaxTradeSize = marketSymbolToken["maxOrderQty"].ConvertInvariant <decimal>(); //market.MinTradeSize = symbol["minQty"].ConvertInvariant<decimal>(); //market.QuantityStepSize = symbol["stepSize"].ConvertInvariant<decimal>(); } catch { } markets.Add(market); } return(markets); }
protected internal override async Task <IEnumerable <ExchangeMarket> > OnGetMarketSymbolsMetadataAsync() { //{"ADACAD": { // "altname": "ADACAD", // "wsname": "ADA/CAD", // "aclass_base": "currency", // "base": "ADA", // "aclass_quote": "currency", // "quote": "ZCAD", // "lot": "unit", // "pair_decimals": 6, // "lot_decimals": 8, // "lot_multiplier": 1, // "leverage_buy": [], // "leverage_sell": [], // "fees": [ // [ // 0, // 0.26 // ], // [ // 50000, // 0.24 // ], // [ // 100000, // 0.22 // ], // [ // 250000, // 0.2 // ], // [ // 500000, // 0.18 // ], // [ // 1000000, // 0.16 // ], // [ // 2500000, // 0.14 // ], // [ // 5000000, // 0.12 // ], // [ // 10000000, // 0.1 // ] // ], // "fees_maker": [ // [ // 0, // 0.16 // ], // [ // 50000, // 0.14 // ], // [ // 100000, // 0.12 // ], // [ // 250000, // 0.1 // ], // [ // 500000, // 0.08 // ], // [ // 1000000, // 0.06 // ], // [ // 2500000, // 0.04 // ], // [ // 5000000, // 0.02 // ], // [ // 10000000, // 0 // ] // ], // "fee_volume_currency": "ZUSD", // "margin_call": 80, // "margin_stop": 40 //}} var markets = new List <ExchangeMarket>(); JToken allPairs = await MakeJsonRequestAsync <JToken>("/0/public/AssetPairs"); var res = (from prop in allPairs.Children <JProperty>() select prop).ToArray(); foreach (JProperty prop in res.Where(p => !p.Name.EndsWith(".d"))) { JToken pair = prop.Value; JToken child = prop.Children().FirstOrDefault(); var quantityStepSize = Math.Pow(0.1, pair["lot_decimals"].ConvertInvariant <int>()).ConvertInvariant <decimal>(); var market = new ExchangeMarket { IsActive = true, MarketSymbol = prop.Name, AltMarketSymbol = child["altname"].ToStringInvariant(), AltMarketSymbol2 = child["wsname"].ToStringInvariant(), MinTradeSize = quantityStepSize, MarginEnabled = pair["leverage_buy"].Children().Any() || pair["leverage_sell"].Children().Any(), BaseCurrency = pair["base"].ToStringInvariant(), QuoteCurrency = pair["quote"].ToStringInvariant(), QuantityStepSize = quantityStepSize, PriceStepSize = Math.Pow(0.1, pair["pair_decimals"].ConvertInvariant <int>()).ConvertInvariant <decimal>() }; markets.Add(market); } return(markets); }
protected override async Task <IEnumerable <ExchangeMarket> > OnGetMarketSymbolsMetadataAsync() { /* * { * "symbol": "ETHBTC", * "status": "TRADING", * "baseAsset": "ETH", * "baseAssetPrecision": 8, * "quoteAsset": "BTC", * "quotePrecision": 8, * "orderTypes": [ * "LIMIT", * "MARKET", * "STOP_LOSS", * "STOP_LOSS_LIMIT", * "TAKE_PROFIT", * "TAKE_PROFIT_LIMIT", * "LIMIT_MAKER" * ], * "icebergAllowed": false, * "filters": [ * { * "filterType": "PRICE_FILTER", * "minPrice": "0.00000100", * "maxPrice": "100000.00000000", * "tickSize": "0.00000100" * }, * { * "filterType": "LOT_SIZE", * "minQty": "0.00100000", * "maxQty": "100000.00000000", * "stepSize": "0.00100000" * }, * { * "filterType": "MIN_NOTIONAL", * "minNotional": "0.00100000" * } * ] * }, */ var markets = new List <ExchangeMarket>(); JToken obj = await MakeJsonRequestAsync <JToken>("/exchangeInfo"); JToken allSymbols = obj["symbols"]; foreach (JToken marketSymbolToken in allSymbols) { var market = new ExchangeMarket { MarketSymbol = marketSymbolToken["symbol"].ToStringUpperInvariant(), IsActive = ParseMarketStatus(marketSymbolToken["status"].ToStringUpperInvariant()), QuoteCurrency = marketSymbolToken["quoteAsset"].ToStringUpperInvariant(), BaseCurrency = marketSymbolToken["baseAsset"].ToStringUpperInvariant() }; // "LOT_SIZE" JToken filters = marketSymbolToken["filters"]; JToken lotSizeFilter = filters?.FirstOrDefault(x => string.Equals(x["filterType"].ToStringUpperInvariant(), "LOT_SIZE")); if (lotSizeFilter != null) { market.MaxTradeSize = lotSizeFilter["maxQty"].ConvertInvariant <decimal>(); market.MinTradeSize = lotSizeFilter["minQty"].ConvertInvariant <decimal>(); market.QuantityStepSize = lotSizeFilter["stepSize"].ConvertInvariant <decimal>(); } // PRICE_FILTER JToken priceFilter = filters?.FirstOrDefault(x => string.Equals(x["filterType"].ToStringUpperInvariant(), "PRICE_FILTER")); if (priceFilter != null) { market.MaxPrice = priceFilter["maxPrice"].ConvertInvariant <decimal>(); market.MinPrice = priceFilter["minPrice"].ConvertInvariant <decimal>(); market.PriceStepSize = priceFilter["tickSize"].ConvertInvariant <decimal>(); } // MIN_NOTIONAL JToken minNotionalFilter = filters?.FirstOrDefault(x => string.Equals(x["filterType"].ToStringUpperInvariant(), "MIN_NOTIONAL")); if (minNotionalFilter != null) { market.MinTradeSizeInQuoteCurrency = minNotionalFilter["minNotional"].ConvertInvariant <decimal>(); } markets.Add(market); } return(markets); }
/// <summary> /// Clamp quantiy using market info /// </summary> /// <param name="symbol">Symbol</param> /// <param name="outputQuantity">Quantity</param> /// <returns>Clamped quantity</returns> protected decimal ClampOrderQuantity(string symbol, decimal outputQuantity) { ExchangeMarket market = GetExchangeMarket(symbol); return(market == null ? outputQuantity : CryptoUtility.ClampDecimal(market.MinTradeSize, market.MaxTradeSize, market.QuantityStepSize, outputQuantity)); }