/// <summary> /// Get the Slippage approximation for this order: /// </summary> public virtual 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 the switch (security.Resolution) { case Resolution.Minute: case Resolution.Second: //Get the last data packet: TradeBar lastBar = (TradeBar)security.GetLastData(); //Assume slippage is 1/10,000th of the price slippage = lastBar.Value * 0.0001m; break; case Resolution.Tick: Tick 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> /// 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> /// The percentage of the holding's absolute cost that must be held in free cash in order to avoid a margin call /// </summary> protected virtual decimal GetMaintenanceMarginRequirement(Security security, decimal holdingValue) { if (security == null || security.GetLastData() == null || holdingValue == 0m) { return(0m); } var symbol = security.Symbol; var date = security.GetLastData().Time.Date; var marginReq = GetCurrentMarginRequirements(symbol, date); return(marginReq.MaintenanceOvernight / holdingValue); }
/// <summary> /// Slippage Model. Return a decimal cash slippage approximation on the order. /// </summary> public override decimal GetSlippageApproximation(Security asset, Order order) { var lastData = asset.GetLastData(); if (lastData == null) return 0; return lastData.Value*_slippagePercent; }
/// <summary> /// Check if the price MarketDataed to our limit price yet: /// </summary> /// <param name="security">Asset we're working with</param> /// <param name="order">Limit order in market</param> public virtual OrderEvent LimitFill(Security security, Order order) { //Initialise; decimal marketDataMinPrice = 0; decimal marketDataMaxPrice = 0; var fill = new OrderEvent(order); try { //If its cancelled don't need anymore checks: if (order.Status == OrderStatus.Canceled) { return(fill); } //Depending on the resolution, return different data types: BaseData marketData = security.GetLastData(); if (marketData.DataType == MarketDataType.TradeBar) { marketDataMinPrice = ((TradeBar)marketData).Low; marketDataMaxPrice = ((TradeBar)marketData).High; } else { marketDataMinPrice = marketData.Value; marketDataMaxPrice = marketData.Value; } //-> Valid Live/Model Order: if (order.Direction == OrderDirection.Buy) { //Buy limit seeks lowest price if (marketDataMinPrice < order.Price) { order.Status = OrderStatus.Filled; } } else if (order.Direction == OrderDirection.Sell) { //Sell limit seeks highest price possible if (marketDataMaxPrice > order.Price) { order.Status = OrderStatus.Filled; } } //Fill price if (order.Status == OrderStatus.Filled || order.Status == OrderStatus.PartiallyFilled) { fill.FillQuantity = order.Quantity; fill.FillPrice = order.Price; fill.Status = order.Status; } } catch (Exception err) { Log.Error("ForexTransactionModel.TransOrderDirection.LimitFill(): " + err.Message); } return(fill); }
/// <summary> /// Check if the price MarketDataed to our limit price yet: /// </summary> /// <param name="security">Asset we're working with</param> /// <param name="order">Limit order in market</param> public virtual void LimitFill(Security security, ref Order order) { //Initialise; decimal marketDataMinPrice = 0; decimal marketDataMaxPrice = 0; try { //If its cancelled don't need anymore checks: if (order.Status == OrderStatus.Canceled) { return; } //Calculate the model slippage: e.g. 0.01c decimal slip = GetSlippageApproximation(security, order); //Depending on the resolution, return different data types: BaseData marketData = security.GetLastData(); if (marketData.DataType == MarketDataType.TradeBar) { marketDataMinPrice = ((TradeBar)marketData).Low; marketDataMaxPrice = ((TradeBar)marketData).High; } else { marketDataMinPrice = marketData.Value; marketDataMaxPrice = marketData.Value; } //-> Valid Live/Model Order: switch (order.Direction) { case OrderDirection.Buy: //Buy limit seeks lowest price if (marketDataMinPrice < order.Price) { order.Status = OrderStatus.Filled; order.Price = security.Price; order.Price += slip; } break; case OrderDirection.Sell: //Sell limit seeks highest price possible if (marketDataMaxPrice > order.Price) { order.Status = OrderStatus.Filled; order.Price = security.Price; order.Price -= slip; } break; } //Round off: order.Price = Math.Round(order.Price, 2); } catch (Exception err) { Log.Error("Equity.TransOrderDirection.LimitFill(): " + err.Message); } }
/// <summary> /// The percentage of an order's absolute cost that must be held in free cash in order to place the order /// </summary> protected virtual decimal GetInitialMarginRequirement(Security security, decimal holdingValue) { var symbol = security.Symbol; var date = security.GetLastData().Time.Date; var marginReq = GetCurrentMarginRequirements(symbol, date); return(marginReq.InitialOvernight / holdingValue); }
/// <summary> /// Gets the margin currently alloted to the specified holding /// </summary> /// <param name="security">The security to compute maintenance margin for</param> /// <returns>The maintenance margin required for the </returns> public virtual decimal GetMaintenanceMargin(Security security) { if (security?.GetLastData() == null || security.Holdings.HoldingsCost == 0m) { return(0m); } var symbol = security.Symbol; var date = security.GetLastData().Time.Date; var marginReq = GetCurrentMarginRequirements(symbol, date); return(marginReq.MaintenanceOvernight * Math.Sign(security.Holdings.HoldingsCost)); }
/// <summary> /// Determines if the exchange is open using the current time of the asset /// </summary> private static bool IsExchangeOpen(Security asset) { if (!asset.Exchange.DateTimeIsOpen(asset.LocalTime)) { // if we're not open at the current time exactly, check the bar size, this handle large sized bars (hours/days) var currentBar = asset.GetLastData(); if (!asset.Exchange.IsOpenDuringBar(currentBar.Time, currentBar.EndTime, false)) { return(false); } } return(true); }
/// <summary> /// Slippage Model. Return a decimal cash slippage approximation on the order. /// </summary> public virtual decimal GetSlippageApproximation(Security asset, Order order) { var lastData = asset.GetLastData(); var lastTick = lastData as Tick; // if we have tick data use the spread if (lastTick != null) { return (lastTick.AskPrice - lastTick.BidPrice) / 2; } return 0m; }
/// <summary> /// Get the minimum and maximum price for this security in the last bar: /// </summary> /// <param name="asset">Security asset we're checking</param> /// <param name="minimumPrice">Minimum price in the last data bar</param> /// <param name="maximumPrice">Minimum price in the last data bar</param> public virtual void DataMinMaxPrices(Security asset, out decimal minimumPrice, out decimal maximumPrice) { var marketData = asset.GetLastData(); if (marketData.DataType == MarketDataType.TradeBar) { minimumPrice = ((TradeBar)marketData).Low; maximumPrice = ((TradeBar)marketData).High; } else { minimumPrice = marketData.Value; maximumPrice = marketData.Value; } }
/// <summary> /// Get the minimum and maximum price for this security in the last bar: /// </summary> /// <param name="asset">Security asset we're checking</param> /// <param name="minimumPrice">Minimum price in the last data bar</param> /// <param name="maximumPrice">Minimum price in the last data bar</param> public virtual void DataMinMaxPrices(Security asset, out decimal minimumPrice, out decimal maximumPrice) { var marketData = asset.GetLastData(); var tradeBar = marketData as TradeBar; if (tradeBar != null) { minimumPrice = tradeBar.Low; maximumPrice = tradeBar.High; } else { minimumPrice = marketData.Value; maximumPrice = marketData.Value; } }
//LIMIT FILL MODEL: public virtual void LimitFill(Security security, ref Order order) { //Initialise; decimal marketDataMinPrice = 0; decimal marketDataMaxPrice = 0; try { //If its cancelled don't need anymore checks: if (order.Status == OrderStatus.Canceled) return; //Depending on the resolution, return different data types: Futures contract = security.GetLastData() as Futures; if (contract == null) { //Shouldnt happen. } marketDataMinPrice = contract.Low; marketDataMaxPrice = contract.High; //Valid Live/Model Order: if (order.Direction == OrderDirection.Buy) { //Buy limit seeks lowest price if (marketDataMinPrice < order.Price) { order.Status = OrderStatus.Filled; } } else if (order.Direction == OrderDirection.Sell) { //Sell limit seeks highest price possible if (marketDataMaxPrice > order.Price) { order.Status = OrderStatus.Filled; } } } catch (Exception err) { algo.Error("CustomTransactionModel.TransOrderDirection.LimitFill():" + err.Message); } }
/// <summary> /// Get the minimum and maximum price for this security in the last bar: /// </summary> /// <param name="asset">Security asset we're checking</param> /// <param name="minimumPrice">Minimum price in the last data bar</param> /// <param name="maximumPrice">Minimum price in the last data bar</param> public virtual void DataMinMaxPrices(Security asset, out decimal minimumPrice, out decimal maximumPrice) { var marketData = asset.GetLastData(); var tradeBar = marketData as TradeBar; if (tradeBar != null) { minimumPrice = tradeBar.Low; maximumPrice = tradeBar.High; } else { minimumPrice = marketData.Value; maximumPrice = marketData.Value; } }
/// <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; }
/// <summary> /// Slippage Model. Return a decimal cash slippage approximation on the order. /// </summary> public virtual decimal GetSlippageApproximation(Security asset, Order order) { var lastData = asset.GetLastData(); var lastTick = lastData as Tick; // if we have tick data use the spread if (lastTick != null) { if (order.Direction == OrderDirection.Buy) { //We're buying, assume slip to Asking Price. return Math.Abs(order.Price - lastTick.AskPrice); } if (order.Direction == OrderDirection.Sell) { //We're selling, assume slip to the bid price. return Math.Abs(order.Price - lastTick.BidPrice); } } return 0m; }
/// <summary> /// Check if the price MarketDataed to our limit price yet: /// </summary> /// <param name="security">Asset we're working with</param> /// <param name="order">Limit order in market</param> public virtual OrderEvent LimitFill(Security security, Order order) { //Initialise; decimal marketDataMinPrice = 0; decimal marketDataMaxPrice = 0; var fill = new OrderEvent(order); try { //If its cancelled don't need anymore checks: if (fill.Status == OrderStatus.Canceled) return fill; //Depending on the resolution, return different data types: BaseData marketData = security.GetLastData(); if (marketData.DataType == MarketDataType.TradeBar) { marketDataMinPrice = ((TradeBar)marketData).Low; marketDataMaxPrice = ((TradeBar)marketData).High; } else { marketDataMinPrice = marketData.Value; marketDataMaxPrice = marketData.Value; } //-> Valid Live/Model Order: switch (order.Direction) { case OrderDirection.Buy: //Buy limit seeks lowest price if (marketDataMinPrice < order.Price) { //Set order fill: order.Status = OrderStatus.Filled; order.Price = security.Price; } break; case OrderDirection.Sell: //Sell limit seeks highest price possible if (marketDataMaxPrice > order.Price) { order.Status = OrderStatus.Filled; order.Price = security.Price; } break; } if (order.Status == OrderStatus.Filled || order.Status == OrderStatus.PartiallyFilled) { fill.FillQuantity = order.Quantity; fill.FillPrice = security.Price; fill.Status = order.Status; } } catch (Exception err) { Log.Error("SecurityTransactionModel.TransOrderDirection.LimitFill(): " + err.Message); } return fill; }
/// <summary> /// Default market fill model for the base security class. Fills at the last traded price. /// </summary> /// <param name="asset">Security asset we're filling</param> /// <param name="order">Order packet to model</param> /// <returns>Order fill informaton detailing the average price and quantity filled.</returns> /// <seealso cref="StopMarketFill(Security, StopMarketOrder)"/> /// <seealso cref="LimitFill(Security, LimitOrder)"/> public virtual OrderEvent MarketFill(Security asset, MarketOrder order) { //Default order event to return. var fill = new OrderEvent(order); if (order.Status == OrderStatus.Canceled) return fill; // make sure the exchange is open before filling var currentBar = asset.GetLastData(); if (!asset.Exchange.IsOpenDuringBar(currentBar.Time, currentBar.EndTime, false)) return fill; try { //Order [fill]price for a market order model is the current security price. order.Price = asset.Price; order.Status = OrderStatus.Filled; //Calculate the model slippage: e.g. 0.01c var slip = GetSlippageApproximation(asset, order); //Apply slippage switch (order.Direction) { case OrderDirection.Buy: order.Price += slip; break; case OrderDirection.Sell: order.Price -= slip; break; } //For backtesting, we assuming the order is 100% filled on first attempt. fill.FillPrice = order.Price; fill.FillQuantity = order.Quantity; fill.Status = order.Status; } catch (Exception err) { Log.Error("SecurityTransactionModel.MarketFill(): " + err.Message); } return fill; }
/// <summary> /// Default limit order fill model in the base security class. /// </summary> /// <param name="asset">Security asset we're filling</param> /// <param name="order">Order packet to model</param> /// <returns>Order fill informaton detailing the average price and quantity filled.</returns> /// <seealso cref="StopMarketFill(Security, StopMarketOrder)"/> /// <seealso cref="MarketFill(Security, MarketOrder)"/> public OrderEvent LimitFill(Security asset, LimitOrder order) { //Initialise; var fill = new OrderEvent(order); try { //If its cancelled don't need anymore checks: if (fill.Status == OrderStatus.Canceled) { return(fill); } //Depending on the resolution, return different data types: var marketData = asset.GetLastData(); decimal marketDataMinPrice; decimal marketDataMaxPrice; if (marketData.DataType == MarketDataType.TradeBar) { marketDataMinPrice = ((TradeBar)marketData).Low; marketDataMaxPrice = ((TradeBar)marketData).High; } else { marketDataMinPrice = marketData.Value; marketDataMaxPrice = marketData.Value; } //-> Valid Live/Model Order: switch (order.Direction) { case OrderDirection.Buy: //Buy limit seeks lowest price if (marketDataMinPrice < order.LimitPrice) { //Set order fill: order.Status = OrderStatus.Filled; order.Price = asset.Price; } break; case OrderDirection.Sell: //Sell limit seeks highest price possible if (marketDataMaxPrice > order.LimitPrice) { order.Status = OrderStatus.Filled; order.Price = asset.Price; } break; } if (order.Status == OrderStatus.Filled || order.Status == OrderStatus.PartiallyFilled) { fill.FillQuantity = order.Quantity; fill.FillPrice = asset.Price; fill.Status = order.Status; } } catch (Exception err) { Log.Error("SecurityTransactionModel.LimitFill(): " + err.Message); } return(fill); }
/// <summary> /// Default stop fill model implementation in base class security. (Stop Market Order Type) /// </summary> /// <param name="asset">Security asset we're filling</param> /// <param name="order">Order packet to model</param> /// <returns>Order fill informaton detailing the average price and quantity filled.</returns> /// <seealso cref="MarketFill(Security, MarketOrder)"/> /// <seealso cref="LimitFill(Security, LimitOrder)"/> public virtual OrderEvent StopMarketFill(Security asset, StopMarketOrder order) { //Default order event to return. var fill = new OrderEvent(order); // make sure the exchange is open before filling var currentBar = asset.GetLastData(); if (!asset.Exchange.IsOpenDuringBar(currentBar.Time, currentBar.EndTime, false)) return fill; try { //If its cancelled don't need anymore checks: if (order.Status == OrderStatus.Canceled) return fill; //Get the range of prices in the last bar: decimal minimumPrice; decimal maximumPrice; DataMinMaxPrices(asset, out minimumPrice, out maximumPrice); //Calculate the model slippage: e.g. 0.01c var slip = GetSlippageApproximation(asset, order); //Check if the Stop Order was filled: opposite to a limit order switch (order.Direction) { case OrderDirection.Sell: //-> 1.1 Sell Stop: If Price below setpoint, Sell: if (minimumPrice < order.StopPrice) { order.Status = OrderStatus.Filled; // Assuming worse case scenario fill - fill at lowest of the stop & asset price. order.Price = Math.Min(order.StopPrice, asset.Price - slip); } break; case OrderDirection.Buy: //-> 1.2 Buy Stop: If Price Above Setpoint, Buy: if (maximumPrice > order.StopPrice) { order.Status = OrderStatus.Filled; // Assuming worse case scenario fill - fill at highest of the stop & asset price. order.Price = Math.Max(order.StopPrice, asset.Price + slip); } break; } if (order.Status == OrderStatus.Filled || order.Status == OrderStatus.PartiallyFilled) { fill.FillQuantity = order.Quantity; fill.FillPrice = order.Price; //we picked the correct fill price above, just respect it here fill.Status = order.Status; } } catch (Exception err) { Log.Error("SecurityTransactionModel.StopMarketFill(): " + err.Message); } return fill; }
/// <summary> /// Market on Open Fill Model. Return an order event with the fill details /// </summary> /// <param name="asset">Asset we're trading with this order</param> /// <param name="order">Order to be filled</param> /// <returns>Order fill informaton detailing the average price and quantity filled.</returns> public OrderEvent MarketOnOpenFill(Security asset, MarketOnOpenOrder order) { var fill = new OrderEvent(order); if (order.Status == OrderStatus.Canceled) { return(fill); } try { // MOO should never fill on the same bar or on stale data // Imagine the case where we have a thinly traded equity, ASUR, and another liquid // equity, say SPY, SPY gets data every minute but ASUR, if not on fill forward, maybe // have large gaps, in which case the currentBar.EndTime will be in the past // ASUR | | | [order] | | | | | | | // SPY | | | | | | | | | | | | | | | | | | | | var currentBar = asset.GetLastData(); var localOrderTime = order.Time.ConvertFromUtc(asset.Exchange.TimeZone); if (currentBar == null || localOrderTime >= currentBar.EndTime) { return(fill); } // if the MOO was submitted during market the previous day, wait for a day to turn over if (asset.Exchange.DateTimeIsOpen(localOrderTime) && localOrderTime.Date == asset.LocalTime.Date) { return(fill); } // wait until market open // make sure the exchange is open before filling if (!IsExchangeOpen(asset)) { return(fill); } order.Price = asset.Open; order.Status = OrderStatus.Filled; //Calculate the model slippage: e.g. 0.01c var slip = GetSlippageApproximation(asset, order); //Apply slippage switch (order.Direction) { case OrderDirection.Buy: order.Price += slip; break; case OrderDirection.Sell: order.Price -= slip; break; } //For backtesting, we assuming the order is 100% filled on first attempt. fill.FillPrice = order.Price; fill.FillQuantity = order.Quantity; fill.Status = order.Status; } catch (Exception err) { Log.Error(err); } return(fill); }
/// <summary> /// Get the minimum and maximum price for this security in the last bar: /// </summary> /// <param name="asset">Security asset we're checking</param> /// <param name="minimumPrice">Minimum price in the last data bar</param> /// <param name="maximumPrice">Minimum price in the last data bar</param> /// <param name="direction">The order direction, decides whether to pick bid or ask</param> public virtual void DataMinMaxPrices(Security asset, out decimal minimumPrice, out decimal maximumPrice, OrderDirection direction) { var tick = asset.Cache.GetData<Tick>(); if (tick != null) { var price = tick.Value; if (direction == OrderDirection.Sell && tick.BidPrice != 0) { price = tick.BidPrice; } else if (direction == OrderDirection.Buy && tick.AskPrice != 0) { price = tick.AskPrice; } minimumPrice = maximumPrice = price; return; } var quoteBar = asset.Cache.GetData<QuoteBar>(); if (quoteBar != null) { var bar = direction == OrderDirection.Sell ? quoteBar.Ask : quoteBar.Bid; minimumPrice = quoteBar.Low; maximumPrice = quoteBar.High; if (bar != null) { if (bar.High != 0) maximumPrice = bar.High; if (bar.Low != 0) minimumPrice = bar.Low; } return; } var tradeBar = asset.Cache.GetData<TradeBar>(); if (tradeBar != null) { minimumPrice = tradeBar.Low; maximumPrice = tradeBar.High; return; } var lastData = asset.GetLastData(); var lastBar = lastData as IBar; if (lastBar != null) { minimumPrice = lastBar.Low; maximumPrice = lastBar.High; return; } minimumPrice = maximumPrice = asset.Price; }
/// <summary> /// Determines if the exchange is open using the current time of the asset /// </summary> private static bool IsExchangeOpen(Security asset) { if (!asset.Exchange.DateTimeIsOpen(asset.LocalTime)) { // if we're not open at the current time exactly, check the bar size, this handle large sized bars (hours/days) var currentBar = asset.GetLastData(); if (asset.LocalTime.Date != currentBar.EndTime.Date || !asset.Exchange.IsOpenDuringBar(currentBar.Time, currentBar.EndTime, false)) { return false; } } return true; }
/// <summary> /// Market on Open Fill Model. Return an order event with the fill details /// </summary> /// <param name="asset">Asset we're trading with this order</param> /// <param name="order">Order to be filled</param> /// <returns>Order fill information detailing the average price and quantity filled.</returns> public OrderEvent MarketOnOpenFill(Security asset, MarketOnOpenOrder order) { var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone); var fill = new OrderEvent(order, utcTime, 0); if (order.Status == OrderStatus.Canceled) return fill; // MOO should never fill on the same bar or on stale data // Imagine the case where we have a thinly traded equity, ASUR, and another liquid // equity, say SPY, SPY gets data every minute but ASUR, if not on fill forward, maybe // have large gaps, in which case the currentBar.EndTime will be in the past // ASUR | | | [order] | | | | | | | // SPY | | | | | | | | | | | | | | | | | | | | var currentBar = asset.GetLastData(); var localOrderTime = order.Time.ConvertFromUtc(asset.Exchange.TimeZone); if (currentBar == null || localOrderTime >= currentBar.EndTime) return fill; // if the MOO was submitted during market the previous day, wait for a day to turn over if (asset.Exchange.DateTimeIsOpen(localOrderTime) && localOrderTime.Date == asset.LocalTime.Date) { return fill; } // wait until market open // make sure the exchange is open before filling if (!IsExchangeOpen(asset)) return fill; fill.FillPrice = asset.Open; fill.Status = OrderStatus.Filled; //Calculate the model slippage: e.g. 0.01c var slip = asset.SlippageModel.GetSlippageApproximation(asset, order); //Apply slippage switch (order.Direction) { case OrderDirection.Buy: fill.FillPrice += slip; break; case OrderDirection.Sell: fill.FillPrice -= slip; break; } // assume the order completely filled if (fill.Status == OrderStatus.Filled) { fill.FillQuantity = order.Quantity; fill.OrderFee = asset.FeeModel.GetOrderFee(asset, order); } return fill; }
/// <summary> /// Market on Open Fill Model. Return an order event with the fill details /// </summary> /// <param name="asset">Asset we're trading with this order</param> /// <param name="order">Order to be filled</param> /// <returns>Order fill informaton detailing the average price and quantity filled.</returns> public OrderEvent MarketOnOpenFill(Security asset, MarketOnOpenOrder order) { var fill = new OrderEvent(order); if (order.Status == OrderStatus.Canceled) return fill; try { // MOO should never fill on the same bar or on stale data // Imagine the case where we have a thinly traded equity, ASUR, and another liquid // equity, say SPY, SPY gets data every minute but ASUR, if not on fill forward, maybe // have large gaps, in which case the currentBar.EndTime will be in the past // ASUR | | | [order] | | | | | | | // SPY | | | | | | | | | | | | | | | | | | | | var currentBar = asset.GetLastData(); var localOrderTime = order.Time.ConvertTo(TimeZones.Utc, asset.Exchange.TimeZone); if (localOrderTime >= currentBar.EndTime) return fill; // if the MOO was submitted during market the previous day, wait for a day to turn over if (asset.Exchange.DateTimeIsOpen(localOrderTime) && localOrderTime.Date == asset.LocalTime.Date) { return fill; } // wait until market open // make sure the exchange is open before filling if (!IsExchangeOpen(asset)) return fill; order.Price = asset.Open; order.Status = OrderStatus.Filled; //Calculate the model slippage: e.g. 0.01c var slip = GetSlippageApproximation(asset, order); //Apply slippage switch (order.Direction) { case OrderDirection.Buy: order.Price += slip; break; case OrderDirection.Sell: order.Price -= slip; break; } //For backtesting, we assuming the order is 100% filled on first attempt. fill.FillPrice = order.Price; fill.FillQuantity = order.Quantity; fill.Status = order.Status; } catch (Exception err) { Log.Error(err); } return fill; }
/// <summary> /// Check if the price MarketDataed to our limit price yet: /// </summary> /// <param name="security">Asset we're working with</param> /// <param name="order">Limit order in market</param> public virtual void LimitFill(Security security, ref Order order) { //Initialise; decimal marketDataMinPrice = 0; decimal marketDataMaxPrice = 0; try { //If its cancelled don't need anymore checks: if (order.Status == OrderStatus.Canceled) return; //Calculate the model slippage: e.g. 0.01c decimal slip = GetSlippageApproximation(security, order); //Depending on the resolution, return different data types: BaseData marketData = security.GetLastData(); if (marketData.DataType == MarketDataType.TradeBar) { marketDataMinPrice = ((TradeBar)marketData).Low; marketDataMaxPrice = ((TradeBar)marketData).High; } else { marketDataMinPrice = marketData.Value; marketDataMaxPrice = marketData.Value; } //-> Valid Live/Model Order: switch (order.Direction) { case OrderDirection.Buy: //Buy limit seeks lowest price if (marketDataMinPrice < order.Price) { order.Status = OrderStatus.Filled; order.Price = security.Price; order.Price += slip; } break; case OrderDirection.Sell: //Sell limit seeks highest price possible if (marketDataMaxPrice > order.Price) { order.Status = OrderStatus.Filled; order.Price = security.Price; order.Price -= slip; } break; } //Round off: order.Price = Math.Round(order.Price, 2); } catch (Exception err) { Log.Error("Equity.TransOrderDirection.LimitFill(): " + err.Message); } }
/// <summary> /// Get the minimum and maximum price for this security in the last bar: /// </summary> /// <param name="asset">Security asset we're checking</param> /// <param name="direction">The order direction, decides whether to pick bid or ask</param> private Prices GetPrices(Security asset, OrderDirection direction) { var low = asset.Low; var high = asset.High; var open = asset.Open; var close = asset.Close; var current = asset.Price; if (direction == OrderDirection.Hold) { return new Prices(current, open, high, low, close); } var tick = asset.Cache.GetData<Tick>(); if (tick != null) { return new Prices(current, open, high, low, close); } var quoteBar = asset.Cache.GetData<QuoteBar>(); if (quoteBar != null) { var bar = direction == OrderDirection.Sell ? quoteBar.Bid : quoteBar.Ask; if (bar != null) { return new Prices(bar); } } var tradeBar = asset.Cache.GetData<TradeBar>(); if (tradeBar != null) { return new Prices(tradeBar); } var lastData = asset.GetLastData(); var lastBar = lastData as IBar; if (lastBar != null) { return new Prices(lastBar); } return new Prices(current, open, high, low, close); }
/// <summary> /// Check if the price MarketDataed to our limit price yet: /// </summary> /// <param name="security">Asset we're working with</param> /// <param name="order">Limit order in market</param> public virtual void LimitFill(Security security, ref Order order) { //Initialise; decimal marketDataMinPrice = 0; decimal marketDataMaxPrice = 0; try { //If its cancelled don't need anymore checks: if (order.Status == OrderStatus.Canceled) return; //Depending on the resolution, return different data types: BaseData marketData = security.GetLastData(); if (marketData.DataType == MarketDataType.TradeBar) { marketDataMinPrice = ((TradeBar)marketData).Low; marketDataMaxPrice = ((TradeBar)marketData).High; } else { marketDataMinPrice = marketData.Value; marketDataMaxPrice = marketData.Value; } //-> Valid Live/Model Order: if (order.Direction == OrderDirection.Buy) { //Buy limit seeks lowest price if (marketDataMinPrice < order.Price) { order.Status = OrderStatus.Filled; } } else if (order.Direction == OrderDirection.Sell) { //Sell limit seeks highest price possible if (marketDataMaxPrice > order.Price) { order.Status = OrderStatus.Filled; } } } catch (Exception err) { Log.Error("ForexTransactionModel.TransOrderDirection.LimitFill(): " + err.Message); } }