/// <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 EmitFillOrder(TradeExecutionUpdate update) { try { var brokerId = update.OrderId.ToStringInvariant(); var order = CachedOrderIDs .FirstOrDefault(o => o.Value.BrokerId.Contains(brokerId)) .Value; if (order == null) { order = _algorithm.Transactions.GetOrderByBrokerageId(brokerId); if (order == null) { Log.Error($"BitfinexBrokerage.EmitFillOrder(): order not found: BrokerId: {brokerId}"); return; } } var symbol = _symbolMapper.GetLeanSymbol(update.Symbol, SecurityType.Crypto, Market.Bitfinex); var fillPrice = update.ExecPrice; var fillQuantity = update.ExecAmount; var direction = fillQuantity < 0 ? OrderDirection.Sell : OrderDirection.Buy; var updTime = Time.UnixMillisecondTimeStampToDateTime(update.MtsCreate); var orderFee = new OrderFee(new CashAmount(Math.Abs(update.Fee), GetLeanCurrency(update.FeeCurrency))); var status = OrderStatus.Filled; if (fillQuantity != order.Quantity) { decimal totalFillQuantity; _fills.TryGetValue(order.Id, out totalFillQuantity); totalFillQuantity += fillQuantity; _fills[order.Id] = totalFillQuantity; status = totalFillQuantity == order.Quantity ? OrderStatus.Filled : OrderStatus.PartiallyFilled; } if (_algorithm.BrokerageModel.AccountType == AccountType.Cash && order.Direction == OrderDirection.Buy) { var symbolProperties = _symbolPropertiesDatabase.GetSymbolProperties(symbol.ID.Market, symbol, symbol.SecurityType, AccountBaseCurrency); Crypto.DecomposeCurrencyPair(symbol, symbolProperties, out var baseCurrency, out var _); if (orderFee.Value.Currency != baseCurrency) { OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, "UnexpectedFeeCurrency", $"Unexpected fee currency {orderFee.Value.Currency} for symbol {symbol}. OrderId {order.Id}. BrokerageOrderId {brokerId}. " + "This error can happen because your account is Margin type and Lean is configured to be Cash type or while using Cash type the Bitfinex account fee settings are set to 'Asset Trading Fee' and should be set to 'Currency Exchange Fee'.")); } else { // fees are debited in the base currency, so we have to subtract them from the filled quantity fillQuantity -= orderFee.Value.Amount; orderFee = new ModifiedFillQuantityOrderFee(orderFee.Value); } } var orderEvent = new OrderEvent ( order.Id, symbol, updTime, status, direction, fillPrice, fillQuantity, orderFee, $"Bitfinex Order Event {direction}" ); // if the order is closed, we no longer need it in the active order list if (status == OrderStatus.Filled) { Order outOrder; CachedOrderIDs.TryRemove(order.Id, out outOrder); decimal ignored; _fills.TryRemove(order.Id, out ignored); var clientOrderId = _orderMap.FirstOrDefault(x => x.Value.BrokerId.Contains(brokerId)).Key; if (clientOrderId > 0) { _orderMap.TryRemove(clientOrderId, out outOrder); } } OnOrderEvent(orderEvent); } catch (Exception e) { Log.Error(e); throw; } }
/// <summary> /// Gets all orders not yet closed /// </summary> /// <returns></returns> public override List <Order> GetOpenOrders() { var endpoint = GetEndpoint("auth/r/orders"); var request = new RestRequest(endpoint, Method.POST); var parameters = new JsonObject(); request.AddJsonBody(parameters.ToString()); SignRequest(request, endpoint, parameters); var response = ExecuteRestRequest(request); if (response.StatusCode != HttpStatusCode.OK) { throw new Exception($"BitfinexBrokerage.GetOpenOrders: request failed: [{(int)response.StatusCode}] {response.StatusDescription}, Content: {response.Content}, ErrorMessage: {response.ErrorMessage}"); } var orders = JsonConvert.DeserializeObject <Messages.Order[]>(response.Content) .Where(OrderFilter(_algorithm.BrokerageModel.AccountType)); var list = new List <Order>(); foreach (var item in orders) { Order order; if (item.Type.Replace("EXCHANGE", "").Trim() == "MARKET") { order = new MarketOrder { Price = item.Price }; } else if (item.Type.Replace("EXCHANGE", "").Trim() == "LIMIT") { order = new LimitOrder { LimitPrice = item.Price }; } else if (item.Type.Replace("EXCHANGE", "").Trim() == "STOP") { order = new StopMarketOrder { StopPrice = item.Price }; } else { OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, (int)response.StatusCode, "BitfinexBrokerage.GetOpenOrders: Unsupported order type returned from brokerage: " + item.Type)); continue; } order.Quantity = item.Amount; order.BrokerId = new List <string> { item.Id.ToStringInvariant() }; order.Symbol = _symbolMapper.GetLeanSymbol(item.Symbol); order.Time = Time.UnixMillisecondTimeStampToDateTime(item.MtsCreate); order.Status = ConvertOrderStatus(item); order.Price = item.Price; list.Add(order); } foreach (var item in list) { if (item.Status.IsOpen()) { var cached = CachedOrderIDs .FirstOrDefault(c => c.Value.BrokerId.Contains(item.BrokerId.First())); if (cached.Value != null) { CachedOrderIDs[cached.Key] = item; } } } return(list); }
/// <summary> /// Gets all orders not yet closed /// </summary> /// <returns></returns> public override List <Order> GetOpenOrders() { var list = new List <Order>(); var endpoint = GetEndpoint("orders"); var request = new RestRequest(endpoint, Method.POST); JsonObject payload = new JsonObject(); payload.Add("request", endpoint); payload.Add("nonce", GetNonce().ToStringInvariant()); request.AddJsonBody(payload.ToString()); SignRequest(request, payload.ToString()); var response = ExecuteRestRequest(request); if (response.StatusCode != HttpStatusCode.OK) { throw new Exception($"BitfinexBrokerage.GetOpenOrders: request failed: [{(int)response.StatusCode}] {response.StatusDescription}, Content: {response.Content}, ErrorMessage: {response.ErrorMessage}"); } var orders = JsonConvert.DeserializeObject <Messages.Order[]>(response.Content) .Where(OrderFilter(_algorithm.BrokerageModel.AccountType)); foreach (var item in orders) { Order order; if (item.Type.Replace("exchange", "").Trim() == "market") { order = new MarketOrder { Price = item.Price }; } else if (item.Type.Replace("exchange", "").Trim() == "limit") { order = new LimitOrder { LimitPrice = item.Price }; } else if (item.Type.Replace("exchange", "").Trim() == "stop") { order = new StopMarketOrder { StopPrice = item.Price }; } else { OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, (int)response.StatusCode, "BitfinexBrokerage.GetOpenOrders: Unsupported order type returned from brokerage: " + item.Type)); continue; } order.Quantity = item.Side == "sell" ? -item.OriginalAmount : item.OriginalAmount; order.BrokerId = new List <string> { item.Id }; order.Symbol = _symbolMapper.GetLeanSymbol(item.Symbol); order.Time = Time.UnixTimeStampToDateTime(item.Timestamp); order.Status = ConvertOrderStatus(item); order.Price = item.Price; list.Add(order); } foreach (var item in list) { if (item.Status.IsOpen()) { var cached = CachedOrderIDs.Where(c => c.Value.BrokerId.Contains(item.BrokerId.First())); if (cached.Any()) { CachedOrderIDs[cached.First().Key] = item; } } } return(list); }
private bool SubmitOrder(string endpoint, Order order) { LockStream(); var payload = new JsonObject(); payload.Add("request", endpoint); payload.Add("nonce", GetNonce().ToStringInvariant()); payload.Add("symbol", _symbolMapper.GetBrokerageSymbol(order.Symbol)); payload.Add("amount", Math.Abs(order.Quantity).ToString(CultureInfo.InvariantCulture)); payload.Add("side", ConvertOrderDirection(order.Direction)); payload.Add("type", ConvertOrderType(_algorithm.BrokerageModel.AccountType, order.Type)); payload.Add("price", GetOrderPrice(order).ToString(CultureInfo.InvariantCulture)); if (order.BrokerId.Any()) { payload.Add("order_id", Parse.Long(order.BrokerId.FirstOrDefault())); } var orderProperties = order.Properties as BitfinexOrderProperties; if (orderProperties != null) { if (order.Type == OrderType.Limit) { payload.Add("is_hidden", orderProperties.Hidden); payload.Add("is_postonly", orderProperties.PostOnly); } } var request = new RestRequest(endpoint, Method.POST); request.AddJsonBody(payload.ToString()); SignRequest(request, payload.ToString()); var response = ExecuteRestRequest(request); var orderFee = OrderFee.Zero; if (response.StatusCode == HttpStatusCode.OK) { var raw = JsonConvert.DeserializeObject <Messages.Order>(response.Content); if (string.IsNullOrEmpty(raw?.Id)) { var errorMessage = $"Error parsing response from place order: {response.Content}"; OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, orderFee, "Bitfinex 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.Clear(); CachedOrderIDs[order.Id].BrokerId.Add(brokerId); } else { order.BrokerId.Add(brokerId); CachedOrderIDs.TryAdd(order.Id, order); } // Generate submitted event OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, orderFee, "Bitfinex Order Event") { Status = OrderStatus.Submitted }); Log.Trace($"Order submitted 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, orderFee, "Bitfinex Order Event") { Status = OrderStatus.Invalid }); OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, -1, message)); UnlockStream(); return(true); }
/// <summary> /// Gets all orders not yet closed /// </summary> /// <returns></returns> public override List <Order> GetOpenOrders() { var orders = _apiClient.GetOpenOrders(); List <Order> list = new List <Order>(); foreach (var item in orders) { Order order; switch (item.Type.LazyToUpper()) { case "MARKET": order = new MarketOrder { Price = item.Price }; break; case "LIMIT": case "LIMIT_MAKER": order = new LimitOrder { LimitPrice = item.Price }; break; case "STOP_LOSS": case "TAKE_PROFIT": order = new StopMarketOrder { StopPrice = item.StopPrice, Price = item.Price }; break; case "STOP_LOSS_LIMIT": case "TAKE_PROFIT_LIMIT": order = new StopLimitOrder { StopPrice = item.StopPrice, LimitPrice = item.Price }; break; default: OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, -1, "BinanceBrokerage.GetOpenOrders: Unsupported order type returned from brokerage: " + item.Type)); continue; } order.Quantity = item.Quantity; order.BrokerId = new List <string> { item.Id }; order.Symbol = _symbolMapper.GetLeanSymbol(item.Symbol, SecurityType.Crypto, Market.Binance); order.Time = Time.UnixMillisecondTimeStampToDateTime(item.Time); order.Status = ConvertOrderStatus(item.Status); order.Price = item.Price; if (order.Status.IsOpen()) { var cached = CachedOrderIDs.Where(c => c.Value.BrokerId.Contains(order.BrokerId.First())).ToList(); if (cached.Any()) { CachedOrderIDs[cached.First().Key] = order; } } list.Add(order); } return(list); }
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); }
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); } }
/// <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); }
/// <summary> /// Gets all orders not yet closed /// </summary> /// <returns></returns> public override List <Order> GetOpenOrders() { var list = new List <Order>(); var req = new RestRequest("/orders?status=open&status=pending", Method.GET); GetAuthenticationToken(req); var response = ExecuteRestRequest(req, GdaxEndpointType.Private); if (response.StatusCode != HttpStatusCode.OK) { throw new Exception($"GDAXBrokerage.GetOpenOrders: request failed: [{(int)response.StatusCode}] {response.StatusDescription}, Content: {response.Content}, ErrorMessage: {response.ErrorMessage}"); } var orders = JsonConvert.DeserializeObject <Messages.Order[]>(response.Content); foreach (var item in orders) { Order order; if (item.Type == "market") { order = new MarketOrder { Price = item.Price }; } else if (item.Type == "limit") { order = new LimitOrder { LimitPrice = item.Price }; } else if (item.Type == "stop") { order = new StopMarketOrder { StopPrice = item.Price }; } else { OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, (int)response.StatusCode, "GDAXBrokerage.GetOpenOrders: Unsupported order type returned from brokerage: " + item.Type)); continue; } order.Quantity = item.Side == "sell" ? -item.Size : item.Size; order.BrokerId = new List <string> { item.Id }; order.Symbol = ConvertProductId(item.ProductId); order.Time = DateTime.UtcNow; order.Status = ConvertOrderStatus(item); order.Price = item.Price; list.Add(order); } foreach (var item in list) { if (item.Status.IsOpen()) { var cached = CachedOrderIDs.Where(c => c.Value.BrokerId.Contains(item.BrokerId.First())); if (cached.Any()) { CachedOrderIDs[cached.First().Key] = item; } } } return(list); }
private void EmitFillOrder(TradeExecutionUpdate update) { try { var brokerId = update.OrderId.ToStringInvariant(); var order = CachedOrderIDs .FirstOrDefault(o => o.Value.BrokerId.Contains(brokerId)) .Value; if (order == null) { order = _algorithm.Transactions.GetOrderByBrokerageId(brokerId); if (order == null) { Log.Error($"EmitFillOrder(): order not found: BrokerId: {brokerId}"); return; } } var symbol = _symbolMapper.GetLeanSymbol(update.Symbol); var fillPrice = update.ExecPrice; var fillQuantity = update.ExecAmount; var direction = fillQuantity < 0 ? OrderDirection.Sell : OrderDirection.Buy; var updTime = Time.UnixMillisecondTimeStampToDateTime(update.MtsCreate); var orderFee = new OrderFee(new CashAmount(Math.Abs(update.Fee), update.FeeCurrency)); var status = OrderStatus.Filled; if (fillQuantity != order.Quantity) { decimal totalFillQuantity; _fills.TryGetValue(order.Id, out totalFillQuantity); totalFillQuantity += fillQuantity; _fills[order.Id] = totalFillQuantity; status = totalFillQuantity == order.Quantity ? OrderStatus.Filled : OrderStatus.PartiallyFilled; } if (_algorithm.BrokerageModel.AccountType == AccountType.Cash && order.Direction == OrderDirection.Buy) { // fees are debited in the base currency, so we have to subtract them from the filled quantity fillQuantity -= orderFee.Value.Amount; orderFee = new ModifiedFillQuantityOrderFee(orderFee.Value); } var orderEvent = new OrderEvent ( order.Id, symbol, updTime, status, direction, fillPrice, fillQuantity, orderFee, $"Bitfinex Order Event {direction}" ); // if the order is closed, we no longer need it in the active order list if (status == OrderStatus.Filled) { Order outOrder; CachedOrderIDs.TryRemove(order.Id, out outOrder); decimal ignored; _fills.TryRemove(order.Id, out ignored); var clientOrderId = _orderMap.FirstOrDefault(x => x.Value.BrokerId.Contains(brokerId)).Key; if (clientOrderId > 0) { _orderMap.TryRemove(clientOrderId, out outOrder); } } OnOrderEvent(orderEvent); } catch (Exception e) { Log.Error(e); throw; } }
private void EmitFillOrder(string[] entries) { try { var brokerId = entries[4]; var order = CachedOrderIDs .FirstOrDefault(o => o.Value.BrokerId.Contains(brokerId)) .Value; if (order == null) { order = _algorithm.Transactions.GetOrderByBrokerageId(brokerId); if (order == null) { // not our order, nothing else to do here return; } } var symbol = _symbolMapper.GetLeanSymbol(entries[2]); var fillPrice = decimal.Parse(entries[6], NumberStyles.Float, CultureInfo.InvariantCulture); var fillQuantity = decimal.Parse(entries[5], NumberStyles.Float, CultureInfo.InvariantCulture); var direction = fillQuantity < 0 ? OrderDirection.Sell : OrderDirection.Buy; var updTime = Time.UnixTimeStampToDateTime(double.Parse(entries[3], NumberStyles.Float, CultureInfo.InvariantCulture)); var orderFee = new OrderFee(new CashAmount( Math.Abs(decimal.Parse(entries[9], NumberStyles.Float, CultureInfo.InvariantCulture)), entries[10] )); var status = OrderStatus.Filled; if (fillQuantity != order.Quantity) { decimal totalFillQuantity; _fills.TryGetValue(order.Id, out totalFillQuantity); totalFillQuantity += fillQuantity; _fills[order.Id] = totalFillQuantity; status = totalFillQuantity == order.Quantity ? OrderStatus.Filled : OrderStatus.PartiallyFilled; } var orderEvent = new OrderEvent ( order.Id, symbol, updTime, status, direction, fillPrice, fillQuantity, orderFee, $"Bitfinex Order Event {direction}" ); // if the order is closed, we no longer need it in the active order list if (status == OrderStatus.Filled) { Order outOrder; CachedOrderIDs.TryRemove(order.Id, out outOrder); decimal ignored; _fills.TryRemove(order.Id, out ignored); } OnOrderEvent(orderEvent); } catch (Exception e) { Log.Error(e); throw; } }
/// <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); }