public Quotation[] GetQuotes() { // TODO: Return the list of quotes for the specified ticker. List <Quotation> QList = new List <Quotation>(); foreach (Ticker item in this.Data) { Quotation qt = new Quotation(); qt.High = item.high; qt.Low = item.low; qt.Open = item.open; qt.Price = item.close; qt.Volume = item.volumefrom; ///item.volumeto - item.volumefrom; // Date AmiDate tickerDate = new AmiDate(HistoryData.UnixTimeStampToDateTime(item.time)); qt.DateTime = tickerDate.ToUInt64(); QList.Add(qt); } return(QList.ToArray()); }
// Получение котировок за последние 24 часа public static List <Ticker> getLast24hBars(string pairName) { List <Ticker> result = new List <Ticker>(); // Строка запроса - данные за последние 24 часа в 1 мин интервале string url = String.Format(LAST24_BARS, pairName, "1m"); // Получаем ответ string answer = getJSONData(url); //Log.Write(answer, "answer_" + pairName + ".log"); // Массив данных List <ArrayList> decodedList = null; try { decodedList = JsonConvert.DeserializeObject <List <ArrayList> >(answer); } catch (Exception e) { Log.Write("Can't parse JSON data for last 24H bars!"); return(null); } if (decodedList.Count == 0) { Log.Write("Decooded JSON data for last 24H bars is empty!"); return(null); } int index = 0; foreach (ArrayList arr in decodedList) { Ticker ticker = new Ticker(); // Формирование даты AmiDate time = new AmiDate(Utils.UnixTimeStampToDateTime(Convert.ToUInt64(arr[0]) / 1000)); // Bar ticker.time = time.ToUInt64(); ticker.open = float.Parse(arr[1].ToString().Replace(".", ",")); ticker.high = float.Parse(arr[2].ToString().Replace(".", ",")); ticker.low = float.Parse(arr[3].ToString().Replace(".", ",")); ticker.close = float.Parse(arr[4].ToString().Replace(".", ",")); ticker.volume = float.Parse(arr[5].ToString().Replace(".", ",")); // Запись в массив result.Add(ticker); } // Возвращаем массив return(result); }
public List <Quotation> GetQuotesList() { List <Quotation> result = new List <Quotation>(); foreach (Ticker item in this.Data) { Quotation qt = new Quotation(); qt.High = item.high; qt.Low = item.low; qt.Open = item.open; qt.Price = item.close; qt.Volume = item.volumefrom; ///item.volumeto - item.volumefrom; // Date AmiDate tickerDate = new AmiDate(HistoryData.UnixTimeStampToDateTime(item.time)); qt.DateTime = tickerDate.ToUInt64(); result.Add(qt); } return(result); }
public static unsafe int GetQuotesEx(string ticker, Periodicity periodicity, int lastValid, int size, Quotation *quotes, GQEContext *context) { // Статус - в ожидании данных Status = StatusCode.Wait; #region Данных не было - БД пуста if (lastValid < 0) { List <Ticker> last24hBars = BinanceHelper.getLast24hBars(ticker); // Проверка что не пусто if (last24hBars == null || last24hBars.Count == 0) { return(lastValid + 1); } lastValid = 0; // Если влазим в окно - то i = 0; иначе last24hBars.Count - size for (var i = (last24hBars.Count < size) ? 0 : last24hBars.Count - size; i < last24hBars.Count; i++) { quotes[lastValid].DateTime = last24hBars[i].time; quotes[lastValid].Open = last24hBars[i].open; quotes[lastValid].High = last24hBars[i].high; quotes[lastValid].Low = last24hBars[i].low; quotes[lastValid].Price = last24hBars[i].close; quotes[lastValid].Volume = last24hBars[i].volume; lastValid++; } // Сохраняем значение символьной пары lastTicker = ticker; return(lastValid); } #endregion Log.Write("isConn: " + isSocketConnected + " lastT: " + lastTicker + " TC: " + ticker); // Проверка на то что не переключили символы + первый запуск if (String.IsNullOrEmpty(lastTicker) || !lastTicker.Equals(ticker)) { isFirstRun = true; /* * if (wss != null) * wss.Close(); * * // Запуск сокета * wss = new WebSocket(String.Format("wss://stream.binance.com:9443/ws/{0}@kline_{1}",ticker.ToLower(),"1m")); * wss.Opened += new EventHandler(WebSockedOpened); * wss.MessageReceived += Wss_MessageReceived; * // wss.Closed += Wss_Closed; * wss.Error += Wss_Error; * wss.Open(); */ if (!BinanceHelper.CreateWSS(ticker, periodicity)) { Log.Write("Create WSS failed!"); isSocketConnected = false; return(lastValid + 1); } // BinanceHelper.onWSSMessage = Wss_MessageReceived; // isSocketConnected = true; } else { isFirstRun = false; } // Обозначаем тикер lastTicker = ticker; #region Данные есть и это первый запуск if (isFirstRun && lastValid > 0) { // Получаем данные List <Ticker> last24hBars = BinanceHelper.getLast24hBars(ticker); // Проверка что не пусто if (last24hBars == null || last24hBars.Count == 0) { return(lastValid + 1); } // Кастрируем массив for (var i = 0; i < last24hBars.Count; i++) { AmiDate lastDate = new AmiDate(quotes[lastValid].DateTime); AmiDate requestedDate = new AmiDate(last24hBars[i].time); if (requestedDate.CompareTo(lastDate) <= 0) { last24hBars.RemoveAt(0); i--; } else { // Вываливаемся из цикла так как последний элемент явно старше break; } } // Вариант 1 - Count > size - переносим данные if (last24hBars.Count > size) { lastValid = 0; // Перенос последних = size данных for (var i = last24hBars.Count - size; i < last24hBars.Count; i++) { quotes[lastValid].DateTime = last24hBars[i].time; quotes[lastValid].Open = last24hBars[i].open; quotes[lastValid].High = last24hBars[i].high; quotes[lastValid].Low = last24hBars[i].low; quotes[lastValid].Price = last24hBars[i].close; quotes[lastValid].Volume = last24hBars[i].volume; lastValid++; } return(lastValid); } // Вариант 2 - Count < size и входит в окно - добавить в список if ((last24hBars.Count < size) && (last24hBars.Count < (size - lastValid))) { // Перенос всех из массива for (var i = 0; i < last24hBars.Count; i++) { quotes[lastValid].DateTime = last24hBars[i].time; quotes[lastValid].Open = last24hBars[i].open; quotes[lastValid].High = last24hBars[i].high; quotes[lastValid].Low = last24hBars[i].low; quotes[lastValid].Price = last24hBars[i].close; quotes[lastValid].Volume = last24hBars[i].volume; lastValid++; } return(lastValid); } else if ((last24hBars.Count < size) && (last24hBars.Count > (size - lastValid))) { // Вариант 3 - данные в окно не входят - нужно сдвигать массив lastValid = 0; // Сколько элементов останется var j = size - last24hBars.Count; // Индекс начала копирования var index = lastValid - j; // Смещение первой части while (lastValid < j) { Log.Write("I: " + lastValid + " HIGH: " + quotes[index].High + " VOL: " + quotes[index].Volume, "3.txt"); quotes[lastValid].DateTime = quotes[index].DateTime; quotes[lastValid].Open = quotes[index].Open; quotes[lastValid].High = quotes[index].High; quotes[lastValid].Low = quotes[index].Low; quotes[lastValid].Price = quotes[index].Price; quotes[lastValid].Volume = quotes[index].Volume; lastValid++; index++; } // КОпируем остатки foreach (Ticker item in last24hBars) { Log.Write("I: " + lastValid + " HIGH: " + item.high + " VOL: " + item.volume, "4.txt"); quotes[lastValid].DateTime = item.time; quotes[lastValid].Open = item.open; quotes[lastValid].High = item.high; quotes[lastValid].Low = item.low; quotes[lastValid].Price = item.close; quotes[lastValid].Volume = item.volume; lastValid++; } return(lastValid); } } #endregion // не первый запуск - просто обновляем // Если не подключились - нечего разбирать if (!isSocketConnected) { return(lastValid + 1); } // Показывает что данные тикера устарели //bool isTooOld = false; BinanceData data = null; if (!String.IsNullOrEmpty(jsonAnswer)) { // Парсинг try { data = JsonConvert.DeserializeObject <BinanceData>(jsonAnswer); } catch (Exception e) { Log.Write("Parse Error: " + e.Message); return(lastValid + 1); } } // В БД есть какие-то данные if (lastValid >= 0) { ulong lastDate = quotes[lastValid].DateTime; ulong tickerDate = (new AmiDate(Utils.UnixTimeStampToDateTime(data.k.t / 1000))).ToUInt64(); /* * if (tickerDate < lastDate) * isTooOld = true; */ if (tickerDate > lastDate) { lastValid++; } } else { // Если пусто в БД - начинаем писать с 0го индекса lastValid = 0; } // Поправка на лимит if (size > 0 && lastValid == size) { // Сдвигание массива влево for (int i = 0; i < size - 1; i++) { quotes[i].DateTime = quotes[i + 1].DateTime; quotes[i].Open = quotes[i + 1].Open; quotes[i].High = quotes[i + 1].High; quotes[i].Low = quotes[i + 1].Low; quotes[i].Price = quotes[i + 1].Price; quotes[i].Volume = quotes[i + 1].Volume; } lastValid--; } // Правим //if (!isTooOld) { AmiDate tickerDate = new AmiDate(Utils.UnixTimeStampToDateTime(data.k.t / 1000)); quotes[lastValid].DateTime = tickerDate.ToUInt64(); quotes[lastValid].Open = float.Parse(data.k.o.Replace(".", ",")); quotes[lastValid].High = float.Parse(data.k.h.Replace(".", ",")); quotes[lastValid].Low = float.Parse(data.k.l.Replace(".", ",")); quotes[lastValid].Price = float.Parse(data.k.c.Replace(".", ",")); quotes[lastValid].Volume = float.Parse(data.k.v.Replace(".", ",")); quotes[lastValid].AuxData1 = 0; quotes[lastValid].AuxData2 = 0; } Status = StatusCode.OK; return(lastValid + 1); }
public static unsafe int GetQuotesEx(string ticker, Periodicity periodicity, int lastValid, int size, Quotation *quotes, GQEContext *context) { // Статус - в ожидании данных Status = StatusCode.Wait; // при запуске из базы if (refrashTime == -1) { refrashTime = (int)periodicity; } // ticker - наименование пары // periodicity - период опроса (day, mminute, ...) string baseURL; // Если не фьючерсы Okex if (ticker.IndexOf("Future Contracts") < 0) { baseURL = "https://min-api.cryptocompare.com/data/"; // Вроде как ограничение за последние 30 дней = 30 записей по умолчанию (макс 2000) if (periodicity == Periodicity.EndOfDay) { baseURL += "histoday?"; } // Вроде как ограничение за последние 7 дней = 168 записей по умолчанию (макс 2000) if (periodicity == Periodicity.OneHour) { baseURL += "histohour?"; } // Вроде как ограничение за последние 24 часа = 1440 записей по умолчанию (макс 2000) if (periodicity == Periodicity.OneMinute) { baseURL += "histominute?aggregate=1&"; } // 5 минутная агрегация if (periodicity == Periodicity.FiveMinutes) { baseURL += "histominute?aggregate=5&"; } // 15 минутная агрегация if (periodicity == Periodicity.FifteenMinutes) { baseURL += "histominute?aggregate=15&"; } } else { // https://www.okex.com/api/v1/future_kline.do?symbol=btc_usd&contract_type=this_week&type=5min baseURL = "https://www.okex.com/api/v1/future_kline.do?type="; // Вроде как ограничение за последние 30 дней = 30 записей по умолчанию (макс 2000) if (periodicity == Periodicity.EndOfDay) { baseURL += "1day"; } // Вроде как ограничение за последние 7 дней = 168 записей по умолчанию (макс 2000) if (periodicity == Periodicity.OneHour) { baseURL += "1hour"; } // Вроде как ограничение за последние 24 часа = 1440 записей по умолчанию (макс 2000) if (periodicity == Periodicity.OneMinute) { baseURL += "1min"; } // 5 минутная агрегация if (periodicity == Periodicity.FiveMinutes) { baseURL += "5min"; } // 15 минутная агрегация if (periodicity == Periodicity.FifteenMinutes) { baseURL += "15min"; } } // 2. Выбираем маркет и пару // Разбор вида [1]/[2] - [3] // [1] - 1й символ // [2] - 2й символ // [3] - маркет try { string s1 = ticker.Substring(0, ticker.IndexOf("/")); string s2 = ticker.Substring(ticker.IndexOf("/") + 1, ticker.IndexOf(" ") - ticker.IndexOf("/")); string market = ticker.Substring(ticker.IndexOf(" - ") + 3); // Коррекция CryptoCompare if (market.IndexOf("CryptoCompare") > -1) { market = "CCCAGG"; } if (ticker.IndexOf("Future Contracts") < 0) { // Добавим пары и маркет в URL baseURL += "fsym=" + s1.Trim() + "&tsym=" + s2.Trim() + "&e=" + market.Trim() + "&allData=true"; } else { //symbol=btc_usd baseURL += "&symbol=" + s1.Trim().ToLower() + "_" + s2.Trim().ToLower() + "&contract_type=" + RightClickMenu.contractType + "&size=" + size; } } catch (Exception e) { Status = StatusCode.Error; Log.Write("The symbol translate error!"); return(lastValid + 1); } string jsonResponse = MarketData.getMarketData(baseURL); // Если ошибка if (String.IsNullOrEmpty(jsonResponse)) { Status = StatusCode.Error; Log.Write("Error: " + MarketData.getLastError()); return(lastValid + 1); } MarketData.hasData = false; // В jsonResponse имеем файл ответа сервера // Парсим List <Quotation> newQuotes = null; if (ticker.IndexOf("Future Contracts") < 0) { HistoryData data = null; try { data = JsonConvert.DeserializeObject <HistoryData>(jsonResponse); } catch (Exception e) { Log.Write("Json deserialize error: " + e.Message); return(lastValid + 1); } // В итоге имеем данные newQuotes = data.GetQuotesList(); } else { IList <IList <string> > data = null; try { data = JsonConvert.DeserializeObject <IList <IList <string> > >(jsonResponse); } catch (Exception e) { Log.Write("Json deserialize error: " + e.Message); return(lastValid + 1); } foreach (IList <string> item in data) { Quotation qt = new Quotation(); AmiDate tickerDate = new AmiDate(HistoryData.UnixTimeStampToDateTime(ulong.Parse(item[0]) / 1000)); try { qt.Open = float.Parse(item[1].Replace(".", ",")); qt.High = float.Parse(item[2].Replace(".", ",")); qt.Low = float.Parse(item[3].Replace(".", ",")); qt.Price = float.Parse(item[4].Replace(".", ",")); qt.Volume = float.Parse(item[5].Replace(".", ",")); } catch (Exception e) { Log.Write(e.Message); } qt.DateTime = tickerDate.ToUInt64(); newQuotes.Add(qt); } } // Запись данных в БД #region Пишем с нуля - БД не заполнена if (lastValid < 0) { lastValid = 0; // Что меньше: длинна полученных данных или Limit? int count = Math.Min(size, newQuotes.Count); int i = 0, j = 0; for (i = 0; i < count; i++) { j = newQuotes.Count - count + i; // Date quotes[i].DateTime = newQuotes[j].DateTime; // ticker quotes[i].Open = newQuotes[j].Open; quotes[i].High = newQuotes[j].High; quotes[i].Low = newQuotes[j].Low; quotes[i].Price = newQuotes[j].Price; quotes[i].Volume = newQuotes[j].Volume; // Extra data quotes[i].OpenInterest = 0; quotes[i].AuxData1 = 0; quotes[i].AuxData2 = 0; lastValid++; } // Меняем статус на ОК if (!isUpdateAvailable) { Status = StatusCode.OK; } else { Status = StatusCode.Update; } return(lastValid); } #endregion #region Кастрация данных до последнего тайминга for (var i = 0; i < newQuotes.Count; i++) { // Сейчас у нас в массиве только свежак if (newQuotes[i].DateTime < quotes[lastValid].DateTime) { newQuotes.RemoveAt(i); i--; } } #endregion // Если нечего показывать - выход if (newQuotes.Count == 0) { return(lastValid + 1); } #region Данные уже есть в БД - в корридор входим if (newQuotes.Count <= (size - lastValid - 1)) { foreach (Quotation item in newQuotes) { // Date quotes[lastValid].DateTime = item.DateTime; // ticker quotes[lastValid].Open = item.Open; quotes[lastValid].High = item.High; quotes[lastValid].Low = item.Low; quotes[lastValid].Price = item.Price; quotes[lastValid].Volume = item.Volume; // Extra data quotes[lastValid].OpenInterest = 0; quotes[lastValid].AuxData1 = 0; quotes[lastValid].AuxData2 = 0; lastValid++; } // Меняем статус на ОК if (!isUpdateAvailable) { Status = StatusCode.OK; } else { Status = StatusCode.Update; } return(lastValid); } #endregion #region Данные уже есть в БД - в корридор не входим - сдвиг массива lastValid = 0; // Смещение первой части while (lastValid < (size - newQuotes.Count)) { quotes[lastValid].DateTime = quotes[lastValid + newQuotes.Count].DateTime; quotes[lastValid].Open = quotes[lastValid + newQuotes.Count].Open; quotes[lastValid].High = quotes[lastValid + newQuotes.Count].High; quotes[lastValid].Low = quotes[lastValid + newQuotes.Count].Low; quotes[lastValid].Price = quotes[lastValid + newQuotes.Count].Price; quotes[lastValid].Volume = quotes[lastValid + newQuotes.Count].Volume; lastValid++; } // КОпируем остатки foreach (Quotation item in newQuotes) { quotes[lastValid].DateTime = item.DateTime; quotes[lastValid].Open = item.Open; quotes[lastValid].High = item.High; quotes[lastValid].Low = item.Low; quotes[lastValid].Price = item.Price; quotes[lastValid].Volume = item.Volume; lastValid++; } // Меняем статус на ОК if (!isUpdateAvailable) { Status = StatusCode.OK; } else { Status = StatusCode.Update; } return(lastValid); #endregion }
internal override bool Process(FTController ibController, bool allowNewRequest) { int requestTimeoutPeriod = 75; // if contract of the ticker is still being retrieved or headtimestamp of the ticker is needed (not Offline) AND not yet retrieved if (TickerData.ContractStatus <= ContractStatus.WaitForResponse || ((FTDataSource.Periodicity == Periodicity.EndOfDay || FTDataSource.AllowMixedEODIntra) && TickerData.HeadTimestampStatus <= HeadTimestampStatus.WaitForResponse)) { return(allowNewRequest); } if (TickerData.ContractStatus == ContractStatus.Failed || TickerData.ContractStatus == ContractStatus.Offline || TickerData.HeadTimestampStatus == HeadTimestampStatus.Failed || (TickerData.HeadTimestampStatus == HeadTimestampStatus.Offline && (FTDataSource.Periodicity == Periodicity.EndOfDay || FTDataSource.AllowMixedEODIntra))) { TickerData.QuoteDataStatus = QuotationStatus.Failed; IsFinished = true; return(allowNewRequest); } lock (TickerData) // request handling { // if reqHistoricalData is send to IB and we are waiting for answer if (WaitingForResponse) { // request is not yet timed out... if (RequestTime.AddSeconds(requestTimeoutPeriod) > DateTime.Now) { return(allowNewRequest); } // no response arrived in time, request is timed out... LogAndMessage.LogAndQueue(TickerData, MessageType.Info, "Historical data request has timed out. " + ToString(true, LogAndMessage.VerboseLog)); RequestTimeouts++; WaitingForResponse = false; // if there were too many reqHistoricalData timeouts if (RequestTimeouts > 2) { // drop this ticker... TickerData.QuoteDataStatus = QuotationStatus.Failed; IsFinished = true; return(allowNewRequest); } } // if no new request can be sent (request pacing) bool histThrottling = !allowNewRequest || TickerData.QuoteDataStatus > QuotationStatus.New && RequestTime.AddSeconds(6.5) > DateTime.Now; // process the ticker depending on its state switch (TickerData.QuoteDataStatus) { case QuotationStatus.Offline: LogAndMessage.Log(MessageType.Error, "Program error. Offline ticker cannot get historical update."); IsFinished = true; return(allowNewRequest); // All historical data requests are processed for the ticker // (the last CalcNextHistoricalDataRequest call sets this state) case QuotationStatus.DownloadedEod: #region Merging and backadjusting downloaded quotes of different contracts/expiry into a simgle QuotationList of the continuous contract if (TickerData.SymbolParts.IsContinuous) { QuotationList mergedQuotes = new QuotationList(FTDataSource.Periodicity); int newQuoteIndex; foreach (ContractDetails cd in TickerData.contractDetailsList) { // if there were no quotes receiced for this contract... if (!TickerData.ContinuousQuotesDictionary.ContainsKey(cd.Contract.LocalSymbol)) { continue; } newQuoteIndex = 0; if (mergedQuotes.Count > 0) { int mergedQuoteIndex = mergedQuotes.Count - 1; AmiDate mergedQuoteDateTime = mergedQuotes[mergedQuoteIndex].DateTime; // move forward to the first quote not overlqapping with prev contract while (newQuoteIndex < TickerData.ContinuousQuotesDictionary[cd.Contract.LocalSymbol].Count - 1 && TickerData.ContinuousQuotesDictionary[cd.Contract.LocalSymbol][newQuoteIndex].DateTime.Date < mergedQuoteDateTime.Date) { newQuoteIndex++; } // at this point newQuoteIndex points to a quote of the "same" date as mergedQuoteDateTime (if there are quotes for the same day, if not, then the next day) // if daily database then we look for a day where volume on older contract is greater (switch over day) if (FTDataSource.Periodicity == Periodicity.EndOfDay) { // find the quote that has a lower volume while (newQuoteIndex > 0 && mergedQuoteIndex > 0 && TickerData.ContinuousQuotesDictionary[cd.Contract.LocalSymbol][newQuoteIndex].DateTime.Date == mergedQuotes[mergedQuoteIndex].DateTime.Date && // quotes are of same date TickerData.ContinuousQuotesDictionary[cd.Contract.LocalSymbol][newQuoteIndex].Volume > mergedQuotes[mergedQuoteIndex].Volume) // new contract's volume is higher then old contract's volume { newQuoteIndex--; mergedQuoteIndex--; } // at this point newQuoteIndex and lastQuoteDateTime point to quote at which contract is replaced } if (TickerData.ContinuousQuotesDictionary[cd.Contract.LocalSymbol][newQuoteIndex].DateTime.Date != mergedQuotes[mergedQuoteIndex].DateTime.Date) { LogAndMessage.Log(MessageType.Info, TickerData.ToString(cd.Contract) + ": No overlapping quote found. Used dates to change contracts: " + mergedQuotes[mergedQuoteIndex].DateTime + " and " + TickerData.ContinuousQuotesDictionary[cd.Contract.LocalSymbol][newQuoteIndex].DateTime + "."); } else { LogAndMessage.Log(MessageType.Info, TickerData.ToString(cd.Contract) + ": Switching to new contract on " + mergedQuotes[mergedQuoteIndex].DateTime + "."); } // get "closing prices" of the contract on the same day float closeOfNewer = TickerData.ContinuousQuotesDictionary[cd.Contract.LocalSymbol][newQuoteIndex].Price; float closeOfOlder = mergedQuotes[mergedQuoteIndex].Price; double priceMult = closeOfNewer / closeOfOlder; // back-adjust prev contracts' prices QuotationList tempList = new QuotationList(FTDataSource.Periodicity); for (int i = 0; i < mergedQuoteIndex; i++) { Quotation quote = mergedQuotes[i]; quote.Open = (float)(quote.Open * priceMult); quote.High = (float)(quote.High * priceMult); quote.Low = (float)(quote.Low * priceMult); quote.Price = (float)(quote.Price * priceMult); tempList.Merge(quote); } mergedQuotes.Clear(); mergedQuotes = tempList; } // add quotes of newer contract for (; newQuoteIndex < TickerData.ContinuousQuotesDictionary[cd.Contract.LocalSymbol].Count; newQuoteIndex++) { mergedQuotes.Merge(TickerData.ContinuousQuotesDictionary[cd.Contract.LocalSymbol][newQuoteIndex]); } } TickerData.Quotes = mergedQuotes; } #endregion // this is not THROTTLED, but counted in general throttling queue ibController.SendSubscriptionRequest(0, TickerData, false); TickerData.QuoteDataStatus = QuotationStatus.Online; return(allowNewRequest); // this should never happen (ticker with online status should not be in the queue...) case QuotationStatus.Online: LogAndMessage.LogAndQueue(TickerData, MessageType.Info, "Backfill finished, symbol is ready. "); IsFinished = true; return(allowNewRequest); // if any error happend case QuotationStatus.Failed: // if intraday download received no data response if (errorCode == 162 && FTDataSource.Periodicity < Periodicity.EndOfDay && FTDataSource.Periodicity > Periodicity.FifteenSeconds) { errorCode = 0; // move forward 4 periods to speed up download/find first valid period with available data CalcNextBackfillRequest(); CalcNextBackfillRequest(); CalcNextBackfillRequest(); LogAndMessage.Log(TickerData, MessageType.Trace, "No data returned, fast forward download period."); // start next download TickerData.QuoteDataStatus = QuotationStatus.DownloadingIntra; return(allowNewRequest); } else { LogAndMessage.LogAndQueue(TickerData, MessageType.Info, "Backfill failed, symbol is offline."); IsFinished = true; return(allowNewRequest); } // start historical data refresh case QuotationStatus.New: if (histThrottling) { return(false); } // calc download properties downloadPeriodicity = FTDataSource.Periodicity; downloadStep = IBClientHelper.GetDownloadStep(FTDataSource.Periodicity); downloadInterval = IBClientHelper.GetDownloadInterval(FTDataSource.Periodicity); downloadStart = IBClientHelper.GetAdjustedStartDate(TickerData.RefreshStartDate, FTDataSource.Periodicity, GetEarliestDownloadDate(), true); downloadEnd = downloadStart.AddMinutes(downloadInterval); downloadContract = GetCurrentContract(downloadStart); // remove quotes already stored TickerData.Quotes.Clear(); // set next state if (FTDataSource.Periodicity == Periodicity.EndOfDay) { TickerData.QuoteDataStatus = QuotationStatus.DownloadingEod; } else { TickerData.QuoteDataStatus = QuotationStatus.DownloadingIntra; } // not to wait to send next request RequestTime = DateTime.MinValue; // download historical data SendBackfillRequest(ibController); return(false); case QuotationStatus.DownloadingEod: case QuotationStatus.DownloadingIntra: if (histThrottling) { return(false); } // if previous request timed out if (RequestTimeouts != 0) { SendBackfillRequest(ibController); } // download historical data else if (CalcNextBackfillRequest()) { SendBackfillRequest(ibController); } return(false); // last CalcNextHistoricalDataRequest call for intraday bars should have set this state case QuotationStatus.DownloadedIntra: // if we need EOD data as well if (FTDataSource.AllowMixedEODIntra) { if (histThrottling) { return(false); } // calc download properties for EOD downloadPeriodicity = Periodicity.EndOfDay; downloadStep = IBClientHelper.GetDownloadStep(Periodicity.EndOfDay); downloadInterval = IBClientHelper.GetDownloadInterval(Periodicity.EndOfDay); downloadStart = IBClientHelper.GetAdjustedStartDate(TickerData.RefreshStartDate, Periodicity.EndOfDay, GetEarliestDownloadDate(), true); downloadEnd = downloadStart.AddMinutes(downloadInterval); downloadContract = GetCurrentContract(downloadStart); SendBackfillRequest(ibController); TickerData.QuoteDataStatus = QuotationStatus.DownloadingEod; } else { TickerData.QuoteDataStatus = QuotationStatus.DownloadedEod; } return(false); default: LogAndMessage.LogAndQueue(TickerData, MessageType.Info, "Program error in backfill logic."); IsFinished = true; return(true); } } }