/// <summary>
 /// Creates a new InteractiveBrokersBrokerage from the specified values
 /// </summary>
 /// <param name="account">The Interactive Brokers account name</param>
 /// <param name="host">host name or IP address of the machine where TWS is running. Leave blank to connect to the local host.</param>
 /// <param name="port">must match the port specified in TWS on the Configure&gt;API&gt;Socket Port field.</param>
 /// <param name="agentDescription">Used for Rule 80A describes the type of trader.</param>
 public InteractiveBrokersBrokerage(string account, string host, int port, IB.AgentDescription agentDescription = IB.AgentDescription.Individual)
     : base("Interactive Brokers Brokerage")
 {
     _account = account;
     _host = host;
     _port = port;
     _clientID = Interlocked.Increment(ref _nextClientID);
     _agentDescription = agentDescription;
     _client = new IB.IBClient();
 }
        /// <summary>
        /// Maps the IB Contract's symbol to a QC symbol
        /// </summary>
        private static Symbol MapSymbol(IB.Contract contract)
        {
            var securityType = ConvertSecurityType(contract.SecurityType);
            if (securityType == SecurityType.Forex)
            {
                // reformat for QC
                var symbol = contract.Symbol + contract.Currency;
                return new Symbol(SecurityIdentifier.GenerateForex(symbol, Market.FXCM), symbol);
            }
            if (securityType == SecurityType.Equity)
            {
                return new Symbol(SecurityIdentifier.GenerateEquity(contract.Symbol, Market.USA), contract.Symbol);
            }

            throw new NotImplementedException("The specified security type has not been implemented: " + securityType);
        }
        /// <summary>
        /// Converts a QC order to an IB order
        /// </summary>
        private IB.Order ConvertOrder(Order order, IB.Contract contract, int ibOrderID)
        {
            var ibOrder = new IB.Order
            {
                ClientId = _clientID,
                OrderId = ibOrderID,
                Account = _account,
                Action = ConvertOrderDirection(order.Direction),
                TotalQuantity = Math.Abs(order.Quantity),
                OrderType = ConvertOrderType(order.Type),
                AllOrNone = false,
                Tif = IB.TimeInForce.GoodTillCancel,
                Transmit = true,
                Rule80A = _agentDescription
            };

            if (order.Type == OrderType.MarketOnOpen)
            {
                ibOrder.Tif = IB.TimeInForce.MarketOnOpen;
            }

            var limitOrder = order as LimitOrder;
            var stopMarketOrder = order as StopMarketOrder;
            var stopLimitOrder = order as StopLimitOrder;
            if (limitOrder != null)
            {
                ibOrder.LimitPrice = RoundPrice(limitOrder.LimitPrice, GetMinTick(contract));
            }
            else if (stopMarketOrder != null)
            {
                ibOrder.AuxPrice = RoundPrice(stopMarketOrder.StopPrice, GetMinTick(contract));
            }
            else if (stopLimitOrder != null)
            {
                var minTick = GetMinTick(contract);
                ibOrder.LimitPrice = RoundPrice(stopLimitOrder.LimitPrice, minTick);
                ibOrder.AuxPrice = RoundPrice(stopLimitOrder.StopPrice, minTick);
            }

            // not yet supported
            //ibOrder.ParentId = 
            //ibOrder.OcaGroup =

            return ibOrder;
        }
        /// <summary>
        /// Handle order events from IB
        /// </summary>
        private void HandleOrderStatusUpdates(object sender, IB.OrderStatusEventArgs update)
        {
            try
            {
                var order = _orderProvider.GetOrderByBrokerageId(update.OrderId);
                if (order == null)
                {
                    Log.Error("InteractiveBrokersBrokerage.HandleOrderStatusUpdates(): Unable to locate order with BrokerageID " + update.OrderId);
                    return;
                }


                var status = ConvertOrderStatus(update.Status);
                if (order.Status == OrderStatus.Filled && update.Filled == 0 && update.Remaining == 0)
                {
                    // we're done with this order, remove from our state
                    int value;
                    _orderFills.TryRemove(order.Id, out value);
                }

                var orderFee = 0m;
                int filledThisTime;
                lock (_orderFillsLock)
                {
                    // lock since we're getting and updating in multiple operations
                    var currentFilled = _orderFills.GetOrAdd(order.Id, 0);
                    if (currentFilled == 0)
                    {
                        // apply order fees on the first fill event TODO: What about partial filled orders that get cancelled?
                        var security = _securityProvider.GetSecurity(order.Symbol);
                        orderFee = security.FeeModel.GetOrderFee(security, order);
                    }
                    filledThisTime = update.Filled - currentFilled;
                    _orderFills.AddOrUpdate(order.Id, currentFilled, (sym, filled) => update.Filled);
                }

                if (status == OrderStatus.Invalid)
                {
                    Log.Error("InteractiveBrokersBrokerage.HandleOrderStatusUpdates(): ERROR -- " + update.OrderId);
                }

                // set status based on filled this time
                if (filledThisTime != 0)
                {
                    status = update.Remaining != 0 ? OrderStatus.PartiallyFilled : OrderStatus.Filled;
                }
                // don't send empty fill events
                else if (status == OrderStatus.PartiallyFilled || status == OrderStatus.Filled)
                {
                    Log.Trace("InteractiveBrokersBrokerage.HandleOrderStatusUpdates(): Ignored zero fill event: OrderId: " + update.OrderId + " Remaining: " + update.Remaining);
                    return;
                }

                // mark sells as negative quantities
                var fillQuantity = order.Direction == OrderDirection.Buy ? filledThisTime : -filledThisTime;
                order.PriceCurrency = _securityProvider.GetSecurity(order.Symbol).SymbolProperties.QuoteCurrency;
                var orderEvent = new OrderEvent(order, DateTime.UtcNow, orderFee, "Interactive Brokers Fill Event")
                {
                    Status = status,
                    FillPrice = update.LastFillPrice,
                    FillQuantity = fillQuantity
                };
                if (update.Remaining != 0)
                {
                    orderEvent.Message += " - " + update.Remaining + " remaining";
                }

                // if we're able to add to our fixed length, unique queue then send the event
                // otherwise it is a duplicate, so skip it
                if (_recentOrderEvents.Add(orderEvent.ToString() + update.Remaining))
                {
                    OnOrderEvent(orderEvent);
                }
            }
            catch(InvalidOperationException err)
            {
                Log.Error("InteractiveBrokersBrokerage.HandleOrderStatusUpdates(): Unable to resolve executions for BrokerageID: " + update.OrderId + " - " + err);
            }
            catch (Exception err)
            {
                Log.Error("InteractiveBrokersBrokerage.HandleOrderStatusUpdates(): " + err);
            }
        }
        /// <summary>
        /// Handles error messages from IB
        /// </summary>
        private void HandleError(object sender, IB.ErrorEventArgs e)
        {
            // https://www.interactivebrokers.com/en/software/api/apiguide/tables/api_message_codes.htm

            // rewrite these messages to be single lined
            e.ErrorMsg = e.ErrorMsg.Replace("\r\n", ". ").Replace("\r", ". ").Replace("\n", ". ");
            Log.Trace(string.Format("InteractiveBrokersBrokerage.HandleError(): Order: {0} ErrorCode: {1} - {2}", e.TickerId, e.ErrorCode, e.ErrorMsg));

            // figure out the message type based on our code collections below
            var brokerageMessageType = BrokerageMessageType.Information;
            if (ErrorCodes.Contains((int) e.ErrorCode))
            {
                brokerageMessageType = BrokerageMessageType.Error;
            }
            else if (WarningCodes.Contains((int) e.ErrorCode))
            {
                brokerageMessageType = BrokerageMessageType.Warning;
            }

            // code 1100 is a connection failure, we'll wait a minute before exploding gracefully
            if ((int) e.ErrorCode == 1100 && !_disconnected1100Fired)
            {
                _disconnected1100Fired = true;

                // begin the try wait logic
                TryWaitForReconnect();
            }
            else if ((int) e.ErrorCode == 1102)
            {
                // we've reconnected
                _disconnected1100Fired = false;
                OnMessage(BrokerageMessageEvent.Reconnected(e.ErrorMsg));
            }

            if (InvalidatingCodes.Contains((int)e.ErrorCode))
            {
                Log.Trace(string.Format("InteractiveBrokersBrokerage.HandleError.InvalidateOrder(): Order: {0} ErrorCode: {1} - {2}", e.TickerId, e.ErrorCode, e.ErrorMsg));

                // invalidate the order
                var order = _orderProvider.GetOrderByBrokerageId(e.TickerId);
                const int orderFee = 0;
                var orderEvent = new OrderEvent(order, DateTime.UtcNow, orderFee) { Status = OrderStatus.Invalid };
                OnOrderEvent(orderEvent);
            }

            OnMessage(new BrokerageMessageEvent(brokerageMessageType, (int) e.ErrorCode, e.ErrorMsg));
        }
        private decimal GetMinTick(IB.Contract contract)
        {
            IB.ContractDetails details;
            if (_contractDetails.TryGetValue(contract.Symbol, out details))
            {
                return (decimal) details.MinTick;
            }

            details = GetContractDetails(contract);
            if (details == null)
            {
                // we were unable to find the contract details
                return 0;
            }

            return (decimal) details.MinTick;
        }
        /// <summary>
        /// Gets the execution details matching the filter
        /// </summary>
        /// <returns>A list of executions matching the filter</returns>
        public List<IB.ExecDetailsEventArgs> GetExecutions(string symbol, IB.SecurityType? type, string exchange, DateTime? timeSince, IB.ActionSide? side)
        {
            var filter = new IB.ExecutionFilter
            {
                AcctCode = _account,
                ClientId = _clientID,
                Exchange = exchange,
                SecurityType = type ?? IB.SecurityType.Undefined,
                Symbol = symbol,
                Time = timeSince ?? DateTime.MinValue,
                Side = side ?? IB.ActionSide.Undefined
            };

            var details = new List<IB.ExecDetailsEventArgs>();
            using (var client = new IB.IBClient())
            {
                client.Connect(_host, _port, IncrementClientID());

                var manualResetEvent = new ManualResetEvent(false);

                int requestID = GetNextRequestID();

                // define our event handlers
                EventHandler<IB.ExecutionDataEndEventArgs> clientOnExecutionDataEnd = (sender, args) =>
                {
                    if (args.RequestId == requestID) manualResetEvent.Set();
                };
                EventHandler<IB.ExecDetailsEventArgs> clientOnExecDetails = (sender, args) =>
                {
                    if (args.RequestId == requestID) details.Add(args);
                };

                client.ExecDetails += clientOnExecDetails;
                client.ExecutionDataEnd += clientOnExecutionDataEnd;

                // no need to be fancy with request id since that's all this client does is 1 request
                client.RequestExecutions(requestID, filter);

                if (!manualResetEvent.WaitOne(5000))
                {
                    throw new TimeoutException("InteractiveBrokersBrokerage.GetExecutions(): Operation took longer than 1 second.");
                }

                // remove our event handlers
                client.ExecDetails -= clientOnExecDetails;
                client.ExecutionDataEnd -= clientOnExecutionDataEnd;
            }

            return details;
        }
        /// <summary>
        /// Creates a new InteractiveBrokersBrokerage from the specified values
        /// </summary>
        /// <param name="orderProvider">An instance of IOrderProvider used to fetch Order objects by brokerage ID</param>
        /// <param name="securityProvider">The security provider used to give access to algorithm securities</param>
        /// <param name="account">The Interactive Brokers account name</param>
        /// <param name="host">host name or IP address of the machine where TWS is running. Leave blank to connect to the local host.</param>
        /// <param name="port">must match the port specified in TWS on the Configure&gt;API&gt;Socket Port field.</param>
        /// <param name="agentDescription">Used for Rule 80A describes the type of trader.</param>
        public InteractiveBrokersBrokerage(IOrderProvider orderProvider, ISecurityProvider securityProvider, string account, string host, int port, IB.AgentDescription agentDescription = IB.AgentDescription.Individual)
            : base("Interactive Brokers Brokerage")
        {
            _orderProvider = orderProvider;
            _securityProvider = securityProvider;
            _account = account;
            _host = host;
            _port = port;
            _clientID = IncrementClientID();
            _agentDescription = agentDescription;
            _client = new IB.IBClient();

            // set up event handlers
            _client.UpdatePortfolio += HandlePortfolioUpdates;
            _client.OrderStatus += HandleOrderStatusUpdates;
            _client.UpdateAccountValue += HandleUpdateAccountValue;
            _client.Error += HandleError;
            _client.TickPrice += HandleTickPrice;
            _client.TickSize += HandleTickSize;
            _client.CurrentTime += HandleBrokerTime;

            // we need to wait until we receive the next valid id from the server
            _client.NextValidId += (sender, e) =>
            {
                // only grab this id when we initialize, and we'll manually increment it here to avoid threading issues
                if (_nextValidID == 0)
                {
                    _nextValidID = e.OrderId;
                    _waitForNextValidID.Set();
                }
                Log.Trace("InteractiveBrokersBrokerage.HandleNextValidID(): " + e.OrderId);
            };
        }
 /// <summary>
 /// Handle portfolio changed events from IB
 /// </summary>
 private void HandlePortfolioUpdates(object sender, IB.UpdatePortfolioEventArgs e)
 {
     _accountHoldingsResetEvent.Reset();
     var holding = CreateHolding(e);
     _accountHoldings[holding.Symbol] = holding;
     OnPortfolioChanged(new SecurityEvent(holding.Symbol, e.Position, e.AverageCost));
 }
        /// <summary>
        /// Handle order events from IB
        /// </summary>
        private void HandleOrderStatusUpdates(object sender, IB.OrderStatusEventArgs update)
        {
            try
            {
                if (update.Status == IB.OrderStatus.PreSubmitted
                 || update.Status == IB.OrderStatus.PendingSubmit)
                {
                    return;
                }

                var status = ConvertOrderStatus(update.Status);
                if (status != OrderStatus.PartiallyFilled &&
                    status != OrderStatus.Filled &&
                    status != OrderStatus.Canceled &&
                    status != OrderStatus.Submitted &&
                    status != OrderStatus.Invalid)
                {
                    Log.Trace("InteractiveBrokersBrokerage.HandleOrderStatusUpdates(): Status: " + status);
                    return;
                }

                if (status == OrderStatus.Invalid)
                {
                    Log.Error("InteractiveBrokersBrokerage.HandleOrderStatusUpdates(): ERROR -- " + update.OrderId);
                }

                var order = _orderProvider.GetOrderByBrokerageId(update.OrderId);
                if (order == null)
                {
                    Log.Error("InteractiveBrokersBrokerage.HandleOrderStatusUpdates(): Unable to locate order with BrokerageID " + update.OrderId);
                    return;
                }

                // mark sells as negative quantities
                var fillQuantity = order.Direction == OrderDirection.Buy ? update.Filled : -update.Filled;
                var orderEvent = new OrderEvent(order, "Interactive Brokers Fill Event")
                {
                    Status = status,
                    FillPrice = update.AverageFillPrice,
                    FillQuantity = fillQuantity
                };
                if (update.Remaining != 0)
                {
                    orderEvent.Message += " - " + update.Remaining + " remaining";
                }

                // if we're able to add to our fixed length, unique queue then send the event
                // otherwise it is a duplicate, so skip it
                if (_recentOrderEvents.Add(orderEvent.ToString() + update.Remaining))
                {
                    OnOrderEvent(orderEvent);
                }
            }
            catch(InvalidOperationException err)
            {
                Log.Error("InteractiveBrokersBrokerage.HandleOrderStatusUpdates(): Unable to resolve executions for BrokerageID: " + update.OrderId + " - " + err.Message);
            }
            catch (Exception err)
            {
                Log.Error("InteractiveBrokersBrokerage.HandleOrderStatusUpdates(): " + err.Message);
            }
        }
 void HandleBrokerTime(object sender, IB.CurrentTimeEventArgs e)
 {
     DateTime brokerTime = e.Time.ToLocalTime();
     _brokerTimeDiff = brokerTime.Subtract(DateTime.Now);
 }
 /// <summary>
 /// Maps the IB Contract's symbol to a QC symbol
 /// </summary>
 private static Symbol MapSymbol(IB.Contract contract)
 {
     if (contract.SecurityType == IB.SecurityType.Cash)
     {
         // reformat for QC
         return new Symbol(contract.Symbol + contract.Currency);
     }
     return new Symbol(contract.Symbol);
 }
        void HandleTickPrice(object sender, IB.TickPriceEventArgs e)
        {
            var symbol = default(SymbolCacheKey);

            if (!_subscribedTickets.TryGetValue(e.TickerId, out symbol)) return;

            var tick = new Tick();
            // in the event of a symbol change this will break since we'll be assigning the
            // new symbol to the permtick which won't be known by the algorithm
            tick.Symbol = new Symbol(symbol.Item2);
            tick.Time = GetBrokerTime();
            if (symbol.Item1 == SecurityType.Forex)
            {
                // forex exchange hours are specified in UTC-05
                tick.Time = tick.Time.ConvertTo(TimeZones.NewYork, TimeZones.EasternStandard);
            }
            tick.Value = e.Price;

            if (e.Price <= 0 &&
                symbol.Item1 != SecurityType.Future &&
                symbol.Item1 != SecurityType.Option)
                return;

            switch (e.TickType)
            {
                case IB.TickType.BidPrice:

                    tick.TickType = TickType.Quote;
                    tick.BidPrice = e.Price;
                    _lastBidSizes.TryGetValue(symbol, out tick.Quantity);
                    _lastBidPrices[symbol] = e.Price;
                    break;

                case IB.TickType.AskPrice:

                    tick.TickType = TickType.Quote;
                    tick.AskPrice = e.Price;
                    _lastAskSizes.TryGetValue(symbol, out tick.Quantity);
                    _lastAskPrices[symbol] = e.Price;
                    break;

                case IB.TickType.LastPrice:

                    tick.TickType = TickType.Trade;
                    tick.Value = e.Price;
                    _lastPrices[symbol] = e.Price;
                    break;

                case IB.TickType.HighPrice:
                case IB.TickType.LowPrice:
                case IB.TickType.ClosePrice:
                case IB.TickType.OpenPrice:
                default:
                    return;
            }

            lock (_ticks)
                if (tick.IsValid()) _ticks.Add(tick);
        }
