@@ -26,7 +26,7 @@ def _tqdm(seq, **_):
26
26
return seq
27
27
28
28
from ._plotting import plot
29
- from ._util import _as_str , _Indicator , _Data , _data_period
29
+ from ._util import _as_str , _Indicator , _Data , _data_period , try_
30
30
31
31
32
32
__pdoc__ = {
@@ -121,18 +121,26 @@ def init():
121
121
name = name .format (* map (_as_str , args ),
122
122
** dict (zip (kwargs .keys (), map (_as_str , kwargs .values ()))))
123
123
124
- value = func (* args , ** kwargs )
125
-
126
124
try :
127
- if isinstance (value , pd .DataFrame ):
128
- value = value .values .T
129
- value = np .asarray (value )
130
- except Exception :
131
- raise ValueError ('Indicators must return array-like sequences of values' )
132
- if value .shape [- 1 ] != len (self ._data .Close ):
133
- raise ValueError ('Indicators must be (a tuple of) arrays of same length as `data`'
134
- '(data: {}, indicator "{}": {})' .format (len (self ._data .Close ),
135
- name , value .shape ))
125
+ value = func (* args , ** kwargs )
126
+ except Exception as e :
127
+ raise RuntimeError ('Indicator "{}" errored with exception: {}' .format (name , e ))
128
+
129
+ if isinstance (value , pd .DataFrame ):
130
+ value = value .values .T
131
+
132
+ value = try_ (lambda : np .asarray (value , order = 'C' ), None )
133
+ is_arraylike = value is not None
134
+
135
+ # Optionally flip the array if the user returned e.g. `df.values`
136
+ if is_arraylike and np .argmax (value .shape ) == 0 :
137
+ value = value .T
138
+
139
+ if not is_arraylike or not 1 <= value .ndim <= 2 or value .shape [- 1 ] != len (self ._data .Close ):
140
+ raise ValueError (
141
+ 'Indicators must return (optionally a tuple of) numpy.arrays of same '
142
+ 'length as `data`(data shape: {}; indicator "{}" shape: {}, value: {})'
143
+ .format (self ._data .Close .shape , name , getattr (value , 'shape' , '' ), value ))
136
144
137
145
if plot and overlay is None and np .issubdtype (value .dtype , np .number ):
138
146
x = value / self ._data .Close
0 commit comments