diff --git a/Algorithm.Framework/Selection/FutureUniverseSelectionModel.cs b/Algorithm.Framework/Selection/FutureUniverseSelectionModel.cs
index aa8ba1fc2dcd..144039370c81 100644
--- a/Algorithm.Framework/Selection/FutureUniverseSelectionModel.cs
+++ b/Algorithm.Framework/Selection/FutureUniverseSelectionModel.cs
@@ -103,4 +103,32 @@ protected virtual FutureFilterUniverse Filter(FutureFilterUniverse filter)
return filter;
}
}
+
+ ///
+ /// Provides an implementation of that subscribes to future chains
+ ///
+ public class FuturesUniverseSelectionModel : FutureUniverseSelectionModel
+ {
+ ///
+ /// Creates a new instance of
+ ///
+ /// Time interval between universe refreshes
+ /// Selects symbols from the provided future chain
+ public FuturesUniverseSelectionModel(TimeSpan refreshInterval, Func> futureChainSymbolSelector)
+ : base(refreshInterval, futureChainSymbolSelector)
+ {
+ }
+ ///
+ /// Creates a new instance of
+ ///
+ /// Time interval between universe refreshes
+ /// Selects symbols from the provided future chain
+ /// Universe settings define attributes of created subscriptions, such as their resolution and the minimum time in universe before they can be removed
+ public FuturesUniverseSelectionModel(TimeSpan refreshInterval,
+ Func> futureChainSymbolSelector,
+ UniverseSettings universeSettings)
+ : base(refreshInterval, futureChainSymbolSelector, universeSettings)
+ {
+ }
+ }
}
diff --git a/Algorithm/QCAlgorithm.cs b/Algorithm/QCAlgorithm.cs
index 8e216e9dab5f..a7079e1bee59 100644
--- a/Algorithm/QCAlgorithm.cs
+++ b/Algorithm/QCAlgorithm.cs
@@ -3362,6 +3362,24 @@ public OptionChains OptionChains(IEnumerable symbols, bool flatten = fal
return chains;
}
+ ///
+ /// Get the futures chain for the specified symbol at the current time ()
+ ///
+ ///
+ /// The symbol for which the futures chain is asked for.
+ /// It can be either the canonical future, a contract or an option symbol.
+ ///
+ ///
+ /// Whether to flatten the resulting data frame. Used from Python when accessing .
+ /// See
+ ///
+ /// The futures chain
+ [DocumentationAttribute(AddingData)]
+ public FuturesChain FutureChain(Symbol symbol, bool flatten = false)
+ {
+ return FuturesChain(symbol, flatten);
+ }
+
///
/// Get the futures chain for the specified symbol at the current time ()
///
@@ -3381,6 +3399,24 @@ public FuturesChain FuturesChain(Symbol symbol, bool flatten = false)
new FuturesChain(GetCanonicalFutureSymbol(symbol), Time.Date);
}
+ ///
+ /// Get the futures chains for the specified symbols at the current time ()
+ ///
+ ///
+ /// The symbols for which the futures chains are asked for.
+ /// It can be either the canonical future, a contract or an option symbol.
+ ///
+ ///
+ /// Whether to flatten the resulting data frame. Used from Python when accessing .
+ /// See
+ ///
+ /// The futures chains
+ [DocumentationAttribute(AddingData)]
+ public FuturesChains FutureChains(IEnumerable symbols, bool flatten = false)
+ {
+ return FuturesChains(symbols, flatten);
+ }
+
///
/// Get the futures chains for the specified symbols at the current time ()
///
diff --git a/Common/Exceptions/StackExceptionInterpreter.cs b/Common/Exceptions/StackExceptionInterpreter.cs
index 30f5fd04c9b9..04892d06f365 100644
--- a/Common/Exceptions/StackExceptionInterpreter.cs
+++ b/Common/Exceptions/StackExceptionInterpreter.cs
@@ -116,14 +116,16 @@ public string GetExceptionMessageHeader(Exception exception)
public static StackExceptionInterpreter CreateFromAssemblies(IEnumerable assemblies)
{
var interpreters =
- from assembly in assemblies
+ from assembly in assemblies.Where(x =>
+ (!x.FullName?.StartsWith("System.", StringComparison.InvariantCultureIgnoreCase) ?? false)
+ && (!x.FullName?.StartsWith("Microsoft.", StringComparison.InvariantCultureIgnoreCase) ?? false))
from type in assembly.GetTypes()
// ignore non-public and non-instantiable abstract types
where type.IsPublic && !type.IsAbstract
// type implements IExceptionInterpreter
where typeof(IExceptionInterpreter).IsAssignableFrom(type)
// type is not mocked with MOQ library
- where type.FullName != null && !type.FullName.StartsWith("Castle.Proxies.ObjectProxy")
+ where type.FullName != null && !type.FullName.StartsWith("Castle.Proxies.ObjectProxy", StringComparison.InvariantCultureIgnoreCase)
// type has default parameterless ctor
where type.GetConstructor(new Type[0]) != null
// provide guarantee of deterministic ordering
diff --git a/Tests/AlgorithmRunner.cs b/Tests/AlgorithmRunner.cs
index 00229da733d3..93e3af09b91c 100644
--- a/Tests/AlgorithmRunner.cs
+++ b/Tests/AlgorithmRunner.cs
@@ -111,7 +111,11 @@ public static AlgorithmRunnerResults RunLocalBacktest(
var initialLogHandler = Log.LogHandler;
var initialDebugEnabled = Log.DebuggingEnabled;
- var newLogHandlers = new List() { MaintainLogHandlerAttribute.LogHandler };
+ var newLogHandlers = new List();
+ if (MaintainLogHandlerAttribute.LogHandler != null)
+ {
+ newLogHandlers.Add(MaintainLogHandlerAttribute.LogHandler);
+ }
// Use our current test LogHandler and a FileLogHandler
if (!reducedDiskSize)
{
@@ -190,30 +194,7 @@ public static AlgorithmRunnerResults RunLocalBacktest(
}
}
- if (algorithmManager?.State != expectedFinalStatus)
- {
- Assert.Fail($"Algorithm state should be {expectedFinalStatus} and is: {algorithmManager?.State}");
- }
-
- foreach (var expectedStat in expectedStatistics)
- {
- string result;
- Assert.IsTrue(statistics.TryGetValue(expectedStat.Key, out result), "Missing key: " + expectedStat.Key);
-
- // normalize -0 & 0, they are the same thing
- var expected = expectedStat.Value;
- if (expected == "-0")
- {
- expected = "0";
- }
-
- if (result == "-0")
- {
- result = "0";
- }
-
- Assert.AreEqual(expected, result, "Failed on " + expectedStat.Key);
- }
+ AssertAlgorithmState(expectedFinalStatus, algorithmManager?.State, expectedStatistics, statistics);
if (!reducedDiskSize)
{
@@ -272,5 +253,40 @@ public override IEnumerable GetHistory(IEnumerable reques
public class TestWorkerThread : WorkerThread
{
}
+
+ public static void AssertAlgorithmState(AlgorithmStatus expectedFinalStatus, AlgorithmStatus? actualState,
+ IDictionary expectedStatistics, IDictionary statistics)
+ {
+ if (actualState != expectedFinalStatus)
+ {
+ Assert.Fail($"Algorithm state should be {expectedFinalStatus} and is: {actualState}");
+ }
+
+ foreach (var expectedStat in expectedStatistics)
+ {
+ if (statistics == null)
+ {
+ Assert.Fail("Algorithm statistics are null");
+ break;
+ }
+
+ string result;
+ Assert.IsTrue(statistics.TryGetValue(expectedStat.Key, out result), "Missing key: " + expectedStat.Key);
+
+ // normalize -0 & 0, they are the same thing
+ var expected = expectedStat.Value;
+ if (expected == "-0")
+ {
+ expected = "0";
+ }
+
+ if (result == "-0")
+ {
+ result = "0";
+ }
+
+ Assert.AreEqual(expected, result, "Failed on " + expectedStat.Key);
+ }
+ }
}
}
diff --git a/Tests/Api/ApiTestBase.cs b/Tests/Api/ApiTestBase.cs
index b90d2cfecab1..218f276eeae3 100644
--- a/Tests/Api/ApiTestBase.cs
+++ b/Tests/Api/ApiTestBase.cs
@@ -104,7 +104,7 @@ private void CreateTestProjectAndBacktest()
return;
}
Log.Debug("ApiTestBase.Setup(): Waiting for test backtest to complete");
- TestBacktest = WaitForBacktestCompletion(TestProject.ProjectId, backtest.BacktestId);
+ TestBacktest = WaitForBacktestCompletion(ApiClient, TestProject.ProjectId, backtest.BacktestId);
if (!TestBacktest.Success)
{
Assert.Warn("Could not create backtest for the test project, tests using it will fail.");
@@ -151,7 +151,7 @@ public static bool IsValidJson(string jsonString)
/// Id of the project
/// Id of the compilation of the project
///
- protected static Compile WaitForCompilerResponse(Api.Api apiClient, int projectId, string compileId, int seconds = 60)
+ public static Compile WaitForCompilerResponse(Api.Api apiClient, int projectId, string compileId, int seconds = 60)
{
var compile = new Compile();
var finish = DateTime.UtcNow.AddSeconds(seconds);
@@ -159,7 +159,7 @@ protected static Compile WaitForCompilerResponse(Api.Api apiClient, int projectI
{
Thread.Sleep(100);
compile = apiClient.ReadCompile(projectId, compileId);
- } while (compile.State != CompileState.BuildSuccess && DateTime.UtcNow < finish);
+ } while (compile.State == CompileState.InQueue && DateTime.UtcNow < finish);
return compile;
}
@@ -170,14 +170,18 @@ protected static Compile WaitForCompilerResponse(Api.Api apiClient, int projectI
/// Project id to scan
/// Backtest id previously started
/// Completed backtest object
- protected Backtest WaitForBacktestCompletion(int projectId, string backtestId)
+ public static Backtest WaitForBacktestCompletion(Api.Api apiClient, int projectId, string backtestId, int secondsTimeout = 60)
{
Backtest backtest;
- var finish = DateTime.UtcNow.AddSeconds(60);
+ var finish = DateTime.UtcNow.AddSeconds(secondsTimeout);
do
{
Thread.Sleep(1000);
- backtest = ApiClient.ReadBacktest(projectId, backtestId);
+ backtest = apiClient.ReadBacktest(projectId, backtestId);
+ if (backtest != null && (!string.IsNullOrEmpty(backtest.Error) || backtest.HasInitializeError))
+ {
+ Assert.Fail($"Backtest {projectId}/{backtestId} failed: {backtest.Error}. Stacktrace: {backtest.Stacktrace}. Api errors: {string.Join(",", backtest.Errors)}");
+ }
} while (backtest.Success && backtest.Progress < 1 && DateTime.UtcNow < finish);
return backtest;
diff --git a/Tests/Api/OptimizationTests.cs b/Tests/Api/OptimizationTests.cs
index 70f53cde84fa..3a1fb4ef5a99 100644
--- a/Tests/Api/OptimizationTests.cs
+++ b/Tests/Api/OptimizationTests.cs
@@ -230,7 +230,7 @@ private int GetProjectCompiledAndWithBacktest(out Compile compile)
var backtest = ApiClient.CreateBacktest(projectId, compile.CompileId, backtestName);
// Now wait until the backtest is completed and request the orders again
- var backtestReady = WaitForBacktestCompletion(projectId, backtest.BacktestId);
+ var backtestReady = WaitForBacktestCompletion(ApiClient, projectId, backtest.BacktestId);
Assert.IsTrue(backtestReady.Success);
return projectId;
diff --git a/Tests/Api/ProjectTests.cs b/Tests/Api/ProjectTests.cs
index d8c704398f9c..6e9dd01fec63 100644
--- a/Tests/Api/ProjectTests.cs
+++ b/Tests/Api/ProjectTests.cs
@@ -283,7 +283,7 @@ private void Perform_CreateCompileBackTest_Tests(string projectName, Language la
Assert.IsTrue(backtest.Success);
// Now read the backtest and wait for it to complete
- var backtestRead = WaitForBacktestCompletion(project.Projects.First().ProjectId, backtest.BacktestId);
+ var backtestRead = WaitForBacktestCompletion(ApiClient, project.Projects.First().ProjectId, backtest.BacktestId);
Assert.IsTrue(backtestRead.Success);
Assert.AreEqual(1, backtestRead.Progress);
Assert.AreEqual(backtestName, backtestRead.Name);
@@ -355,7 +355,7 @@ public void ReadBacktestOrdersReportAndChart()
Assert.IsTrue(backtestRead.Success);
// Now wait until the backtest is completed and request the orders again
- backtestRead = WaitForBacktestCompletion(project.ProjectId, backtest.BacktestId);
+ backtestRead = WaitForBacktestCompletion(ApiClient, project.ProjectId, backtest.BacktestId);
var backtestOrdersRead = ApiClient.ReadBacktestOrders(project.ProjectId, backtest.BacktestId);
string stringRepresentation;
foreach(var backtestOrder in backtestOrdersRead)
@@ -684,7 +684,7 @@ public void CreatesOptimization()
var backtest = ApiClient.CreateBacktest(projectId, compile.CompileId, backtestName);
// Now wait until the backtest is completed and request the orders again
- var backtestReady = WaitForBacktestCompletion(projectId, backtest.BacktestId);
+ var backtestReady = WaitForBacktestCompletion(ApiClient, projectId, backtest.BacktestId);
Assert.IsTrue(backtestReady.Success);
var optimization = ApiClient.CreateOptimization(
diff --git a/Tests/Optimizer/Models.cs b/Tests/Optimizer/Models.cs
index ab68284dc92b..a0f03b0833de 100644
--- a/Tests/Optimizer/Models.cs
+++ b/Tests/Optimizer/Models.cs
@@ -1,4 +1,4 @@
-/*
+/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
@@ -19,7 +19,7 @@
namespace QuantConnect.Tests.Optimizer
{
- public class BacktestResult
+ internal class BacktestResult
{
private static JsonSerializerSettings _jsonSettings = new JsonSerializerSettings { Culture = CultureInfo.InvariantCulture, NullValueHandling = NullValueHandling.Ignore};
public Statistics Statistics { get; set; }
diff --git a/Tests/RegressionTestMessageHandler.cs b/Tests/RegressionTestMessageHandler.cs
index 83e22a1ccf84..4623d596bb9b 100644
--- a/Tests/RegressionTestMessageHandler.cs
+++ b/Tests/RegressionTestMessageHandler.cs
@@ -64,7 +64,7 @@ public override void Send(Packet packet)
{
if (_updateRegressionStatistics && _job.Language == Language.CSharp)
{
- UpdateRegressionStatisticsInSourceFile(result);
+ UpdateRegressionStatisticsInSourceFile(result.Results.Statistics, _algorithmManager, "../../../Algorithm.CSharp", $"{_job.AlgorithmId}.cs");
}
}
break;
@@ -74,9 +74,9 @@ public override void Send(Packet packet)
}
}
- private void UpdateRegressionStatisticsInSourceFile(BacktestResultPacket result)
+ public static void UpdateRegressionStatisticsInSourceFile(IDictionary statistics, AlgorithmManager algorithmManager, string folder, string fileToUpdate)
{
- var algorithmSource = Directory.EnumerateFiles("../../../Algorithm.CSharp", $"{_job.AlgorithmId}.cs", SearchOption.AllDirectories).Single();
+ var algorithmSource = Directory.EnumerateFiles(folder, fileToUpdate, SearchOption.AllDirectories).Single();
var file = File.ReadAllLines(algorithmSource).ToList().GetEnumerator();
var lines = new List();
while (file.MoveNext())
@@ -90,7 +90,7 @@ private void UpdateRegressionStatisticsInSourceFile(BacktestResultPacket result)
if (line.Contains("Dictionary ExpectedStatistics => new Dictionary")
|| line.Contains("Dictionary ExpectedStatistics => new()"))
{
- if (!result.Results.Statistics.Any() || line.EndsWith("();"))
+ if (!statistics.Any() || line.EndsWith("();"))
{
lines.Add(line);
continue;
@@ -99,7 +99,7 @@ private void UpdateRegressionStatisticsInSourceFile(BacktestResultPacket result)
lines.Add(line);
lines.Add(" {");
- foreach (var pair in result.Results.Statistics)
+ foreach (var pair in statistics)
{
lines.Add($" {{\"{pair.Key}\", \"{pair.Value}\"}},");
}
@@ -127,7 +127,7 @@ private void UpdateRegressionStatisticsInSourceFile(BacktestResultPacket result)
}
else
{
- lines.Add(GetDataPointLine(line, _algorithmManager?.DataPoints.ToString()));
+ lines.Add(GetDataPointLine(line, algorithmManager?.DataPoints.ToString()));
}
}
else if (line.Contains($"int AlgorithmHistoryDataPoints =>"))
@@ -138,12 +138,12 @@ private void UpdateRegressionStatisticsInSourceFile(BacktestResultPacket result)
}
else
{
- lines.Add(GetDataPointLine(line, _algorithmManager?.AlgorithmHistoryDataPoints.ToString()));
+ lines.Add(GetDataPointLine(line, algorithmManager?.AlgorithmHistoryDataPoints.ToString()));
}
}
else if (line.Contains($"AlgorithmStatus AlgorithmStatus =>"))
{
- lines.Add(GetAlgorithmStatusLine(line, _algorithmManager?.State.ToString()));
+ lines.Add(GetAlgorithmStatusLine(line, algorithmManager?.State.ToString()));
}
else
{
diff --git a/Tests/RegressionTests.cs b/Tests/RegressionTests.cs
index 59803f419500..f03537dec8e9 100644
--- a/Tests/RegressionTests.cs
+++ b/Tests/RegressionTests.cs
@@ -27,7 +27,7 @@ namespace QuantConnect.Tests
[TestFixture, Category("TravisExclude"), Category("RegressionTests")]
public class RegressionTests
{
- [Test, TestCaseSource(nameof(GetRegressionTestParameters))]
+ [Test, TestCaseSource(nameof(GetLocalRegressionTestParameters))]
public void AlgorithmStatisticsRegression(AlgorithmStatisticsTestParameters parameters)
{
// ensure we start with a fresh config every time when running multiple tests
@@ -78,7 +78,17 @@ public void AlgorithmStatisticsRegression(AlgorithmStatisticsTestParameters para
}
}
- private static TestCaseData[] GetRegressionTestParameters()
+ public static TestCaseData[] GetLocalRegressionTestParameters()
+ {
+ return GetRegressionTestParameters(canRunLocally: true,
+ (instance, language) => new AlgorithmStatisticsTestParameters(instance.GetType().Name, instance.ExpectedStatistics, language,
+ instance.AlgorithmStatus, instance.DataPoints, instance.AlgorithmHistoryDataPoints));
+ }
+
+ public static TestCaseData[] GetRegressionTestParameters(bool canRunLocally, Func factory)
+ where T : IRegressionAlgorithmDefinition
+ where K : AlgorithmStatisticsTestParameters
+ where J : class
{
TestGlobals.Initialize();
@@ -91,14 +101,14 @@ private static TestCaseData[] GetRegressionTestParameters()
// find all regression algorithms in Algorithm.CSharp
return (
- from type in typeof(BasicTemplateAlgorithm).Assembly.GetTypes()
- where typeof(IRegressionAlgorithmDefinition).IsAssignableFrom(type)
+ from type in typeof(J).Assembly.GetTypes()
+ where typeof(T).IsAssignableFrom(type)
where !type.IsAbstract // non-abstract
where type.GetConstructor(Array.Empty()) != null // has default ctor
- let instance = (IRegressionAlgorithmDefinition)Activator.CreateInstance(type)
- where instance.CanRunLocally // open source has data to run this algorithm
+ let instance = (T)Activator.CreateInstance(type)
+ where instance.CanRunLocally == canRunLocally // open source has data to run this algorithm
from language in instance.Languages.Where(languages.Contains)
- select new AlgorithmStatisticsTestParameters(type.Name, instance.ExpectedStatistics, language, instance.AlgorithmStatus, instance.DataPoints, instance.AlgorithmHistoryDataPoints)
+ select factory(instance, language)
)
.OrderBy(x => x.Language).ThenBy(x => x.Algorithm)
// generate test cases from test parameters
diff --git a/mypy.ini b/mypy.ini
index d69ef0b693be..9ee13709de1b 100644
--- a/mypy.ini
+++ b/mypy.ini
@@ -1,4 +1,4 @@
# Global options:
[mypy]
-disable_error_code = var-annotated,has-type
\ No newline at end of file
+disable_error_code = var-annotated,has-type,union-attr
\ No newline at end of file