A data array accessor. Provides access to OHLCV "columns" as a standard `pd.DataFrame` would, except it's not a DataFrame and the returned "series" are _not_ `pd.Series` but `np.ndarray` for performance reasons.
| 154 | |
| 155 | |
| 156 | class _Data: |
| 157 | """ |
| 158 | A data array accessor. Provides access to OHLCV "columns" |
| 159 | as a standard `pd.DataFrame` would, except it's not a DataFrame |
| 160 | and the returned "series" are _not_ `pd.Series` but `np.ndarray` |
| 161 | for performance reasons. |
| 162 | """ |
| 163 | def __init__(self, df: pd.DataFrame): |
| 164 | self.__df = df |
| 165 | self.__len = len(df) # Current length |
| 166 | self.__pip: Optional[float] = None |
| 167 | self.__cache: Dict[str, _Array] = {} |
| 168 | self.__arrays: Dict[str, _Array] = {} |
| 169 | self._update() |
| 170 | |
| 171 | def __getitem__(self, item): |
| 172 | return self.__get_array(item) |
| 173 | |
| 174 | def __getattr__(self, item): |
| 175 | try: |
| 176 | return self.__get_array(item) |
| 177 | except KeyError: |
| 178 | raise AttributeError(f"Column '{item}' not in data") from None |
| 179 | |
| 180 | def _set_length(self, length): |
| 181 | self.__len = length |
| 182 | self.__cache.clear() |
| 183 | |
| 184 | def _update(self): |
| 185 | index = self.__df.index.copy() |
| 186 | self.__arrays = {col: _Array(arr, index=index) |
| 187 | for col, arr in self.__df.items()} |
| 188 | # Leave index as Series because pd.Timestamp nicer API to work with |
| 189 | self.__arrays['__index'] = index |
| 190 | |
| 191 | def __repr__(self): |
| 192 | i = min(self.__len, len(self.__df)) - 1 |
| 193 | index = self.__arrays['__index'][i] |
| 194 | items = ', '.join(f'{k}={v}' for k, v in self.__df.iloc[i].items()) |
| 195 | return f'<Data i={i} ({index}) {items}>' |
| 196 | |
| 197 | def __len__(self): |
| 198 | return self.__len |
| 199 | |
| 200 | @property |
| 201 | def df(self) -> pd.DataFrame: |
| 202 | return (self.__df.iloc[:self.__len] |
| 203 | if self.__len < len(self.__df) |
| 204 | else self.__df) |
| 205 | |
| 206 | @property |
| 207 | def pip(self) -> float: |
| 208 | if self.__pip is None: |
| 209 | self.__pip = float(10**-np.median([len(s.partition('.')[-1]) |
| 210 | for s in self.__arrays['Close'].astype(str)])) |
| 211 | return self.__pip |
| 212 | |
| 213 | def __get_array(self, key) -> _Array: |