diff --git a/Common/Data/Market/FuturesContract.cs b/Common/Data/Market/FuturesContract.cs index 8bf0e11bfbaa..56918c97f530 100644 --- a/Common/Data/Market/FuturesContract.cs +++ b/Common/Data/Market/FuturesContract.cs @@ -80,7 +80,16 @@ public override long Volume { return (long)_universeData.Volume; } - return (long)(_tradeBar?.Volume ?? 0); + + if (_tradeBar == null && _tradeTick == null) + { + return 0L; + } + if (_tradeBar != null) + { + return (long)(_tradeTick != null && _tradeTick.EndTime > _tradeBar.EndTime ? _tradeTick.Quantity : _tradeBar.Volume); + } + return (long)_tradeTick.Quantity; } } @@ -101,7 +110,12 @@ public override decimal BidPrice } if (_quoteBar != null) { - return _quoteTick != null && _quoteTick.EndTime > _quoteBar.EndTime ? _quoteTick.BidPrice : _quoteBar.Bid.Close; + var quoteBarPrice = _quoteBar.Bid?.Close ?? decimal.Zero; + if (_quoteTick != null) + { + return _quoteTick.EndTime > _quoteBar.EndTime ? _quoteTick.BidPrice : quoteBarPrice; + } + return quoteBarPrice; } return _quoteTick.BidPrice; } @@ -143,7 +157,12 @@ public override decimal AskPrice } if (_quoteBar != null) { - return _quoteTick != null && _quoteTick.EndTime > _quoteBar.EndTime ? _quoteTick.AskPrice : _quoteBar.Ask.Close; + var quoteBarPrice = _quoteBar.Ask?.Close ?? decimal.Zero; + if (_quoteTick != null) + { + return _quoteTick.EndTime > _quoteBar.EndTime ? _quoteTick.AskPrice : quoteBarPrice; + } + return quoteBarPrice; } return _quoteTick.AskPrice; } diff --git a/Tests/Common/Data/Market/FuturesContractTests.cs b/Tests/Common/Data/Market/FuturesContractTests.cs new file mode 100644 index 000000000000..6a59e71b3bd5 --- /dev/null +++ b/Tests/Common/Data/Market/FuturesContractTests.cs @@ -0,0 +1,109 @@ +/* + * 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 NUnit.Framework; +using QuantConnect.Data.Market; + +namespace QuantConnect.Tests.Common.Data.Market +{ + [TestFixture] + public class FuturesContractTests + { + [TestCase(true, true)] + [TestCase(true, false)] + [TestCase(false, true)] + [TestCase(false, false)] + public void QuoteBarNullBidAsk(bool hasBid, bool hasAsk) + { + var futureContract = new FuturesContract(Symbols.Future_CLF19_Jan2019); + + Bar bid = hasBid ? new Bar(1, 1, 1, 1) : null; + Bar ask = hasAsk ? new Bar(2, 2, 2, 2) : null; + var quoteBar = new QuoteBar(new DateTime(2025, 12, 10), Symbols.Future_CLF19_Jan2019, bid, 10, ask, 20); + futureContract.Update(quoteBar); + Assert.AreEqual(hasBid ? bid.Close : 0, futureContract.BidPrice); + Assert.AreEqual(hasAsk ? ask.Close : 0, futureContract.AskPrice); + Assert.AreEqual(hasAsk ? 20 : 0, futureContract.AskSize); + Assert.AreEqual(hasBid ? 10 : 0, futureContract.BidSize); + Assert.AreEqual(0, futureContract.Volume); + Assert.AreEqual(0, futureContract.LastPrice); + Assert.AreEqual(0, futureContract.OpenInterest); + } + + [Test] + public void QuoteTickUpdate() + { + var futureContract = new FuturesContract(Symbols.Future_CLF19_Jan2019); + + var tick = new Tick(new DateTime(2025, 12, 10), Symbols.Future_CLF19_Jan2019, 1, 2, 3, 4); + futureContract.Update(tick); + Assert.AreEqual(1, futureContract.BidSize); + Assert.AreEqual(2, futureContract.BidPrice); + Assert.AreEqual(3, futureContract.AskSize); + Assert.AreEqual(4, futureContract.AskPrice); + Assert.AreEqual(0, futureContract.Volume); + Assert.AreEqual(0, futureContract.LastPrice); + Assert.AreEqual(0, futureContract.OpenInterest); + } + + [Test] + public void TradeTickUpdate() + { + var futureContract = new FuturesContract(Symbols.Future_CLF19_Jan2019); + + var tick = new Tick(new DateTime(2025, 12, 10), Symbols.Future_CLF19_Jan2019, string.Empty, Exchange.UNKNOWN, 1, 2); + futureContract.Update(tick); + Assert.AreEqual(1, futureContract.Volume); + Assert.AreEqual(2, futureContract.LastPrice); + Assert.AreEqual(0, futureContract.BidSize); + Assert.AreEqual(0, futureContract.BidPrice); + Assert.AreEqual(0, futureContract.AskSize); + Assert.AreEqual(0, futureContract.AskPrice); + Assert.AreEqual(0, futureContract.OpenInterest); + } + + [Test] + public void TradeBarUpdate() + { + var futureContract = new FuturesContract(Symbols.Future_CLF19_Jan2019); + + var tick = new TradeBar(new DateTime(2025, 12, 10), Symbols.Future_CLF19_Jan2019, 1, 2, 3, 4, 5); + futureContract.Update(tick); + Assert.AreEqual(5, futureContract.Volume); + Assert.AreEqual(4, futureContract.LastPrice); + Assert.AreEqual(0, futureContract.BidSize); + Assert.AreEqual(0, futureContract.BidPrice); + Assert.AreEqual(0, futureContract.AskSize); + Assert.AreEqual(0, futureContract.AskPrice); + Assert.AreEqual(0, futureContract.OpenInterest); + } + + [Test] + public void OpenInterest() + { + var futureContract = new FuturesContract(Symbols.Future_CLF19_Jan2019); + var tick = new OpenInterest(new DateTime(2025, 12, 10), Symbols.Future_CLF19_Jan2019, 10); + futureContract.Update(tick); + Assert.AreEqual(10, futureContract.OpenInterest); + Assert.AreEqual(0, futureContract.Volume); + Assert.AreEqual(0, futureContract.LastPrice); + Assert.AreEqual(0, futureContract.BidSize); + Assert.AreEqual(0, futureContract.BidPrice); + Assert.AreEqual(0, futureContract.AskSize); + Assert.AreEqual(0, futureContract.AskPrice); + } + } +}