Пример #1
0
        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;
        }