Skip to content

Commit bcdc04d

Browse files
Minor improvements for LLMs
- Minor tweaks in regression tests - Minor API sugars
1 parent 2cafba8 commit bcdc04d

File tree

10 files changed

+150
-54
lines changed

10 files changed

+150
-54
lines changed

Algorithm.Framework/Selection/FutureUniverseSelectionModel.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,32 @@ protected virtual FutureFilterUniverse Filter(FutureFilterUniverse filter)
103103
return filter;
104104
}
105105
}
106+
107+
/// <summary>
108+
/// Provides an implementation of <see cref="IUniverseSelectionModel"/> that subscribes to future chains
109+
/// </summary>
110+
public class FuturesUniverseSelectionModel : FutureUniverseSelectionModel
111+
{
112+
/// <summary>
113+
/// Creates a new instance of <see cref="FutureUniverseSelectionModel"/>
114+
/// </summary>
115+
/// <param name="refreshInterval">Time interval between universe refreshes</param>
116+
/// <param name="futureChainSymbolSelector">Selects symbols from the provided future chain</param>
117+
public FuturesUniverseSelectionModel(TimeSpan refreshInterval, Func<DateTime, IEnumerable<Symbol>> futureChainSymbolSelector)
118+
: base(refreshInterval, futureChainSymbolSelector)
119+
{
120+
}
121+
/// <summary>
122+
/// Creates a new instance of <see cref="FutureUniverseSelectionModel"/>
123+
/// </summary>
124+
/// <param name="refreshInterval">Time interval between universe refreshes</param>
125+
/// <param name="futureChainSymbolSelector">Selects symbols from the provided future chain</param>
126+
/// <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>
127+
public FuturesUniverseSelectionModel(TimeSpan refreshInterval,
128+
Func<DateTime, IEnumerable<Symbol>> futureChainSymbolSelector,
129+
UniverseSettings universeSettings)
130+
: base(refreshInterval, futureChainSymbolSelector, universeSettings)
131+
{
132+
}
133+
}
106134
}

Algorithm/QCAlgorithm.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3362,6 +3362,24 @@ public OptionChains OptionChains(IEnumerable<Symbol> symbols, bool flatten = fal
33623362
return chains;
33633363
}
33643364

3365+
/// <summary>
3366+
/// Get the futures chain for the specified symbol at the current time (<see cref="Time"/>)
3367+
/// </summary>
3368+
/// <param name="symbol">
3369+
/// The symbol for which the futures chain is asked for.
3370+
/// It can be either the canonical future, a contract or an option symbol.
3371+
/// </param>
3372+
/// <param name="flatten">
3373+
/// Whether to flatten the resulting data frame. Used from Python when accessing <see cref="FuturesChain.DataFrame"/>.
3374+
/// See <see cref="History(PyObject, int, Resolution?, bool?, bool?, DataMappingMode?, DataNormalizationMode?, int?, bool)"/>
3375+
/// </param>
3376+
/// <returns>The futures chain</returns>
3377+
[DocumentationAttribute(AddingData)]
3378+
public FuturesChain FutureChain(Symbol symbol, bool flatten = false)
3379+
{
3380+
return FuturesChain(symbol, flatten);
3381+
}
3382+
33653383
/// <summary>
33663384
/// Get the futures chain for the specified symbol at the current time (<see cref="Time"/>)
33673385
/// </summary>
@@ -3381,6 +3399,24 @@ public FuturesChain FuturesChain(Symbol symbol, bool flatten = false)
33813399
new FuturesChain(GetCanonicalFutureSymbol(symbol), Time.Date);
33823400
}
33833401

3402+
/// <summary>
3403+
/// Get the futures chains for the specified symbols at the current time (<see cref="Time"/>)
3404+
/// </summary>
3405+
/// <param name="symbols">
3406+
/// The symbols for which the futures chains are asked for.
3407+
/// It can be either the canonical future, a contract or an option symbol.
3408+
/// </param>
3409+
/// <param name="flatten">
3410+
/// Whether to flatten the resulting data frame. Used from Python when accessing <see cref="FuturesChains.DataFrame"/>.
3411+
/// See <see cref="History(PyObject, int, Resolution?, bool?, bool?, DataMappingMode?, DataNormalizationMode?, int?, bool)"/>
3412+
/// </param>
3413+
/// <returns>The futures chains</returns>
3414+
[DocumentationAttribute(AddingData)]
3415+
public FuturesChains FutureChains(IEnumerable<Symbol> symbols, bool flatten = false)
3416+
{
3417+
return FuturesChains(symbols, flatten);
3418+
}
3419+
33843420
/// <summary>
33853421
/// Get the futures chains for the specified symbols at the current time (<see cref="Time"/>)
33863422
/// </summary>

