示例#1
0
        private void OrderMatch(string data)
        {
            var message = JsonConvert.DeserializeObject <Messages.Matched>(data, JsonSettings);
            var cached  = CachedOrderIDs.Where(o => o.Value.BrokerId.Contains(message.MakerOrderId) || o.Value.BrokerId.Contains(message.TakerOrderId));

            var symbol = ConvertProductId(message.ProductId);

            if (!cached.Any())
            {
                return;
            }

            Log.Trace($"GDAXBrokerage.OrderMatch(): Match: {message.ProductId} {data}");
            var orderId  = cached.First().Key;
            var orderObj = cached.First().Value;

            if (!FillSplit.ContainsKey(orderId))
            {
                FillSplit[orderId] = new GDAXFill(orderObj);
            }

            var split = FillSplit[orderId];

            split.Add(message);

            //is this the total order at once? Is this the last split fill?
            var status = Math.Abs(message.Size) == Math.Abs(cached.Single().Value.Quantity) || Math.Abs(split.OrderQuantity) == Math.Abs(split.TotalQuantity())
                ? OrderStatus.Filled : OrderStatus.PartiallyFilled;

            OrderDirection direction;

            // Messages are always from the perspective of the market maker. Flip it in cases of a market order.
            if (orderObj.Type == OrderType.Market)
            {
                direction = message.Side == "sell" ? OrderDirection.Buy : OrderDirection.Sell;
            }
            else
            {
                direction = message.Side == "sell" ? OrderDirection.Sell : OrderDirection.Buy;
            }

            var orderEvent = new OrderEvent
                             (
                cached.First().Key, symbol, message.Time, status,
                direction,
                message.Price, direction == OrderDirection.Sell ? -message.Size : message.Size,
                GetFee(cached.First().Value), $"GDAX Match Event {direction}"
                             );

            //if we're filled we won't wait for done event
            if (orderEvent.Status == OrderStatus.Filled)
            {
                Orders.Order outOrder = null;
                CachedOrderIDs.TryRemove(cached.First().Key, out outOrder);
            }

            OnOrderEvent(orderEvent);
        }
示例#2
0
        private void EmitFillOrderEvent(Messages.Fill fill, Order order)
        {
            var symbol = _symbolMapper.GetLeanSymbol(fill.ProductId, SecurityType.Crypto, Market.GDAX);

            if (!FillSplit.ContainsKey(order.Id))
            {
                FillSplit[order.Id] = new GDAXFill(order);
            }

            var split = FillSplit[order.Id];

            split.Add(fill);

            // is this the total order at once? Is this the last split fill?
            var isFinalFill = Math.Abs(fill.Size) == Math.Abs(order.Quantity) || Math.Abs(split.OrderQuantity) == Math.Abs(split.TotalQuantity);

            var status = isFinalFill ? OrderStatus.Filled : OrderStatus.PartiallyFilled;

            var direction = fill.Side == "sell" ? OrderDirection.Sell : OrderDirection.Buy;

            var fillPrice    = fill.Price;
            var fillQuantity = direction == OrderDirection.Sell ? -fill.Size : fill.Size;

            var currency = order.PriceCurrency == string.Empty
                ? _algorithm.Securities[symbol].SymbolProperties.QuoteCurrency
                : order.PriceCurrency;

            var orderFee = new OrderFee(new CashAmount(fill.Fee, currency));

            var orderEvent = new OrderEvent
                             (
                order.Id, symbol, fill.CreatedAt, status,
                direction, fillPrice, fillQuantity,
                orderFee, $"GDAX Fill Event {direction}"
                             );

            // when the order is completely filled, we no longer need it in the active order list
            if (orderEvent.Status == OrderStatus.Filled)
            {
                Order outOrder;
                CachedOrderIDs.TryRemove(order.Id, out outOrder);

                PendingOrder removed;
                _pendingOrders.TryRemove(fill.OrderId, out removed);
            }

            OnOrderEvent(orderEvent);
        }
        private void OnMatch(string data)
        {
            // deserialize the current match (trade) message
            var message = JsonConvert.DeserializeObject <Messages.Matched>(data, JsonSettings);

            if (string.IsNullOrEmpty(message.UserId))
            {
                // message received from the "matches" channel
                if (_isDataQueueHandler)
                {
                    EmitTradeTick(message);
                }
                return;
            }

            // message received from the "user" channel, this trade is ours

            // check the list of currently active orders, if the current trade is ours we are either a maker or a taker
            var currentOrder = CachedOrderIDs
                               .FirstOrDefault(o => o.Value.BrokerId.Contains(message.MakerOrderId) || o.Value.BrokerId.Contains(message.TakerOrderId));

            if (currentOrder.Value == null)
            {
                // should never happen, log just in case
                Log.Error($"GDAXBrokerage.OrderMatch(): Unexpected match: {message.ProductId} {data}");
                return;
            }

            Log.Trace($"GDAXBrokerage.OrderMatch(): Match: {message.ProductId} {data}");

            var order = currentOrder.Value;

            if (!FillSplit.ContainsKey(order.Id))
            {
                FillSplit[order.Id] = new GDAXFill(order);
            }

            var split = FillSplit[order.Id];

            split.Add(message);

            var symbol = ConvertProductId(message.ProductId);

            // is this the total order at once? Is this the last split fill?
            var isFinalFill = Math.Abs(message.Size) == Math.Abs(order.Quantity) || Math.Abs(split.OrderQuantity) == Math.Abs(split.TotalQuantity);

            EmitFillOrderEvent(message, symbol, split, isFinalFill);
        }
