Ejemplo n.º 1
0
class StochasticStrategySR(BotStrategy):
    """
    Stochastic Oscillator Strategy with Support and Resistance and moving average trends.
    Includes flags to control entry/exit based on various moving average cases.

    Trades will only be made once `ready to buy` is set to True when stochastic %K crosses over %D below mid line (50).
    """
    _NAME = 'Stochastic-SR-MA Strategy'

    # Entry strategy flags. Keep at least one set to true or the trade will never enter
    PRICE_ABOVE_MA = False
    STOCH_OS = True

    # Exit strategy flags. Keep at least one set to true or the trade will never exit
    TAKE_PROFIT = True
    take_profit = 0.005  # Exit when realized profit of trade is above this percentage. e.g. 0.005 == 0.5%
    MA_SLOPE_SLOWING = True  # Exit when moving average slope begins to decline
    PRICE_BELOW_MA = False  # Exit when price is below moving average
    OVER_BOUGHT = False  # Exit when stochastic oscillator is over bought


    def __init__(self, pair, period):
        BotStrategy.__init__(self, pair, period)

        self.highs = []
        self.lows = []
        self.closes = []
        self.moving_avgs = []  # Keep track of MAs
        self.ma_slopes = []  # Keep track of Slope of MAs
        self.slope_difference = []
        self.support_levels = []
        self.resistance_levels = []
        self.current_price = ""
        self.pair = pair
        self.stoch_stop_loss = 2.5
        self.cool_down_period = 0

        self.indicators = BotIndicators()

        # Lookback window sizes
        self.stoch_period = 5  # n previous candles for stochastic indicator
        self.ma_period_fast = 9  # n previous candles for fast moving average indicator
        self.ma_slope_lookback = 9  # n previous values for calculating slope of MA.
        self.ma_period_slow = 26  # n previous candles for slow moving average indicator
        self.sr_period = 700  # n previous candles for support/resistance
        self.sr_n = 25  # n-value for S/R indicator. Controls Smoothing.
        self.hist_period = max(self.stoch_period, self.sr_period, self.ma_period_fast, self.ma_period_slow)

        self.over_bought = 80
        self.over_sold = 20
        self.mid_line = 50
        self.stoch_ready_to_buy = False

        # Prime the bot with past data.
        self.past_typical_prices, self.past_opens, self.past_closes, self.past_highs, self.past_lows = self.get_past_prices(self.period, self.hist_period)
        if self.past_typical_prices and self.past_closes and self.past_highs and self.past_lows and self.past_opens:
            self.prices = self.past_typical_prices
            self.closes = self.past_closes
            self.highs = self.past_highs
            self.lows = self.past_lows
        for i in reversed(range(self.ma_period_fast)):
            sub_slice = self.prices[:-i - 1]
            _ma = self.indicators.moving_average(sub_slice[-self.ma_period_fast:], self.ma_period_fast)
            self.moving_avgs.append(_ma)

    def tick(self, candlestick):
        if self.cool_down_period:
            self.cool_down_period -= 1
        # self.output.log("cool down: {}".format(self.cool_down_period))

        self.current_price = float(candlestick.typical_price)
        self.prices.append(self.current_price)

        # Highs
        self.currentHigh = float(candlestick.high)
        self.highs.append(self.currentHigh)

        # Lows
        self.currentLow = float(candlestick.low)
        self.lows.append(self.currentLow)

        # Closes
        self.currentClose = float(candlestick.close)
        self.closes.append(self.currentClose)

        # Resistance levels - highest highs
        self.highest_high = float(max(self.highs))
        # self.resistance.append(self.highest_high)
        statsd.histogram('stochastic.resistance', self.highest_high, tags=['stochastic.resistance', 'bot_name:{}.bot_id:{}'.format(BOT_NAME, BOT_ID)])

        # Support levels - lowest lows
        self.lowest_low = float(min(self.lows))
        # self.support.append(self.lowest_low)
        statsd.histogram('stochastic.support', self.lowest_low, tags=['stochastic.support', 'bot_name:{}.bot_id:{}'.format(BOT_NAME, BOT_ID)])

        if len(self.prices) > self.hist_period:
            # Price action
            self.output.log("\n{color}Typical Price: {price}".format(color=Red, price=str(candlestick.typical_price)))

            # Moving average
            m_a = self.indicators.moving_average(self.closes, self.ma_period_fast)
            self.moving_avgs.append(m_a)
            self.output.log("Current Moving Average Value: " + str(m_a))
            # self.output.log("Last {} Moving Average Values: {}".format(self.ma_period_fast, str(self.moving_avgs[-self.ma_period_fast:])))
            statsd.histogram('stochastic.moving_average', m_a, tags=['stochastic.moving_average', 'bot_name:{}.bot_id:{}'.format(BOT_NAME, BOT_ID)])

            # Slope of moving averages trend line
            slope = self.indicators.slope(self.moving_avgs, lookback=self.ma_slope_lookback)
            self.ma_slopes.append(slope)
            self.output.log("Slope over last {} periods: {}".format(str(self.ma_slope_lookback), str(self.ma_slopes[-1])))
            # self.output.log("Slope values: {}".format(str(self.ma_slopes)))
            # Create a new list of change in value between values in an old list. e.g. [1,1,2,3,5,8] == [0, 1, 1, 2, 3]
            # Simple approximation for rate of change.
            self.slope_difference = [j-i for i, j in zip(self.ma_slopes[:-1], self.ma_slopes[1:])]
            # self.output.log("Slope differences: {}".format(self.slope_difference))
            statsd.histogram('stochastic.slope', slope, tags=['stochastic.slope', 'bot_name:{}.bot_id:{}'.format(BOT_NAME, BOT_ID)])

            # Trend direction
            trend_dir = self.price_to_ma_trend(candlestick.typical_price, m_a)
            self.output.log("Price to MA trend direction: {} (price to moving average) ".format(str(trend_dir)))
            statsd.histogram('stochastic.trend', trend_dir, tags=['stochastic.trend', 'bot_name:{}.bot_id:{}'.format(BOT_NAME, BOT_ID)])

            # Stochastic
            k, d = self.indicators.STOCH(self.highs, self.lows, self.closes, fastk_period=self.stoch_period)
            self.output.log("\n{color}%K: {k}\t%D: {d}".format(color=Yellow, k=str(k[-1:]), d=str(d[-1:])))

            # Stochastic Ready to Buy - True
            if (k[-1] < self.mid_line and d[-1] < self.mid_line) and k[-1] > d[-1]:
                self.stoch_ready_to_buy = True
            # Stochastic Ready to Buy - False
            if (k[-1] > self.mid_line and d[-1] > self.mid_line) and k[-1] < d[-1]:
                self.stoch_ready_to_buy = False
            self.output.log("Stochastic ready to buy: {}".format(self.stoch_ready_to_buy))

            statsd.histogram('stochastic.percent_k', k[-1], tags=['stochastic.percent_k', 'bot_name:{}.bot_id:{}'.format(BOT_NAME, BOT_ID)])
            statsd.histogram('stochastic.percent_d', d[-1], tags=['stochastic.percent_d', 'bot_name:{}.bot_id:{}'.format(BOT_NAME, BOT_ID)])

            # Support and Resistance
            self.sup, self.res = self.indicators.support_resistance(self.closes, n=self.sr_n)
            self.output.log("\n{color}n={n} - {prices} available prices".format(color=Cyan, n=self.sr_n, prices=len(self.closes)))
            self.output.log("Support: {sup}\nResistance: {res}{color}".format(sup=self.sup, res=self.res, color=White))
            statsd.histogram('sr.support_major', min(self.sup), tags=['sr.support_major', 'bot_name:{}.bot_id:{}'.format(BOT_NAME, BOT_ID)])
            statsd.histogram('sr.resistance_major', max(self.res), tags=['sr.resistance_major', 'bot_name:{}.bot_id:{}'.format(BOT_NAME, BOT_ID)])
            statsd.histogram('sr.support_minor', max(self.sup), tags=['sr.support_minor', 'bot_name:{}.bot_id:{}'.format(BOT_NAME, BOT_ID)])
            statsd.histogram('sr.resistance_minor', min(self.res), tags=['sr.resistance_minor', 'bot_name:{}.bot_id:{}'.format(BOT_NAME, BOT_ID)])

        self.evaluate_positions()
        self.update_open_trades()
        self.show_positions()

    def evaluate_positions(self):
        openTrades = []
        for trade in self.trades:
            if trade.status == "OPEN":
                openTrades.append(trade)

        # Instantiate indicator.
        slowk, slowd = self.indicators.STOCH(self.highs, self.lows, self.closes, fastk_period=self.stoch_period)
        # Only open a trade when the number of open trades is not more than the configured max allowed.
        # And there is enough data for indicator
        if len(openTrades) < self.num_simultaneous_trades and len(self.prices) > self.hist_period:
            #########################
            # Entry Strategy
            #########################
            # Buy/Long
            if self.PRICE_ABOVE_MA:
                if self.moving_avgs[-1] < self.current_price and self.stoch_ready_to_buy and not self.cool_down_period:
                    self.output.log("{} buy signal. Ready to buy: {} & typical price {} above MA".format(self._NAME,
                                                                                                         self.stoch_ready_to_buy,
                                                                                                         self.current_price - self.moving_avgs[-1]))
                    self.trades.append(BotTrade(pair=self.pair, current_price=self.current_price, trade_type="BUY", order_type='market',  stop_loss_percent=self.stoch_stop_loss))
                    self.cool_down_period = 5  # Wait n periods until another trade can be made
            if self.STOCH_OS:
                # Stochastic %K crossover %D below midline (50)
                if slowk[-1] < self.mid_line and slowd[-1] < self.mid_line and slowk[-2] < slowd[-2] and slowk[-1] > slowd[-1]:
                    self.output.log("{} buy signal. Stochastic %K crossover %D below midline(50)".format(self._NAME,
                                                                                                         self.stoch_ready_to_buy,
                                                                                                         self.current_price - self.moving_avgs[-1]))
                    self.trades.append(BotTrade(pair=self.pair, current_price=self.current_price, trade_type="BUY", order_type='market',  stop_loss_percent=self.stoch_stop_loss))

            # NOTE: LEVERAGE REQUIRED FOR SELLING ON KRAKEN
        ###############
        # Exit Strategy
        ###############
        for trade in openTrades:
            # Buy/Long

            if self.PRICE_BELOW_MA:
                # Exit when price is below moving average for two consecutive candles, indicating a downward price trend
                if self.current_price < self.moving_avgs[-1] and self.current_price < self.moving_avgs[-2]:
                    self.output.log("{} Closed on Price {} below MA for two candles".format(self._NAME, self.moving_avgs[-1] - self.current_price))
                    trade.close(self.current_price)
                    continue
            if self.MA_SLOPE_SLOWING and len(self.slope_difference) > 6:
                # Exit when the rate of the SLOPE of the n-period MA begins to slow.
                if self.slope_difference[-1] < self.slope_difference[-2] < self.slope_difference[-3] < \
                        self.slope_difference[-4] < self.slope_difference[-5] < self.slope_difference[-6] < \
                        self.slope_difference[-7] < self.slope_difference[-8]:
                    self.output.log("{} Closed on slope slowing. Slope went down by {}, {}, {}, {}".format(
                        self._NAME,
                        self.slope_difference[-4],
                        self.slope_difference[-3],
                        self.slope_difference[-2],
                        self.slope_difference[-1]))
                    trade.close(self.current_price)
                    continue
            if self.TAKE_PROFIT:
                # Exit on % profit.
                if self.current_price >= (trade.entry_price * (1.0 + self.take_profit)) and trade.trade_type.upper() == "BUY":
                    self.output.log("{} Closed on {}% gain".format(self._NAME, self.take_profit * 100))
                    trade.close(self.current_price)
                    continue
            if self.OVER_BOUGHT:
                # Exit on overbought signal.
                if slowk[-1] >= self.over_bought and trade.trade_type.upper() == "BUY":
                    self.output.log("{} Closed on over bought signal".format(self._NAME))
                    trade.close(self.current_price)
                    continue

            # TODO
            # Sell/Short Exit Strategy  LEVERAGE REQUIRED FOR SHORTING ON KRAKEN

    @staticmethod
    def price_to_ma_trend(price, moving_avg):
        if price <= moving_avg:
            trend = "DOWN"
        elif price > moving_avg:
            trend = "UP"
        else:
            trend = "NEUTRAL"
        return trend
