def init(self): self.sma = self.I(SMA, self.data.Close, 10) self.remains_indicator = np.r_[2] * np.cumsum(self.sma * 5 + 1) * np.r_[2] self.transpose_invalid = self.I(lambda: np.column_stack((self.data.Open, self.data.Close))) resampled = resample_apply('W', SMA, self.data.Close, 3) resampled_ind = resample_apply('W', SMA, self.sma, 3) assert np.unique(resampled[-5:]).size == 1 assert np.unique(resampled[-6:]).size == 2 assert resampled in self._indicators, "Strategy.I not called" assert resampled_ind in self._indicators, "Strategy.I not called" assert 1 == try_(lambda: self.data.X, 1, AttributeError) assert 1 == try_(lambda: self.data['X'], 1, KeyError) assert self.data.pip == .01 assert float(self.data.Close) == self.data.Close[-1]
def loop_through_data(broker, data, indicator_attrs, start, strategy, self_data): with np.errstate(invalid='ignore'): for i in range(start, len(self_data)): data._set_length(i + 1) for attr, indicator in indicator_attrs: setattr(strategy, attr, indicator[..., :i + 1]) try: broker.next() except _OutOfMoneyError: break strategy.next() else: for trade in broker.trades: trade.close() if start < len(self_data): try_(broker.next, exception=_OutOfMoneyError) return get_perf_metrics(broker, data, strategy, self_data)
def next(self): try_(self.step.__next__, None, StopIteration)
def I( self, func: Callable, *args, name=None, plot=True, overlay=None, color=None, scatter=False, **kwargs, ) -> np.ndarray: if name is None: params = ','.join(filter(None, map(_as_str, chain(args, kwargs.values())))) func_name = _as_str(func) name = f'{func_name}({params})' if params else f'{func_name}' else: name = name.format( *map(_as_str, args), **dict(zip(kwargs.keys(), map(_as_str, kwargs.values()))), ) try: value = func(*args, **kwargs) except Exception as e: raise RuntimeError(f'I "{name}" errored with exception: {e}') if isinstance(value, pd.DataFrame): value = value.values.T if value is not None: value = try_(lambda: np.asarray(value, order='C'), None) is_arraylike = value is not None # Optionally flip the array if the user returned e.g. `df.values` if is_arraylike and np.argmax(value.shape) == 0: value = value.T if not is_arraylike or not 1 <= value.ndim <= 2 or value.shape[-1] != len(self._data.Close): raise ValueError( 'Indicators must return (optionally a tuple of) numpy.arrays of same ' f'length as `data` (data shape: {self._data.Close.shape}; indicator "{name}"' f'shape: {getattr(value, "shape" , "")}, returned value: {value})' ) if plot and overlay is None and np.issubdtype(value.dtype, np.number): x = value / self._data.Close # By default, overlay if strong majority of indicator values # is within 30% of Close with np.errstate(invalid='ignore'): overlay = ((x < 1.4) & (x > 0.6)).mean() > 0.6 value = _Indicator( value, name=name, plot=plot, overlay=overlay, color=color, scatter=scatter, index=self.data.index, ) self._indicators.append(value) return value