1
+ import warnings
1
2
from typing import Sequence
2
3
from numbers import Number
3
4
@@ -44,12 +45,10 @@ class _Array(np.ndarray):
44
45
ndarray extended to supply .name and other arbitrary properties
45
46
in ._opts dict.
46
47
"""
47
- def __new__ (cls , array , * , name = None , write = False , ** kwargs ):
48
+ def __new__ (cls , array , * , name = None , ** kwargs ):
48
49
obj = np .asarray (array ).view (cls )
49
50
obj .name = name or array .name
50
51
obj ._opts = kwargs
51
- if not write :
52
- obj .setflags (write = False )
53
52
return obj
54
53
55
54
def __array_finalize__ (self , obj ):
@@ -70,7 +69,20 @@ def __float__(self):
70
69
return super ().__float__ ()
71
70
72
71
def to_series (self ):
73
- return pd .Series (self , index = self ._opts ['data' ].index , name = self .name )
72
+ warnings .warn ("`.to_series()` is deprecated. For pd.Series conversion, use accessor `.s`" )
73
+ return self .s
74
+
75
+ @property
76
+ def s (self ) -> pd .Series :
77
+ values = np .atleast_2d (self )
78
+ return pd .Series (values [0 ], index = self ._opts ['data' ].index , name = self .name )
79
+
80
+ @property
81
+ def df (self ) -> pd .DataFrame :
82
+ values = np .atleast_2d (np .asarray (self ))
83
+ df = pd .DataFrame (values .T , index = self ._opts ['data' ].index ,
84
+ columns = [self .name ] * len (values ))
85
+ return df
74
86
75
87
76
88
class _Indicator (_Array ):
@@ -84,15 +96,13 @@ class _Data:
84
96
and the returned "series" are _not_ `pd.Series` but `np.ndarray`
85
97
for performance reasons.
86
98
"""
87
- def __init__ (self , df ):
99
+ def __init__ (self , df : pd .DataFrame ):
100
+ self .__df = df
88
101
self .__i = len (df )
89
102
self .__pip = None
90
103
self .__cache = {}
91
-
92
- self .__arrays = {col : _Array (arr , data = self )
93
- for col , arr in df .items ()}
94
- # Leave index as Series because pd.Timestamp nicer API to work with
95
- self .__arrays ['__index' ] = df .index .copy ()
104
+ self .__arrays = None
105
+ self ._update ()
96
106
97
107
def __getitem__ (self , item ):
98
108
return self .__get_array (item )
@@ -107,17 +117,35 @@ def _set_length(self, i):
107
117
self .__i = i
108
118
self .__cache .clear ()
109
119
120
+ def _update (self ):
121
+ self .__arrays = {col : _Array (arr , data = self )
122
+ for col , arr in self .__df .items ()}
123
+ # Leave index as Series because pd.Timestamp nicer API to work with
124
+ self .__arrays ['__index' ] = self .__df .index .copy ()
125
+
126
+ def __repr__ (self ):
127
+ i = min (self .__i , len (self .__df ) - 1 )
128
+ return '<Data i={} ({}) {}>' .format (i , self .__arrays ['__index' ][i ],
129
+ ', ' .join ('{}={}' .format (k , v )
130
+ for k , v in self .__df .iloc [i ].items ()))
131
+
110
132
def __len__ (self ):
111
133
return self .__i
112
134
135
+ @property
136
+ def df (self ) -> pd .DataFrame :
137
+ return (self .__df .iloc [:self .__i ]
138
+ if self .__i < len (self .__df )
139
+ else self .__df )
140
+
113
141
@property
114
142
def pip (self ):
115
143
if self .__pip is None :
116
144
self .__pip = 10 ** - np .median ([len (s .partition ('.' )[- 1 ])
117
145
for s in self .__arrays ['Close' ].astype (str )])
118
146
return self .__pip
119
147
120
- def __get_array (self , key ):
148
+ def __get_array (self , key ) -> _Array :
121
149
arr = self .__cache .get (key )
122
150
if arr is None :
123
151
arr = self .__cache [key ] = self .__arrays [key ][:self .__i ]
@@ -144,8 +172,8 @@ def Volume(self):
144
172
return self .__get_array ('Volume' )
145
173
146
174
@property
147
- def index (self ):
148
- return self .__get_array ('__index' )
175
+ def index (self ) -> pd . DatetimeIndex :
176
+ return self .__get_array ('__index' ) # type: ignore
149
177
150
178
# Make pickling in Backtest.optimize() work with our catch-all __getattr__
151
179
def __getstate__ (self ):
0 commit comments