Exemplo n.º 1
0
 def test_avg_of_last_elems(self):
     close = [1.0, 2.0, 3.0, 4.0]
     open = [2.0, 3.0, 4.0, 5.0]
     ohlcs = [
         OHLC(date(2021, 1, 1) + timedelta(days=i),
              open=open[i],
              low=0.0,
              high=10.0,
              close=close[i],
              num_trades=1,
              volume=1.0,
              waprice=1.0) for i in range(len(close))
     ]
     series = OHLCSeries("i", ohlcs)
     assert series.mean_of_last_elems(2) == (close[-2] + close[-1]) / 2
     assert series.mean_of_last_elems(
         2, lambda ohlc: ohlc.open) == (open[-2] + open[-1]) / 2
     assert series.mean_of_last_elems(4) == (close[0] + close[1] +
                                             close[2] + close[3]) / 4
     with pytest.raises(ValueError):
         series.mean_of_last_elems(5)
Exemplo n.º 2
0
    def get_triggered_signals(self, instr: moex.Instrument,
                              ser: instruments.OHLCSeries,
                              intraday_state: IntradayState) -> (Outcome, str):
        """returns tuple whose first element is true if triggered and second is message with details"""
        # TODO: for bonds: add notification its price gets < 100.
        try:
            instr.update_ohlc_table(ser)
            quote = instr.load_intraday_quotes()
            if not quote.is_trading:
                return Outcome.NOT_TRIGGERED_DUE_TO_NOT_READY, f"Skipping {instr} as it's not currently trading"
            time_of_last_trade = quote.time
            if intraday_state.time_of_last_trade is not None and \
                    time_of_last_trade < intraday_state.time_of_last_trade:
                # trading day switched
                logger.info(
                    f"Trading day switched for {instr}, resetting intraday averager"
                )
                intraday_state.moving_avg = MovingAvgCalculator(
                    self.intraday_window_size)
            intraday_state.time_of_last_trade = time_of_last_trade
            # TODO: add only if time of last trade differs, to track only deals, not ticks
            intraday_state.moving_avg.add(quote.last)

            hist_mean = ser.mean_of_last_elems(self.hist_window_size)
            std_dev = ser.std_dev_of_last_elems(self.hist_window_size,
                                                mean=hist_mean)
            intra_mean = intraday_state.moving_avg.avg()
            if intra_mean is None:
                return Outcome.NOT_TRIGGERED_DUE_TO_NOT_READY, f"Skipping {instr} as it hasn't accumulated " \
                                                               f"{self.intraday_window_size} intraday quotes yet"
            rel_diff = (intra_mean - hist_mean) / max(intra_mean, hist_mean)
            nstd_devs = self.num_std_devs_thresh * std_dev
            observed_num_std_devs_jump = abs(intra_mean - hist_mean) / std_dev
            if intra_mean > hist_mean + nstd_devs:
                return Outcome.TRIGGERED, f"Jump UP {round(rel_diff * 100.0, 2)}% to {round(intra_mean, 2)} " \
                                          f"({self.intraday_window_size} tick avg) " \
                                          f"from average of {round(hist_mean, 2)} of last {self.hist_window_size} days " \
                                          f"(jump of {round(observed_num_std_devs_jump, 2)} std. devs from mean)"
            elif intra_mean < hist_mean - nstd_devs:
                return Outcome.TRIGGERED, f"Jump DOWN {round(rel_diff * 100.0, 2)}% to {round(intra_mean, 2)} " \
                                          f"({self.intraday_window_size} tick avg) " \
                                          f"from average of {round(hist_mean, 2)} of last {self.hist_window_size} days " \
                                          f"(jump of {round(observed_num_std_devs_jump, 2)} std. devs from mean)"
            else:
                return Outcome.NOT_TRIGGERED_DUE_TO_THRESHOLD, f"{round(intra_mean, 2)} ({self.intraday_window_size} tick avg) is " \
                        f"{round(rel_diff * 100.0, 2)}% jump over last " \
                        f"{self.hist_window_size} days average of {round(hist_mean, 2)} " \
                        f"(jump of {round(observed_num_std_devs_jump, 2)} std. devs from mean)"
        except Exception as ex:
            logger.error(f"Error happened checking {instr}",
                         exc_info=ex,
                         stack_info=True)
            return True, f"Could not check: {ex}"