Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d94a1d09a4 | ||
|
|
2c843cae9e | ||
|
|
039fdf7e4a | ||
|
|
2c63546c37 | ||
|
|
58682e1bbd | ||
|
|
d11a375fdb | ||
|
|
6ab91a13e1 | ||
|
|
beaa705646 | ||
|
|
4c830c8235 | ||
|
|
395c1123da | ||
|
|
8e50645640 | ||
|
|
68ca504d3a | ||
|
|
12df1c9a31 |
@@ -28,11 +28,11 @@ namespace QuantConnect.Algorithm.CSharp.Benchmarks
|
||||
_symbol = AddEquity("SPY").Symbol;
|
||||
}
|
||||
|
||||
public override void OnEndOfDay()
|
||||
public override void OnEndOfDay(Symbol symbol)
|
||||
{
|
||||
var minuteHistory = History(_symbol, 60, Resolution.Minute);
|
||||
var minuteHistory = History(symbol, 60, Resolution.Minute);
|
||||
var lastHourHigh = minuteHistory.Select(minuteBar => minuteBar.High).DefaultIfEmpty(0).Max();
|
||||
var dailyHistory = History(_symbol, 1, Resolution.Daily).First();
|
||||
var dailyHistory = History(symbol, 1, Resolution.Daily).First();
|
||||
var dailyHigh = dailyHistory.High;
|
||||
var dailyLow = dailyHistory.Low;
|
||||
var dailyOpen = dailyHistory.Open;
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// OnEndOfDay Event Handler - At the end of each trading day we fire this code.
|
||||
/// To avoid flooding, we recommend running your plotting at the end of each day.
|
||||
/// </summary>
|
||||
public override void OnEndOfDay()
|
||||
public override void OnEndOfDay(Symbol symbol)
|
||||
{
|
||||
//Log the end of day prices:
|
||||
Plot("Trade Plot", "Price", _lastPrice);
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// End of a trading day event handler. This method is called at the end of the algorithm day (or multiple times if trading multiple assets).
|
||||
/// </summary>
|
||||
/// <remarks>Method is called 10 minutes before closing to allow user to close out position.</remarks>
|
||||
public override void OnEndOfDay()
|
||||
public override void OnEndOfDay(Symbol symbol)
|
||||
{
|
||||
Plot("Nifty Closing Price", _today.NiftyPrice);
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfDay()
|
||||
public override void OnEndOfDay(Symbol symbol)
|
||||
{
|
||||
Plot("Indicator Signal", "EOD", IsDownTrend ? -1 : IsUpTrend ? 1 : 0);
|
||||
}
|
||||
|
||||
@@ -158,8 +158,10 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Fire plotting events once per day.
|
||||
/// </summary>
|
||||
public override void OnEndOfDay()
|
||||
public override void OnEndOfDay(Symbol symbol)
|
||||
{
|
||||
if (symbol != _symbol) return;
|
||||
|
||||
if (!_indicators.BB.IsReady) return;
|
||||
|
||||
Plot("BB", "Price", _price);
|
||||
|
||||
@@ -151,7 +151,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// End of a trading day event handler. This method is called at the end of the algorithm day (or multiple times if trading multiple assets).
|
||||
/// </summary>
|
||||
/// <remarks>Method is called 10 minutes before closing to allow user to close out position.</remarks>
|
||||
public override void OnEndOfDay()
|
||||
public override void OnEndOfDay(Symbol symbol)
|
||||
{
|
||||
int i = 0;
|
||||
foreach (var kvp in Data.OrderBy(x => x.Value.Symbol))
|
||||
|
||||
@@ -30,7 +30,6 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
private int _onEndOfDaySpyCallCount;
|
||||
private int _onEndOfDayBacCallCount;
|
||||
private int _onEndOfDayIbmCallCount;
|
||||
private int _onEndOfDayCallCount;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
|
||||
@@ -55,14 +54,6 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obsolete overload to be removed.
|
||||
/// </summary>
|
||||
public override void OnEndOfDay()
|
||||
{
|
||||
_onEndOfDayCallCount++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We expect it to be called for the universe selected <see cref="Symbol"/>
|
||||
/// and the post initialize manually added equity <see cref="Symbol"/>
|
||||
@@ -115,10 +106,6 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
throw new Exception($"OnEndOfDay(IBM) unexpected count call {_onEndOfDayIbmCallCount}");
|
||||
}
|
||||
if (_onEndOfDayCallCount != 4)
|
||||
{
|
||||
throw new Exception($"OnEndOfDay() unexpected count call {_onEndOfDayCallCount}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -398,9 +398,9 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// If we're still invested by the end of the day, liquidate
|
||||
/// </summary>
|
||||
public override void OnEndOfDay()
|
||||
public override void OnEndOfDay(Symbol symbol)
|
||||
{
|
||||
if (Security.Invested)
|
||||
if (symbol == Security.Symbol && Security.Invested)
|
||||
{
|
||||
Liquidate();
|
||||
}
|
||||
|
||||
@@ -76,11 +76,14 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfDay()
|
||||
public override void OnEndOfDay(Symbol symbol)
|
||||
{
|
||||
Plot("Trade Plot", "UpperChannel", _rc.UpperChannel);
|
||||
Plot("Trade Plot", "LowerChannel", _rc.LowerChannel);
|
||||
Plot("Trade Plot", "Regression", _rc.LinearRegression);
|
||||
if (symbol == _spy)
|
||||
{
|
||||
Plot("Trade Plot", "UpperChannel", _rc.UpperChannel);
|
||||
Plot("Trade Plot", "LowerChannel", _rc.LowerChannel);
|
||||
Plot("Trade Plot", "Regression", _rc.LinearRegression);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ class HistoryRequestBenchmark(QCAlgorithm):
|
||||
self.SetCash(10000)
|
||||
self.symbol = self.AddEquity("SPY").Symbol
|
||||
|
||||
def OnEndOfDay(self):
|
||||
def OnEndOfDay(self, symbol):
|
||||
minuteHistory = self.History([self.symbol], 60, Resolution.Minute)
|
||||
lastHourHigh = 0
|
||||
for index, row in minuteHistory.loc["SPY"].iterrows():
|
||||
|
||||
@@ -84,6 +84,6 @@ class CustomChartingAlgorithm(QCAlgorithm):
|
||||
self.Plot("Trade Plot", "Sell", self.lastPrice)
|
||||
self.Liquidate()
|
||||
|
||||
def OnEndOfDay(self):
|
||||
def OnEndOfDay(self, symbol):
|
||||
#Log the end of day prices:
|
||||
self.Plot("Trade Plot", "Price", self.lastPrice)
|
||||
@@ -73,7 +73,7 @@ class FuturesMomentumAlgorithm(QCAlgorithm):
|
||||
if self.Portfolio.Invested and self.IsDownTrend:
|
||||
self.Liquidate()
|
||||
|
||||
def OnEndOfDay(self):
|
||||
def OnEndOfDay(self, symbol):
|
||||
if self.IsUpTrend:
|
||||
self.Plot("Indicator Signal", "EOD",1)
|
||||
elif self.IsDownTrend:
|
||||
|
||||
@@ -89,7 +89,7 @@ class MultipleSymbolConsolidationAlgorithm(QCAlgorithm):
|
||||
|
||||
# End of a trading day event handler. This method is called at the end of the algorithm day (or multiple times if trading multiple assets).
|
||||
# Method is called 10 minutes before closing to allow user to close out position.
|
||||
def OnEndOfDay(self):
|
||||
def OnEndOfDay(self, symbol):
|
||||
|
||||
i = 0
|
||||
for symbol in sorted(self.Data.keys()):
|
||||
|
||||
@@ -64,7 +64,7 @@ class RegressionChannelAlgorithm(QCAlgorithm):
|
||||
self.SetHoldings(self._spy, -1)
|
||||
self.Plot("Trade Plot", "Sell", value)
|
||||
|
||||
def OnEndOfDay(self):
|
||||
def OnEndOfDay(self, symbol):
|
||||
self.Plot("Trade Plot", "UpperChannel", self._rc.UpperChannel.Current.Value)
|
||||
self.Plot("Trade Plot", "LowerChannel", self._rc.LowerChannel.Current.Value)
|
||||
self.Plot("Trade Plot", "Regression", self._rc.LinearRegression.Current.Value)
|
||||
@@ -820,7 +820,7 @@ namespace QuantConnect.Algorithm
|
||||
/// <remarks>Method is called 10 minutes before closing to allow user to close out position.</remarks>
|
||||
/// <remarks>Deprecated because different assets have different market close times,
|
||||
/// and because Python does not support two methods with the same name</remarks>
|
||||
[Obsolete("This method is deprecated. Please use this overload: OnEndOfDay(Symbol symbol)")]
|
||||
[Obsolete("This method is deprecated and will be removed after August 2021. Please use this overload: OnEndOfDay(Symbol symbol)")]
|
||||
public virtual void OnEndOfDay()
|
||||
{
|
||||
|
||||
|
||||
@@ -64,10 +64,6 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="NodaTime" Version="3.0.5" />
|
||||
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
|
||||
|
||||
@@ -53,6 +53,11 @@ namespace QuantConnect.AlgorithmFactory.Python.Wrappers
|
||||
/// </summary>
|
||||
public bool IsOnEndOfDayImplemented { get; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the underlying python algorithm implements "OnEndOfDay(symbol)"
|
||||
/// </summary>
|
||||
public bool IsOnEndOfDaySymbolImplemented { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref = "AlgorithmPythonWrapper"/> constructor.
|
||||
/// Creates and wraps the algorithm written in python.
|
||||
@@ -100,7 +105,26 @@ namespace QuantConnect.AlgorithmFactory.Python.Wrappers
|
||||
|
||||
_onOrderEvent = pyAlgorithm.GetAttr("OnOrderEvent");
|
||||
|
||||
IsOnEndOfDayImplemented = pyAlgorithm.GetPythonMethod("OnEndOfDay") != null;
|
||||
PyObject endOfDayMethod = pyAlgorithm.GetPythonMethod("OnEndOfDay");
|
||||
if (endOfDayMethod != null)
|
||||
{
|
||||
// Since we have a EOD method implemented
|
||||
// Determine which one it is by inspecting its arg count
|
||||
var argCount = endOfDayMethod.GetPythonArgCount();
|
||||
switch (argCount)
|
||||
{
|
||||
case 0: // EOD()
|
||||
IsOnEndOfDayImplemented = true;
|
||||
break;
|
||||
case 1: // EOD(Symbol)
|
||||
IsOnEndOfDaySymbolImplemented = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Its important to note that even if both are implemented
|
||||
// python will only use the last implemented, meaning only one will
|
||||
// be used and seen.
|
||||
}
|
||||
}
|
||||
attr.Dispose();
|
||||
}
|
||||
|
||||
@@ -51,10 +51,6 @@
|
||||
<Reference Include="CSharpAPI">
|
||||
<HintPath>InteractiveBrokers\CSharpAPI.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="QuantConnect.Fxcm, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>Fxcm\QuantConnect.Fxcm.dll</HintPath>
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
|
||||
|
||||
@@ -435,6 +435,27 @@ namespace QuantConnect
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a python methods arg count
|
||||
/// </summary>
|
||||
/// <param name="method">The Python method</param>
|
||||
/// <returns>Count of arguments</returns>
|
||||
public static int GetPythonArgCount(this PyObject method)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
int argCount;
|
||||
var pyArgCount = PythonEngine.ModuleFromString(Guid.NewGuid().ToString(),
|
||||
"from inspect import signature\n" +
|
||||
"def GetArgCount(method):\n" +
|
||||
" return len(signature(method).parameters)\n"
|
||||
).GetAttr("GetArgCount").Invoke(method);
|
||||
pyArgCount.TryConvert(out argCount);
|
||||
|
||||
return argCount;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an ordered enumerable where position reducing orders are executed first
|
||||
/// and the remaining orders are executed in decreasing order value.
|
||||
@@ -1743,16 +1764,21 @@ namespace QuantConnect
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the first in the series of names, or find the one that matches the configured algirithmTypeName
|
||||
/// Return the first in the series of names, or find the one that matches the configured algorithmTypeName
|
||||
/// </summary>
|
||||
/// <param name="names">The list of class names</param>
|
||||
/// <param name="algorithmTypeName">The configured algorithm type name from the config</param>
|
||||
/// <returns>The name of the class being run</returns>
|
||||
public static string SingleOrAlgorithmTypeName(this List<string> names, string algorithmTypeName)
|
||||
{
|
||||
// if there's only one use that guy
|
||||
// if there's more than one then find which one we should use using the algorithmTypeName specified
|
||||
return names.Count == 1 ? names.Single() : names.SingleOrDefault(x => x.EndsWith("." + algorithmTypeName));
|
||||
// If there's only one name use that guy
|
||||
if (names.Count == 1) { return names.Single(); }
|
||||
|
||||
// If we have multiple names we need to search the names based on the given algorithmTypeName
|
||||
// If the given name already contains dots (fully named) use it as it is
|
||||
// otherwise add a dot to the beginning to avoid matching any subsets of other names
|
||||
var searchName = algorithmTypeName.Contains(".") ? algorithmTypeName : "." + algorithmTypeName;
|
||||
return names.SingleOrDefault(x => x.EndsWith(searchName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -48,7 +48,9 @@ namespace QuantConnect
|
||||
/// Initializes a new instance of the <see cref="TimeZoneOffsetProvider"/> class
|
||||
/// </summary>
|
||||
/// <param name="timeZone">The time zone to provide offsets for</param>
|
||||
/// <param name="utcStartTime">The start of the range of offsets</param>
|
||||
/// <param name="utcStartTime">The start of the range of offsets.
|
||||
/// Careful here, it will determine the current discontinuity offset value. When requested to convert a date we only look forward for new discontinuities
|
||||
/// but we suppose the current offset is correct for the requested date if in the past.</param>
|
||||
/// <param name="utcEndTime">The end of the range of offsets</param>
|
||||
public TimeZoneOffsetProvider(DateTimeZone timeZone, DateTime utcStartTime, DateTime utcEndTime)
|
||||
{
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace QuantConnect.Lean.Engine.RealTime
|
||||
// For performance only add OnEndOfDay Symbol scheduled events if the method is implemented.
|
||||
// When there are many securities it adds a significant overhead
|
||||
private bool _implementsOnEndOfDaySymbol;
|
||||
private bool _implementsOnEndOfDay;
|
||||
|
||||
/// <summary>
|
||||
/// Keep track of this event so we can remove it when we need to update it
|
||||
@@ -90,8 +91,6 @@ namespace QuantConnect.Lean.Engine.RealTime
|
||||
/// </summary>
|
||||
protected void Setup(DateTime start, DateTime end, Language language, DateTime? currentUtcTime = null)
|
||||
{
|
||||
AddAlgorithmEndOfDayEvent(start, end, currentUtcTime);
|
||||
|
||||
if (language == Language.CSharp)
|
||||
{
|
||||
var method = Algorithm.GetType().GetMethod("OnEndOfDay", new[] { typeof(Symbol) });
|
||||
@@ -101,16 +100,31 @@ namespace QuantConnect.Lean.Engine.RealTime
|
||||
{
|
||||
_implementsOnEndOfDaySymbol = true;
|
||||
}
|
||||
|
||||
// Also determine if we are using the soon to be deprecated EOD so we don't use it
|
||||
// unnecessarily and post messages about its deprecation to the user
|
||||
var eodMethod = Algorithm.GetType().GetMethod("OnEndOfDay", Type.EmptyTypes);
|
||||
if (eodMethod != null && eodMethod.DeclaringType != typeof(QCAlgorithm))
|
||||
{
|
||||
_implementsOnEndOfDay = true;
|
||||
}
|
||||
}
|
||||
else if (language == Language.Python)
|
||||
{
|
||||
var wrapper = Algorithm as AlgorithmPythonWrapper;
|
||||
_implementsOnEndOfDaySymbol = wrapper.IsOnEndOfDayImplemented;
|
||||
if (wrapper != null)
|
||||
{
|
||||
_implementsOnEndOfDaySymbol = wrapper.IsOnEndOfDaySymbolImplemented;
|
||||
_implementsOnEndOfDay = wrapper.IsOnEndOfDayImplemented;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException(nameof(language));
|
||||
}
|
||||
|
||||
// Here to maintain functionality until deprecation in August 2021
|
||||
AddAlgorithmEndOfDayEvent(start, end, currentUtcTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -132,6 +146,9 @@ namespace QuantConnect.Lean.Engine.RealTime
|
||||
[Obsolete("This method is deprecated. It will add ScheduledEvents for the deprecated IAlgorithm.OnEndOfDay()")]
|
||||
protected void AddAlgorithmEndOfDayEvent(DateTime start, DateTime end, DateTime? currentUtcTime = null)
|
||||
{
|
||||
// If the algorithm didn't implement it no need to support it.
|
||||
if (!_implementsOnEndOfDay) { return; }
|
||||
|
||||
if (_algorithmOnEndOfDay != null)
|
||||
{
|
||||
// if we already set it once we remove the previous and
|
||||
@@ -213,6 +230,7 @@ namespace QuantConnect.Lean.Engine.RealTime
|
||||
|
||||
// we re add the algorithm end of day event because it depends on the securities
|
||||
// tradable dates
|
||||
// Here to maintain functionality until deprecation in August 2021
|
||||
AddAlgorithmEndOfDayEvent(Algorithm.UtcTime, Algorithm.EndDate, Algorithm.UtcTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,9 @@ namespace QuantConnect.Lean.Engine.RealTime
|
||||
where !currentUtcTime.HasValue || eventUtcTime > currentUtcTime.Value
|
||||
select eventUtcTime;
|
||||
|
||||
// Log a message warning the user this EOD will be deprecated soon
|
||||
algorithm.Debug("Usage of QCAlgorithm.OnEndOfDay() without a symbol will be deprecated August 2021. Always use a symbol when overriding this method: OnEndOfDay(symbol)");
|
||||
|
||||
return new ScheduledEvent(CreateEventName("Algorithm", "EndOfDay"), times, (name, triggerTime) =>
|
||||
{
|
||||
try
|
||||
|
||||
@@ -97,7 +97,7 @@ namespace QuantConnect.Report
|
||||
var backtestPoints = ResultsUtil.EquityPoints(backtestResult);
|
||||
var livePoints = ResultsUtil.EquityPoints(liveResult);
|
||||
|
||||
if (backtestPoints.Count == 0 && livePoints.Count == 0)
|
||||
if (backtestPoints.Count < 2 && livePoints.Count < 2)
|
||||
{
|
||||
return new Series<DateTime, double>(new DateTime[] { }, new double[] { });
|
||||
}
|
||||
|
||||
@@ -67,10 +67,6 @@
|
||||
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -52,10 +52,6 @@
|
||||
<PackageReference Include="NodaTime" Version="3.0.5" />
|
||||
<PackageReference Include="System.ComponentModel.Composition" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Algorithm.Framework\QuantConnect.Algorithm.Framework.csproj" />
|
||||
<ProjectReference Include="..\Algorithm\QuantConnect.Algorithm.csproj" />
|
||||
|
||||
@@ -77,13 +77,15 @@ namespace QuantConnect.Tests.AlgorithmFactory
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LoadsAlgorithm_UsingSingleOrAlgorithmTypeName_ExtensionMethod()
|
||||
[TestCase("BasicTemplateAlgorithm")]
|
||||
[TestCase("QuantConnect.Algorithm.CSharp.BasicTemplateAlgorithm")]
|
||||
public void LoadsAlgorithm_UsingSingleOrAlgorithmTypeName_ExtensionMethod(string algorithmName)
|
||||
{
|
||||
var assemblyPath1 = "QuantConnect.Algorithm.CSharp.dll";
|
||||
|
||||
string error1;
|
||||
IAlgorithm algorithm1;
|
||||
var one = new Loader(false, Language.CSharp, TimeSpan.FromMinutes(1), names => names.SingleOrAlgorithmTypeName("BasicTemplateAlgorithm"), _workerThread)
|
||||
var one = new Loader(false, Language.CSharp, TimeSpan.FromMinutes(1), names => names.SingleOrAlgorithmTypeName(algorithmName), _workerThread)
|
||||
.TryCreateAlgorithmInstanceWithIsolator(assemblyPath1, 5120, out algorithm1, out error1);
|
||||
|
||||
Assert.IsTrue(one);
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace QuantConnect.Tests
|
||||
var initialDebugEnabled = Log.DebuggingEnabled;
|
||||
|
||||
// Use our current test LogHandler and a FileLogHandler
|
||||
var newLogHandlers = new ILogHandler[] { MaintainLogHandlerAttribute.GetLogHandler(), new FileLogHandler(logFile, false) };
|
||||
var newLogHandlers = new ILogHandler[] { MaintainLogHandlerAttribute.LogHandler, new FileLogHandler(logFile, false) };
|
||||
|
||||
using (Log.LogHandler = new CompositeLogHandler(newLogHandlers))
|
||||
using (var algorithmHandlers = LeanEngineAlgorithmHandlers.FromConfiguration(Composer.Instance))
|
||||
|
||||
@@ -18,33 +18,35 @@ using System;
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using QuantConnect;
|
||||
using QuantConnect.Configuration;
|
||||
using QuantConnect.Logging;
|
||||
using QuantConnect.Python;
|
||||
using QuantConnect.Tests;
|
||||
using QuantConnect.Util;
|
||||
|
||||
[assembly: MaintainLogHandler()]
|
||||
[SetUpFixture]
|
||||
public class AssemblyInitialize
|
||||
namespace QuantConnect.Tests
|
||||
{
|
||||
[OneTimeSetUp]
|
||||
public void InitializeTestEnvironment()
|
||||
[SetUpFixture]
|
||||
public class AssemblyInitialize
|
||||
{
|
||||
AdjustCurrentDirectory();
|
||||
}
|
||||
[OneTimeSetUp]
|
||||
public void InitializeTestEnvironment()
|
||||
{
|
||||
AdjustCurrentDirectory();
|
||||
}
|
||||
|
||||
public static void AdjustCurrentDirectory()
|
||||
{
|
||||
// nunit 3 sets the current folder to a temp folder we need it to be the test bin output folder
|
||||
var dir = TestContext.CurrentContext.TestDirectory;
|
||||
Environment.CurrentDirectory = dir;
|
||||
Directory.SetCurrentDirectory(dir);
|
||||
Config.Reset();
|
||||
Globals.Reset();
|
||||
PythonInitializer.SetPythonPathEnvironmentVariable(
|
||||
new[]
|
||||
{
|
||||
public static void AdjustCurrentDirectory()
|
||||
{
|
||||
// nunit 3 sets the current folder to a temp folder we need it to be the test bin output folder
|
||||
var dir = TestContext.CurrentContext.TestDirectory;
|
||||
Environment.CurrentDirectory = dir;
|
||||
Directory.SetCurrentDirectory(dir);
|
||||
Config.Reset();
|
||||
Globals.Reset();
|
||||
PythonInitializer.SetPythonPathEnvironmentVariable(
|
||||
new[]
|
||||
{
|
||||
"./Alphas",
|
||||
"./Execution",
|
||||
"./Portfolio",
|
||||
@@ -57,52 +59,60 @@ public class AssemblyInitialize
|
||||
"../../../Algorithm.Framework",
|
||||
"../../../Algorithm.Framework/Selection",
|
||||
"../../../Algorithm.Python"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Assembly)]
|
||||
public class MaintainLogHandlerAttribute : Attribute, ITestAction
|
||||
{
|
||||
private static ILogHandler logHandler;
|
||||
|
||||
public MaintainLogHandlerAttribute()
|
||||
{
|
||||
logHandler = GetLogHandler();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the log handler defined by test context parameters. Defaults to ConsoleLogHandler if no
|
||||
/// "log-handler" parameter is found.
|
||||
/// </summary>
|
||||
/// <returns>A new LogHandler</returns>
|
||||
public static ILogHandler GetLogHandler()
|
||||
{
|
||||
if (TestContext.Parameters.Exists("log-handler"))
|
||||
{
|
||||
var logHandler = TestContext.Parameters["log-handler"];
|
||||
Log.Trace($"QuantConnect.Tests.AssemblyInitialize(): Log handler test parameter loaded {logHandler}");
|
||||
|
||||
return Composer.Instance.GetExportedValueByTypeName<ILogHandler>(logHandler);
|
||||
});
|
||||
}
|
||||
|
||||
// If no parameter just use ConsoleLogHandler
|
||||
return new ConsoleLogHandler();
|
||||
}
|
||||
|
||||
public void BeforeTest(ITest details)
|
||||
[AttributeUsage(AttributeTargets.Assembly)]
|
||||
public class MaintainLogHandlerAttribute : Attribute, ITestAction
|
||||
{
|
||||
Log.LogHandler = logHandler;
|
||||
}
|
||||
public static ILogHandler LogHandler { get; private set; }
|
||||
|
||||
public void AfterTest(ITest details)
|
||||
{
|
||||
//NOP
|
||||
}
|
||||
public MaintainLogHandlerAttribute()
|
||||
{
|
||||
LogHandler = LoadLogHandler();
|
||||
}
|
||||
|
||||
public ActionTargets Targets
|
||||
{ // Set only to act on test fixture not individual tests
|
||||
get { return ActionTargets.Suite; }
|
||||
/// <summary>
|
||||
/// Replace the log handler if it has been changed
|
||||
/// </summary>
|
||||
/// <param name="details"></param>
|
||||
public void BeforeTest(ITest details)
|
||||
{
|
||||
if (Log.LogHandler != LogHandler)
|
||||
{
|
||||
Log.LogHandler = LogHandler;
|
||||
}
|
||||
}
|
||||
|
||||
public void AfterTest(ITest details)
|
||||
{
|
||||
//NOP
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set to act on all tests
|
||||
/// </summary>
|
||||
public ActionTargets Targets => ActionTargets.Test;
|
||||
|
||||
/// <summary>
|
||||
/// Load the log handler defined by test context parameters. Defaults to ConsoleLogHandler if no
|
||||
/// "log-handler" parameter is found.
|
||||
/// </summary>
|
||||
/// <returns>An instance of a new LogHandler</returns>
|
||||
private static ILogHandler LoadLogHandler()
|
||||
{
|
||||
if (TestContext.Parameters.Exists("log-handler"))
|
||||
{
|
||||
var logHandler = TestContext.Parameters["log-handler"];
|
||||
Log.Trace($"QuantConnect.Tests.AssemblyInitialize(): Log handler test parameter loaded {logHandler}");
|
||||
|
||||
return Composer.Instance.GetExportedValueByTypeName<ILogHandler>(logHandler);
|
||||
}
|
||||
|
||||
// If no parameter just use ConsoleLogHandler
|
||||
return new ConsoleLogHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -647,10 +647,11 @@ namespace QuantConnect.Tests.Engine.DataFeeds
|
||||
[Test]
|
||||
public void DelistedEventEmitted_Equity()
|
||||
{
|
||||
_startDate = new DateTime(2016, 2, 18);
|
||||
Log.Error($"Starting Test DelistedEventEmitted_Equity at: {DateTime.UtcNow}");
|
||||
_startDate = new DateTime(2016, 2, 18, 6, 0, 0);
|
||||
CustomMockedFileBaseData.StartDate = _startDate;
|
||||
_manualTimeProvider.SetCurrentTimeUtc(_startDate);
|
||||
var delistingDate = _startDate.AddDays(1);
|
||||
var delistingDate = _startDate.Date.AddDays(1);
|
||||
|
||||
var autoResetEvent = new AutoResetEvent(false);
|
||||
var feed = RunDataFeed(getNextTicksFunction: handler =>
|
||||
@@ -658,16 +659,14 @@ namespace QuantConnect.Tests.Engine.DataFeeds
|
||||
autoResetEvent.Set();
|
||||
return new[] { new Delisting(Symbols.AAPL, delistingDate, 1, DelistingType.Warning) };
|
||||
});
|
||||
|
||||
_algorithm.AddEquity(Symbols.AAPL);
|
||||
_algorithm.OnEndOfTimeStep();
|
||||
_algorithm.SetFinishedWarmingUp();
|
||||
|
||||
Assert.IsTrue(autoResetEvent.WaitOne(TimeSpan.FromMilliseconds(200)));
|
||||
|
||||
var receivedDelistedWarning = 0;
|
||||
var receivedDelisted = 0;
|
||||
ConsumeBridge(feed, TimeSpan.FromSeconds(5), ts =>
|
||||
ConsumeBridge(feed, TimeSpan.FromSeconds(10), ts =>
|
||||
{
|
||||
foreach (var delistingEvent in ts.Slice.Delistings)
|
||||
{
|
||||
@@ -678,10 +677,12 @@ namespace QuantConnect.Tests.Engine.DataFeeds
|
||||
|
||||
if (delistingEvent.Value.Type == DelistingType.Warning)
|
||||
{
|
||||
Log.Error("Received Delisted Warning");
|
||||
Interlocked.Increment(ref receivedDelistedWarning);
|
||||
}
|
||||
if (delistingEvent.Value.Type == DelistingType.Delisted)
|
||||
{
|
||||
Log.Error("Received Delisted Event");
|
||||
Interlocked.Increment(ref receivedDelisted);
|
||||
// we got what we wanted, end unit test
|
||||
_manualTimeProvider.SetCurrentTimeUtc(DateTime.UtcNow);
|
||||
@@ -689,9 +690,10 @@ namespace QuantConnect.Tests.Engine.DataFeeds
|
||||
}
|
||||
},
|
||||
alwaysInvoke: false,
|
||||
secondsTimeStep: 3600 * 8,
|
||||
secondsTimeStep: 3600 * 6,
|
||||
endDate: delistingDate.AddDays(2));
|
||||
|
||||
Log.Error($"Finished Test DelistedEventEmitted_Equity at: {DateTime.UtcNow}");
|
||||
Assert.AreEqual(1, receivedDelistedWarning, $"Did not receive {DelistingType.Warning}");
|
||||
Assert.AreEqual(1, receivedDelisted, $"Did not receive {DelistingType.Delisted}");
|
||||
}
|
||||
|
||||
@@ -395,13 +395,13 @@ namespace QuantConnect.Tests.Engine.RealTime
|
||||
_resultHandler,
|
||||
null,
|
||||
new TestTimeLimitManager());
|
||||
// the generic OnEndOfDay()
|
||||
Assert.AreEqual(1, realTimeHandler.GetScheduledEventsCount);
|
||||
|
||||
Assert.AreEqual(0, realTimeHandler.GetScheduledEventsCount);
|
||||
|
||||
realTimeHandler.OnSecuritiesChanged(
|
||||
new SecurityChanges(new[] { security }, Enumerable.Empty<Security>()));
|
||||
|
||||
Assert.AreEqual(1, realTimeHandler.GetScheduledEventsCount);
|
||||
Assert.AreEqual(0, realTimeHandler.GetScheduledEventsCount);
|
||||
|
||||
realTimeHandler.Exit();
|
||||
}
|
||||
@@ -414,7 +414,7 @@ namespace QuantConnect.Tests.Engine.RealTime
|
||||
IAlgorithm algorithm;
|
||||
if (language == Language.CSharp)
|
||||
{
|
||||
algorithm = new TestAlgorithm();
|
||||
algorithm = new TestAlgorithmB();
|
||||
security = (algorithm as QCAlgorithm).AddEquity("SPY");
|
||||
}
|
||||
else
|
||||
@@ -436,13 +436,14 @@ namespace QuantConnect.Tests.Engine.RealTime
|
||||
_resultHandler,
|
||||
null,
|
||||
new TestTimeLimitManager());
|
||||
// the generic OnEndOfDay()
|
||||
Assert.AreEqual(1, realTimeHandler.GetScheduledEventsCount);
|
||||
|
||||
// Because neither implement EOD() deprecated it should be zero
|
||||
Assert.AreEqual(0, realTimeHandler.GetScheduledEventsCount);
|
||||
|
||||
realTimeHandler.OnSecuritiesChanged(
|
||||
new SecurityChanges(new[] { security }, Enumerable.Empty<Security>()));
|
||||
|
||||
Assert.AreEqual(2, realTimeHandler.GetScheduledEventsCount);
|
||||
Assert.AreEqual(1, realTimeHandler.GetScheduledEventsCount);
|
||||
realTimeHandler.Exit();
|
||||
}
|
||||
|
||||
@@ -472,12 +473,24 @@ namespace QuantConnect.Tests.Engine.RealTime
|
||||
private class TestAlgorithm : AlgorithmStub
|
||||
{
|
||||
public bool OnEndOfDayFired { get; set; }
|
||||
|
||||
public override void OnEndOfDay()
|
||||
{
|
||||
OnEndOfDayFired = true;
|
||||
}
|
||||
|
||||
public override void OnEndOfDay(Symbol symbol)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TestAlgorithmB is just for use where we need EOD() not to
|
||||
/// be implemented, because it is deprecated.
|
||||
/// For tests that require EOD() use TestAlgorithm
|
||||
/// </summary>
|
||||
private class TestAlgorithmB : AlgorithmStub
|
||||
{
|
||||
public override void OnEndOfDay(Symbol symbol)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -54,6 +54,42 @@ namespace QuantConnect.Tests.Python
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("def OnEndOfDay(self): self.Name = 'EOD'\r\n def OnEndOfDay(self, symbol): self.Name = 'EODSymbol'", "EODSymbol")]
|
||||
[TestCase("def OnEndOfDay(self, symbol): self.Name = 'EODSymbol'\r\n def OnEndOfDay(self): self.Name = 'EOD'", "EOD")]
|
||||
public void OnEndOfDayBothImplemented(string code, string expectedImplementation)
|
||||
{
|
||||
// If we implement both OnEndOfDay functions we expect it to not throw,
|
||||
// but only the latest will be seen and used.
|
||||
// To test this we will have the functions set something we can verify such as Algo name
|
||||
using (Py.GIL())
|
||||
{
|
||||
var algorithm = GetAlgorithm(code);
|
||||
|
||||
Assert.Null(algorithm.RunTimeError);
|
||||
Assert.DoesNotThrow(() => algorithm.OnEndOfDay());
|
||||
Assert.Null(algorithm.RunTimeError);
|
||||
Assert.DoesNotThrow(() => algorithm.OnEndOfDay(Symbols.SPY));
|
||||
Assert.Null(algorithm.RunTimeError);
|
||||
|
||||
// Check the name
|
||||
Assert.AreEqual(expectedImplementation, algorithm.Name);
|
||||
|
||||
// Check the wrapper EOD Implemented variables to confirm
|
||||
switch (expectedImplementation)
|
||||
{
|
||||
case "EOD":
|
||||
Assert.IsTrue(algorithm.IsOnEndOfDayImplemented);
|
||||
Assert.IsFalse(algorithm.IsOnEndOfDaySymbolImplemented);
|
||||
break;
|
||||
case "EODSymbol":
|
||||
Assert.IsTrue(algorithm.IsOnEndOfDaySymbolImplemented);
|
||||
Assert.IsFalse(algorithm.IsOnEndOfDayImplemented);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CallOnEndOfDayExceptionNoParameter()
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Packets;
|
||||
using QuantConnect.Report;
|
||||
|
||||
namespace QuantConnect.Tests.Report
|
||||
@@ -41,5 +42,29 @@ namespace QuantConnect.Tests.Report
|
||||
Assert.AreEqual(1, collection.Count);
|
||||
Assert.AreEqual(0.2, collection.First().Drawdown, 0.0001);
|
||||
}
|
||||
|
||||
[TestCase(false)]
|
||||
[TestCase(true)]
|
||||
public void NoDrawdown(bool hasEquityPoint)
|
||||
{
|
||||
var strategyEquityChart = new Chart("Strategy Equity");
|
||||
var equitySeries = new Series("Equity");
|
||||
strategyEquityChart.AddSeries(equitySeries);
|
||||
|
||||
if (hasEquityPoint)
|
||||
{
|
||||
equitySeries.AddPoint(new DateTime(2020, 1, 1), 100000);
|
||||
}
|
||||
|
||||
var backtest = new BacktestResult
|
||||
{
|
||||
Charts = new Dictionary<string, Chart> {[strategyEquityChart.Name] = strategyEquityChart}
|
||||
};
|
||||
|
||||
var normalizedResults = DrawdownCollection.NormalizeResults(backtest, null);
|
||||
|
||||
Assert.AreEqual(0, normalizedResults.KeyCount);
|
||||
Assert.AreEqual(0, normalizedResults.ValueCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user