/// <summary> /// Get the contract for the historical data request for a specified start date /// </summary> /// <param name="startDate"></param> /// /// <returns></returns> private Contract GetCurrentContract(DateTime startDate) { Contract contract; if (TickerData.SymbolParts.IsContinuous) { string exStr = startDate.ToString("yyyyMMdd"); int i = 0; for (; i < TickerData.contractDetailsList.Count - 1; i++) { if (TickerData.contractDetailsList[i].Contract.LastTradeDateOrContractMonth.CompareTo(exStr) >= 0) { break; } } contract = TickerData.contractDetailsList[i].Contract; } else { contract = TickerData.ContractDetails.Contract; } contract.IncludeExpired = IBClientHelper.IsContractExpired(contract); // TODO: Do we need it? Does not hurt... if (string.IsNullOrEmpty(contract.LocalSymbol)) { LogAndMessage.Log(MessageType.Trace, "No valid security for continuous contract."); return(null); } return(contract); }
/// <summary> /// Writes an error message sent by TWS to the log file and queues it for the plugin notification area /// </summary> /// <param name="tickerData"></param> /// <param name="context"></param> /// <param name="e"></param> internal static void LogAndQueue(TickerData tickerData, string context, int id, int errorCode, string errorMsg) { MessageType messageType = IBClientHelper.GetMessageType(errorCode); if (IsLoggable(messageType)) { LogMessage(messageType, GetTwsMessageForLog(tickerData, context, id, errorCode, errorMsg)); QueueMessage(messageType, GetTwsMessageForUser(tickerData, context, id, errorCode, errorMsg)); } }
/// <summary> /// Writes a message sent by TWS to the log file /// </summary> /// <param name="tickerData"></param> /// <param name="context"></param> /// <param name="e"></param> internal static void Log(TickerData tickerData, string context, int id, int errorCode, string errorMsg) { MessageType messageType = IBClientHelper.GetMessageType(errorCode); if (IsLoggable(messageType)) { string message = GetTwsMessageForLog(tickerData, context, id, errorCode, errorMsg); LogMessage(messageType, message); } }
internal bool IsFinished; // Response/Timeout/Error closed this request internal Request(TickerData tickerData) { if (tickerData == null) { throw new ArgumentNullException(); } Id = IBClientHelper.GetNextReqId(); // this Id is used (instead of collections key) in case of HistoricalDataRequest and HeadTimestampRequest // because the same logical request may result in more actual request (e.g timeouts and multiple download periods) TickerData = tickerData; IsFinished = false; RequestTimeouts = 0; }
private void SendBackfillRequest(FTController ibController) { if (downloadContract != null // && (string.IsNullOrEmpty(contract.LastTradeDateOrContractMonth) || histStart.ToString("yyyyMMdd").CompareTo(contract.LastTradeDateOrContractMonth) <= 0) && (downloadStep > 24 * 60 || IBClientHelper.IsBankingHour(downloadStart) || IBClientHelper.IsBankingHour(downloadEnd))) //check if we need to queue a requests { //try to avoid intraday request for saturday-sunday Id = IBClientHelper.GetNextReqId(); // add quotelist of subcontracts of continuous contract if (TickerData.SymbolParts.IsContinuous) { if (!TickerData.ContinuousQuotesDictionary.ContainsKey(downloadContract.LocalSymbol)) { TickerData.ContinuousQuotesDictionary.Add(downloadContract.LocalSymbol, new QuotationList(FTDataSource.Periodicity)); } } if (downloadPeriodicity <= Periodicity.FifteenSeconds) // download step is smaller than a day { LogAndMessage.LogAndQueue(MessageType.Info, TickerData.ToString(downloadContract) + ": Requesting data from " + downloadStart.ToShortDateString() + " " + downloadStart.ToShortTimeString() + " to " + downloadEnd.ToShortDateString() + " " + downloadEnd.ToShortTimeString() + " " + ToString(false, LogAndMessage.VerboseLog)); } else { LogAndMessage.LogAndQueue(MessageType.Info, TickerData.ToString(downloadContract) + ": Requesting data from " + downloadStart.ToShortDateString() + " to " + downloadEnd.ToShortDateString() + " " + ToString(false, LogAndMessage.VerboseLog)); } WaitingForResponse = true; RequestTime = DateTime.Now; ibController.SendHistoricalDataRequest(Id, downloadContract, downloadEnd, downloadStart, downloadPeriodicity, TickerData.SymbolParts.DataType); } else { // calc next download period if (CalcNextBackfillRequest()) { SendBackfillRequest(ibController); } } }
private bool CalcNextBackfillRequest() { downloadStart = downloadStart.AddMinutes(downloadStep); // if contiguous contract is being backfilled if (TickerData.SymbolParts.IsContinuous) { DateTime currExp = IBClientHelper.GetContractExpiryDateTime(downloadContract); if (currExp < downloadStart) { downloadContract = GetCurrentContract(currExp.AddDays(1)); downloadStart = currExp.AddMinutes(-downloadStep); downloadStart = IBClientHelper.GetAdjustedStartDate(downloadStart, downloadPeriodicity, GetEarliestDownloadDate(), true); } } // if no more to download if (downloadPeriodicity < Periodicity.EndOfDay && downloadStart > DateTime.Now || downloadPeriodicity == Periodicity.EndOfDay && downloadStart.Date >= DateTime.Now.Date) { if (TickerData.QuoteDataStatus == QuotationStatus.DownloadingIntra) { TickerData.QuoteDataStatus = QuotationStatus.DownloadedIntra; } if (TickerData.QuoteDataStatus == QuotationStatus.DownloadingEod) { TickerData.QuoteDataStatus = QuotationStatus.DownloadedEod; } return(false); } // set the end of the download period downloadEnd = downloadStart.AddMinutes(downloadInterval); // indicate that more request is needed return(true); }
private void mBackfill_Click(object sender, EventArgs e) { if (firstGetQuotesExCall) // db periodicity not known yet { return; } if (string.IsNullOrEmpty(currentTicker)) // no selected ticker { return; } DateTime refreshStartDate = GetRefreshStartDate(sender); refreshStartDate = IBClientHelper.GetAdjustedStartDate(refreshStartDate, Periodicity, DateTime.MinValue, false); LogAndMessage.Log(MessageType.Info, currentTicker + ": Manual backfill from " + refreshStartDate.ToShortDateString() + "."); StringCollection tickerToBackfill = new StringCollection(); tickerToBackfill.Add(currentTicker); StartBackfills(refreshStartDate, tickerToBackfill); }
internal override bool Process(FTController ibController, bool allowNewRequest) { const int requestTimeoutPeriod = 20; // if no contract received yet if (TickerData.ContractStatus == ContractStatus.SendRequest || TickerData.ContractStatus == ContractStatus.WaitForResponse) { return(allowNewRequest); } // if no contract found if (TickerData.ContractStatus != ContractStatus.Ok) { TickerData.HeadTimestampStatus = HeadTimestampStatus.Failed; IsFinished = true; return(allowNewRequest); } lock (TickerData) // request handling { // if not waiting for response switch (TickerData.HeadTimestampStatus) { // if marked to get headtimestamp case HeadTimestampStatus.SendRequest: if (allowNewRequest) { LogAndMessage.Log(TickerData, MessageType.Trace, "Requesting earliest data point. " + ToString(false, LogAndMessage.VerboseLog)); TickerData.HeadTimestampStatus = HeadTimestampStatus.WaitForResponse; RequestTime = DateTime.Now; ibController.SendHeadTimestampRequest(Id, TickerData); } return(false); // if request is sent, but response has not arrived yet // see ibClient_HeadTimestamp event handler case HeadTimestampStatus.WaitForResponse: // if no answer in time if (RequestTime.AddSeconds(requestTimeoutPeriod) < DateTime.Now) { if (RequestTimeouts == 0) { LogAndMessage.LogAndQueue(TickerData, MessageType.Error, "Request of earliest data point timed out. Retrying. " + ToString(true, LogAndMessage.VerboseLog)); RequestTimeouts++; TickerData.HeadTimestampStatus = HeadTimestampStatus.SendRequest; Id = IBClientHelper.GetNextReqId(); goto case HeadTimestampStatus.SendRequest; } LogAndMessage.LogAndQueue(TickerData, MessageType.Error, "Request of earliest data point timed out. " + ToString(true, LogAndMessage.VerboseLog)); TickerData.HeadTimestampStatus = HeadTimestampStatus.Failed; goto case HeadTimestampStatus.Failed; } return(allowNewRequest); // if new, offline ticker case HeadTimestampStatus.Offline: // ticker's HeadTimestamp is updated case HeadTimestampStatus.Ok: // ticker's HeadTimestamp is NOT updated (we do not mark ticker as failed. it still may work!) case HeadTimestampStatus.Failed: IsFinished = true; return(allowNewRequest); // this is program error default: TickerData.HeadTimestampStatus = HeadTimestampStatus.Failed; IsFinished = true; return(allowNewRequest); } } }
private void mBackfillAll_Click(object sender, EventArgs e) { if (firstGetQuotesExCall) // db periodicity not known yet { return; } if (string.IsNullOrEmpty(currentTicker)) // no selected ticker { return; } DateTime refreshStartDate = GetRefreshStartDate(sender); refreshStartDate = IBClientHelper.GetAdjustedStartDate(refreshStartDate, Periodicity, DateTime.MinValue, false); // // collecting all tickers // int stockCount = 0; StringCollection tickersInDatabase = new StringCollection(); LogAndMessage.Log(MessageType.Info, "Manual backfill of all tickers from " + refreshStartDate.ToShortDateString() + "."); try { Type abAppType = Type.GetTypeFromProgID("Broker.Application"); object abApp = Activator.CreateInstance(abAppType); // access AB COM interface to get current ticker if (abAppType != null && abApp != null) { object abStocks = abAppType.InvokeMember("Stocks", BindingFlags.GetProperty, null, abApp, null); Type abStocksType = abStocks.GetType(); // get the number of stocks in the db stockCount = (int)abStocksType.InvokeMember("Count", BindingFlags.GetProperty, null, abStocks, null); if (stockCount > 0) { Type abStockType = abStocksType.InvokeMember("Item", BindingFlags.GetProperty, null, abStocks, new object[] { 0 }).GetType(); for (int i = 0; i < stockCount; i++) { object abStock = abStocksType.InvokeMember("Item", BindingFlags.GetProperty, null, abStocks, new object[] { i }); if (abStock != null) { string ticker = (string)abStockType.InvokeMember("Ticker", BindingFlags.GetProperty, null, abStock, null); if (!tickersInDatabase.Contains(ticker)) { tickersInDatabase.Add(ticker); } } } } else { LogAndMessage.Log(MessageType.Trace, "Manual backfill of all symbols failed. Database has no symbols."); } } else { LogAndMessage.Log(MessageType.Warning, "Manual backfill of all symbols failed. ActiveX interface error."); } } catch (Exception ex) { LogAndMessage.LogAndQueue(MessageType.Error, "Manual backfill of all symbols failed. Exception: " + ex); } StartBackfills(refreshStartDate, tickersInDatabase); }
private void mBackfillWL_Click(object sender, EventArgs e) { if (firstGetQuotesExCall) // db periodicity not known yet { return; } if (string.IsNullOrEmpty(currentTicker)) // no selected ticker { return; } DateTime refreshStartDate = GetRefreshStartDate(sender); refreshStartDate = IBClientHelper.GetAdjustedStartDate(refreshStartDate, Periodicity, DateTime.MinValue, false); WatchlistForm watchlistForm = new WatchlistForm(DatabasePath); if (DialogResult.OK != watchlistForm.ShowDialog() || watchlistForm.SelectedWatchlistIndices == null) { return; } int[] selectedWatchlistIndices = watchlistForm.SelectedWatchlistIndices; watchlistForm.Dispose(); StringCollection tickersInWatchlists = new StringCollection(); ulong watchlistBits = 0; foreach (int watchlistId in selectedWatchlistIndices) { watchlistBits |= (ulong)1 << watchlistId; } LogAndMessage.Log(MessageType.Info, "Manual backfill of watchlists (" + watchlistBits.ToString("0x") + ") from " + refreshStartDate.ToShortDateString() + "."); try { Type abAppType = Type.GetTypeFromProgID("Broker.Application"); object abApp = Activator.CreateInstance(abAppType); // access AB COM interface to get current ticker if (abAppType != null && abApp != null) { object abStocks = abAppType.InvokeMember("Stocks", BindingFlags.GetProperty, null, abApp, null); Type abStocksType = abStocks.GetType(); // get the number of stocks in the db int stockCount = (int)abStocksType.InvokeMember("Count", BindingFlags.GetProperty, null, abStocks, null); if (stockCount > 0) { Type abStockType = abStocksType.InvokeMember("Item", BindingFlags.GetProperty, null, abStocks, new object[] { 0 }).GetType(); for (int i = 0; i < stockCount; i++) { object abStock = abStocksType.InvokeMember("Item", BindingFlags.GetProperty, null, abStocks, new object[] { i }); if (abStock != null) { string ticker = (string)abStockType.InvokeMember("Ticker", BindingFlags.GetProperty, null, abStock, null); uint watchlistBits1 = (uint)(int)abStockType.InvokeMember("WatchListBits", BindingFlags.GetProperty, null, abStock, null); uint watchlistBits2 = (uint)(int)abStockType.InvokeMember("WatchListBits2", BindingFlags.GetProperty, null, abStock, null); ulong watchlistBitsCombined = (watchlistBits2 << 32) + watchlistBits1; if ((watchlistBitsCombined & watchlistBits) != 0) { if (!tickersInWatchlists.Contains(ticker)) { tickersInWatchlists.Add(ticker); } } } } } else { LogAndMessage.Log(MessageType.Trace, "Manual backfill of watchlists failed. Database has no symbols."); } } else { LogAndMessage.Log(MessageType.Warning, "Manual backfill of watchlists failed. ActiveX interface error."); } } catch (Exception ex) { LogAndMessage.LogAndQueue(MessageType.Error, "Manual backfill of watchlists failed. Exception: " + ex); } if (tickersInWatchlists.Count == 0) { LogAndMessage.Log(MessageType.Trace, "Manual backfill of watchlists failed. Selected watchlists have no symbols."); } else { StartBackfills(refreshStartDate, tickersInWatchlists); } }
private void StartBackfills(DateTime refreshStartDate, StringCollection tickersToBackfill) { if (tickersToBackfill.Count == 0) { return; } // // checking if long download is accepted // int stepSize = IBClientHelper.GetDownloadStep(Periodicity); TimeSpan ts = DateTime.Now.Subtract(refreshStartDate); int requestsNo = (int)Math.Ceiling(ts.TotalMinutes / stepSize * tickersToBackfill.Count); // if mixed database, add number of EOD requests requestsNo = (Periodicity != Periodicity.EndOfDay && Workspace.AllowMixedEODIntra != 0) ? requestsNo + tickersToBackfill.Count : requestsNo; // // checking if database can accomodate the data // // quoteNum is aproximate value int quoteNum = (int)(ts.TotalSeconds / (int)Periodicity); // total number of bars if traded 24 hours if (Periodicity != Periodicity.EndOfDay) { if (IBConfiguration.RthOnly) { quoteNum /= 3; } } // // building and showing warning // bool tooManyRequest = requestsNo > 3; bool tooManyQuotes = quoteNum / 1.2 > Workspace.NumBars; if (tooManyRequest || tooManyQuotes) { TimeSpan mts = new TimeSpan(0, 0, requestsNo * 10); StringBuilder msg = new StringBuilder(500); msg.Append("The requested data refresh"); if (tooManyRequest) { msg.Append(" may download more bar data (~"); msg.Append((quoteNum / 1.2).ToString("N0")); msg.Append(") than your database can accomodate ("); msg.Append(Workspace.NumBars.ToString()); msg.Append(")"); } if (tooManyRequest) { if (tooManyRequest) { msg.Append(" and it"); } msg.Append(" may start a long data download operation (~"); msg.Append(mts); msg.Append(")"); } msg.AppendLine("."); msg.AppendLine("Do you still want it?"); if (MessageBox.Show(msg.ToString(), "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.No) { return; } } // // start backfills of selected length // foreach (var ticker in tickersToBackfill) { if (!controller.RefreshTicker(ticker, refreshStartDate)) { LogAndMessage.LogAndQueue(MessageType.Warning, ticker + ": Cannot start manual backfill because ticker is being backfilled."); } } }
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); } } }