Ejemplo n.º 2
0
class StochasticStrategyBasic(BotStrategy):
    """
    Basic Stochastic Oscillator Strategy.
    """
    _NAME = 'Stochastic strategy basic'

    # Entry strategy flags. Keep at least one set to true or the trade will never enter.

    # Exit strategy flags. Keep at least one set to true or the trade will never exit.
    TAKE_PROFIT = True
    take_profit = 0.004

    OVER_BOUGHT = True

    def __init__(self, pair, period):
        BotStrategy.__init__(self, pair, period)

        self.highs = []
        self.lows = []
        self.closes = []
        self.currentPrice = ""
        self.pair = pair
        self.stoch_stop_loss = 100.0

        self.indicators = BotIndicators()

        self.stoch_period = 14  # n previous trading sessions (candles)
        self.over_bought = 80
        self.over_sold = 20

        self.past_avg_prices, self.past_opens, self.past_closes, self.past_highs, self.past_lows = self.get_past_prices(
            self.period, self.stoch_period)
        if self.past_avg_prices and self.past_closes and self.past_highs and self.past_lows and self.past_opens:
            self.prices = self.past_avg_prices
            self.closes = self.past_closes
            self.highs = self.past_highs
            self.lows = self.past_lows

    def tick(self, candlestick):
        self.currentPrice = float(candlestick.typical_price)
        self.prices.append(self.currentPrice)

        # Highs
        self.currentHigh = float(candlestick.high)
        self.highs.append(self.currentHigh)

        # Lows
        self.currentLow = float(candlestick.low)
        self.lows.append(self.currentLow)

        # Closes
        self.currentLow = float(candlestick.close)
        self.closes.append(self.currentLow)

        if len(self.prices) > self.stoch_period:
            k, d = self.indicators.STOCH(self.highs, self.lows, self.closes)
            self.output.log("Price: " + str(candlestick.typical_price) +
                            "\t%K: " + str(k[-1:]) + "\t%D: " + str(d[-1:]))

        self.evaluate_positions()
        self.update_open_trades()
        self.show_positions()

    def evaluate_positions(self):
        openTrades = []
        for trade in self.trades:
            if trade.status == "OPEN":
                openTrades.append(trade)

        # Instantiate indicator.
        slowk, slowd = self.indicators.STOCH(self.highs, self.lows,
                                             self.closes)

        # Only open a trade when the number of open trades is not more than the configured max allowed.
        # And there is enough data for indicator
        if len(openTrades) < self.num_simultaneous_trades and len(
                self.prices) > self.stoch_period:
            #########################
            # Entry Strategy
            #########################
            # Buy/Long
            if slowk[-1] <= self.over_sold:
                self.output.log("{} buy signal".format(self._NAME))
                self.trades.append(
                    BotTrade(pair=self.pair,
                             current_price=self.currentPrice,
                             trade_type="BUY",
                             order_type='market',
                             stop_loss_percent=self.stoch_stop_loss))
            # NOTE: LEVERAGE REQUIRED FOR SELLING ON KRAKEN
        ###############
        # Exit Strategy
        ###############
        for trade in openTrades:
            # Buy/Long

            if self.TAKE_PROFIT:
                # Exit on % profit.
                if self.currentPrice >= (
                        trade.entry_price * (1.0 + self.take_profit)
                ) and trade.trade_type.upper() == "BUY":
                    self.output.log("{} Closed on {}% gain".format(
                        self._NAME, self.take_profit * 100))
                    trade.close(self.currentPrice)
                    continue
            if self.OVER_BOUGHT:
                # Exit on overbought signal.
                if slowk[-1] >= self.over_bought and trade.trade_type.upper(
                ) == "BUY":
                    self.output.log("{} Closed on over bought signal".format(
                        self._NAME))
                    trade.close(self.currentPrice)
                    continue