/*
* 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;
}
}
}