private OrderFilled GenerateOrderFilledEvent(ExecutionReport message)
        {
            var orderId          = this.GetOrderId(message);
            var executionId      = new ExecutionId(message.GetField(Tags.ExecID));
            var positionIdBroker = new PositionIdBroker(message.GetField(FxcmTags.PosID));
            var symbol           = this.GetSymbol(message.GetString(Tags.Symbol));
            var orderSide        = FxcmMessageHelper.GetOrderSide(message.GetField(Tags.Side));
            var filledQuantity   = Quantity.Create(message.GetDecimal(Tags.CumQty));
            var averagePrice     = Price.Create(message.GetDecimal(Tags.AvgPx));
            var quoteCurrency    = this.GetQuoteCurrency(symbol, message.GetField(Tags.Currency));
            var executionTime    = FxcmMessageHelper.ParseTimestamp(message.GetField(Tags.TransactTime));

            return(new OrderFilled(
                       this.accountId,
                       orderId,
                       executionId,
                       positionIdBroker,
                       symbol,
                       orderSide,
                       filledQuantity,
                       averagePrice,
                       quoteCurrency,
                       executionTime,
                       this.NewGuid(),
                       this.TimeNow()));
        }
        public override object GetField(Fields field)
        {
            if (ExecutionReport == null)
            {
                return(ExecutionReportFields.NULL);
            }

            if (field == Fields.KEY)
            {
                return(Key);
            }
            else
            {
                ExecutionReportFields xrField = (ExecutionReportFields)field;

                if (xrField == ExecutionReportFields.OrderID)
                {
                    return(GetOrdId(ExecutionReport, QuickFix.Fields.Tags.OrderID));
                }
                else if (xrField == ExecutionReportFields.ClOrdID)
                {
                    return(GetOrdId(ExecutionReport, QuickFix.Fields.Tags.ClOrdID));
                }
                else if (xrField == ExecutionReportFields.OrigClOrdID)
                {
                    return(GetOrdId(ExecutionReport, QuickFix.Fields.Tags.OrigClOrdID));
                }
                else if (xrField == ExecutionReportFields.ExecType)
                {
                    return(GetExecType(ExecutionReport.GetChar(QuickFix.Fields.Tags.ExecType)));
                }
                else if (xrField == ExecutionReportFields.ExecID)
                {
                    return(ExecutionReport.GetString(QuickFix.Fields.Tags.ExecID));
                }
                else if (xrField == ExecutionReportFields.OrdStatus)
                {
                    return(GetOrdStatus(ExecutionReport.GetChar(QuickFix.Fields.Tags.OrdStatus)));
                }
                else if (xrField == ExecutionReportFields.OrdRejReason)
                {
                    return(FixHelper.GetNullIntFieldIfSet(ExecutionReport, QuickFix.Fields.Tags.OrdRejReason));
                }
                else if (xrField == ExecutionReportFields.LeavesQty)
                {
                    return(ExecutionReport.GetInt(QuickFix.Fields.Tags.LeavesQty));
                }
                else if (xrField == ExecutionReportFields.CumQty)
                {
                    return(ExecutionReport.GetInt(QuickFix.Fields.Tags.CumQty));
                }
                else if (xrField == ExecutionReportFields.AvgPx)
                {
                    return(FixHelper.GetNullDoubleFieldIfSet(ExecutionReport, QuickFix.Fields.Tags.AvgPx));
                }
                else if (xrField == ExecutionReportFields.Commission)
                {
                    return(FixHelper.GetNullDoubleFieldIfSet(ExecutionReport, QuickFix.Fields.Tags.Commission));
                }
                else if (xrField == ExecutionReportFields.Text)
                {
                    return(ExecutionReport.GetField(QuickFix.Fields.Tags.Text));
                }
                else if (xrField == ExecutionReportFields.TransactTime)
                {
                    return(DateTime.Now);
                }
                else if (xrField == ExecutionReportFields.LastQty)
                {
                    return(FixHelper.GetNullIntFieldIfSet(ExecutionReport, QuickFix.Fields.Tags.LastQty));
                }
                else if (xrField == ExecutionReportFields.LastPx)
                {
                    return(FixHelper.GetNullDoubleFieldIfSet(ExecutionReport, QuickFix.Fields.Tags.LastPx));
                }
                else if (xrField == ExecutionReportFields.LastMkt)
                {
                    return(FixHelper.GetNullFieldIfSet(ExecutionReport, QuickFix.Fields.Tags.LastMkt));
                }


                else if (xrField == ExecutionReportFields.Symbol)
                {
                    return(FixHelper.GetNullFieldIfSet(ExecutionReport, QuickFix.Fields.Tags.Symbol));
                }
                else if (xrField == ExecutionReportFields.OrderQty)
                {
                    return(FixHelper.GetNullDoubleFieldIfSet(ExecutionReport, QuickFix.Fields.Tags.OrderQty));
                }
                else if (xrField == ExecutionReportFields.CashOrderQty)
                {
                    return(FixHelper.GetNullDoubleFieldIfSet(ExecutionReport, QuickFix.Fields.Tags.CashOrderQty));
                }
                else if (xrField == ExecutionReportFields.OrdType)
                {
                    return(GetOrdType(ExecutionReport.GetChar(QuickFix.Fields.Tags.OrdType)));
                }
                else if (xrField == ExecutionReportFields.Price)
                {
                    return(FixHelper.GetNullDoubleFieldIfSet(ExecutionReport, QuickFix.Fields.Tags.Price));
                }
                else if (xrField == ExecutionReportFields.StopPx)
                {
                    return(FixHelper.GetNullDoubleFieldIfSet(ExecutionReport, QuickFix.Fields.Tags.StopPx));
                }
                else if (xrField == ExecutionReportFields.Currency)
                {
                    return(FixHelper.GetNullFieldIfSet(ExecutionReport, QuickFix.Fields.Tags.Currency));
                }
                else if (xrField == ExecutionReportFields.ExpireDate)
                {
                    return(FixHelper.GetDateTimeFieldIfSet(ExecutionReport, QuickFix.Fields.Tags.ExpireDate));
                }
                else if (xrField == ExecutionReportFields.MinQty)
                {
                    return(FixHelper.GetNullDoubleFieldIfSet(ExecutionReport, QuickFix.Fields.Tags.MinQty));
                }
                else if (xrField == ExecutionReportFields.Side)
                {
                    GetSide(ExecutionReport.GetChar(QuickFix.Fields.Tags.Side));
                }
                else if (xrField == ExecutionReportFields.QuantityType)
                {
                    return(QuantityType.SHARES);
                }
                else if (xrField == ExecutionReportFields.PriceType)
                {
                    return(PriceType.FixedAmount);
                }
                else if (xrField == ExecutionReportFields.Account)
                {
                    return(ExecutionReport.GetField(QuickFix.Fields.Tags.Account));
                }
                else if (xrField == ExecutionReportFields.ExecInst)
                {
                    return(ExecutionReport.GetField(QuickFix.Fields.Tags.ExecInst));
                }
            }

            return(ExecutionReportFields.NULL);
        }
        private async void Handle(ExecutionReport msg)
        {
            var    clOrderId     = msg.ClOrdID.Obj;
            string origClOrderId = null;

            if (msg.IsSetOrigClOrdID())
            {
                origClOrderId = msg.OrigClOrdID.Obj;
            }

            var state = msg.IsSetOrdStatus() ? ConvertOrderState(msg.OrdStatus) : null;
            var price = msg.IsSetPrice() ? msg.Price.Obj : (decimal?)null;

            price = price != 0M ? price : null;
            var qty             = msg.IsSetOrderQty() ? (uint)msg.OrderQty.Obj : (uint?)null;
            var filledQty       = msg.IsSetLastQty() ? (uint)msg.LastQty.Obj : (uint?)null;
            var activeQty       = msg.IsSetLeavesQty() ? (uint)msg.LeavesQty.Obj : (uint?)null;
            var time            = msg.IsSetTransactTime() ? ParseTransactTime() : DateTime.Now;
            var orderExchangeId = msg.IsSetOrderID() ? msg.OrderID.Obj : null;

            var transactionId = _orders.GetOrCreateOrderTransactionId(origClOrderId ?? clOrderId);

            var oscm = new OrderStateChangeMessage
            {
                TransactionId   = transactionId,
                State           = state,
                Price           = price,
                Quantity        = qty,
                FilledQuantity  = filledQty,
                ActiveQuantity  = activeQty,
                ChangeTime      = time,
                OrderExchangeId = orderExchangeId
            };

            switch (msg.ExecType.Obj)
            {
            case ExecType.NEW:
                _newOrderTransactions.Accept(clOrderId);
                _orders.SaveOrderExchangeId(transactionId, orderExchangeId);
                SendMessage(oscm);
                break;

            case ExecType.TRADE:
                _newOrderTransactions.Accept(clOrderId);
                _orders.SaveOrderExchangeId(transactionId, orderExchangeId);

                SendMessage(oscm);
                await EmitFillAsync();

                if (oscm.State == OrderState.Filled)
                {
                    _newOrderTransactions.Forget(transactionId);
                    _killOrderTransactions.Forget(msg.ClOrdID.Obj);
                    _orders.ForgetOrder(transactionId);
                }
                break;

            case ExecType.FILL:
                _newOrderTransactions.Accept(clOrderId);

                _orders.SaveOrderExchangeId(transactionId, orderExchangeId);
                SendMessage(oscm);
                await EmitFillAsync();

                if (oscm.State == OrderState.Filled)
                {
                    _newOrderTransactions.Forget(transactionId);
                    _killOrderTransactions.Forget(msg.ClOrdID.Obj);
                    _orders.ForgetOrder(transactionId);
                }
                break;

            case ExecType.PARTIAL_FILL:
                _newOrderTransactions.Accept(clOrderId);
                _orders.SaveOrderExchangeId(transactionId, orderExchangeId);
                SendMessage(oscm);
                await EmitFillAsync();

                break;

            case ExecType.REJECTED:
                string rejectReason;
                switch (msg.OrdRejReason.Obj)
                {
                case OrdRejReason.UNKNOWN_SYMBOL:
                    rejectReason = "Unknown symbol";
                    break;

                case OrdRejReason.EXCHANGE_CLOSED:
                    rejectReason = "Exchange closed";
                    break;

                case OrdRejReason.ORDER_EXCEEDS_LIMIT:
                    rejectReason = "Order exceeds limit";
                    break;

                case OrdRejReason.DUPLICATE_ORDER:
                    rejectReason = "Duplicate order";
                    break;

                default:
                    rejectReason = $"#{msg.OrdRejReason.Obj}";
                    break;
                }
                if (!string.IsNullOrEmpty(msg.Text?.Obj))
                {
                    rejectReason = $"{rejectReason}. {msg.Text?.Obj}";
                }
                _newOrderTransactions.Reject(clOrderId, rejectReason);
                _orders.ForgetOrder(transactionId);
                break;

            case ExecType.PENDING_CANCEL:
                _killOrderTransactions.Accept(msg.ClOrdID.Obj);
                SendMessage(oscm);
                break;

            case ExecType.CANCELED:
                _killOrderTransactions.Accept(msg.ClOrdID.Obj);
                _newOrderTransactions.Forget(transactionId);
                _killOrderTransactions.Forget(msg.ClOrdID.Obj);
                _orders.ForgetOrder(transactionId);
                SendMessage(oscm);
                break;

            case ExecType.PENDING_REPLACE:
                _modifyOrderTransactions.Accept(clOrderId);
                break;

            case ExecType.REPLACED:
                _newOrderTransactions.Forget(origClOrderId);
                _killOrderTransactions.Forget(clOrderId);
                _orders.UpdateOrderParams(
                    orderExchangeId: orderExchangeId,
                    origClOrderId: origClOrderId,
                    clOrderId: clOrderId,
                    symbol: msg.Symbol.Obj,
                    qty: msg.OrderQty.Obj,
                    side: msg.Side.Obj);

                SendMessage(oscm);
                break;

            default:
                _Log.Warn().PrintFormat("Unknown ExecType: {0}", msg.ExecType.Obj);
                break;
            }

            OrderState?ConvertOrderState(OrdStatus field)
            {
                switch (field.Obj)
                {
                case OrdStatus.PENDING_NEW: return(OrderState.New);

                case OrdStatus.NEW: return(OrderState.Active);

                case OrdStatus.CANCELED: return(OrderState.Cancelled);

                case OrdStatus.FILLED: return(OrderState.Filled);

                case OrdStatus.PARTIALLY_FILLED: return(OrderState.PartiallyFilled);

                case OrdStatus.REJECTED: return(OrderState.Error);

                case OrdStatus.PENDING_CANCEL: return(null);

                case OrdStatus.PENDING_REPLACE: return(null);

                default:
                    _Log.Warn().PrintFormat("Unknown OrdStatus: {0}", field.Obj);
                    return(null);
                }
            }

            DateTime ParseTransactTime()
            {
                var maxLen = "20170912-09:39:57.2761221".Length;

                var str = msg.GetString(60 /*TransactTime*/);

                if (str.Length > maxLen)
                {
                    str = str.Substring(0, maxLen);
                }
                var t = DateTime.ParseExact(str, "yyyyMMdd-HH:mm:ss.fffffff", null, DateTimeStyles.AssumeUniversal);

                return(t);
            }

            async Task EmitFillAsync()
            {
                if (!msg.IsSetSecondaryExecID())
                {
                    return;
                }

                var fillExchangeId = msg.SecondaryExecID.Obj;
                var fillPrice      = msg.IsSetLastPx() ? msg.LastPx.Obj : (decimal?)null;

                price = price != 0M ? price : null;
                var fillQty = msg.IsSetLastQty() ? (uint)msg.LastQty.Obj : (uint?)null;

                if (fillPrice == null || fillQty == null)
                {
                    return;
                }

                Instrument instrument;

                try
                {
                    instrument = await _settings.InstrumentConverter.ResolveSymbolAsync(this, msg.Symbol.Obj);
                }
                catch (Exception e)
                {
                    _Log.Error().Print(e, $"Failed to resolve instrument {msg.Symbol.Obj}");
                    return;
                }

                var fill = new FillMessage
                {
                    Price           = fillPrice.Value,
                    Quantity        = fillQty.Value,
                    DateTime        = time,
                    Instrument      = instrument,
                    Account         = msg.Account.Obj,
                    Operation       = msg.Side.Obj == Side.BUY ? OrderOperation.Buy : OrderOperation.Sell,
                    ExchangeId      = fillExchangeId,
                    ExchangeOrderId = orderExchangeId
                };

                SendMessage(fill);
            }
        }