// callback (from controller) to indicate no more contracts private void SearchContractReady() { try { // limit result list if (securityDatas.Count > maxDisplayedContract) { securityDatas.RemoveRange(maxDisplayedContract, securityDatas.Count - maxDisplayedContract); } // InvokeRequired did not always work! May be called back on UI thread as well. // However, there can be a lengthy operation to do it on background thread. sorting = true; if (securityDatas.Count > 1) { securityDatas.Sort(new SecurityDataComparer(SecurityDataField.LocalSymbol, SortOrder.Ascending)); } // if window is not shown, we cannot invoke a method through window message loop if (IsHandleCreated) { BeginInvoke(new MethodInvoker(ShowContracts)); } } catch (Exception ex) { LogAndMessage.Log(MessageType.Error, "Search form could not collect contracts: " + ex); MessageBox.Show("Error while retrieving contracts:" + ex, "Search form error", MessageBoxButtons.OK, MessageBoxIcon.Stop); } }
internal void HeadTimestampReceived(string headTimestamp) { DateTime date; bool result = DateTime.TryParseExact(headTimestamp, "yyyyMMdd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out date); lock (TickerData) // event handling { if (result) { TickerData.EarliestDataPoint = date; TickerData.HeadTimestampStatus = HeadTimestampStatus.Ok; } else { TickerData.EarliestDataPoint = DateTime.MinValue; TickerData.HeadTimestampStatus = HeadTimestampStatus.Failed; } } if (result) { LogAndMessage.Log(TickerData, MessageType.Info, "Earliest data point value is updated. " + ToString(true, LogAndMessage.VerboseLog)); } else { LogAndMessage.Log(TickerData, MessageType.Error, "Invalid earliest data point value received: " + headTimestamp + " " + ToString(true, LogAndMessage.VerboseLog)); } }
/// <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> /// This method creates a new or gets an existing TickerData object for a ticker /// </summary> /// <param name="ticker"></param> /// <returns></returns> /// <remarks>It looks for registered ticker object first and returns it. If ticker is not registered yet, it registers it and returns the new object. </remarks> internal TickerData RegisterTickerData(string ticker) { lock (mapTickerToTickerData) { TickerData tickerData; if (mapTickerToTickerData.TryGetValue(ticker, out tickerData)) { return(tickerData); } tickerData = new TickerData(); tickerData.Ticker = ticker; mapTickerToTickerData.Add(ticker, tickerData); SymbolParts ftTicker; try { ftTicker = new SymbolParts(ticker); } catch (Exception) { ftTicker = null; tickerData.QuoteDataStatus = QuotationStatus.Failed; LogAndMessage.LogAndQueue(tickerData, MessageType.Warning, "Invalid symbol."); } tickerData.SymbolParts = ftTicker; return(tickerData); } }
/// <summary> /// Convert contract expiry string to DateTime /// </summary> /// <param name="contract"></param> /// <returns></returns> internal static DateTime GetContractExpiryDateTime(Contract contract) { if (contract == null || string.IsNullOrEmpty(contract.LastTradeDateOrContractMonth)) { return(DateTime.MaxValue); } try { if (contract.LastTradeDateOrContractMonth.Length == 8) { return(DateTime.ParseExact(contract.LastTradeDateOrContractMonth, "yyyyMMdd", CultureInfo.InvariantCulture)); } if (contract.LastTradeDateOrContractMonth.Length == 6) { // format: "yyyyMM" int year = int.Parse(contract.LastTradeDateOrContractMonth.Substring(0, 4)); int month = int.Parse(contract.LastTradeDateOrContractMonth.Substring(4, 2)); DateTime lastTradeDay = new DateTime(year, month, 1); lastTradeDay = lastTradeDay.AddMonths(1); lastTradeDay = lastTradeDay.AddDays(-1); // last day of the month return(lastTradeDay); } LogAndMessage.LogAndQueue(MessageType.Error, "Program error. Unknown format of last trade data/contract month:" + contract.LastTradeDateOrContractMonth); } catch { LogAndMessage.LogAndQueue(MessageType.Error, "Program error. Cannot interpret format of last trade data/contract month:" + contract.LastTradeDateOrContractMonth); } return(DateTime.MinValue); }
private void buttonAdd_Click(object sender, EventArgs e) { if (dataGridViewResult.SelectedRows.Count == 0) { return; } try { Type appType = Type.GetTypeFromProgID("Broker.Application"); object abApp = Activator.CreateInstance(appType); object abStocks = appType.InvokeMember("Stocks", System.Reflection.BindingFlags.GetProperty, null, abApp, null); Type stocksType = abStocks.GetType(); for (int i = 0; i < dataGridViewResult.SelectedRows.Count; i++) { SecurityData sd = (SecurityData)dataGridViewResult.SelectedRows[i].DataBoundItem;; SymbolParts ibTicker = new SymbolParts(sd.LocalSymbol, sd.Exchange, sd.SecType, sd.Currency, ""); object stock = stocksType.InvokeMember("Add", System.Reflection.BindingFlags.InvokeMethod, null, abStocks, new object[] { ibTicker.Ticker }); if (stock != null) { Type stockType = stock.GetType(); stockType.InvokeMember("Alias", System.Reflection.BindingFlags.SetProperty, null, stock, new object[] { sd.Symbol }); stockType.InvokeMember("Currency", System.Reflection.BindingFlags.SetProperty, null, stock, new object[] { sd.Currency }); stockType.InvokeMember("FullName", System.Reflection.BindingFlags.SetProperty, null, stock, new object[] { sd.LongName }); stockType.InvokeMember("PointValue", System.Reflection.BindingFlags.SetProperty, null, stock, new object[] { sd.PriceMagnifier }); stockType.InvokeMember("TickSize", System.Reflection.BindingFlags.SetProperty, null, stock, new object[] { sd.MinTick }); stockType.InvokeMember("WebID", System.Reflection.BindingFlags.SetProperty, null, stock, new object[] { sd.ContractId }); } if (checkBoxAddContinuous.Checked && sd.SecType.ToUpper() == "FUT") { string localNamePart = sd.LocalSymbol.Substring(0, sd.LocalSymbol.Length - 2); SymbolParts contTicker = new SymbolParts(localNamePart + "~/" + sd.Symbol, sd.Exchange, sd.SecType, sd.Currency, ""); stock = stocksType.InvokeMember("Add", System.Reflection.BindingFlags.InvokeMethod, null, abStocks, new object[] { contTicker.Ticker }); } } } catch (Exception ex) { LogAndMessage.Log(MessageType.Error, "Search form could not add tickers: " + ex); MessageBox.Show("Error while adding tickers: " + ex, "Search form error", MessageBoxButtons.OK, MessageBoxIcon.Stop); } }
private void mUpdateSymbolInfo_Click(object sender, EventArgs e) { if (currentSI == null) { return; } LogAndMessage.Log(MessageType.Info, currentTicker + ": Symbol info manually updated."); controller.UpdateSymbolInfo(currentTicker); }
private void Listener_MessageReceived(object sender, XDMessageEventArgs e) { if (e.DataGram.Message == "reconnect") { LogAndMessage.Log(MessageType.Info, "Reconencting..."); //manuallyDisconnected = true; //if (controller.IsIbConnected) controller.Disconnect(); controller.Connect(false); } }
private void SaveQuote(Quotation quote) { DateTime quoteDate = (DateTime)quote.DateTime; // get the trading day //DateTime tradingDay = DateTime.MinValue; //tradingDay = tickerData.LiquidHours.GetTradeDate(quoteDate); //if (!IBDataSource.RthOnly && tradingDay == DateTime.MinValue) //tradingDay = tickerData.TradingDays.GetTradeDate(quoteDate); // if no trading day found and RTH only //if (tradingDay == DateTime.MinValue) // if (IBDataSource.RthOnly) // return; // else // tradingDay = DateTime.Now.Date; DateTime tradingDay = tickerData.TradingDays.GetTradeDate(quoteDate); if (tradingDay == DateTime.MinValue) { tradingDay = DateTime.Now.Date; } try { lock (tickerData.Quotes) { // Merge quote into last intraday quote in QuotationList if (tickerData.Quotes.Periodicity != Periodicity.EndOfDay) { tickerData.Quotes.Merge(quote); } // Merge quote into last EOD quote in QuotationList if (tickerData.Quotes.Periodicity == Periodicity.EndOfDay || FTDataSource.AllowMixedEODIntra) { tickerData.Quotes.MergeEod(quote, (AmiDate)tradingDay); } } } catch (Exception ex) { LogAndMessage.Log(tickerData, MessageType.Error, "Error while merging received quote: " + ex); } }
internal virtual bool ProcessQueuedRequests(FTController ibController, bool allowNewRequest, bool writeLog) { int cntAtStart; int cntAtEnd; bool savedAllowNewRequest = allowNewRequest; lock (requestList) { cntAtStart = requestList.Count; for (int i = cntAtStart - 1; i >= 0; i--) { noTimeOuts &= requestList.Values[i].RequestTimeouts == 0; if (requestList.Values[i].IsFinished) { requestList.RemoveAt(i); } } cntAtEnd = requestList.Count; } // we must limit the open requests to 5 for (int i = 0; i < cntAtEnd && i < 5; i++) { allowNewRequest &= requestList.Values[i].Process(ibController, allowNewRequest); } if (cntAtEnd == 0) { noTimeOuts = true; } #if DEBUG if (writeLog) { LogAndMessage.Log(MessageType.Trace, queueName + ": Allow new request: " + (savedAllowNewRequest ? "1" : "0") + "/" + (allowNewRequest ? "1" : "0") + " Requests: " + cntAtStart.ToString("#0") + "/" + cntAtEnd.ToString("#0")); } #endif return(allowNewRequest); }
/// <summary> /// Start processing the list of contract details in the event of ContractDetailsEnd /// </summary> internal void ContractDetailsReceived(List <ContractDetails> list) { bool result = false; lock (TickerData) // event handling { result = ProcessContractDetailsList(list); TickerData.ContractStatus = result ? ContractStatus.Ok : ContractStatus.Failed; } if (result) { LogAndMessage.Log(TickerData, MessageType.Info, "Contract is updated. " + ToString(true, LogAndMessage.VerboseLog)); } else { LogAndMessage.Log(TickerData, MessageType.Error, "Failed to update contract. " + ToString(true, LogAndMessage.VerboseLog)); } }
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); } } }
public static FTConfiguration GetConfigObject(string config) { // if no config string, set default values if (string.IsNullOrEmpty(config) || config.Trim().Length == 0) { return(GetDefaultConfigObject()); } try { XmlSerializer serializer = new XmlSerializer(typeof(FTConfiguration)); Stream stream = new MemoryStream(ASCIIEncoding.Default.GetBytes(config)); return((FTConfiguration)serializer.Deserialize(stream)); } catch (Exception ex) { LogAndMessage.Log(MessageType.Error, "Configuration error:" + ex); return(GetDefaultConfigObject()); } }
private void mFindIBContract_Click(object sender, EventArgs e) { try { if (searchForm == null) { searchForm = new SearchForm(controller); } searchForm.ShowDialog(); PostMessage(MainWindowHandle, 0x001C, new IntPtr(1), new IntPtr(0)); // WM_ACTIVATEAPP = 0x001C PostMessage(MainWindowHandle, 0x0086, new IntPtr(1), new IntPtr(0)); // WM_NCACTIVATE = 0x0086 PostMessage(MainWindowHandle, 0x0006, new IntPtr(1), new IntPtr(0)); // WM_ACTIVATE = 0x0006 PostMessage(MainWindowHandle, 0x36E, new IntPtr(1), new IntPtr(0)); // WM_ACTIVATETOPLEVEL = 0x36E PostMessage(MainWindowHandle, 0x2862, new IntPtr(0xb6d), new IntPtr(0)); //WM_USER + 9314 PostMessage(MainWindowHandle, 0x0111, new IntPtr(0xb6d), new IntPtr(0)); //WM_COMMAND = 0x0111; } catch (Exception ex) { LogAndMessage.Log(MessageType.Error, "Error while opening Search window:" + ex); } }
internal override bool ProcessQueuedRequests(FTController ibController, bool allowNewRequest, bool writeLog) { int cntAtStart; int cntAtEnd; bool savedAllowNewRequest = allowNewRequest; // // we must limit the open requests to 1 // lock (requestList) { cntAtStart = requestList.Count; if (cntAtStart > 0) { if (requestList.Values[0].IsFinished) { requestList.RemoveAt(0); } } cntAtEnd = requestList.Count; } if (cntAtEnd > 0) { allowNewRequest &= requestList.Values[0].Process(ibController, allowNewRequest); } #if DEBUG if (writeLog) { LogAndMessage.Log(MessageType.Trace, queueName + ": Allow new request: " + (savedAllowNewRequest ? "1" : "0") + "/" + (allowNewRequest ? "1" : "0") + " Requests: " + cntAtStart.ToString("#0") + "/" + cntAtEnd.ToString("#0")); } #endif return(allowNewRequest); }
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); }
private void mCancel_Click(object sender, EventArgs e) { LogAndMessage.Log(MessageType.Info, "All backfill operations are manually cancelled."); controller.CancelAllRefreshes(); }
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 void MergePrice(int tickerId, int field, float price, DateTime time) { // // if price is 0 or less (no live data) // if (price <= 0.0f) { return; } decimal p = (decimal)price; // // save BID and ASK prices // if (field == TickType.ASK) { lastAsk = p; } else if (field == TickType.BID) { lastBid = p; } // // calc MID price if data is available // if (lastAsk > 0m && lastBid > 0m) { lastMid = (lastAsk + lastBid) / 2m; } // // check if we need to use this price tick or it is irrelevant // if (tickerData.SymbolParts.DataType == IBHistoricalDataType.Midpoint || tickerData.SymbolParts.DataType == IBHistoricalDataType.BidAsk) { if (field != TickType.BID && field != TickType.ASK && field != TickType.LAST) { return; } } else if (tickerData.SymbolParts.DataType == IBHistoricalDataType.Trades || tickerData.SymbolParts.DataType == IBHistoricalDataType.Adjusted) { if (field != TickType.LAST) { return; } } else if (tickerData.SymbolParts.DataType == IBHistoricalDataType.Bid) { if (field != TickType.BID) { return; } } else if (tickerData.SymbolParts.DataType == IBHistoricalDataType.Ask) { if (field != TickType.ASK) { return; } } else if (tickerData.SymbolParts.DataType == IBHistoricalDataType.HistoricalVolatility) { if (field != TickType.OPTION_HISTORICAL_VOL) { return; } } else if (tickerData.SymbolParts.DataType == IBHistoricalDataType.ImpliedVolatility) { if (field != TickType.OPTION_IMPLIED_VOL) { return; } } else { return; } // // check if tick is in acceptable range // if (filter) { // // filter #1 // // it is a simple filter to remove bad price ticks // it works only during high volume, continuously traded period, fell free to improve it if (lastMid != 0.0m && // there is a midpoint price already (DateTime.Now.Ticks - date.Ticks) / TimeSpan.TicksPerSecond < 5 && // time elapsed since last tick event is less then 5 second Math.Abs(p - lastMid) / lastMid > 0.03m) // price change is greater then 3 % { // This may impose 5 sec delay and loss of some ticks in higly volatily and thin market (not in Forex) LogAndMessage.Log(tickerData, MessageType.Trace, "Bad tick has been rejected. Price:" + price.ToString() + " MidPoint:" + lastMid.ToString()); return; } // // filter #2 // // // // // it is a "round robin" array store // check if index points behind last element of the array if (sampleIdx == MaxSampleNo) { sampleIdx = 0; sampleOk = true; } // if there is enough data yet if (sampleOk) { // calc avg prior to this tick decimal avg = 0; for (int i = 0; i < MaxSampleNo; i++) { avg += sample[i]; } // calc price move compared to avg price decimal rate = avg / MaxSampleNo / p; // if to big, reject tick if (rate > 1.02M || rate < 0.98M) { LogAndMessage.Log(tickerData, MessageType.Trace, "Bad tick has been rejected. Price:" + price.ToString() + " Avg of last " + MaxSampleNo + " ticks:" + (avg / MaxSampleNo).ToString()); return; } // store current price in sample array sample[sampleIdx] = p; sampleIdx++; } // if there is NOT enough data yet else { // store current price in sample array sample[sampleIdx] = p; sampleIdx++; } } // storing the tick into a Quote to merge if (tickerData.SymbolParts.DataType == IBHistoricalDataType.Midpoint) { if (lastMid == 0.0m) { return; } lastPrice = (float)lastMid; } else { lastPrice = price; } date = time; Quotation quote = new Quotation(); quote.DateTime = (AmiDate)date; quote.Price = lastPrice; quote.Low = lastPrice; quote.High = lastPrice; quote.Open = prevPrice != 0 ? prevPrice : lastPrice; quote.Volume = 0; prevPrice = lastPrice; SaveQuote(quote); }
internal override bool Process(FTController ibController, bool allowNewRequest) { const int requestTimeoutPeriod = 10; lock (TickerData) // request handling { // if not waiting for response switch (TickerData.ContractStatus) { // if marked to get contract details case ContractStatus.SendRequest: if (allowNewRequest) { LogAndMessage.Log(TickerData, MessageType.Trace, "Getting contract. " + ToString(false, LogAndMessage.VerboseLog)); Contract contract = new Contract(); contract.Exchange = TickerData.SymbolParts.Exchange; contract.SecType = TickerData.SymbolParts.SecurityType; contract.Currency = TickerData.SymbolParts.Currency; contract.IncludeExpired = true; if (TickerData.SymbolParts.IsContinuous) { if (!string.IsNullOrEmpty(TickerData.SymbolParts.Underlying)) { contract.Symbol = TickerData.SymbolParts.Underlying; } else { contract.Symbol = TickerData.SymbolParts.Symbol; } } else { contract.LocalSymbol = TickerData.SymbolParts.Symbol; } TickerData.contractDetailsList.Clear(); TickerData.ContractStatus = ContractStatus.WaitForResponse; RequestTime = DateTime.Now; ibController.SendContractDetailsRequest(Id, contract); } return(false); // if request is sent, but response has not arrived yet // see ibclient_ContractDetails and ibclient_ContractDetailsEnd event handlers case ContractStatus.WaitForResponse: // if no answer in Time if (RequestTime.AddSeconds(requestTimeoutPeriod) < DateTime.Now) { LogAndMessage.LogAndQueue(TickerData, MessageType.Error, "Getting contract info timed out, symbol is offline. " + ToString(true, LogAndMessage.VerboseLog)); TickerData.ContractStatus = ContractStatus.Failed; goto case ContractStatus.Failed; } return(allowNewRequest); // if new, offline ticker case ContractStatus.Offline: goto case ContractStatus.Failed; // contract found case ContractStatus.Ok: goto case ContractStatus.Failed; // no contract found case ContractStatus.Failed: IsFinished = true; return(allowNewRequest); default: throw new ArgumentOutOfRangeException(); } } }
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); } } }
public override PluginStatus GetStatus() { PluginStatus status = new PluginStatus(); IBPluginState ibPluginState = IBPluginState.Disconnected; if (controller != null) { ibPluginState = controller.GetIBPluginState(); } switch (ibPluginState) { case IBPluginState.Disconnected: status.Status = StatusCode.SevereError; status.Color = System.Drawing.Color.IndianRed; status.ShortMessage = "Off-line"; break; case IBPluginState.Busy: status.Status = StatusCode.OK; status.Color = System.Drawing.Color.Yellow; status.ShortMessage = "Busy"; break; case IBPluginState.Ready: status.Status = StatusCode.OK; status.Color = System.Drawing.Color.ForestGreen; status.ShortMessage = "Ready"; break; } status.LongMessage = LogAndMessage.GetMessages(); // collect and display data issues string failedTickers = controller.GetFailedTickers(); bool dataError = !string.IsNullOrEmpty(failedTickers); if (dataError && ibPluginState != IBPluginState.Disconnected) // if disconnected, data issues are meaningless... { status.ShortMessage += " !"; status.Status = StatusCode.Warning; if (string.IsNullOrEmpty(status.LongMessage)) { status.LongMessage = "Failed tickers: " + failedTickers; } } // if there is no message, we show the short message (Busy, Ok, etc) if (string.IsNullOrEmpty(status.LongMessage)) { status.LongMessage = status.ShortMessage; // save as last shown message to avoid status popup lastLongMessageText = status.ShortMessage; } // if new message we use a new lastLongMessageTime value to cause status popup if (lastLongMessageText != status.LongMessage) { lastLongMessageText = status.LongMessage; lastLongMessageTime = (int)DateTime.Now.TimeOfDay.TotalMilliseconds; } // set status and "timestamp" status.Status = (StatusCode)((int)status.Status + lastLongMessageTime); return(status); }
/// <summary> /// Process the list of contract details received from TWS, and selects the current front month contract /// </summary> /// <param name="TickerData"></param> /// <returns></returns> private bool ProcessContractDetailsList(List <ContractDetails> list) { try { TickerData.contractDetailsList = list; // if no contract found if (TickerData.contractDetailsList.Count == 0) { return(false); } // not a continuos contract there must be exactly 1 found contract if (TickerData.contractDetailsList.Count == 1 && !TickerData.SymbolParts.IsContinuous) { TickerData.ContractDetails = TickerData.contractDetailsList[0]; return(true); } // continuos contract if (TickerData.SymbolParts.IsContinuous) { // // only expired and the nearest expiration may remain in the list // // sort contract details on expiry TickerData.contractDetailsList.Sort(new ContractDetailsComparer()); // current date in the format of contract expiry string frontMonthExpiry = DateTime.Now.ToString("yyyyMMdd"); // this may be an already expired contract or a contract that will expire in the far future ContractDetails temp = TickerData.contractDetailsList[0]; // find the contract that ... for (int i = 1; i < TickerData.contractDetailsList.Count; i++) { if (TickerData.contractDetailsList[i].Contract.LastTradeDateOrContractMonth.CompareTo(frontMonthExpiry) >= 0) // expires in the future or today { temp = TickerData.contractDetailsList[i]; break; } } // setting the found CURRENT (front month) contract as the contractdetails TickerData.ContractDetails = temp; frontMonthExpiry = temp.Contract.LastTradeDateOrContractMonth; // remove all future contract with later expiry then current front month for (int i = TickerData.contractDetailsList.Count - 1; i >= 0; i--) { if (TickerData.contractDetailsList[i].Contract.LastTradeDateOrContractMonth.CompareTo(frontMonthExpiry) > 0) { TickerData.contractDetailsList.RemoveAt(i); } } return(true); } return(false); } finally { try { // update trading days using current contract details if (TickerData.ContractDetails != null) { //tickerData.LiquidHours = new TradingDayList(tickerData.ContractDetails.LiquidHours, true); //if (IBDataSource.RthOnly && string.IsNullOrEmpty(tickerData.ContractDetails.LiquidHours)) // LogAndMessage.LogAndQueue(tickerData, MessageType.Warning, "No liquid hours data is available."); //else // LogAndMessage.Log(tickerData, MessageType.Trace, "Liquid hour:" + tickerData.ContractDetails.LiquidHours); TickerData.TradingDays = new TradingDayList(TickerData.ContractDetails.TradingHours, false); if ((FTDataSource.AllowMixedEODIntra || FTDataSource.Periodicity == Periodicity.EndOfDay) && string.IsNullOrEmpty(TickerData.ContractDetails.TradingHours)) { LogAndMessage.Log(TickerData, MessageType.Warning, "No trading hours data is available. Daily quotation data may not be correct."); } //else // LogAndMessage.Log(tickerData, MessageType.Trace, "Trading hour:" + tickerData.ContractDetails.TradingHours); } else { //tickerData.LiquidHours = new TradingDayList(null, true); TickerData.TradingDays = new TradingDayList(null, false); } } catch (Exception e) { LogAndMessage.Log(TickerData, MessageType.Error, "Failed to parse (" + TickerData.ContractDetails.TradingHours + ") and update trading hours:" + e.ToString()); } } }
/// <summary> /// Timer event handler /// </summary> /// <param name="sender"></param> /// <remarks> /// If connection is broken, it tries to reconnect in every 30 secs. /// If it reconnects, it starts a backfill of all tickers /// If status is ready and autorefresh is configured, it starts the scheduled refresh /// </remarks> private void timer_Tick(object sender) { if (controller == null) { return; } // check and indicate thread entry if (Interlocked.CompareExchange(ref inTimerTick, 1, 0) != 0) { return; } IBPluginState currPluginState = IBPluginState.Disconnected; try { currPluginState = controller.GetIBPluginState(); if (currPluginState != prevPluginState) { LogAndMessage.Log(MessageType.Info, "Plugin status: " + currPluginState); } // if no connection between the data plugin and the TWS client if (currPluginState == IBPluginState.Disconnected) { // if not manually disconnected, try to reconnect if (manuallyDisconnected == false) { // if retry period has elapsed if (connectionRetryTime < DateTime.Now) { // if connection has just got broken (prevent repeated log entries) if (prevPluginState != IBPluginState.Disconnected) { LogAndMessage.LogAndQueue(MessageType.Warning, "Data plugin has been disconnected from TWS. Trying to reconnect in every " + ConnectionRetryInterval + " sec."); } // set new retry time and increase interval up to 30 secs connectionRetryTime = DateTime.Now.AddSeconds(ConnectionRetryInterval); // try to reconnect controller.Connect(true); } } } // data plugin is connected to TWS else { // reset connection retrying connectionRetryTime = DateTime.MinValue; // if an existing connection was disconnected if (prevPluginState == IBPluginState.Disconnected && !firstConnection || controller.RestartStreaming) { LogAndMessage.LogAndQueue(MessageType.Warning, "TWS has been reconnected. Starting database refresh."); // request refresh of all tickers, restart subscriptions, etc. controller.RestartAfterReconnect(lastUptodateTime); return; } // clear flag of first connection firstConnection = false; // check if it's time to run AutoRefresh if (prevPluginState == IBPluginState.Ready) // Plugin works with up to date data { lastUptodateTime = DateTime.Now; if (IBConfiguration != null && IBConfiguration.AutoRefreshEnabled && // Auto refresh is enabled nextAutoRefreshTime < DateTime.Now) // The time of auto refresh has passed { DateTime refreshStartDate = DateTime.Now.Date.AddDays(-IBConfiguration.AutoRefreshDays); LogAndMessage.LogAndQueue(MessageType.Warning, "Starting automatic database refresh (" + refreshStartDate.ToShortDateString() + ")."); controller.RefreshAllUsed(refreshStartDate); CalcNextAutoRefreshTime(); } } } } catch (Exception ex) { LogAndMessage.Log(MessageType.Error, "Error in data source timer event handler: " + ex); } finally { // update plugin state prevPluginState = currPluginState; // indicate thread exit Interlocked.Exchange(ref inTimerTick, 0); } }
internal override bool Process(FTController ibController, bool allowNewRequest) { // if no contract received yet if (TickerData.ContractStatus == ContractStatus.SendRequest || TickerData.ContractStatus == ContractStatus.WaitForResponse) { return(allowNewRequest); } // if GetQuotesEx was not yet called (stockInfo is not ready yet), symbol data cannot be updated if (TickerData.StockInfo == null) { IsFinished = true; return(allowNewRequest); } // if not waiting for response switch (TickerData.SymbolStatus) { // if marked to update AB symbol's data case SymbolStatus.WaitForContractUpdate: // if no contract found if (TickerData.ContractStatus != ContractStatus.Ok) { LogAndMessage.LogAndQueue(TickerData, MessageType.Error, "Getting contract info failed, AmiBroker symbol data cannot be updated."); TickerData.SymbolStatus = SymbolStatus.Failed; goto case SymbolStatus.Failed; } // plugin may call it when StockInfo is not available (E.g. start watchlist backfill) if (TickerData.StockInfo == null) { LogAndMessage.Log(TickerData, MessageType.Trace, "StockInfo data is not available. AmiBroker symbol data is not updated."); return(false); } try { // update AB's information TickerData.StockInfo.AliasName = TickerData.ContractDetails.Contract.LocalSymbol + '/' + TickerData.ContractDetails.Contract.Symbol; TickerData.StockInfo.FullName = TickerData.ContractDetails.LongName; TickerData.StockInfo.PointValue = TickerData.ContractDetails.PriceMagnifier; TickerData.StockInfo.TickSize = (float)TickerData.ContractDetails.MinTick; TickerData.StockInfo.WebId = TickerData.ContractDetails.Contract.ConId.ToString(); TickerData.StockInfo.Currency = TickerData.ContractDetails.Contract.Currency; TickerData.SymbolStatus = SymbolStatus.Ok; LogAndMessage.LogAndQueue(TickerData, MessageType.Info, "AmiBroker symbol data is updated."); } catch (Exception ex) { TickerData.SymbolStatus = SymbolStatus.Failed; LogAndMessage.LogAndQueue(TickerData, MessageType.Error, "AmiBroker symbol data update failed:" + ex); return(false); } goto case SymbolStatus.Ok; // if new ticker case SymbolStatus.Offline: // AB symbol's data are updated case SymbolStatus.Ok: // AB symbol's data NOT updated, e.g: no contract found case SymbolStatus.Failed: IsFinished = true; return(allowNewRequest); default: TickerData.SymbolStatus = SymbolStatus.Failed; IsFinished = true; return(allowNewRequest); } }
public override bool Notify(ref PluginNotification notifyData) { bool result = true; switch (notifyData.Reason) { case Reason.DatabaseLoaded: // if database is loaded if (controller != null) { // disconnect from TWS and reset all data controller.Disconnect(); ((IDisposable)controller).Dispose(); controller = null; } Workspace = notifyData.Workspace; DatabasePath = notifyData.DatabasePath; MainWindowHandle = notifyData.MainWnd; AllowMixedEODIntra = Workspace.AllowMixedEODIntra != 0; // start logging the opening of the database LogAndMessage.Log(MessageType.Info, "Database: " + DatabasePath); LogAndMessage.Log(MessageType.Info, "Mixed EOD/Intra: " + (Workspace.AllowMixedEODIntra != 0)); LogAndMessage.Log(MessageType.Info, "Number of bars: " + Workspace.NumBars); LogAndMessage.Log(MessageType.Info, "Database config: " + Settings); // create the config object IBConfiguration = FTConfiguration.GetConfigObject(Settings); LogAndMessage.VerboseLog = IBConfiguration.VerboseLog; RthOnly = IBConfiguration.RthOnly; CalcNextAutoRefreshTime(); // create new controller connectionRetryTime = DateTime.Now.AddSeconds(ConnectionRetryInterval); prevPluginState = IBPluginState.Disconnected; firstConnection = true; controller = new FTController(); // connect database to tws controller.Connect(false); if (rtWindowTickersBck.Count > 0) { for (int i = 0; i < rtWindowTickersBck.Count; i++) { controller.GetRecentInfo(rtWindowTickersBck[i]); } } break; // user changed the db case Reason.DatabaseUnloaded: // disconnect from TWS if (controller != null) { controller.Disconnect(); ((IDisposable)controller).Dispose(); controller = null; } // clean up Workspace = new Workspace(); DatabasePath = null; MainWindowHandle = IntPtr.Zero; searchForm = null; break; // seams to be obsolete case Reason.SettingsChanged: break; // user right clicks data plugin area in AB case Reason.RightMouseClick: if (controller != null) { currentSI = notifyData.CurrentSI; if (currentSI != null) { currentTicker = currentSI.ShortName; if (currentTicker.Length > 10) { currentTickerShortend = currentTicker.Substring(0, 7) + "..."; } else { currentTickerShortend = currentTicker; } } else { currentTicker = null; currentTickerShortend = null; } } SetContextMenuState(); ShowContextMenu(mContextMenu); break; default: result = false; break; } return(result); }
private void mReconnect_Click(object sender, EventArgs e) { LogAndMessage.Log(MessageType.Info, "TWS is manually reconnected."); manuallyDisconnected = false; controller.Connect(false); }
private void mDisconnect_Click(object sender, EventArgs e) { LogAndMessage.Log(MessageType.Info, "TWS is manually disconnected."); manuallyDisconnected = true; controller.Disconnect(); }
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); } }