Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
2 changes: 1 addition & 1 deletion Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
<DebugType>portable</DebugType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.60" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.61" />
<PackageReference Include="Accord" Version="3.6.0" />
<PackageReference Include="Accord.Fuzzy" Version="3.6.0" />
<PackageReference Include="Accord.MachineLearning" Version="3.6.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.60" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.61" />
<PackageReference Include="Accord" Version="3.6.0" />
<PackageReference Include="Accord.Math" Version="3.6.0" />
<PackageReference Include="Accord.Statistics" Version="3.6.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,12 @@ public ScheduledUniverseSelectionModel(IDateRule dateRule, ITimeRule timeRule, P
public ScheduledUniverseSelectionModel(DateTimeZone timeZone, IDateRule dateRule, ITimeRule timeRule, PyObject selector, UniverseSettings settings = null)
{
Func<DateTime, object> func;
selector.TrySafeAs(out func);
if (!selector.TrySafeAs(out func))
{
throw new ArgumentException(
$"Unable to create a scheduled universe selector from {selector.ToDisplayString()}: it is not a function. " +
"Please provide a function that takes the firing date time and returns the selected symbols.");
}
_timeZone = timeZone;
_dateRule = dateRule;
_timeRule = timeRule;
Expand Down
2 changes: 1 addition & 1 deletion Algorithm.Python/QuantConnect.Algorithm.Python.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.60" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.61" />
</ItemGroup>
<ItemGroup>
<Content Include="OptionUniverseFilterGreeksShortcutsRegressionAlgorithm.py" />
Expand Down
13 changes: 12 additions & 1 deletion Algorithm/QCAlgorithm.Python.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1375,7 +1375,18 @@ public void SetBenchmark(PyObject benchmark)
SetBenchmark(pyBenchmark);
return;
}
SetBenchmark((Symbol)benchmark.AsManagedObject(typeof(Symbol)));

