/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using QuantConnect.Util; using QuantConnect.Data; using QuantConnect.Securities; using QuantConnect.Orders.Fees; namespace QuantConnect.Orders.Fills { /// /// Represents the fill model used to simulate order fills for futures /// public class FutureFillModel : ImmediateFillModel { /// /// Default market fill model for the base security class. Fills at the last traded price. /// /// Security asset we're filling /// Order packet to model /// Order fill information detailing the average price and quantity filled. public override OrderEvent MarketFill(Security asset, MarketOrder order) { //Default order event to return. var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone); var fill = new OrderEvent(order, utcTime, OrderFee.Zero); if (order.Status == OrderStatus.Canceled) return fill; // make sure the exchange is open on regular/extended market hours before filling if (!IsExchangeOpen(asset, true)) return fill; var prices = GetPricesCheckingPythonWrapper(asset, order.Direction); var pricesEndTimeUtc = prices.EndTime.ConvertToUtc(asset.Exchange.TimeZone); // if the order is filled on stale (fill-forward) data, set a warning message on the order event if (pricesEndTimeUtc.Add(Parameters.StalePriceTimeSpan) < order.Time) { fill.Message = Messages.FillModel.FilledAtStalePrice(asset, prices); } //Order [fill]price for a market order model is the current security price fill.FillPrice = prices.Current; 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 fill.FillQuantity = order.Quantity; return fill; } /// /// Stop fill model implementation for Future. /// /// Security asset we're filling /// Order packet to model /// Order fill information detailing the average price and quantity filled. /// /// A Stop order is an instruction to submit a buy or sell market order /// if and when the user-specified stop trigger price is attained or penetrated. /// /// A Sell Stop order is always placed below the current market price. /// We assume a fluid/continuous, high volume market. Therefore, it is filled at the stop trigger price /// if the current low price of trades is less than or equal to this price. /// /// A Buy Stop order is always placed above the current market price. /// We assume a fluid, high volume market. Therefore, it is filled at the stop trigger price /// if the current high price of trades is greater or equal than this price. /// /// The continuous market assumption is not valid if the market opens with an unfavorable gap. /// In this case, a new bar opens below/above the stop trigger price, and the order is filled with the opening price. /// public override OrderEvent StopMarketFill(Security asset, StopMarketOrder order) { //Default order event to return. var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone); var fill = new OrderEvent(order, utcTime, OrderFee.Zero); //If its cancelled don't need anymore checks: if (order.Status == OrderStatus.Canceled) return fill; // Fill only if open or extended // even though data from internal configurations are not sent to the algorithm.OnData they still drive security cache and data // this is specially relevant for the continuous contract underlying mapped contracts which are internal configurations if (!IsExchangeOpen(asset, Parameters.ConfigProvider.GetSubscriptionDataConfigs(asset.Symbol, includeInternalConfigs: true).IsExtendedMarketHours())) { return fill; } //Get the range of prices in the last bar: var prices = GetPricesCheckingPythonWrapper(asset, order.Direction); var pricesEndTime = prices.EndTime.ConvertToUtc(asset.Exchange.TimeZone); // do not fill on stale data if (pricesEndTime <= order.Time) return fill; //Calculate the model slippage: e.g. 0.01c var slip = asset.SlippageModel.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 (prices.Low < order.StopPrice) { fill.Status = OrderStatus.Filled; // Assuming worse case scenario fill - fill at lowest of the stop & asset price. fill.FillPrice = Math.Min(order.StopPrice, prices.Current - slip); // assume the order completely filled fill.FillQuantity = order.Quantity; } break; case OrderDirection.Buy: //-> 1.2 Buy Stop: If Price Above Setpoint, Buy: if (prices.High > order.StopPrice) { fill.Status = OrderStatus.Filled; // Assuming worse case scenario fill - fill at highest of the stop & asset price. fill.FillPrice = Math.Max(order.StopPrice, prices.Current + slip); // assume the order completely filled fill.FillQuantity = order.Quantity; } break; } return fill; } } }