Common/Exceptions/StackExceptionInterpreter.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,14 +116,16 @@ public string GetExceptionMessageHeader(Exception exception)
116116
public static StackExceptionInterpreter CreateFromAssemblies(IEnumerable<Assembly> assemblies)
117117
{
118118
var interpreters =
119-
from assembly in assemblies
119+
from assembly in assemblies.Where(x =>
120+
(!x.FullName?.StartsWith("System.", StringComparison.InvariantCultureIgnoreCase) ?? false)
121+
&& (!x.FullName?.StartsWith("Microsoft.", StringComparison.InvariantCultureIgnoreCase) ?? false))
120122
from type in assembly.GetTypes()
121123
// ignore non-public and non-instantiable abstract types
122124
where type.IsPublic && !type.IsAbstract
123125
// type implements IExceptionInterpreter
124126
where typeof(IExceptionInterpreter).IsAssignableFrom(type)
125127
// type is not mocked with MOQ library
126-
where type.FullName != null && !type.FullName.StartsWith("Castle.Proxies.ObjectProxy")
128+
where type.FullName != null && !type.FullName.StartsWith("Castle.Proxies.ObjectProxy", StringComparison.InvariantCultureIgnoreCase)
127129
// type has default parameterless ctor
128130
where type.GetConstructor(new Type[0]) != null
129131
// provide guarantee of deterministic ordering

Tests/AlgorithmRunner.cs

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,11 @@ public static AlgorithmRunnerResults RunLocalBacktest(
111111
var initialLogHandler = Log.LogHandler;
112112
var initialDebugEnabled = Log.DebuggingEnabled;
113113

114-
var newLogHandlers = new List<ILogHandler>() { MaintainLogHandlerAttribute.LogHandler };
114+
var newLogHandlers = new List<ILogHandler>();
115+
if (MaintainLogHandlerAttribute.LogHandler != null)
116+
{
117+
newLogHandlers.Add(MaintainLogHandlerAttribute.LogHandler);
118+
}
115119
// Use our current test LogHandler and a FileLogHandler
116120
if (!reducedDiskSize)
117121
{
@@ -190,30 +194,7 @@ public static AlgorithmRunnerResults RunLocalBacktest(
190194
}
191195
}
192196

193-
if (algorithmManager?.State != expectedFinalStatus)
194-
{
195-
Assert.Fail($"Algorithm state should be {expectedFinalStatus} and is: {algorithmManager?.State}");
196-
}
197-
198-
foreach (var expectedStat in expectedStatistics)
199-
{
200-
string result;
201-
Assert.IsTrue(statistics.TryGetValue(expectedStat.Key, out result), "Missing key: " + expectedStat.Key);
202-
203-
// normalize -0 & 0, they are the same thing
204-
var expected = expectedStat.Value;
205-
if (expected == "-0")
206-
{
207-
expected = "0";
208-
}
209-
210-
if (result == "-0")
211-
{
212-
result = "0";
213-
}
214-
215-
Assert.AreEqual(expected, result, "Failed on " + expectedStat.Key);
216-
}
197+
AssertAlgorithmState(expectedFinalStatus, algorithmManager?.State, expectedStatistics, statistics);
217198

218199
if (!reducedDiskSize)
219200
{
@@ -272,5 +253,40 @@ public override IEnumerable<Slice> GetHistory(IEnumerable<HistoryRequest> reques
272253
public class TestWorkerThread : WorkerThread
273254
{
274255
}
256+
257+
public static void AssertAlgorithmState(AlgorithmStatus expectedFinalStatus, AlgorithmStatus? actualState,
258+
IDictionary<string, string> expectedStatistics, IDictionary<string, string> statistics)
259+
{
260+
if (actualState != expectedFinalStatus)
261+
{
262+
Assert.Fail($"Algorithm state should be {expectedFinalStatus} and is: {actualState}");
263+
}
264+
265+
foreach (var expectedStat in expectedStatistics)
266+
{
267+
if (statistics == null)
268+
{
269+
Assert.Fail("Algorithm statistics are null");
270+
break;
271+
}
272+
273+
string result;
274+
Assert.IsTrue(statistics.TryGetValue(expectedStat.Key, out result), "Missing key: " + expectedStat.Key);
275+
276+
// normalize -0 & 0, they are the same thing
277+
var expected = expectedStat.Value;
278+
if (expected == "-0")
279+
{
280+
expected = "0";
281+
}
282+
283+
if (result == "-0")
284+
{
285+
result = "0";
286+
}
287+
288+
Assert.AreEqual(expected, result, "Failed on " + expectedStat.Key);
289+
}
290+
}
275291
}
276292
}

Tests/Api/ApiTestBase.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ private void CreateTestProjectAndBacktest()
104104
return;
105105
}
106106
Log.Debug("ApiTestBase.Setup(): Waiting for test backtest to complete");
107-
TestBacktest = WaitForBacktestCompletion(TestProject.ProjectId, backtest.BacktestId);
107+
TestBacktest = WaitForBacktestCompletion(ApiClient, TestProject.ProjectId, backtest.BacktestId);
108108
if (!TestBacktest.Success)
109109
{
110110
Assert.Warn("Could not create backtest for the test project, tests using it will fail.");
@@ -151,15 +151,15 @@ public static bool IsValidJson(string jsonString)
151151
/// <param name="projectId">Id of the project</param>
152152
/// <param name="compileId">Id of the compilation of the project</param>
153153
/// <returns></returns>
154-
protected static Compile WaitForCompilerResponse(Api.Api apiClient, int projectId, string compileId, int seconds = 60)
154+
public static Compile WaitForCompilerResponse(Api.Api apiClient, int projectId, string compileId, int seconds = 60)
155155
{
156156
var compile = new Compile();
157157
var finish = DateTime.UtcNow.AddSeconds(seconds);
158158
do
159159
{
160160
Thread.Sleep(100);
161161
compile = apiClient.ReadCompile(projectId, compileId);
162-
} while (compile.State != CompileState.BuildSuccess && DateTime.UtcNow < finish);
162+
} while (compile.State == CompileState.InQueue && DateTime.UtcNow < finish);
163163

164164
return compile;
165165
}
@@ -170,14 +170,18 @@ protected static Compile WaitForCompilerResponse(Api.Api apiClient, int projectI
170170
/// <param name="projectId">Project id to scan</param>
171171
/// <param name="backtestId">Backtest id previously started</param>
172172
/// <returns>Completed backtest object</returns>
173-
protected Backtest WaitForBacktestCompletion(int projectId, string backtestId)
173+
public static Backtest WaitForBacktestCompletion(Api.Api apiClient, int projectId, string backtestId, int secondsTimeout = 60)
174174
{
175175
Backtest backtest;
176-
var finish = DateTime.UtcNow.AddSeconds(60);
176+
var finish = DateTime.UtcNow.AddSeconds(secondsTimeout);
177177
do
178178
{
179179
Thread.Sleep(1000);
180-
backtest = ApiClient.ReadBacktest(projectId, backtestId);
180+
backtest = apiClient.ReadBacktest(projectId, backtestId);
181+
if (backtest != null && (!string.IsNullOrEmpty(backtest.Error) || backtest.HasInitializeError))
182+
{
183+
Assert.Fail($"Backtest {projectId}/{backtestId} failed: {backtest.Error}. Stacktrace: {backtest.Stacktrace}. Api errors: {string.Join(",", backtest.Errors)}");
184+
}
181185
} while (backtest.Success && backtest.Progress < 1 && DateTime.UtcNow < finish);
182186

183187
return backtest;

Tests/Api/OptimizationTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ private int GetProjectCompiledAndWithBacktest(out Compile compile)
230230
var backtest = ApiClient.CreateBacktest(projectId, compile.CompileId, backtestName);
231231

232232
// Now wait until the backtest is completed and request the orders again
233-
var backtestReady = WaitForBacktestCompletion(projectId, backtest.BacktestId);
233+
var backtestReady = WaitForBacktestCompletion(ApiClient, projectId, backtest.BacktestId);
234234
Assert.IsTrue(backtestReady.Success);
235235

236236
return projectId;

Tests/Api/ProjectTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ private void Perform_CreateCompileBackTest_Tests(string projectName, Language la
283283
Assert.IsTrue(backtest.Success);
284284

285285
// Now read the backtest and wait for it to complete
286-
var backtestRead = WaitForBacktestCompletion(project.Projects.First().ProjectId, backtest.BacktestId);
286+
var backtestRead = WaitForBacktestCompletion(ApiClient, project.Projects.First().ProjectId, backtest.BacktestId);
287287
Assert.IsTrue(backtestRead.Success);
288288
Assert.AreEqual(1, backtestRead.Progress);
289289
Assert.AreEqual(backtestName, backtestRead.Name);
@@ -355,7 +355,7 @@ public void ReadBacktestOrdersReportAndChart()
355355
Assert.IsTrue(backtestRead.Success);
356356

357357
// Now wait until the backtest is completed and request the orders again
358-
backtestRead = WaitForBacktestCompletion(project.ProjectId, backtest.BacktestId);
358+
backtestRead = WaitForBacktestCompletion(ApiClient, project.ProjectId, backtest.BacktestId);
359359
var backtestOrdersRead = ApiClient.ReadBacktestOrders(project.ProjectId, backtest.BacktestId);
360360
string stringRepresentation;
361361
foreach(var backtestOrder in backtestOrdersRead)
@@ -684,7 +684,7 @@ public void CreatesOptimization()
684684
var backtest = ApiClient.CreateBacktest(projectId, compile.CompileId, backtestName);
685685

686686
// Now wait until the backtest is completed and request the orders again
687-
var backtestReady = WaitForBacktestCompletion(projectId, backtest.BacktestId);
687+
var backtestReady = WaitForBacktestCompletion(ApiClient, projectId, backtest.BacktestId);
688688
Assert.IsTrue(backtestReady.Success);
689689

690690
var optimization = ApiClient.CreateOptimization(

Tests/Optimizer/Models.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*
1+
/*
22
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
33
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
44
*
@@ -19,7 +19,7 @@
1919

2020
namespace QuantConnect.Tests.Optimizer
2121
{
22-
public class BacktestResult
22+
internal class BacktestResult
2323
{
2424
private static JsonSerializerSettings _jsonSettings = new JsonSerializerSettings { Culture = CultureInfo.InvariantCulture, NullValueHandling = NullValueHandling.Ignore};
2525
public Statistics Statistics { get; set; }

Tests/RegressionTestMessageHandler.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public override void Send(Packet packet)
6464
{
6565
if (_updateRegressionStatistics && _job.Language == Language.CSharp)
6666
{
67-
UpdateRegressionStatisticsInSourceFile(result);
67+
UpdateRegressionStatisticsInSourceFile(result.Results.Statistics, _algorithmManager, "../../../Algorithm.CSharp", $"{_job.AlgorithmId}.cs");
6868
}
6969
}
7070
break;
@@ -74,9 +74,9 @@ public override void Send(Packet packet)
7474
}
7575
}
7676

77-
private void UpdateRegressionStatisticsInSourceFile(BacktestResultPacket result)
77+
public static void UpdateRegressionStatisticsInSourceFile(IDictionary<string, string> statistics, AlgorithmManager algorithmManager, string folder, string fileToUpdate)
7878
{
79-
var algorithmSource = Directory.EnumerateFiles("../../../Algorithm.CSharp", $"{_job.AlgorithmId}.cs", SearchOption.AllDirectories).Single();
79+
var algorithmSource = Directory.EnumerateFiles(folder, fileToUpdate, SearchOption.AllDirectories).Single();
8080
var file = File.ReadAllLines(algorithmSource).ToList().GetEnumerator();
8181
var lines = new List<string>();
8282
while (file.MoveNext())
@@ -90,7 +90,7 @@ private void UpdateRegressionStatisticsInSourceFile(BacktestResultPacket result)
9090
if (line.Contains("Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>")
9191
|| line.Contains("Dictionary<string, string> ExpectedStatistics => new()"))
9292
{
93-
if (!result.Results.Statistics.Any() || line.EndsWith("();"))
93+
if (!statistics.Any() || line.EndsWith("();"))
9494
{
9595
lines.Add(line);
9696
continue;
@@ -99,7 +99,7 @@ private void UpdateRegressionStatisticsInSourceFile(BacktestResultPacket result)
9999
lines.Add(line);
100100
lines.Add(" {");
101101

102-
foreach (var pair in result.Results.Statistics)
102+
foreach (var pair in statistics)
103103
{
104104
lines.Add($" {{\"{pair.Key}\", \"{pair.Value}\"}},");
105105
}
@@ -127,7 +127,7 @@ private void UpdateRegressionStatisticsInSourceFile(BacktestResultPacket result)
127127
}
128128
else
129129
{
130-
lines.Add(GetDataPointLine(line, _algorithmManager?.DataPoints.ToString()));
130+
lines.Add(GetDataPointLine(line, algorithmManager?.DataPoints.ToString()));
131131
}
132132
}
133133
else if (line.Contains($"int AlgorithmHistoryDataPoints =>"))
@@ -138,12 +138,12 @@ private void UpdateRegressionStatisticsInSourceFile(BacktestResultPacket result)
138138
}
139139
else
140140
{
141-
lines.Add(GetDataPointLine(line, _algorithmManager?.AlgorithmHistoryDataPoints.ToString()));
141+
lines.Add(GetDataPointLine(line, algorithmManager?.AlgorithmHistoryDataPoints.ToString()));
142142
}
143143
}
144144
else if (line.Contains($"AlgorithmStatus AlgorithmStatus =>"))
145145
{
146-
lines.Add(GetAlgorithmStatusLine(line, _algorithmManager?.State.ToString()));
146+
lines.Add(GetAlgorithmStatusLine(line, algorithmManager?.State.ToString()));
147147
}
148148
else
149149
{

0 commit comments

Comments
 (0)