Ejemplo n.º 1
0
        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.");
                }
            }
        }
Ejemplo n.º 2
0
        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);
                }
            }
        }