Esempio n. 1
0
class BacktestingStrategy(object):
    def __init__(self,
                 pair,
                 capital,
                 buy_strategy,
                 sell_strategy,
                 trading_fee=0,
                 stop_loss=0):
        self.output = structlog.get_logger()
        self.prices = []
        self.trades = []
        self.sells = []
        self.buys = []
        self.max_trades_at_once = 1
        self.indicators = StrategyAnalyzer(exchange_interface=None)
        self.profit = 0
        self.pair = pair
        self.reserve = capital
        self.buy_strategy = buy_strategy
        self.sell_stategy = sell_strategy
        self.trading_fee = trading_fee
        self.stop_loss = stop_loss

    '''
    Runs our backtesting strategy on the set of backtesting candlestick data
    '''

    def run(self, candlesticks):

        # Samples a random price within the range [candlestick.open, candlestick.close]
        sample_price = lambda op, close: random.uniform(
            min(op, close), max(op, close))

        # The zero's are to take up space since our indicators require a full dataframe of OHLC datas
        self.prices = [[
            0, 0, 0, 0, sample_price(candle.open, candle.close), 0
        ] for candle in candlesticks]

        # Hacky way to ensure indices match up :/
        rsi = [None] * 14
        nine_period = [None] * 9
        fifteen_period = [None] * 15
        nine_period_ema = [None] * 9

        rsi.extend(
            self.indicators.analyze_rsi(self.prices,
                                        period_count=14,
                                        all_data=True))
        nine_period.extend(
            self.indicators.analyze_sma(self.prices,
                                        period_count=9,
                                        all_data=True))
        fifteen_period.extend(
            self.indicators.analyze_sma(self.prices,
                                        period_count=15,
                                        all_data=True))
        nine_period_ema.extend(
            self.indicators.analyze_ema(self.prices,
                                        period_count=9,
                                        all_data=True))

        for i in range(len(self.prices)):

            # Get the (sampled) closing price
            current_price = self.prices[i][4]
            current_rsi = rsi[i]["values"] if rsi[i] else None
            current_nine_period = nine_period[i]["values"] if nine_period[
                i] else None
            current_fifteen_period = fifteen_period[i][
                "values"] if fifteen_period[i] else None
            current_nine_period_ema = nine_period_ema[i][
                "values"] if nine_period_ema[i] else None

            decision = Decision({
                'currentprice': current_price,
                'rsi': current_rsi,
                'sma9': current_nine_period,
                'sma15': current_fifteen_period,
                'ema9': current_nine_period_ema
            })

            open_trades = [
                trade for trade in self.trades if trade.status == 'OPEN'
            ]

            ### CHECK TO SEE IF WE CAN OPEN A BUY POSITION
            if len(open_trades) < self.max_trades_at_once:
                if decision.should_buy(self.buy_strategy):
                    assert self.reserve > 0

                    self.buys.append((i, current_price))
                    new_trade = Trade(self.pair,
                                      current_price,
                                      self.reserve * (1 - self.trading_fee),
                                      stop_loss=self.stop_loss)
                    self.reserve = 0
                    self.trades.append(new_trade)

            ### CHECK TO SEE IF WE NEED TO SELL ANY OPEN POSITIONS
            for trade in open_trades:
                if decision.should_sell(self.sell_stategy):

                    self.sells.append((i, current_price))
                    profit, total = trade.close(current_price)
                    self.profit += profit * (1 - self.trading_fee)
                    self.reserve = total * (1 - self.trading_fee)

            ### CHECK TO SEE IF WE HAVE ACTIVATED A STOP LOSS
            for trade in self.trades:

                # Check our stop losses
                if trade.status == "OPEN" and trade.stop_loss and current_price < trade.stop_loss:
                    profit, total = trade.close(current_price)
                    self.sells.append((i, current_price))
                    self.profit += profit * (1 - self.trading_fee)
                    self.reserve = total * (1 - self.trading_fee)

    def show_positions(self):
        for trade in self.trades:
            trade.show_trade()