try
{
SetBenchmark((Symbol)benchmark.AsManagedObject(typeof(Symbol)));
}
catch (InvalidCastException exception)
{
throw new ArgumentException(
$"Unable to set the benchmark from {benchmark.ToDisplayString()}: it is not a supported benchmark type. " +
MethodSignatureFormatter.FormatOverloads(typeof(QCAlgorithm).GetMethods().Where(m => m.Name == nameof(SetBenchmark))),
exception);
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion Algorithm/QuantConnect.Algorithm.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.60" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.61" />
<PackageReference Include="MathNet.Numerics" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="NodaTime" Version="3.0.5" />
Expand Down
2 changes: 1 addition & 1 deletion AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.60" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.61" />
<PackageReference Include="NodaTime" Version="3.0.5" />
</ItemGroup>
<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion Common/Data/Consolidators/BaseDataConsolidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace QuantConnect.Data.Consolidators
public class BaseDataConsolidator : TradeBarConsolidatorBase<BaseData>
{
/// <summary>
/// Create a new TickConsolidator for the desired resolution
/// Create a new BaseDataConsolidator for the desired resolution
/// </summary>
/// <param name="resolution">The resolution desired</param>
/// <returns>A consolidator that produces data on the resolution interval</returns>
Expand Down
14 changes: 13 additions & 1 deletion Common/Data/Consolidators/PeriodCountConsolidatorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,20 @@ protected PeriodCountConsolidatorBase(Func<DateTime, CalendarInfo> func)
/// </summary>
/// <param name="pyObject">Python object that defines either a function object that defines the start time of a consolidated data or a timespan</param>
protected PeriodCountConsolidatorBase(PyObject pyObject)
: this(GetPeriodSpecificationFromPyObject(pyObject))
{
try
{
_periodSpecification = GetPeriodSpecificationFromPyObject(pyObject);
}
catch (InvalidCastException exception)
{
var type = GetType();
throw new ArgumentException(
$"Unable to create a consolidator period from {pyObject.ToDisplayString()}: it is not a supported period type. " +
MethodSignatureFormatter.FormatOverloads(type.GetConstructors(), displayName: type.Name),
exception);
}
_period = _periodSpecification.Period;
}

/// <summary>
Expand Down
10 changes: 10 additions & 0 deletions Common/Data/Consolidators/QuoteBarConsolidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ namespace QuantConnect.Data.Consolidators
/// </summary>
public class QuoteBarConsolidator : PeriodCountConsolidatorBase<QuoteBar, QuoteBar>
{
/// <summary>
/// Create a new QuoteBarConsolidator for the desired resolution
/// </summary>
/// <param name="resolution">The resolution desired</param>
/// <returns>A consolidator that produces data on the resolution interval</returns>
public static QuoteBarConsolidator FromResolution(Resolution resolution)
{
return new QuoteBarConsolidator(resolution.ToTimeSpan());
}

/// <summary>
/// Initializes a new instance of the <see cref="QuoteBarConsolidator"/> class
/// </summary>
Expand Down
25 changes: 21 additions & 4 deletions Common/Data/DynamicData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public object SetProperty(string name, object value)
{
if (value is PyObject pyobject)
{
Time = pyobject.As<DateTime>();
Time = ConvertPropertyValue<DateTime>(pyobject, name);
}
else
{
Expand All @@ -70,7 +70,7 @@ public object SetProperty(string name, object value)
{
if (value is PyObject pyobject)
{
EndTime = pyobject.As<DateTime>();
EndTime = ConvertPropertyValue<DateTime>(pyobject, name);
}
else
{
Expand All @@ -81,7 +81,7 @@ public object SetProperty(string name, object value)
{
if (value is PyObject pyobject)
{
Value = pyobject.As<decimal>();
Value = ConvertPropertyValue<decimal>(pyobject, name);
}
else
{
Expand All @@ -98,7 +98,7 @@ public object SetProperty(string name, object value)
{
if (value is PyObject pyobject)
{
Symbol = pyobject.As<Symbol>();
Symbol = ConvertPropertyValue<Symbol>(pyobject, name);
}
else
{
Expand Down Expand Up @@ -196,5 +196,22 @@ public override BaseData Clone()
}
return clone;
}

/// <summary>
/// Converts the given Python value into the type expected by the reserved property with the given name
/// </summary>
private static T ConvertPropertyValue<T>(PyObject value, string name)
{
try
{
return value.As<T>();
}
catch (InvalidCastException exception)
{
throw new ArgumentException(
$"Unable to set the '{name}' property from {value.ToDisplayString()}: it is not a supported {typeof(T).Name} value.",
exception);
}
}
}
}
7 changes: 6 additions & 1 deletion Common/Data/UniverseSelection/ScheduledUniverse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,12 @@ public ScheduledUniverse(IDateRule dateRule, ITimeRule timeRule, Func<DateTime,
public ScheduledUniverse(DateTimeZone timeZone, IDateRule dateRule, ITimeRule timeRule, PyObject selector, UniverseSettings settings = null)
: base(CreateConfiguration(timeZone, dateRule, timeRule))
{
selector.TrySafeAs<Func<DateTime, object>>(out var func);
if (!selector.TrySafeAs<Func<DateTime, object>>(out var func))
{
throw new ArgumentException(
$"Unable to create a scheduled universe selector from {selector.ToDisplayString()}: it is not a function. " +
"Please provide a function that takes the firing date time and returns the selected symbols.");
}
_dateRule = dateRule;
_timeRule = timeRule;
_selector = func.ConvertSelectionSymbolDelegate();
Expand Down
14 changes: 14 additions & 0 deletions Common/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3039,6 +3039,20 @@ public static bool TryConvert<T>(this PyObject pyObject, out T result, bool allo
return false;
}

/// <summary>
/// Gets a string representation of the given Python object and its type
/// to be used in user-facing messages, e.g. "'Daily' of type 'Resolution'"
/// </summary>
/// <param name="pyObject">The Python object to represent</param>
/// <returns>The string representation of the Python object</returns>
public static string ToDisplayString(this PyObject pyObject)
{
using (Py.GIL())
{
return $"'{pyObject}' of type '{pyObject.GetPythonType().Name}'";
}
}

/// <summary>
/// Safely convert PyObject to ManagedObject using Py.GIL Lock
/// If no type is given it will convert the PyObject's Python Type to a ManagedObject Type
Expand Down
2 changes: 1 addition & 1 deletion Common/QuantConnect.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
<Message Text="SelectedOptimization $(SelectedOptimization)" Importance="high" />
</Target>
<ItemGroup>
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.60" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.61" />
<PackageReference Include="CloneExtensions" Version="1.3.0" />
<PackageReference Include="fasterflect" Version="3.0.0" />
<PackageReference Include="MathNet.Numerics" Version="5.0.0" />
Expand Down
19 changes: 14 additions & 5 deletions Common/Securities/Security.cs
Original file line number Diff line number Diff line change
Expand Up @@ -992,7 +992,7 @@ public bool TryGet<T>(string key, out T value)
{
if (Cache.Properties.TryGetValue(key, out var obj))
{
value = CastDynamicPropertyValue<T>(obj);
value = CastDynamicPropertyValue<T>(key, obj);
return true;
}
value = default;
Expand All @@ -1007,7 +1007,7 @@ public bool TryGet<T>(string key, out T value)
/// <exception cref="KeyNotFoundException">If the property is not found</exception>
public T Get<T>(string key)
{
return CastDynamicPropertyValue<T>(Cache.Properties[key]);
return CastDynamicPropertyValue<T>(key, Cache.Properties[key]);
}

/// <summary>
Expand All @@ -1032,7 +1032,7 @@ public bool Remove<T>(string key, out T value)
var result = Cache.Properties.Remove(key, out object objectValue);
if (result)
{
value = CastDynamicPropertyValue<T>(objectValue);
value = CastDynamicPropertyValue<T>(key, objectValue);
}
return result;
}
Expand Down Expand Up @@ -1158,15 +1158,24 @@ private void UpdateMarketPrice(BaseData data)
/// Casts a dynamic property value to the specified type.
/// Useful for cases where the property value is a PyObject and we want to cast it to the underlying type.
/// </summary>
private static T CastDynamicPropertyValue<T>(object obj)
private static T CastDynamicPropertyValue<T>(string key, object obj)
{
T value;
var pyObj = obj as PyObject;
if (pyObj != null)
{
using (Py.GIL())
{
value = pyObj.As<T>();
try
{
value = pyObj.As<T>();
}
catch (InvalidCastException exception)
{
throw new InvalidCastException(
$"Unable to get the '{key}' property from {pyObj.ToDisplayString()}: it is not a supported {typeof(T).Name} value.",
exception);
}
}
}
else
Expand Down
2 changes: 1 addition & 1 deletion Engine/QuantConnect.Lean.Engine.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
<Message Text="SelectedOptimization $(SelectedOptimization)" Importance="high" />
</Target>
<ItemGroup>
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.60" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.61" />
<PackageReference Include="fasterflect" Version="3.0.0" />
<PackageReference Include="MathNet.Numerics" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
Expand Down
2 changes: 1 addition & 1 deletion Indicators/QuantConnect.Indicators.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
<Message Text="SelectedOptimization $(SelectedOptimization)" Importance="high" />
</Target>
<ItemGroup>
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.60" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.61" />
<PackageReference Include="MathNet.Numerics" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion Report/QuantConnect.Report.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.60" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.61" />
<PackageReference Include="Deedle" Version="2.1.0" />
<PackageReference Include="MathNet.Numerics" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
Expand Down
2 changes: 1 addition & 1 deletion Research/QuantConnect.Research.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
<PackageReference Include="Plotly.NET" Version="5.1.0" />
<PackageReference Include="Plotly.NET.CSharp" Version="0.13.0" />
<PackageReference Include="Plotly.NET.Interactive" Version="5.0.0" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.60" />
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.61" />
<PackageReference Include="NodaTime" Version="3.0.5" />
</ItemGroup>
<ItemGroup>
Expand Down
23 changes: 23 additions & 0 deletions Tests/Algorithm/AlgorithmBenchmarkTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using System.Collections.Generic;
using NodaTime;
using NUnit.Framework;
using Python.Runtime;
using QuantConnect.Algorithm;
using QuantConnect.Configuration;
using QuantConnect.Interfaces;
Expand Down Expand Up @@ -124,6 +125,28 @@ public void MisalignedBenchmarkAndAlgorithmTimeZones(Resolution resolution, bool
}
}

[Test]
public void PythonSetBenchmarkThrowsDescriptiveErrorForUnsupportedBenchmarkType()
{
var algorithm = new QCAlgorithm();
var dataManager = new DataManagerStub(algorithm, new MockDataFeed());
algorithm.SubscriptionManager.SetDataManager(dataManager);

using (Py.GIL())
{
// Not a benchmark producing function nor a symbol
using var pyBenchmark = Resolution.Daily.ToPython();
var exception = Assert.Throws<ArgumentException>(() => algorithm.SetBenchmark(pyBenchmark));

// The value rendering is case-insensitive to support both pythonnet enum str() conventions ("Daily" and "DAILY")
Assert.That(exception.Message, Does.Contain("Daily").IgnoreCase);
Assert.That(exception.Message, Does.Contain("'Resolution'"));
Assert.That(exception.Message, Does.Contain("The following overloads are available:"));
Assert.That(exception.Message, Does.Contain("set_benchmark("));
Assert.That(exception.InnerException, Is.TypeOf<InvalidCastException>());
}
}

[Test]
public void BenchmarkIsNotInitializeWithCustomSecurityInitializer()
{
Expand Down
Loading
Loading