chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:02:50 +08:00
commit 0fc60fdcb1
5008 changed files with 910633 additions and 0 deletions
@@ -0,0 +1,66 @@
/*
* 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 NUnit.Framework;
using Python.Runtime;
using QuantConnect.Algorithm;
using QuantConnect.Algorithm.Framework.Alphas;
using QuantConnect.Algorithm.Framework.Selection;
namespace QuantConnect.Tests.Algorithm.Framework.Alphas
{
[TestFixture]
public class BasePairsTradingAlphaModelTests : CommonAlphaModelTests
{
private const int _lookback = 15;
private const Resolution _resolution = Resolution.Minute;
protected override int MaxSliceCount => 1500;
protected override IAlphaModel CreateCSharpAlphaModel()
{
return new BasePairsTradingAlphaModel(_lookback, _resolution);
}
protected override void InitializeAlgorithm(QCAlgorithm algorithm)
{
algorithm.SetUniverseSelection(new ManualUniverseSelectionModel(
Symbol.Create("AIG", SecurityType.Equity, Market.USA),
Symbol.Create("BAC", SecurityType.Equity, Market.USA)));
}
protected override string GetExpectedModelName(IAlphaModel model)
{
return $"{nameof(BasePairsTradingAlphaModel)}({_lookback},{_resolution},1)";
}
protected override IAlphaModel CreatePythonAlphaModel()
{
using (Py.GIL())
{
dynamic model = Py.Import("BasePairsTradingAlphaModel").GetAttr("BasePairsTradingAlphaModel");
var instance = model(_lookback, _resolution);
return new AlphaModelPythonWrapper(instance);
}
}
protected override IEnumerable<Insight> ExpectedInsights()
{
Assert.Ignore("The CommonAlphaModelTests need to be refactored to support multiple securities with different prices for each security");
return null;
}
}
}
@@ -0,0 +1,395 @@
/*
* 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 NUnit.Framework;
using QuantConnect.Algorithm.Framework.Alphas;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Algorithm.Framework.Selection;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Lean.Engine.DataFeeds;
using QuantConnect.Lean.Engine.HistoricalData;
using QuantConnect.Securities;
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Algorithm;
using QuantConnect.Python;
using QuantConnect.Tests.Common.Data.UniverseSelection;
using QuantConnect.Tests.Engine.DataFeeds;
using QuantConnect.Util;
namespace QuantConnect.Tests.Algorithm.Framework.Alphas
{
/// <summary>
/// Provides a framework for testing alpha models.
/// </summary>
public abstract class CommonAlphaModelTests
{
protected QCAlgorithm Algorithm { get; set; }
[OneTimeSetUp]
public void Initialize()
{
PythonInitializer.Initialize();
Algorithm = new QCAlgorithm();
Algorithm.PortfolioConstruction = new NullPortfolioConstructionModel();
Algorithm.HistoryProvider = new SineHistoryProvider(Algorithm.Securities);
Algorithm.SubscriptionManager.SetDataManager(new DataManagerStub(Algorithm));
InitializeAlgorithm(Algorithm);
}
[Test]
[TestCase(Language.CSharp)]
[TestCase(Language.Python)]
public void AddAlphaModel(Language language)
{
IAlphaModel model;
IAlphaModel model2 = null;
IAlphaModel model3 = null;
if (!TryCreateModel(language, out model)
|| !TryCreateModel(language, out model2)
|| !TryCreateModel(language, out model3))
{
Assert.Ignore($"Ignore {GetType().Name}: Could not create {language} model.");
}
// Set the alpha model
Algorithm.SetAlpha(model);
Algorithm.AddAlpha(model2);
Algorithm.AddAlpha(model3);
Algorithm.SetUniverseSelection(new ManualUniverseSelectionModel());
var changes = SecurityChangesTests.CreateNonInternal(AddedSecurities, RemovedSecurities);
Algorithm.OnFrameworkSecuritiesChanged(changes);
var actualInsights = new List<Insight>();
Algorithm.InsightsGenerated += (s, e) => actualInsights.AddRange(e.Insights);
var expectedInsights = ExpectedInsights().ToList();
var consolidators = Algorithm.Securities.SelectMany(kvp => kvp.Value.Subscriptions).SelectMany(x => x.Consolidators);
var slices = CreateSlices();
foreach (var slice in slices.ToList())
{
Algorithm.SetDateTime(slice.Time);
foreach (var symbol in slice.Keys)
{
var data = slice[symbol];
Algorithm.Securities[symbol].SetMarketPrice(data);
foreach (var consolidator in consolidators)
{
consolidator.Update(data);
}
}
Algorithm.OnFrameworkData(slice);
}
Assert.AreEqual(expectedInsights.Count * 3, actualInsights.Count);
for (var i = 0; i < actualInsights.Count; i = i + 3)
{
var expected = expectedInsights[i / 3];
for (int j = i; j < 3; j++)
{
var actual = actualInsights[j];
Assert.AreEqual(expected.Symbol, actual.Symbol);
Assert.AreEqual(expected.Type, actual.Type);
Assert.AreEqual(expected.Direction, actual.Direction);
Assert.LessOrEqual(expected.Period, actual.Period); // It can be canceled and discarded early
Assert.AreEqual(expected.Magnitude, actual.Magnitude);
Assert.AreEqual(expected.Confidence, actual.Confidence);
}
}
}
[Test]
[TestCase(Language.CSharp)]
[TestCase(Language.Python)]
public void InsightsGenerationTest(Language language)
{
IAlphaModel model;
if (!TryCreateModel(language, out model))
{
Assert.Ignore($"Ignore {GetType().Name}: Could not create {language} model.");
}
// Set the alpha model
Algorithm.SetAlpha(model);
Algorithm.SetUniverseSelection(new ManualUniverseSelectionModel());
var changes = SecurityChangesTests.CreateNonInternal(AddedSecurities, RemovedSecurities);
Algorithm.OnFrameworkSecuritiesChanged(changes);
var actualInsights = new List<Insight>();
Algorithm.InsightsGenerated += (s, e) => actualInsights.AddRange(e.Insights);
var expectedInsights = ExpectedInsights().ToList();
var consolidators = Algorithm.Securities.SelectMany(kvp => kvp.Value.Subscriptions).SelectMany(x => x.Consolidators);
var slices = CreateSlices();
foreach (var slice in slices.ToList())
{
Algorithm.SetDateTime(slice.Time);
foreach (var symbol in slice.Keys)
{
var data = slice[symbol];
Algorithm.Securities[symbol].SetMarketPrice(data);
foreach (var consolidator in consolidators)
{
consolidator.Update(data);
}
}
Algorithm.OnFrameworkData(slice);
}
Assert.AreEqual(expectedInsights.Count, actualInsights.Count);
for (var i = 0; i < actualInsights.Count; i++)
{
var actual = actualInsights[i];
var expected = expectedInsights[i];
Assert.AreEqual(expected.Symbol, actual.Symbol);
Assert.AreEqual(expected.Type, actual.Type);
Assert.AreEqual(expected.Direction, actual.Direction);
Assert.LessOrEqual(expected.Period, actual.Period); // It can be canceled and discarded early
Assert.AreEqual(expected.Magnitude, actual.Magnitude);
Assert.AreEqual(expected.Confidence, actual.Confidence);
}
}
[Test]
[TestCase(Language.CSharp)]
[TestCase(Language.Python)]
public void AddedSecuritiesTest(Language language)
{
IAlphaModel model;
if (!TryCreateModel(language, out model))
{
Assert.Ignore($"Ignore {GetType().Name}: Could not create {language} model.");
}
var changes = SecurityChangesTests.CreateNonInternal(AddedSecurities, RemovedSecurities);
Assert.DoesNotThrow(() => model.OnSecuritiesChanged(Algorithm, changes));
}
[Test]
[TestCase(Language.CSharp)]
[TestCase(Language.Python)]
public void RemovedSecuritiesTest(Language language)
{
IAlphaModel model;
if (!TryCreateModel(language, out model))
{
Assert.Ignore($"Ignore {GetType().Name}: Could not create {language} model.");
}
var removedSecurities = Algorithm.Securities.Values;
// We have to add some security if we then want to remove it, that's why we cannot use here
// RemovedSecurities, because it doesn't contain any security
var changes = SecurityChangesTests.CreateNonInternal(removedSecurities, AddedSecurities);
Assert.DoesNotThrow(() => model.OnSecuritiesChanged(Algorithm, changes));
}
[Test]
[TestCase(Language.CSharp)]
[TestCase(Language.Python)]
public void ModelNameTest(Language language)
{
IAlphaModel model;
if (!TryCreateModel(language, out model))
{
Assert.Ignore($"Ignore {GetType().Name}: Could not create {language} model.");
}
var actual = model.GetModelName();
var expected = GetExpectedModelName(model);
Assert.AreEqual(expected, actual);
}
/// <summary>
/// Returns a new instance of the alpha model to test
/// </summary>
protected abstract IAlphaModel CreateCSharpAlphaModel();
/// <summary>
/// Returns a new instance of the alpha model to test
/// </summary>
protected abstract IAlphaModel CreatePythonAlphaModel();
/// <summary>
/// Returns an enumerable with the expected insights
/// </summary>
protected abstract IEnumerable<Insight> ExpectedInsights();
/// <summary>
/// List of securities to be added to the model
/// </summary>
protected virtual IEnumerable<Security> AddedSecurities => Algorithm.Securities.Values;
/// <summary>
/// List of securities to be removed to the model
/// </summary>
protected virtual IEnumerable<Security> RemovedSecurities => Enumerable.Empty<Security>();
/// <summary>
/// To be override for model types that implement <see cref="INamedModel"/>
/// </summary>
protected abstract string GetExpectedModelName(IAlphaModel model);
/// <summary>
/// Provides derived types a chance to initialize anything special they require
/// </summary>
protected virtual void InitializeAlgorithm(QCAlgorithm algorithm)
{
Algorithm.SetStartDate(2018, 1, 4);
Algorithm.AddEquity(Symbols.SPY.Value, Resolution.Daily);
}
/// <summary>
/// Creates an enumerable of Slice to update the alpha model
/// </summary>
protected virtual IEnumerable<Slice> CreateSlices()
{
var timeSliceFactory = new TimeSliceFactory(TimeZones.NewYork);
var changes = SecurityChanges.None;
var sliceDateTimes = GetSliceDateTimes(MaxSliceCount);
for (var i = 0; i < sliceDateTimes.Count; i++)
{
var utcDateTime = sliceDateTimes[i];
var packets = new List<DataFeedPacket>();
// TODO : Give securities different values -- will require updating all derived types
var last = Convert.ToDecimal(100 + 10 * Math.Sin(Math.PI * i / 180.0));
var high = last * 1.005m;
var low = last / 1.005m;
foreach (var kvp in Algorithm.Securities)
{
var security = kvp.Value;
var exchange = security.Exchange.Hours;
var configs = Algorithm.SubscriptionManager.SubscriptionDataConfigService
.GetSubscriptionDataConfigs(security.Symbol);
var extendedMarket = configs.IsExtendedMarketHours();
var localDateTime = utcDateTime.ConvertFromUtc(exchange.TimeZone);
if (!exchange.IsOpen(localDateTime, extendedMarket))
{
continue;
}
var configuration = security.Subscriptions.FirstOrDefault();
var period = configs.GetHighestResolution().ToTimeSpan();
var time = (utcDateTime - period).ConvertFromUtc(configuration.DataTimeZone);
var tradeBar = new TradeBar(time, security.Symbol, last, high, low, last, 1000, period);
packets.Add(new DataFeedPacket(security, configuration, new List<BaseData> { tradeBar }));
}
if (packets.Count > 0)
{
yield return timeSliceFactory.Create(utcDateTime, packets, changes, new Dictionary<Universe, BaseDataCollection>()).Slice;
}
}
}
/// <summary>
/// Set up the HistoryProvider for algorithm
/// </summary>
protected void SetUpHistoryProvider()
{
Algorithm.HistoryProvider = new SubscriptionDataReaderHistoryProvider();
Algorithm.HistoryProvider.Initialize(new HistoryProviderInitializeParameters(
null,
null,
TestGlobals.DataProvider,
TestGlobals.DataCacheProvider,
TestGlobals.MapFileProvider,
TestGlobals.FactorFileProvider,
null,
false,
new DataPermissionManager(),
Algorithm.ObjectStore,
Algorithm.Settings));
}
/// <summary>
/// Gets the maximum number of slice objects to generate
/// </summary>
protected virtual int MaxSliceCount => 360;
private List<DateTime> GetSliceDateTimes(int maxCount)
{
var i = 0;
var sliceDateTimes = new List<DateTime>();
var utcDateTime = Algorithm.StartDate;
while (sliceDateTimes.Count < maxCount)
{
foreach (var kvp in Algorithm.Securities)
{
var security = kvp.Value;
var configs = Algorithm.SubscriptionManager.SubscriptionDataConfigService
.GetSubscriptionDataConfigs(security.Symbol);
var resolution = configs.GetHighestResolution().ToTimeSpan();
utcDateTime = utcDateTime.Add(resolution);
if (resolution == Time.OneDay && utcDateTime.TimeOfDay == TimeSpan.Zero)
{
utcDateTime = utcDateTime.AddHours(17);
}
var exchange = security.Exchange.Hours;
var extendedMarket = configs.IsExtendedMarketHours();
var localDateTime = utcDateTime.ConvertFromUtc(exchange.TimeZone);
if (exchange.IsOpen(localDateTime, extendedMarket))
{
sliceDateTimes.Add(utcDateTime);
}
i++;
}
}
return sliceDateTimes;
}
private bool TryCreateModel(Language language, out IAlphaModel model)
{
model = default(IAlphaModel);
switch (language)
{
case Language.CSharp:
model = CreateCSharpAlphaModel();
return true;
case Language.Python:
Algorithm.SetPandasConverter();
model = CreatePythonAlphaModel();
return true;
default:
return false;
}
}
}
}
@@ -0,0 +1,99 @@
/*
* 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 NUnit.Framework;
using Python.Runtime;
using QuantConnect.Algorithm.Framework.Alphas;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using static System.FormattableString;
namespace QuantConnect.Tests.Algorithm.Framework.Alphas
{
[TestFixture]
public class ConstantAlphaModelTests : CommonAlphaModelTests
{
private InsightType _type = InsightType.Price;
private InsightDirection _direction = InsightDirection.Up;
private TimeSpan _period = Time.OneDay;
private double? _magnitude = 0.025;
private double? _confidence = null;
protected override IAlphaModel CreateCSharpAlphaModel() => new ConstantAlphaModel(_type, _direction, _period, _magnitude, _confidence);
protected override IAlphaModel CreatePythonAlphaModel()
{
using (Py.GIL())
{
dynamic model = Py.Import("ConstantAlphaModel").GetAttr("ConstantAlphaModel");
var instance = model(_type, _direction, _period, _magnitude, _confidence);
return new AlphaModelPythonWrapper(instance);
}
}
[TestCase(Language.CSharp)]
[TestCase(Language.Python)]
public void ConstructorWithWeightOnlySetsWeightCorrectly(Language language)
{
IAlphaModel alpha;
if (language == Language.CSharp)
{
alpha = new ConstantAlphaModel(InsightType.Price, InsightDirection.Up, TimeSpan.FromDays(1), weight: 0.1);
}
else
{
using (Py.GIL())
{
var testModule = PyModule.FromString("test_module",
@"
from AlgorithmImports import *
def test_constructor():
model = ConstantAlphaModel(InsightType.Price, InsightDirection.Up, timedelta(1), weight=0.1)
return model
");
alpha = testModule.GetAttr("test_constructor").Invoke().As<ConstantAlphaModel>();
}
}
var magnitude = GetPrivateField(alpha, "_magnitude");
var confidence = GetPrivateField(alpha, "_confidence");
var weight = GetPrivateField(alpha, "_weight");
Assert.IsNull(magnitude);
Assert.IsNull(confidence);
Assert.AreEqual(0.1, weight);
}
private static object GetPrivateField(object obj, string fieldName)
{
var field = obj.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
return field?.GetValue(obj);
}
protected override IEnumerable<Insight> ExpectedInsights()
{
return Enumerable.Range(0, 360).Select(x => new Insight(Symbols.SPY, _period, _type, _direction, _magnitude, _confidence));
}
protected override string GetExpectedModelName(IAlphaModel model)
{
return Invariant($"{nameof(ConstantAlphaModel)}({_type},{_direction},{_period},{_magnitude})");
}
}
}
@@ -0,0 +1,137 @@
/*
* 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 NUnit.Framework;
using Python.Runtime;
using QuantConnect.Algorithm.Framework.Alphas;
using System;
using System.Collections.Generic;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Algorithm.Framework.Selection;
using QuantConnect.Util;
using QuantConnect.Tests.Common.Data.UniverseSelection;
namespace QuantConnect.Tests.Algorithm.Framework.Alphas
{
[TestFixture]
public class EmaCrossAlphaModelTests : CommonAlphaModelTests
{
protected override IAlphaModel CreateCSharpAlphaModel() => new EmaCrossAlphaModel();
protected override IAlphaModel CreatePythonAlphaModel()
{
using (Py.GIL())
{
dynamic model = Py.Import("EmaCrossAlphaModel").GetAttr("EmaCrossAlphaModel");
var instance = model();
return new AlphaModelPythonWrapper(instance);
}
}
protected override IEnumerable<Insight> ExpectedInsights()
{
var period = TimeSpan.FromDays(12);
return new[]
{
Insight.Price(Symbols.SPY, period, InsightDirection.Down),
Insight.Price(Symbols.SPY, period, InsightDirection.Up)
};
}
protected override string GetExpectedModelName(IAlphaModel model)
{
return $"{nameof(EmaCrossAlphaModel)}(12,26,Daily)";
}
[Test]
public void WarmsUpProperly()
{
SetUpHistoryProvider();
Algorithm.SetStartDate(2013, 10, 08);
Algorithm.SetUniverseSelection(new ManualUniverseSelectionModel());
// Create a EmaCrossAlphaModel for the test
var model = new TestEmaCrossAlphaModel();
// Set the alpha model
Algorithm.SetAlpha(model);
Algorithm.SetUniverseSelection(new ManualUniverseSelectionModel());
var changes = SecurityChangesTests.CreateNonInternal(AddedSecurities, RemovedSecurities);
Algorithm.OnFrameworkSecuritiesChanged(changes);
// Get the dictionary of macd indicators
var symbolData = model.GetSymbolData();
// Check the symbolData dictionary is not empty
Assert.NotZero(symbolData.Count);
// Check all EmaCross indicators from the alpha are ready and have at least
// one datapoint
foreach (var item in symbolData)
{
var fast = item.Value.Fast;
var slow = item.Value.Slow;
Assert.IsTrue(fast.IsReady);
Assert.NotZero(fast.Samples);
Assert.IsTrue(slow.IsReady);
Assert.NotZero(slow.Samples);
}
}
[Test]
public void PythonVersionWarmsUpProperly()
{
using (Py.GIL())
{
SetUpHistoryProvider();
Algorithm.SetStartDate(2013, 10, 08);
Algorithm.SetUniverseSelection(new ManualUniverseSelectionModel());
// Create and set alpha model
dynamic model = Py.Import("EmaCrossAlphaModel").GetAttr("EmaCrossAlphaModel");
var instance = model();
Algorithm.SetAlpha(instance);
var changes = SecurityChangesTests.CreateNonInternal(AddedSecurities, RemovedSecurities);
Algorithm.OnFrameworkSecuritiesChanged(changes);
// Get the dictionary of ema cross indicators
var symbolData = instance.symbol_data_by_symbol;
// Check the dictionary is not empty
Assert.NotZero(symbolData.Length());
// Check all Ema Cross indicators from the alpha are ready and have at least
// one datapoint
foreach (var item in symbolData)
{
var fast = symbolData[item].fast;
var slow = symbolData[item].slow;
Assert.IsTrue(fast.IsReady.IsTrue());
Assert.NotZero(((PyObject)fast.Samples).GetAndDispose<int>());
Assert.IsTrue(slow.IsReady.IsTrue());
Assert.NotZero(((PyObject)slow.Samples).GetAndDispose<int>());
}
}
}
}
}
@@ -0,0 +1,270 @@
/*
* 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.Linq;
using NUnit.Framework;
using System.Collections.Generic;
using QuantConnect.Algorithm.Framework.Alphas;
namespace QuantConnect.Tests.Algorithm.Framework.Alphas
{
[TestFixture]
public class InsightCollectionTests
{
private static readonly DateTime _referenceTime = new DateTime(2019, 1, 1);
[Test]
public void InsightCollectionShouldBeAbleToBeConvertedToListWithoutStackOverflow()
{
var insightCollection = new InsightCollection
{
new Insight(Symbols.AAPL, new TimeSpan(1, 0, 0, 0), InsightType.Price, InsightDirection.Up)
{
CloseTimeUtc = new DateTime(2019, 1, 1),
},
new Insight(Symbols.AAPL, new TimeSpan(1, 0, 0, 0), InsightType.Volatility, InsightDirection.Up)
{
CloseTimeUtc = new DateTime(2019, 1, 2),
}
};
Assert.DoesNotThrow(() => insightCollection.OrderBy(x => x.CloseTimeUtc).ToList());
}
[Test]
public void HasActiveInsights()
{
var collection = new InsightCollection();
Assert.IsFalse(collection.HasActiveInsights(Symbols.AAPL, DateTime.MinValue));
collection.AddRange(GetTestInsight());
Assert.IsFalse(collection.HasActiveInsights(Symbols.AAPL, DateTime.MaxValue));
Assert.IsTrue(collection.HasActiveInsights(Symbols.AAPL, _referenceTime));
}
[Test]
public void GetNextExpiryTime()
{
var collection = new InsightCollection();
Assert.AreEqual(null, collection.GetNextExpiryTime());
collection.AddRange(GetTestInsight());
Assert.AreEqual(_referenceTime, collection.GetNextExpiryTime());
var nextDay = _referenceTime.AddDays(1);
Assert.AreEqual(1, collection.RemoveExpiredInsights(nextDay).Count);
Assert.AreEqual(nextDay, collection.GetNextExpiryTime());
}
[Test]
public void TryGetValue()
{
var collection = new InsightCollection();
Assert.IsFalse(collection.TryGetValue(Symbols.AAPL, out var _));
collection.AddRange(GetTestInsight());
Assert.IsTrue(collection.TryGetValue(Symbols.AAPL, out var insights));
Assert.AreEqual(2, insights.Count);
Assert.AreEqual(2, insights.Count(insight => insight.Symbol == Symbols.AAPL));
}
[Test]
public void KeyNotFoundException()
{
var collection = new InsightCollection();
Assert.Throws<KeyNotFoundException>(() =>
{
var insight = collection[Symbols.AAPL];
});
}
[Test]
public void Contains()
{
var collection = new InsightCollection();
var insights = GetTestInsight();
collection.AddRange(insights);
foreach (var insight in insights)
{
Assert.IsTrue(collection.Contains(insight));
Assert.IsTrue(collection.ContainsKey(insight.Symbol));
}
Assert.IsFalse(collection.ContainsKey(Symbols.BTCEUR));
var anotherInsight = new Insight(Symbols.BTCEUR, new TimeSpan(1, 0, 0, 0), InsightType.Price, InsightDirection.Up);
Assert.IsFalse(collection.Contains(anotherInsight));
}
[Test]
public void Addition()
{
var collection = new InsightCollection();
var insight = new Insight(Symbols.AAPL, new TimeSpan(1, 0, 0, 0), InsightType.Price, InsightDirection.Up) { CloseTimeUtc = _referenceTime };
collection.Add(insight);
collection.Add(new Insight(Symbols.SPY, new TimeSpan(1, 0, 0, 0), InsightType.Price, InsightDirection.Up) { CloseTimeUtc = _referenceTime });
collection.Add(new Insight(Symbols.IBM, new TimeSpan(1, 0, 0, 0), InsightType.Price, InsightDirection.Down) { CloseTimeUtc = _referenceTime.AddDays(-1) });
var beforeExpiration = insight.CloseTimeUtc.AddDays(-1);
Assert.AreEqual(3, collection.Count);
Assert.IsTrue(collection.TryGetValue(Symbols.AAPL, out var insightInCollection));
Assert.IsTrue(collection.HasActiveInsights(Symbols.AAPL, beforeExpiration));
Assert.AreEqual(insight, insightInCollection.Single());
Assert.AreEqual(insight, collection[Symbols.AAPL].Single());
Assert.AreEqual(3, collection.Count);
Assert.AreEqual(3, collection.GetActiveInsights(beforeExpiration).Count);
Assert.AreEqual(3, collection.GetInsights().Count);
Assert.AreEqual(insight, collection.GetInsights(x => insight == x).Single());
Assert.AreEqual(0, collection.GetActiveInsights(_referenceTime.AddYears(1)).Count);
Assert.AreEqual(3, collection.TotalCount);
}
[Test]
public void GetInsights()
{
var collection = new InsightCollection();
var insights = GetTestInsight();
collection.AddRange(insights);
Assert.AreEqual(5, collection.Count);
collection.RemoveInsights(x => x == insights[0]);
Assert.AreEqual(4, collection.GetInsights().Count);
Assert.AreEqual(4, collection.Count);
}
[Test]
public void Removal()
{
var collection = new InsightCollection();
var insights = GetTestInsight();
collection.AddRange(insights);
var insightCount = collection.Count;
foreach (var insight in insights)
{
Assert.IsTrue(collection.Remove(insight));
Assert.AreEqual(--insightCount, collection.Count);
}
// readd the first insight
var firstInsight = insights[0];
collection.Add(firstInsight);
Assert.AreEqual(1, collection.Count);
// we only remove 'firstInsight' from the global collection
collection.RemoveInsights(x => x == firstInsight);
Assert.AreEqual(4, collection.GetInsights().Count);
Assert.AreEqual(0, collection.Count);
Assert.AreEqual(6, collection.TotalCount);
}
[Test]
public void ExpiredRemoval()
{
var collection = new InsightCollection();
var insights = GetTestInsight();
collection.AddRange(insights);
Assert.AreEqual(5, collection.Count);
Assert.AreEqual(0, collection.RemoveExpiredInsights(_referenceTime.AddDays(-1)).Count);
// expire 1 insight
Assert.AreEqual(insights[0], collection.RemoveExpiredInsights(_referenceTime.AddDays(1)).Single());
// expire 2 insights
Assert.AreEqual(2, collection.RemoveExpiredInsights(_referenceTime.AddDays(2)).Count);
Assert.AreEqual(2, collection.Count);
Assert.AreEqual(5, collection.TotalCount);
}
[Test]
public void IndexAccess()
{
var collection = new InsightCollection();
collection.AddRange(GetTestInsight());
collection[Symbols.AAPL] = null;
Assert.AreEqual(3, collection.Count);
var insight = new Insight(Symbols.AAPL, new TimeSpan(1, 0, 0, 0), InsightType.Price, InsightDirection.Up) { CloseTimeUtc = new DateTime(2019, 1, 1) };
var insight2 = new Insight(Symbols.AAPL, new TimeSpan(1, 0, 0, 0), InsightType.Price, InsightDirection.Up) { CloseTimeUtc = new DateTime(2019, 1, 2) };
collection[Symbols.AAPL] = new() { insight, insight2 };
Assert.AreEqual(5, collection.Count);
collection[Symbols.AAPL] = null;
Assert.AreEqual(3, collection.Count);
Assert.AreEqual(7, collection.TotalCount);
}
[Test]
public void AddRange()
{
var collection = new InsightCollection();
var insights = GetTestInsight();
collection.AddRange(insights);
Assert.AreEqual(5, collection.Count);
foreach (var insight in insights)
{
Assert.IsTrue(collection.Contains(insight));
Assert.IsTrue(collection.ContainsKey(insight.Symbol));
}
Assert.AreEqual(5, collection.TotalCount);
}
[Test]
public void ClearSymbols()
{
var collection = new InsightCollection();
collection.AddRange(GetTestInsight());
collection.Clear(Array.Empty<Symbol>());
Assert.AreEqual(5, collection.Count);
collection.Clear(new[] { Symbols.AAPL });
Assert.AreEqual(3, collection.Count);
Assert.IsTrue(collection.ContainsKey(Symbols.SPY));
Assert.IsTrue(collection.ContainsKey(Symbols.IBM));
Assert.IsFalse(collection.ContainsKey(Symbols.AAPL));
Assert.AreEqual(5, collection.TotalCount);
}
private static List<Insight> GetTestInsight()
{
var insight = new Insight(Symbols.AAPL, new TimeSpan(1, 0, 0, 0), InsightType.Price, InsightDirection.Up) { CloseTimeUtc = _referenceTime };
var insight2 = new Insight(Symbols.AAPL, new TimeSpan(1, 0, 0, 0), InsightType.Price, InsightDirection.Up) { CloseTimeUtc = _referenceTime.AddDays(1) };
var insight3 = new Insight(Symbols.SPY, new TimeSpan(1, 0, 0, 0), InsightType.Price, InsightDirection.Up) { CloseTimeUtc = _referenceTime.AddDays(1) };
var insight4 = new Insight(Symbols.SPY, new TimeSpan(1, 0, 0, 0), InsightType.Price, InsightDirection.Down) { CloseTimeUtc = _referenceTime.AddMonths(1) };
var insight5 = new Insight(Symbols.IBM, new TimeSpan(1, 0, 0, 0), InsightType.Price, InsightDirection.Down) { CloseTimeUtc = _referenceTime.AddMonths(1) };
return new List<Insight> { insight, insight2, insight3, insight4, insight5 };
}
}
}
@@ -0,0 +1,136 @@
/*
* 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.Linq;
using NUnit.Framework;
using QuantConnect.Tests.Engine.DataFeeds;
using QuantConnect.Algorithm.Framework.Alphas;
using QuantConnect.Algorithm.Framework.Alphas.Analysis;
namespace QuantConnect.Tests.Algorithm.Framework.Alphas
{
[TestFixture]
public class InsightManagerTests
{
private static readonly DateTime _utcNow = new (2019, 1, 1);
[TestCase(false)]
[TestCase(true)]
public void ExpireSameTime(bool useCancelApi)
{
var algorithm = new AlgorithmStub();
algorithm.SetDateTime(_utcNow);
var insightManager = new InsightManager(algorithm);
insightManager.AddRange(GetInsights());
Assert.IsTrue(insightManager.All(insight => insight.IsActive(_utcNow)));
if (useCancelApi)
{
insightManager.Cancel(new[] { Symbols.IBM, Symbols.SPY });
}
else
{
insightManager.Expire(new[] { Symbols.IBM, Symbols.SPY });
}
Assert.IsTrue(insightManager[Symbols.IBM].All(insight => insight.IsExpired(algorithm.UtcTime) && insight.Period == TimeSpan.FromSeconds(-1)));
Assert.IsTrue(insightManager[Symbols.SPY].All(insight => insight.IsExpired(algorithm.UtcTime) && insight.Period == TimeSpan.FromSeconds(-1)));
Assert.IsTrue(insightManager[Symbols.AAPL].All(insight => insight.IsActive(algorithm.UtcTime)));
}
[TestCase(false)]
[TestCase(true)]
public void ExpireBySymbol(bool useCancelApi)
{
var algorithm = new AlgorithmStub();
algorithm.SetDateTime(_utcNow);
var insightManager = new InsightManager(algorithm);
insightManager.AddRange(GetInsights());
Assert.IsTrue(insightManager.All(insight => insight.IsActive(_utcNow)));
algorithm.SetDateTime(algorithm.UtcTime.AddMinutes(1));
if (useCancelApi)
{
insightManager.Cancel(new[] { Symbols.IBM, Symbols.SPY });
}
else
{
insightManager.Expire(new[] { Symbols.IBM, Symbols.SPY });
}
var expectedPeriod = Time.OneMinute.Subtract(Time.OneSecond);
Assert.IsTrue(insightManager[Symbols.IBM].All(insight => insight.IsExpired(algorithm.UtcTime) && insight.Period == expectedPeriod));
Assert.IsTrue(insightManager[Symbols.SPY].All(insight => insight.IsExpired(algorithm.UtcTime) && insight.Period == expectedPeriod));
Assert.IsTrue(insightManager[Symbols.AAPL].All(insight => insight.IsActive(algorithm.UtcTime)));
}
[TestCase(false)]
[TestCase(true)]
public void ExpireByInsight(bool useCancelApi)
{
var algorithm = new AlgorithmStub();
algorithm.SetDateTime(_utcNow);
var insights = GetInsights();
var insightManager = new InsightManager(algorithm);
insightManager.AddRange(insights);
Assert.IsTrue(insightManager.All(insight => insight.IsActive(_utcNow)));
algorithm.SetDateTime(algorithm.UtcTime.AddMinutes(1));
if (useCancelApi)
{
insightManager.Cancel(new[] { insights[2], insights[3] });
}
else
{
insightManager.Expire(new[] { insights[2], insights[3] });
}
var expectedPeriod = Time.OneMinute.Subtract(Time.OneSecond);
Assert.IsTrue(insightManager[Symbols.IBM].All(insight => insight.IsExpired(algorithm.UtcTime) && insight.Period == expectedPeriod));
Assert.AreEqual(1, insightManager[Symbols.SPY].Count(insight => insight.IsExpired(algorithm.UtcTime) && insight.Period == expectedPeriod));
Assert.AreEqual(1, insightManager[Symbols.SPY].Count(insight => insight.IsActive(algorithm.UtcTime)));
Assert.IsTrue(insightManager[Symbols.AAPL].All(insight => insight.IsActive(algorithm.UtcTime)));
}
private static Insight[] GetInsights()
{
return new[] {
new Insight(Symbols.AAPL, new TimeSpan(1, 0, 0, 0), InsightType.Price, InsightDirection.Up)
{
GeneratedTimeUtc = _utcNow,
CloseTimeUtc = _utcNow.AddDays(1),
},
new Insight(Symbols.SPY, new TimeSpan(2, 0, 0, 0), InsightType.Volatility, InsightDirection.Up)
{
GeneratedTimeUtc = _utcNow,
CloseTimeUtc = _utcNow.AddDays(2),
},
new Insight(Symbols.SPY, new TimeSpan(3, 0, 0, 0), InsightType.Volatility, InsightDirection.Up)
{
GeneratedTimeUtc = _utcNow,
CloseTimeUtc = _utcNow.AddDays(3),
},
new Insight(Symbols.IBM, new TimeSpan(4, 0, 0, 0), InsightType.Volatility, InsightDirection.Up)
{
GeneratedTimeUtc = _utcNow,
CloseTimeUtc = _utcNow.AddDays(4),
} };
}
}
}
@@ -0,0 +1,127 @@
/*
* 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 NUnit.Framework;
using Python.Runtime;
using QuantConnect.Algorithm.Framework.Alphas;
using System;
using System.Collections.Generic;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Algorithm.Framework.Selection;
using QuantConnect.Tests.Common.Data.UniverseSelection;
using QuantConnect.Util;
namespace QuantConnect.Tests.Algorithm.Framework.Alphas
{
[TestFixture]
public class MacdAlphaModelTests : CommonAlphaModelTests
{
protected override IAlphaModel CreateCSharpAlphaModel() => new MacdAlphaModel();
protected override IAlphaModel CreatePythonAlphaModel()
{
using (Py.GIL())
{
dynamic model = Py.Import("MacdAlphaModel").GetAttr("MacdAlphaModel");
var instance = model();
return new AlphaModelPythonWrapper(instance);
}
}
protected override IEnumerable<Insight> ExpectedInsights()
{
var period = TimeSpan.FromDays(12);
return new[]
{
Insight.Price(Symbols.SPY, period, InsightDirection.Up),
Insight.Price(Symbols.SPY, period, InsightDirection.Down),
Insight.Price(Symbols.SPY, period, InsightDirection.Up)
};
}
protected override string GetExpectedModelName(IAlphaModel model)
{
return $"{nameof(MacdAlphaModel)}(12,26,9,Exponential,Daily)";
}
[Test]
public void MacdAlphaModelWarmsUpProperly()
{
SetUpHistoryProvider();
Algorithm.SetStartDate(2013, 10, 08);
// Create a MacdAlphaModel for the test
var model = new TestMacdAlphaModel();
// Set the alpha model
Algorithm.SetAlpha(model);
Algorithm.SetUniverseSelection(new ManualUniverseSelectionModel());
var changes = SecurityChangesTests.CreateNonInternal(AddedSecurities, RemovedSecurities);
Algorithm.OnFrameworkSecuritiesChanged(changes);
// Get the dictionary of macd indicators
var symbolData = model.GetSymbolData();
// Check the symbolData dictionary is not empty
Assert.NotZero(symbolData.Count);
// Check all MACD indicators from the alpha are ready and have at least
// one datapoint
foreach (var item in symbolData)
{
var macd = item.Value.MACD;
Assert.IsTrue(macd.IsReady);
Assert.NotZero(macd.Samples);
}
}
[Test]
public void PythonMacdAlphaModelWarmsUpProperly()
{
using (Py.GIL())
{
SetUpHistoryProvider();
Algorithm.SetStartDate(2013, 10, 08);
Algorithm.SetUniverseSelection(new ManualUniverseSelectionModel());
// Create and set alpha model
dynamic model = Py.Import("MacdAlphaModel").GetAttr("MacdAlphaModel");
var instance = model();
Algorithm.SetAlpha(instance);
var changes = SecurityChangesTests.CreateNonInternal(AddedSecurities, RemovedSecurities);
Algorithm.OnFrameworkSecuritiesChanged(changes);
// Get the dictionary of macd indicators
var symbolData = instance.symbolData;
// Check the dictionary is not empty
Assert.NotZero(symbolData.Length());
// Check all MACD indicators from the alpha are ready and have at least
// one datapoint
foreach (var item in symbolData)
{
var macd = symbolData[item].MACD;
Assert.IsTrue(macd.IsReady.IsTrue());
Assert.NotZero(((PyObject)macd.Samples).GetAndDispose<int>());
}
}
}
}
}
@@ -0,0 +1,53 @@
/*
* 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 NUnit.Framework;
using Python.Runtime;
using QuantConnect.Algorithm.Framework.Alphas;
using System;
using System.Collections.Generic;
namespace QuantConnect.Tests.Algorithm.Framework.Alphas
{
[TestFixture]
public class RsiAlphaModelTests : CommonAlphaModelTests
{
protected override IAlphaModel CreateCSharpAlphaModel() => new RsiAlphaModel();
protected override IAlphaModel CreatePythonAlphaModel()
{
using (Py.GIL())
{
dynamic model = Py.Import("RsiAlphaModel").GetAttr("RsiAlphaModel");
var instance = model();
return new AlphaModelPythonWrapper(instance);
}
}
protected override IEnumerable<Insight> ExpectedInsights()
{
var period = TimeSpan.FromDays(14);
foreach (var direction in new[] { InsightDirection.Up, InsightDirection.Down })
{
yield return Insight.Price(Symbols.SPY, period, direction);
}
}
protected override string GetExpectedModelName(IAlphaModel model)
{
return $"{nameof(RsiAlphaModel)}(14,Daily)";
}
}
}
@@ -0,0 +1,301 @@
/*
* 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 Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using NUnit.Framework;
using QuantConnect.Algorithm.Framework.Alphas;
using QuantConnect.Algorithm.Framework.Alphas.Serialization;
namespace QuantConnect.Tests.Algorithm.Framework.Alphas.Serialization
{
[TestFixture, Parallelizable(ParallelScope.All)]
public class InsightJsonConverterTests
{
private JsonSerializerSettings _serializerSettings = new()
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy
{
ProcessDictionaryKeys = false,
OverrideSpecifiedNames = true
}
}
};
[Test]
public void DeserializesInsightWithoutScore()
{
var jObject = JObject.Parse(jsonNoScoreBackwardsCompatible);
var result = JsonConvert.DeserializeObject<Insight>(jsonNoScoreBackwardsCompatible);
Assert.AreEqual(jObject["id"].Value<string>(), result.Id.ToStringInvariant("N"));
Assert.AreEqual(jObject["source-model"].Value<string>(), result.SourceModel);
Assert.AreEqual(jObject["group-id"]?.Value<string>(), result.GroupId?.ToStringInvariant("N"));
Assert.AreEqual(jObject["created-time"].Value<double>(), Time.DateTimeToUnixTimeStamp(result.GeneratedTimeUtc), 5e-4);
Assert.AreEqual(jObject["close-time"].Value<double>(), Time.DateTimeToUnixTimeStamp(result.CloseTimeUtc), 5e-4);
Assert.AreEqual(jObject["symbol"].Value<string>(), result.Symbol.ID.ToString());
Assert.AreEqual(jObject["ticker"].Value<string>(), result.Symbol.Value);
Assert.AreEqual(jObject["type"].Value<string>(), result.Type.ToLower());
Assert.AreEqual(jObject["reference"].Value<decimal>(), result.ReferenceValue);
Assert.AreEqual(jObject["direction"].Value<string>(), result.Direction.ToLower());
Assert.AreEqual(jObject["period"].Value<double>(), result.Period.TotalSeconds);
Assert.AreEqual(jObject["magnitude"].Value<double>(), result.Magnitude);
Assert.AreEqual(null, result.Confidence);
// default values for scores
Assert.AreEqual(false, result.Score.IsFinalScore);
Assert.AreEqual(0, result.ReferenceValueFinal);
Assert.AreEqual(0, result.Score.Magnitude);
Assert.AreEqual(0, result.Score.Direction);
}
[Test]
public void DeserializesInsightWithScore()
{
var jObject = JObject.Parse(jsonWithScoreBackwardsCompatible);
var result = JsonConvert.DeserializeObject<Insight>(jsonWithScoreBackwardsCompatible);
Assert.AreEqual(jObject["id"].Value<string>(), result.Id.ToStringInvariant("N"));
Assert.AreEqual(jObject["source-model"].Value<string>(), result.SourceModel);
Assert.AreEqual(jObject["group-id"]?.Value<string>(), result.GroupId?.ToStringInvariant("N"));
Assert.AreEqual(jObject["created-time"].Value<double>(), Time.DateTimeToUnixTimeStamp(result.GeneratedTimeUtc), 5e-4);
Assert.AreEqual(jObject["close-time"].Value<double>(), Time.DateTimeToUnixTimeStamp(result.CloseTimeUtc), 5e-4);
Assert.AreEqual(jObject["symbol"].Value<string>(), result.Symbol.ID.ToString());
Assert.AreEqual(jObject["ticker"].Value<string>(), result.Symbol.Value);
Assert.AreEqual(jObject["type"].Value<string>(), result.Type.ToLower());
Assert.AreEqual(jObject["reference"].Value<decimal>(), result.ReferenceValue);
Assert.AreEqual(jObject["direction"].Value<string>(), result.Direction.ToLower());
Assert.AreEqual(jObject["period"].Value<double>(), result.Period.TotalSeconds);
Assert.AreEqual(jObject["magnitude"].Value<double>(), result.Magnitude);
Assert.AreEqual(null, result.Confidence);
Assert.AreEqual(true, result.Score.IsFinalScore);
Assert.AreEqual(jObject["score-magnitude"].Value<double>(), result.Score.Magnitude);
Assert.AreEqual(jObject["score-direction"].Value<double>(), result.Score.Direction);
Assert.AreEqual(jObject["reference-final"].Value<decimal>(), result.ReferenceValueFinal);
}
[TestCase(true)]
[TestCase(false)]
public void SerializesInsightWithoutScore(bool backwardsCompatible)
{
var serializedInsight = JsonConvert.DeserializeObject<SerializedInsight>(backwardsCompatible ? jsonNoScoreBackwardsCompatible : jsonNoScore2);
var insight = Insight.FromSerializedInsight(serializedInsight);
var result = JsonConvert.SerializeObject(insight, Formatting.None, _serializerSettings);
Assert.AreEqual(jsonNoScore2, result);
}
[TestCase(true)]
[TestCase(false)]
public void SerializesInsightWithScore(bool backwardsCompatible)
{
var serializedInsight = JsonConvert.DeserializeObject<SerializedInsight>(backwardsCompatible ? jsonWithScoreBackwardsCompatible : jsonWithScore);
var insight = Insight.FromSerializedInsight(serializedInsight);
var result = JsonConvert.SerializeObject(insight, Formatting.None, _serializerSettings);
Assert.AreEqual(jsonWithScore, result);
}
[Test]
public void SerializesOldInsightWithMissingCreatedTime()
{
var serializedInsight = JsonConvert.DeserializeObject<SerializedInsight>(jsonWithMissingCreatedTimeBackwardsCompatible);
var insight = Insight.FromSerializedInsight(serializedInsight);
var result = JsonConvert.SerializeObject(insight, Formatting.None, _serializerSettings);
Assert.AreEqual(serializedInsight.CreatedTime, serializedInsight.GeneratedTime);
Assert.AreEqual(jsonWithExpectedOutputFromMissingCreatedTimeValue, result);
}
[TestCase(true)]
[TestCase(false)]
public void SerializesInsightWithTag(bool backwardsCompatible)
{
var serializedInsight = JsonConvert.DeserializeObject<SerializedInsight>(backwardsCompatible ? jsonWithTagBackwardsCompatible : jsonWithTag);
var insight = Insight.FromSerializedInsight(serializedInsight);
var result = JsonConvert.SerializeObject(insight, Formatting.None, _serializerSettings);
Assert.AreEqual(jsonWithTag, result);
}
[TestCase(true)]
[TestCase(false)]
public void SerializesInsightWithoutTag(bool backwardsCompatible)
{
var serializedInsight = JsonConvert.DeserializeObject<SerializedInsight>(backwardsCompatible ? jsonWithoutTagBackwardsCompatible : jsonWithoutTag);
var insight = Insight.FromSerializedInsight(serializedInsight);
var result = JsonConvert.SerializeObject(insight, Formatting.None, _serializerSettings);
Assert.AreEqual(jsonWithoutTag, result);
}
[Test]
public void DeserializesInsightWithTag()
{
var jObject = JObject.Parse(jsonWithTagBackwardsCompatible);
var result = JsonConvert.DeserializeObject<Insight>(jsonWithTagBackwardsCompatible);
Assert.AreEqual(jObject["tag"].Value<string>(), result.Tag);
}
[Test]
public void DeserializesInsightWithoutTag()
{
var result = JsonConvert.DeserializeObject<Insight>(jsonWithoutTagBackwardsCompatible);
Assert.IsNull(result.Tag);
}
private string jsonNoScore2 = @"{""id"":""e02be50f56a8496b9ba995d19a904ada"",""groupId"":null,""sourceModel"":""mySourceModel-1"",""generatedTime"":1520711961.00055,
""createdTime"":1520711961.00055,""closeTime"":1520711961.00055,""symbol"":""BTCUSD XJ"",""ticker"":""BTCUSD"",""type"":""price"",""reference"":9143.53,""referenceValueFinal"":0.0,
""direction"":""up"",""period"":5.0,""magnitude"":""0.025"",""confidence"":null,""weight"":null,""scoreIsFinal"":false,""scoreMagnitude"":""0"",""scoreDirection"":""0"",
""estimatedValue"":""0"",""tag"":null}".ReplaceLineEndings(string.Empty);
private const string jsonNoScoreBackwardsCompatible =
"{" +
"\"id\":\"e02be50f56a8496b9ba995d19a904ada\"," +
"\"group-id\":null," +
"\"source-model\":\"mySourceModel-1\"," +
"\"generated-time\":1520711961.00055," +
"\"created-time\":1520711961.00055," +
"\"close-time\":1520711961.00055," +
"\"symbol\":\"BTCUSD XJ\"," +
"\"ticker\":\"BTCUSD\"," +
"\"type\":\"price\"," +
"\"reference\":9143.53," +
"\"reference-final\":0.0," +
"\"direction\":\"up\"," +
"\"period\":5.0," +
"\"magnitude\":\"0.025\"," +
"\"confidence\":null," +
"\"weight\":null," +
"\"score-final\":false," +
"\"score-magnitude\":\"0\"," +
"\"score-direction\":\"0\"," +
"\"estimated-value\":\"0\"," +
"\"tag\":null}";
private string jsonWithScore = @"{""id"":""e02be50f56a8496b9ba995d19a904ada"",""groupId"":""a02be50f56a8496b9ba995d19a904ada"",""sourceModel"":""mySourceModel-1"",
""generatedTime"":1520711961.00055,""createdTime"":1520711961.00055,""closeTime"":1520711961.00055,""symbol"":""BTCUSD XJ"",""ticker"":""BTCUSD"",""type"":""price"",
""reference"":9143.53,""referenceValueFinal"":9243.53,""direction"":""up"",""period"":5.0,""magnitude"":""0.025"",""confidence"":null,""weight"":null,
""scoreIsFinal"":true,""scoreMagnitude"":""1"",""scoreDirection"":""1"",""estimatedValue"":""1113.2484"",""tag"":null}".ReplaceLineEndings(string.Empty);
private const string jsonWithScoreBackwardsCompatible =
"{" +
"\"id\":\"e02be50f56a8496b9ba995d19a904ada\"," +
"\"group-id\":\"a02be50f56a8496b9ba995d19a904ada\"," +
"\"source-model\":\"mySourceModel-1\"," +
"\"generated-time\":1520711961.00055," +
"\"created-time\":1520711961.00055," +
"\"close-time\":1520711961.00055," +
"\"symbol\":\"BTCUSD XJ\"," +
"\"ticker\":\"BTCUSD\"," +
"\"type\":\"price\"," +
"\"reference\":9143.53," +
"\"reference-final\":9243.53," +
"\"direction\":\"up\"," +
"\"period\":5.0," +
"\"magnitude\":\"0.025\"," +
"\"confidence\":null," +
"\"weight\":null," +
"\"score-final\":true," +
"\"score-magnitude\":\"1\"," +
"\"score-direction\":\"1\"," +
"\"estimated-value\":\"1113.2484\"," +
"\"tag\":null}";
private const string jsonWithMissingCreatedTimeBackwardsCompatible =
"{" +
"\"id\":\"e02be50f56a8496b9ba995d19a904ada\"," +
"\"group-id\":\"a02be50f56a8496b9ba995d19a904ada\"," +
"\"source-model\":\"mySourceModel-1\"," +
"\"generated-time\":1520711961.00055," +
"\"close-time\":1520711961.00055," +
"\"symbol\":\"BTCUSD XJ\"," +
"\"ticker\":\"BTCUSD\"," +
"\"type\":\"price\"," +
"\"reference\":9143.53," +
"\"reference-final\":9243.53," +
"\"direction\":\"up\"," +
"\"period\":5.0," +
"\"magnitude\":0.025," +
"\"confidence\":null," +
"\"weight\":null," +
"\"score-final\":true," +
"\"score-magnitude\":\"1\"," +
"\"score-direction\":\"1\"," +
"\"estimated-value\":\"1113.2484\"," +
"\"tag\":null}";
private string jsonWithExpectedOutputFromMissingCreatedTimeValue = @"{""id"":""e02be50f56a8496b9ba995d19a904ada"",""groupId"":""a02be50f56a8496b9ba995d19a904ada"",
""sourceModel"":""mySourceModel-1"",""generatedTime"":1520711961.00055,""createdTime"":1520711961.00055,""closeTime"":1520711961.00055,""symbol"":""BTCUSD XJ"",""ticker"":
""BTCUSD"",""type"":""price"",""reference"":9143.53,""referenceValueFinal"":9243.53,""direction"":""up"",""period"":5.0,""magnitude"":""0.025"",""confidence"":null,
""weight"":null,""scoreIsFinal"":true,""scoreMagnitude"":""1"",""scoreDirection"":""1"",""estimatedValue"":""1113.2484"",""tag"":null}".ReplaceLineEndings(string.Empty);
private string jsonWithTag = @"{""id"":""e02be50f56a8496b9ba995d19a904ada"",""groupId"":""a02be50f56a8496b9ba995d19a904ada"",""sourceModel"":""mySourceModel-1"",
""generatedTime"":1520711961.00055,""createdTime"":1520711961.00055,""closeTime"":1520711961.00055,""symbol"":""BTCUSD XJ"",""ticker"":""BTCUSD"",""type"":
""price"",""reference"":9143.53,""referenceValueFinal"":9243.53,""direction"":""up"",""period"":5.0,""magnitude"":null,""confidence"":null,""weight"":null,
""scoreIsFinal"":true,""scoreMagnitude"":""1"",""scoreDirection"":""1"",""estimatedValue"":""1113.2484"",""tag"":""additional information""}".ReplaceLineEndings(string.Empty);
private const string jsonWithTagBackwardsCompatible =
"{" +
"\"id\":\"e02be50f56a8496b9ba995d19a904ada\"," +
"\"group-id\":\"a02be50f56a8496b9ba995d19a904ada\"," +
"\"source-model\":\"mySourceModel-1\"," +
"\"generated-time\":1520711961.00055," +
"\"created-time\":1520711961.00055," +
"\"close-time\":1520711961.00055," +
"\"symbol\":\"BTCUSD XJ\"," +
"\"ticker\":\"BTCUSD\"," +
"\"type\":\"price\"," +
"\"reference\":9143.53," +
"\"reference-final\":9243.53," +
"\"direction\":\"up\"," +
"\"period\":5.0," +
"\"magnitude\":null," +
"\"confidence\":null," +
"\"weight\":null," +
"\"score-final\":true," +
"\"score-magnitude\":\"1\"," +
"\"score-direction\":\"1\"," +
"\"estimated-value\":\"1113.2484\"," +
"\"tag\":\"additional information\"}";
private string jsonWithoutTag = @"{""id"":""e02be50f56a8496b9ba995d19a904ada"",""groupId"":""a02be50f56a8496b9ba995d19a904ada"",
""sourceModel"":""mySourceModel-1"",""generatedTime"":1520711961.00055,""createdTime"":1520711961.00055,""closeTime"":1520711961.00055,""symbol"":""BTCUSD XJ"",
""ticker"":""BTCUSD"",""type"":""price"",""reference"":9143.53,""referenceValueFinal"":9243.53,""direction"":""up"",""period"":5.0,""magnitude"":null,
""confidence"":null,""weight"":null,""scoreIsFinal"":true,""scoreMagnitude"":""1"",""scoreDirection"":""1"",""estimatedValue"":""1113.2484"",""tag"":null}".ReplaceLineEndings(string.Empty);
private const string jsonWithoutTagBackwardsCompatible =
"{" +
"\"id\":\"e02be50f56a8496b9ba995d19a904ada\"," +
"\"group-id\":\"a02be50f56a8496b9ba995d19a904ada\"," +
"\"source-model\":\"mySourceModel-1\"," +
"\"generated-time\":1520711961.00055," +
"\"created-time\":1520711961.00055," +
"\"close-time\":1520711961.00055," +
"\"symbol\":\"BTCUSD XJ\"," +
"\"ticker\":\"BTCUSD\"," +
"\"type\":\"price\"," +
"\"reference\":9143.53," +
"\"reference-final\":9243.53," +
"\"direction\":\"up\"," +
"\"period\":5.0," +
"\"magnitude\":null," +
"\"confidence\":null," +
"\"weight\":null," +
"\"score-final\":true," +
"\"score-magnitude\":\"1\"," +
"\"score-direction\":\"1\"," +
"\"estimated-value\":\"1113.2484\"," +
"\"tag\":null}";
}
}
@@ -0,0 +1,32 @@
/*
* 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 QuantConnect.Algorithm.Framework.Alphas;
using System.Collections.Generic;
namespace QuantConnect.Tests.Algorithm.Framework.Alphas
{
class TestEmaCrossAlphaModel : EmaCrossAlphaModel
{
/// <summary>
/// Get the _symbolDataBySymbol dictionary from EmaCrossAlphaModel
/// </summary>
/// <returns>_symbolDataBySymbol dictionary from EmaCrossAlphaModel</returns>
public Dictionary<Symbol, SymbolData> GetSymbolData()
{
return SymbolDataBySymbol;
}
}
}
@@ -0,0 +1,32 @@
/*
* 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 QuantConnect.Algorithm.Framework.Alphas;
using System.Collections.Generic;
namespace QuantConnect.Tests.Algorithm.Framework.Alphas
{
class TestMacdAlphaModel: MacdAlphaModel
{
/// <summary>
/// Get the _symbolData dictionary from MacdAlphaModel
/// </summary>
/// <returns>_symbolData dictionary from MacdAlphaModel</returns>
public Dictionary<Symbol, SymbolData> GetSymbolData()
{
return _symbolData;
}
}
}