Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions Algorithm.Framework/Selection/FutureUniverseSelectionModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,32 @@ protected virtual FutureFilterUniverse Filter(FutureFilterUniverse filter)
return filter;
}
}

/// <summary>
/// Provides an implementation of <see cref="IUniverseSelectionModel"/> that subscribes to future chains
/// </summary>
public class FuturesUniverseSelectionModel : FutureUniverseSelectionModel
{
/// <summary>
/// Creates a new instance of <see cref="FutureUniverseSelectionModel"/>
/// </summary>
/// <param name="refreshInterval">Time interval between universe refreshes</param>
/// <param name="futureChainSymbolSelector">Selects symbols from the provided future chain</param>
public FuturesUniverseSelectionModel(TimeSpan refreshInterval, Func<DateTime, IEnumerable<Symbol>> futureChainSymbolSelector)
: base(refreshInterval, futureChainSymbolSelector)
{
}
/// <summary>
/// Creates a new instance of <see cref="FutureUniverseSelectionModel"/>
/// </summary>
/// <param name="refreshInterval">Time interval between universe refreshes</param>
/// <param name="futureChainSymbolSelector">Selects symbols from the provided future chain</param>
/// <param name="universeSettings">Universe settings define attributes of created subscriptions, such as their resolution and the minimum time in universe before they can be removed</param>
public FuturesUniverseSelectionModel(TimeSpan refreshInterval,
Func<DateTime, IEnumerable<Symbol>> futureChainSymbolSelector,
UniverseSettings universeSettings)
: base(refreshInterval, futureChainSymbolSelector, universeSettings)
{
}
}
}
36 changes: 36 additions & 0 deletions Algorithm/QCAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3362,6 +3362,24 @@ public OptionChains OptionChains(IEnumerable<Symbol> symbols, bool flatten = fal
return chains;
}

/// <summary>
/// Get the futures chain for the specified symbol at the current time (<see cref="Time"/>)
/// </summary>
/// <param name="symbol">
/// The symbol for which the futures chain is asked for.
/// It can be either the canonical future, a contract or an option symbol.
/// </param>
/// <param name="flatten">
/// Whether to flatten the resulting data frame. Used from Python when accessing <see cref="FuturesChain.DataFrame"/>.
/// See <see cref="History(PyObject, int, Resolution?, bool?, bool?, DataMappingMode?, DataNormalizationMode?, int?, bool)"/>
/// </param>
/// <returns>The futures chain</returns>
[DocumentationAttribute(AddingData)]
public FuturesChain FutureChain(Symbol symbol, bool flatten = false)
{
return FuturesChain(symbol, flatten);
}

/// <summary>
/// Get the futures chain for the specified symbol at the current time (<see cref="Time"/>)
/// </summary>
Expand All @@ -3381,6 +3399,24 @@ public FuturesChain FuturesChain(Symbol symbol, bool flatten = false)
new FuturesChain(GetCanonicalFutureSymbol(symbol), Time.Date);
}

/// <summary>
/// Get the futures chains for the specified symbols at the current time (<see cref="Time"/>)
/// </summary>
/// <param name="symbols">
/// The symbols for which the futures chains are asked for.
/// It can be either the canonical future, a contract or an option symbol.
/// </param>
/// <param name="flatten">
/// Whether to flatten the resulting data frame. Used from Python when accessing <see cref="FuturesChains.DataFrame"/>.
/// See <see cref="History(PyObject, int, Resolution?, bool?, bool?, DataMappingMode?, DataNormalizationMode?, int?, bool)"/>
/// </param>
/// <returns>The futures chains</returns>
[DocumentationAttribute(AddingData)]
public FuturesChains FutureChains(IEnumerable<Symbol> symbols, bool flatten = false)
{
return FuturesChains(symbols, flatten);
}

/// <summary>
/// Get the futures chains for the specified symbols at the current time (<see cref="Time"/>)
/// </summary>
Expand Down
6 changes: 4 additions & 2 deletions Common/Exceptions/StackExceptionInterpreter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,16 @@ public string GetExceptionMessageHeader(Exception exception)
public static StackExceptionInterpreter CreateFromAssemblies(IEnumerable<Assembly> 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
Expand Down
66 changes: 41 additions & 25 deletions Tests/AlgorithmRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,11 @@ public static AlgorithmRunnerResults RunLocalBacktest(
var initialLogHandler = Log.LogHandler;
var initialDebugEnabled = Log.DebuggingEnabled;

var newLogHandlers = new List<ILogHandler>() { MaintainLogHandlerAttribute.LogHandler };
var newLogHandlers = new List<ILogHandler>();
if (MaintainLogHandlerAttribute.LogHandler != null)
{
newLogHandlers.Add(MaintainLogHandlerAttribute.LogHandler);
}
// Use our current test LogHandler and a FileLogHandler
if (!reducedDiskSize)
{
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -272,5 +253,40 @@ public override IEnumerable<Slice> GetHistory(IEnumerable<HistoryRequest> reques
public class TestWorkerThread : WorkerThread
{
}

public static void AssertAlgorithmState(AlgorithmStatus expectedFinalStatus, AlgorithmStatus? actualState,
IDictionary<string, string> expectedStatistics, IDictionary<string, string> 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);
}
}
}
}
16 changes: 10 additions & 6 deletions Tests/Api/ApiTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
Expand Down Expand Up @@ -151,15 +151,15 @@ public static bool IsValidJson(string jsonString)
/// <param name="projectId">Id of the project</param>
/// <param name="compileId">Id of the compilation of the project</param>
/// <returns></returns>
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);
do
{
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;
}
Expand All @@ -170,14 +170,18 @@ protected static Compile WaitForCompilerResponse(Api.Api apiClient, int projectI
/// <param name="projectId">Project id to scan</param>
/// <param name="backtestId">Backtest id previously started</param>
/// <returns>Completed backtest object</returns>
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;
Expand Down
2 changes: 1 addition & 1 deletion Tests/Api/OptimizationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions Tests/Api/ProjectTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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(
Expand Down
4 changes: 2 additions & 2 deletions Tests/Optimizer/Models.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
Expand All @@ -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; }
Expand Down
16 changes: 8 additions & 8 deletions Tests/RegressionTestMessageHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -74,9 +74,9 @@ public override void Send(Packet packet)
}
}

private void UpdateRegressionStatisticsInSourceFile(BacktestResultPacket result)
public static void UpdateRegressionStatisticsInSourceFile(IDictionary<string, string> 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<string>();
while (file.MoveNext())
Expand All @@ -90,7 +90,7 @@ private void UpdateRegressionStatisticsInSourceFile(BacktestResultPacket result)
if (line.Contains("Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>")
|| line.Contains("Dictionary<string, string> ExpectedStatistics => new()"))
{
if (!result.Results.Statistics.Any() || line.EndsWith("();"))
if (!statistics.Any() || line.EndsWith("();"))
{
lines.Add(line);
continue;
Expand All @@ -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}\"}},");
}
Expand Down Expand Up @@ -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 =>"))
Expand All @@ -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
{
Expand Down
Loading
Loading