示例#4
0
        /// <summary>
        /// Creates a new order
        /// </summary>
        /// <param name="order"></param>
        /// <returns></returns>
        public override bool PlaceOrder(Orders.Order order)
        {
            LockStream();

            var req = new RestRequest("/orders", Method.POST);

            dynamic payload = new ExpandoObject();

            payload.size       = Math.Abs(order.Quantity);
            payload.side       = order.Direction.ToString().ToLower();
            payload.type       = ConvertOrderType(order.Type);
            payload.price      = order is LimitOrder ? ((LimitOrder)order).LimitPrice : order is StopMarketOrder ? ((StopMarketOrder)order).StopPrice : 0;
            payload.product_id = ConvertSymbol(order.Symbol);

            if (_algorithm.BrokerageModel.AccountType == AccountType.Margin)
            {
                payload.overdraft_enabled = true;
            }

            req.AddJsonBody(payload);

            GetAuthenticationToken(req);
            var response = RestClient.Execute(req);

            if (response.StatusCode == System.Net.HttpStatusCode.OK && response.Content != null)
            {
                var raw = JsonConvert.DeserializeObject <Messages.Order>(response.Content);

                if (raw == null || raw.Id == null)
                {
                    OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, (int)response.StatusCode, "GDAXBrokerage.PlaceOrder: Error parsing response from place order: " + response.Content));
                    UnlockStream();
                    return(false);
                }

                var brokerId = raw.Id;
                if (CachedOrderIDs.ContainsKey(order.Id))
                {
                    CachedOrderIDs[order.Id].BrokerId.Add(brokerId);
                }
                else
                {
                    order.BrokerId.Add(brokerId);
                    CachedOrderIDs.TryAdd(order.Id, order);
                }

                // Add fill splits in all cases; we'll need to handle market fills too.
                FillSplit.TryAdd(order.Id, new GDAXFill(order));

                // Generate submitted event
                OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, 0, "GDAX Order Event")
                {
                    Status = OrderStatus.Submitted
                });

                OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Information, -1, "GDAXBrokerage.PlaceOrder: Order completed successfully orderid:" + order.Id.ToString()));
                UnlockStream();
                return(true);
            }

            OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, 0, "GDAX Order Event")
            {
                Status = OrderStatus.Invalid
            });

            var message = $"GDAXBrokerage.PlaceOrder: Order failed Order Id: {order.Id} timestamp: {order.Time} quantity: {order.Quantity} content: {response.Content}";

            OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, -1, message));
            UnlockStream();
            return(false);
        }
        private void OrderMatch(string data)
        {
            // deserialize the current match (trade) message
            var message = JsonConvert.DeserializeObject <Messages.Matched>(data, JsonSettings);

            if (_isDataQueueHandler)
            {
                EmitTradeTick(message);
            }

            // check the list of currently active orders, if the current trade is ours we are either a maker or a taker
            var currentOrder = CachedOrderIDs
                               .FirstOrDefault(o => o.Value.BrokerId.Contains(message.MakerOrderId) || o.Value.BrokerId.Contains(message.TakerOrderId));

            if (_pendingGdaxMarketOrderId != null &&
                // order fill for other users
                (currentOrder.Value == null ||
                 // order fill for other order of ours (less likely but may happen)
                 currentOrder.Value.BrokerId[0] != _pendingGdaxMarketOrderId))
            {
                // process all fills for our pending market order
                var fills        = FillSplit[_pendingLeanMarketOrderId];
                var fillMessages = fills.Messages;

                for (var i = 0; i < fillMessages.Count; i++)
                {
                    var fillMessage = fillMessages[i];
                    var isFinalFill = i == fillMessages.Count - 1;

                    // emit all order events with OrderStatus.PartiallyFilled except for the last one which has OrderStatus.Filled
                    EmitFillOrderEvent(fillMessage, fills.Order.Symbol, fills, isFinalFill);
                }

                // clear the pending market order
                _pendingGdaxMarketOrderId = null;
                _pendingLeanMarketOrderId = 0;
            }

            if (currentOrder.Value == null)
            {
                // not our order, nothing else to do here
                return;
            }

            Log.Trace($"GDAXBrokerage.OrderMatch(): Match: {message.ProductId} {data}");

            var order = currentOrder.Value;

            if (order.Type == OrderType.Market)
            {
                // Fill events for this order will be delayed until we receive messages for a different order,
                // so we can know which is the last fill.
                // The market order total filled quantity can be less than the total order quantity,
                // details here: https://github.com/QuantConnect/Lean/issues/1751

                // do not process market order fills immediately, save off the order ids
                _pendingGdaxMarketOrderId = order.BrokerId[0];
                _pendingLeanMarketOrderId = order.Id;
            }

            if (!FillSplit.ContainsKey(order.Id))
            {
                FillSplit[order.Id] = new GDAXFill(order);
            }

            var split = FillSplit[order.Id];

            split.Add(message);

            if (order.Type != OrderType.Market)
            {
                var symbol = ConvertProductId(message.ProductId);

                // is this the total order at once? Is this the last split fill?
                var isFinalFill = Math.Abs(message.Size) == Math.Abs(order.Quantity) || Math.Abs(split.OrderQuantity) == Math.Abs(split.TotalQuantity);

                EmitFillOrderEvent(message, symbol, split, isFinalFill);
            }
        }
        private void OrderMatch(string data)
        {
            var message = JsonConvert.DeserializeObject <Messages.Matched>(data, JsonSettings);

            if (_isDataQueueHandler)
            {
                EmitTradeTick(message);
            }

            var cached = CachedOrderIDs
                         .Where(o => o.Value.BrokerId.Contains(message.MakerOrderId) || o.Value.BrokerId.Contains(message.TakerOrderId))
                         .ToList();

            var symbol = ConvertProductId(message.ProductId);

            if (cached.Count == 0)
            {
                return;
            }

            Log.Trace($"GDAXBrokerage.OrderMatch(): Match: {message.ProductId} {data}");
            var orderId = cached[0].Key;
            var order   = cached[0].Value;

            if (!FillSplit.ContainsKey(orderId))
            {
                FillSplit[orderId] = new GDAXFill(order);
            }

            var split = FillSplit[orderId];

            split.Add(message);

            //is this the total order at once? Is this the last split fill?
            var status = Math.Abs(message.Size) == Math.Abs(order.Quantity) || Math.Abs(split.OrderQuantity) == Math.Abs(split.TotalQuantity())
                ? OrderStatus.Filled : OrderStatus.PartiallyFilled;

            OrderDirection direction;

            // Messages are always from the perspective of the market maker. Flip direction if executed as a taker.
            if (order.BrokerId[0] == message.TakerOrderId)
            {
                direction = message.Side == "sell" ? OrderDirection.Buy : OrderDirection.Sell;
            }
            else
            {
                direction = message.Side == "sell" ? OrderDirection.Sell : OrderDirection.Buy;
            }

            decimal totalOrderFee;

            if (!_orderFees.TryGetValue(orderId, out totalOrderFee))
            {
                totalOrderFee       = GetFee(order);
                _orderFees[orderId] = totalOrderFee;
            }

            // apply order fee on a pro rata basis
            var orderFee = totalOrderFee * Math.Abs(message.Size) / Math.Abs(order.Quantity);

            var orderEvent = new OrderEvent
                             (
                orderId, symbol, message.Time, status,
                direction,
                message.Price, direction == OrderDirection.Sell ? -message.Size : message.Size,
                orderFee, $"GDAX Match Event {direction}"
                             );

            //if we're filled we won't wait for done event
            if (orderEvent.Status == OrderStatus.Filled)
            {
                Order outOrder;
                CachedOrderIDs.TryRemove(orderId, out outOrder);

                decimal outOrderFee;
                _orderFees.TryRemove(orderId, out outOrderFee);
            }

            OnOrderEvent(orderEvent);
        }