Esempio n. 2
0
class Chart(object):
    def __init__(self,
                 pair,
                 period,
                 exchange_name,
                 exchange_interface,
                 start_time=time() - 2000000):

        self.pair = pair
        self.period = period

        self.start_time = start_time
        self.indicators = StrategyAnalyzer()

        self.data = []

        # Query the data to fill our chart truncate it to 'length' elements
        rawdata = exchange_interface.get_historical_data(
            pair, exchange_name, period, start_time * 1000)

        for i in range(len(rawdata)):
            datum = rawdata[i]
            stick = Candlestick(open=datum[1],
                                high=datum[2],
                                low=datum[3],
                                close=datum[4],
                                price_average=(datum[2] + datum[3]) / 2.)
            stick.time = i
            self.data.append(stick)

    def get_points(self):
        return self.data

    '''
    Returns the indicators specified in the **kwargs dictionary as a json-serializable dictionary
    '''

    def get_indicators(self, **kwargs):

        # Indicators are hardcoded for now. Will be updated to accommodate variable-sized MA's
        response = {
            'bollinger_upper': [],
            'bollinger_lower': [],
            'sma9': [],
            'sma15': []
        }

        # Get closing historical datapoints
        closings = [[0, 0, 0, 0, x.close, 0] for x in self.data]

        # The 'bollinger' keyword argument takes in a period, i.e. bollinger=21
        if "bollinger" in kwargs:
            period = kwargs["bollinger"]
            assert type(period) is int

            # Offset each band by "period" data points
            bbupper = [(i + period, datum["values"][0])
                       for i, datum in enumerate(
                           self.indicators.analyze_bollinger_bands(
                               closings, all_data=True))]
            bblower = [(i + period, datum["values"][2])
                       for i, datum in enumerate(
                           self.indicators.analyze_bollinger_bands(
                               closings, all_data=True))]

            response['bollinger_upper'] = bbupper
            response['bollinger_lower'] = bblower

        # The 'sma' keyword argument takes in a list of periods, i.e. sma=[9,15,21]
        if "sma" in kwargs:
            periods = kwargs["sma"]
            assert type(periods) is list

            for period in periods:
                # Offset each sma by "period" data points
                response['sma' + str(period)] = [
                    (i + period, datum["values"][0]) for i, datum in enumerate(
                        self.indicators.analyze_sma(
                            closings, period_count=period, all_data=True))
                ]

        return response

    '''
    ####################################################################
    ### THIS FUNCTION IS DEPRECATED. PLOT IS NOW DISPLAYED ON THE UI ###
    ####################################################################
    
    Plots the specified indicators on a matplotlib plot
    '''

    def plot_indicators(self, **kwargs):
        import numpy as np

        # Get closing historical datapoints
        closings = [[0, 0, 0, 0, x.close, 0] for x in self.data]
        plt.plot([x.close for x in self.data])

        # The 'bollinger' keyword argument takes in a period, i.e. bollinger=21
        if "bollinger" in kwargs:
            period = kwargs["bollinger"]
            assert type(period) is int

            bbupper = [
                np.nan_to_num(datum["values"][0])
                for datum in self.indicators.analyze_bollinger_bands(
                    closings, all_data=True)
            ]
            bblower = [
                np.nan_to_num(datum["values"][2])
                for datum in self.indicators.analyze_bollinger_bands(
                    closings, all_data=True)
            ]
            plt.plot(np.arange(period, len(closings)), bbupper[period:], 'g--')
            plt.plot(np.arange(period, len(closings)), bblower[period:], 'b--')

        # The 'sma' keyword argument takes in a list of periods, i.e. sma=[9,15,21]
        if "sma" in kwargs:
            periods = kwargs["sma"]
            assert type(periods) is list

            for period in periods:
                plt.plot([
                    np.nan_to_num(datum["values"][0])
                    for datum in self.indicators.analyze_sma(
                        closings, period_count=period, all_data=True)
                ])

    '''
    Plots each buy trade as a green 'x', and each sell trade as a red 'x'
    '''

    def plot_trades(self, buys, sells):
        for timestamp, price in buys:
            plt.plot(timestamp, price, 'gx')

        for timestamp, price in sells:
            plt.plot(timestamp, price, 'rx')
