diff --git a/Algorithm.Framework/Portfolio/AlphaStreamsPortfolioConstructionModel.cs b/Algorithm.Framework/Portfolio/AlphaStreamsPortfolioConstructionModel.cs index 326e75037090..34b188409176 100644 --- a/Algorithm.Framework/Portfolio/AlphaStreamsPortfolioConstructionModel.cs +++ b/Algorithm.Framework/Portfolio/AlphaStreamsPortfolioConstructionModel.cs @@ -22,8 +22,13 @@ namespace QuantConnect.Algorithm.Framework.Portfolio /// /// Base alpha streams portfolio construction model /// - public class AlphaStreamsPortfolioConstructionModel : IPortfolioConstructionModel + public class AlphaStreamsPortfolioConstructionModel : IPortfolioConstructionModel, INamedModel { + /// + /// Defines a name for the framework model + /// + public virtual string Name { get; set; } = nameof(AlphaStreamsPortfolioConstructionModel); + /// /// Get's the weight for an alpha /// diff --git a/Algorithm.Framework/Portfolio/MaximumSharpeRatioPortfolioOptimizer.cs b/Algorithm.Framework/Portfolio/MaximumSharpeRatioPortfolioOptimizer.cs index abad946fdee4..871b6305211a 100644 --- a/Algorithm.Framework/Portfolio/MaximumSharpeRatioPortfolioOptimizer.cs +++ b/Algorithm.Framework/Portfolio/MaximumSharpeRatioPortfolioOptimizer.cs @@ -18,6 +18,8 @@ using Accord.Math; using Accord.Math.Optimization; using Accord.Statistics; +using QuantConnect.Algorithm.Framework.Alphas; +using static System.FormattableString; namespace QuantConnect.Algorithm.Framework.Portfolio { @@ -26,12 +28,17 @@ namespace QuantConnect.Algorithm.Framework.Portfolio /// The interval of weights in optimization method can be changed based on the long-short algorithm. /// The default model uses flat risk free rate and weight for an individual security range from -1 to 1. /// - public class MaximumSharpeRatioPortfolioOptimizer : IPortfolioOptimizer + public class MaximumSharpeRatioPortfolioOptimizer : IPortfolioOptimizer, INamedModel { private double _lower; private double _upper; private double _riskFreeRate; + /// + /// Defines a name for the portfolio optimizer + /// + public string Name { get; } + /// /// Initialize a new instance of /// @@ -43,6 +50,7 @@ public MaximumSharpeRatioPortfolioOptimizer(double lower = -1, double upper = 1, _lower = lower; _upper = upper; _riskFreeRate = riskFreeRate; + Name = Invariant($"{nameof(MaximumSharpeRatioPortfolioOptimizer)}({lower},{upper},{riskFreeRate})"); } /// @@ -137,4 +145,4 @@ public double[] Optimize(double[,] historicalReturns, double[] expectedReturns = return sum > 0 ? y.Divide(sum) : equalWeights; } } -} \ No newline at end of file +} diff --git a/Algorithm.Framework/Portfolio/MinimumVariancePortfolioOptimizer.cs b/Algorithm.Framework/Portfolio/MinimumVariancePortfolioOptimizer.cs index c986087a7e5b..32b013bc94f4 100644 --- a/Algorithm.Framework/Portfolio/MinimumVariancePortfolioOptimizer.cs +++ b/Algorithm.Framework/Portfolio/MinimumVariancePortfolioOptimizer.cs @@ -18,6 +18,8 @@ using Accord.Math; using Accord.Math.Optimization; using Accord.Statistics; +using QuantConnect.Algorithm.Framework.Alphas; +using static System.FormattableString; namespace QuantConnect.Algorithm.Framework.Portfolio { @@ -26,12 +28,17 @@ namespace QuantConnect.Algorithm.Framework.Portfolio /// with the weight range from -1 to 1 and minimize the portfolio variance with a target return of 2% /// /// The budged constrain is scaled down/up to ensure that the sum of the absolute value of the weights is 1. - public class MinimumVariancePortfolioOptimizer : IPortfolioOptimizer + public class MinimumVariancePortfolioOptimizer : IPortfolioOptimizer, INamedModel { private double _lower; private double _upper; private double _targetReturn; + /// + /// Defines a name for the portfolio optimizer + /// + public string Name { get; } + /// /// Initialize a new instance of /// @@ -43,6 +50,7 @@ public MinimumVariancePortfolioOptimizer(double lower = -1, double upper = 1, do _lower = lower; _upper = upper; _targetReturn = targetReturn; + Name = Invariant($"{nameof(MinimumVariancePortfolioOptimizer)}({lower},{upper},{targetReturn})"); } /// diff --git a/Algorithm.Framework/Portfolio/PortfolioOptimizerPythonWrapper.cs b/Algorithm.Framework/Portfolio/PortfolioOptimizerPythonWrapper.cs index 5bb22c7ffd0e..8a9366a5edf3 100644 --- a/Algorithm.Framework/Portfolio/PortfolioOptimizerPythonWrapper.cs +++ b/Algorithm.Framework/Portfolio/PortfolioOptimizerPythonWrapper.cs @@ -15,6 +15,7 @@ */ using Python.Runtime; +using QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Python; namespace QuantConnect.Algorithm.Framework.Portfolio @@ -22,8 +23,27 @@ namespace QuantConnect.Algorithm.Framework.Portfolio /// /// Python wrapper for custom portfolio optimizer /// - public class PortfolioOptimizerPythonWrapper : BasePythonWrapper, IPortfolioOptimizer + public class PortfolioOptimizerPythonWrapper : BasePythonWrapper, IPortfolioOptimizer, INamedModel { + /// + /// Defines a name for the portfolio optimizer + /// + public string Name + { + get + { + using (Py.GIL()) + { + if (HasAttr(nameof(Name))) + { + return GetProperty(nameof(Name)); + } + + return GetProperty("__class__").GetAttr("__name__").GetAndDispose(); + } + } + } + /// /// Creates a new instance /// diff --git a/Algorithm.Framework/Portfolio/RiskParityPortfolioOptimizer.cs b/Algorithm.Framework/Portfolio/RiskParityPortfolioOptimizer.cs index 64cad6f656ce..b77119e8aa63 100644 --- a/Algorithm.Framework/Portfolio/RiskParityPortfolioOptimizer.cs +++ b/Algorithm.Framework/Portfolio/RiskParityPortfolioOptimizer.cs @@ -17,6 +17,8 @@ using System.Linq; using Accord.Math; using Accord.Statistics; +using QuantConnect.Algorithm.Framework.Alphas; +using static System.FormattableString; namespace QuantConnect.Algorithm.Framework.Portfolio { @@ -24,11 +26,16 @@ namespace QuantConnect.Algorithm.Framework.Portfolio /// Provides an implementation of a risk parity portfolio optimizer that calculate the optimal weights /// with the weight range from 0 to 1 and equalize the risk carried by each asset /// - public class RiskParityPortfolioOptimizer : IPortfolioOptimizer + public class RiskParityPortfolioOptimizer : IPortfolioOptimizer, INamedModel { private double _lower = 1e-05; private double _upper = Double.MaxValue; + /// + /// Defines a name for the portfolio optimizer + /// + public string Name { get; } + /// /// Initialize a new instance of /// @@ -38,6 +45,7 @@ public RiskParityPortfolioOptimizer(double? lower = null, double? upper = null) { _lower = lower ?? _lower; // has to be greater than or equal to 0 _upper = upper ?? _upper; + Name = Invariant($"{nameof(RiskParityPortfolioOptimizer)}({_lower},{_upper})"); } /// diff --git a/Algorithm.Framework/Portfolio/UnconstrainedMeanVariancePortfolioOptimizer.cs b/Algorithm.Framework/Portfolio/UnconstrainedMeanVariancePortfolioOptimizer.cs index edcb75c05084..ce04df774711 100644 --- a/Algorithm.Framework/Portfolio/UnconstrainedMeanVariancePortfolioOptimizer.cs +++ b/Algorithm.Framework/Portfolio/UnconstrainedMeanVariancePortfolioOptimizer.cs @@ -15,14 +15,20 @@ using Accord.Math; using Accord.Statistics; +using QuantConnect.Algorithm.Framework.Alphas; namespace QuantConnect.Algorithm.Framework.Portfolio { /// /// Provides an implementation of a portfolio optimizer with unconstrained mean variance. /// - public class UnconstrainedMeanVariancePortfolioOptimizer : IPortfolioOptimizer + public class UnconstrainedMeanVariancePortfolioOptimizer : IPortfolioOptimizer, INamedModel { + /// + /// Defines a name for the portfolio optimizer + /// + public string Name { get; } = nameof(UnconstrainedMeanVariancePortfolioOptimizer); + /// /// Perform portfolio optimization for a provided matrix of historical returns and an array of expected returns /// @@ -37,4 +43,4 @@ public double[] Optimize(double[,] historicalReturns, double[] expectedReturns = return Π.Dot(Σ.Inverse()); } } -} \ No newline at end of file +} diff --git a/Algorithm/Execution/ExecutionModel.cs b/Algorithm/Execution/ExecutionModel.cs index 5407cd10876d..b451dc825611 100644 --- a/Algorithm/Execution/ExecutionModel.cs +++ b/Algorithm/Execution/ExecutionModel.cs @@ -14,6 +14,7 @@ */ using QuantConnect.Algorithm.Framework.Portfolio; +using QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Data.UniverseSelection; using QuantConnect.Orders; using QuantConnect.Python; @@ -23,8 +24,13 @@ namespace QuantConnect.Algorithm.Framework.Execution /// /// Provides a base class for execution models /// - public class ExecutionModel : BasePythonWrapper, IExecutionModel + public class ExecutionModel : BasePythonWrapper, IExecutionModel, INamedModel { + /// + /// Defines a name for the framework model + /// + public virtual string Name { get; set; } + /// /// If true, orders should be submitted asynchronously. /// @@ -36,6 +42,7 @@ public class ExecutionModel : BasePythonWrapper, IExecutionModel /// If true, orders should be submitted asynchronously public ExecutionModel(bool asynchronous = true) { + Name = GetType().Name; Asynchronous = asynchronous; } diff --git a/Algorithm/Execution/ExecutionModelPythonWrapper.cs b/Algorithm/Execution/ExecutionModelPythonWrapper.cs index bfc7848ab390..8cd4160f445b 100644 --- a/Algorithm/Execution/ExecutionModelPythonWrapper.cs +++ b/Algorithm/Execution/ExecutionModelPythonWrapper.cs @@ -29,6 +29,25 @@ public class ExecutionModelPythonWrapper : ExecutionModel { private readonly bool _onOrderEventsDefined; + /// + /// Defines a name for the framework model + /// + public override string Name + { + get + { + using (Py.GIL()) + { + if (HasAttr(nameof(Name))) + { + return GetProperty(nameof(Name)); + } + + return GetProperty("__class__").GetAttr("__name__").GetAndDispose(); + } + } + } + /// /// Constructor for initialising the class with wrapped object /// diff --git a/Algorithm/Portfolio/PortfolioConstructionModel.cs b/Algorithm/Portfolio/PortfolioConstructionModel.cs index bd5a3b5b00fb..3b26da63a3a9 100644 --- a/Algorithm/Portfolio/PortfolioConstructionModel.cs +++ b/Algorithm/Portfolio/PortfolioConstructionModel.cs @@ -27,12 +27,17 @@ namespace QuantConnect.Algorithm.Framework.Portfolio /// /// Provides a base class for portfolio construction models /// - public class PortfolioConstructionModel : IPortfolioConstructionModel + public class PortfolioConstructionModel : IPortfolioConstructionModel, INamedModel { private Func _rebalancingFunc; private DateTime? _rebalancingTime; private bool _securityChanges; + /// + /// Defines a name for the framework model + /// + public virtual string Name { get; set; } + /// /// True if should rebalance portfolio on security changes. True by default /// @@ -63,6 +68,7 @@ public class PortfolioConstructionModel : IPortfolioConstructionModel /// will trigger rebalance. If null will be ignored public PortfolioConstructionModel(Func rebalancingFunc) { + Name = GetType().Name; _rebalancingFunc = rebalancingFunc; } diff --git a/Algorithm/Portfolio/PortfolioConstructionModelPythonWrapper.cs b/Algorithm/Portfolio/PortfolioConstructionModelPythonWrapper.cs index d381df49c248..3e77e08cf096 100644 --- a/Algorithm/Portfolio/PortfolioConstructionModelPythonWrapper.cs +++ b/Algorithm/Portfolio/PortfolioConstructionModelPythonWrapper.cs @@ -30,6 +30,25 @@ public class PortfolioConstructionModelPythonWrapper : PortfolioConstructionMode private readonly BasePythonWrapper _model; private readonly bool _implementsDetermineTargetPercent; + /// + /// Defines a name for the framework model + /// + public override string Name + { + get + { + using (Py.GIL()) + { + if (_model.HasAttr(nameof(Name))) + { + return _model.GetProperty(nameof(Name)); + } + + return _model.GetProperty("__class__").GetAttr("__name__").GetAndDispose(); + } + } + } + /// /// True if should rebalance portfolio on security changes. True by default /// diff --git a/Algorithm/Risk/RiskManagementModel.cs b/Algorithm/Risk/RiskManagementModel.cs index 9e5c0b7f2999..54efb6a7aa16 100644 --- a/Algorithm/Risk/RiskManagementModel.cs +++ b/Algorithm/Risk/RiskManagementModel.cs @@ -14,6 +14,7 @@ */ using System.Collections.Generic; +using QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Algorithm.Framework.Portfolio; using QuantConnect.Data.UniverseSelection; @@ -22,8 +23,21 @@ namespace QuantConnect.Algorithm.Framework.Risk /// /// Provides a base class for risk management models /// - public class RiskManagementModel : IRiskManagementModel + public class RiskManagementModel : IRiskManagementModel, INamedModel { + /// + /// Defines a name for the framework model + /// + public virtual string Name { get; set; } + + /// + /// Initializes a new instance of the class + /// + public RiskManagementModel() + { + Name = GetType().Name; + } + /// /// Manages the algorithm's risk at each time step /// @@ -43,4 +57,4 @@ public virtual void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges c { } } -} \ No newline at end of file +} diff --git a/Algorithm/Risk/RiskManagementModelPythonWrapper.cs b/Algorithm/Risk/RiskManagementModelPythonWrapper.cs index bc7a2a6b243d..362dbb0945f0 100644 --- a/Algorithm/Risk/RiskManagementModelPythonWrapper.cs +++ b/Algorithm/Risk/RiskManagementModelPythonWrapper.cs @@ -28,6 +28,25 @@ public class RiskManagementModelPythonWrapper : RiskManagementModel { private readonly BasePythonWrapper _model; + /// + /// Defines a name for the framework model + /// + public override string Name + { + get + { + using (Py.GIL()) + { + if (_model.HasAttr(nameof(Name))) + { + return _model.GetProperty(nameof(Name)); + } + + return _model.GetProperty("__class__").GetAttr("__name__").GetAndDispose(); + } + } + } + /// /// Constructor for initialising the class with wrapped object /// diff --git a/Algorithm/Selection/UniverseSelectionModel.cs b/Algorithm/Selection/UniverseSelectionModel.cs index 68f070fcca9f..58bcbf46940f 100644 --- a/Algorithm/Selection/UniverseSelectionModel.cs +++ b/Algorithm/Selection/UniverseSelectionModel.cs @@ -15,6 +15,7 @@ using System; using System.Collections.Generic; +using QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Data.UniverseSelection; using QuantConnect.Interfaces; using QuantConnect.Python; @@ -24,14 +25,19 @@ namespace QuantConnect.Algorithm.Framework.Selection /// /// Provides a base class for universe selection models. /// - public class UniverseSelectionModel : BasePythonWrapper, IUniverseSelectionModel + public class UniverseSelectionModel : BasePythonWrapper, IUniverseSelectionModel, INamedModel { + /// + /// Defines a name for the framework model + /// + public virtual string Name { get; set; } /// /// Initializes a new instance of the class. /// public UniverseSelectionModel() { + Name = GetType().Name; } /// @@ -52,4 +58,4 @@ public virtual IEnumerable CreateUniverses(QCAlgorithm algorithm) throw new NotImplementedException("Types deriving from 'UniverseSelectionModel' must implement the 'IEnumerable CreateUniverses(QCAlgorithm) method."); } } -} \ No newline at end of file +} diff --git a/Algorithm/Selection/UniverseSelectionModelPythonWrapper.cs b/Algorithm/Selection/UniverseSelectionModelPythonWrapper.cs index 8f2c88e14804..11fccbfb1ed8 100644 --- a/Algorithm/Selection/UniverseSelectionModelPythonWrapper.cs +++ b/Algorithm/Selection/UniverseSelectionModelPythonWrapper.cs @@ -29,6 +29,25 @@ public class UniverseSelectionModelPythonWrapper : UniverseSelectionModel { private readonly bool _modelHasGetNextRefreshTime; + /// + /// Defines a name for the framework model + /// + public override string Name + { + get + { + using (Py.GIL()) + { + if (HasAttr(nameof(Name))) + { + return GetProperty(nameof(Name)); + } + + return GetProperty("__class__").GetAttr("__name__").GetAndDispose(); + } + } + } + /// /// Gets the next time the framework should invoke the `CreateUniverses` method to refresh the set of universes. /// diff --git a/Tests/Algorithm/Framework/FrameworkModelNameTests.cs b/Tests/Algorithm/Framework/FrameworkModelNameTests.cs new file mode 100644 index 000000000000..49b71e3b01ab --- /dev/null +++ b/Tests/Algorithm/Framework/FrameworkModelNameTests.cs @@ -0,0 +1,68 @@ +/* + * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. + * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +using System.Collections.Generic; +using NUnit.Framework; +using QuantConnect.Algorithm.Framework.Alphas; +using QuantConnect.Algorithm.Framework.Execution; +using QuantConnect.Algorithm.Framework.Portfolio; +using QuantConnect.Algorithm.Framework.Risk; +using QuantConnect.Algorithm.Framework.Selection; + +namespace QuantConnect.Tests.Algorithm.Framework +{ + [TestFixture] + public class FrameworkModelNameTests + { + private static IEnumerable FrameworkComponents + { + get + { + yield return new TestCaseData(new ExecutionModel(), "ExecutionModel"); + yield return new TestCaseData(new PortfolioConstructionModel(), "PortfolioConstructionModel"); + yield return new TestCaseData(new RiskManagementModel(), "RiskManagementModel"); + yield return new TestCaseData(new UniverseSelectionModel(), "UniverseSelectionModel"); + yield return new TestCaseData(new AlphaStreamsPortfolioConstructionModel(), "AlphaStreamsPortfolioConstructionModel"); + } + } + + private static IEnumerable PortfolioOptimizers + { + get + { + yield return new TestCaseData( + new MaximumSharpeRatioPortfolioOptimizer(-2.5, 3.5, 0.125), + "MaximumSharpeRatioPortfolioOptimizer(-2.5,3.5,0.125)"); + yield return new TestCaseData( + new MinimumVariancePortfolioOptimizer(-2.5, 3.5, 0.125), + "MinimumVariancePortfolioOptimizer(-2.5,3.5,0.125)"); + yield return new TestCaseData( + new RiskParityPortfolioOptimizer(0.05, 2.5), + "RiskParityPortfolioOptimizer(0.05,2.5)"); + yield return new TestCaseData( + new UnconstrainedMeanVariancePortfolioOptimizer(), + "UnconstrainedMeanVariancePortfolioOptimizer"); + } + } + + [TestCaseSource(nameof(FrameworkComponents))] + [TestCaseSource(nameof(PortfolioOptimizers))] + public void FrameworkComponentProvidesExpectedName(object component, string expectedName) + { + Assert.IsInstanceOf(component); + Assert.AreEqual(expectedName, ((INamedModel)component).Name); + } + } +}