/* * 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 Python.Runtime; using QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Data.UniverseSelection; using QuantConnect.Python; using System; using System.Collections.Generic; namespace QuantConnect.Algorithm.Framework.Portfolio { /// /// Provides an implementation of that wraps a object /// public class PortfolioConstructionModelPythonWrapper : PortfolioConstructionModel { private readonly BasePythonWrapper _model; private readonly bool _implementsDetermineTargetPercent; /// /// True if should rebalance portfolio on security changes. True by default /// public override bool RebalanceOnSecurityChanges { get { return _model.GetProperty(nameof(RebalanceOnSecurityChanges)); } set { _model.SetProperty(nameof(RebalanceOnSecurityChanges), value); } } /// /// True if should rebalance portfolio on new insights or expiration of insights. True by default /// public override bool RebalanceOnInsightChanges { get { return _model.GetProperty(nameof(RebalanceOnInsightChanges)); } set { _model.SetProperty(nameof(RebalanceOnInsightChanges), value); } } /// /// Constructor for initialising the class with wrapped object /// /// Model defining how to build a portfolio from alphas public PortfolioConstructionModelPythonWrapper(PyObject model) { _model = new BasePythonWrapper(model, false); using (Py.GIL()) { foreach (var attributeName in new[] { "CreateTargets", "OnSecuritiesChanged" }) { if (!_model.HasAttr(attributeName)) { throw new NotImplementedException($"IPortfolioConstructionModel.{attributeName} must be implemented. Please implement this missing method on {model.GetPythonType()}"); } } _model.InvokeVoidMethod(nameof(SetPythonWrapper), this); _implementsDetermineTargetPercent = model.GetPythonMethod("DetermineTargetPercent") != null; } } /// /// Create portfolio targets from the specified insights /// /// The algorithm instance /// The insights to create portfolio targets from /// An enumerable of portfolio targets to be sent to the execution model public override IEnumerable CreateTargets(QCAlgorithm algorithm, Insight[] insights) { return _model.InvokeMethodAndEnumerate(nameof(CreateTargets), algorithm, insights); } /// /// Event fired each time the we add/remove securities from the data feed /// /// The algorithm instance that experienced the change in securities /// The security additions and removals from the algorithm public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes) { _model.InvokeVoidMethod(nameof(OnSecuritiesChanged), algorithm, changes); } /// /// Method that will determine if the portfolio construction model should create a /// target for this insight /// /// The insight to create a target for /// True if the portfolio should create a target for the insight protected override bool ShouldCreateTargetForInsight(Insight insight) { return _model.InvokeMethod(nameof(ShouldCreateTargetForInsight), insight); } /// /// Determines if the portfolio should be rebalanced base on the provided rebalancing func, /// if any security change have been taken place or if an insight has expired or a new insight arrived /// If the rebalancing function has not been provided will return true. /// /// The insights to create portfolio targets from /// The current algorithm UTC time /// True if should rebalance protected override bool IsRebalanceDue(Insight[] insights, DateTime algorithmUtc) { return _model.InvokeMethod(nameof(IsRebalanceDue), insights, algorithmUtc); } /// /// Gets the target insights to calculate a portfolio target percent for /// /// An enumerable of the target insights protected override List GetTargetInsights() { return _model.InvokeMethod>(nameof(GetTargetInsights)); } /// /// Will determine the target percent for each insight /// /// The active insights to generate a target for /// A target percent for each insight protected override Dictionary DetermineTargetPercent(List activeInsights) { if (!_implementsDetermineTargetPercent) { // the implementation is in C# return _model.InvokeMethod>(nameof(DetermineTargetPercent), activeInsights); } return _model.InvokeMethodAndGetDictionary(nameof(DetermineTargetPercent), activeInsights); } } }