Example #14
0
        void HandleTickSize(object sender, IB.TickSizeEventArgs e)
        {
            var symbol = default(SymbolCacheKey);

            if (!_subscribedTickets.TryGetValue(e.TickerId, out symbol)) return;

            var tick = new Tick();
            // in the event of a symbol change this will break since we'll be assigning the
            // new symbol to the permtick which won't be known by the algorithm
            tick.Symbol = new Symbol(symbol.Item2);
            tick.Quantity = AdjustQuantity(symbol.Item1, e.Size);
            tick.Time = GetBrokerTime();

            if (tick.Quantity == 0) return;

            switch (e.TickType)
            {
                case IB.TickType.BidSize:

                    tick.TickType = TickType.Quote;

                    _lastBidPrices.TryGetValue(symbol, out tick.BidPrice);
                    _lastBidSizes[symbol] = tick.Quantity;

                    tick.Value = tick.BidPrice;
                    break;

                case IB.TickType.AskSize:

                    tick.TickType = TickType.Quote;

                    _lastAskPrices.TryGetValue(symbol, out tick.AskPrice);
                    _lastAskSizes[symbol] = tick.Quantity;

                    tick.Value = tick.AskPrice;
                    break;

                case IB.TickType.LastSize:
                    tick.TickType = TickType.Trade;

                    decimal lastPrice;
                    _lastPrices.TryGetValue(symbol, out lastPrice);
                    _lastVolumes[symbol] = tick.Quantity;

                    tick.Value = lastPrice;

                    break;

                default:
                    return;
            }
            lock (_ticks)
                if (tick.IsValid()) _ticks.Add(tick);
        }
        /// <summary>
        /// Handle order events from IB
        /// </summary>
        private void HandleOrderStatusUpdates(object sender, IB.OrderStatusEventArgs update)
        {
            try
            {
                if (update.Status == IB.OrderStatus.PreSubmitted
                 || update.Status == IB.OrderStatus.PendingSubmit)
                {
                    return;
                }

                var status = ConvertOrderStatus(update.Status);
                if (status != OrderStatus.PartiallyFilled &&
                    status != OrderStatus.Filled &&
                    status != OrderStatus.Canceled &&
                    status != OrderStatus.Submitted &&
                    status != OrderStatus.Invalid)
                {
                    Log.Trace("InteractiveBrokersBrokerage.HandleOrderStatusUpdates(): Status: " + status);
                    return;
                }

                if (status == OrderStatus.Invalid)
                {
                    Log.Error("InteractiveBrokersBrokerage.HandleOrderStatusUpdates(): ERROR -- " + update.OrderId);
                }

                var order = _orderProvider.GetOrderByBrokerageId(update.OrderId);
                if (order == null)
                {
                    Log.Error("InteractiveBrokersBrokerage.HandleOrderStatusUpdates(): Unable to locate order with BrokerageID " + update.OrderId);
                    return;
                }

                int filledThisTime;
                lock (_orderFillsLock)
                {
                    // lock since we're getting and updating in multiple operations
                    var currentFilled = _orderFills.GetOrAdd(order.Symbol, 0);
                    filledThisTime = update.Filled - currentFilled;
                    _orderFills.AddOrUpdate(order.Symbol, currentFilled, (sym, filled) => update.Filled);
                }

                // don't send empty fill events
                if (filledThisTime == 0 && (status == OrderStatus.PartiallyFilled || status == OrderStatus.Filled))
                {
                    Log.Trace("InteractiveBrokersBrokerage.HandleOrderStatusUpdates(): Ignored zero fill event: OrderId: " + update.OrderId + " Remaining: " + update.Remaining);
                    return;
                }

                // mark sells as negative quantities
                var fillQuantity = order.Direction == OrderDirection.Buy ? filledThisTime : -filledThisTime;
                const int orderFee = 0;
                var orderEvent = new OrderEvent(order, DateTime.UtcNow, orderFee, "Interactive Brokers Fill Event")
                {
                    Status = status,
                    FillPrice = update.LastFillPrice,
                    FillQuantity = fillQuantity
                };
                if (update.Remaining != 0)
                {
                    orderEvent.Message += " - " + update.Remaining + " remaining";
                }

                // if we're able to add to our fixed length, unique queue then send the event
                // otherwise it is a duplicate, so skip it
                if (_recentOrderEvents.Add(orderEvent.ToString() + update.Remaining))
                {
                    OnOrderEvent(orderEvent);
                }
            }
            catch(InvalidOperationException err)
            {
                Log.Error("InteractiveBrokersBrokerage.HandleOrderStatusUpdates(): Unable to resolve executions for BrokerageID: " + update.OrderId + " - " + err);
            }
            catch (Exception err)
            {
                Log.Error("InteractiveBrokersBrokerage.HandleOrderStatusUpdates(): " + err);
            }
        }
        /// <summary>
        /// Maps the IB Contract's symbol to a QC symbol
        /// </summary>
        private Symbol MapSymbol(IB.Contract contract)
        {
            var securityType = ConvertSecurityType(contract.SecurityType);
            var ibSymbol = securityType == SecurityType.Forex ? contract.Symbol + contract.Currency : contract.Symbol;
            var market = securityType == SecurityType.Forex ? Market.FXCM : Market.USA;

            return _symbolMapper.GetLeanSymbol(ibSymbol, securityType, market);
        }
 void HandleBrokerTime(object sender, IB.CurrentTimeEventArgs e)
 {
     // keep track of clock drift
     _brokerTimeDiff = e.Time.Subtract(DateTime.UtcNow);
 }
        void HandleTickPrice(object sender, IB.TickPriceEventArgs e)
        {
            var symbol = default(SymbolCacheKey);

            if (!_subscribedTickets.TryGetValue(e.TickerId, out symbol)) return;

            var tick = new Tick();
            tick.Symbol = symbol.Item2;
            tick.Time = GetBrokerTime();
            tick.Value = e.Price;

            if (e.Price <= 0 &&
                symbol.Item1 != SecurityType.Future &&
                symbol.Item1 != SecurityType.Option)
                return;

            switch (e.TickType)
            {
                case IB.TickType.BidPrice:

                    tick.TickType = TickType.Quote;
                    tick.BidPrice = e.Price;
                    _lastBidSizes.TryGetValue(symbol, out tick.Quantity);
                    _lastBidPrices[symbol] = e.Price;
                    break;

                case IB.TickType.AskPrice:

                    tick.TickType = TickType.Quote;
                    tick.AskPrice = e.Price;
                    _lastAskSizes.TryGetValue(symbol, out tick.Quantity);
                    _lastAskPrices[symbol] = e.Price;
                    break;

                case IB.TickType.LastPrice:

                    tick.TickType = TickType.Trade;
                    tick.Value = e.Price;
                    _lastPrices[symbol] = e.Price;
                    break;

                case IB.TickType.HighPrice:
                case IB.TickType.LowPrice:
                case IB.TickType.ClosePrice:
                case IB.TickType.OpenPrice:
                default:
                    return;
            }

            lock (_ticks)
                if (tick.IsValid()) _ticks.Add(tick);
        }
        void HandleTickSize(object sender, IB.TickSizeEventArgs e)
        {
            var symbol = default(Symbol);

            if (!_subscribedTickets.TryGetValue(e.TickerId, out symbol)) return;

            var tick = new Tick();
            // in the event of a symbol change this will break since we'll be assigning the
            // new symbol to the permtick which won't be known by the algorithm
            tick.Symbol = symbol;
            var securityType = symbol.ID.SecurityType;
            tick.Quantity = AdjustQuantity(securityType, e.Size);
            tick.Time = GetBrokerTime();
            if (securityType == SecurityType.Forex)
            {
                // forex exchange hours are specified in UTC-05
                tick.Time = tick.Time.ConvertTo(TimeZones.NewYork, TimeZones.EasternStandard);
            }

            if (tick.Quantity == 0) return;

            switch (e.TickType)
            { 
                case IB.TickType.BidSize:

                    tick.TickType = TickType.Quote;

                    _lastBidPrices.TryGetValue(symbol, out tick.BidPrice);
                    _lastBidSizes[symbol] = tick.Quantity;

                    tick.Value = tick.BidPrice;
                    tick.BidSize = tick.Quantity;
                    break;

                case IB.TickType.AskSize:

                    tick.TickType = TickType.Quote;

                    _lastAskPrices.TryGetValue(symbol, out tick.AskPrice);
                    _lastAskSizes[symbol] = tick.Quantity;

                    tick.Value = tick.AskPrice;
                    tick.AskSize = tick.Quantity;
                    break;
                
                
                case IB.TickType.LastSize:
                    tick.TickType = TickType.Trade;

                    decimal lastPrice;
                    _lastPrices.TryGetValue(symbol, out lastPrice);
                    _lastVolumes[symbol] = tick.Quantity;

                    tick.Value = lastPrice;
                        
                    break;

                default:
                    return;
            }
            lock (_ticks)
                if (tick.IsValid()) _ticks.Add(tick);

        }
        void HandleTickSize(object sender, IB.TickSizeEventArgs e)
        {
            var symbol = default(SymbolCacheKey);

            if (!_subscribedTickets.TryGetValue(e.TickerId, out symbol)) return;

            var tick = new Tick();
            tick.Symbol = symbol.Item2;
            tick.Quantity = AdjustQuantity(symbol.Item1, e.Size);
            tick.Time = GetBrokerTime();

            if (tick.Quantity == 0) return;

            switch (e.TickType)
            {
                case IB.TickType.BidSize:

                    tick.TickType = TickType.Quote;

                    _lastBidPrices.TryGetValue(symbol, out tick.BidPrice);
                    _lastBidSizes[symbol] = tick.Quantity;

                    tick.Value = tick.BidPrice;
                    break;

                case IB.TickType.AskSize:

                    tick.TickType = TickType.Quote;

                    _lastAskPrices.TryGetValue(symbol, out tick.AskPrice);
                    _lastAskSizes[symbol] = tick.Quantity;

                    tick.Value = tick.AskPrice;
                    break;

                case IB.TickType.LastSize:
                    tick.TickType = TickType.Trade;

                    decimal lastPrice;
                    _lastPrices.TryGetValue(symbol, out lastPrice);
                    _lastVolumes[symbol] = tick.Quantity;

                    tick.Value = lastPrice;

                    break;

                default:
                    return;
            }
            lock (_ticks)
                if (tick.IsValid()) _ticks.Add(tick);
        }
        private string GetPrimaryExchange(IB.Contract contract)
        {
            IB.ContractDetails details;
            if (_contractDetails.TryGetValue(contract.Symbol, out details))
            {
                return details.Summary.PrimaryExchange;
            }

            details = GetContractDetails(contract);
            if (details == null)
            {
                // we were unable to find the contract details
                return null;
            }

            return details.Summary.PrimaryExchange;
        }
 /// <summary>
 /// Maps the IB Contract's symbol to a QC symbol
 /// </summary>
 private static string MapSymbol(IB.Contract contract)
 {
     if (contract.SecurityType == IB.SecurityType.Cash)
     {
         // reformat for QC
         return contract.Symbol + contract.Currency;
     }
     return contract.Symbol;
 }
        private IB.ContractDetails GetContractDetails(IB.Contract contract)
        {
            IB.ContractDetails details = null;
            var requestID = GetNextRequestID();

            var manualResetEvent = new ManualResetEvent(false);

            // define our event handlers
            EventHandler<IB.ContractDetailsEventArgs> clientOnContractDetails = (sender, args) =>
            {
                // ignore other requests
                if (args.RequestId != requestID) return;
                details = args.ContractDetails;
                _contractDetails.TryAdd(contract.Symbol, details);
                manualResetEvent.Set();
            };

            _client.ContractDetails += clientOnContractDetails;

            // make the request for data
            _client.RequestContractDetails(requestID, contract);

            // we'll wait a second, but it may not exist so just pass through
            manualResetEvent.WaitOne(1000);

            // be sure to remove our event handlers
            _client.ContractDetails -= clientOnContractDetails;

            return details;
        }
        /// <summary>
        /// Maps OrderType enum
        /// </summary>
        private OrderType ConvertOrderType(IB.Order order)
        {
            switch (order.OrderType)
            {
                case IB.OrderType.Limit:            return OrderType.Limit;
                case IB.OrderType.Stop:             return OrderType.StopMarket;
                case IB.OrderType.StopLimit:        return OrderType.StopLimit;
                case IB.OrderType.MarketOnClose:    return OrderType.MarketOnClose;

                case IB.OrderType.Market:
                    if (order.Tif == IB.TimeInForce.MarketOnOpen)
                    {
                        return OrderType.MarketOnOpen;
                    }
                    return OrderType.Market;

                default:
                    throw new InvalidEnumArgumentException("order.OrderType", (int)order.OrderType, typeof(OrderType));
            }
        }
        /// <summary>
        /// Stores all the account values
        /// </summary>
        private void HandleUpdateAccountValue(object sender, IB.UpdateAccountValueEventArgs e)
        {
            //https://www.interactivebrokers.com/en/software/api/apiguide/activex/updateaccountvalue.htm

            try
            {
                _accountProperties[e.Currency + ":" + e.Key] = e.Value;

                // we want to capture if the user's cash changes so we can reflect it in the algorithm
                if (e.Key == AccountValueKeys.CashBalance && e.Currency != "BASE")
                {
                    var cashBalance = decimal.Parse(e.Value, CultureInfo.InvariantCulture);
                    _cashBalances.AddOrUpdate(e.Currency, cashBalance);
                    OnAccountChanged(new AccountEvent(e.Currency, cashBalance));
                }
            }
            catch (Exception err)
            {
                Log.Error("InteractiveBrokersBrokerage.HandleUpdateAccountValue(): " + err);
            }
        }
        /// <summary>
        /// Maps IB's OrderStats enum
        /// </summary>
        private OrderStatus ConvertOrderStatus(IB.OrderStatus status)
        {
            switch (status)
            {
                case IB.OrderStatus.ApiPending:
                case IB.OrderStatus.PendingSubmit:
                case IB.OrderStatus.PreSubmitted: 
                    return OrderStatus.New;

                case IB.OrderStatus.ApiCancelled:
                case IB.OrderStatus.PendingCancel:
                case IB.OrderStatus.Canceled: 
                    return OrderStatus.Canceled;

                case IB.OrderStatus.Submitted: 
                    return OrderStatus.Submitted;

                case IB.OrderStatus.Filled: 
                    return OrderStatus.Filled;

                case IB.OrderStatus.PartiallyFilled: 
                    return OrderStatus.PartiallyFilled;

                case IB.OrderStatus.Error: 
                    return OrderStatus.Invalid;

                case IB.OrderStatus.Inactive:
                    Log.Error("InteractiveBrokersBrokerage.ConvertOrderStatus(): Inactive order");
                    return OrderStatus.None;

                case IB.OrderStatus.None: 
                    return OrderStatus.None;
                    
                // not sure how to map these guys
                default:
                    throw new InvalidEnumArgumentException("status", (int)status, typeof(IB.OrderStatus));
            }
        }
 /// <summary>
 /// Handle portfolio changed events from IB
 /// </summary>
 private void HandlePortfolioUpdates(object sender, IB.UpdatePortfolioEventArgs e)
 {
     _accountHoldingsResetEvent.Reset();
     var holding = CreateHolding(e);
     _accountHoldings[holding.Symbol.Value] = holding;
 }
        /// <summary>
        /// Maps SecurityType enum
        /// </summary>
        private static SecurityType ConvertSecurityType(IB.SecurityType type)
        {
            switch (type)
            {
                case IB.SecurityType.Stock:
                    return SecurityType.Equity;

                case IB.SecurityType.Option:
                    return SecurityType.Option;

                case IB.SecurityType.Commodity:
                    return SecurityType.Commodity;

                case IB.SecurityType.Cash:
                    return SecurityType.Forex;

                case IB.SecurityType.Future:
                    return SecurityType.Future;

                // we don't map these security types to anything specific yet, load them as custom data instead of throwing
                case IB.SecurityType.Index:
                case IB.SecurityType.FutureOption:
                case IB.SecurityType.Bag:
                case IB.SecurityType.Bond:
                case IB.SecurityType.Warrant:
                case IB.SecurityType.Bill:
                case IB.SecurityType.Undefined:
                    return SecurityType.Base;

                default:
                    throw new ArgumentOutOfRangeException("type");
            }
        }
        private Order ConvertOrder(IB.Order ibOrder, IB.Contract contract)
        {
            // this function is called by GetOpenOrders which is mainly used by the setup handler to
            // initialize algorithm state.  So the only time we'll be executing this code is when the account
            // has orders sitting and waiting from before algo initialization...
            // because of this we can't get the time accurately

            Order order;
            var mappedSymbol = MapSymbol(contract);
            var orderType = ConvertOrderType(ibOrder);
            switch (orderType)
            {
                case OrderType.Market:
                    order = new MarketOrder(mappedSymbol,
                        ibOrder.TotalQuantity,
                        new DateTime() // not sure how to get this data
                        );
                    break;

                case OrderType.MarketOnOpen:
                    order = new MarketOnOpenOrder(mappedSymbol, 
                        ibOrder.TotalQuantity,
                        new DateTime());
                    break;

                case OrderType.MarketOnClose:
                    order = new MarketOnCloseOrder(mappedSymbol,
                        ibOrder.TotalQuantity,
                        new DateTime()
                        );
                    break;

                case OrderType.Limit:
                    order = new LimitOrder(mappedSymbol,
                        ibOrder.TotalQuantity,
                        ibOrder.LimitPrice,
                        new DateTime()
                        );
                    break;

                case OrderType.StopMarket:
                    order = new StopMarketOrder(mappedSymbol,
                        ibOrder.TotalQuantity,
                        ibOrder.AuxPrice,
                        new DateTime()
                        );
                    break;

                case OrderType.StopLimit:
                    order = new StopLimitOrder(mappedSymbol,
                        ibOrder.TotalQuantity,
                        ibOrder.AuxPrice,
                        ibOrder.LimitPrice,
                        new DateTime()
                        );
                    break;

                default:
                    throw new InvalidEnumArgumentException("orderType", (int) orderType, typeof (OrderType));
            }

            order.BrokerId.Add(ibOrder.OrderId.ToString());

            return order;
        }
        /// <summary>
        /// Creates a holding object from te UpdatePortfolioEventArgs
        /// </summary>
        private Holding CreateHolding(IB.UpdatePortfolioEventArgs e)
        {
            string currencySymbol;
            if (!Currencies.CurrencySymbols.TryGetValue(e.Contract.Currency, out currencySymbol))
            {
                currencySymbol = "$";
            }

            return new Holding
            {
                Symbol = MapSymbol(e.Contract),
                Type = ConvertSecurityType(e.Contract.SecurityType),
                Quantity = e.Position,
                AveragePrice = e.AverageCost,
                MarketPrice = e.MarketPrice,
                ConversionRate = 1m, // this will be overwritten when GetAccountHoldings is called to ensure fresh values
                CurrencySymbol =  currencySymbol
            };
        }