示例#7
0
        /// <summary>
        /// Creates a new order
        /// </summary>
        /// <param name="order"></param>
        /// <returns></returns>
        public override bool PlaceOrder(Order order)
        {
            LockStream();

            var req = new RestRequest("/orders", Method.POST);

            dynamic payload = new ExpandoObject();

            payload.size       = Math.Abs(order.Quantity);
            payload.side       = order.Direction.ToString().ToLower();
            payload.type       = ConvertOrderType(order.Type);
            payload.price      = (order as LimitOrder)?.LimitPrice ?? ((order as StopMarketOrder)?.StopPrice ?? 0);
            payload.product_id = ConvertSymbol(order.Symbol);

            if (_algorithm.BrokerageModel.AccountType == AccountType.Margin)
            {
                payload.overdraft_enabled = true;
            }

            var orderProperties = order.Properties as GDAXOrderProperties;

            if (orderProperties != null)
            {
                if (order.Type == OrderType.Limit)
                {
                    payload.post_only = orderProperties.PostOnly;
                }
            }

            req.AddJsonBody(payload);

            GetAuthenticationToken(req);
            var response = ExecuteRestRequest(req, GdaxEndpointType.Private);

            if (response.StatusCode == HttpStatusCode.OK && response.Content != null)
            {
                var raw = JsonConvert.DeserializeObject <Messages.Order>(response.Content);

                if (raw?.Id == null)
                {
                    var errorMessage = $"Error parsing response from place order: {response.Content}";
                    OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, 0, "GDAX Order Event")
                    {
                        Status = OrderStatus.Invalid, Message = errorMessage
                    });
                    OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, (int)response.StatusCode, errorMessage));

                    UnlockStream();
                    return(true);
                }

                if (raw.Status == "rejected")
                {
                    var errorMessage = "Reject reason: " + raw.RejectReason;
                    OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, 0, "GDAX Order Event")
                    {
                        Status = OrderStatus.Invalid, Message = errorMessage
                    });
                    OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, (int)response.StatusCode, errorMessage));

                    UnlockStream();
                    return(true);
                }

                var brokerId = raw.Id;
                if (CachedOrderIDs.ContainsKey(order.Id))
                {
                    CachedOrderIDs[order.Id].BrokerId.Add(brokerId);
                }
                else
                {
                    order.BrokerId.Add(brokerId);
                    CachedOrderIDs.TryAdd(order.Id, order);
                }

                // Add fill splits in all cases; we'll need to handle market fills too.
                FillSplit.TryAdd(order.Id, new GDAXFill(order));

                // Generate submitted event
                OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, 0, "GDAX Order Event")
                {
                    Status = OrderStatus.Submitted
                });
                OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Information, -1, "Order completed successfully orderid:" + order.Id));

                UnlockStream();
                return(true);
            }

            var message = $"Order failed, Order Id: {order.Id} timestamp: {order.Time} quantity: {order.Quantity} content: {response.Content}";

            OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, 0, "GDAX Order Event")
            {
                Status = OrderStatus.Invalid
            });
            OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, -1, message));

            UnlockStream();
            return(true);
        }
