/// <summary> /// Places a new order and assigns a new broker ID to the order /// </summary> /// <param name="order">The order to be placed</param> /// <returns>True if the request for a new order has been placed, false otherwise</returns> public override bool PlaceOrder(Order order) { var requestParams = new Dictionary <string, string> { { "instrument", SymbolMapper.GetBrokerageSymbol(order.Symbol) }, { "units", Convert.ToInt32(order.AbsoluteQuantity).ToString() } }; const int orderFee = 0; var marketOrderFillQuantity = 0; var marketOrderRemainingQuantity = 0; decimal marketOrderFillPrice; var marketOrderStatus = OrderStatus.Filled; order.PriceCurrency = SecurityProvider.GetSecurity(order.Symbol).SymbolProperties.QuoteCurrency; PopulateOrderRequestParameters(order, requestParams); lock (Locker) { var postOrderResponse = PostOrderAsync(requestParams); if (postOrderResponse == null) { return(false); } // Market orders are special, due to the callback not being triggered always, if the order was filled, // find fill quantity and price and inform the user if (postOrderResponse.tradeOpened != null && postOrderResponse.tradeOpened.id > 0) { if (order.Type == OrderType.Market) { marketOrderFillQuantity = postOrderResponse.tradeOpened.units; } else { order.BrokerId.Add(postOrderResponse.tradeOpened.id.ToString()); } } if (postOrderResponse.tradeReduced != null && postOrderResponse.tradeReduced.id > 0) { if (order.Type == OrderType.Market) { marketOrderFillQuantity = postOrderResponse.tradeReduced.units; } else { order.BrokerId.Add(postOrderResponse.tradeReduced.id.ToString()); } } if (postOrderResponse.orderOpened != null && postOrderResponse.orderOpened.id > 0) { if (order.Type != OrderType.Market) { order.BrokerId.Add(postOrderResponse.orderOpened.id.ToString()); } } if (postOrderResponse.tradesClosed != null && postOrderResponse.tradesClosed.Count > 0) { marketOrderFillQuantity += postOrderResponse.tradesClosed .Where(trade => order.Type == OrderType.Market) .Sum(trade => trade.units); } marketOrderFillPrice = Convert.ToDecimal(postOrderResponse.price); marketOrderRemainingQuantity = Convert.ToInt32(order.AbsoluteQuantity - Math.Abs(marketOrderFillQuantity)); if (marketOrderRemainingQuantity > 0) { marketOrderStatus = OrderStatus.PartiallyFilled; // The order was not fully filled lets save it so the callback can inform the user PendingFilledMarketOrders[order.Id] = marketOrderStatus; } } OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, orderFee) { Status = OrderStatus.Submitted }); // If 'marketOrderRemainingQuantity < order.AbsoluteQuantity' is false it means the order was not even PartiallyFilled, wait for callback if (order.Type == OrderType.Market && marketOrderRemainingQuantity < order.AbsoluteQuantity) { OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, orderFee) { Status = marketOrderStatus, FillPrice = marketOrderFillPrice, FillQuantity = marketOrderFillQuantity * Math.Sign(order.Quantity) }); } return(true); }
/// <summary> /// Places a new order and assigns a new broker ID to the order /// </summary> /// <param name="order">The order to be placed</param> /// <returns>True if the request for a new order has been placed, false otherwise</returns> public override bool PlaceOrder(Order order) { var requestParams = new Dictionary <string, string> { { "instrument", SymbolMapper.GetBrokerageSymbol(order.Symbol) }, { "units", Convert.ToInt32(order.AbsoluteQuantity).ToString() } }; PopulateOrderRequestParameters(order, requestParams); var postOrderResponse = PostOrderAsync(requestParams); if (postOrderResponse == null) { return(false); } // if market order, find fill quantity and price var marketOrderFillPrice = 0m; if (order.Type == OrderType.Market) { marketOrderFillPrice = Convert.ToDecimal(postOrderResponse.price); } var marketOrderFillQuantity = 0; if (postOrderResponse.tradeOpened != null && postOrderResponse.tradeOpened.id > 0) { if (order.Type == OrderType.Market) { marketOrderFillQuantity = postOrderResponse.tradeOpened.units; } else { order.BrokerId.Add(postOrderResponse.tradeOpened.id.ToString()); } } if (postOrderResponse.tradeReduced != null && postOrderResponse.tradeReduced.id > 0) { if (order.Type == OrderType.Market) { marketOrderFillQuantity = postOrderResponse.tradeReduced.units; } else { order.BrokerId.Add(postOrderResponse.tradeReduced.id.ToString()); } } if (postOrderResponse.orderOpened != null && postOrderResponse.orderOpened.id > 0) { if (order.Type != OrderType.Market) { order.BrokerId.Add(postOrderResponse.orderOpened.id.ToString()); } } if (postOrderResponse.tradesClosed != null && postOrderResponse.tradesClosed.Count > 0) { marketOrderFillQuantity += postOrderResponse.tradesClosed .Where(trade => order.Type == OrderType.Market) .Sum(trade => trade.units); } // send Submitted order event const int orderFee = 0; order.PriceCurrency = SecurityProvider.GetSecurity(order.Symbol).SymbolProperties.QuoteCurrency; OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, orderFee) { Status = OrderStatus.Submitted }); if (order.Type == OrderType.Market) { // if market order, also send Filled order event OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, orderFee) { Status = OrderStatus.Filled, FillPrice = marketOrderFillPrice, FillQuantity = marketOrderFillQuantity * Math.Sign(order.Quantity) }); } return(true); }
/// <summary> /// Places a new order and assigns a new broker ID to the order /// </summary> /// <param name="order">The order to be placed</param> /// <returns>True if the request for a new order has been placed, false otherwise</returns> public override bool PlaceOrder(Order order) { var orderFee = OrderFee.Zero; var marketOrderFillQuantity = 0; var marketOrderFillPrice = 0m; var marketOrderRemainingQuantity = 0; var marketOrderStatus = OrderStatus.Filled; var request = GenerateOrderRequest(order); order.PriceCurrency = SecurityProvider.GetSecurity(order.Symbol).SymbolProperties.QuoteCurrency; lock (Locker) { var response = _apiRest.CreateOrder(Authorization, AccountId, request); order.BrokerId.Add(response.Data.OrderCreateTransaction.Id); // Market orders are special, due to the callback not being triggered always, // if the order was Filled/PartiallyFilled, find fill quantity and price and inform the user if (order.Type == OrderType.Market) { var fill = response.Data.OrderFillTransaction; marketOrderFillPrice = fill.Price.ConvertInvariant <decimal>(); if (fill.TradeOpened != null && fill.TradeOpened.TradeID.Length > 0) { marketOrderFillQuantity = fill.TradeOpened.Units.ConvertInvariant <int>(); } if (fill.TradeReduced != null && fill.TradeReduced.TradeID.Length > 0) { marketOrderFillQuantity = fill.TradeReduced.Units.ConvertInvariant <int>(); } if (fill.TradesClosed != null && fill.TradesClosed.Count > 0) { marketOrderFillQuantity += fill.TradesClosed.Sum(trade => trade.Units.ConvertInvariant <int>()); } marketOrderRemainingQuantity = Convert.ToInt32(order.AbsoluteQuantity - Math.Abs(marketOrderFillQuantity)); if (marketOrderRemainingQuantity > 0) { marketOrderStatus = OrderStatus.PartiallyFilled; // The order was not fully filled lets save it so the callback can inform the user PendingFilledMarketOrders[order.Id] = marketOrderStatus; } } } OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, orderFee) { Status = OrderStatus.Submitted }); // If 'marketOrderRemainingQuantity < order.AbsoluteQuantity' is false it means the order was not even PartiallyFilled, wait for callback if (order.Type == OrderType.Market && marketOrderRemainingQuantity < order.AbsoluteQuantity) { OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, orderFee, "Oanda Fill Event") { Status = marketOrderStatus, FillPrice = marketOrderFillPrice, FillQuantity = marketOrderFillQuantity }); } return(true); }