class RSRS(IndicatorAbstract): def __init__(self, _N: int = 30, _use_key: typing.List[str] = ['highprice', 'lowprice'], _idx_key: str = 'time', _ret_key: str = 'rsrs'): """ _N: sample length """ super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct([self.idx_key, self.ret_key], self.idx_key) self.N = _N self.high_buf = deque(maxlen=self.N) self.low_buf = deque(maxlen=self.N) def _addOne(self, _data_struct: DataStruct): index_value = _data_struct.index()[0] self.high_buf.append(_data_struct[self.use_key[0]][-1]) self.low_buf.append(_data_struct[self.use_key[1]][-1]) if len(self.high_buf) >= self.N and len(self.low_buf) >= self.N: x = np.arange(self.N) high_beta = linregress(x, self.high_buf)[0] low_beta = linregress(x, self.low_buf)[0] self.data.addDict({ self.idx_key: index_value, self.ret_key: high_beta - low_beta, })
class SharpRate(IndicatorAbstract): def __init__( self, _period: int, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'sharprate', ): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct([self.idx_key, self.ret_key], self.idx_key) self.last_price = None self.period = _period self.buf = deque(maxlen=self.period) def _addOne(self, _data_struct: DataStruct): index_value = _data_struct.index()[0] price_value = _data_struct[self.use_key][0] if self.last_price is not None: chg_rate = price_value / self.last_price - 1 self.buf.append(chg_rate) buf_std = statistics.pstdev(self.buf) if buf_std != 0: self.data.addDict({ self.idx_key: index_value, self.ret_key: statistics.mean(self.buf) / buf_std, }) self.last_price = price_value
class LogReturn(IndicatorAbstract): def __init__(self, _skip_period: int = 1, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'logreturn'): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct([self.idx_key, self.ret_key], self.idx_key) self.skip_period = _skip_period self.buf = deque(maxlen=self.skip_period) def _addOne(self, _data_struct: DataStruct): index = _data_struct.index()[0] value = _data_struct[self.use_key][0] if len(self.buf) >= self.skip_period: last_value = self.buf.popleft() chg_rate = math.log(value / last_value) self.data.addDict({ self.idx_key: index, self.ret_key: chg_rate, }) self.buf.append(value)
class EFF(IndicatorAbstract): def __init__(self, _period: int, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'eff'): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct([self.idx_key, self.ret_key], self.idx_key) self.period = _period self.buf = deque(maxlen=self.period) def _addOne(self, _data_struct: DataStruct): index_value = _data_struct.index()[0] self.buf.append(_data_struct.getColumn(self.use_key)[0]) if len(self.buf) == self.period: buf_list = list(self.buf) tmp = 0.0 for a, b in zip(buf_list[:-1], buf_list[1:]): tmp += abs(b - a) self.data.addDict({ self.idx_key: index_value, self.ret_key: (self.buf[-1] - self.buf[0]) / tmp, })
class BIAS(IndicatorAbstract): """ rolling ma """ def __init__(self, _period: int, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'bias'): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct([self.idx_key, self.ret_key], self.idx_key) self.period = _period self.buf = deque(maxlen=self.period) def _addOne(self, _data_struct: DataStruct): index_value = _data_struct.index()[0] price = _data_struct[self.use_key][0] self.buf.append(price) price_mean = statistics.mean(self.buf) self.data.addDict({ self.idx_key: index_value, self.ret_key: (price - price_mean) / price_mean * 100, })
class EMA(IndicatorAbstract): def __init__( self, _period: int, _use_key: str='closeprice', _idx_key: str = 'time', _ret_key: str = 'ema' ): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) self.period = _period def _addOne(self, _data_struct: DataStruct): index_value = _data_struct.index()[0] tmp_value = _data_struct[self.use_key][0] if len(self) > 0: last_ret = self.getLastData().toDict()[self.ret_key] tmp_value = (tmp_value - last_ret) / self.period + last_ret self.data.addDict({ self.idx_key: index_value, self.ret_key: tmp_value, })
class Kurtosis(IndicatorAbstract): def __init__( self, _period: int, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'kurtosis' ): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) self.period = _period self.last_price = None self.buf = deque(maxlen=self.period) def _addOne(self, _data_struct: DataStruct): index = _data_struct.index()[0] price = _data_struct[self.use_key][0] if self.last_price: self.buf.append(math.log(price / self.last_price)) if len(self.buf) >= self.period: self.data.addDict({ self.idx_key: index, self.ret_key: kurtosis(self.buf), }) self.last_price = price
class ATRConstStop(StopIndicatorAbstract): def __init__( self, _price_data: DataStruct, _atr_data: DataStruct, _stop_type: int, _rate: float = 3, _price_use_key: str = 'closeprice', _atr_use_key: str = 'atr', _idx_key: str = 'time', _ret_key: str = 'stopprice', ): super().__init__() assert len(_price_data) == 1 assert len(_atr_data) == 1 self.stop_type = _stop_type self.rate = _rate self.price_use_key = _price_use_key self.atr_use_key = _atr_use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key, ) price_value = _price_data[self.price_use_key][0] atr_value = _atr_data[self.atr_use_key][0] if self.stop_type == SignalType.LONG: self.stopprice = price_value - self.rate * atr_value elif self.stop_type == SignalType.SHORT: self.stopprice = price_value + self.rate * atr_value else: raise Exception('unknown type') self._addOne(_price_data) def _addOne( self, _price_data: DataStruct, ): self.data.addDict({ self.idx_key: _price_data.index()[0], self.ret_key: self.stopprice, }) def _isStop(self, _data_struct: DataStruct): price = _data_struct.toDict()[self.price_use_key] stop_price = self.data[self.ret_key][-1] if self.stop_type == SignalType.LONG: if price < stop_price: self.is_stop = True elif self.stop_type == SignalType.SHORT: if price > stop_price: self.is_stop = True else: raise Exception('unknown type')
class STD(IndicatorAbstract): def __init__( self, _period: int, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'std' ): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) self.period = _period self.buf = deque(maxlen=self.period) def _addOne(self, _data_struct: DataStruct): index_value = _data_struct.index()[0] self.buf.append(_data_struct.getColumn(self.use_key)[0]) self.data.addDict({ self.idx_key: index_value, self.ret_key: statistics.pstdev(self.buf), })
class Diff(IndicatorAbstract): def __init__(self, _use_key: str, _init_value: float = None, _idx_key: str = 'time', _ret_key: str = 'diff'): """ :param _use_key: :param _init_value: if init_value set, the first ret will be complated, else 0 :param _idx_key: :param _ret_key: """ super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct([self.idx_key, self.ret_key], self.idx_key) self.last_value = _init_value def _addOne(self, _data_struct: DataStruct): index_value = _data_struct.index()[0] cur_value = _data_struct[self.use_key][0] if self.last_value is not None: diff_value = cur_value - self.last_value self.data.addDict({ self.idx_key: index_value, self.ret_key: diff_value, }) self.last_value = cur_value
class EFF(IndicatorAbstract): def __init__( self, _period: int, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'eff' ): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) self.period = _period self.buf = deque(maxlen=self.period) def _addOne(self, _data_struct: DataStruct): index_value = _data_struct.index()[0] self.buf.append(_data_struct.getColumn(self.use_key)[0]) if len(self.buf) == self.period: buf_list = list(self.buf) tmp = 0.0 for a, b in zip(buf_list[:-1], buf_list[1:]): tmp += abs(b - a) self.data.addDict({ self.idx_key: index_value, self.ret_key: (self.buf[-1] - self.buf[0]) / tmp, })
class BIAS(IndicatorAbstract): """ rolling ma """ def __init__( self, _period: int, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'bias' ): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) self.period = _period self.buf = deque(maxlen=self.period) def _addOne(self, _data_struct: DataStruct): index_value = _data_struct.index()[0] price = _data_struct[self.use_key][0] self.buf.append(price) price_mean = statistics.mean(self.buf) self.data.addDict({ self.idx_key: index_value, self.ret_key: (price - price_mean) / price_mean * 100, })
class Kurtosis(IndicatorAbstract): def __init__(self, _period: int, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'kurtosis'): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct([self.idx_key, self.ret_key], self.idx_key) self.period = _period self.last_price = None self.buf = deque(maxlen=self.period) def _addOne(self, _data_struct: DataStruct): index = _data_struct.index()[0] price = _data_struct[self.use_key][0] if self.last_price: self.buf.append(math.log(price / self.last_price)) if len(self.buf) >= self.period: self.data.addDict({ self.idx_key: index, self.ret_key: kurtosis(self.buf), }) self.last_price = price
class LogReturn(IndicatorAbstract): def __init__( self, _skip_period: int = 1, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'logreturn' ): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) self.skip_period = _skip_period self.buf = deque(maxlen=self.skip_period) def _addOne(self, _data_struct: DataStruct): index = _data_struct.index()[0] value = _data_struct[self.use_key][0] if len(self.buf) >= self.skip_period: last_value = self.buf.popleft() chg_rate = math.log(value / last_value) self.data.addDict({ self.idx_key: index, self.ret_key: chg_rate, }) self.buf.append(value)
def getSettlementData(self): tmp = DataStruct(['tradingday', 'fund', 'commission', 'margin'], 'tradingday') for d in self.settlement_record: tmp.addDict(d) return tmp
class MAX(IndicatorAbstract): """ rolling simple ma """ def __init__( self, _period: int, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'max' ): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) self.period = _period self.buf = deque(maxlen=self.period) def _addOne(self, _data_struct: DataStruct): index_value = _data_struct.index()[0] self.buf.append(_data_struct.getColumn(self.use_key)[0]) self.data.addDict({ self.idx_key: index_value, self.ret_key: max(self.buf), })
class SharpRate(IndicatorAbstract): def __init__( self, _period: int, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'sharprate', ): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) self.last_price = None self.period = _period self.buf = deque(maxlen=self.period) def _addOne(self, _data_struct: DataStruct): index_value = _data_struct.index()[0] price_value = _data_struct[self.use_key][0] if self.last_price is not None: chg_rate = price_value / self.last_price - 1 self.buf.append(chg_rate) buf_std = statistics.pstdev(self.buf) if buf_std != 0: self.data.addDict({ self.idx_key: index_value, self.ret_key: statistics.mean(self.buf) / buf_std, }) self.last_price = price_value
class FastVolatility(IndicatorAbstract): def __init__( self, _period: int, _factor: int = 1, _smooth: int = 1, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'volatility', ): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) self.last_price = None self.period = _period self.factor = math.sqrt(_factor) self.smooth = _smooth self.buf = deque(maxlen=self.period) self.mean = 0.0 self.sum_of_pow = 0.0 def _addOne(self, _data_struct: DataStruct): index_value = _data_struct.index()[0] price_value = _data_struct[self.use_key][0] if self.last_price is not None: chg_rate = price_value / self.last_price - 1 if len(self.buf) >= self.period: last_value = self.buf.popleft() self.buf.append(chg_rate) self.sum_of_pow += chg_rate ** 2 self.sum_of_pow -= last_value ** 2 self.mean += (chg_rate - last_value) / self.period else: n = len(self.buf) self.buf.append(chg_rate) self.sum_of_pow += chg_rate ** 2 self.mean = (self.mean * n + chg_rate) / len(self.buf) var = self.sum_of_pow / len(self.buf) - self.mean ** 2 std_value = math.sqrt(max(0.0, var)) * self.factor if self.smooth > 1 and len(self.data): last_std_value = self.data[self.ret_key][-1] std_value = ( (self.smooth - 1) * last_std_value + std_value ) / self.smooth self.data.addDict({ self.idx_key: index_value, self.ret_key: std_value, }) self.last_price = price_value
def settlement(self, _backtest_key) -> DataStruct: settlement_list = self.fetchSettlementRecords(_backtest_key) keys = ['tradingday', 'type', 'fund', 'commission', 'margin'] ret = DataStruct(keys, 'tradingday') for d in settlement_list: ret.addDict(d) return ret
def getSignalData(self): tmp = DataStruct( ['strategy', 'tradingday', 'datetime', 'symbol', 'strength'], 'datetime') for d in self.signal_record: tmp.addDict(d) return tmp
class FastVolatility(IndicatorAbstract): def __init__( self, _period: int, _factor: int = 1, _smooth: int = 1, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'volatility', ): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct([self.idx_key, self.ret_key], self.idx_key) self.last_price = None self.period = _period self.factor = math.sqrt(_factor) self.smooth = _smooth self.buf = deque(maxlen=self.period) self.mean = 0.0 self.sum_of_pow = 0.0 def _addOne(self, _data_struct: DataStruct): index_value = _data_struct.index()[0] price_value = _data_struct[self.use_key][0] if self.last_price is not None: chg_rate = price_value / self.last_price - 1 if len(self.buf) >= self.period: last_value = self.buf.popleft() self.buf.append(chg_rate) self.sum_of_pow += chg_rate**2 self.sum_of_pow -= last_value**2 self.mean += (chg_rate - last_value) / self.period else: n = len(self.buf) self.buf.append(chg_rate) self.sum_of_pow += chg_rate**2 self.mean = (self.mean * n + chg_rate) / len(self.buf) std_value = math.sqrt(self.sum_of_pow / len(self.buf) - self.mean**2) * self.factor if self.smooth > 1 and len(self.data): last_std_value = self.data[self.ret_key][-1] std_value = ((self.smooth - 1) * last_std_value + std_value) / self.smooth self.data.addDict({ self.idx_key: index_value, self.ret_key: std_value, }) self.last_price = price_value
class RateConstStop(StopIndicatorAbstract): def __init__( self, _data: DataStruct, _stop_type: int, _stop_rate: float = 0.05, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'stopprice', ): super().__init__() assert len(_data) == 1 self.stop_type = _stop_type self.stop_rate = _stop_rate self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key price = _data[self.use_key][0] if self.stop_type == SignalType.LONG: stop_price = price * (1 - self.stop_rate) elif self.stop_type == SignalType.SHORT: stop_price = price * (1 + self.stop_rate) else: raise Exception('unknown type') time = _data.index()[0] self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key, [[time, stop_price]] ) def _addOne(self, _data_struct: DataStruct): stop_price = self.data[self.ret_key][-1] time = _data_struct.index()[0] self.data.addDict({ self.idx_key: time, self.ret_key: stop_price, }) def _isStop(self, _data_struct: DataStruct): price = _data_struct[self.use_key][0] stop_price = self.data[self.ret_key][-1] if self.stop_type == SignalType.LONG: if price < stop_price: self.is_stop = True elif self.stop_type == SignalType.SHORT: if price > stop_price: self.is_stop = True else: raise Exception('unknown type')
def settlement(self, _backtest_key) -> DataStruct: settlement_list = self.fetchSettlementRecords(_backtest_key) keys = [ 'tradingday', 'type', 'fund', 'commission', 'margin' ] ret = DataStruct(keys, 'tradingday') for d in settlement_list: ret.addDict(d) return ret
def getFillData(self): tmp = DataStruct([ 'strategy', 'tradingday', 'datetime', 'symbol', 'index', 'action', 'direction', 'price', 'quantity', 'commission' ], 'datetime') for d in self.fill_record: d = copy(d) d['action'] = ActionType.toStr(d['action']) d['direction'] = DirectionType.toStr(d['direction']) tmp.addDict(d) return tmp
class AdaKalman(IndicatorAbstract): def __init__( self, _ada_period: int=30, _init_x: float = 0.0, _init_P: float =1.0, _init_R: float = 0.1 ** 2, _init_Q: float = 0.01 ** 2, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'kalman' ): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) self.ada_period = _ada_period self.value_std = FastSTD(_ada_period, _use_key=self.use_key) self.x_std = FastSTD(_ada_period, _use_key='x') self.R = _init_R self.Q = _init_Q self.x = _init_x self.P = _init_P def _addOne(self, _data_struct: DataStruct): index = _data_struct.index()[0] value = _data_struct[self.use_key][0] self.value_std.addOne(_data_struct) if len(self.value_std) > 1: self.R = self.value_std.getLastData()['std'][0] ** 2 if len(self.x_std) > 1: self.Q = self.x_std.getLastData()['std'][0] ** 2 # predict # self.x += 0.0 # x assume not changed self.P += self.Q # update k = self.P / (self.P + self.R) x_diff_value = k * (value - self.x) self.x += x_diff_value self.P = (1 - k) * self.P self.data.addDict({ self.idx_key: index, self.ret_key: self.x })
class RateConstStop(StopIndicatorAbstract): def __init__( self, _data: DataStruct, _stop_type: int, _stop_rate: float = 0.05, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'stopprice', ): super().__init__() assert len(_data) == 1 self.stop_type = _stop_type self.stop_rate = _stop_rate self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key price = _data[self.use_key][0] if self.stop_type == SignalType.LONG: stop_price = price * (1 - self.stop_rate) elif self.stop_type == SignalType.SHORT: stop_price = price * (1 + self.stop_rate) else: raise Exception('unknown type') time = _data.index()[0] self.data = DataStruct([self.idx_key, self.ret_key], self.idx_key, [[time, stop_price]]) def _addOne(self, _data_struct: DataStruct): stop_price = self.data[self.ret_key][-1] time = _data_struct.index()[0] self.data.addDict({ self.idx_key: time, self.ret_key: stop_price, }) def _isStop(self, _data_struct: DataStruct): price = _data_struct[self.use_key][0] stop_price = self.data[self.ret_key][-1] if self.stop_type == SignalType.LONG: if price < stop_price: self.is_stop = True elif self.stop_type == SignalType.SHORT: if price > stop_price: self.is_stop = True else: raise Exception('unknown type')
def getOrderData(self): tmp = DataStruct([ 'strategy', 'tradingday', 'datetime', 'symbol', 'index', 'order_type', 'action', 'direction', 'quantity', 'price' ], 'datetime') for d in self.order_record: d = copy(d) d['order_type'] = OrderType.toStr(d['order_type']) d['action'] = ActionType.toStr(d['action']) d['direction'] = DirectionType.toStr(d['direction']) tmp.addDict(d) return tmp
class CCI(IndicatorAbstract): """ rolling ma """ def __init__( self, _period: int, _constant: float = 0.15, _close_key: str = 'closeprice', _high_key: str = 'highprice', _low_key: str = 'lowprice', _idx_key: str = 'time', _ret_key: str = 'cci', ): super().__init__() self.close_key = _close_key self.high_key = _high_key self.low_key = _low_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) self.period = _period self.constant = _constant self.tp_buf = deque(maxlen=self.period) self.dev_buf = deque(maxlen=self.period) def _addOne(self, _data_struct: DataStruct): index_value = _data_struct.index()[0] close_price = _data_struct[self.close_key][0] high_price = _data_struct[self.high_key][0] low_price = _data_struct[self.low_key][0] tp = (close_price + high_price + low_price) / 3 if len(self.tp_buf) == 0: dev = high_price - low_price else: dev = abs(tp - self.tp_buf[-1]) self.tp_buf.append(tp) self.dev_buf.append(dev) self.data.addDict({ self.idx_key: index_value, self.ret_key: (tp - statistics.mean(self.tp_buf)) / ( self.constant * statistics.mean(self.dev_buf) ), })
class SimMA(IndicatorAbstract): EMPTY = 0 LONG = 1 SHORT = 2 def __init__( self, _period: int, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'simma' ): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) self.value = 1.0 self.last_price = None self.last_status = self.EMPTY self.period = _period self.buf = deque(maxlen=self.period) def _addOne(self, _data_struct: DataStruct): index = _data_struct.index()[0] price = _data_struct[self.use_key][0] self.buf.append(price) ma_value = sum(self.buf) / len(self.buf) if self.last_status == self.LONG: self.value *= price / self.last_price if self.last_status == self.SHORT: self.value /= price / self.last_price self.data.addDict({ self.idx_key: index, self.ret_key: self.value, }) if price > ma_value: self.last_status = self.LONG elif price < ma_value: self.last_status = self.SHORT else: self.last_status = self.EMPTY self.last_price = price
class CCI(IndicatorAbstract): """ rolling ma """ def __init__( self, _period: int, _constant: float = 0.15, _close_key: str = 'closeprice', _high_key: str = 'highprice', _low_key: str = 'lowprice', _idx_key: str = 'time', _ret_key: str = 'cci', ): super().__init__() self.close_key = _close_key self.high_key = _high_key self.low_key = _low_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct([self.idx_key, self.ret_key], self.idx_key) self.period = _period self.constant = _constant self.tp_buf = deque(maxlen=self.period) self.dev_buf = deque(maxlen=self.period) def _addOne(self, _data_struct: DataStruct): index_value = _data_struct.index()[0] close_price = _data_struct[self.close_key][0] high_price = _data_struct[self.high_key][0] low_price = _data_struct[self.low_key][0] tp = (close_price + high_price + low_price) / 3 if len(self.tp_buf) == 0: dev = high_price - low_price else: dev = abs(tp - self.tp_buf[-1]) self.tp_buf.append(tp) self.dev_buf.append(dev) self.data.addDict({ self.idx_key: index_value, self.ret_key: (tp - statistics.mean(self.tp_buf)) / (self.constant * statistics.mean(self.dev_buf)), })
class ReturnRate(IndicatorAbstract): def __init__( self, _smooth_period: int = 1, _skip_period: int = 1, _use_abs: bool=False, _use_percent: bool=False, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'returnrate' ): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) self.skip_period = _skip_period self.smooth_period = _smooth_period self.buf = deque(maxlen=self.skip_period) self.last_rate = None self.use_abs = _use_abs self.use_percent = _use_percent def _addOne(self, _data_struct: DataStruct): index = _data_struct.index()[0] value = _data_struct[self.use_key][0] if len(self.buf) >= self.skip_period: last_value = self.buf.popleft() chg_rate = value / last_value - 1 if self.use_abs: chg_rate = abs(chg_rate) if self.use_percent: chg_rate *= 100.0 if self.last_rate is None: self.last_rate = chg_rate else: self.last_rate = (chg_rate - self.last_rate) / \ self.smooth_period + self.last_rate self.data.addDict({ self.idx_key: index, self.ret_key: self.last_rate }) self.buf.append(value)
class FastSTD(IndicatorAbstract): def __init__( self, _period: int, _ignore_mean: bool = False, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'std' ): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) self.sum_of_pow = 0.0 self.mean = 0.0 self.period = _period self.ignore_mean = _ignore_mean self.buf = deque(maxlen=self.period) def _addOne(self, _data_struct: DataStruct): value = _data_struct[self.use_key][0] index = _data_struct.index()[0] if len(self.buf) >= self.period: last_value = self.buf.popleft() self.buf.append(value) self.sum_of_pow += value ** 2 self.sum_of_pow -= last_value ** 2 if not self.ignore_mean: self.mean += (value - last_value) / self.period else: n = len(self.buf) self.buf.append(value) self.sum_of_pow += value ** 2 if not self.ignore_mean: self.mean = (self.mean * n + value) / len(self.buf) self.data.addDict({ self.idx_key: index, self.ret_key: math.sqrt( self.sum_of_pow / len(self.buf) - self.mean ** 2 ) })
def deal(self, _market_event: MarketEvent): """ recv ticker data from market supply, and split into min, the call do_deal(...) :param _market_event: :return: """ symbol = _market_event.symbol data = _market_event.data for period in self.min_periods: key = symbol + (period, ) try: spliter = self.split_dict[key] except KeyError: spliter = self.split_dict[key] = SplitIntoMinute(period) flag = False for d in data: flag = flag or spliter.addOne(d) if flag: # gen new bar if len(spliter.getBarList()) > 1: # has finished bar last_bar = spliter.getBarList().pop(0) last_begin_time = spliter.getBarBeginTimeList().pop(0) last_end_time = spliter.getBarEndTimeList().pop(0) price_list = last_bar['price'] volume_list = last_bar['amount'] bar_data = DataStruct([ 'open', 'high', 'low', 'close', 'volume', 'begin_time', 'end_time' ], 'begin_time') bar_data.addDict({ 'open': price_list[0], 'high': max(price_list), 'low': min(price_list), 'close': price_list[-1], 'volume': sum(volume_list), 'begin_time': last_begin_time, 'end_time': last_end_time }) self.do_deal( MarketEvent(_market_event.market_register_key, _market_event.strategy, _market_event.symbol, bar_data), period)
def signalToLongShort( self, _backtest_key: str, _strategy: str = None ) -> typing.Tuple[DataStruct, DataStruct]: signal_list = self.fetchSignalRecords(_backtest_key, _strategy) keys = [ 'type', 'symbol', 'strategy', 'signal_type', 'tradingday', 'datetime', 'strength' ] long_ret = DataStruct(keys, 'datetime') short_ret = DataStruct(keys, 'datetime') for d in signal_list: if d['signal_type'] == SignalType.LONG: long_ret.addDict(d) if d['signal_type'] == SignalType.SHORT: short_ret.addDict(d) return long_ret, short_ret
class Volatility(IndicatorAbstract): def __init__( self, _period: int, _factor: int = 1, _smooth: int = 1, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'volatility', ): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) self.last_price = None self.period = _period self.factor = math.sqrt(_factor) self.smooth = _smooth self.buf = deque(maxlen=self.period) def _addOne(self, _data_struct: DataStruct): index_value = _data_struct.index()[0] price_value = _data_struct[self.use_key][0] if self.last_price is not None: chg_rate = price_value / self.last_price - 1 self.buf.append(chg_rate) std_value = statistics.pstdev(self.buf) * self.factor if self.smooth > 1 and len(self.data): last_std_value = self.data[self.ret_key][-1] std_value = ( (self.smooth - 1) * last_std_value + std_value ) / self.smooth self.data.addDict({ self.idx_key: index_value, self.ret_key: std_value, }) self.last_price = price_value
class FastSTD(IndicatorAbstract): def __init__(self, _period: int, _ignore_mean: bool = False, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'std'): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct([self.idx_key, self.ret_key], self.idx_key) self.sum_of_pow = 0.0 self.mean = 0.0 self.period = _period self.ignore_mean = _ignore_mean self.buf = deque(maxlen=self.period) def _addOne(self, _data_struct: DataStruct): value = _data_struct[self.use_key][0] index = _data_struct.index()[0] if len(self.buf) >= self.period: last_value = self.buf.popleft() self.buf.append(value) self.sum_of_pow += value**2 self.sum_of_pow -= last_value**2 if not self.ignore_mean: self.mean += (value - last_value) / self.period else: n = len(self.buf) self.buf.append(value) self.sum_of_pow += value**2 if not self.ignore_mean: self.mean = (self.mean * n + value) / len(self.buf) self.data.addDict({ self.idx_key: index, self.ret_key: math.sqrt(self.sum_of_pow / len(self.buf) - self.mean**2) })
class ReturnRate(IndicatorAbstract): def __init__(self, _smooth_period: int = 1, _skip_period: int = 1, _use_abs: bool = False, _use_percent: bool = False, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'returnrate'): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct([self.idx_key, self.ret_key], self.idx_key) self.skip_period = _skip_period self.smooth_period = _smooth_period self.buf = deque(maxlen=self.skip_period) self.last_rate = None self.use_abs = _use_abs self.use_percent = _use_percent def _addOne(self, _data_struct: DataStruct): index = _data_struct.index()[0] value = _data_struct[self.use_key][0] if len(self.buf) >= self.skip_period: last_value = self.buf.popleft() chg_rate = value / last_value - 1 if self.use_abs: chg_rate = abs(chg_rate) if self.use_percent: chg_rate *= 100.0 if self.last_rate is None: self.last_rate = chg_rate else: self.last_rate = (chg_rate - self.last_rate) / \ self.smooth_period + self.last_rate self.data.addDict({ self.idx_key: index, self.ret_key: self.last_rate }) self.buf.append(value)
def fillToBuySell( self, _backtest_key: str, _strategy: str = None) -> typing.Tuple[DataStruct, DataStruct]: fill_list = self.fetchFillRecords(_backtest_key, _strategy) keys = [ 'type', 'index', 'symbol', 'tradingday', 'datetime', 'quantity', 'action', 'direction', 'price', 'commission', 'strategy' ] buy_ret = DataStruct(keys, 'datetime') sell_ret = DataStruct(keys, 'datetime') for d in fill_list: if d['direction'] == DirectionType.BUY: buy_ret.addDict(d) if d['direction'] == DirectionType.SELL: sell_ret.addDict(d) return buy_ret, sell_ret
def signalToLongShort( self, _backtest_key: str, _strategy: str = None) -> typing.Tuple[DataStruct, DataStruct]: signal_list = self.fetchSignalRecords(_backtest_key, _strategy) keys = [ 'type', 'symbol', 'strategy', 'signal_type', 'tradingday', 'datetime', 'strength' ] long_ret = DataStruct(keys, 'datetime') short_ret = DataStruct(keys, 'datetime') for d in signal_list: if d['signal_type'] == SignalType.LONG: long_ret.addDict(d) if d['signal_type'] == SignalType.SHORT: short_ret.addDict(d) return long_ret, short_ret
def fillToBuySell( self, _backtest_key: str, _strategy: str = None ) -> typing.Tuple[DataStruct, DataStruct]: fill_list = self.fetchFillRecords(_backtest_key, _strategy) keys = [ 'type', 'index', 'symbol', 'tradingday', 'datetime', 'quantity', 'action', 'direction', 'price', 'commission', 'strategy' ] buy_ret = DataStruct(keys, 'datetime') sell_ret = DataStruct(keys, 'datetime') for d in fill_list: if d['direction'] == DirectionType.BUY: buy_ret.addDict(d) if d['direction'] == DirectionType.SELL: sell_ret.addDict(d) return buy_ret, sell_ret
class ATR(IndicatorAbstract): def __init__( self, _period: int, _high_key: str = 'highprice', _low_key: str = 'lowprice', _close_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'atr' ): super().__init__() self.high_key = _high_key self.low_key = _low_key self.close_key = _close_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) self.period = _period self.last_atr = None self.last_close_price = None def _addOne(self, _data_struct: DataStruct): if self.last_close_price is not None: index_value = _data_struct.index()[0] tr_value = max( _data_struct[self.high_key][0], self.last_close_price ) - min( _data_struct[self.low_key][0], self.last_close_price ) if self.last_atr is None: self.last_atr = tr_value else: self.last_atr = (tr_value - self.last_atr) / self.period + self.last_atr self.data.addDict({ self.idx_key: index_value, self.ret_key: self.last_atr, }) self.last_close_price = _data_struct[self.close_key][0]
class LowBar(BarIndicatorAbstract): def __init__(self, _use_key: str, _idx_key: str = 'time', _ret_key: str = 'low'): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct([self.idx_key, self.ret_key], self.idx_key) def _addOne(self, _data_struct: DataStruct, _idx: typing.Union[str, datetime] = None): self.data.addDict({ self.idx_key: _idx, self.ret_key: min(_data_struct[self.use_key]) })
class RSI(IndicatorAbstract): def __init__( self, _period: int, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'rsi' ): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) self.period = _period self.gain_buf = deque(maxlen=self.period) self.loss_buf = deque(maxlen=self.period) self.last_price = None def _addOne(self, _data_struct: DataStruct): price = _data_struct[self.use_key][0] if self.last_price is not None: price_diff = price - self.last_price if price_diff >= 0: self.gain_buf.append(price_diff) self.loss_buf.append(0) else: self.gain_buf.append(0) self.loss_buf.append(-price_diff) index_value = _data_struct.index()[0] gain_mean = statistics.mean(self.gain_buf) loss_mean = max(statistics.mean(self.loss_buf), 0.01) self.data.addDict({ self.idx_key: index_value, self.ret_key: 100 - 100 / (1 + gain_mean / loss_mean), }) self.last_price = price
class OpenBar(BarIndicatorAbstract): def __init__( self, _use_key: str, _idx_key: str = 'time', _ret_key: str = 'open' ): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) def _addOne( self, _data_struct: DataStruct, _idx: typing.Union[str, datetime] = None ): self.data.addDict({ self.idx_key: _idx, self.ret_key: _data_struct[self.use_key][0] })
class STD(IndicatorAbstract): def __init__(self, _period: int, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'std'): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct([self.idx_key, self.ret_key], self.idx_key) self.period = _period self.buf = deque(maxlen=self.period) def _addOne(self, _data_struct: DataStruct): index_value = _data_struct.index()[0] self.buf.append(_data_struct.getColumn(self.use_key)[0]) self.data.addDict({ self.idx_key: index_value, self.ret_key: statistics.pstdev(self.buf), })
class Kalman(IndicatorAbstract): def __init__( self, _init_x: float = 0.0, _init_P: float = 1.0, _R: float = 0.1 ** 2, _Q: float = 0.01 ** 2, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'kalman' ): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) self.x = _init_x self.P = _init_P self.R = _R self.Q = _Q def _addOne(self, _data_struct: DataStruct): index = _data_struct.index()[0] value = _data_struct[self.use_key][0] self.P += self.Q k = self.P / (self.P + self.R) self.x += k * (value - self.x) self.P = (1 - k) * self.P self.data.addDict({ self.idx_key: index, self.ret_key: self.x })
class FastMA(IndicatorAbstract): """ rolling ma """ def __init__(self, _period: int, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'ma'): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct([self.idx_key, self.ret_key], self.idx_key) self.period = _period self.buf = deque(maxlen=self.period) self.mean = 0.0 def _addOne(self, _data_struct: DataStruct): index = _data_struct.index()[0] value = _data_struct[self.use_key][0] if len(self.buf) >= self.period: last_value = self.buf.popleft() self.buf.append(value) self.mean += (value - last_value) / self.period else: n = len(self.buf) self.buf.append(value) self.mean = (self.mean * n + value) / len(self.buf) self.data.addDict({ self.idx_key: index, self.ret_key: self.mean, })
class ATRTrailingStop(StopIndicatorAbstract): def __init__( self, _price_data: DataStruct, _atr_data: DataStruct, _stop_type: int, _rate: float = 3, _price_use_key: str = 'closeprice', _atr_use_key: str = 'atr', _idx_key: str = 'time', _ret_key: str = 'stopprice', ): super().__init__() assert len(_price_data) == 1 assert len(_atr_data) == 1 self.stop_type = _stop_type self.rate = _rate self.price_use_key = _price_use_key self.atr_use_key = _atr_use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key, ) self.best_price = _price_data[self.price_use_key][0] self._addOne(_price_data, _atr_data) def get_stop_price(self, _price, _atr) -> float: if self.stop_type == SignalType.LONG: self.best_price = max(self.best_price, _price) return self.best_price - self.rate * _atr elif self.stop_type == SignalType.SHORT: self.best_price = min(self.best_price, _price) return self.best_price + self.rate * _atr else: raise Exception('unknown type') def addOne( self, _data_struct: DataStruct, _atr_data: DataStruct = None, ) -> bool: if not self.is_stop: assert len(_data_struct) == 1 self._addOne(_data_struct, _atr_data) self._isStop(_data_struct) return self.is_stop def _addOne( self, _price_data: DataStruct, _atr_data: DataStruct = None, ): assert len(_atr_data) == 1 price = _price_data.toDict()[self.price_use_key] atr = _atr_data.toDict()[self.atr_use_key] price_time = _price_data.index()[0] atr_time = _atr_data.index()[0] assert price_time == atr_time self.data.addDict({ self.idx_key: price_time, self.ret_key: self.get_stop_price(price, atr), }) def _isStop(self, _data_struct: DataStruct): price = _data_struct.toDict()[self.price_use_key] stop_price = self.data[self.ret_key][-1] if self.stop_type == SignalType.LONG: if price < stop_price: self.is_stop = True elif self.stop_type == SignalType.SHORT: if price > stop_price: self.is_stop = True else: raise Exception('unknown type')
class ARMAGARCH(IndicatorAbstract): def __init__( self, _fit_period: int = 60, _fit_begin: int = 252, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: typing.Tuple[str] = ('mean', 'std'), ): super().__init__() # fitting control self.fit_count = 0 self.fit_period = _fit_period self.fit_begin = _fit_begin self.model = ARMAIGARCHModel(_use_mu=False) self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key[0], self.ret_key[1]], self.idx_key ) # log return buf self.last_price = None self.return_buf = [] # model params self.phi = None self.theta = None self.alpha = None self.beta = None self.const = None self.new_mean = None self.new_info = None self.new_var = None def _addOne(self, _data_struct: DataStruct): index = _data_struct.index()[0] price = _data_struct[self.use_key][0] if self.last_price is not None: rate = math.log(price) - math.log(self.last_price) if self.new_mean is not None: self.new_info = rate - self.new_mean self.return_buf.append(rate) self.fit_count += 1 # retrain model if self.fit_count > self.fit_period and \ len(self.return_buf) >= self.fit_begin: self.model.fit(self.return_buf) self.phi = self.model.getPhis()[0] self.theta = self.model.getThetas()[0] self.alpha = self.model.getAlphas()[0] self.beta = self.model.getBetas()[0] self.const = self.model.getConst()[0] self.new_info = self.model.latent_arma_arr[-1] self.new_var = self.model.latent_garch_arr[-1] self.fit_count = 0 if self.new_info is not None: # predict value self.new_mean = self.phi * rate + self.theta * self.new_info self.new_var = self.alpha * self.new_info ** 2 + \ self.beta * self.new_var + self.const self.data.addDict({ self.idx_key: index, self.ret_key[0]: self.new_mean, self.ret_key[1]: math.sqrt(self.new_var), }) self.last_price = price
class SAR(IndicatorAbstract): BEGIN = 0 RISING = 1 FALLING = -1 def __init__( self, _init_step: float = 0.02, _max_step: float = 0.2, _close_key: str = 'closeprice', _high_key: str = 'highprice', _low_key: str = 'lowprice', _idx_key: str = 'time', _ret_key: str = 'sar', ): super().__init__() self.close_key = _close_key self.high_key = _high_key self.low_key = _low_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) self.init_step = _init_step self.max_step = _max_step self.status = self.BEGIN self.ep = None self.step = None self.sar = None def _addOne(self, _data_struct: DataStruct): index_value = _data_struct.index()[0] close_price = _data_struct[self.close_key][0] high_price = _data_struct[self.high_key][0] low_price = _data_struct[self.low_key][0] if self.status == self.RISING: # last status if rising if low_price < self.sar: # if break sar, change status self.status = self.FALLING self.sar = self.ep # new sar if the ep of last status self.ep = low_price self.step = self.init_step else: # else continue rising self.sar += self.step * (self.ep - self.sar) if high_price > self.ep: self.ep = high_price self.step = min(self.max_step, self.step + self.init_step) elif self.status == self.FALLING: if high_price > self.sar: # if break sar, change status self.status = self.RISING self.sar = self.ep self.ep = high_price self.step = self.init_step else: # else continue falling self.sar -= self.step * (self.sar - self.ep) if low_price < self.ep: self.ep = low_price self.step = min(self.max_step, self.step + self.init_step) else: # just begin if close_price >= (high_price + low_price) / 2: # close price greater than mid of high and low self.status = self.RISING self.ep = high_price self.step = self.init_step self.sar = low_price else: self.status = self.FALLING self.ep = low_price self.step = self.init_step self.sar = high_price self.data.addDict({ self.idx_key: index_value, self.ret_key: self.sar, })
class VolatilityTrailingStop(StopIndicatorAbstract): def __init__( self, _price_data: DataStruct, _volatility_data: DataStruct, _stop_type: int, _rate: float = 4, _price_use_key: str = 'closeprice', _volatility_use_key: str = 'volatility', _idx_key: str = 'time', _ret_key: str = 'stopprice', ): super().__init__() assert len(_price_data) == 1 assert len(_volatility_data) == 1 self.stop_type = _stop_type self.rate = _rate self.price_use_key = _price_use_key self.volatility_use_key = _volatility_use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key, ) self._addOne(_price_data, _volatility_data) def get_stop_price(self, _price, _volatility): if len(self.data): last_stop_price = self.data[self.ret_key][-1] if self.stop_type == SignalType.LONG: return max(_price * (1 - self.rate * _volatility), last_stop_price) elif self.stop_type == SignalType.SHORT: return min(_price * (1 + self.rate * _volatility), last_stop_price) else: raise Exception('unknown type') else: if self.stop_type == SignalType.LONG: return _price * (1 - self.rate * _volatility) elif self.stop_type == SignalType.SHORT: return _price * (1 + self.rate * _volatility) else: raise Exception('unknown type') def addOne( self, _data_struct: DataStruct, _volatility_data: DataStruct = None, ) -> bool: if not self.is_stop: assert len(_data_struct) == 1 self._addOne(_data_struct, _volatility_data) self._isStop(_data_struct) return self.is_stop def _addOne( self, _price_data: DataStruct, _volatility_data: DataStruct = None, ): assert len(_volatility_data) == 1 price = _price_data.toDict()[self.price_use_key] volatility = _volatility_data.toDict()[self.volatility_use_key] price_time = _price_data.index()[0] volatility_time = _volatility_data.index()[0] assert price_time == volatility_time self.data.addDict({ self.idx_key: price_time, self.ret_key: self.get_stop_price(price, volatility), }) def _isStop(self, _data_struct: DataStruct): price = _data_struct.toDict()[self.price_use_key] stop_price = self.data[self.ret_key][-1] if self.stop_type == SignalType.LONG: if price < stop_price: self.is_stop = True elif self.stop_type == SignalType.SHORT: if price > stop_price: self.is_stop = True else: raise Exception('unknown type')
class GARCH(IndicatorAbstract): def __init__( self, _fit_period: int = 60, _fit_begin: int = 252, _factor: int = 1, _smooth_period: int = 1, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'predict', ): super().__init__() # fitting control self.fit_count = 0 self.fit_period = _fit_period self.fit_begin = _fit_begin # scale the volatility self.factor = math.sqrt(_factor) self.smooth_period = _smooth_period self.model = IGARCHModel(_use_mu=False) self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) # log return buf self.last_price = None self.return_buf = [] # model params self.alpha = None self.beta = None self.const = None self.latent = None def _addOne(self, _data_struct: DataStruct): index = _data_struct.index()[0] price = _data_struct[self.use_key][0] if self.last_price is not None: rate = math.log(price) - math.log(self.last_price) self.return_buf.append(rate) self.fit_count += 1 if self.fit_count > self.fit_period and \ len(self.return_buf) >= self.fit_begin: # retrain model and reset sigma2 self.model.fit(self.return_buf) # print( # self.model.getAlphas(), self.model.getBetas(), # self.model.getConst(), self.model.getMu() # ) self.alpha = self.model.getAlphas()[0] self.beta = self.model.getBetas()[0] self.const = self.model.getConst()[0] self.latent = self.model.latent_arr[-1] self.fit_count = 0 if self.latent is not None: # predict value self.latent = self.alpha * rate * rate + \ self.beta * self.latent + self.const predict = math.sqrt(self.latent) predict *= self.factor if self.smooth_period > 1 and len(self.data): # smooth last_value = self.data[self.ret_key][-1] predict = (predict - last_value) / \ self.smooth_period + last_value self.data.addDict({ self.idx_key: index, self.ret_key: predict, }) self.last_price = price
class Plunge(IndicatorAbstract): def __init__( self, _fast_ema_period: int = 50, _slow_ema_period: int = 100, _atr_period: int = 20, _extreme_period: int = 20, _smooth_period: int = 1, _high_key: str = 'highprice', _low_key: str = 'lowprice', _close_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'plunge' ): super().__init__() self.high_key = _high_key self.low_key = _low_key self.close_key = _close_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) self.fast_ema_period = _fast_ema_period self.slow_ema_period = _slow_ema_period self.fast_ema_value = None self.slow_ema_value = None self.atr_buf = deque(maxlen=_atr_period) self.high_buf = deque(maxlen=_extreme_period) self.low_buf = deque(maxlen=_extreme_period) self.ret_buf = deque(maxlen=_smooth_period) self.last_close_price = None def _addOne(self, _data_struct: DataStruct): self.high_buf.append(_data_struct[self.high_key][0]) self.low_buf.append(_data_struct[self.low_key][0]) closeprice = _data_struct[self.close_key][0] if self.last_close_price is not None: index_value = _data_struct.index()[0] # atr tr_value = max( _data_struct[self.high_key][0], self.last_close_price ) - min(_data_struct[self.low_key][0], self.last_close_price) self.atr_buf.append(tr_value) atr_value = sum(self.atr_buf) / len(self.atr_buf) # ema self.fast_ema_value = (closeprice - self.fast_ema_value) / \ self.fast_ema_period + self.fast_ema_value self.slow_ema_value = (closeprice - self.slow_ema_value) / \ self.slow_ema_period + self.slow_ema_value # plunge if self.fast_ema_value > self.slow_ema_value: plunge_value = (max(self.high_buf) - closeprice) / atr_value elif self.fast_ema_value < self.slow_ema_value: plunge_value = (closeprice - min(self.low_buf)) / atr_value else: plunge_value = 0.0 self.ret_buf.append(plunge_value) self.data.addDict({ self.idx_key: index_value, self.ret_key: sum(self.ret_buf) / len(self.ret_buf), }) else: self.fast_ema_value = closeprice self.slow_ema_value = closeprice self.last_close_price = closeprice
class ZigZag(IndicatorAbstract): UNKNOWN = 0 UP = 1 DOWN = 2 def __init__( self, _threshold: float, _use_key: str = 'closeprice', _idx_key: str = 'time', _ret_key: str = 'zigzag' ): super().__init__() self.use_key = _use_key self.idx_key = _idx_key self.ret_key = _ret_key self.data = DataStruct( [self.idx_key, self.ret_key], self.idx_key ) self.threshold = _threshold self.status = ZigZag.UNKNOWN self.high_time: typing.Union[str, datetime] = None self.high_price: float = None self.low_time: typing.Union[str, datetime] = None self.low_price: float = None def _addOne(self, _data_struct: DataStruct): time: typing.Union[str, datetime] = _data_struct.index()[0] price: float = _data_struct[self.use_key][0] if self.status == ZigZag.UNKNOWN: # update high and low point if self.high_price is None or price > self.high_price: self.high_time = time self.high_price = price if self.low_price is None or price < self.low_price: self.low_time = time self.low_price = price # if change status if (price - self.low_price) > self.threshold * self.low_price: self.status = ZigZag.UP self.data.addDict({ self.idx_key: self.low_time, self.ret_key: self.low_price, }) # reset high self.high_time = time self.high_price = price # clear low self.low_time = None self.low_price = None if (self.high_price - price) > self.threshold * self.high_price: self.status = ZigZag.DOWN self.data.addDict({ self.idx_key: self.high_time, self.ret_key: self.high_price, }) # reset low self.low_time = time self.low_price = price # clear high self.high_time = None self.high_price = None elif self.status == ZigZag.UP: if price > self.high_price: self.high_time = time self.high_price = price if (self.high_price - price) > self.threshold * self.high_price: self.status = ZigZag.DOWN self.data.addDict({ self.idx_key: self.high_time, self.ret_key: self.high_price, }) # reset low self.low_time = time self.low_price = price # clear high self.high_time = None self.high_price = None elif self.status == ZigZag.DOWN: if price < self.low_price: self.low_time = time self.low_price = price if (price - self.low_price) > self.threshold * self.low_price: self.status = ZigZag.UP self.data.addDict({ self.idx_key: self.low_time, self.ret_key: self.low_price, }) # reset high self.high_time = time self.high_price = price # clear low self.low_time = None self.low_price = None else: raise Exception('unknown status')
class StepDrawdownStop(StopIndicatorAbstract): def __init__( self, _data: DataStruct, _stop_type: int, _init_stop: float = 0.05, _profit_thresh: typing.Sequence[float] = ( 0.1, 0.2, 0.3, 0.5 ), _stop_thresh: typing.Sequence[float] = ( 1.0, 0.5, 0.3, 0.15 ), _use_key: str = 'closeprice', _idx_key: str = 'time', _status_key: str = 'status', _ret_key: str = 'stopprice', ): super().__init__() assert len(_data) == 1 self.stop_type = _stop_type self.status = 0 self.profit_thresh = _profit_thresh self.stop_thresh = _stop_thresh self.use_key = _use_key self.idx_key = _idx_key self.status_key = _status_key self.ret_key = _ret_key self.init_price = _data.toDict()[self.use_key] if self.stop_type == SignalType.LONG: self.init_stop_price = self.init_price * (1 - _init_stop) elif self.stop_type == SignalType.SHORT: self.init_stop_price = self.init_price * (1 + _init_stop) else: raise Exception('unknown type') self.best_price = self.init_price self.data = DataStruct( [self.idx_key, self.status_key, self.ret_key], self.idx_key, [[_data.index()[0], self.status, self.init_stop_price]] ) def _set_best_price(self, _price): if self.stop_type == SignalType.LONG: self.best_price = max(self.best_price, _price) elif self.stop_type == SignalType.SHORT: self.best_price = min(self.best_price, _price) else: raise Exception('unknown type') def _profit_rate(self, _price) -> float: if self.stop_type == SignalType.LONG: return _price / self.init_price - 1.0 elif self.stop_type == SignalType.SHORT: return 1.0 - _price / self.init_price else: raise Exception('unknown type') def _update_status(self, _profit_rate): while True: if self.status < len(self.profit_thresh): if _profit_rate > self.profit_thresh[self.status]: self.status += 1 else: return else: return def _calc_stop_price(self) -> typing.Union[None, float]: if self.status > 0: if self.stop_type == SignalType.LONG: return self.init_price + \ (1.0 - self.stop_thresh[self.status - 1]) * \ (self.best_price - self.init_price) elif self.stop_type == SignalType.SHORT: return self.init_price - \ (1.0 - self.stop_thresh[self.status - 1]) * \ (self.init_price - self.best_price) else: raise Exception('unknown type') else: return self.init_stop_price def _addOne(self, _data_struct: DataStruct): time = _data_struct.index()[0] price = _data_struct[self.use_key][0] self._set_best_price(price) profit_rate = self._profit_rate(price) # if self.stop_type == SignalType.SHORT: # input((profit_rate, self.status)) self._update_status(profit_rate) self.data.addDict({ self.idx_key: time, self.status_key: self.status, self.ret_key: self._calc_stop_price(), }) def _isStop(self, _data_struct: DataStruct): price = _data_struct.toDict()[self.use_key] stop_price = self.data[self.ret_key][-1] if self.stop_type == SignalType.LONG: if price < stop_price: self.is_stop = True elif self.stop_type == SignalType.SHORT: if price > stop_price: self.is_stop = True else: raise Exception('unknown type')