示例#8
0
        /// <summary>
        /// Creates a new order
        /// </summary>
        /// <param name="order"></param>
        /// <returns></returns>
        public override bool PlaceOrder(Orders.Order order)
        {
            var req = new RestRequest("/orders", Method.POST);

            dynamic payload = new ExpandoObject();

            payload.size       = Math.Abs(order.Quantity);
            payload.side       = order.Direction.ToString().ToLower();
            payload.type       = ConvertOrderType(order.Type);
            payload.price      = order is LimitOrder ? ((LimitOrder)order).LimitPrice : order is StopMarketOrder ? ((StopMarketOrder)order).StopPrice : 0;
            payload.product_id = ConvertSymbol(order.Symbol);

            if (_algorithm.BrokerageModel.AccountType == AccountType.Margin)
            {
                payload.overdraft_enabled = true;
            }

            req.AddJsonBody(payload);

            GetAuthenticationToken(req);
            var response = RestClient.Execute(req);

            if (response.StatusCode == System.Net.HttpStatusCode.OK && response.Content != null)
            {
                dynamic raw = JsonConvert.DeserializeObject <dynamic>(response.Content);

                if (raw == null || raw.id == null)
                {
                    Log.Error("GDAXBrokerage.PlaceOrder: Error parsing response from place order");

                    return(false);
                }

                string brokerId = raw.id;
                if (CachedOrderIDs.ContainsKey(order.Id))
                {
                    CachedOrderIDs[order.Id].BrokerId.Add(brokerId);
                }
                else
                {
                    order.BrokerId.Add(brokerId);
                    CachedOrderIDs.TryAdd(order.Id, order);
                }

                if (order.Type != OrderType.Market)
                {
                    FillSplit.TryAdd(order.Id, new GDAXFill(order));
                }

                OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, 0, "GDAX Order Event")
                {
                    Status = OrderStatus.Submitted
                });

                if (order.Type == OrderType.Market)
                {
                    OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, (decimal)raw.fill_fees, "GDAX Order Event")
                    {
                        Status = OrderStatus.Filled
                    });
                    Orders.Order outOrder = null;
                    CachedOrderIDs.TryRemove(order.Id, out outOrder);
                }

                Log.Trace("GDAXBrokerage.PlaceOrder: Order completed successfully orderid:" + order.Id.ToString());
                return(true);
            }

            OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, 0, "GDAX Order Event")
            {
                Status = OrderStatus.Invalid
            });
            Log.Trace("GDAXBrokerage.PlaceOrder: Order failed Order Id: " + order.Id + " timestamp:" + order.Time + " quantity: " + order.Quantity.ToString()
                      + " content:" + response.Content);
            return(false);
        }