171 lines
7.9 KiB
C#
171 lines
7.9 KiB
C#
/*
|
|
* 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 System.Collections.Generic;
|
|
using QuantConnect.Util;
|
|
using QuantConnect.Data;
|
|
using QuantConnect.Securities;
|
|
using QuantConnect.Orders.Fees;
|
|
|
|
namespace QuantConnect.Orders.Fills
|
|
{
|
|
/// <summary>
|
|
/// Represents the fill model used to simulate order fills for futures
|
|
/// </summary>
|
|
public class FutureFillModel : ImmediateFillModel
|
|
{
|
|
/// <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 information detailing the average price and quantity filled.</returns>
|
|
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 would be filled on stale (fill-forward / already past) data, wait for fresh data instead of
|
|
// filling at a stale price when the latest data is more than one resolution bar behind the order submission
|
|
// time (e.g. the first bar of the session after the open, or an intraday data gap). Otherwise fill on the
|
|
// stale price with a warning.
|
|
// Fetched lazily and reused across the stale-data check and the fill-price resolution so the subscription
|
|
// configs are resolved at most once per fill.
|
|
List<SubscriptionDataConfig> subscriptionConfigs = null;
|
|
if (pricesEndTimeUtc.Add(Parameters.StalePriceTimeSpan) < order.Time)
|
|
{
|
|
subscriptionConfigs = GetSubscriptionDataConfigs(asset);
|
|
if (ShouldWaitForFreshDataOnStale(asset, pricesEndTimeUtc, order.Time, subscriptionConfigs))
|
|
{
|
|
return fill;
|
|
}
|
|
|
|
fill.Message = Messages.FillModel.FilledAtStalePrice(asset, prices);
|
|
}
|
|
|
|
//Order [fill]price for a market order model is the current security price (or the bar open if the order
|
|
//was resting before this bar opened, see GetMarketFillPrice)
|
|
fill.FillPrice = GetMarketFillPrice(asset, order, prices, subscriptionConfigs);
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stop fill model implementation for Future.
|
|
/// </summary>
|
|
/// <param name="asset">Security asset we're filling</param>
|
|
/// <param name="order">Order packet to model</param>
|
|
/// <returns>Order fill information detailing the average price and quantity filled.</returns>
|
|
/// <remarks>
|
|
/// 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.
|
|
/// <seealso cref="MarketFill(Security, MarketOrder)"/></remarks>
|
|
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
|
|
if (!IsExchangeOpen(asset, GetSubscriptionDataConfigs(asset).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;
|
|
}
|
|
}
|
|
}
|