private bool _er_Update(ExecutionReport er, FixOrderStatus ordS, string desc, string bolsa) { try { int account; if (bolsa.Equals(ExchangePrefixes.BOVESPA, StringComparison.InvariantCultureIgnoreCase)) { string acc = er.Account.getValue(); acc = (acc.Remove(acc.Length - 1)); account = Convert.ToInt32(acc); } else { account = Convert.ToInt32(er.Account.getValue()); } // Buscar Ordem Original, de algum jeito!!! // 1a. tentativa: Tentar via ExchangeNumber // 2a. tentativa: Tentar via ClOrdID e OrigClOrdID // 3a. tentativa: Tentar via ClOrdID no tb_fix_order_update e buscar a ordem pelo OrderID // 4a. tentativa: ahhh foda-se, nao achou mesmo OrderDbInfo orderOrig = null; // 1a. tentativa if (er.IsSetOrderID() && !er.OrderID.getValue().Equals("NONE")) { orderOrig = _db.BuscarOrdemPorExchangeNumber(er.OrderID.getValue()); } // 2a. Tentativa if (null == orderOrig || orderOrig.OrderID == 0) { orderOrig = _db.BuscarOrdem(er.ClOrdID.getValue(), account, er.Symbol.getValue()); if (orderOrig.OrderID == 0) { // Se ordem nao encontrada, entao procurar pelo OrigClOrdID if (er.IsSetOrigClOrdID()) { orderOrig = _db.BuscarOrdem(er.OrigClOrdID.getValue(), account, er.Symbol.getValue()); if (orderOrig.OrderID == 0) { orderOrig = _db.BuscarOrdem(er.OrigClOrdID.getValue(), account, er.Symbol.getValue(), true); //if (orderOrig.OrderID == 0) //{ // logger.Info("01 ER - Nao achou a ordem em questao!!!: " + er.OrigClOrdID.getValue() + " Status: " + ordS.ToString() + " Desc: " + desc); // return false; //} } else { orderOrig = _db.BuscarOrdem(er.OrigClOrdID.getValue(), account, er.Symbol.getValue()); } } else { orderOrig = _db.BuscarOrdem(er.ClOrdID.getValue(), account, er.Symbol.getValue(), true); //if (orderOrig.OrderID == 0) //{ // logger.Info("02 ER - Nao achou a ordem em questao!!!: " + er.ClOrdID.getValue() + " Status: " + ordS.ToString() + " Desc: " + desc); // return false; //} } } } // 3a. Tentativa if (null == orderOrig || orderOrig.OrderID == 0) { // Buscar a partir de tb_fix_order_update OrderDbUpdateInfo orderUpdate = _db.BuscarOrdemUpdate(er.ClOrdID.getValue()); // Buscar ordem original a partir do order ID if (orderUpdate.OrderID != 0) { orderOrig = _db.BuscarOrdemPorOrderID(orderUpdate.OrderID); } else { logger.Info("01 ER - Nao achou a ordem em questao!!!: " + er.ClOrdID.getValue() + " Status: " + ordS.ToString() + " Desc: " + desc); return(false); } } if (null == orderOrig || orderOrig.OrderID == 0) { logger.Info("02 ER - Nao achou a ordem em questao!!!: " + er.ClOrdID.getValue() + " Status: " + ordS.ToString() + " Desc: " + desc); return(false); } //} //else //{ // orderOrig = _db.BuscarOrdemPorExchangeNumber(er.OrderID.getValue()); // if (orderOrig.OrderID == 0) // { // logger.Info("ER - Nao achou a ordem em questao via exchange number (OrderID ER)!!!: " + er.OrderID.getValue() + " Status: " + ordS.ToString() + " Desc: " + desc); // return false; // } // } // Adicionar OrdemDetalhe OrderDbDetalheInfo detail = new OrderDbDetalheInfo(); detail.OrderID = orderOrig.OrderID; detail.TransactID = er.ExecID.getValue(); detail.OrderQty = Convert.ToInt32(er.OrderQty.getValue()); detail.Price = er.IsSetField(Tags.Price) ? er.Price.getValue() : Decimal.Zero; detail.OrderStatusID = (int)ordS; detail.TransactTime = er.TransactTime.getValue(); if (er.IsSetField(Tags.Text)) { detail.Description = desc + " - " + er.Text.getValue(); } else { detail.Description = desc; } detail.TradeQty = er.IsSetField(Tags.LastQty) ? Convert.ToInt32(er.LastQty.getValue()) : 0; detail.CumQty = er.IsSetField(Tags.CumQty) ? Convert.ToInt32(er.CumQty.getValue()) : 0; detail.FixMsgSeqNum = Convert.ToInt32(er.Header.GetField(Tags.MsgSeqNum)); if (!_db.InserirOrdemDetalhe(detail, orderOrig.ClOrdID)) { logger.Info("Erro ao inserir o registro na tabela tb_fix_order_update"); return(false); } // Atualizar Ordem orderOrig.ExchangeNumberID = er.OrderID.getValue(); orderOrig.OrdStatus = (int)ordS; orderOrig.TransactTime = er.TransactTime.getValue(); // orderOrig.ClOrdID = er.ClOrdID.getValue(); // if (er.IsSetOrigClOrdID()) // orderOrig.OrigClOrdID = er.OrigClOrdID.getValue(); if (er.IsSetField(Tags.LeavesQty)) { orderOrig.OrderQtyRemaining = Convert.ToInt32(er.LeavesQty.getValue()); } if (er.IsSetField(Tags.CumQty)) { orderOrig.CumQty = Convert.ToInt32(er.CumQty.getValue()); } orderOrig.Memo = er.IsSetField(Tags.Memo) ? er.GetField(Tags.Memo): string.Empty; if (!_db.AtualizarOrdem(orderOrig)) { logger.Info("Problemas na atualizacao da ordem. ClOrdID: " + orderOrig.ClOrdID); return(false); } DropCopyCallbackManager.Instance.EnqueueCallback(orderOrig); return(true); } catch (Exception ex) { logger.Error("_er_Update: Erro na atualizacao dos status da ordem: " + ex.Message, ex); return(false); } }
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); } }
/// <summary> /// Validar os estados da ordem e executar as respectivas operacoes no dicionario de mensagens /// (Mensagens de Execution Report - ER) /// </summary> /// <param name="msg"></param> /// <param name="eType"></param> /// <param name="oStatus"></param> /// <param name="dic"></param> /// Retorna o TOORderSession referente ao Execution Report /// OBS: strChave - no momento esta considerando sempre clOrdID public void VerifyOrderSituationER(ExecutionReport msg, char e, char o, SessionID ss, OrderSessionManager dic, string strChave, out TOOrderSession ord) { ord = null; try { string strOrigClChave = string.Empty; string strExchNumberChave = msg.OrderID.ToString() + "-" + msg.Account.ToString() + "-" + msg.Symbol.ToString(); if (null != ss)// Registry found { // Atualiza o TO com o ExchangeNumber (Tag 37, order ID) TOOrderSession aux = null; dic.GetOrder(strChave, out aux); if (null != aux) { aux.ExchangeNumberID = strExchNumberChave; } // New Order, Exec: New, OrdStatus: New - nao faz nada, somente retorna o TOOrderSession correspondente if (e == ExecType.NEW && o == OrdStatus.NEW) { // Por ser new order, nao se aplica fazer busca pelo exchange number id // pois ainda não foi atualizado dic.GetOrder(strChave, out ord); //ord.ExchangeNumberID = msg.OrderID.getValue() + "-" + msg.Account.getValue() + "-" + msg.Symbol.getValue(); } // Retornar o TOOrderSession correspondente para if (o == OrdStatus.FILLED || o == OrdStatus.PARTIALLY_FILLED) { dic.GetOrder(strChave, out ord, strExchNumberChave); //ord.ExchangeNumberID = msg.OrderID.getValue() + "-" + msg.Account.getValue() + "-" + msg.Symbol.getValue(); } // Stop Order Entry, Exec: New, OrdStatus: New - nao faz nada // No request stop order trigger, Exec: New, OrdStatus: New - nao faz nada // Order with on close, Exec: New, OrdStatus: New // Order with on close attribute is activated when the closing auction starts, Exec: New, Order: New // MinQty order entry, not enough quantity, Exec: new, Order: new // New Order, Exec: Rejected, OrdStatus: Rejected - excluir a chave // New Order, Exec: Suspended, OrdStatus: Suspended // No request, Exec: Trade, OrdStatus: Filled if ((e == ExecType.REJECTED && o == OrdStatus.REJECTED) || (e == ExecType.SUSPENDED && o == OrdStatus.SUSPENDED) || (e == ExecType.TRADE && o == OrdStatus.FILLED) || (e == ExecType.EXPIRED && o == OrdStatus.EXPIRED)) { lock (dic) { TOOrderSession toOS = null; int ret = dic.GetOrder(strChave, out toOS, strExchNumberChave); ord = toOS; toOS = null; if (ret == FindType.EXCHANGE_NUMBER) { dic.RemoveOrder(ord.ChaveDicionario); } else { dic.RemoveOrder(strChave); } } } // Order Modify, Exec: Replace, OrdStatus: Replaced if (e == ExecType.REPLACE && o == OrdStatus.REPLACED) { strOrigClChave = msg.OrigClOrdID.ToString() + "-" + msg.Account.ToString() + "-" + msg.Symbol.ToString(); lock (dic) { //if (dic.ExistOrder(strOrigClChave)) //{ TOOrderSession toOS = null; int ret = dic.GetOrder(strOrigClChave, out toOS, strExchNumberChave, KeyType.ORIGCLORDID); ord = toOS; toOS = null; if (ret == FindType.EXCHANGE_NUMBER) { dic.RemoveOrder(ord.ChaveDicionario); } else { dic.RemoveOrder(strOrigClChave); } //} } } // Cancelation, Exec: Cancelled, OrdStatus: Cancelled // No request FAK Partially Filled, Exec: Canceled, OrdStatus: Canceled // No request FOK Partially Filled, Exec: Canceled, OrdStatus: Canceled // MinQty order entry, not enough quantity rejected, Exec: Cancelled, Order: Cancelled if (e == ExecType.CANCELED && o == OrdStatus.CANCELED) { bool processOrig = true; // Tratamento de outros tipos de timeinforce (para execucao e cancelamento, o orig clord id não é fornecido) if (msg.IsSetField(Tags.TimeInForce)) { switch (msg.TimeInForce.getValue()) { case TimeInForce.IMMEDIATE_OR_CANCEL: case TimeInForce.FILL_OR_KILL: processOrig = false; strOrigClChave = msg.ClOrdID.ToString() + "-" + msg.Account.ToString() + "-" + msg.Symbol.ToString(); break; default: { // Caso o cancelamento tenha partido da bolsa, tambem nao eh fornecido o OrigClOrdID // entao tenta-se utilizar o ClOrdID vindo do ExecutionReport string aux1 = msg.IsSetOrigClOrdID() ? msg.OrigClOrdID.ToString() : msg.ClOrdID.ToString(); strOrigClChave = aux1 + "-" + msg.Account.ToString() + "-" + msg.Symbol.ToString(); } break; } } else { // TimeInForce = 0 (DAY) - Default // Caso o Orig nao esteja na mensagem, tenta-se buscar pelo ClOrdID string aux1 = msg.IsSetOrigClOrdID() ? msg.OrigClOrdID.ToString() : msg.ClOrdID.ToString(); strOrigClChave = aux1 + "-" + msg.Account.ToString() + "-" + msg.Symbol.ToString(); } lock (dic) { TOOrderSession toOS = null; int ret = 0; ret = dic.GetOrder(strOrigClChave, out toOS, strExchNumberChave, KeyType.ORIGCLORDID); if (null != toOS) { ord = toOS; toOS = null; if (ret == FindType.EXCHANGE_NUMBER) { dic.RemoveOrder(ord.ChaveDicionario); } else { dic.RemoveOrder(strOrigClChave); } } if (processOrig) { ret = dic.GetOrder(strChave, out toOS, strExchNumberChave); if (ret == FindType.EXCHANGE_NUMBER) { dic.RemoveOrder(toOS.ChaveDicionario); } else { dic.RemoveOrder(strChave); } toOS = null; } } } } } catch (Exception ex) { logger.Error("VerifyOrderSituationER(): " + ex.Message, ex); } }