/* * 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.Data; using System; namespace QuantConnect.Indicators { /// /// Provides a wrapper for implementations written in python /// public class PythonIndicator : IndicatorBase { private bool _isReady; private dynamic _indicator; /// /// Get the indicator Name. If not defined, use the class name /// /// The python implementation of /// The indicator Name. private static string GetIndicatorName(PyObject indicator) { using (Py.GIL()) { var name = indicator.HasAttr("Name") ? indicator.GetAttr("Name") : indicator.GetAttr("__class__").GetAttr("__name__"); return name.GetAndDispose(); } } /// /// Initializes a new instance of the PythonIndicator class using the specified name. /// /// This overload allows inheritance for python classes with no arguments public PythonIndicator() : base("") { } /// /// Initializes a new instance of the PythonIndicator class using the specified name. /// /// This overload allows inheritance for python classes with multiple arguments public PythonIndicator(params PyObject[] args) : base (GetIndicatorName(args[0])) { } /// /// Initializes a new instance of the PythonIndicator class using the specified name. /// /// The python implementation of public PythonIndicator(PyObject indicator) : base(GetIndicatorName(indicator)) { SetIndicator(indicator); } /// /// Sets the python implementation of the indicator /// /// The python implementation of public void SetIndicator(PyObject indicator) { using (Py.GIL()) { foreach (var attributeName in new[] {"IsReady", "Update", "Value"}) { if (!indicator.HasAttr(attributeName)) { var name = indicator.GetAttr("__class__").GetAttr("__name__"); var message = $"Indicator.{attributeName} must be implemented. " + $"Please implement this missing method in {name}"; if (attributeName == "IsReady") { message += " or use PythonIndicator as base:" + $"{Environment.NewLine}class {name}(PythonIndicator):"; } throw new NotImplementedException(message); } } } _indicator = indicator; } /// /// Gets a flag indicating when this indicator is ready and fully initialized /// public override bool IsReady => _isReady; /// /// Computes the next value of this indicator from the given state /// /// The input given to the indicator /// A new value for this indicator protected override decimal ComputeNextValue(IBaseData input) { using (Py.GIL()) { _isReady = _indicator.Update(input) ?? _indicator.IsReady; return _indicator.Value; } } } }