chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,755 @@
|
||||
/*
|
||||
* 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 System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NUnit.Framework;
|
||||
using Python.Runtime;
|
||||
using QuantConnect.Algorithm.CSharp;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Fundamental;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Python;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Tests.Common.Data.Fundamental;
|
||||
using QuantConnect.Util;
|
||||
|
||||
namespace QuantConnect.Tests.Common.Securities
|
||||
{
|
||||
[TestFixture, Parallelizable(ParallelScope.All)]
|
||||
public class SecurityCacheTests
|
||||
{
|
||||
private static readonly DateTime ReferenceTime = new DateTime(2000, 01, 01);
|
||||
|
||||
private readonly Random _rng = new Random(Seed: 123);
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
FundamentalService.Initialize(TestGlobals.DataProvider, new NullFundamentalDataProvider(), false);
|
||||
}
|
||||
|
||||
[TestCase(MarketDataType.TradeBar, 10, true)]
|
||||
[TestCase(MarketDataType.TradeBar, 10, false)]
|
||||
[TestCase(MarketDataType.QuoteBar, 10, true)]
|
||||
[TestCase(MarketDataType.QuoteBar, 10, false)]
|
||||
[TestCase(MarketDataType.Tick, 10, true)]
|
||||
[TestCase(MarketDataType.Tick, 10, false)]
|
||||
public void AlwaysReturnTheLastData(MarketDataType marketDataType, int quantity, bool sameTime)
|
||||
{
|
||||
// Arrange
|
||||
var securityCache = new SecurityCache();
|
||||
var quotes = GenerateData(marketDataType, quantity, sameTime);
|
||||
// Act
|
||||
foreach (var quoteBar in quotes)
|
||||
{
|
||||
quoteBar.Symbol = Symbols.SPY;
|
||||
securityCache.AddData(quoteBar);
|
||||
}
|
||||
|
||||
var lastData = securityCache.GetData();
|
||||
if (marketDataType == MarketDataType.QuoteBar)
|
||||
{
|
||||
Assert.IsNull(lastData);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.True(lastData.Equals(quotes.Last()));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GivenSameTimeStampForTradeBarAndQuoteQuotebarPrioritizeQuoteBar()
|
||||
{
|
||||
// Arrange
|
||||
var securityCache = new SecurityCache();
|
||||
var time = DateTime.Now;
|
||||
var quotes = GenerateData(MarketDataType.QuoteBar, 5, false, time);
|
||||
var trades = GenerateData(MarketDataType.TradeBar, 5, false, time);
|
||||
var data = quotes.Concat(trades);
|
||||
data = data.OrderBy(d => d.EndTime);
|
||||
// Act
|
||||
foreach (var baseData in data)
|
||||
{
|
||||
securityCache.AddData(baseData);
|
||||
}
|
||||
// Assert
|
||||
Assert.True(securityCache.GetData().Equals(quotes.Last()));
|
||||
Assert.True(securityCache.GetData<TradeBar>().Equals(trades.Last()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UseLatestTradebarIfThereIsntAvailableQuotebar()
|
||||
{
|
||||
// Arrange
|
||||
var securityCache = new SecurityCache();
|
||||
var time = DateTime.Now;
|
||||
var quotes = GenerateData(MarketDataType.QuoteBar, 5, false, time);
|
||||
foreach (var baseData in quotes)
|
||||
{
|
||||
securityCache.AddData(baseData);
|
||||
}
|
||||
// Add one last tradebar with a later timestamp
|
||||
var laterTrade = GenerateData(MarketDataType.TradeBar, 1, true, quotes.Last().Time.AddSeconds(1)).First();
|
||||
// Act
|
||||
securityCache.AddData(laterTrade);
|
||||
// Assert
|
||||
Assert.True(securityCache.GetData().Equals(laterTrade));
|
||||
Assert.True(securityCache.GetData<QuoteBar>().Equals(quotes.Last()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddData_SecurityCacheHasTradeAndQuoteTick()
|
||||
{
|
||||
// Arrange
|
||||
var securityCache = new SecurityCache();
|
||||
var time = DateTime.Now;
|
||||
|
||||
var quote = new Tick(time, Symbol.Empty, 100, 102) { TickType = TickType.Quote };
|
||||
securityCache.AddData(quote);
|
||||
|
||||
var trade = new Tick(time, Symbol.Empty, 100, 100, 102) {TickType = TickType.Trade};
|
||||
securityCache.AddData(trade);
|
||||
|
||||
var openInterest = new OpenInterest(time, Symbol.Empty, 1000);
|
||||
securityCache.AddData(openInterest);
|
||||
|
||||
// Assert
|
||||
Assert.True(securityCache.GetData().Equals(trade));
|
||||
Assert.True(securityCache.GetData<Tick>().Equals(trade));
|
||||
|
||||
Assert.True(securityCache.GetAll<Tick>().LastOrDefault(x => x.TickType == TickType.Quote).Equals(quote));
|
||||
Assert.True(securityCache.GetAll<Tick>().LastOrDefault(x => x.TickType == TickType.Trade).Equals(trade));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StoreData_SecurityCacheHasTradeAndQuoteTick()
|
||||
{
|
||||
// Arrange
|
||||
var securityCache = new SecurityCache();
|
||||
var time = DateTime.Now;
|
||||
|
||||
var quote = new Tick(time, Symbol.Empty, 100, 102) { TickType = TickType.Quote };
|
||||
securityCache.StoreData(new[] {quote}, typeof(Tick));
|
||||
|
||||
var trade = new Tick(time.AddMilliseconds(1), Symbol.Empty, 100, 100, 102) { TickType = TickType.Trade };
|
||||
securityCache.StoreData(new[] { trade }, typeof(Tick));
|
||||
|
||||
// Adding OpenInterest as Tick or OpenInterest should not matter
|
||||
var openInterest = new OpenInterest(time, Symbol.Empty, 1000);
|
||||
securityCache.StoreData(new[] { openInterest }, typeof(Tick)); // Add as Tick
|
||||
securityCache.StoreData(new[] { openInterest }, typeof(OpenInterest)); // Add as OI
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(securityCache.HasData(typeof(Tick)));
|
||||
Assert.True(securityCache.GetData<Tick>().Equals(trade));
|
||||
Assert.True(securityCache.GetData<OpenInterest>().Equals(openInterest));
|
||||
|
||||
Assert.True(securityCache.GetAll<Tick>().LastOrDefault(x => x.TickType == TickType.Quote).Equals(quote));
|
||||
Assert.True(securityCache.GetAll<Tick>().LastOrDefault(x => x.TickType == TickType.Trade).Equals(trade));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StoreData_TargetToModify_SecurityCacheHasTradeAndQuoteTick()
|
||||
{
|
||||
// Arrange
|
||||
var sourceToShare = new SecurityCache();
|
||||
var time = DateTime.Now;
|
||||
|
||||
var quote = new Tick(time, Symbol.Empty, 100, 102) { TickType = TickType.Quote };
|
||||
sourceToShare.StoreData(new[] { quote }, typeof(Tick));
|
||||
|
||||
var trade = new Tick(time, Symbol.Empty, 100, 100, 102) { TickType = TickType.Trade };
|
||||
sourceToShare.StoreData(new[] { trade }, typeof(Tick));
|
||||
|
||||
// Adding OpenInterest as Tick or OpenInterest should not matter
|
||||
var openInterest = new OpenInterest(time, Symbol.Empty, 1000);
|
||||
sourceToShare.StoreData(new[] { openInterest }, typeof(Tick)); // Add as Tick
|
||||
sourceToShare.StoreData(new[] { openInterest }, typeof(OpenInterest)); // Add as OI
|
||||
|
||||
var targetToModify = new SecurityCache();
|
||||
SecurityCache.ShareTypeCacheInstance(sourceToShare, targetToModify);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(targetToModify.HasData(typeof(Tick)));
|
||||
Assert.True(targetToModify.GetData<Tick>().Equals(trade));
|
||||
Assert.True(targetToModify.GetData<OpenInterest>().Equals(openInterest));
|
||||
|
||||
Assert.True(targetToModify.GetAll<Tick>().LastOrDefault(x => x.TickType == TickType.Quote).Equals(quote));
|
||||
Assert.True(targetToModify.GetAll<Tick>().LastOrDefault(x => x.TickType == TickType.Trade).Equals(trade));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(GetSecurityCacheInitialStates))]
|
||||
public void ResetTests(SecurityCache cache, SecuritySeedData seedType)
|
||||
{
|
||||
switch (seedType)
|
||||
{
|
||||
case SecuritySeedData.None:
|
||||
case SecuritySeedData.OpenInterest:
|
||||
case SecuritySeedData.OpenInterestTick:
|
||||
break;
|
||||
case SecuritySeedData.QuoteTick:
|
||||
Assert.IsNotNull(cache.GetData());
|
||||
Assert.Greater(cache.GetAll<Tick>().Count(x => x.TickType == TickType.Quote), 0);
|
||||
cache.Reset();
|
||||
Assert.IsFalse(cache.HasData(typeof(Tick)));
|
||||
Assert.AreEqual(cache.GetAll<Tick>().Count(x => x.TickType == TickType.Quote), 0);
|
||||
break;
|
||||
case SecuritySeedData.TradeTick:
|
||||
Assert.IsNotNull(cache.GetData());
|
||||
Assert.Greater(cache.GetAll<Tick>().Count(x => x.TickType == TickType.Trade), 0);
|
||||
cache.Reset();
|
||||
Assert.IsFalse(cache.HasData(typeof(Tick)));
|
||||
Assert.AreEqual(cache.GetAll<Tick>().Count(x => x.TickType == TickType.Trade), 0);
|
||||
break;
|
||||
case SecuritySeedData.Fundamentals:
|
||||
Assert.IsNotNull(cache.GetData());
|
||||
Assert.IsNotNull(cache.GetData<Fundamental>());
|
||||
cache.Reset();
|
||||
Assert.IsFalse(cache.HasData(typeof(Fundamental)));
|
||||
break;
|
||||
default:
|
||||
Assert.IsNotNull(cache.GetData());
|
||||
cache.Reset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(GetSecurityCacheInitialStates))]
|
||||
public void AddDataWithSameEndTime_SetsOpenInterestValues(SecurityCache cache, SecuritySeedData seedType)
|
||||
{
|
||||
var map = new Dictionary<string, string> {{"OpenInterest", "Value"}};
|
||||
AddDataAndAssertChanges(cache, seedType, SecuritySeedData.OpenInterest, new OpenInterest
|
||||
{
|
||||
Value = 101,
|
||||
EndTime = ReferenceTime
|
||||
}, map);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(GetSecurityCacheInitialStates))]
|
||||
public void AddDataWithSameEndTime_SetsOpenInterestTickValues(SecurityCache cache, SecuritySeedData seedType)
|
||||
{
|
||||
var map = new Dictionary<string, string> { { "OpenInterest", "Value" } };
|
||||
AddDataAndAssertChanges(cache, seedType, SecuritySeedData.OpenInterestTick, new Tick
|
||||
{
|
||||
Value = 101,
|
||||
TickType = TickType.OpenInterest,
|
||||
EndTime = ReferenceTime
|
||||
}, map);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(GetSecurityCacheInitialStates))]
|
||||
public void AddDataWithSameEndTime_SetsQuoteTickValues(SecurityCache cache, SecuritySeedData seedType)
|
||||
{
|
||||
AddDataAndAssertChanges(cache, seedType, SecuritySeedData.QuoteTick, new Tick
|
||||
{
|
||||
AskPrice = 101,
|
||||
AskSize = 102,
|
||||
BidPrice = 103,
|
||||
BidSize = 104,
|
||||
TickType = TickType.Quote,
|
||||
EndTime = ReferenceTime
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(GetSecurityCacheInitialStates))]
|
||||
public void AddDataWithSameEndTime_SetsTradeTickValues(SecurityCache cache, SecuritySeedData seedType)
|
||||
{
|
||||
var map = new Dictionary<string, string> {{"Volume", "Quantity"}};
|
||||
AddDataAndAssertChanges(cache, seedType, SecuritySeedData.TradeTick, new Tick
|
||||
{
|
||||
Value = 101,
|
||||
Quantity = 102,
|
||||
TickType = TickType.Trade,
|
||||
EndTime = ReferenceTime
|
||||
}, map);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(GetSecurityCacheInitialStates))]
|
||||
public void AddDataWithSameEndTime_SetsTradeBarValues(SecurityCache cache, SecuritySeedData seedType)
|
||||
{
|
||||
AddDataAndAssertChanges(cache, seedType, SecuritySeedData.TradeBar, new TradeBar
|
||||
{
|
||||
Open = 101,
|
||||
High = 102,
|
||||
Low = 103,
|
||||
Close = 104,
|
||||
Volume = 105,
|
||||
EndTime = ReferenceTime,
|
||||
Symbol = Symbols.SPY
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddDataEquity_OHLC_IgnoresQuoteBar()
|
||||
{
|
||||
var securityCache = new SecurityCache();
|
||||
var quoteBar = new QuoteBar
|
||||
{
|
||||
Bid = new Bar(101, 102, 103, 104),
|
||||
Ask = new Bar(105, 106, 107, 108),
|
||||
LastAskSize = 109,
|
||||
LastBidSize = 110,
|
||||
EndTime = ReferenceTime,
|
||||
Symbol = Symbols.SPY
|
||||
};
|
||||
securityCache.AddData(quoteBar);
|
||||
|
||||
var last = securityCache.GetData();
|
||||
Assert.IsNull(last);
|
||||
|
||||
Assert.AreEqual(0, securityCache.High);
|
||||
Assert.AreEqual(0, securityCache.Close);
|
||||
Assert.AreEqual(0, securityCache.Low);
|
||||
Assert.AreEqual(0, securityCache.Open);
|
||||
Assert.AreEqual(0, securityCache.Volume);
|
||||
|
||||
var actualQuoteBar = securityCache.GetData<QuoteBar>();
|
||||
Assert.IsNotNull(actualQuoteBar);
|
||||
Assert.AreEqual(108, securityCache.AskPrice);
|
||||
Assert.AreEqual(109, securityCache.AskSize);
|
||||
Assert.AreEqual(104, securityCache.BidPrice);
|
||||
Assert.AreEqual(110, securityCache.BidSize);
|
||||
|
||||
var tradeBar = new TradeBar
|
||||
{
|
||||
Open = 101,
|
||||
High = 102,
|
||||
Low = 103,
|
||||
Close = 104,
|
||||
Volume = 105,
|
||||
EndTime = ReferenceTime,
|
||||
Symbol = Symbols.SPY
|
||||
};
|
||||
securityCache.AddData(tradeBar);
|
||||
|
||||
last = securityCache.GetData();
|
||||
Assert.IsNotNull(last);
|
||||
|
||||
var actualTradeBar = securityCache.GetData<TradeBar>();
|
||||
Assert.IsNotNull(actualTradeBar);
|
||||
Assert.AreEqual(102, securityCache.High);
|
||||
Assert.AreEqual(104, securityCache.Close);
|
||||
Assert.AreEqual(103, securityCache.Low);
|
||||
Assert.AreEqual(101, securityCache.Open);
|
||||
Assert.AreEqual(105, securityCache.Volume);
|
||||
|
||||
// quote bar data should still be the same
|
||||
Assert.AreEqual(108, securityCache.AskPrice);
|
||||
Assert.AreEqual(109, securityCache.AskSize);
|
||||
Assert.AreEqual(104, securityCache.BidPrice);
|
||||
Assert.AreEqual(110, securityCache.BidSize);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(GetSecurityCacheInitialStates))]
|
||||
public void AddDataWithSameEndTime_SetsQuoteBarValues(SecurityCache cache, SecuritySeedData seedType)
|
||||
{
|
||||
var map = new Dictionary<string, string>
|
||||
{
|
||||
{"Price", "Close"},
|
||||
{"BidPrice", "Bid.Close"},
|
||||
{"BidSize", "LastBidSize"},
|
||||
{"AskPrice", "Ask.Close"},
|
||||
{"AskSize", "LastAskSize"}
|
||||
};
|
||||
AddDataAndAssertChanges(cache, seedType, SecuritySeedData.QuoteBar, new QuoteBar
|
||||
{
|
||||
Bid = new Bar(101, 102, 103, 104),
|
||||
Ask = new Bar(105, 106, 107, 108),
|
||||
LastAskSize = 109,
|
||||
LastBidSize = 110,
|
||||
EndTime = ReferenceTime
|
||||
}, map);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(GetSecurityCacheInitialStates))]
|
||||
public void AddDataFundamentals_DoesNotChangeCacheValues(SecurityCache cache, SecuritySeedData seedType)
|
||||
{
|
||||
var map = new Dictionary<string, string>();
|
||||
AddDataAndAssertChanges(cache, seedType, SecuritySeedData.Fundamentals, new Fundamental(ReferenceTime, Symbols.AAPL)
|
||||
{
|
||||
Value = 111,
|
||||
EndTime = ReferenceTime
|
||||
}, map);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TickTypeDependencyTests()
|
||||
{
|
||||
// Arrange
|
||||
var time = DateTime.Now;
|
||||
var price = 100m;
|
||||
var bidPrice = 99m;
|
||||
var askPrice = 101m;
|
||||
var volume = 1m;
|
||||
|
||||
var tick = new Tick(time, Symbols.AAPL, price, bidPrice, askPrice) { Quantity = volume }; ;
|
||||
|
||||
var securityCache = new SecurityCache();
|
||||
securityCache.AddData(tick);
|
||||
Assert.AreEqual(securityCache.Price, price);
|
||||
Assert.AreEqual(securityCache.BidPrice, bidPrice);
|
||||
Assert.AreEqual(securityCache.AskPrice, askPrice);
|
||||
Assert.AreEqual(securityCache.Volume, 0m);
|
||||
|
||||
tick.TickType = TickType.Trade;
|
||||
securityCache = new SecurityCache();
|
||||
securityCache.AddData(tick);
|
||||
Assert.AreEqual(securityCache.Price, price);
|
||||
Assert.AreEqual(securityCache.BidPrice, 0m);
|
||||
Assert.AreEqual(securityCache.AskPrice, 0m);
|
||||
Assert.AreEqual(securityCache.Volume, volume);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetAllData_ReturnsListOfData()
|
||||
{
|
||||
var cache = new SecurityCache();
|
||||
cache.StoreData(new []
|
||||
{
|
||||
new CustomDataBitcoinAlgorithm.Bitcoin{Ask = 1m},
|
||||
new CustomDataBitcoinAlgorithm.Bitcoin{Ask = 2m}
|
||||
}, typeof(CustomDataBitcoinAlgorithm.Bitcoin));
|
||||
|
||||
var data = cache.GetAll<CustomDataBitcoinAlgorithm.Bitcoin>().ToList();
|
||||
Assert.AreEqual(2, data.Count);
|
||||
Assert.AreEqual(1m, data[0].Ask);
|
||||
Assert.AreEqual(2m, data[1].Ask);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetAllData_ReturnsListOfDataOnTargetCache()
|
||||
{
|
||||
var cache = new SecurityCache();
|
||||
cache.StoreData(new[]
|
||||
{
|
||||
new CustomDataBitcoinAlgorithm.Bitcoin{Ask = 1m},
|
||||
new CustomDataBitcoinAlgorithm.Bitcoin{Ask = 2m}
|
||||
}, typeof(CustomDataBitcoinAlgorithm.Bitcoin));
|
||||
|
||||
var targetToModify = new SecurityCache();
|
||||
SecurityCache.ShareTypeCacheInstance(cache, targetToModify);
|
||||
|
||||
var data = targetToModify.GetAll<CustomDataBitcoinAlgorithm.Bitcoin>().ToList();
|
||||
Assert.AreEqual(2, data.Count);
|
||||
Assert.AreEqual(1m, data[0].Ask);
|
||||
Assert.AreEqual(2m, data[1].Ask);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SecurityCacheGetDataWorksCorrectly()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var testModule = PyModule.FromString("TestSecurityCacheGetData",
|
||||
@"
|
||||
from AlgorithmImports import *
|
||||
from QuantConnect.Tests import *
|
||||
|
||||
class MyCustomDataType(PythonData):
|
||||
def get_source(self, config: SubscriptionDataConfig, date: datetime, is_live: bool) -> SubscriptionDataSource:
|
||||
fileName = LeanData.GenerateZipFileName(Symbols.SPY, date, Resolution.MINUTE, config.TickType)
|
||||
|
||||
def reader(self, config: SubscriptionDataConfig, line: str, date: datetime, is_live: bool) -> BaseData:
|
||||
data = line.split(',')
|
||||
result = MyCustomDataType()
|
||||
|
||||
def get_security_cache_quote():
|
||||
securityCache = SecurityCache()
|
||||
quoteBar = QuoteBar()
|
||||
securityCache.add_data(quoteBar)
|
||||
return securityCache.get_data(QuoteBar)
|
||||
|
||||
def get_security_cache_trade():
|
||||
securityCache = SecurityCache()
|
||||
tradeBar = TradeBar()
|
||||
securityCache.add_data(tradeBar)
|
||||
return securityCache.get_data(TradeBar)
|
||||
|
||||
def get_security_cache_margin_interest_rate():
|
||||
securityCache = SecurityCache()
|
||||
marginInterestRate = MarginInterestRate()
|
||||
securityCache.add_data(marginInterestRate)
|
||||
return securityCache.get_data(MarginInterestRate)
|
||||
|
||||
def get_security_cache_custom_data_type():
|
||||
securityCache = SecurityCache()
|
||||
customData = PythonData(MyCustomDataType())
|
||||
securityCache.add_data(customData)
|
||||
return securityCache.get_data(MyCustomDataType)
|
||||
");
|
||||
|
||||
// Test if the GetData method correctly handles QuoteBar type
|
||||
var quoteType = testModule.GetAttr("get_security_cache_quote").Invoke();
|
||||
Assert.IsFalse(quoteType.IsNone());
|
||||
Assert.DoesNotThrow(() => quoteType.As<QuoteBar>());
|
||||
|
||||
// Test if the GetData method correctly handles TradeBar type
|
||||
var tradeType = testModule.GetAttr("get_security_cache_trade").Invoke();
|
||||
Assert.IsFalse(tradeType.IsNone());
|
||||
Assert.DoesNotThrow(() => tradeType.As<TradeBar>());
|
||||
|
||||
// Test if the GetData method correctly handles MarginInterestRate type
|
||||
var marginInterestType = testModule.GetAttr("get_security_cache_margin_interest_rate").Invoke();
|
||||
Assert.IsFalse(marginInterestType.IsNone());
|
||||
Assert.DoesNotThrow(() => marginInterestType.As<MarginInterestRate>());
|
||||
|
||||
// Test if the GetData method correctly handles custom data type (PythonData)
|
||||
var customDataType = testModule.GetAttr("get_security_cache_custom_data_type").Invoke();
|
||||
Assert.IsFalse(customDataType.IsNone());
|
||||
Assert.DoesNotThrow(() => customDataType.As<PythonData>());
|
||||
}
|
||||
}
|
||||
|
||||
private void AddDataAndAssertChanges(SecurityCache cache, SecuritySeedData seedType, SecuritySeedData dataType, BaseData data, Dictionary<string, string> cacheToBaseDataPropertyMap = null)
|
||||
{
|
||||
var before = JObject.FromObject(cache);
|
||||
var dataSnapshot = JObject.FromObject(data);
|
||||
|
||||
cache.AddData(data);
|
||||
var after = JObject.FromObject(cache);
|
||||
|
||||
var updatedCacheProperties = GetPropertiesBy(dataType);
|
||||
if (seedType == SecuritySeedData.QuoteBar && (dataType == SecuritySeedData.QuoteBar || dataType == SecuritySeedData.TradeBar))
|
||||
{
|
||||
// these properties aren't updated when previous data is quote bar at same time as a new IBar
|
||||
updatedCacheProperties = updatedCacheProperties.Where(p =>
|
||||
p != "Open" &&
|
||||
p != "High" &&
|
||||
p != "Low" &&
|
||||
p != "Close" &&
|
||||
p != "Price"
|
||||
).ToArray();
|
||||
}
|
||||
|
||||
foreach (var property in before.Properties())
|
||||
{
|
||||
string dataPropertyName = null;
|
||||
if (updatedCacheProperties.Contains(property.Name))
|
||||
{
|
||||
if (cacheToBaseDataPropertyMap?.TryGetValue(property.Name, out dataPropertyName) == true)
|
||||
{
|
||||
// avoiding failures due to decimal <> long in JToken.DeepEquals
|
||||
var e = dataSnapshot.SelectToken(dataPropertyName).ToString();
|
||||
var a = after.SelectToken(property.Name).ToString();
|
||||
Assert.AreEqual(e, a, $"{property.Name}: Expected {e}. Actual {a}");
|
||||
}
|
||||
else
|
||||
{
|
||||
dataSnapshot.IsEqualTo(after, property.Name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
before.IsEqualTo(after, property.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum SecuritySeedData
|
||||
{
|
||||
None,
|
||||
TradeTick,
|
||||
QuoteTick,
|
||||
OpenInterestTick,
|
||||
OpenInterest,
|
||||
TradeBar,
|
||||
QuoteBar,
|
||||
Fundamentals
|
||||
}
|
||||
|
||||
public string[] GetPropertiesBy(SecuritySeedData type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case SecuritySeedData.None:
|
||||
return new string[0];
|
||||
|
||||
case SecuritySeedData.OpenInterest:
|
||||
return new[] { "OpenInterest" };
|
||||
|
||||
case SecuritySeedData.OpenInterestTick:
|
||||
return new[] { "OpenInterest" };
|
||||
|
||||
case SecuritySeedData.TradeTick:
|
||||
return new[] {"Price", "Volume"};
|
||||
|
||||
case SecuritySeedData.QuoteTick:
|
||||
return new[] {"AskPrice", "AskSize", "BidPrice", "BidSize"};
|
||||
|
||||
case SecuritySeedData.TradeBar:
|
||||
return new[] {"Price", "Volume", "Open", "High", "Low", "Close"};
|
||||
|
||||
case SecuritySeedData.QuoteBar:
|
||||
return new[] { "Price", "Open", "High", "Low", "Close", "AskPrice", "AskSize", "BidPrice", "BidSize" };
|
||||
|
||||
case SecuritySeedData.Fundamentals:
|
||||
// fundamentals data does not modify security cache properties
|
||||
return new string[0];
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(type), type, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static TestCaseData[] GetSecurityCacheInitialStates()
|
||||
{
|
||||
var defaultInstance = new SecurityCache();
|
||||
|
||||
var tradeTick = new SecurityCache();
|
||||
tradeTick.AddData(new Tick
|
||||
{
|
||||
Value = 5,
|
||||
Quantity = 6,
|
||||
EndTime = ReferenceTime,
|
||||
TickType = TickType.Trade
|
||||
});
|
||||
|
||||
var quoteTick = new SecurityCache();
|
||||
quoteTick.AddData(new Tick
|
||||
{
|
||||
AskPrice = 1,
|
||||
AskSize = 2,
|
||||
BidPrice = 3,
|
||||
BidSize = 4,
|
||||
EndTime = ReferenceTime,
|
||||
TickType = TickType.Quote
|
||||
});
|
||||
|
||||
var openInterestTick = new SecurityCache();
|
||||
openInterestTick.AddData(new Tick
|
||||
{
|
||||
Value = 24,
|
||||
EndTime = ReferenceTime,
|
||||
TickType = TickType.OpenInterest
|
||||
});
|
||||
|
||||
var openInterest = new SecurityCache();
|
||||
openInterest.AddData(new OpenInterest
|
||||
{
|
||||
Value = 23,
|
||||
EndTime = ReferenceTime,
|
||||
});
|
||||
|
||||
var tradeBar = new SecurityCache();
|
||||
tradeBar.AddData(new TradeBar
|
||||
{
|
||||
Open = 7,
|
||||
High = 8,
|
||||
Low = 9,
|
||||
Close = 10,
|
||||
Volume = 11,
|
||||
EndTime = ReferenceTime
|
||||
});
|
||||
|
||||
var quoteBar = new SecurityCache();
|
||||
quoteBar.AddData(new QuoteBar
|
||||
{
|
||||
Ask = new Bar(12, 13, 14, 15),
|
||||
Bid = new Bar(16, 17, 18, 19),
|
||||
LastAskSize = 20,
|
||||
LastBidSize = 21,
|
||||
Value = 22,
|
||||
EndTime = ReferenceTime
|
||||
});
|
||||
|
||||
var fundamentals = new SecurityCache();
|
||||
fundamentals.AddData(new Fundamental(ReferenceTime, Symbols.AAPL)
|
||||
{
|
||||
Value = 23
|
||||
});
|
||||
|
||||
return new[]
|
||||
{
|
||||
new TestCaseData(defaultInstance, SecuritySeedData.None).SetName("Default Instance"),
|
||||
new TestCaseData(tradeTick, SecuritySeedData.TradeTick).SetName("Seeded w/ Trade Tick"),
|
||||
new TestCaseData(quoteTick, SecuritySeedData.QuoteTick).SetName("Seeded w/ Quote Tick"),
|
||||
new TestCaseData(openInterestTick, SecuritySeedData.OpenInterestTick).SetName("Seeded w/ OpenInterest Tick"),
|
||||
new TestCaseData(openInterest, SecuritySeedData.OpenInterest).SetName("Seeded w/ OpenInterest"),
|
||||
new TestCaseData(tradeBar, SecuritySeedData.TradeBar).SetName("Seeded w/ TradeBar"),
|
||||
new TestCaseData(quoteBar, SecuritySeedData.QuoteBar).SetName("Seeded w/ QuoteBar"),
|
||||
new TestCaseData(fundamentals, SecuritySeedData.Fundamentals).SetName("Seeded w/ Fundamentals")
|
||||
};
|
||||
}
|
||||
|
||||
private IReadOnlyCollection<BaseData> GenerateData(MarketDataType type, int quantity, bool sameTime, DateTime? firstTimeStamp = null)
|
||||
{
|
||||
var time = firstTimeStamp ?? DateTime.Now;
|
||||
var outputTradeBars = new List<BaseData>();
|
||||
for (var i = 0; i < quantity; i++)
|
||||
{
|
||||
var rnd = _rng.Next(minValue: 50, maxValue: 150);
|
||||
var ask = new Bar { Close = 1m * rnd, High = 1.2m * rnd, Low = 0.9m * rnd, Open = 1.1m * rnd };
|
||||
var bid = new Bar { Close = 0.9m * rnd, High = 1.1m * rnd, Low = 0.8m * rnd, Open = 1m * rnd };
|
||||
BaseData data = new TradeBar();
|
||||
switch (type)
|
||||
{
|
||||
case MarketDataType.TradeBar:
|
||||
data = new TradeBar
|
||||
{
|
||||
Close = (ask.Close + bid.Close) / 2,
|
||||
Open = (ask.Open + bid.Open) / 2,
|
||||
High = (ask.High + bid.High) / 2,
|
||||
Low = (ask.Low + bid.Low) / 2,
|
||||
Volume = 1,
|
||||
DataType = type
|
||||
};
|
||||
break;
|
||||
case MarketDataType.Tick:
|
||||
data = new Tick
|
||||
{
|
||||
AskPrice = ask.Close,
|
||||
BidPrice = bid.Close,
|
||||
AskSize = 1,
|
||||
BidSize = 1
|
||||
};
|
||||
break;
|
||||
case MarketDataType.QuoteBar:
|
||||
data = new QuoteBar
|
||||
{
|
||||
Ask = ask,
|
||||
Bid = bid,
|
||||
LastAskSize = 1,
|
||||
LastBidSize = 1,
|
||||
DataType = type,
|
||||
Value = (ask.Close + bid.Close) / 2
|
||||
};
|
||||
break;
|
||||
case MarketDataType.Auxiliary:
|
||||
case MarketDataType.OptionChain:
|
||||
case MarketDataType.FuturesChain:
|
||||
case MarketDataType.Base:
|
||||
throw new NotImplementedException("Cases not tested yet");
|
||||
}
|
||||
data.Time = time;
|
||||
data.EndTime = time.AddSeconds(value: 1);
|
||||
time = sameTime ? time : time.AddSeconds(value: 1);
|
||||
outputTradeBars.Add(data);
|
||||
}
|
||||
return outputTradeBars;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user