public void Update(Ticker ticker, IncrementalUpdateInfo info) { for (int i = 0; i < info.BidsUpdates.Count; i++) { string[] item = info.BidsUpdates[i]; ticker.OrderBook.ApplyIncrementalUpdate(OrderBookEntryType.Bid, item[1], item[2]); } for (int i = 0; i < info.AsksUpdates.Count; i++) { string[] item = info.AsksUpdates[i]; ticker.OrderBook.ApplyIncrementalUpdate(OrderBookEntryType.Ask, item[1], item[2]); } for (int i = 0; i < info.TradeUpdates.Count; i++) { string[] item = info.TradeUpdates[i]; TradeInfoItem trade = new TradeInfoItem(null, ticker) { Type = item[1][0] == 'S' ? TradeType.Sell : TradeType.Buy, RateString = item[2], AmountString = item[3], Time = new DateTime(Convert.ToInt64(item[4])).ToLocalTime() }; ticker.AddTradeHistoryItem(trade);//.InsertTradeHistoryItem(trade); CandleStickChartHelper.UpdateVolumes(ticker.CandleStickData, trade, ticker.CandleStickPeriodMin); } ticker.OnApplyIncrementalUpdate(); }
public void Update(Ticker ticker, IncrementalUpdateInfo info) { foreach (string[] item in info.Updates) { if (item[0][0] == 'o') { ticker.OrderBook.ApplyIncrementalUpdate( item[1][0] == '1' ? OrderBookEntryType.Bid : OrderBookEntryType.Ask, item[2], item[3]); } else if (item[0][0] == 't') { TradeInfoItem trade = new TradeInfoItem(null, ticker) { Type = item[2][0] == '0' ? TradeType.Sell : TradeType.Buy, RateString = item[3], AmountString = item[4], Time = new DateTime(Convert.ToInt64(item[5])) }; ticker.TradeHistory.Insert(0, trade); CandleStickChartHelper.UpdateVolumes(ticker.CandleStickData, trade, ticker.CandleStickPeriodMin); } } ticker.OnApplyIncrementalUpdate(); }
public List <TradeInfoItem> GetTradeVolumesForCandleStick(Ticker ticker, long start, long end) { List <TradeInfoItem> trades = new List <TradeInfoItem>(); string address = string.Format("https://api.binance.com/api/v1/aggTrades?symbol={0}&limit={1}&startTime={2}&endTime={3}", Uri.EscapeDataString(ticker.CurrencyPair), 1000, start, end); byte[] data = ((Ticker)ticker).DownloadBytes(address); if (data == null || data.Length == 0) { return(trades); } int parseIndex = 0; List <string[]> items = JSonHelper.Default.DeserializeArrayOfObjects(data, ref parseIndex, AggTradesItem); for (int i = items.Count - 1; i >= 0; i--) { string[] item = items[i]; DateTime time = FromUnixTime(FastValueConverter.ConvertPositiveLong(item[5])); TradeInfoItem t = new TradeInfoItem(null, ticker); bool isBuy = item[6][0] != 't'; t.AmountString = item[2]; t.Time = time; t.Type = isBuy ? TradeType.Buy : TradeType.Sell; trades.Add(t); } return(trades); }
public void Update(Ticker ticker, IncrementalUpdateInfo info) { for (int i = 0; i < info.Updates.Count; i++) { string[] item = info.Updates[i]; if (item[0][0] == 'o') { ticker.OrderBook.ApplyIncrementalUpdate(item[1][0] == '1' ? OrderBookEntryType.Bid : OrderBookEntryType.Ask, item[2], item[3]); } else if (item[0][0] == 't') { TradeInfoItem trade = new TradeInfoItem(null, ticker) { Type = item[2][0] == '0' ? TradeType.Sell : TradeType.Buy, RateString = item[3], AmountString = item[4], Time = ticker.Exchange.FromUnixTimestamp(Convert.ToInt64(item[5])) }; if (trade.Time.Year == 1) { throw new InvalidOperationException(); } ticker.TradeHistory.Insert(0, trade); CandleStickChartHelper.UpdateVolumes(ticker.CandleStickData, trade, ticker.CandleStickPeriodMin); } } ticker.OnApplyIncrementalUpdate(); }
public override bool UpdateTrades(Ticker info) { string address = string.Format("https://bittrex.com/api/v1.1/public/getmarkethistory?market={0}", Uri.EscapeDataString(info.MarketName)); byte[] bytes = null; try { bytes = GetDownloadBytes(address); } catch (Exception) { return(false); } if (bytes == null) { return(false); } int startIndex = 1; if (!JSonHelper.Default.SkipSymbol(bytes, ':', 3, ref startIndex)) { return(false); } long lastId = info.TradeHistory.Count > 0 ? info.TradeHistory.First().Id : -1; string lastIdString = lastId.ToString(); List <string[]> res = JSonHelper.Default.DeserializeArrayOfObjects(bytes, ref startIndex, new string[] { "Id", "TimeStamp", "Quantity", "Price", "Total", "FillType", "OrderType" }, (itemIndex, paramIndex, value) => { return(paramIndex != 0 || lastIdString != value); }); if (res == null || res.Count == 0) { return(true); } int index = 0; lock (info) { foreach (string[] obj in res) { TradeInfoItem item = new TradeInfoItem(null, info); item.Id = Convert.ToInt64(obj[0]); item.Time = Convert.ToDateTime(obj[1]); item.AmountString = obj[2]; item.RateString = obj[3]; item.Total = FastValueConverter.Convert(obj[4]); item.Type = obj[6].Length == 3 ? TradeType.Buy : TradeType.Sell; item.Fill = obj[5].Length == 4 ? TradeFillType.Fill : TradeFillType.PartialFill; info.TradeHistory.Insert(index, item); index++; } } info.RaiseTradeHistoryAdd(); return(true); }
public void ApplySnapshot(Dictionary <string, object> jObject, Ticker ticker) { ticker.OrderBook.Clear(); OrderBook orderBook = ticker.OrderBook; List <object> jbids = (List <object>)jObject["Z"]; List <object> jasks = (List <object>)jObject["S"]; List <OrderBookEntry> entries = orderBook.Asks; List <OrderBookEntry> entriesInverted = orderBook.AsksInverted; foreach (Dictionary <string, object> item in jasks) { entries.Add(new OrderBookEntry() { ValueString = (string)item["R"], AmountString = (string)item["Q"] }); entriesInverted.Insert(0, new OrderBookEntry() { ValueString = (string)item["R"], AmountString = (string)item["Q"] }); } entries = orderBook.Bids; foreach (Dictionary <string, object> item in jbids) { entries.Add(new OrderBookEntry() { ValueString = (string)item["R"], AmountString = (string)item["Q"] }); } orderBook.UpdateEntries(); orderBook.RaiseOnChanged(new IncrementalUpdateInfo()); ticker.TradeHistory.Clear(); List <object> jtrades = (List <object>)jObject["f"]; foreach (Dictionary <string, object> item in jtrades) { TradeInfoItem t = new TradeInfoItem(null, ticker); t.AmountString = (string)item["Q"]; t.RateString = (string)item["P"]; t.Time = new DateTime(Convert.ToInt64((string)item["T"])).ToLocalTime(); t.TimeString = t.Time.ToLongTimeString(); t.Total = t.Rate * t.Amount; t.Type = ((string)item["OT"]) == "BUY"? TradeType.Buy: TradeType.Sell; ticker.TradeHistory.Add(t); } ticker.RaiseTradeHistoryAdd(); }
public override ResizeableArray <TradeInfoItem> GetTrades(Ticker ticker, DateTime starTime) { string address = string.Format("https://api.binance.com/api/v1/depth?symbol={0}&limit={1}", Uri.EscapeDataString(ticker.CurrencyPair), 1000); string text = ((Ticker)ticker).DownloadString(address); if (string.IsNullOrEmpty(text)) { return(null); } JArray trades = JsonConvert.DeserializeObject <JArray>(text); if (trades.Count == 0) { return(null); } ResizeableArray <TradeInfoItem> list = new ResizeableArray <TradeInfoItem>(1000); int index = 0; for (int i = 0; i < trades.Count; i++) { JObject obj = (JObject)trades[i]; DateTime time = new DateTime(obj.Value <Int64>("time")); int tradeId = obj.Value <int>("id"); if (time < starTime) { break; } TradeInfoItem item = new TradeInfoItem(null, ticker); bool isBuy = obj.Value <string>("type").Length == 3; item.AmountString = obj.Value <string>("qty"); 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; list.Add(item); index++; } if (ticker.HasTradeHistorySubscribers) { ticker.RaiseTradeHistoryChanged(new TradeHistoryChangedEventArgs() { NewItems = list }); } return(list); }
void OnTradeHistoryItemRecv(Ticker ticker, string[] str) { TradeInfoItem item = new TradeInfoItem(null, ticker); item.Id = FastValueConverter.ConvertPositiveInteger(str[3]); item.RateString = str[4]; item.AmountString = str[5]; item.Time = FromUnixTime(FastValueConverter.ConvertPositiveLong(str[8])); item.Type = str[9][0] == 't' ? TradeType.Sell : TradeType.Buy; ticker.TradeHistory.Insert(0, item); ticker.RaiseTradeHistoryAdd(); }
public override List <TradeInfoItem> GetTrades(Ticker info, DateTime starTime) { string address = string.Format("https://bittrex.com/api/v1.1/public/getmarkethistory?market={0}", Uri.EscapeDataString(info.MarketName)); byte[] bytes = null; try { bytes = GetDownloadBytes(address); } catch (Exception) { return(null); } if (bytes == null) { return(null); } int startIndex = 1; if (!JSonHelper.Default.SkipSymbol(bytes, ':', 3, ref startIndex)) { return(null); } List <string[]> res = JSonHelper.Default.DeserializeArrayOfObjects(bytes, ref startIndex, new string[] { "Id", "TimeStamp", "Quantity", "Price", "Total", "FillType", "OrderType" }, (itemIndex, paramIndex, value) => { return(paramIndex != 1 || Convert.ToDateTime(value) >= starTime); }); if (res == null) { return(null); } List <TradeInfoItem> list = new List <TradeInfoItem>(); int index = 0; foreach (string[] obj in res) { TradeInfoItem item = new TradeInfoItem(null, info); item.Id = Convert.ToInt64(obj[0]); item.Time = Convert.ToDateTime(obj[1]); item.AmountString = obj[2]; item.RateString = obj[3]; item.Total = FastValueConverter.Convert(obj[4]); item.Type = obj[6].Length == 3 ? TradeType.Buy : TradeType.Sell; item.Fill = obj[5].Length == 4 ? TradeFillType.Fill : TradeFillType.PartialFill; list.Insert(index, item); index++; } return(list); }
protected override TradeInfoItem InitializeAccountTradeInfoItem(string[] item, Ticker ticker) { DateTime time = FromUnixTime(FastValueConverter.ConvertPositiveLong(item[13])); TradeInfoItem t = new TradeInfoItem(null, ticker); t.OrderNumber = item[5]; t.AmountString = item[7]; t.Time = time; t.Type = String2TradeType(item[10]); t.RateString = item[6]; t.IdString = item[3]; return(t); }
protected override TradeInfoItem InitializeTradeInfoItem(string[] item, Ticker ticker) { DateTime time = FromUnixTime(FastValueConverter.ConvertPositiveLong(item[4])); TradeInfoItem t = new TradeInfoItem(null, ticker); bool isBuy = item[4][0] != 't'; t.AmountString = item[2]; t.Time = time; t.Type = isBuy ? TradeType.Buy : TradeType.Sell; t.RateString = item[1]; t.IdString = item[0]; return(t); }
bool OnGetAccountTrades(AccountInfo account, Ticker ticker, byte[] bytes) { if(bytes == null) return false; int startIndex = 1; if(!JSonHelper.Default.SkipSymbol(bytes, ':', 3, ref startIndex)) return false; string tradeUuid = ticker.MyTradeHistory.Count == 0 ? null : ticker.MyTradeHistory.First().IdString; List<string[]> res = JSonHelper.Default.DeserializeArrayOfObjects(bytes, ref startIndex, new string[] { "OrderUuid", // 0 "Exchange", // 1 "TimeStamp", // 2 "OrderType", // 3 "Limit", // 4 "Quantity", // 5 "QuantityRemaining", // 6 "Commission", // 7 "Price", // 8 "PricePerUnit", // 9 "IsConditional", "Condition", "ConditionTarget", "ImmediateOrCancel", }, (itemIndex, paramIndex, value) => { return paramIndex != 0 || value != tradeUuid; }); if(res == null) return false; if(res.Count == 0) return true; int index = 0; for(int i = 0; i < res.Count; i++) { string[] obj = res[i]; TradeInfoItem item = new TradeInfoItem(account, ticker); item.IdString = obj[0]; item.Type = obj[3] == "LIMIT_BUY" ? TradeType.Buy : TradeType.Sell; item.AmountString = obj[5]; item.RateString = obj[9]; item.Fee = FastValueConverter.Convert(obj[7]); item.Total = FastValueConverter.Convert(obj[8]); item.TimeString = obj[2]; ticker.MyTradeHistory.Insert(index, item); index++; } return true; }
public bool GetTrades(BitFinexTicker info) { string address = string.Format("https://bittrex.com/api/v1.1/public/getmarkethistory?market={0}", Uri.EscapeDataString(info.MarketName)); byte[] bytes = null; try { bytes = GetDownloadBytes(address); } catch (Exception) { return(false); } if (bytes == null) { return(false); } int startIndex = 1; if (!JSonHelper.Default.SkipSymbol(bytes, ':', 3, ref startIndex)) { return(false); } List <string[]> res = JSonHelper.Default.DeserializeArrayOfObjects(bytes, ref startIndex, new string[] { "Id", "TimeStamp", "Quantity", "Price", "Total", "FillType", "OrderType" }); if (res == null) { return(false); } lock (info) { info.TradeHistory.Clear(); foreach (string[] obj in res) { TradeInfoItem item = new TradeInfoItem(null, info); item.Id = Convert.ToInt64(obj[0]);; item.Time = Convert.ToDateTime(obj[1]); item.AmountString = obj[2]; item.RateString = obj[3]; item.Total = FastValueConverter.Convert(obj[4]); item.Type = obj[6].Length == 3 ? TradeType.Buy : TradeType.Sell; item.Fill = obj[5].Length == 4 ? TradeFillType.Fill : TradeFillType.PartialFill; info.TradeHistory.Add(item); } } info.RaiseTradeHistoryAdd(); return(true); }
protected internal override void OnTradeHistorySocketMessageReceived(object sender, MessageReceivedEventArgs e) { LastWebSocketRecvTime = DateTime.Now; SocketConnectionInfo info = TradeHistorySockets.FirstOrDefault(c => c.Key == sender); if (info == null) { return; } Ticker t = info.Ticker; if (t.IsUpdatingTrades) { return; } if (t.CaptureData) { t.CaptureDataCore(CaptureStreamType.TradeHistory, CaptureMessageType.Incremental, e.Message); } JObject obj = JsonConvert.DeserializeObject <JObject>(e.Message); JArray items = obj.Value <JArray>("data"); if (items == null) { return; } foreach (JObject item in items) { TradeInfoItem ti = new TradeInfoItem(null, t); ti.TimeString = item.Value <string>("timestamp"); ti.Type = String2TradeType(item.Value <string>("side")); ti.AmountString = item.Value <string>("size"); ti.RateString = item.Value <string>("price"); t.AddTradeHistoryItem(ti); //.InsertTradeHistoryItem(ti); } if (t.HasTradeHistorySubscribers) { TradeHistoryChangedEventArgs ee = new TradeHistoryChangedEventArgs() { NewItem = t.TradeHistory.First() }; t.RaiseTradeHistoryChanged(ee); } }
public override bool UpdateTrades(Ticker ticker) { string address = string.Format("https://api.binance.com/api/v1/trades?symbol={0}&limit={1}", Uri.EscapeDataString(ticker.CurrencyPair), 1000); byte[] data = ((Ticker)ticker).DownloadBytes(address); if (data == null || data.Length == 0) { return(false); } ticker.TradeHistory.Clear(); ticker.TradeStatistic.Clear(); int index = 0, parseIndex = 0; List <string[]> items = JSonHelper.Default.DeserializeArrayOfObjects(data, ref parseIndex, TradeItemString); ResizeableArray <TradeInfoItem> newItems = new ResizeableArray <TradeInfoItem>(items.Count); for (int i = items.Count - 1; i >= 0; i--) { string[] item = items[i]; DateTime time = FromUnixTime(FastValueConverter.ConvertPositiveLong(item[3])); int tradeId = FastValueConverter.ConvertPositiveInteger(item[0]); TradeInfoItem t = new TradeInfoItem(null, ticker); bool isBuy = item[4][0] != 't'; t.AmountString = item[2]; t.Time = time; t.Type = isBuy ? TradeType.Buy : TradeType.Sell; t.RateString = item[1]; t.Id = tradeId; double price = t.Rate; double amount = t.Amount; t.Total = price * amount; ticker.TradeHistory.Add(t); newItems.Add(t); index++; } if (ticker.HasTradeHistorySubscribers) { ticker.RaiseTradeHistoryChanged(new TradeHistoryChangedEventArgs() { NewItems = newItems }); } return(true); }
protected override bool GetTradesCore(ResizeableArray <TradeInfoItem> list, Ticker ticker, DateTime startTime, DateTime endTime) { string address = string.Format("https://www.bitmex.com/api/v1/trade?symbol={0}&count=1000&startTime={1}&endTime={2}", ticker.Name, DateToString(startTime), DateToString(endTime)); string text = string.Empty; try { text = GetDownloadString(address); if (string.IsNullOrEmpty(text)) { return(false); } if (text[0] == '{') { JObject obj = JsonConvert.DeserializeObject <JObject>(text); LogManager.Default.Add(LogType.Error, this, Type.ToString(), "error in GetTradesCore", obj.Value <string>("message")); return(false); } JArray res = JsonConvert.DeserializeObject <JArray>(text); foreach (JObject obj in res.Children()) { TradeInfoItem item = new TradeInfoItem(); item.Ticker = ticker; item.RateString = obj.Value <string>("price"); item.AmountString = obj.Value <string>("size"); item.Type = obj.Value <string>("side")[0] == 'S' ? TradeType.Sell : TradeType.Buy; item.TimeString = obj.Value <string>("timestamp"); DateTime time = item.Time; if (list.Last() == null || list.Last().Time <= time) { list.Add(item); } else { break; } } } catch (Exception) { return(false); } return(true); }
void OnTradeHistoryItemRecv(Ticker ticker, string[] str) { TradeInfoItem item = new TradeInfoItem(null, ticker); item.Id = FastValueConverter.ConvertPositiveInteger(str[3]); item.RateString = str[4]; item.AmountString = str[5]; item.Time = FromUnixTime(FastValueConverter.ConvertPositiveLong(str[8])); item.Type = str[9][0] == 't' ? TradeType.Sell : TradeType.Buy; ticker.TradeHistory.Insert(0, item); if (ticker.HasTradeHistorySubscribers) { TradeHistoryChangedEventArgs e = new TradeHistoryChangedEventArgs() { NewItem = item }; ticker.RaiseTradeHistoryChanged(e); } }
protected override bool GetTradesCore(ResizeableArray <TradeInfoItem> list, Ticker ticker, DateTime start, DateTime end) { string address = string.Format("https://api.binance.com/api/v1/aggTrades?symbol={0}&limit={1}&startTime={2}&endTime={3}", Uri.EscapeDataString(ticker.CurrencyPair), 1000, ToUnixTimestampMs(start), ToUnixTimestampMs(end)); byte[] data = ((Ticker)ticker).DownloadBytes(address); if (data == null || data.Length == 0) { return(false); } int parseIndex = 0; List <string[]> items = JSonHelper.Default.DeserializeArrayOfObjects(data, ref parseIndex, AggTradeItemString); for (int i = 0; i < items.Count; i++) { string[] item = items[i]; DateTime time = FromUnixTimestampMs(FastValueConverter.ConvertPositiveLong(item[5])); //if(time > end) // break; int tradeId = FastValueConverter.ConvertPositiveInteger(item[0]); TradeInfoItem t = new TradeInfoItem(null, ticker); bool isBuy = item[6][0] != 't'; t.AmountString = item[2]; t.Time = time; t.Type = isBuy ? TradeType.Buy : TradeType.Sell; t.RateString = item[1]; t.Id = tradeId; double price = t.Rate; double amount = t.Amount; t.Total = price * amount; if (list.Last() == null || list.Last().Time < time) { list.Add(t); } } return(true); }
ResizeableArray <TickerTradeHistoryInfoItem> CalcItems() { if (TradeHistory.Count == 0) { return(new ResizeableArray <TickerTradeHistoryInfoItem>()); } DateTime min = TradeHistory[0].Time; DateTime max = TradeHistory.Last().Time; if (min > max) { DateTime tmp = min; min = max; max = tmp; } int interval = HistogrammIntervalSec * 1000; int count = (int)((max - min).TotalMilliseconds / interval + 0.5); ResizeableArray <TickerTradeHistoryInfoItem> list = new ResizeableArray <TickerTradeHistoryInfoItem>(count + 1); double k = 1.0 / interval; for (int i = 0; i < count + 1; i++) { list[i] = new TickerTradeHistoryInfoItem() { Time = min.AddMilliseconds(i * interval) }; list[i].Low = double.MaxValue; list[i].OpenTime = list[i].Time.AddMilliseconds(interval); list[i].CloseTime = list[i].Time; } for (int i = 0; i < TradeHistory.Count; i++) { DateTime time = TradeHistory[i].Time; int index = (int)((time - min).TotalMilliseconds * k); TickerTradeHistoryInfoItem item = list[index]; TradeInfoItem trade = TradeHistory[i]; item.TradeCount++; item.TradeVolume += trade.Amount; if (TradeHistory[i].Type == TradeType.Sell) { item.SellCount--; item.SellVolume -= trade.Amount; } else { item.BuyCount++; item.BuyVolume += trade.Amount; } if (item.Low > trade.Rate) { item.Low = trade.Rate; } if (item.High < trade.Rate) { item.High = trade.Rate; } if (item.OpenTime >= trade.Time) { item.OpenTime = trade.Time; item.Open = trade.Rate; } if (item.CloseTime <= trade.Time) { item.CloseTime = trade.Time; item.Close = trade.Rate; } } TickerTradeHistoryInfoItem last = null; for (int i = 0; i < count + 1; i++) { if (list[i].TradeCount == 0) { if (last != null) { list[i].Open = list[i].Close = list[i].High = list[i].Low = last.Close; } else { list[i].Open = list[i].Close = list[i].High = list[i].Low = 0; } } else { last = list[i]; } } for (int i = 0; i < list.Count; i++) { CalculateTrendInfo(list, i); } return(list); }
public void ApplySnapshot(JObject jObject, Ticker ticker) { ticker.OrderBook.BeginUpdate(); try { ticker.OrderBook.Clear(); OrderBook orderBook = ticker.OrderBook; JArray jbids = jObject.Value <JArray>("Z"); JArray jasks = jObject.Value <JArray>("S"); List <OrderBookEntry> entries = orderBook.Asks; List <OrderBookEntry> entriesInverted = orderBook.AsksInverted; for (int i = 0; i < jasks.Count; i++) { JObject item = (JObject)jasks[i]; entries.Add(new OrderBookEntry() { ValueString = item.Value <string>("R"), AmountString = item.Value <string>("Q") }); if (entriesInverted != null) { entriesInverted.Insert(0, new OrderBookEntry() { ValueString = item.Value <string>("R"), AmountString = item.Value <string>("Q") }); } } entries = orderBook.Bids; for (int i = 0; i < jbids.Count; i++) { JObject item = (JObject)jbids[i]; entries.Add(new OrderBookEntry() { ValueString = item.Value <string>("R"), AmountString = item.Value <string>("Q") }); } } finally { ticker.OrderBook.IsDirty = false; ticker.OrderBook.EndUpdate(); } ticker.ClearTradeHistory(); JArray jtrades = jObject.Value <JArray>("f"); foreach (JObject item in jtrades) { TradeInfoItem t = new TradeInfoItem(null, ticker); t.AmountString = item.Value <string>("Q"); t.RateString = item.Value <string>("P"); t.Time = new DateTime(Convert.ToInt64(item.Value <string>("T"))).ToLocalTime(); t.TimeString = t.Time.ToLongTimeString(); t.Type = (item.Value <string>("OT")) == "BUY" ? TradeType.Buy : TradeType.Sell; ticker.AddTradeHistoryItem(t); } if (ticker.HasTradeHistorySubscribers) { ticker.RaiseTradeHistoryChanged(new TradeHistoryChangedEventArgs() { NewItems = ticker.TradeHistory }); } }