public void UpdateTrades(BittrexMarketInfo info) { Timer.Reset(); Timer.Stop(); string address = string.Format("https://bittrex.com/api/v1.1/public/getmarkethistory?market={0}", Uri.EscapeDataString(info.MarketName)); string text = GetDownloadString(info, address); if (string.IsNullOrEmpty(text)) { return; } JObject res = (JObject)JsonConvert.DeserializeObject(text); foreach (JProperty prop in res.Children()) { if (prop.Name == "success") { if (prop.Value.Value <bool>() == false) { break; } } if (prop.Name == "message") { continue; } if (prop.Name == "result") { lock (info) { JArray trades = (JArray)prop.Value; UpdateList.Clear(); int lastId = info.TradeHistory.Count > 0 ? info.TradeHistory.First().Id : -1; foreach (JObject obj in trades) { int id = obj.Value <int>("Id"); if (id == lastId) { info.TradeHistory.InsertRange(0, UpdateList); break; } TradeHistoryItem item = new TradeHistoryItem(); item.Id = id; item.Time = obj.Value <DateTime>("TimeStamp"); item.Amount = obj.Value <double>("Quantity"); item.Rate = obj.Value <double>("Price"); item.Total = obj.Value <double>("Total"); item.Type = obj.Value <string>("OrderType") == "BUY" ? TradeType.Buy : TradeType.Sell; item.Fill = obj.Value <string>("FillType") == "FILL" ? TradeFillType.Fill : TradeFillType.PartialFill; TickerUpdateHelper.UpdateHistoryForTradeItem(item, info); UpdateList.Add(item); } } Timer.Stop(); Console.WriteLine(info.Time.ToString("hh:mm:ss.fff") + " trade update. process time = " + Timer.ElapsedMilliseconds); info.RaiseTradeHistoryAdd(); } } }
public FullHistoryTradeOffer(TradeHistoryItem historyItem, List <AssetDescription> assetDescriptions) { TradeId = historyItem.TradeId; SteamIdOther = new SteamID(ulong.Parse(historyItem.SteamIdOther)); TimeInit = CommonUtils.ParseSteamUnixDate(int.Parse(historyItem.TimeInit)); TimeEscrowEnd = historyItem.TimeEscrowEnd; Status = historyItem.Status; MyItems = GetFullHistoryTradeItemsList(historyItem.AssetsGiven, historyItem.CurrencyGiven, assetDescriptions); HisItems = GetFullHistoryTradeItemsList(historyItem.AssetsReceived, historyItem.CurrencyReceived, assetDescriptions); }
public static ICandle CreateCandle(this TradeHistoryItem trade, string assetPairId, CandlePriceType priceType, CandleTimeInterval interval, decimal volumeMultiplier = 1.0M) { return(Candle.Create( assetPairId, priceType, interval, trade.DateTime.TruncateTo(interval), (double)trade.Price, (double)trade.Price, (double)trade.Price, (double)trade.Price, Convert.ToDouble((trade.IsStraight ? trade.Volume : trade.OppositeVolume) * volumeMultiplier), Convert.ToDouble((trade.IsStraight ? trade.OppositeVolume : trade.Volume) * volumeMultiplier), 0, // Last Trade Price is enforced to be = 0 trade.DateTime )); }
private async Task ProcessTrade(TradeHistoryItem trade, decimal volumeMultiplier) { var dataWritingTasks = new List <Task>(); foreach (var interval in Candles.Constants.DbStoredIntervals) { if (_activeCandles.TryGetValue(interval, out var activeCandle)) { if (trade.BelongsTo(activeCandle)) { _activeCandles[interval] = activeCandle.ExtendBy(trade, volumeMultiplier); } else { _persistenceCandleQueue[interval].Add(activeCandle); _activeCandles[interval] = trade.CreateCandle(_assetPairId, CandlePriceType.Trades, interval, volumeMultiplier); } } else { _activeCandles[interval] = trade.CreateCandle(_assetPairId, CandlePriceType.Trades, interval, volumeMultiplier); continue; } if (_persistenceCandleQueue[interval].Count < _persistenceQueueMaxSize) { continue; } dataWritingTasks.Add(_historyRepo.ReplaceCandlesAsync(_persistenceCandleQueue[interval])); _healthService[_assetPairId].SummarySavedCandles += _persistenceCandleQueue[interval].Count; _persistenceCandleQueue[interval] = new List <ICandle>(_persistenceQueueMaxSize); } await Task.WhenAll(dataWritingTasks); _healthService.Health.PersistenceQueueSize = PersistenceQueueSize; }
public async Task <IReadOnlyCollection <TradeHistoryItem> > GetNextBatchAsync() { // If we got the last batch in the previous iteration, there is no reason to execute one more query // with empty result. Just return. if (_gotTheLastBatch) { return(Array.Empty <TradeHistoryItem>()); } try { var result = new List <TradeHistoryItem>(); // Renew the connection on every call. using (var sqlConnection = new SqlConnection(_sqlConnString)) { sqlConnection.Open(); if (sqlConnection.State != ConnectionState.Open) { throw new InvalidOperationException("Can't fetch from DB while connection is not opened."); } _log.Info(nameof(GetNextBatchAsync), $"Trying to fetch next {_sqlQueryBatchSize} rows...", $"Starting offset = {StartingRowOffset}, asset pair ID = {AssetPairId}"); using (var sqlCommand = BuildCurrentQueryCommand(sqlConnection)) { sqlCommand.CommandTimeout = (int)_sqlTimeout.TotalSeconds; using (var reader = await sqlCommand.ExecuteReaderAsync()) { while (await reader.ReadAsync()) { var trade = new TradeHistoryItem { Id = reader.GetInt64(0), AssetToken = reader.GetString(1), Direction = (TradeDirection)Enum.Parse(typeof(TradeDirection), reader.GetString(2)), Volume = reader.GetDecimal(3), Price = reader.GetDecimal(4), DateTime = reader.GetDateTime(5), OppositeVolume = reader.GetDecimal(6), OrderId = Guid.Parse(reader.GetString(7)), OppositeOrderId = Guid.Parse(reader.GetString(8)), TradeId = reader.GetString(9), IsStraight = reader.GetString(1) == SearchToken // If the trade is straight or reverse. }; // We must ignore trades with negative prices and\or volumes (if any). if (trade.Price > 0 && trade.Volume > 0 && trade.OppositeVolume > 0) { result.Add(trade); } else { _healthNotifier.Notify("Got a trade with non-posotive price or volume(s) values.", trade); } } } } sqlConnection.Close(); } if (result.Count > 0) { // Now we need to remove the last several trades which have the same date and time (accuracy - to seconds). // This will guarantee that we did not peek up some orders of the same trade on this iteration, and others // on the next. On the next iteration we will read them again for the next batch. No temporary buffer, for // it can't save any observable value of time. NOTE: if we have got less records than _sqlQueryBatchSize, // this means that we obtained the last (or the single) data pack, and there is no reason to delete "tail" // trades. if (result.Count == _sqlQueryBatchSize) { var lastDateTime = result.Last().DateTime.TruncateTo(CandleTimeInterval.Sec); var resultWithoutTail = result.TakeWhile(t => t.DateTime < lastDateTime).ToList(); if (!resultWithoutTail.Any()) { throw new InvalidOperationException($"Got an SQL data batch of {result.Count} trade records with the same timestamp {lastDateTime:O}. " + $"Migration for asset pair {AssetPairId} will be terminated. Row offset was {StartingRowOffset} before the incident."); } result = resultWithoutTail; } else { _gotTheLastBatch = true; // If we have got smaller amount of records than _sqlQueryBatchSize, this only means we have the last batch now. } _log.Info(nameof(GetNextBatchAsync), $"Fetched {result.Count} rows successfully. First date is {result.First().DateTime:O}, last date is {result.Last().DateTime:O}", $"Starting offset = {StartingRowOffset}, asset pair ID = {AssetPairId}"); StartingRowOffset += result.Count; } else { _log.Info(nameof(GetNextBatchAsync), "No data to fetch.", $"Starting offset = {StartingRowOffset}, asset pair ID = {AssetPairId}"); } return(result); } catch (Exception ex) { _log.Error(nameof(GetNextBatchAsync), ex); // We can just report about the error and return an empty list - this will be interpreted as "no data". return(Array.Empty <TradeHistoryItem>()); } }
public bool UpdateTradesStatistic(ExmoTicker ticker, int count) { string text = string.Empty; string address = string.Format("https://api.exmo.me/v1/trades/?pair={0}", ticker.MarketName); try { text = GetDownloadString(address); if (text == null) { return(false); } } catch (Exception) { return(false); } Dictionary <string, object> res = null; lock (JsonParser) { res = (Dictionary <string, object>)JsonParser.Parse(text); } List <object> trades = (List <object>)res[ticker.MarketName]; if (trades.Count == 0) { ticker.TradeStatistic.Add(new TradeStatisticsItem() { Time = DateTime.Now }); if (ticker.TradeStatistic.Count > 5000) { for (int i = 0; i < 100; i++) { ticker.TradeStatistic.RemoveAt(0); } } return(true); } TradeStatisticsItem st = new TradeStatisticsItem(); st.MinBuyPrice = decimal.MaxValue; st.MinSellPrice = decimal.MaxValue; st.Time = DateTime.Now; foreach (Dictionary <string, object> obj in trades) { TradeHistoryItem item = new TradeHistoryItem(); decimal amount = Convert.ToDecimal(obj["amount"]); decimal price = Convert.ToDecimal(obj["price"]); bool isBuy = obj["type"].ToString().Length == 3; if (isBuy) { st.BuyAmount += amount; st.MinBuyPrice = Math.Min(st.MinBuyPrice, price); st.MaxBuyPrice = Math.Max(st.MaxBuyPrice, price); st.BuyVolume += amount * price; } else { st.SellAmount += amount; st.MinSellPrice = Math.Min(st.MinSellPrice, price); st.MaxSellPrice = Math.Max(st.MaxSellPrice, price); st.SellVolume += amount * price; } } if (st.MinSellPrice == decimal.MaxValue) { st.MinSellPrice = 0; } if (st.MinBuyPrice == decimal.MaxValue) { st.MinBuyPrice = 0; } ticker.LastTradeId = Convert.ToInt64(((Dictionary <string, object>)trades.First())["trade_id"]); ticker.LastTradeStatisticTime = DateTime.Now; ticker.TradeStatistic.Add(st); if (ticker.TradeStatistic.Count > 5000) { for (int i = 0; i < 100; i++) { ticker.TradeStatistic.RemoveAt(0); } } return(true); }
public bool UpdateTradesStatistic(HitBtcTicker ticker, int count) { string text = string.Empty; string address = string.Format("http://api.hitbtc.com//api/1/public/{0}/trades?from={1}&by=trade_id&sort=desc&start_index=0&max_results={2}&side=true", ticker.MarketName, ticker.LastTradeId, count); try { text = GetDownloadString(address); if (text == null) { return(false); } } catch (Exception) { return(false); } Dictionary <string, object> res = null; lock (JsonParser) { res = (Dictionary <string, object>)JsonParser.Parse(text); } List <object> trades = (List <object>)res["trades"]; if (trades.Count == 0) { ticker.TradeStatistic.Add(new TradeStatisticsItem() { Time = DateTime.Now }); if (ticker.TradeStatistic.Count > 5000) { for (int i = 0; i < 100; i++) { ticker.TradeStatistic.RemoveAt(0); } } return(true); } TradeStatisticsItem st = new TradeStatisticsItem(); st.MinBuyPrice = decimal.MaxValue; st.MinSellPrice = decimal.MaxValue; st.Time = DateTime.Now; foreach (List <object> obj in trades) { TradeHistoryItem item = new TradeHistoryItem(); decimal amount = Convert.ToDecimal(obj[1]); decimal price = Convert.ToDecimal(obj[2]); bool isBuy = obj[4].ToString().Length == 3; if (isBuy) { st.BuyAmount += amount; st.MinBuyPrice = Math.Min(st.MinBuyPrice, price); st.MaxBuyPrice = Math.Max(st.MaxBuyPrice, price); st.BuyVolume += amount * price; } else { st.SellAmount += amount; st.MinSellPrice = Math.Min(st.MinSellPrice, price); st.MaxSellPrice = Math.Max(st.MaxSellPrice, price); st.SellVolume += amount * price; } } if (st.MinSellPrice == decimal.MaxValue) { st.MinSellPrice = 0; } if (st.MinBuyPrice == decimal.MaxValue) { st.MinBuyPrice = 0; } ticker.LastTradeId = Convert.ToInt64(((List <object>)trades.First())[0]); ticker.LastTradeStatisticTime = DateTime.Now; ticker.TradeStatistic.Add(st); if (ticker.TradeStatistic.Count > 5000) { for (int i = 0; i < 100; i++) { ticker.TradeStatistic.RemoveAt(0); } } return(true); }
/// <summary> /// Detects if the given trade item lays in time borders of the given candle. /// </summary> public static bool BelongsTo(this TradeHistoryItem trade, ICandle candle) { return(trade.DateTime.TruncateTo(candle.TimeInterval) == candle.Timestamp); }
/// <summary> /// Extends a candle by a trade, if trade's DateTime corresponds to candle's TimeStamp (i.e., the trade belongs to the same time period). /// </summary> public static ICandle ExtendBy(this ICandle self, TradeHistoryItem trade, decimal volumeMultiplier = 1.0M) { var tradeCandle = trade.CreateCandle(self.AssetPairId, self.PriceType, self.TimeInterval, volumeMultiplier); return(self.ExtendBy(tradeCandle)); }
public override bool UpdateTrades(TickerBase ticker) { string address = string.Format("https://yobit.net/api/3/trades/{0}", Uri.EscapeDataString(ticker.CurrencyPair)); string text = ((TickerBase)ticker).DownloadString(address); if (string.IsNullOrEmpty(text)) { return(false); } JObject obj2 = (JObject)JsonConvert.DeserializeObject(text); JArray trades = (JArray)obj2.Value <JArray>(ticker.CurrencyPair); if (trades.Count == 0) { return(true); } int lastTradeId = trades.First().Value <int>("tid"); long lastGotTradeId = ticker.TradeHistory.Count > 0 ? ticker.TradeHistory.First().Id : 0; if (lastGotTradeId == lastTradeId) { ticker.TradeStatistic.Add(new TradeStatisticsItem() { Time = DateTime.UtcNow }); if (ticker.TradeStatistic.Count > 5000) { for (int i = 0; i < 100; i++) { ticker.TradeStatistic.RemoveAt(0); } } return(true); } TradeStatisticsItem st = new TradeStatisticsItem(); st.MinBuyPrice = double.MaxValue; st.MinSellPrice = double.MaxValue; st.Time = DateTime.UtcNow; int index = 0; foreach (JObject obj in trades) { DateTime time = new DateTime(1970, 1, 1).AddSeconds(obj.Value <long>("timestamp")); int tradeId = obj.Value <int>("tid"); if (lastGotTradeId == tradeId) { break; } TradeHistoryItem item = new TradeHistoryItem(); bool isBuy = obj.Value <string>("type") == "bid"; item.AmountString = obj.Value <string>("amount"); item.Time = time; item.Type = isBuy ? TradeType.Buy : TradeType.Sell; item.RateString = obj.Value <string>("price"); item.Id = tradeId; double price = item.Rate; double amount = item.Amount; item.Total = price * amount; if (isBuy) { st.BuyAmount += amount; st.MinBuyPrice = Math.Min(st.MinBuyPrice, price); st.MaxBuyPrice = Math.Max(st.MaxBuyPrice, price); st.BuyVolume += amount * price; } else { st.SellAmount += amount; st.MinSellPrice = Math.Min(st.MinSellPrice, price); st.MaxSellPrice = Math.Max(st.MaxSellPrice, price); st.SellVolume += amount * price; } ticker.TradeHistory.Insert(index, item); index++; } if (st.MinSellPrice == double.MaxValue) { st.MinSellPrice = 0; } if (st.MinBuyPrice == double.MaxValue) { st.MinBuyPrice = 0; } ticker.LastTradeStatisticTime = DateTime.UtcNow; ticker.TradeStatistic.Add(st); if (ticker.TradeStatistic.Count > 5000) { for (int i = 0; i < 100; i++) { ticker.TradeStatistic.RemoveAt(0); } } return(true); }