public async void Visit(KillOrderTransaction transaction) { try { var reply = await transClient.CancelOrder(new CancelOrder { order_id = transaction.OrderExchangeId }); OnMessageReceived(TransactionReply.Accepted(transaction, reply.code)); var oscm = new OrderStateChangeMessage { TransactionId = transaction.TransactionId, OrderExchangeId = reply.code, Quantity = reply.qty, ActiveQuantity = reply.qtyLeft, FilledQuantity = reply.qty_executed, ChangeTime = DateTime.Now, State = OrderState.Cancelled }; OnMessageReceived(oscm); } catch (FTEException e) { OnMessageReceived(TransactionReply.Rejected(transaction, e.Message)); } }
private void InfoClient_OnInfoOrder(InfoOrder order) { if (!accounts.ContainsKey(order.account)) { return; } var activeQuantity = order.qtyLeft; OrderState state; switch (order.status) { case OrderStatus.CANCELED: state = !string.IsNullOrEmpty(order.res_code) && order.res_code != "ENF_CANC" // TODO Hardcode волшебной строки ? OrderState.Error : OrderState.Cancelled; activeQuantity = 0; break; case OrderStatus.MATCHED: state = OrderState.Filled; break; case OrderStatus.FREEZED: case OrderStatus.QUEUED: state = order.qty_executed > 0 ? OrderState.PartiallyFilled : OrderState.Active; break; case OrderStatus.WAIT_APPROVAL: default: return; } //long filledQty = order.ActiveQty - (long)infoOrder.qtyLeft; Guid.TryParseExact(order.trn, "N", out var trId); var oscm = new OrderStateChangeMessage { TransactionId = trId, OrderExchangeId = order.code, Quantity = order.qty, ActiveQuantity = activeQuantity, FilledQuantity = order.qty_executed, // TODO Price = PriceHelper.ToPrice(order.price), ChangeTime = DateTime.Now, // TODO State = state }; OnMessageReceived(oscm); }
/// <summary> /// Обработка заявки как уже существующей /// </summary> private OrderStateChangeMessage HandleOrderStateAsOrderStateChange(Order order, OrderStatus message) { Logger.Debug().Print( "Order state change received", LogFields.ExchangeOrderId(message.order_id), LogFields.ChainOrderId(message.chain_order_id), LogFields.State(ConvertionHelper.GetOrderState(message)), LogFields.AccountId(message.account_id), LogFields.ExecOrderId(message.exec_order_id) ); using (ordersLock.Lock()) { Guid transactionId; if (!Guid.TryParse(message.order.cl_order_id, out transactionId)) { Logger.Debug().Print( $"Unable to parse {LogFieldNames.TransactionId} from {LogFields.ClOrderId(message.order.cl_order_id)}. Use order's {LogFieldNames.TransactionId}" ); transactionId = order.TransactionId; } var change = new OrderStateChangeMessage { OrderExchangeId = message.chain_order_id, TransactionId = transactionId, ActiveQuantity = message.remaining_qty, FilledQuantity = message.fill_qty, Price = instrumentResolver.ConvertPriceBack(order.Instrument, message.order.limit_price), ChangeTime = adapter.ResolveDateTime(message.status_utc_time), Quantity = message.remaining_qty + message.fill_qty, State = ConvertionHelper.GetOrderState(message) }; // Обработка изменения order_id (происходит при модификации заявки) if (order.OrderExchangeId != change.OrderExchangeId) { ordersByOrderExchangeId.Remove(order.OrderExchangeId); ordersByOrderExchangeId.Add(change.OrderExchangeId, order); } orderStatusByChainOrderId[message.chain_order_id] = message; //order.AcceptStateMessage(change); return(change); } }
public async void Visit(NewOrderTransaction transaction) { try { //internal async Task<string> ResolveInstrumentAsync(Instrument instrument) //{ // var data = await instrumentConverter.ResolveInstrumentAsync(this, instrument); // return data?.Symbol; //} var data = await connector.ResolveInstrumentDataAsync(transaction.Instrument); if (data == null) { OnMessageReceived(TransactionReply.Rejected(transaction, $"Unable to get symbol of {transaction.Instrument}")); return; } if (!accounts.TryGetValue(transaction.Account, out var account)) { OnMessageReceived(TransactionReply.Rejected(transaction, $"Не найден счет {transaction.Account}")); return; } var order = new SpimexAdapter.FTE.Order { account = account.code, firm = account.firm, client = account.client, security = data.Symbol, board = data.Board, @type = transaction.GetOrderType(), @params = transaction.GetOrderParams(), buy_sell = transaction.GetOperation(), price = PriceHelper.FromPrice(transaction.Price), qty = transaction.Quantity, trn = transaction.TransactionId.ToString("N"), isMarketMaker = transaction.IsMarketMakerOrder, comment = transaction.Comment, }; var reply = await transClient.SendOrder(order); OnMessageReceived(TransactionReply.Accepted(transaction, reply.code)); OrderState?state = null; switch (reply.status) { case OrderStatus.CANCELED: state = OrderState.Cancelled; break; case OrderStatus.MATCHED: state = OrderState.Filled; break; case OrderStatus.FREEZED: case OrderStatus.QUEUED: state = reply.qty_executed > 0 ? OrderState.PartiallyFilled : OrderState.Active; break; } var oscm = new OrderStateChangeMessage { TransactionId = transaction.TransactionId, OrderExchangeId = reply.code, Quantity = order.qty, ActiveQuantity = reply.qtyLeft, FilledQuantity = reply.qty_executed, Price = PriceHelper.ToPrice(order.price), ChangeTime = DateTime.Now, State = state }; OnMessageReceived(oscm); } catch (FTEException e) { OnMessageReceived(TransactionReply.Rejected(transaction, e.Message)); } }
/// <summary> /// Выплюнуть из раутера сообщение /// </summary> /// <param name="message"> /// Сообщение /// </param> internal void Transmit(OrderStateChangeMessage message) { OnMessageReceived(message); }
public override void orderStatus( int orderId, string status, int filled, int remaining, double avgFillPrice, int permId, int parentId, double lastFillPrice, int clientId, string whyHeld) { using (orderInfoContainerLock.Lock()) { OrderInfo orderInfo; if (!orderInfoContainer.TryGetByTickerId(orderId, out orderInfo) && !orderInfoContainer.TryGetByPermId(permId, out orderInfo)) { // Неизвестная заявка, ничего не поделаешь return; } var orderState = IBUtils.ParseOrderState(status) ?? orderInfo.State; // Если заявка снялась по запросу из OW, надо выплюнуть TransactionReply if (orderState == OrderState.Cancelled && orderInfo.KillOrderTransactionId != null && !orderInfo.KillOrderTransactionReplySent) { connector.IBOrderRouter.Transmit(new TransactionReply { TransactionId = orderInfo.KillOrderTransactionId.Value, Success = true }); orderInfo.KillOrderTransactionReplySent = true; } // Вычисляем OSCM var message = new OrderStateChangeMessage { ActiveQuantity = (uint)remaining, ChangeTime = DateTime.Now, FilledQuantity = (uint)filled, OrderExchangeId = permId.ToString(CultureInfo.InvariantCulture), Quantity = (uint?)(filled + remaining), TransactionId = orderInfo.NewOrderTransactionId, State = orderState }; if (!double.IsNaN(lastFillPrice) && Math.Abs(lastFillPrice) > double.Epsilon) { message.Price = (decimal?)lastFillPrice; } if (message.State != null) { orderInfo.State = message.State.Value; } if (message.ActiveQuantity != null) { orderInfo.ActiveQuantity = (int)message.ActiveQuantity.Value; } // Выплевываем OSCM connector.IBOrderRouter.Transmit(message); // Пробуем отправить ожидающие филы orderInfoContainer.ProcessPendingFills(orderInfo); } }
public override void error(int id, int errorCode, string errorMsg) { // See https://www.interactivebrokers.com/en/software/api/apiguide/tables/api_message_codes.htm switch (errorCode) { case 504: // Not connected case 1100: // Connectivity between IB and TWS has been lost case 1300: // TWS socket port has been reset and this connection is being dropped. Please reconnect on the new port - <port_num> Log.Error().Print(errorMsg); errorEvent.Set(); connectedEvent.Reset(); disconnectedEvent.Set(); connector.RaiseConnectionStatusChanged(ConnectionStatus.Disconnected); break; case 1102: Log.Info().Print(errorMsg); connectedEvent.Set(); disconnectedEvent.Reset(); connector.RaiseConnectionStatusChanged(ConnectionStatus.Connected); break; case 162: // Historical market data Service error message. // Historical data request pacing violation // HMDS query returned no data if (errorMsg.IndexOf("query returned no data", StringComparison.OrdinalIgnoreCase) >= 0) { // TODO ебаный стыд HistoricalDepthTickers.NoMoreData(id); } else if (errorMsg.IndexOf("Historical data request pacing violation", StringComparison.OrdinalIgnoreCase) >= 0) { // TODO ебаный стыд HistoricalDepthTickers.PaceViolation(id); } else if (errorMsg.IndexOf("Time length exceed max", StringComparison.OrdinalIgnoreCase) >= 0) { // TODO ебаный стыд HistoricalDepthTickers.TimeLengthExceedMax(id); } else { HistoricalDepthTickers.Fail(id, errorMsg); } break; case 200: // No security definition has been found for the request. { PendingTestResult testResult; if (pendingTestResultTickerContainer.TryGetPendingTestResult(id, out testResult)) { testResult.Reject(); pendingTestResultTickerContainer.RemoveTickerId(id); } } HistoricalDepthTickers.Fail(id, errorMsg); connector.ContractContainer.RejectContract(id); Log.Error().Print(errorMsg); break; case 202: // Order Canceled break; case 501: // Already connected case 2104: // A market data farm is connected. case 2106: // A historical data farm is connected. Log.Info().Print(errorMsg); break; case 10092: // Deep market data is not supported for this combination of security type/exchange Instrument instrument; if (marketDepthTickers.TryGetInstrument(id, out instrument)) { Log.Error().Print($"Unable to retreive order book on {instrument.Code}. {errorMsg.Preformatted()}"); break; } goto default; case 110: // The price does not conform to the minimum price variation for this contract. case 201: // Order rejected - reason:THIS ACCOUNT MAY NOT HOLD SHORT STOCK POSITIONS. case 203: // The security <security> is not available or allowed for this account HistoricalDepthTickers.Fail(id, errorMsg); using (orderInfoContainerLock.Lock()) { OrderInfo orderInfo; if (!orderInfoContainer.TryGetByTickerId(id, out orderInfo)) { // Неизвестная заявка, ничего не поделаешь goto default; } orderInfo.State = OrderState.Error; // Вычисляем OSCM var oscm = new OrderStateChangeMessage { TransactionId = orderInfo.NewOrderTransactionId, ActiveQuantity = (uint)orderInfo.ActiveQuantity, ChangeTime = DateTime.Now, FilledQuantity = (uint)(orderInfo.Quantity - orderInfo.ActiveQuantity), OrderExchangeId = orderInfo.PermId.ToString(CultureInfo.InvariantCulture), Quantity = (uint?)(orderInfo.Quantity), State = OrderState.Error }; // Выплевываем OSCM connector.IBOrderRouter.Transmit(oscm); Log.Error().Print($"Unable to place order {orderInfo.PermId}. {errorMsg.Preformatted()}"); } break; default: // Warning codes range if (errorCode >= 2100 && errorCode <= 2110) { Log.Warn().Print(errorMsg); } else { PendingTestResult testResult; Instrument subscriptionInstrument; if (pendingTestResultTickerContainer.TryGetPendingTestResult(id, out testResult)) { testResult.Reject(); pendingTestResultTickerContainer.RemoveTickerId(id); } else if (marketDataTickers.TryGetInstrumentAndPendingTestResult(id, out subscriptionInstrument, out testResult)) { testResult?.Reject(); marketDataTickers.RemoveTickerId(id); } Log.Error().Print($"IB error #{errorCode}: {errorMsg.Preformatted()}"); errorEvent.Set(); HistoricalDepthTickers.Fail(id, errorMsg); } break; } // TODO если пришла ошибка по тикеру - тикер нужно убить? }
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); } }