/// <summary> /// Updates the order with the same id /// </summary> /// <param name="order">The new order information</param> /// <returns>True if the request was made for the order to be updated, false otherwise</returns> public override bool UpdateOrder(Order order) { Log.Trace("OandaBrokerage.UpdateOrder(): " + order); if (!order.BrokerId.Any()) { // we need the brokerage order id in order to perform an update Log.Trace("OandaBrokerage.UpdateOrder(): Unable to update order without BrokerId."); return(false); } var request = GenerateOrderRequest(order); var orderId = order.BrokerId.First(); var response = _apiRest.ReplaceOrder(Authorization, AccountId, orderId, request); // replace the brokerage order id order.BrokerId[0] = response.Data.OrderCreateTransaction.Id; // check if the updated (marketable) order was filled if (response.Data.OrderFillTransaction != null) { OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, OrderFee.Zero, "Oanda Fill Event") { Status = OrderStatus.Filled, FillPrice = response.Data.OrderFillTransaction.Price.ToDecimal(), FillQuantity = response.Data.OrderFillTransaction.Units.ConvertInvariant <int>() }); } return(true); }
/// <summary> /// Get the slippage approximation for this order /// </summary> /// <returns>Decimal value of the slippage approximation</returns> /// <seealso cref="Order"/> public override decimal GetSlippageApproximation(Security security, Order order) { //Return 0 by default decimal slippage = 0; //For FOREX, the slippage is the Bid/Ask Spread for Tick, and an approximation for TradeBars switch (security.Resolution) { case Resolution.Minute: case Resolution.Second: //Get the last data packet: //Assume slippage is 1/10,000th of the price slippage = security.GetLastData().Value * 0.0001m; break; case Resolution.Tick: var lastTick = (Tick)security.GetLastData(); switch (order.Direction) { case OrderDirection.Buy: //We're buying, assume slip to Asking Price. slippage = Math.Abs(order.Price - lastTick.AskPrice); break; case OrderDirection.Sell: //We're selling, assume slip to the bid price. slippage = Math.Abs(order.Price - lastTick.BidPrice); break; } break; } return slippage; }
/// <summary> /// Returns true if the brokerage could accept this order. This takes into account /// order type, security type, and order size limits. /// </summary> /// <remarks> /// For example, a brokerage may have no connectivity at certain times, or an order rate/size limit /// </remarks> /// <param name="security"></param> /// <param name="order">The order to be processed</param> /// <param name="message">If this function returns false, a brokerage message detailing why the order may not be submitted</param> /// <returns>True if the brokerage could process the order, false otherwise</returns> public override bool CanSubmitOrder(Security security, Order order, out BrokerageMessageEvent message) { message = null; // validate security type if (security.Type != SecurityType.Forex && security.Type != SecurityType.Cfd) { message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", "This model does not support " + security.Type + " security type." ); return false; } // validate order type if (order.Type != OrderType.Limit && order.Type != OrderType.Market && order.Type != OrderType.StopMarket) { message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", "This model does not support " + order.Type + " order type." ); return false; } return true; }
/// <summary> /// Updates the order with the same id /// </summary> /// <param name="order">The new order information</param> /// <returns>True if the request was made for the order to be updated, false otherwise</returns> public override bool UpdateOrder(Order order) { //! UPDATE ORDER //todo: maybe make it so, that this broker doesn't support updating orders (because it doesnt) bool success = false; if (CancelOrder(order)) { order.BrokerId.Clear(); if (PlaceOrder(order)) { return(true); } else { // try again if (PlaceOrder(order)) { return(true); } else { throw new KrakenException("The update failed! Order was canceled but not placed again"); } } } return(false); }
/// <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) { KrakenOrder krakenOrder = new KrakenOrder(); krakenOrder.Pair = SymbolMapper.GetBrokerageSymbol(order.Symbol); // buy/sell krakenOrder.Type = TranslateDirectionToKraken(order.Direction); krakenOrder.OrderType = TranslateOrderTypeToKraken(order.Type); krakenOrder.Volume = order.AbsoluteQuantity; if (order.Type == OrderType.Limit) { krakenOrder.Price = order.Price; } // krakenOrder.Leverage = ? var result = _restApi.AddOrder(krakenOrder); if (result.Txid != null & result.Txid.Length != 0) { order.BrokerId.AddRange(result.Txid); return(true); } return(false); }
public override bool ModifyOrderToFill(IBrokerage brokerage, Order order, decimal lastMarketPrice) { // FXCM Buy Limit orders will be rejected if the limit price is above the market price // FXCM Sell Limit orders will be rejected if the limit price is below the market price var limit = (LimitOrder)order; var previousLimit = limit.LimitPrice; var fxcmBrokerage = (FxcmBrokerage)brokerage; var quotes = fxcmBrokerage.GetBidAndAsk(new List<string> { new FxcmSymbolMapper().GetBrokerageSymbol(order.Symbol) }); if (order.Quantity > 0) { // for limit buys we need to increase the limit price // buy limit price must be at bid price or below var bidPrice = Convert.ToDecimal(quotes.Single().BidPrice); Log.Trace("FxcmLimitOrderTestParameters.ModifyOrderToFill(): Bid: " + bidPrice); limit.LimitPrice = Math.Max(previousLimit, Math.Min(bidPrice, limit.LimitPrice * 2)); } else { // for limit sells we need to decrease the limit price // sell limit price must be at ask price or above var askPrice = Convert.ToDecimal(quotes.Single().AskPrice); Log.Trace("FxcmLimitOrderTestParameters.ModifyOrderToFill(): Ask: " + askPrice); limit.LimitPrice = Math.Min(previousLimit, Math.Max(askPrice, limit.LimitPrice / 2)); } return limit.LimitPrice != previousLimit; }
/// <summary> /// Returns true if the brokerage could accept this order. This takes into account /// order type, security type, and order size limits. /// </summary> /// <remarks> /// For example, a brokerage may have no connectivity at certain times, or an order rate/size limit /// </remarks> /// <param name="security">The security of the order</param> /// <param name="order">The order to be processed</param> /// <param name="message">If this function returns false, a brokerage message detailing why the order may not be submitted</param> /// <returns>True if the brokerage could process the order, false otherwise</returns> public override bool CanSubmitOrder(Security security, Order order, out BrokerageMessageEvent message) { message = null; var securityType = order.SecurityType; if (securityType != SecurityType.Equity) { message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", "This model only supports equities." ); return false; } if (order.Type == OrderType.MarketOnOpen || order.Type == OrderType.MarketOnClose) { message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", "Tradier brokerage only supports Market orders. MarketOnOpen and MarketOnClose orders not supported." ); return false; } if (!CanExecuteOrder(security, order)) { message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "ExtendedMarket", "Tradier does not support extended market hours trading. Your order will be processed at market open." ); } // tradier order limits return true; }
public void ClientCancelsLimitOrder() { OrderStatus status = OrderStatus.New; var manualResetEvent = new ManualResetEvent(false); var ib = new InteractiveBrokersBrokerage(); ib.Connect(); ib.OrderEvent += (sender, args) => { status = args.Status; manualResetEvent.Set(); }; // try to sell a single share at a ridiculous price, we'll cancel this later var order = new Order("AAPL", SecurityType.Equity, -1, OrderType.Limit, DateTime.UtcNow, 100000); ib.PlaceOrder(order); manualResetEvent.WaitOne(2500); ib.CancelOrder(order); manualResetEvent.Reset(); manualResetEvent.WaitOne(2500); Assert.AreEqual(OrderStatus.Canceled, status); }
/******************************************************** * CLASS PROPERTIES *********************************************************/ /******************************************************** * CLASS METHODS *********************************************************/ /// <summary> /// Process a order fill with the supplied security and order. /// </summary> /// <param name="vehicle">Asset we're working with</param> /// <param name="order">Order class to check if filled.</param> /// <returns>OrderEvent packet with the full or partial fill information</returns> public virtual OrderEvent Fill(Security vehicle, Order order) { var fill = new OrderEvent(order); try { //Based on the order type, select the fill model method. switch (order.Type) { case OrderType.Limit: fill = LimitFill(vehicle, order); break; case OrderType.StopMarket: fill = StopFill(vehicle, order); break; case OrderType.Market: fill = MarketFill(vehicle, order); break; } } catch (Exception err) { Log.Error("Equity.EquityTransactionModel.Fill(): " + err.Message); } return fill; }
public override bool ModifyOrderToFill(IBrokerage brokerage, Order order, decimal lastMarketPrice) { var stop = (StopLimitOrder) order; var previousStop = stop.StopPrice; if (order.Quantity > 0) { // for stop buys we need to decrease the stop price stop.StopPrice = Math.Min(stop.StopPrice, Math.Max(stop.StopPrice/2, Math.Round(lastMarketPrice, 2, MidpointRounding.AwayFromZero))); //change behaviour for forex type unit tests if(order.SecurityType == SecurityType.Forex) { stop.StopPrice = Math.Min(stop.StopPrice, Math.Max(stop.StopPrice / 2, Math.Round(lastMarketPrice, 4, MidpointRounding.AwayFromZero))); } } else { // for stop sells we need to increase the stop price stop.StopPrice = Math.Max(stop.StopPrice, Math.Min(stop.StopPrice * 2, Math.Round(lastMarketPrice, 2, MidpointRounding.AwayFromZero))); //change behaviour for forex type unit tests if (order.SecurityType == SecurityType.Forex) { stop.StopPrice = Math.Max(stop.StopPrice, Math.Min(stop.StopPrice * 2, Math.Round(lastMarketPrice, 4, MidpointRounding.AwayFromZero))); } } stop.LimitPrice = stop.StopPrice; return stop.StopPrice != previousStop; }
/******************************************************** * CLASS PROPERTIES *********************************************************/ /******************************************************** * CLASS METHODS *********************************************************/ /// <summary> /// Perform neccessary check to see if the model has been filled, appoximate the best we can. /// </summary> /// <param name="vehicle">Asset we're working with</param> /// <param name="order">Order class to check if filled.</param> /// <returns>OrderEvent packet with the full or partial fill information</returns> /// <seealso cref="OrderEvent"/> /// <seealso cref="Order"/> public virtual OrderEvent Fill(Security vehicle, Order order) { var fill = new OrderEvent(order); try { switch (order.Type) { case OrderType.Limit: fill = LimitFill(vehicle, order); break; case OrderType.StopMarket: fill = StopFill(vehicle, order); break; case OrderType.Market: fill = MarketFill(vehicle, order); break; } } catch (Exception err) { Log.Error("Forex.ForexTransactionModel.Fill(): " + err.Message); } return fill; }
/// <summary> /// Cancels the order with the specified ID /// </summary> /// <param name="order">The order to cancel</param> /// <returns>True if the request was made for the order to be canceled, false otherwise</returns> public override bool CancelOrder(QuantConnect.Orders.Order order) { try { Log.Trace("TEBBrokerage.CancelOrder(): Symbol: " + order.Symbol.Value + " Quantity: " + order.Quantity); if (!IsConnected) { Log.Error("TEBBrokerage.CancelOrder(): Unable to place order while not connected."); throw new InvalidOperationException("TEBBrokerage.CancelOrder(): Unable to place order while not connected."); } // this could be better foreach (var id in order.BrokerId) { TEBCancelOrder(order, int.Parse(id)); } // canceled order events fired upon confirmation, see HandleError } catch (Exception err) { Log.Error("TEBBrokerage.CancelOrder(): OrderID: " + order.Id + " - " + err); return(false); } return(true); }
/// <summary> /// Cancels the order with the specified ID /// </summary> /// <param name="order">The order to cancel</param> /// <returns>True if the request was submitted for cancellation, false otherwise</returns> public override bool CancelOrder(Order order) { Log.Trace("BitfinexBrokerage.CancelOrder(): {0}", order); if (!order.BrokerId.Any()) { // we need the brokerage order id in order to perform a cancellation Log.Trace("BitfinexBrokerage.CancelOrder(): Unable to cancel order without BrokerId."); return(false); } var parameters = new JsonObject { { "id", order.BrokerId.Select(Parse.Long).First() } }; var obj = new JsonArray { 0, "oc", null, parameters }; var json = JsonConvert.SerializeObject(obj); WebSocket.Send(json); return(true); }
/// <summary> /// Updates the order with the same id /// </summary> /// <param name="order">The new order information</param> /// <returns>True if the request was made for the order to be updated, false otherwise</returns> public override bool UpdateOrder(Order order) { if (order.BrokerId.Count == 0) { throw new ArgumentNullException(nameof(order.BrokerId), "BitfinexBrokerage.UpdateOrder: There is no brokerage id to be updated for this order."); } if (order.BrokerId.Count > 1) { throw new NotSupportedException("BitfinexBrokerage.UpdateOrder: Multiple orders update not supported. Please cancel and re-create."); } var parameters = new JsonObject { { "id", Parse.Long(order.BrokerId.First()) }, { "amount", order.Quantity.ToStringInvariant() }, { "price", GetOrderPrice(order).ToStringInvariant() } }; var obj = new JsonArray { 0, "ou", null, parameters }; var json = JsonConvert.SerializeObject(obj); WebSocket.Send(json); return(true); }
public override bool ModifyOrderToFill(IBrokerage brokerage, Order order, decimal lastMarketPrice) { // FXCM Buy StopMarket orders will be rejected if the stop price is below the market price // FXCM Sell StopMarket orders will be rejected if the stop price is above the market price var stop = (StopMarketOrder)order; var previousStop = stop.StopPrice; var fxcmBrokerage = (FxcmBrokerage)brokerage; var quotes = fxcmBrokerage.GetBidAndAsk(new List<string> { new FxcmSymbolMapper().GetBrokerageSymbol(order.Symbol) }); if (order.Quantity > 0) { // for stop buys we need to decrease the stop price // buy stop price must be strictly above ask price var askPrice = Convert.ToDecimal(quotes.Single().AskPrice); Log.Trace("FxcmStopMarketOrderTestParameters.ModifyOrderToFill(): Ask: " + askPrice); stop.StopPrice = Math.Min(previousStop, Math.Max(askPrice, stop.StopPrice / 2) + 0.00001m); } else { // for stop sells we need to increase the stop price // sell stop price must be strictly below bid price var bidPrice = Convert.ToDecimal(quotes.Single().BidPrice); Log.Trace("FxcmStopMarketOrderTestParameters.ModifyOrderToFill(): Bid: " + bidPrice); stop.StopPrice = Math.Max(previousStop, Math.Min(bidPrice, stop.StopPrice * 2) - 0.00001m); } return stop.StopPrice != previousStop; }
/// <summary> /// Updates the order with the same id /// </summary> /// <param name="order">The new order information</param> /// <returns>True if the request was made for the order to be updated, false otherwise</returns> public override bool UpdateOrder(Order order) { Log.Trace("OandaBrokerage.UpdateOrder(): " + order); if (!order.BrokerId.Any()) { // we need the brokerage order id in order to perform an update Log.Trace("OandaBrokerage.UpdateOrder(): Unable to update order without BrokerId."); return(false); } var requestParams = new Dictionary <string, string> { { "instrument", SymbolMapper.GetBrokerageSymbol(order.Symbol) }, { "units", order.AbsoluteQuantity.ConvertInvariant <int>().ToStringInvariant() }, }; // we need the brokerage order id in order to perform an update PopulateOrderRequestParameters(order, requestParams); if (UpdateOrder(Parse.Long(order.BrokerId.First()), requestParams)) { OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, OrderFee.Zero) { Status = OrderStatus.UpdateSubmitted }); } 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) { const int orderFee = 0; ApiResponse <InlineResponse201> response; lock (_locker) { var request = GenerateOrderRequest(order); response = _apiRest.CreateOrder(Authorization, AccountId, request); order.BrokerId.Add(response.Data.OrderCreateTransaction.Id); // send Submitted order event order.PriceCurrency = SecurityProvider.GetSecurity(order.Symbol).SymbolProperties.QuoteCurrency; OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, orderFee) { Status = OrderStatus.Submitted }); } // if market order, find fill quantity and price var fill = response.Data.OrderFillTransaction; var marketOrderFillPrice = 0m; var marketOrderFillQuantity = 0; if (order.Type == OrderType.Market) { marketOrderFillPrice = Convert.ToDecimal(fill.Price); if (fill.TradeOpened != null && fill.TradeOpened.TradeID.Length > 0) { marketOrderFillQuantity = Convert.ToInt32(fill.TradeOpened.Units); } if (fill.TradeReduced != null && fill.TradeReduced.TradeID.Length > 0) { marketOrderFillQuantity = Convert.ToInt32(fill.TradeReduced.Units); } if (fill.TradesClosed != null && fill.TradesClosed.Count > 0) { marketOrderFillQuantity += fill.TradesClosed.Sum(trade => Convert.ToInt32(trade.Units)); } } if (order.Type == OrderType.Market && order.Status != OrderStatus.Filled) { order.Status = OrderStatus.Filled; // if market order, also send Filled order event OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, orderFee, "Oanda Fill Event") { Status = OrderStatus.Filled, FillPrice = marketOrderFillPrice, FillQuantity = marketOrderFillQuantity }); } return(true); }
/// <summary> /// Generates an Oanda order request /// </summary> /// <param name="order">The LEAN order</param> /// <returns>The request in JSON format</returns> private string GenerateOrderRequest(Order order) { var instrument = SymbolMapper.GetBrokerageSymbol(order.Symbol); string request; switch (order.Type) { case OrderType.Market: var marketOrderRequest = new MarketOrderRequest { Type = MarketOrderRequest.TypeEnum.MARKET, Instrument = instrument, Units = order.Quantity.ToStringInvariant() }; request = JsonConvert.SerializeObject(new { order = marketOrderRequest }); break; case OrderType.Limit: var limitOrderRequest = new LimitOrderRequest { Type = LimitOrderRequest.TypeEnum.LIMIT, Instrument = instrument, Units = order.Quantity.ToStringInvariant(), Price = ((LimitOrder)order).LimitPrice.ToString(CultureInfo.InvariantCulture) }; request = JsonConvert.SerializeObject(new { order = limitOrderRequest }); break; case OrderType.StopMarket: var marketIfTouchedOrderRequest = new MarketIfTouchedOrderRequest { Type = MarketIfTouchedOrderRequest.TypeEnum.MARKETIFTOUCHED, Instrument = instrument, Units = order.Quantity.ToStringInvariant(), Price = ((StopMarketOrder)order).StopPrice.ToString(CultureInfo.InvariantCulture) }; request = JsonConvert.SerializeObject(new { order = marketIfTouchedOrderRequest }); break; case OrderType.StopLimit: var stopOrderRequest = new StopOrderRequest { Type = StopOrderRequest.TypeEnum.STOP, Instrument = instrument, Units = order.Quantity.ToStringInvariant(), Price = ((StopLimitOrder)order).StopPrice.ToString(CultureInfo.InvariantCulture), PriceBound = ((StopLimitOrder)order).LimitPrice.ToString(CultureInfo.InvariantCulture) }; request = JsonConvert.SerializeObject(new { order = stopOrderRequest }); break; default: throw new NotSupportedException("The order type " + order.Type + " is not supported."); } return(request); }
/// <summary> /// Gets the total margin required to execute the specified order in units of the account currency including fees /// </summary> /// <param name="security">The security to compute initial margin for</param> /// <param name="order">The order to be executed</param> /// <returns>The total margin in terms of the currency quoted in the order</returns> public override decimal GetInitialMarginRequiredForOrder(Security security, Order order) { //Get the order value from the non-abstract order classes (MarketOrder, LimitOrder, StopMarketOrder) //Market order is approximated from the current security price and set in the MarketOrder Method in QCAlgorithm. var orderFees = security.FeeModel.GetOrderFee(security, order); var orderCostInAccountCurrency = order.GetValue(security); return orderCostInAccountCurrency*InitialMarginRequirement + orderFees; }
/// <summary> /// Returns true if the brokerage would be able to execute this order at this time assuming /// market prices are sufficient for the fill to take place. This is used to emulate the /// brokerage fills in backtesting and paper trading. For example some brokerages may not perform /// executions during extended market hours. This is not intended to be checking whether or not /// the exchange is open, that is handled in the Security.Exchange property. /// </summary> /// <param name="security">The security being ordered</param> /// <param name="order">The order to test for execution</param> /// <returns>True if the brokerage would be able to perform the execution, false otherwise</returns> public bool CanExecuteOrder(Security security, Order order) { // tradier doesn't support after hours trading var timeOfDay = security.Time.TimeOfDay; if (timeOfDay < EquityExchange.MarketOpen || timeOfDay > EquityExchange.MarketClose) { return false; } return true; }
/// <summary> /// Returns true if the brokerage could accept this order. This takes into account /// order type, security type, and order size limits. /// </summary> /// <remarks> /// For example, a brokerage may have no connectivity at certain times, or an order rate/size limit /// </remarks> /// <param name="security"></param> /// <param name="order">The order to be processed</param> /// <param name="message">If this function returns false, a brokerage message detailing why the order may not be submitted</param> /// <returns>True if the brokerage could process the order, false otherwise</returns> public override bool CanSubmitOrder(Security security, Order order, out BrokerageMessageEvent message) { message = null; // validate security type if (security.Type != SecurityType.Forex && security.Type != SecurityType.Cfd) { message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", "This model does not support " + security.Type + " security type." ); return false; } // validate order type if (order.Type != OrderType.Limit && order.Type != OrderType.Market && order.Type != OrderType.StopMarket) { message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", "This model does not support " + order.Type + " order type." ); return false; } // validate order quantity if (order.Quantity % 1000 != 0) { message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", "The order quantity must be a multiple of 1000." ); return false; } // validate stop/limit orders= prices var limit = order as LimitOrder; if (limit != null) { return IsValidOrderPrices(security, OrderType.Limit, limit.Direction, security.Price, limit.LimitPrice, ref message); } var stopMarket = order as StopMarketOrder; if (stopMarket != null) { return IsValidOrderPrices(security, OrderType.StopMarket, stopMarket.Direction, stopMarket.StopPrice, security.Price, ref message); } var stopLimit = order as StopLimitOrder; if (stopLimit != null) { return IsValidOrderPrices(security, OrderType.StopLimit, stopLimit.Direction, stopLimit.StopPrice, stopLimit.LimitPrice, ref message); } return true; }
/// <summary> /// Returns true if the brokerage would allow updating the order as specified by the request /// </summary> /// <param name="security">The security of the order</param> /// <param name="order">The order to be updated</param> /// <param name="request">The requested update to be made to the order</param> /// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param> /// <returns>True if the brokerage would allow updating the order, false otherwise</returns> public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message) { message = null; if (order.SecurityType == SecurityType.Forex && request.Quantity != null) { return IsForexWithinOrderSizeLimits(order.Symbol.Value, request.Quantity.Value, out message); } return true; }
/// <summary> /// Cancels the order with the specified ID /// </summary> /// <param name="order">The order to cancel</param> /// <returns>True if the request was made for the order to be canceled, false otherwise</returns> public override bool CancelOrder(Order order) { int sum = 0; foreach (string txid in order.BrokerId) { var result = _restApi.CancelOrder(txid); sum += result.Count; } return(sum > 0); }
/// <summary> /// Gets the total margin required to execute the specified order in units of the account currency including fees /// </summary> /// <param name="security">The security to compute initial margin for</param> /// <param name="order">The order to be executed</param> /// <returns>The total margin in terms of the currency quoted in the order</returns> public override decimal GetInitialMarginRequiredForOrder(Security security, Order order) { var forex = (Forex)security; //Get the order value from the non-abstract order classes (MarketOrder, LimitOrder, StopMarketOrder) //Market order is approximated from the current security price and set in the MarketOrder Method in QCAlgorithm. var orderFees = security.TransactionModel.GetOrderFee(security, order); var price = order.Status.IsFill() ? order.Price : security.Price; var orderCostInAccountCurrency = order.GetValue(price)*forex.QuoteCurrency.ConversionRate; return orderCostInAccountCurrency*InitialMarginRequirement + orderFees; }
/// <summary> /// Returns true if the brokerage could accept this order. This takes into account /// order type, security type, and order size limits. /// </summary> /// <remarks> /// For example, a brokerage may have no connectivity at certain times, or an order rate/size limit /// </remarks> /// <param name="security"></param> /// <param name="order">The order to be processed</param> /// <param name="message">If this function returns false, a brokerage message detailing why the order may not be submitted</param> /// <returns>True if the brokerage could process the order, false otherwise</returns> public override bool CanSubmitOrder(Security security, Order order, out BrokerageMessageEvent message) { message = null; // validate security type if (security.Type != SecurityType.Forex && security.Type != SecurityType.Cfd) { message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", "This model does not support " + security.Type + " security type." ); return false; } // validate order type if (order.Type != OrderType.Limit && order.Type != OrderType.Market && order.Type != OrderType.StopMarket) { message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", "This model does not support " + order.Type + " order type." ); return false; } // validate order quantity if (order.Quantity % 1000 != 0) { message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", "The order quantity must be a multiple of 1000." ); return false; } // validate order price var invalidPrice = order.Type == OrderType.Limit && order.Direction == OrderDirection.Buy && ((LimitOrder)order).LimitPrice > security.Price || order.Type == OrderType.Limit && order.Direction == OrderDirection.Sell && ((LimitOrder)order).LimitPrice < security.Price || order.Type == OrderType.StopMarket && order.Direction == OrderDirection.Buy && ((StopMarketOrder)order).StopPrice < security.Price || order.Type == OrderType.StopMarket && order.Direction == OrderDirection.Sell && ((StopMarketOrder)order).StopPrice > security.Price; if (invalidPrice) { message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", "Limit Buy orders and Stop Sell orders must be below market, Limit Sell orders and Stop Buy orders must be above market." ); } return true; }
public override bool ModifyOrderToFill(IBrokerage brokerage, Order order, decimal lastMarketPrice) { var stop = (StopMarketOrder)order; var previousStop = stop.StopPrice; if (order.Quantity > 0) { // for stop buys we need to decrease the stop price stop.StopPrice = Math.Min(stop.StopPrice, Math.Max(stop.StopPrice / 2, lastMarketPrice)); } else { // for stop sells we need to increase the stop price stop.StopPrice = Math.Max(stop.StopPrice, Math.Min(stop.StopPrice * 2, lastMarketPrice)); } return stop.StopPrice != previousStop; }
/// <summary> /// Get the fee for this order /// </summary> /// <param name="security">The security matching the order</param> /// <param name="order">The order to compute fees for</param> /// <returns>The cost of the order in units of the account currency</returns> public override decimal GetOrderFee(Security security, Order order) { // From http://www.fxcm.com/forex/forex-pricing/ (on Oct 6th, 2015) // Forex: $0.04 per side per 1k lot for EURUSD, GBPUSD, USDJPY, USDCHF, AUDUSD, EURJPY, GBPJPY // $0.06 per side per 1k lot for other instruments // From https://www.fxcm.com/uk/markets/cfds/frequently-asked-questions/ // CFD: no commissions if (security.Type != SecurityType.Forex) return 0m; var commissionRate = _groupCommissionSchedule1.Contains(security.Symbol) ? 0.04m : 0.06m; return commissionRate * order.AbsoluteQuantity / 1000; }
/// <summary> /// Returns true if the brokerage would allow updating the order as specified by the request /// </summary> /// <param name="security">The security of the order</param> /// <param name="order">The order to be updated</param> /// <param name="request">The requested update to be made to the order</param> /// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param> /// <returns>True if the brokerage would allow updating the order, false otherwise</returns> public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message) { message = null; // Tradier doesn't allow updating order quantities if (request.Quantity != null && request.Quantity != order.Quantity) { message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "UpdateRejected", "Traider does not support updating order quantities." ); return false; } return true; }
public override bool ModifyOrderToFill(IBrokerage brokerage, Order order, decimal lastMarketPrice) { // limit orders will process even if they go beyond the market price var limit = (LimitOrder) order; if (order.Quantity > 0) { // for limit buys we need to increase the limit price limit.LimitPrice *= 2; } else { // for limit sells we need to decrease the limit price limit.LimitPrice /= 2; } return true; }
/// <summary> /// Returns true if the brokerage would be able to execute this order at this time assuming /// market prices are sufficient for the fill to take place. This is used to emulate the /// brokerage fills in backtesting and paper trading. For example some brokerages may not perform /// executions during extended market hours. This is not intended to be checking whether or not /// the exchange is open, that is handled in the Security.Exchange property. /// </summary> /// <param name="security">The security being ordered</param> /// <param name="order">The order to test for execution</param> /// <returns>True if the brokerage would be able to perform the execution, false otherwise</returns> public override bool CanExecuteOrder(Security security, Order order) { EquityExchange.SetLocalDateTimeFrontier(security.Exchange.LocalTime); var cache = security.GetLastData(); if (cache == null) { return false; } // tradier doesn't support after hours trading if (!EquityExchange.IsOpenDuringBar(cache.Time, cache.EndTime, false)) { return false; } return true; }
public override bool ModifyOrderToFill(Order order, decimal lastMarketPrice) { var stop = (StopLimitOrder) order; var previousStop = stop.StopPrice; if (order.Quantity > 0) { // for stop buys we need to decrease the stop price stop.StopPrice = Math.Min(stop.StopPrice, Math.Max(stop.StopPrice/2, Math.Round(lastMarketPrice, 2, MidpointRounding.AwayFromZero))); } else { // for stop sells we need to increase the stop price stop.StopPrice = Math.Max(stop.StopPrice, Math.Min(stop.StopPrice * 2, Math.Round(lastMarketPrice, 2, MidpointRounding.AwayFromZero))); } stop.LimitPrice = stop.StopPrice; return stop.StopPrice != previousStop; }
/// <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 parameters = new JsonObject { { "symbol", _symbolMapper.GetBrokerageSymbol(order.Symbol) }, { "amount", order.Quantity.ToStringInvariant() }, { "type", ConvertOrderType(_algorithm.BrokerageModel.AccountType, order.Type) }, { "price", GetOrderPrice(order).ToStringInvariant() } }; var orderProperties = order.Properties as BitfinexOrderProperties; if (orderProperties != null) { if (order.Type == OrderType.Limit) { var flags = 0; if (orderProperties.Hidden) { flags |= OrderFlags.Hidden; } if (orderProperties.PostOnly) { flags |= OrderFlags.PostOnly; } parameters.Add("flags", flags); } } var clientOrderId = GetNextClientOrderId(); parameters.Add("cid", clientOrderId); _orderMap.TryAdd(clientOrderId, order); var obj = new JsonArray { 0, "on", null, parameters }; var json = JsonConvert.SerializeObject(obj); WebSocket.Send(json); return(true); }
/// <summary> /// Return a relevant price for order depending on order type /// Price must be positive /// </summary> /// <param name="order"></param> /// <returns></returns> private static decimal GetOrderPrice(Order order) { switch (order.Type) { case OrderType.Limit: return(((LimitOrder)order).LimitPrice); case OrderType.Market: // Order price must be positive for market order too; // refuses for price = 0 return(1); case OrderType.StopMarket: return(((StopMarketOrder)order).StopPrice); } throw new NotSupportedException($"BitfinexBrokerage.ConvertOrderType: Unsupported order type: {order.Type}"); }
/// <summary> /// Updates the order with the same id /// </summary> /// <param name="order">The new order information</param> /// <returns>True if the request was made for the order to be updated, false otherwise</returns> public override bool UpdateOrder(QuantConnect.Orders.Order order) { try { Log.Trace("TEBBrokerage.UpdateOrder(): Symbol: " + order.Symbol.Value + " Quantity: " + order.Quantity + " Status: " + order.Status); if (!IsConnected) { Log.Error("TEBBrokerage.UpdateOrder(): Unable to place order while not connected."); throw new InvalidOperationException("TEBBrokerage.UpdateOrder(): Unable to place order while not connected."); } int clOrdID = 0; //if (needsNewID) //{ // the order ids are generated for us by the SecurityTransactionManaer int id = NextClientID(); order.BrokerId.Add(id.ToString()); clOrdID = id; //} //else if (order.BrokerId.Any()) //{ // // this is *not* perfect code // clOrdID = int.Parse(order.BrokerId[0]); //} //else //{ // throw new ArgumentException("Expected order with populated BrokerId for updating orders."); //} var content = CreateContent(order, clOrdID); bool isSent = false; _fixContext.SendNewOrderToCash(content, out isSent); return(isSent); } catch (Exception err) { Log.Error("TEBBrokerage.UpdateOrder(): " + err); return(false); } }
/// <summary> /// Cancels the order with the specified ID /// </summary> /// <param name="order">The order to cancel</param> /// <returns>True if the request was made for the order to be canceled, false otherwise</returns> public override bool CancelOrder(Order order) { Log.Trace("OandaBrokerage.CancelOrder(): " + order); if (!order.BrokerId.Any()) { Log.Trace("OandaBrokerage.CancelOrder(): Unable to cancel order without BrokerId."); return(false); } foreach (var orderId in order.BrokerId) { _apiRest.CancelOrder(Authorization, AccountId, orderId); OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, 0, "Oanda Cancel Order Event") { Status = OrderStatus.Canceled }); } return(true); }
public void PlaceOrderTest(string orderId, HttpStatusCode httpStatus, Orders.OrderStatus status, decimal quantity, decimal price, OrderType orderType) { var response = new { id = _brokerId, fill_fees = "0.11" }; SetupResponse(JsonConvert.SerializeObject(response), httpStatus); _unit.OrderStatusChanged += (s, e) => { Assert.AreEqual(status, e.Status); if (orderId != null) { Assert.AreEqual("BTCUSD", e.Symbol.Value); Assert.That((quantity > 0 && e.Direction == Orders.OrderDirection.Buy) || (quantity < 0 && e.Direction == Orders.OrderDirection.Sell)); Assert.IsTrue(orderId == null || _unit.CachedOrderIDs.SelectMany(c => c.Value.BrokerId.Where(b => b == _brokerId)).Any()); } }; Order order = null; if (orderType == OrderType.Limit) { order = new Orders.LimitOrder(_symbol, quantity, price, DateTime.UtcNow); } else if (orderType == OrderType.Market) { order = new Orders.MarketOrder(_symbol, quantity, DateTime.UtcNow); } else { order = new Orders.StopMarketOrder(_symbol, quantity, price, DateTime.UtcNow); } bool actual = _unit.PlaceOrder(order); Assert.IsTrue(actual || (orderId == null && !actual)); }
/// <summary> /// Uses the Interactive Brokers equities fixes fee schedule. /// </summary> /// <remarks> /// Default implementation uses the Interactive Brokers fee model of 0.5c per share with a maximum of 0.5% per order /// and minimum of $1.00. /// </remarks> /// <param name="security">The security matching the order</param> /// <param name="order">The order to compute fees for</param> /// <returns>The cost of the order in units of the account currency</returns> public override decimal GetOrderFee(Security security, Order order) { var tradeValue = Math.Abs(order.Value); //Per share fees var tradeFee = 0.005m*order.AbsoluteQuantity; //Maximum Per Order: 0.5% //Minimum per order. $1.0 var maximumPerOrder = 0.005m*tradeValue; if (tradeFee < 1) { tradeFee = 1; } else if (tradeFee > maximumPerOrder) { tradeFee = maximumPerOrder; } //Always return a positive fee. return Math.Abs(tradeFee); }
/// <summary> /// Returns true if the brokerage could accept this order. This takes into account /// order type, security type, and order size limits. /// </summary> /// <remarks> /// For example, a brokerage may have no connectivity at certain times, or an order rate/size limit /// </remarks> /// <param name="security">The security being ordered</param> /// <param name="order">The order to be processed</param> /// <param name="message">If this function returns false, a brokerage message detailing why the order may not be submitted</param> /// <returns>True if the brokerage could process the order, false otherwise</returns> public override bool CanSubmitOrder(Security security, Order order, out BrokerageMessageEvent message) { message = null; //https://www.interactivebrokers.com/en/?f=%2Fen%2Ftrading%2FforexOrderSize.php switch (order.SecurityType) { case SecurityType.Base: return false; case SecurityType.Equity: return true; // could not find order limits on equities case SecurityType.Option: return true; case SecurityType.Commodity: return true; case SecurityType.Forex: return IsForexWithinOrderSizeLimits(order.Symbol.Value, order.Quantity, out message); case SecurityType.Future: return true; default: throw new ArgumentOutOfRangeException(); } }
public QuantConnect.Orders.Order ConvertToOrder(Teb.FIX.Model.Order tebOrder) { QuantConnect.Orders.Order o = null; if (tebOrder.OrdType == CashDefinition.ORDTYPE_MARKET) { o = new QuantConnect.Orders.MarketOrder(); } else { o = new QuantConnect.Orders.LimitOrder(); (o as LimitOrder).LimitPrice = tebOrder.Price.HasValue ? tebOrder.Price.Value : 0; } o.Duration = ConvertToTimeInForce(tebOrder.Core.TimeInForce);; o.Id = int.Parse(tebOrder.ClOrdID.Replace("CS", "")); o.Price = tebOrder.Price.HasValue ? tebOrder.Price.Value : 0; o.Quantity = ConvertToQuantity(tebOrder); o.Status = ConvertToStatus(tebOrder); o.Time = tebOrder.TransactTime.HasValue ? tebOrder.TransactTime.Value : DateTime.Now; o.Symbol = tebOrder.Symbol; o.BrokerId.Add(tebOrder.ConnectionClOrdID); return(o); }
/// <summary> /// Updates the order with the same id /// </summary> /// <param name="order">The new order information</param> /// <returns>True if the request was made for the order to be updated, false otherwise</returns> public override bool UpdateOrder(Order order) { Log.Trace("OandaBrokerage.UpdateOrder(): " + order); if (!order.BrokerId.Any()) { // we need the brokerage order id in order to perform an update Log.Trace("OandaBrokerage.UpdateOrder(): Unable to update order without BrokerId."); return(false); } var requestParams = new Dictionary <string, string> { { "instrument", SymbolMapper.GetBrokerageSymbol(order.Symbol) }, { "units", Convert.ToInt32(order.AbsoluteQuantity).ToString() }, }; // we need the brokerage order id in order to perform an update PopulateOrderRequestParameters(order, requestParams); UpdateOrder(long.Parse(order.BrokerId.First()), requestParams); return(true); }
private Content CreateContent(QuantConnect.Orders.Order order, int clOrdId) { Content content = ContentFactory.Create(); content.ServerStatus = ContentServerStatus.Waiting; content.ClientStatus = ContentClientStatus.PendingNew; content.TransactTime = DateTime.Now; content.ClOrdID = clOrdId.ToString(); content.MarketID = clOrdId.ToString(); content.Side = convertHelper.ConvertToSide(order.Quantity); //content.IsSellShort = builder.IsSellShort; content.OrdType = convertHelper.ConvertToOrdType(order.Type); content.Account = _account; content.OrderCapacity = CashDefinition.ACCOUNT_TYPE_CUSTOMER; content.SenderSubID = _senderSubId; content.Symbol = order.Symbol; //content.SymbolSfxMarket = builder.Market.Value.Trim(); content.OrderQty = Math.Abs(order.Quantity); if (order is LimitOrder) { decimal limitPrice = (decimal)((LimitOrder)order).LimitPrice; content.Price = limitPrice; } else { content.Price = order.Price; } content.TransactTime = order.Time; content.OrderQty = Math.Abs(order.Quantity); content.TimeInForce = convertHelper.ConvertToTimeInForce(order.Type); content.SecurityType = CashDefinition.SECURITY_CASH; return(content); }
/// <summary> /// Cancels the order with the specified ID /// </summary> /// <param name="order">The order to cancel</param> /// <returns>True if the request was made for the order to be canceled, false otherwise</returns> public override bool CancelOrder(Order order) { Log.Trace("OandaBrokerage.CancelOrder(): " + order); if (!order.BrokerId.Any()) { Log.Trace("OandaBrokerage.CancelOrder(): Unable to cancel order without BrokerId."); return(false); } foreach (var orderId in order.BrokerId) { CancelOrder(long.Parse(orderId)); OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, OrderFee.Zero, "Oanda Cancel Order Event") { Status = OrderStatus.Canceled }); } return(true); }
/// <summary> /// Gets all open orders on the account. /// NOTE: The order objects returned do not have QC order IDs. /// </summary> /// <returns>The open orders returned from IB</returns> public override List <Order> GetOpenOrders() { List <Order> list = new List <Order>(); Dictionary <string, OrderInfo> orders = _restApi.GetOpenOrders(); foreach (KeyValuePair <string, OrderInfo> pair in orders) { OrderInfo info = pair.Value; OrderDescription desc = info.Descr; // check for debug purposes here if (pair.Key != desc.Pair) { throw new KrakenException("this doesn't match, please inspect!!"); } var SOR = new SubmitOrderRequest( TranslateOrderTypeToLean(info.Descr.OrderType), SecurityType.Crypto, this.SymbolMapper.GetLeanSymbol(desc.Pair, SecurityType.Crypto, Market.Kraken), info.Volume - info.VolumeExecuted, info.StopPrice.HasValue ? info.StopPrice.Value : 0m, info.LimitPrice.HasValue ? info.LimitPrice.Value : 0m, UnixTimeStampToDateTime(info.OpenTm), "" ); var order = Order.CreateOrder(SOR); list.Add(order); } return(list); }
/// <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) { Log.Trace("FxcmBrokerage.PlaceOrder(): {0}", order); if (!IsConnected) throw new InvalidOperationException("FxcmBrokerage.PlaceOrder(): Unable to place order while not connected."); if (order.Direction != OrderDirection.Buy && order.Direction != OrderDirection.Sell) throw new ArgumentException("FxcmBrokerage.PlaceOrder(): Invalid Order Direction"); var fxcmSymbol = _symbolMapper.GetBrokerageSymbol(order.Symbol); var orderSide = order.Direction == OrderDirection.Buy ? SideFactory.BUY : SideFactory.SELL; var quantity = (double)order.AbsoluteQuantity; OrderSingle orderRequest; switch (order.Type) { case OrderType.Market: orderRequest = MessageGenerator.generateMarketOrder(_accountId, quantity, orderSide, fxcmSymbol, ""); break; case OrderType.Limit: var limitPrice = (double)((LimitOrder)order).LimitPrice; orderRequest = MessageGenerator.generateOpenOrder(limitPrice, _accountId, quantity, orderSide, fxcmSymbol, ""); orderRequest.setOrdType(OrdTypeFactory.LIMIT); orderRequest.setTimeInForce(TimeInForceFactory.GOOD_TILL_CANCEL); break; case OrderType.StopMarket: var stopPrice = (double)((StopMarketOrder)order).StopPrice; orderRequest = MessageGenerator.generateOpenOrder(stopPrice, _accountId, quantity, orderSide, fxcmSymbol, ""); orderRequest.setOrdType(OrdTypeFactory.STOP); orderRequest.setTimeInForce(TimeInForceFactory.GOOD_TILL_CANCEL); break; default: throw new NotSupportedException("FxcmBrokerage.PlaceOrder(): Order type " + order.Type + " is not supported."); } _isOrderSubmitRejected = false; AutoResetEvent autoResetEvent; lock (_locker) { _currentRequest = _gateway.sendMessage(orderRequest); _mapRequestsToOrders[_currentRequest] = order; autoResetEvent = new AutoResetEvent(false); _mapRequestsToAutoResetEvents[_currentRequest] = autoResetEvent; } if (!autoResetEvent.WaitOne(ResponseTimeout)) throw new TimeoutException(string.Format("FxcmBrokerage.PlaceOrder(): Operation took longer than {0} seconds.", (decimal)ResponseTimeout / 1000)); return !_isOrderSubmitRejected; }
/// <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); }
/// <summary> /// Returns true when the specified order is in a completed state /// </summary> private static bool Completed(Order order) { return order.Status == OrderStatus.Filled || order.Status == OrderStatus.PartiallyFilled || order.Status == OrderStatus.Invalid || order.Status == OrderStatus.Canceled; }
/// <summary> /// Check if there is sufficient capital to execute this order. /// </summary> /// <param name="portfolio">Our portfolio</param> /// <param name="order">Order we're checking</param> /// <returns>True if suficient capital.</returns> public bool GetSufficientCapitalForOrder(SecurityPortfolioManager portfolio, Order order) { // short circuit the div 0 case if (order.Quantity == 0) return true; var security = _securities[order.Symbol]; var ticket = GetOrderTicket(order.Id); if (ticket == null) { Log.Error("SecurityTransactionManager.GetSufficientCapitalForOrder(): Null order ticket for id: " + order.Id); return false; } var freeMargin = security.MarginModel.GetMarginRemaining(portfolio, security, order.Direction); var initialMarginRequiredForOrder = security.MarginModel.GetInitialMarginRequiredForOrder(security, order); // pro-rate the initial margin required for order based on how much has already been filled var percentUnfilled = (Math.Abs(order.Quantity) - Math.Abs(ticket.QuantityFilled))/Math.Abs(order.Quantity); var initialMarginRequiredForRemainderOfOrder = percentUnfilled*initialMarginRequiredForOrder; if (Math.Abs(initialMarginRequiredForRemainderOfOrder) > freeMargin) { Log.Error(string.Format("SecurityTransactionManager.GetSufficientCapitalForOrder(): Id: {0}, Initial Margin: {1}, Free Margin: {2}", order.Id, initialMarginRequiredForOrder, freeMargin)); return false; } return true; }
/// <summary> /// Returns true if the specified order can be updated /// </summary> /// <param name="order">The order to check if we can update</param> /// <returns>True if the order can be updated, false otherwise</returns> private bool CanUpdateOrder(Order order) { return order.Status != OrderStatus.Filled && order.Status != OrderStatus.Canceled && order.Status != OrderStatus.PartiallyFilled && order.Status != OrderStatus.Invalid; }
/// <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() } }; var orderFee = OrderFee.Zero; 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> /// Cancels the order with the specified ID /// </summary> /// <param name="order">The order to cancel</param> /// <returns>True if the request was made for the order to be canceled, false otherwise</returns> public override bool CancelOrder(Order order) { Log.Trace("FxcmBrokerage.CancelOrder(): {0}", order); if (!IsConnected) throw new InvalidOperationException("FxcmBrokerage.UpdateOrder(): Unable to cancel order while not connected."); if (!order.BrokerId.Any()) { // we need the brokerage order id in order to perform a cancellation Log.Trace("FxcmBrokerage.CancelOrder(): Unable to cancel order without BrokerId."); return false; } var fxcmOrderId = order.BrokerId[0].ToString(); ExecutionReport fxcmOrder; if (!_openOrders.TryGetValue(fxcmOrderId, out fxcmOrder)) throw new ArgumentException("FxcmBrokerage.CancelOrder(): FXCM order id not found: " + fxcmOrderId); _isOrderUpdateOrCancelRejected = false; var orderCancelRequest = MessageGenerator.generateOrderCancelRequest("", fxcmOrder.getOrderID(), fxcmOrder.getSide(), fxcmOrder.getAccount()); AutoResetEvent autoResetEvent; lock (_locker) { _currentRequest = _gateway.sendMessage(orderCancelRequest); autoResetEvent = new AutoResetEvent(false); _mapRequestsToAutoResetEvents[_currentRequest] = autoResetEvent; } if (!autoResetEvent.WaitOne(ResponseTimeout)) throw new TimeoutException(string.Format("FxcmBrokerage.CancelOrder(): Operation took longer than {0} seconds.", (decimal)ResponseTimeout / 1000)); return !_isOrderUpdateOrCancelRejected; }
/// <summary> /// Sets the pending order as a clone to prevent object reference nastiness /// </summary> /// <param name="order">The order to be added to the pending orders dictionary</param> /// <returns></returns> private void SetPendingOrder(Order order) { // only save off clones! _pending[order.Id] = order.Clone(); }
private static Order AssertOrderOpened(bool orderFilled, InteractiveBrokersBrokerage ib, Order order) { // if the order didn't fill check for it as an open order if (!orderFilled) { // find the right order and return it foreach (var openOrder in ib.GetOpenOrders()) { if (openOrder.BrokerId.Any(id => order.BrokerId.Any(x => x == id))) { return(openOrder); } } Assert.Fail("The order was not filled and was unable to be located via GetOpenOrders()"); } Assert.Pass("The order was successfully filled!"); return(null); }
/// <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 requestParams = new Dictionary <string, string> { { "instrument", order.Symbol.Value }, { "units", Convert.ToInt32(order.AbsoluteQuantity).ToString() } }; PopulateOrderRequestParameters(order, requestParams); Log.Trace(order.ToString()); var priorOrderPositions = GetTradeList(requestParams); var postOrderResponse = PostOrderAsync(requestParams); if (postOrderResponse != null) { if (postOrderResponse.tradeOpened != null) { order.BrokerId.Add(postOrderResponse.tradeOpened.id); } if (postOrderResponse.tradeReduced != null) { order.BrokerId.Add(postOrderResponse.tradeReduced.id); } if (postOrderResponse.orderOpened != null) { order.BrokerId.Add(postOrderResponse.orderOpened.id); } const int orderFee = 0; OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, orderFee) { Status = OrderStatus.Submitted }); } else { return(false); } // we need to determine if there was an existing order and wheter we closed it with market orders. if (order.Type == OrderType.Market && order.Direction == OrderDirection.Buy) { //assume that we are opening a new buy market order if (postOrderResponse.tradeOpened != null && postOrderResponse.tradeOpened.id > 0) { var tradeOpenedId = postOrderResponse.tradeOpened.id; requestParams = new Dictionary <string, string>(); var tradeListResponse = GetTradeList(requestParams); if (tradeListResponse.trades.Any(trade => trade.id == tradeOpenedId)) { order.BrokerId.Add(tradeOpenedId); const int orderFee = 0; OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, orderFee) { Status = OrderStatus.Filled }); } } if (postOrderResponse.tradesClosed != null) { var tradePositionClosedIds = postOrderResponse.tradesClosed.Select(tradesClosed => tradesClosed.id).ToList(); var priorOrderPositionIds = priorOrderPositions.trades.Select(previousTrade => previousTrade.id).ToList(); var verifyClosedOrder = tradePositionClosedIds.Intersect(priorOrderPositionIds).Count() == tradePositionClosedIds.Count(); if (verifyClosedOrder) { const int orderFee = 0; OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, orderFee) { Status = OrderStatus.Filled }); } } } if (order.Type == OrderType.Market && order.Direction == OrderDirection.Sell) { //assume that we are opening a new buy market order if (postOrderResponse.tradeOpened != null && postOrderResponse.tradeOpened.id > 0) { var tradeOpenedId = postOrderResponse.tradeOpened.id; requestParams = new Dictionary <string, string>(); var tradeListResponse = GetTradeList(requestParams); if (tradeListResponse.trades.Any(trade => trade.id == tradeOpenedId)) { order.BrokerId.Add(tradeOpenedId); const int orderFee = 0; OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, orderFee) { Status = OrderStatus.Filled }); } } if (postOrderResponse.tradesClosed != null) { var tradePositionClosedIds = postOrderResponse.tradesClosed.Select(tradesClosed => tradesClosed.id).ToList(); var priorOrderPositionIds = priorOrderPositions.trades.Select(previousTrade => previousTrade.id).ToList(); var verifyClosedOrder = tradePositionClosedIds.Intersect(priorOrderPositionIds).Count() == tradePositionClosedIds.Count(); if (verifyClosedOrder) { const int orderFee = 0; OnOrderEvent(new OrderEvent(order, DateTime.UtcNow, orderFee) { Status = OrderStatus.Filled }); } } } return(true); }
private static void PopulateOrderRequestParameters(Order order, Dictionary <string, string> requestParams) { if (order.Direction != OrderDirection.Buy && order.Direction != OrderDirection.Sell) { throw new Exception("Invalid Order Direction"); } requestParams.Add("side", order.Direction == OrderDirection.Buy ? "buy" : "sell"); if (order.Type == OrderType.Market) { requestParams.Add("type", "market"); } if (order.Type == OrderType.Limit) { requestParams.Add("type", "limit"); requestParams.Add("price", ((LimitOrder)order).LimitPrice.ToString(CultureInfo.InvariantCulture)); switch (order.Direction) { case OrderDirection.Buy: //Limit Order Does not like Lower Bound Values == Limit Price value //Don't set bounds when placing limit orders. //Orders can be submitted with lower and upper bounds. If the market price on execution falls outside these bounds, it is considered a "Bounds Violation" and the order is cancelled. break; case OrderDirection.Sell: //Limit Order Does not like Lower Bound Values == Limit Price value //Don't set bounds when placing limit orders. //Orders can be submitted with lower and upper bounds. If the market price on execution falls outside these bounds, it is considered a "Bounds Violation" and the order is cancelled. break; } //3 months is the max expiry for Oanda, and OrderDuration.GTC is only currently available requestParams.Add("expiry", XmlConvert.ToString(DateTime.Now.AddMonths(3), XmlDateTimeSerializationMode.Utc)); } //this type should contain a stop and a limit to that stop. if (order.Type == OrderType.StopLimit) { requestParams.Add("type", "stop"); requestParams.Add("price", ((StopLimitOrder)order).StopPrice.ToString(CultureInfo.InvariantCulture)); switch (order.Direction) { case OrderDirection.Buy: requestParams.Add("upperBound", ((StopLimitOrder)order).LimitPrice.ToString(CultureInfo.InvariantCulture)); break; case OrderDirection.Sell: requestParams.Add("lowerBound", ((StopLimitOrder)order).LimitPrice.ToString(CultureInfo.InvariantCulture)); break; } //3 months is the max expiry for Oanda, and OrderDuration.GTC is only currently available requestParams.Add("expiry", XmlConvert.ToString(DateTime.Now.AddMonths(3), XmlDateTimeSerializationMode.Utc)); } if (order.Type == OrderType.StopMarket) { requestParams.Add("type", "marketIfTouched"); requestParams.Add("price", ((StopMarketOrder)order).StopPrice.ToString(CultureInfo.InvariantCulture)); //3 months is the max expiry for Oanda, and OrderDuration.GTC is only currently available requestParams.Add("expiry", XmlConvert.ToString(DateTime.Now.AddMonths(3), XmlDateTimeSerializationMode.Utc)); } }
/// <summary> /// Updates the order with the same id /// </summary> /// <param name="order">The new order information</param> /// <returns>True if the request was made for the order to be updated, false otherwise</returns> public override bool UpdateOrder(Order order) { Log.Trace("FxcmBrokerage.UpdateOrder(): {0}", order); if (!IsConnected) throw new InvalidOperationException("FxcmBrokerage.UpdateOrder(): Unable to update order while not connected."); if (!order.BrokerId.Any()) { // we need the brokerage order id in order to perform an update Log.Trace("FxcmBrokerage.UpdateOrder(): Unable to update order without BrokerId."); return false; } var fxcmOrderId = order.BrokerId[0].ToString(); ExecutionReport fxcmOrder; if (!_openOrders.TryGetValue(fxcmOrderId, out fxcmOrder)) throw new ArgumentException("FxcmBrokerage.UpdateOrder(): FXCM order id not found: " + fxcmOrderId); double price; switch (order.Type) { case OrderType.Limit: price = (double)((LimitOrder)order).LimitPrice; break; case OrderType.StopMarket: price = (double)((StopMarketOrder)order).StopPrice; break; default: throw new NotSupportedException("FxcmBrokerage.UpdateOrder(): Invalid order type."); } _isOrderUpdateOrCancelRejected = false; var orderReplaceRequest = MessageGenerator.generateOrderReplaceRequest("", fxcmOrder.getOrderID(), fxcmOrder.getSide(), fxcmOrder.getOrdType(), price, fxcmOrder.getAccount()); orderReplaceRequest.setInstrument(fxcmOrder.getInstrument()); orderReplaceRequest.setOrderQty((double)order.AbsoluteQuantity); AutoResetEvent autoResetEvent; lock (_locker) { _currentRequest = _gateway.sendMessage(orderReplaceRequest); autoResetEvent = new AutoResetEvent(false); _mapRequestsToAutoResetEvents[_currentRequest] = autoResetEvent; } if (!autoResetEvent.WaitOne(ResponseTimeout)) throw new TimeoutException(string.Format("FxcmBrokerage.UpdateOrder(): Operation took longer than {0} seconds.", (decimal)ResponseTimeout / 1000)); return !_isOrderUpdateOrCancelRejected; }