/// <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 = Convert.ToDecimal(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>
 /// 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>
        /// 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>
        /// 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

            var tickerId = e.Id;
            var errorCode = e.Code;
            var errorMsg = e.Message;

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

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

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

                // begin the try wait logic
                TryWaitForReconnect();
            }
            else if (errorCode == 1102)
            {
                // we've reconnected
                _disconnected1100Fired = false;
                OnMessage(BrokerageMessageEvent.Reconnected(errorMsg));
            }
            else if (errorCode == 506)
            {
                Log.Trace("InteractiveBrokersBrokerage.HandleError(): Server Version: " + _client.ClientSocket.ServerVersion);
            }

            if (InvalidatingCodes.Contains(errorCode))
            {
                Log.Trace(string.Format("InteractiveBrokersBrokerage.HandleError.InvalidateOrder(): Order: {0} ErrorCode: {1} - {2}", tickerId, errorCode, errorMsg));

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

            OnMessage(new BrokerageMessageEvent(brokerageMessageType, errorCode, errorMsg));
        }
        private void HandleTickSize(object sender, IB.TickSizeEventArgs e)
        {
            Symbol 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.Field)
            { 
                case IBApi.TickType.BID_SIZE:

                    tick.TickType = TickType.Quote;

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

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

                case IBApi.TickType.ASK_SIZE:

                    tick.TickType = TickType.Quote;

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

                    tick.Value = tick.AskPrice;
                    tick.AskSize = tick.Quantity;
                    break;
                
                
                case IBApi.TickType.LAST_SIZE:
                    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 void HandleBrokerTime(object sender, IB.CurrentTimeUtcEventArgs e)
 {
     // keep track of clock drift
     _brokerTimeDiff = e.CurrentTimeUtc.Subtract(DateTime.UtcNow);
 }
        /// <summary>
        /// Creates a holding object from te UpdatePortfolioEventArgs
        /// </summary>
        private Holding CreateHolding(IB.UpdatePortfolioEventArgs e)
        {
            var currencySymbol = Currencies.GetCurrencySymbol(e.Contract.Currency);

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