diff --git a/Common/Orders/Fills/EquityFillModel.cs b/Common/Orders/Fills/EquityFillModel.cs index a1c766b70d4f..d347fd4096e9 100644 --- a/Common/Orders/Fills/EquityFillModel.cs +++ b/Common/Orders/Fills/EquityFillModel.cs @@ -563,9 +563,6 @@ public override OrderEvent MarketOnOpenFill(Security asset, MarketOnOpenOrder or fill.FillQuantity = order.Quantity; fill.Status = OrderStatus.Filled; - //Calculate the model slippage: e.g. 0.01c - var slip = asset.SlippageModel.GetSlippageApproximation(asset, order); - var bestEffortMessage = ""; // If there is no trade information, get the bid or ask, then apply the slippage @@ -578,7 +575,7 @@ public override OrderEvent MarketOnOpenFill(Security asset, MarketOnOpenOrder or fill.Message += bestEffortMessage; } - fill.FillPrice += slip; + fill.FillPrice += asset.SlippageModel.GetSlippageApproximation(asset, order, fill.FillPrice); break; case OrderDirection.Sell: if (fill.FillPrice == 0) @@ -587,7 +584,7 @@ public override OrderEvent MarketOnOpenFill(Security asset, MarketOnOpenOrder or fill.Message += bestEffortMessage; } - fill.FillPrice -= slip; + fill.FillPrice -= asset.SlippageModel.GetSlippageApproximation(asset, order, fill.FillPrice); break; } diff --git a/Common/Orders/Slippage/ConstantSlippageModel.cs b/Common/Orders/Slippage/ConstantSlippageModel.cs index 43e348060b68..c8991e403d19 100644 --- a/Common/Orders/Slippage/ConstantSlippageModel.cs +++ b/Common/Orders/Slippage/ConstantSlippageModel.cs @@ -43,5 +43,18 @@ public decimal GetSlippageApproximation(Security asset, Order order) return lastData.Value*_slippagePercent; } + + /// + /// Slippage Model. Return a decimal cash slippage approximation on the order + /// using the provided reference price. + /// + /// The security matching the order + /// The order to compute slippage for + /// The price used as the reference for the slippage calculation + /// The slippage approximation + public decimal GetSlippageApproximation(Security asset, Order order, decimal referencePrice) + { + return referencePrice * _slippagePercent; + } } } diff --git a/Common/Orders/Slippage/ISlippageModel.cs b/Common/Orders/Slippage/ISlippageModel.cs index c0826f2560ab..589dabd9c7d6 100644 --- a/Common/Orders/Slippage/ISlippageModel.cs +++ b/Common/Orders/Slippage/ISlippageModel.cs @@ -26,5 +26,18 @@ public interface ISlippageModel /// Slippage Model. Return a decimal cash slippage approximation on the order. /// decimal GetSlippageApproximation(Security asset, Order order); + + /// + /// Slippage Model. Return a decimal cash slippage approximation on the order + /// using the provided reference price. + /// + /// The security matching the order + /// The order to compute slippage for + /// The price used as the reference for the slippage calculation + /// The slippage approximation + decimal GetSlippageApproximation(Security asset, Order order, decimal referencePrice) + { + return GetSlippageApproximation(asset, order); + } } -} \ No newline at end of file +} diff --git a/Tests/Common/Orders/Fills/EquityFillModelTests.cs b/Tests/Common/Orders/Fills/EquityFillModelTests.cs index 8dc4c81bbe7a..9cec4fe8fb0a 100644 --- a/Tests/Common/Orders/Fills/EquityFillModelTests.cs +++ b/Tests/Common/Orders/Fills/EquityFillModelTests.cs @@ -22,6 +22,7 @@ using QuantConnect.Indicators; using QuantConnect.Orders; using QuantConnect.Orders.Fills; +using QuantConnect.Orders.Slippage; using QuantConnect.Securities; using QuantConnect.Securities.Forex; using QuantConnect.Tests.Common.Data; @@ -446,6 +447,69 @@ public void PerformsMarketOnOpenUsingOpenPriceWithMinuteSubscription(int quantit Assert.AreEqual(expected, fill.FillPrice); } + [TestCase(-100, 103.896)] + [TestCase(100, 104.104)] + public void PerformsMarketOnOpenUsingOpenPriceForConstantSlippageWithDailySubscription(int quantity, decimal expected) + { + const decimal open = 104m; + const decimal baselineClose = 105m; + const decimal mutatedClose = 103.5m; + const decimal slippagePercent = 0.001m; + + var reference = new DateTime(2015, 06, 05, 12, 0, 0); + var config = CreateTradeBarConfig(Symbols.SPY, Resolution.Daily); + + var baselineEquity = CreateEquity(config); + var mutatedEquity = CreateEquity(config); + + baselineEquity.SetSlippageModel(new ConstantSlippageModel(slippagePercent)); + mutatedEquity.SetSlippageModel(new ConstantSlippageModel(slippagePercent)); + + var time = baselineEquity.Exchange.Hours.GetNextMarketOpen(reference, false); + TimeKeeper.SetUtcDateTime(time.ConvertToUtc(TimeZones.NewYork)); + + TradeBar GetTradeBar(decimal close) => new TradeBar( + time.RoundDown(Time.OneDay), + Symbols.SPY, + open, + 106m, + 100m, + close, + 100, + Time.OneDay); + + baselineEquity.SetMarketPrice(GetTradeBar(baselineClose)); + mutatedEquity.SetMarketPrice(GetTradeBar(mutatedClose)); + + var baselineOrder = new MarketOnOpenOrder(Symbols.SPY, quantity, reference); + var mutatedOrder = new MarketOnOpenOrder(Symbols.SPY, quantity, reference); + + var configProvider = new MockSubscriptionDataConfigProvider(config); + + var baselineFill = ((EquityFillModel)baselineEquity.FillModel) + .Fill(new FillModelParameters( + baselineEquity, + baselineOrder, + configProvider, + Time.OneHour, + null)) + .Single(); + + var mutatedFill = ((EquityFillModel)mutatedEquity.FillModel) + .Fill(new FillModelParameters( + mutatedEquity, + mutatedOrder, + configProvider, + Time.OneHour, + null)) + .Single(); + + Assert.AreEqual(quantity, baselineFill.FillQuantity); + Assert.AreEqual(quantity, mutatedFill.FillQuantity); + Assert.AreEqual(expected, baselineFill.FillPrice); + Assert.AreEqual(baselineFill.FillPrice, mutatedFill.FillPrice); + } + [TestCase(-100)] [TestCase(100)] public void PerformsMarketOnOpenUsingOpenPriceWithDailySubscription(int quantity) diff --git a/Tests/Common/Orders/Slippage/SlippageModelsTests.cs b/Tests/Common/Orders/Slippage/SlippageModelsTests.cs index b37dda8e0ccf..0a468e0dc31a 100644 --- a/Tests/Common/Orders/Slippage/SlippageModelsTests.cs +++ b/Tests/Common/Orders/Slippage/SlippageModelsTests.cs @@ -161,5 +161,22 @@ public void AlphaStreamsSlippageModel_ForexTest() var actual = model.GetSlippageApproximation(_forex, _forexBuyOrder); Assert.AreEqual(expected, actual); } + [Test] + public void SlippageModelReferencePriceOverloadIsBackwardsCompatible() + { + ISlippageModel model = new LegacySlippageModel(); + + var actual = model.GetSlippageApproximation(_equity, _equityBuyOrder, 123m); + + Assert.AreEqual(42m, actual); + } + + private sealed class LegacySlippageModel : ISlippageModel + { + public decimal GetSlippageApproximation(Security asset, Order order) + { + return 42m; + } + } } }