public void AddQuoteBasic(IQuoteBasicBase qb, bool isAddToAll, bool isTriggerEvent) { lock (this) { if (qb != null && qb.Count > 0 && Intervals.Contains(qb.Interval)) { if (isAddToAll) { var index = this.Intervals.FindIndex(i => i == qb.Interval); var interval = this.Intervals[index]; var quoteId = this.CreateQuote(qb.Symbol, interval); var num = Quotes[quoteId].Append(qb, isTriggerEvent); //trigger OnQuoteBasicDataAddedOrUpdated //var num = Quotes[quoteId].Append(qb, false); //do not trigger OnQuoteBasicDataAddedOrUpdated until we add to all quotes if (num >= 0) { var d = this.AddToOtherQuotes(Quotes[quoteId], index + 1, isTriggerEvent); //QuoteStore_OnDataAddedOrUpdated(this, this.Quotes[quoteId], num); //foreach (var qId in d.Keys) QuoteStore_OnDataAddedOrUpdated(this, this.Quotes[qId], d[qId]); } } else { var quoteId = this.CreateQuote(qb.Symbol, qb.Interval); var num = Quotes[quoteId].Append(qb, isTriggerEvent); } } } }
public static void SaveToFile(this IQuoteBasicBase quote, string filename) { var writestream = new FileStream(filename, FileMode.Create); quote.AppendStream(writestream); writestream.Close(); }
void QuoteStore_OnDataAddedOrUpdated(object sender, IQuoteBasicBase qb, int numAppended) { OnQuoteBasicDataAddedOrUpdated?.Invoke(sender, this.Exchange, qb, numAppended); if (qb.Count > MaxNumCandles * 2) { qb.Clear(0, qb.Count - MaxNumCandles - 1); } }
public static IQuoteBasicBase LoadFile(this IQuoteBasicBase quote, string filename) { var readtream = new FileStream(filename, FileMode.Open); quote = QuoteBasicBase.InitByStream(readtream); readtream.Close(); return(quote); }
/// <summary> /// 复制IQuoteBasic的内容 /// </summary> /// <param name="q"></param> public QuoteBasicBase(IQuoteBasicBase q) { Symbol = q.Symbol; Interval = q.Interval; Time = new List <long>(q.Time); Open = new List <double>(q.Open); Close = new List <double>(q.Close); High = new List <double>(q.High); Low = new List <double>(q.Low); Volume = new List <double>(q.Volume); }
private Dictionary <string, int> AddToOtherQuotes(IQuoteBasicBase prevQuote, int sindex, bool isTriggerEvent) { var addedNumber = new Dictionary <string, int>(); var originQuoteId = prevQuote.QuoteID; var interval = prevQuote.Interval; for (int i = sindex; i < this.Intervals.Count; i++) { var interv = this.Intervals[i]; if (interv < interval || interv % interval != 0) { continue; } var quoteId = this.CreateQuote(prevQuote.Symbol, interv); var num = Quotes[quoteId].Append(prevQuote, isTriggerEvent); if (num == -1) { break; } addedNumber.Add(quoteId, num); prevQuote = Quotes[quoteId]; interval = interv; } var q = this.Quotes[originQuoteId]; interval = q.Interval; for (int i = sindex; i < this.Intervals.Count; i++) { var interv = this.Intervals[i]; if (interv <= interval || interv % interval != 0) { continue; } var quoteId = this.CreateQuote(q.Symbol, interv); var num = Quotes[quoteId].Append(q, isTriggerEvent); if (num >= 0) { if (!addedNumber.ContainsKey(quoteId)) { addedNumber.Add(quoteId, 0); } addedNumber[quoteId] += num; } } return(addedNumber); }
private int AddItemByQuoteBasic(IQuoteBasicBase q, int sindex, int eindex) { var len = eindex - sindex + 1; var open = q.Open[sindex]; var close = q.Close[eindex]; var high = q.High.GetRange(sindex, len).Max(); var low = q.Low.GetRange(sindex, len).Min(); var volume = q.Volume.GetRange(sindex, len).Sum(); var time = q.Time[sindex] / this.Interval * this.Interval; //repeat quote to fill gap //FillGap(isFillGap, endTime, ref numAddedElement); return(this.AddUpdate(time, open, high, low, close, volume)); //add the data }
//file naming convention: exchange_symbol_interval_index.txt public bool Save(IQuoteBasicBase quote, int numBarsToRemoveGap = -1) { lock (this) { if (quote == null || quote.Count == 0) { return(false); } var files = this.FindQuoteFiles(quote.Symbol, quote.Interval); if (files != null && files.Count > 0) { files.Sort(); var q1 = QuoteBasicExension.LoadFile(files.Last()); if (numBarsToRemoveGap > 0) { for (int i = Math.Max(0, q1.Count - numBarsToRemoveGap); i < q1.Count - 1; i++) { if (q1.Time[i + 1] - q1.Time[i] > q1.Interval) { q1.Clear(i + 1, q1.Count - 1); break; } } } //var readtream = new FileStream(files.Last(), FileMode.Open); //var q1 = QuoteBasicBase.InitByStream(readtream); //readtream.Close(); if (quote.LastTime <= q1.LastTime) { return(false); } if (q1.Count < _maxNumBarsInFile) { var fn = files.Last(); q1.Append(quote); q1.SaveToFile(fn); //var writestream = new FileStream(files.Last(), FileMode.Truncate); //q1.AppendStream(writestream); //writestream.Close(); OnQuoteSaved?.Invoke(this, this.Exchange, fn); } else { var last = files.Last().Split('_').Last(); var index = int.Parse(last.Split('.')[0]); var fn = this.GetQuoteFileName(quote.Symbol, quote.Interval, index + 1); //var writestream = new FileStream(fn, FileMode.OpenOrCreate); if (q1.LastTime >= quote.FirstTime) //remove redundant sticks from new files { var sind = quote.FindIndexForGivenTime(q1.LastTime); var q2 = new QuoteBasicBase(quote); q2.Clear(0, sind); q2.SaveToFile(fn); //q2.AppendStream(writestream); } else { quote.SaveToFile(fn); //quote.AppendStream(writestream); } //writestream.Close(); OnQuoteSaved?.Invoke(this, this.Exchange, fn); } } else { var fn = this.GetQuoteFileName(quote.Symbol, quote.Interval, 0); quote.SaveToFile(fn); //var writestream = new FileStream(fn, FileMode.OpenOrCreate); //quote.AppendStream(writestream); //writestream.Close(); OnQuoteSaved?.Invoke(this, this.Exchange, fn); } return(true); } }
private void _qbStore_OnQuoteBasicDataAddedOrUpdated(object sender, string exchange, IQuoteBasicBase quote, int numAppended) { OnQuoteBasicDataAddedOrUpated?.Invoke(sender, this.Exchange, quote, numAppended); //var j = quote.Count - numAppended; //var lst = new List<OHLC>(); //for (int i = j; i < quote.Count; i++) //{ // var ohlc = new OHLC // { // Symbol = quote.Symbol, // Interval = quote.Interval, // Time = quote.Time[i], // Open = quote.Open[i], // Close = quote.Close[i], // High = quote.High[i], // Low = quote.Low[i], // Volume = quote.Volume[i] // }; // lst.Add(ohlc); //} //OnCandleListReceived?.Invoke(this, this.Exchange, lst); // save quote if (!_quoteIdToSave.Contains(quote.QuoteID)) { _quoteIdToSave.Enqueue(quote.QuoteID); } //var utcnow = DateTime.UtcNow.GetUnixTimeFromUTC(); //if (!_lastSaveTime.ContainsKey(quote.QuoteID)) //{ // var t = quote.Interval <= 900 ? utcnow + _random.Next(900) : 0; // _lastSaveTime.TryAdd(quote.QuoteID, t); //} //if (utcnow - _lastSaveTime[quote.QuoteID] > _saveInterval) //{ // _quoteIdToSave.Enqueue(quote.QuoteID); // _lastSaveTime[quote.QuoteID] = utcnow; //} //if (_quoteIdToSave.Count > 0) // this.ProcessSaveQuoteBasicQueue(); }
private void TestAddEventInternal(object sender, IQuoteBasicBase quote, int numAppended) { throw new Exception(); }
//[0,1,2,4,5) public int Append(IQuoteBasicBase q, bool isTriggerDataUpdated = false) { //no data to add if (q == null || q.Count <= 0 || q.LastTime < this.LastTime || this.Symbol != q.Symbol || this.Interval < q.Interval || this.Interval % q.Interval != 0) { return(0); } //search backward for quotes to be added. the found time should be >= this.LastTime int indexStartSearch = -1; for (int i = q.Count - 1; i >= 0; i--) { if (q.Time[i] < this.LastTime) { break; } indexStartSearch = i; } if (indexStartSearch == -1) { return(0); } ///////////////////////////////////////////////////////////////////////////////// var numBeforeAdd = this.Count; var isDataChanged = false; var numAddedElement = 0; int sindex = indexStartSearch; // interval区间的开始索引 var eindex = -1; var endTime = q.Time[sindex] / this.Interval * this.Interval + this.Interval; //use the first time as the data bar time for (int i = indexStartSearch; i <= q.Count - 1; i++) { if (q.Time[i] >= endTime) { eindex = i - 1; // interval区间的结束索引 var num = this.AddItemByQuoteBasic(q, sindex, eindex); if (num >= 0) { numAddedElement += num; isDataChanged = true; } sindex = i; endTime = q.Time[sindex] / this.Interval * this.Interval + this.Interval; } } //add last element if (sindex > eindex) { var num = this.AddItemByQuoteBasic(q, sindex, q.Count - 1); if (num >= 0) { numAddedElement += num; isDataChanged = true; } } if (isTriggerDataUpdated && isDataChanged) { OnDataAddedOrUpdated?.Invoke(this, this, numAddedElement); } return(isDataChanged? numAddedElement : -1); //-1 means nothing changed, 0 means updated, >=1 means added }