Compare commits

..

13 Commits
11250 ... 11348

Author SHA1 Message Date
Martin-Molinero
d94a1d09a4 Remove unrequired references 2021-04-12 16:05:01 -03:00
Martin-Molinero
2c843cae9e Fix rebase
- Fix ambiguous Index
- Remove StrategyCapacity.cs
- Update System.Threading.Tasks.Extensionsy
2021-04-12 15:09:34 -03:00
Stefano Raggi
039fdf7e4a Upgrade IBAutomater to v1.0.51
ignored, and an empty message aborts the commit.
2021-04-12 15:09:33 -03:00
Martin Molinero
2c63546c37 Remove FXCM 2021-04-12 15:09:33 -03:00
Martin Molinero
58682e1bbd Fix ambiguous errors. Add IBAutomator net5 2021-04-12 15:09:33 -03:00
Gerardo Salazar
d11a375fdb Update projects to use .NET 5.0, the successor to .NET Core 2021-04-12 15:09:33 -03:00
Martin-Molinero
6ab91a13e1 Add note for TimeZoneOffsetProvider StartTime (#5469) 2021-04-08 18:00:17 -03:00
Colton Sellers
beaa705646 Loader Support Full Algorithm Name (#5467)
* Apply fix

* Address possible mismatching subset of name
2021-04-08 17:59:08 -03:00
Colton Sellers
4c830c8235 Fix Breaking Unit Test (#5466)
* Adjust Timeout; Reduce time advance

* Move logging of EndTime above asserts
2021-04-07 21:09:56 -03:00
Colton Sellers
395c1123da Remove Obsolete QCAlgorithm.OnEndOfDay() (#5441)
* Remove and replace OnEndOfDay() ref

* Restore functionality of obsolete EOD, waiting for deprecation in August 2021

* Cleanup

* Adjustments to only post message when using obsolete EOD

* nit, extra space

* Address review

* Adjust test to reflect new behaviour

* Move GetPythonArgCount to an extension method

* Add unit test

* nit accidental import

* Refactor broken test

* Use Py.GIL() state for extension
2021-04-07 13:39:49 -03:00
Martin-Molinero
8e50645640 Update System.Threading.Tasks.Extensions (#5340) 2021-04-07 12:36:22 -03:00
Colton Sellers
68ca504d3a Apply fixes (#5464) 2021-04-07 11:46:31 -03:00
Jasper van Merle
12df1c9a31 Fix drawdown plotting failing on single equity point (#5461) 2021-04-06 16:30:23 -07:00
32 changed files with 271 additions and 134 deletions

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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))

View File

@@ -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>

View File

@@ -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();
}

View File

@@ -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);
}
}
}
}

View File

@@ -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():

View File

@@ -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)

View File

@@ -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:

View File

@@ -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()):

View File

@@ -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)

View File

@@ -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()
{

View File

@@ -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" />

View File

@@ -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();
}

View File

@@ -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" />

View File

@@ -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>

View File

@@ -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)
{

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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[] { });
}

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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);

View File

@@ -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))

View File

@@ -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();
}
}
}

View File

@@ -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}");
}

View File

@@ -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)
{
}

View File

@@ -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()
{

View File

@@ -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);
}
}
}