diff --git a/Algorithm.CSharp/MarketImpactSlippageModelRegressionAlgorithm.cs b/Algorithm.CSharp/MarketImpactSlippageModelRegressionAlgorithm.cs
index f67f2fd48245..b4148a2489b9 100644
--- a/Algorithm.CSharp/MarketImpactSlippageModelRegressionAlgorithm.cs
+++ b/Algorithm.CSharp/MarketImpactSlippageModelRegressionAlgorithm.cs
@@ -95,31 +95,31 @@ public override void OnOrderEvent(OrderEvent orderEvent)
{"Total Orders", "9"},
{"Average Win", "0%"},
{"Average Loss", "-0.04%"},
- {"Compounding Annual Return", "-93.847%"},
- {"Drawdown", "4.200%"},
+ {"Compounding Annual Return", "-94.178%"},
+ {"Drawdown", "4.300%"},
{"Expectancy", "-1"},
{"Start Equity", "10000000"},
- {"End Equity", "9649801.20"},
- {"Net Profit", "-3.502%"},
- {"Sharpe Ratio", "-2.93"},
- {"Sortino Ratio", "-2.869"},
- {"Probabilistic Sharpe Ratio", "7.243%"},
+ {"End Equity", "9642964.36"},
+ {"Net Profit", "-3.570%"},
+ {"Sharpe Ratio", "-2.896"},
+ {"Sortino Ratio", "-2.829"},
+ {"Probabilistic Sharpe Ratio", "7.047%"},
{"Loss Rate", "100%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
- {"Alpha", "-3.355"},
- {"Beta", "1.244"},
- {"Annual Standard Deviation", "0.306"},
- {"Annual Variance", "0.094"},
- {"Information Ratio", "-20.203"},
- {"Tracking Error", "0.142"},
- {"Treynor Ratio", "-0.722"},
- {"Total Fees", "$1859.00"},
+ {"Alpha", "-3.395"},
+ {"Beta", "1.262"},
+ {"Annual Standard Deviation", "0.312"},
+ {"Annual Variance", "0.097"},
+ {"Information Ratio", "-19.58"},
+ {"Tracking Error", "0.147"},
+ {"Treynor Ratio", "-0.715"},
+ {"Total Fees", "$1860.21"},
{"Estimated Strategy Capacity", "$470000000.00"},
{"Lowest Capacity Asset", "AAPL R735QTJ8XC9X"},
- {"Portfolio Turnover", "21.04%"},
+ {"Portfolio Turnover", "21.06%"},
{"Drawdown Recovery", "0"},
- {"OrderListHash", "fc0626f660981cb698f6a9a5d5d1389a"}
+ {"OrderListHash", "6a2a541fbde8de8454e9ae3a20d1ed69"}
};
}
}
diff --git a/Algorithm.CSharp/MarketOnOpenOrderSlippageRegressionAlgorithm.cs b/Algorithm.CSharp/MarketOnOpenOrderSlippageRegressionAlgorithm.cs
new file mode 100644
index 000000000000..00131740aaa0
--- /dev/null
+++ b/Algorithm.CSharp/MarketOnOpenOrderSlippageRegressionAlgorithm.cs
@@ -0,0 +1,150 @@
+/*
+ * 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.Collections.Generic;
+using QuantConnect.Data;
+using QuantConnect.Data.Market;
+using QuantConnect.Interfaces;
+using QuantConnect.Orders;
+using QuantConnect.Orders.Slippage;
+
+namespace QuantConnect.Algorithm.CSharp
+{
+ ///
+ /// Regression algorithm asserting that market on open orders using daily data are filled at the bar open
+ /// with the slippage referenced to that same open price, not to the bar close which is not known at the open.
+ /// See GH issue 9753
+ ///
+ public class MarketOnOpenOrderSlippageRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
+ {
+ private const decimal SlippagePercent = 0.01m;
+ private Symbol _symbol;
+ private int _fills;
+
+ public override void Initialize()
+ {
+ SetStartDate(2013, 10, 07);
+ SetEndDate(2013, 10, 11);
+ SetCash(100000);
+
+ var security = AddEquity("SPY", Resolution.Daily);
+ security.SetSlippageModel(new ConstantSlippageModel(SlippagePercent));
+ _symbol = security.Symbol;
+ }
+
+ public override void OnData(Slice slice)
+ {
+ if (!slice.Bars.ContainsKey(_symbol) || Transactions.GetOpenOrders(_symbol).Count > 0)
+ {
+ return;
+ }
+
+ // alternate buys and sells so both directions are checked
+ MarketOnOpenOrder(_symbol, Portfolio[_symbol].Invested ? -100 : 100);
+ }
+
+ public override void OnOrderEvent(OrderEvent orderEvent)
+ {
+ if (orderEvent.Status != OrderStatus.Filled)
+ {
+ return;
+ }
+
+ // the fill happens when the daily bar arrives, so this is the bar the order was filled with
+ var bar = Securities[_symbol].Cache.GetData();
+ if (bar.Open == bar.Close)
+ {
+ throw new RegressionTestException($"Expected the fill bar open and close to differ so the slippage reference can be asserted: {bar}");
+ }
+
+ var slippage = bar.Open * SlippagePercent;
+ var expectedFillPrice = orderEvent.Direction == OrderDirection.Buy ? bar.Open + slippage : bar.Open - slippage;
+ if (orderEvent.FillPrice != expectedFillPrice)
+ {
+ throw new RegressionTestException($"Expected {orderEvent.Direction} fill price {expectedFillPrice} (open {bar.Open} +/- {SlippagePercent:P} slippage) but was {orderEvent.FillPrice}. Bar: {bar}");
+ }
+
+ _fills++;
+ }
+
+ public override void OnEndOfAlgorithm()
+ {
+ if (_fills < 2)
+ {
+ throw new RegressionTestException($"Expected at least a buy and a sell fill but got {_fills}");
+ }
+ }
+
+ ///
+ /// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
+ ///
+ public bool CanRunLocally { get; } = true;
+
+ ///
+ /// This is used by the regression test system to indicate which languages this algorithm is written in.
+ ///
+ public List Languages { get; } = new() { Language.CSharp };
+
+ ///
+ /// Data Points count of all timeslices of algorithm
+ ///
+ public long DataPoints => 48;
+
+ ///
+ /// Data Points count of the algorithm history
+ ///
+ public int AlgorithmHistoryDataPoints => 0;
+
+ ///
+ /// Final status of the algorithm
+ ///
+ public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
+
+ ///
+ /// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
+ ///
+ public Dictionary ExpectedStatistics => new Dictionary
+ {
+ {"Total Orders", "5"},
+ {"Average Win", "0%"},
+ {"Average Loss", "-0.29%"},
+ {"Compounding Annual Return", "-36.910%"},
+ {"Drawdown", "0.600%"},
+ {"Expectancy", "-1"},
+ {"Start Equity", "100000"},
+ {"End Equity", "99412.82"},
+ {"Net Profit", "-0.587%"},
+ {"Sharpe Ratio", "-14.31"},
+ {"Sortino Ratio", "-19.441"},
+ {"Probabilistic Sharpe Ratio", "1.568%"},
+ {"Loss Rate", "100%"},
+ {"Win Rate", "0%"},
+ {"Profit-Loss Ratio", "0"},
+ {"Alpha", "-0.502"},
+ {"Beta", "0.093"},
+ {"Annual Standard Deviation", "0.022"},
+ {"Annual Variance", "0"},
+ {"Information Ratio", "-11.354"},
+ {"Tracking Error", "0.202"},
+ {"Treynor Ratio", "-3.402"},
+ {"Total Fees", "$4.00"},
+ {"Estimated Strategy Capacity", "$1300000000.00"},
+ {"Lowest Capacity Asset", "SPY R735QTJ8XC9X"},
+ {"Portfolio Turnover", "11.63%"},
+ {"Drawdown Recovery", "0"},
+ {"OrderListHash", "4bbdfd7aaf0f2e4fa6cc9226fbf3d9e8"}
+ };
+ }
+}
diff --git a/Common/Orders/Slippage/AlphaStreamsSlippageModel.cs b/Common/Orders/Slippage/AlphaStreamsSlippageModel.cs
index 5158247cee7e..4dd98e3a6f0a 100644
--- a/Common/Orders/Slippage/AlphaStreamsSlippageModel.cs
+++ b/Common/Orders/Slippage/AlphaStreamsSlippageModel.cs
@@ -13,6 +13,7 @@
* limitations under the License.
*/
+using QuantConnect.Data.Market;
using QuantConnect.Securities;
using System.Collections.Generic;
@@ -40,7 +41,13 @@ public decimal GetSlippageApproximation(Security asset, Order order)
return 0;
}
- return _slippagePercent * asset.GetLastData()?.Value ?? 0;
+ var lastData = asset.GetLastData();
+ if (lastData == null) return 0;
+
+ // Market on open orders fill at the bar open, which is the price we have to reference, not the bar close
+ var referencePrice = order.Type == OrderType.MarketOnOpen && lastData is IBar bar ? bar.Open : lastData.Value;
+
+ return _slippagePercent * referencePrice;
}
}
}
\ No newline at end of file
diff --git a/Common/Orders/Slippage/ConstantSlippageModel.cs b/Common/Orders/Slippage/ConstantSlippageModel.cs
index 43e348060b68..ab77d7728784 100644
--- a/Common/Orders/Slippage/ConstantSlippageModel.cs
+++ b/Common/Orders/Slippage/ConstantSlippageModel.cs
@@ -14,6 +14,7 @@
*/
using QuantConnect.Data;
+using QuantConnect.Data.Market;
using QuantConnect.Securities;
namespace QuantConnect.Orders.Slippage
@@ -41,7 +42,10 @@ public decimal GetSlippageApproximation(Security asset, Order order)
var lastData = asset.GetLastData();
if (lastData == null) return 0;
- return lastData.Value*_slippagePercent;
+ // Market on open orders fill at the bar open, which is the price we have to reference, not the bar close
+ var referencePrice = order.Type == OrderType.MarketOnOpen && lastData is IBar bar ? bar.Open : lastData.Value;
+
+ return referencePrice * _slippagePercent;
}
}
}
diff --git a/Common/Orders/Slippage/MarketImpactSlippageModel.cs b/Common/Orders/Slippage/MarketImpactSlippageModel.cs
index 394e05a69486..7de6a5de472e 100644
--- a/Common/Orders/Slippage/MarketImpactSlippageModel.cs
+++ b/Common/Orders/Slippage/MarketImpactSlippageModel.cs
@@ -127,8 +127,11 @@ public decimal GetSlippageApproximation(Security asset, Order order)
// realized market impact
var realizedImpact = temporaryImpact + permanentImpact * 0.5d;
+ // Market on open orders fill at the bar open, which is the price we have to reference, not the bar close
+ var referencePrice = order.Type == OrderType.MarketOnOpen && asset.GetLastData() is IBar bar ? bar.Open : asset.Price;
+
// estimate the slippage by temporary impact
- return SlippageFromImpactEstimation(realizedImpact) * asset.Price;
+ return SlippageFromImpactEstimation(realizedImpact) * referencePrice;
}
///
diff --git a/Common/Orders/Slippage/VolumeShareSlippageModel.cs b/Common/Orders/Slippage/VolumeShareSlippageModel.cs
index 2841182f8bfa..ff6552174f69 100644
--- a/Common/Orders/Slippage/VolumeShareSlippageModel.cs
+++ b/Common/Orders/Slippage/VolumeShareSlippageModel.cs
@@ -86,7 +86,10 @@ public decimal GetSlippageApproximation(Security asset, Order order)
slippagePercent = volumeShare * volumeShare * _priceImpact;
}
- return slippagePercent * lastData.Value;
+ // Market on open orders fill at the bar open, which is the price we have to reference, not the bar close
+ var referencePrice = order.Type == OrderType.MarketOnOpen ? ((IBar)lastData).Open : lastData.Value;
+
+ return slippagePercent * referencePrice;
}
}
}
diff --git a/Common/Orders/Slippage/VolumeShareSlippageModel.py b/Common/Orders/Slippage/VolumeShareSlippageModel.py
index 3921c414a741..8cd3edb25fe8 100644
--- a/Common/Orders/Slippage/VolumeShareSlippageModel.py
+++ b/Common/Orders/Slippage/VolumeShareSlippageModel.py
@@ -58,4 +58,7 @@ def get_slippage_approximation(self, asset: Security, order: Order) -> float:
slippage_percent = volume_share * volume_share * self.price_impact
- return slippage_percent * last_data.Value;
+ # Market on open orders fill at the bar open, which is the price we have to reference, not the bar close
+ reference_price = last_data.open if order.type == OrderType.MARKET_ON_OPEN else last_data.value
+
+ return slippage_percent * reference_price
diff --git a/Tests/Common/Orders/Fills/EquityFillModelTests.cs b/Tests/Common/Orders/Fills/EquityFillModelTests.cs
index 8dc4c81bbe7a..b7ce196777ee 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,75 @@ public void PerformsMarketOnOpenUsingOpenPriceWithMinuteSubscription(int quantit
Assert.AreEqual(expected, fill.FillPrice);
}
+ // The slippage is referenced to the bar open, so the fill price does not depend on the bar close
+ [TestCase(-100, 103.896, Resolution.Daily)]
+ [TestCase(100, 104.104, Resolution.Daily)]
+ [TestCase(-100, 103.896, Resolution.Hour)]
+ [TestCase(100, 104.104, Resolution.Hour)]
+ [TestCase(-100, 103.896, Resolution.Minute)]
+ [TestCase(100, 104.104, Resolution.Minute)]
+ public void PerformsMarketOnOpenUsingOpenPriceForConstantSlippage(int quantity, decimal expected, Resolution resolution)
+ {
+ 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);
+
+ 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));
+
+ var period = resolution.ToTimeSpan();
+ TradeBar GetTradeBar(decimal close) => new TradeBar(
+ time.RoundDown(period),
+ Symbols.SPY,
+ open,
+ 106m,
+ 100m,
+ close,
+ 100,
+ period);
+
+ 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/MarketImpactSlippageModelTest.cs b/Tests/Common/Orders/Slippage/MarketImpactSlippageModelTest.cs
index 6a931d6a4688..81a69ca8bb0d 100644
--- a/Tests/Common/Orders/Slippage/MarketImpactSlippageModelTest.cs
+++ b/Tests/Common/Orders/Slippage/MarketImpactSlippageModelTest.cs
@@ -162,6 +162,23 @@ public void SlippageExpectationTests(decimal orderQuantity, int index, double ex
Assert.AreEqual(expected, (double)slippage, 0.005d);
}
+ // Market on open orders fill at the bar open, so the slippage is referenced to it instead of the close
+ [TestCase(10000)]
+ [TestCase(-10000)]
+ public void MarketOnOpenOrdersReferenceTheOpenPrice(decimal orderQuantity)
+ {
+ var asset = _securities[0];
+ asset.SetMarketPrice(new TradeBar(_algorithm.Time, asset.Symbol, 90m, 110m, 80m, 100m, 1));
+ var time = new DateTime(2015, 6, 10, 14, 00, 0);
+
+ // fresh models so both use the same noise sequence
+ var marketSlippage = new MarketImpactSlippageModel(_algorithm).GetSlippageApproximation(asset, new MarketOrder(asset.Symbol, orderQuantity, time));
+ var marketOnOpenSlippage = new MarketImpactSlippageModel(_algorithm).GetSlippageApproximation(asset, new MarketOnOpenOrder(asset.Symbol, orderQuantity, time));
+
+ Assert.AreEqual(0.5075d, (double)marketSlippage, 0.005d);
+ Assert.AreEqual((double)marketSlippage * 0.9d, (double)marketOnOpenSlippage, 0.0001d);
+ }
+
// Test on buy & sell orders
[TestCase(1)]
[TestCase(-1)]
diff --git a/Tests/Common/Orders/Slippage/SlippageModelsTests.cs b/Tests/Common/Orders/Slippage/SlippageModelsTests.cs
index b37dda8e0ccf..a15f5e4725a5 100644
--- a/Tests/Common/Orders/Slippage/SlippageModelsTests.cs
+++ b/Tests/Common/Orders/Slippage/SlippageModelsTests.cs
@@ -14,13 +14,17 @@
*/
using NUnit.Framework;
+using Python.Runtime;
+using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Orders;
using QuantConnect.Orders.Slippage;
+using QuantConnect.Python;
using QuantConnect.Securities;
using QuantConnect.Securities.Equity;
using QuantConnect.Securities.Forex;
using System;
+using System.IO;
namespace QuantConnect.Tests.Common.Orders.Slippage
{
@@ -161,5 +165,104 @@ public void AlphaStreamsSlippageModel_ForexTest()
var actual = model.GetSlippageApproximation(_forex, _forexBuyOrder);
Assert.AreEqual(expected, actual);
}
+
+ // Market on open orders fill at the bar open, so the slippage is referenced to it instead of the close.
+ // Any other order type keeps using the last price. Ticks have no open so the price is used
+ [TestCase(MarketDataType.TradeBar, OrderType.Market, 100)]
+ [TestCase(MarketDataType.TradeBar, OrderType.MarketOnOpen, 90)]
+ [TestCase(MarketDataType.TradeBar, OrderType.MarketOnClose, 100)]
+ [TestCase(MarketDataType.TradeBar, OrderType.Limit, 100)]
+ [TestCase(MarketDataType.QuoteBar, OrderType.Market, 100)]
+ [TestCase(MarketDataType.QuoteBar, OrderType.MarketOnOpen, 90)]
+ [TestCase(MarketDataType.Tick, OrderType.Market, 100)]
+ [TestCase(MarketDataType.Tick, OrderType.MarketOnOpen, 100)]
+ public void SlippageModelsReferenceTheOpenPriceForMarketOnOpenOrders(MarketDataType dataType, OrderType orderType, decimal expectedReferencePrice)
+ {
+ var security = CreateSecurityWithData(dataType);
+ var order = CreateOrder(orderType, security.Symbol);
+
+ Assert.AreEqual(expectedReferencePrice * 0.5m, new ConstantSlippageModel(0.5m).GetSlippageApproximation(security, order));
+
+ if (security.Type == SecurityType.Equity)
+ {
+ Assert.AreEqual(expectedReferencePrice * 0.0001m, new AlphaStreamsSlippageModel().GetSlippageApproximation(security, order));
+ }
+
+ var volumeShareModel = new VolumeShareSlippageModel();
+ if (dataType == MarketDataType.Tick)
+ {
+ Assert.Throws(() => volumeShareModel.GetSlippageApproximation(security, order));
+ }
+ else
+ {
+ // order quantity is 1 and the bar volume is 100, below the volume limit
+ var volumeShare = 1m / 100m;
+ Assert.AreEqual(expectedReferencePrice * volumeShare * volumeShare * 0.1m, volumeShareModel.GetSlippageApproximation(security, order));
+ }
+ }
+
+ [TestCase(MarketDataType.TradeBar, OrderType.Market, 100)]
+ [TestCase(MarketDataType.TradeBar, OrderType.MarketOnOpen, 90)]
+ [TestCase(MarketDataType.QuoteBar, OrderType.Market, 100)]
+ [TestCase(MarketDataType.QuoteBar, OrderType.MarketOnOpen, 90)]
+ public void PythonVolumeShareSlippageModelReferencesTheOpenPriceForMarketOnOpenOrders(MarketDataType dataType, OrderType orderType, decimal expectedReferencePrice)
+ {
+ var security = CreateSecurityWithData(dataType);
+ var order = CreateOrder(orderType, security.Symbol);
+
+ ISlippageModel model;
+ using (Py.GIL())
+ {
+ var module = PyModule.FromString("VolumeShareSlippageModelTest",
+ File.ReadAllText("../../../Common/Orders/Slippage/VolumeShareSlippageModel.py"));
+ model = new SlippageModelPythonWrapper(module.GetAttr("VolumeShareSlippageModel").Invoke());
+ }
+
+ // order quantity is 1 and the bar volume is 100, below the volume limit
+ var volumeShare = 1m / 100m;
+ var expected = expectedReferencePrice * volumeShare * volumeShare * 0.1m;
+ Assert.AreEqual((double)expected, (double)model.GetSlippageApproximation(security, order), 1e-12);
+ }
+
+ private static Order CreateOrder(OrderType orderType, Symbol symbol)
+ {
+ var time = new DateTime(2015, 6, 10, 9, 0, 0);
+ return orderType switch
+ {
+ OrderType.Market => new MarketOrder(symbol, 1, time),
+ OrderType.MarketOnOpen => new MarketOnOpenOrder(symbol, 1, time),
+ OrderType.MarketOnClose => new MarketOnCloseOrder(symbol, 1, time),
+ OrderType.Limit => new LimitOrder(symbol, 1, 100, time),
+ _ => throw new ArgumentOutOfRangeException(nameof(orderType))
+ };
+ }
+
+ ///
+ /// Sets data whose open is 90 and close is 100 as the last data of a security and returns it.
+ /// Quote bars are not the default data type for equities, so forex is used for them
+ ///
+ private Security CreateSecurityWithData(MarketDataType dataType)
+ {
+ var time = new DateTime(2015, 6, 10, 9, 30, 0);
+ BaseData data;
+ switch (dataType)
+ {
+ case MarketDataType.TradeBar:
+ data = new TradeBar(time, Symbols.SPY, 90m, 110m, 80m, 100m, 100);
+ break;
+ case MarketDataType.QuoteBar:
+ data = new QuoteBar(time, Symbols.EURUSD, new Bar(89m, 109m, 79m, 99m), 100, new Bar(91m, 111m, 81m, 101m), 100);
+ break;
+ case MarketDataType.Tick:
+ data = new Tick(time, Symbols.SPY, 100m, 100m) { TickType = TickType.Trade, Quantity = 100 };
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(dataType));
+ }
+
+ Security security = data.Symbol == Symbols.SPY ? _equity : _forex;
+ security.SetMarketPrice(data);
+ return security;
+ }
}
}