Exemple #1
0
        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));
            }
        }
Exemple #2
0
        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);
        }
Exemple #3
0
        /// <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);
            }
        }
Exemple #4
0
        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));
            }
        }
Exemple #5
0
 /// <summary>
 ///     Выплюнуть из раутера сообщение
 /// </summary>
 /// <param name="message">
 ///     Сообщение
 /// </param>
 internal void Transmit(OrderStateChangeMessage message)
 {
     OnMessageReceived(message);
 }
Exemple #6
0
        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);
            }
        }
Exemple #7
0
        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);
            }
        }