1+ /*
2+ * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3+ * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4+ *
5+ * Licensed under the Apache License, Version 2.0 (the "License");
6+ * you may not use this file except in compliance with the License.
7+ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8+ *
9+ * Unless required by applicable law or agreed to in writing, software
10+ * distributed under the License is distributed on an "AS IS" BASIS,
11+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ * See the License for the specific language governing permissions and
13+ * limitations under the License.
14+ */
15+
16+ using Python . Runtime ;
17+ using QuantConnect . Data ;
18+ using QuantConnect . Data . Market ;
19+ using System ;
20+
21+ namespace QuantConnect . Indicators
22+ {
23+ /// <summary>
24+ /// Provides a wrapper for <see cref="IndicatorBase{IBaseDataBar}"/> implementations written in python
25+ /// </summary>
26+ public class PythonIndicator : IndicatorBase < IBaseDataBar >
27+ {
28+ private readonly dynamic _indicator ;
29+
30+ /// <summary>
31+ /// Get the indicator Name. If not defined, use the class name
32+ /// </summary>
33+ /// <param name="indicator">The python implementation of <see cref="IndicatorBase{IBaseDataBar}"/></param>
34+ /// <returns>The indicator Name.</returns>
35+ private static string GetIndicatorName ( PyObject indicator )
36+ {
37+ using ( Py . GIL ( ) )
38+ {
39+ var name = indicator . HasAttr ( "Name" )
40+ ? indicator . GetAttr ( "Name" )
41+ : indicator . GetAttr ( "__class__" ) . GetAttr ( "__name__" ) ;
42+
43+ return name . GetAndDispose < string > ( ) ;
44+ }
45+ }
46+
47+ /// <summary>
48+ /// Initializes a new instance of the PythonIndicator class using the specified name.
49+ /// </summary>
50+ /// <param name="indicator">The python implementation of <see cref="IndicatorBase{IBaseDataBar}"/></param>
51+ public PythonIndicator ( PyObject indicator )
52+ : base ( GetIndicatorName ( indicator ) )
53+ {
54+ using ( Py . GIL ( ) )
55+ {
56+ foreach ( var attributeName in new [ ] { "Update" , "IsReady" , "Value" } )
57+ {
58+ if ( ! indicator . HasAttr ( attributeName ) )
59+ {
60+ throw new NotImplementedException (
61+ $ "Indicator.{ attributeName } must be implemented. Please implement this missing method on { indicator . GetPythonType ( ) } "
62+ ) ;
63+ }
64+ }
65+ }
66+
67+ _indicator = indicator ;
68+ }
69+
70+ /// <summary>
71+ /// Updates the state of this indicator with the given value and returns true
72+ /// if this indicator is ready, false otherwise
73+ /// </summary>
74+ /// <param name="input">The value to use to update this indicator</param>
75+ /// <returns>True if this indicator is ready, false otherwise</returns>
76+ public new bool Update ( IBaseData input )
77+ {
78+ using ( Py . GIL ( ) )
79+ {
80+ _indicator . Update ( input ) ;
81+ }
82+
83+ return IsReady ;
84+ }
85+
86+ /// <summary>
87+ /// Gets a flag indicating when this indicator is ready and fully initialized
88+ /// </summary>
89+ /// <remarks>If IsReady is not defined in the Python indicator, always returns false</remarks>
90+ public override bool IsReady
91+ {
92+ get
93+ {
94+ using ( Py . GIL ( ) )
95+ {
96+ return _indicator . IsReady ;
97+ }
98+ }
99+ }
100+
101+ /// <summary>
102+ /// Computes the next value of this indicator from the given state
103+ /// </summary>
104+ /// <param name="input">The input given to the indicator</param>
105+ /// <returns>A new value for this indicator</returns>
106+ protected override decimal ComputeNextValue ( IBaseDataBar input )
107+ {
108+ Update ( input ) ;
109+
110+ using ( Py . GIL ( ) )
111+ {
112+ return _indicator . Value ;
113+ }
114+ }
115+ }
116+ }
0 commit comments