Beispiel #1
0
 def calculate_stop_limit(
     self, tradeDirection, current_offer, current_bid, limit_perc, stop_perc
 ):
     """
     Calculate the stop and limit levels from the given percentages
     """
     limit = None
     stop = None
     if tradeDirection == TradeDirection.BUY:
         limit = current_offer + Utils.percentage_of(limit_perc, current_offer)
         stop = current_bid - Utils.percentage_of(stop_perc, current_bid)
     elif tradeDirection == TradeDirection.SELL:
         limit = current_bid - Utils.percentage_of(limit_perc, current_bid)
         stop = current_offer + Utils.percentage_of(stop_perc, current_offer)
     return limit, stop
    def find_trade_signal(self, market, datapoints):
        """
        TODO add description of strategy key points
        """
        limit_perc = self.limit_p
        stop_perc = max(market.stop_distance_min, self.stop_p)

        # Spread constraint
        if market.bid - market.offer > self.max_spread:
            return TradeDirection.NONE, None, None

        # Compute mid price
        current_mid = Utils.midpoint(market.bid, market.offer)

        high_prices = datapoints["high"]
        low_prices = datapoints["low"]
        close_prices = datapoints["close"]
        ltv = datapoints["volume"]

        # Check dataset integrity
        array_len_check = []
        array_len_check.append(len(high_prices))
        array_len_check.append(len(low_prices))
        array_len_check.append(len(close_prices))
        array_len_check.append(len(ltv))
        if not all(x == array_len_check[0] for x in array_len_check):
            logging.error("Historic datapoints incomplete for {}".format(market.epic))
            return TradeDirection.NONE, None, None

        # compute weighted average and std deviation of prices using volume as weight
        low_prices = numpy.ma.asarray(low_prices)
        high_prices = numpy.ma.asarray(high_prices)
        ltv = numpy.ma.asarray(ltv)
        low_weighted_avg, low_weighted_std_dev = self.weighted_avg_and_std(
            low_prices, ltv
        )
        high_weighted_avg, high_weighted_std_dev = self.weighted_avg_and_std(
            high_prices, ltv
        )

        # The VWAP can be used similar to moving averages, where prices above
        # the VWAP reflect a bullish sentiment and prices below the VWAP
        # reflect a bearish sentiment. Traders may initiate short positions as
        # a stock price moves below VWAP for a given time period or initiate
        # long position as the price moves above VWAP

        tmp_high_weight_var = float(high_weighted_avg + high_weighted_std_dev)
        tmp_low_weight_var = float(low_weighted_avg + low_weighted_std_dev)
        # e.g
        # series = [0,0,0,2,0,0,0,-2,0,0,0,2,0,0,0,-2,0]

        maxtab_high, _mintab_high = self.peakdet(high_prices, 0.3)
        _maxtab_low, mintab_low = self.peakdet(low_prices, 0.3)

        # convert to array so can work on min/max
        mintab_low_a = array(mintab_low)[:, 1]
        maxtab_high_a = array(maxtab_high)[:, 1]

        xb = range(0, len(mintab_low_a))
        xc = range(0, len(maxtab_high_a))

        mintab_low_a_slope, mintab_low_a_intercept, mintab_low_a_lo_slope, mintab_low_a_hi_slope = stats.mstats.theilslopes(
            mintab_low_a, xb, 0.99
        )
        maxtab_high_a_slope, maxtab_high_a_intercept, maxtab_high_a_lo_slope, maxtab_high_a_hi_slope = stats.mstats.theilslopes(
            maxtab_high_a, xc, 0.99
        )

        peak_count_high = 0
        peak_count_low = 0
        # how may "peaks" are BELOW the threshold
        for a in mintab_low_a:
            if float(a) < float(tmp_low_weight_var):
                peak_count_low += 1

        # how may "peaks" are ABOVE the threshold
        for a in maxtab_high_a:
            if float(a) > float(tmp_high_weight_var):
                peak_count_high += 1

        additional_checks_sell = [
            int(peak_count_low) > int(peak_count_high),
            float(mintab_low_a_slope) < float(maxtab_high_a_slope),
        ]
        additional_checks_buy = [
            int(peak_count_high) > int(peak_count_low),
            float(maxtab_high_a_slope) > float(mintab_low_a_slope),
        ]

        sell_rules = [
            float(current_mid) >= float(numpy.max(maxtab_high_a)),
            all(additional_checks_sell),
        ]
        buy_rules = [
            float(current_mid) <= float(numpy.min(mintab_low_a)),
            all(additional_checks_buy),
        ]

        trade_direction = TradeDirection.NONE
        if any(buy_rules):
            trade_direction = TradeDirection.BUY
        elif any(sell_rules):
            trade_direction = TradeDirection.SELL

        if trade_direction is TradeDirection.NONE:
            return trade_direction, None, None

        logging.info("Strategy says: {} {}".format(trade_direction.name, market.id))

        ATR = self.calculate_stop_loss(close_prices, high_prices, low_prices)

        if trade_direction is TradeDirection.BUY:
            pip_limit = int(
                abs(float(max(high_prices)) - float(market.bid))
                * self.profit_indicator_multiplier
            )
            ce_stop = self.Chandelier_Exit_formula(
                trade_direction, ATR, min(low_prices)
            )
            stop_pips = str(int(abs(float(market.bid) - (ce_stop))))
        elif trade_direction is TradeDirection.SELL:
            pip_limit = int(
                abs(float(min(low_prices)) - float(market.bid))
                * self.profit_indicator_multiplier
            )
            ce_stop = self.Chandelier_Exit_formula(
                trade_direction, ATR, max(high_prices)
            )
        stop_pips = str(int(abs(float(market.bid) - (ce_stop))))

        esma_new_margin_req = int(Utils.percentage_of(self.ESMA_new_margin, market.bid))

        if int(esma_new_margin_req) > int(stop_pips):
            stop_pips = int(esma_new_margin_req)
        # is there a case for a 20% drop? ... Especially over 18 weeks or
        # so?
        if int(stop_pips) > int(esma_new_margin_req):
            stop_pips = int(esma_new_margin_req)
        if int(pip_limit) == 0:
            # not worth the trade
            trade_direction = TradeDirection.NONE
        if int(pip_limit) == 1:
            # not worth the trade
            trade_direction = TradeDirection.NONE
        if int(pip_limit) >= int(self.greed_indicator):
            pip_limit = int(self.greed_indicator - 1)
        if int(stop_pips) > int(self.too_high_margin):
            logging.warning("Junk data for {}".format(market.epic))
            return TradeDirection.NONE, None, None
        return trade_direction, pip_limit, stop_pips
Beispiel #3
0
def test_percentage_of():
    assert Utils.percentage_of(1, 100) == 1
    assert Utils.percentage_of(0, 100) == 0
    assert Utils.percentage_of(1, 1) == 0.01