Esempio n. 3
0
class Chart(object):
    def __init__(self, pair, period, exchange_interface):

        self.pair = pair
        self.indicators = StrategyAnalyzer()
        self.data = []

        # Query the data to fill our chart truncate it to 'length' elements
        raw_data = exchange_interface.get_historical_data(
            pair, interval=Chart.period_to_integer(period))

        for datum in raw_data:
            stick = Candlestick(time=datum[0],
                                open=datum[1],
                                high=datum[2],
                                low=datum[3],
                                close=datum[4],
                                price_average=(datum[2] + datum[3]) / 2.)
            self.data.append(stick)

    def get_points(self, start_time=None):
        """
        Retrieve all candlesticks from the chart. If a start time is specified, then we return candlesticks only
        from that time onward. By default we return all candlesticks

        Args:
            start_time (int): Defaults to None. Otherwise is an integer denoting the starting time in epoch seconds
                that our candles will be returned from

        Returns:
            list[Candlestick]: A list of candlestick objects
        """
        if start_time:
            return [stick for stick in self.data if stick.time >= start_time]

        return self.data

    @staticmethod
    def period_to_integer(period):
        """
        Converts a period string into a integer
        Args:
            period (str): A string denoting the period of time each candlestick represents (i.e., '15m')
        Returns:
            int: An integer denoting the period of time in seconds
        """

        import re

        try:
            num_units = re.findall(r'\d+', period)[0]
            unit_type = period[len(num_units):]
            if unit_type == 'm':
                return 60 * int(num_units)
            if unit_type == 'h':
                return 60 * 60 * int(num_units)
            if unit_type == 'd':
                return 24 * 60 * 60 * int(num_units)

        except IndexError:
            raise ValueError(
                "`Period` string should contain a character prefixed with an integer"
            )

    def get_indicators(self, **kwargs):
        '''
        Returns the indicators specified in the **kwargs dictionary as a json-serializable dictionary
        '''
        from math import isnan

        # Indicators are hardcoded for now. Will be updated to accommodate variable-sized MA's
        response = {
            'bollinger_upper': [],
            'bollinger_lower': [],
            'sma9': [],
            'sma15': []
        }

        # Get closing historical datapoints
        closings = [[0, 0, 0, 0, x.close, 0] for x in self.data]

        # The 'bollinger' keyword argument takes in a period, i.e. bollinger=21
        if "bollinger" in kwargs:
            period = kwargs["bollinger"]
            assert type(period) is int

            # Offset each band by "period" data points
            bbupper = [(i + period, datum["values"][0])
                       for i, datum in enumerate(
                           self.indicators.analyze_bollinger_bands(
                               closings, all_data=True))]
            bblower = [(i + period, datum["values"][2])
                       for i, datum in enumerate(
                           self.indicators.analyze_bollinger_bands(
                               closings, all_data=True))]

            response['bollinger_upper'] = bbupper
            response['bollinger_lower'] = bblower

        # The 'sma' keyword argument takes in a list of periods, i.e. sma=[9,15,21]
        if "sma" in kwargs:
            periods = kwargs["sma"]
            assert type(periods) is list

            for period in periods:
                # Offset each sma by "period" data points
                response['sma' + str(period)] = [
                    (i + period, datum["values"][0]) for i, datum in enumerate(
                        self.indicators.analyze_sma(
                            closings, period_count=period, all_data=True))
                ]

        return response