public IBSource(IMessageBus ether, string hostname, int port, int clientId, string accountName, bool useVarBars, int barLength) { Ether = ether; Hostname = hostname; Port = port; ClientId = clientId; AccountName = accountName; UseVarBars = useVarBars; BarLength = barLength; if (BarLength < Ether.EpochSecs) { throw new Exception(string.Format("Error, TWSSource bar length ({0}) is less than the Ether's epoch interval ({1})!", BarLength, ether.EpochSecs)); } // Set synch context for IBClient SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); IsConnected = false; Signal = new IB.EReaderMonitorSignal(); Client = new IBClient(Signal); // Contract details, currently just market hours. Client.ContractDetails += ContractDetailsHandler; Client.ContractDetailsEnd += (reqId => Debug.WriteLine(reqId)); // Tick data. Client.TickPrice += TickPriceHandler; Client.TickSize += TickSizeHandler; Client.TickGeneric += TickGenericHandler; Client.TickString += TickStringHandler; Client.TickOptionCommunication += TickOptionHandler; Client.TickEFP += EFPDataHandler; // Historical data. Client.HistoricalData += HistoricalDataHandler; Client.HistoricalDataUpdate += HistoricalDataUpdateHandler; Client.HistoricalDataEnd += HistoricalDataEndHandler; // Account and portfolio updates. Client.UpdateAccountTime += AccountTimeHandler; Client.UpdateAccountValue += AccountValueHandler; Client.UpdatePortfolio += PortfolioInfoHandler; // Errors. Client.Error += ErrorHandler; Client.ConnectionClosed += ConnectionClosedHandler; // Server time. Client.CurrentTime += CurrentTimeHandler; // Instance variables. Contracts = new Dictionary <int, Tuple <Contract, Market> >(); Bars = new Dictionary <int, IBarList>(); AccountInfo = new AccountInfo(AccountName); // Daily things-to-do. Ether.AsObservable <Heartbeat>().Where(x => x.IsHourly()).Subscribe(x => { // At 5AM each day, we request contract details so we can see when the contracts are traded. foreach (var c in Contracts.Keys) { RequestContractDetails(c * 9999, Contracts[c].Item1); } }, e => { Debug.WriteLine(e.ToString()); }); }
/// <summary> /// Connects the client to the IB gateway /// </summary> public override void Connect() { if (IsConnected) return; // we're going to receive fresh values for both of these collections, so clear them _accountHoldings.Clear(); _accountProperties.Clear(); var attempt = 1; const int maxAttempts = 65; while (true) { try { Log.Trace("InteractiveBrokersBrokerage.Connect(): Attempting to connect ({0}/{1}) ...", attempt, maxAttempts); // we're going to try and connect several times, if successful break _client.ClientSocket.eConnect(_host, _port, _clientId); // create the message processing thread var signal = new EReaderMonitorSignal(); var reader = new EReader(_client.ClientSocket, signal); reader.Start(); var messageProcessingThread = new Thread(() => { Log.Trace("IB message processing thread started."); while (_client.ClientSocket.IsConnected()) { try { signal.waitForSignal(); reader.processMsgs(); } catch (Exception error) { // error in message processing thread, log error and disconnect Log.Error("Error in message processing thread: " + error); } } Log.Trace("IB message processing thread ended."); }) { IsBackground = true }; messageProcessingThread.Start(); // pause for a moment to receive next valid ID message from gateway if (!_waitForNextValidId.WaitOne(15000)) { Log.Trace("InteractiveBrokersBrokerage.Connect(): Operation took longer than 15 seconds."); // no response, disconnect and retry _client.ClientSocket.eDisconnect(); messageProcessingThread.Join(); // max out at 65 attempts to connect ~1 minute if (attempt++ < maxAttempts) { Thread.Sleep(1000); continue; } throw new TimeoutException("InteractiveBrokersBrokerage.Connect(): Operation took longer than 15 seconds."); } Log.Trace("IB next valid id received."); if (!_client.Connected) throw new Exception("InteractiveBrokersBrokerage.Connect(): Connection returned but was not in connected state."); break; } catch (Exception err) { // max out at 65 attempts to connect ~1 minute if (attempt++ < maxAttempts) { Thread.Sleep(1000); continue; } // we couldn't connect after several attempts, log the error and throw an exception Log.Error(err); // add a blurb about TWS for connection refused errors if (err.Message.Contains("Connection refused")) { throw new Exception(err.Message + ". Be sure to logout of Trader Workstation. " + "IB only allows one active log in at a time. " + "This can also be caused by requiring two-factor authentication. " + "Be sure to disable this in IB Account Management > Security > SLS Opt out.", err); } throw; } } // define our event handler, this acts as stop to make sure when we leave Connect we have downloaded the full account EventHandler<IB.AccountDownloadEndEventArgs> clientOnAccountDownloadEnd = (sender, args) => { Log.Trace("InteractiveBrokersBrokerage.AccountDownloadEnd(): Finished account download for " + args.Account); _accountHoldingsResetEvent.Set(); }; _client.AccountDownloadEnd += clientOnAccountDownloadEnd; // we'll wait to get our first account update, we need to be absolutely sure we // have downloaded the entire account before leaving this function var firstAccountUpdateReceived = new ManualResetEvent(false); EventHandler<IB.UpdateAccountValueEventArgs> clientOnUpdateAccountValue = (sender, args) => { firstAccountUpdateReceived.Set(); }; _client.UpdateAccountValue += clientOnUpdateAccountValue; // first we won't subscribe, wait for this to finish, below we'll subscribe for continuous updates _client.ClientSocket.reqAccountUpdates(true, _account); // wait to see the first account value update firstAccountUpdateReceived.WaitOne(2500); // take pause to ensure the account is downloaded before continuing, this was added because running in // linux there appears to be different behavior where the account download end fires immediately. Thread.Sleep(2500); if (!_accountHoldingsResetEvent.WaitOne(15000)) { throw new TimeoutException("InteractiveBrokersBrokerage.GetAccountHoldings(): Operation took longer than 15 seconds."); } // remove our end handler _client.AccountDownloadEnd -= clientOnAccountDownloadEnd; _client.UpdateAccountValue -= clientOnUpdateAccountValue; }