Exemple #1
0
    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)
Exemple #2
0
    def __init__(self, config, exchange_interface, notifier):
        """Initializes DefaultBehaviour class.

        Args:
            indicator_conf (dict): A dictionary of configuration for this analyzer.
            exchange_interface (ExchangeInterface): Instance of the ExchangeInterface class for
                making exchange queries.
            notifier (Notifier): Instance of the notifier class for informing a user when a
                threshold has been crossed.
        """

        self.logger = structlog.get_logger()
        self.indicator_conf = config.indicators
        self.informant_conf = config.informants
        self.crossover_conf = config.crossovers
        self.exchange_interface = exchange_interface
        self.strategy_analyzer = StrategyAnalyzer()
        self.notifier = notifier
        self.all_historical_data = dict()

        output_interface = Output()
        self.output = output_interface.dispatcher

        self.enable_charts = config.settings['enable_charts']
        self.timezone = config.settings['timezone']
Exemple #3
0
 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
Exemple #4
0
    def __configure_simple_bot(self, behaviour_config):
        """Configures and returns the bot behaviour class.

        Args:
            behaviour_config (dict): A dictionary of configuration values pertaining to the
                behaviour.

        Returns:
            SimpleBotBehaviour: A class of functionality for the rsi bot behaviour.
        """

        exchange_interface = ExchangeInterface(self.config.exchanges)
        strategy_analyzer = StrategyAnalyzer()
        notifier = Notifier(self.config.notifiers)
        db_handler = DatabaseHandler(self.config.database)

        behaviour = SimpleBotBehaviour(
            behaviour_config,
            exchange_interface,
            strategy_analyzer,
            notifier,
            db_handler
        )

        return behaviour
Exemple #5
0
    def __configure_default(self, behaviour_config):
        """Configures and returns the default behaviour class.

        Args:
            behaviour_config (dict): A dictionary of configuration values pertaining to the
                behaviour.

        Returns:
            DefaultBehaviour: A class of functionality for the default behaviour.
        """

        exchange_interface = ExchangeInterface(self.config.exchanges)

        strategy_analyzer = StrategyAnalyzer()

        notifier = Notifier(self.config.notifiers)

        behaviour = DefaultBehaviour(
            behaviour_config,
            exchange_interface,
            strategy_analyzer,
            notifier
        )

        return behaviour
    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 __init__(self,
              pair,
              capital,
              buy_strategy,
              sell_strategy,
              trading_fee=0,
              stop_loss=0):
     self.prices = []
     self.trades = []
     self.sells = []
     self.buys = []
     self.max_trades_at_once = 1
     self.indicators = StrategyAnalyzer()
     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
Exemple #8
0
    def configure_rsi_bot(self, behaviour_config):
        exchange_interface = ExchangeInterface(
            self.config.fetch_exchange_config())
        strategy_analyzer = StrategyAnalyzer(exchange_interface)
        notifier = Notifier(self.config.fetch_notifier_config())
        db_handler = DatabaseHandler(self.config.fetch_database_config())

        behaviour = RSIBot(behaviour_config, exchange_interface,
                           strategy_analyzer, notifier, db_handler)

        return behaviour
Exemple #9
0
    def configure_default(self, behaviour_config):
        exchange_interface = ExchangeInterface(
            self.config.fetch_exchange_config())

        strategy_analyzer = StrategyAnalyzer(exchange_interface)

        notifier = Notifier(self.config.fetch_notifier_config())

        behaviour = DefaultBehaviour(behaviour_config, exchange_interface,
                                     strategy_analyzer, notifier)

        return behaviour
Exemple #10
0
def main():
     # Load settings and create the config object
    secrets = {}
    if os.path.isfile('secrets.json'):
        secrets = json.load(open('secrets.json'))
    config = json.load(open('default-config.json'))

    config.update(secrets)

    config['settings']['market_pairs'] = os.environ.get('MARKET_PAIRS', config['settings']['market_pairs'])
    config['settings']['loglevel'] = os.environ.get('LOGLEVEL', config['settings']['loglevel'])
    config['settings']['app_mode'] = os.environ.get('APP_MODE', config['settings']['app_mode'])
    config['exchanges']['bittrex']['required']['key'] = os.environ.get('BITTREX_KEY', config['exchanges']['bittrex']['required']['key'])
    config['exchanges']['bittrex']['required']['secret'] = os.environ.get('BITTREX_SECRET', config['exchanges']['bittrex']['required']['secret'])
    config['notifiers']['twilio']['required']['key'] = os.environ.get('TWILIO_KEY', config['notifiers']['twilio']['required']['key'])
    config['notifiers']['twilio']['required']['secret'] = os.environ.get('TWILIO_SECRET', config['notifiers']['twilio']['required']['secret'])
    config['notifiers']['twilio']['required']['sender_number'] = os.environ.get('TWILIO_SENDER_NUMBER', config['notifiers']['twilio']['required']['sender_number'])
    config['notifiers']['twilio']['required']['receiver_number'] = os.environ.get('TWILIO_RECEIVER_NUMBER', config['notifiers']['twilio']['required']['receiver_number'])
    config['notifiers']['gmail']['required']['username'] = os.environ.get('GMAIL_USERNAME', config['notifiers']['gmail']['required']['username'])
    config['notifiers']['gmail']['required']['password'] = os.environ.get('GMAIL_PASSWORD', config['notifiers']['gmail']['required']['password'])
    config['notifiers']['gmail']['required']['destination_emails'] = os.environ.get('GMAIL_DESTINATION_EMAILS', config['notifiers']['gmail']['required']['destination_emails'])

    # Set up logger
    logs.configure_logging(config['settings']['loglevel'], config['settings']['app_mode'])
    logger = structlog.get_logger()

    exchange_interface = ExchangeInterface(config)
    strategy_analyzer = StrategyAnalyzer(config)
    notifier = Notifier(config)

    # The coin pairs
    coin_pairs = []
    if config['settings']['market_pairs']:
        coin_pairs = config['settings']['market_pairs'].translate(str.maketrans('', '', whitespace)).split(",")
    else:
        user_markets = exchange_interface.get_user_markets()
        for user_market in user_markets['info']:
            if 'BTC' in user_market['Currency']:
                continue
            market_pair = user_market['Currency'] + '/BTC'
            coin_pairs.append(market_pair)
    logger.debug(coin_pairs)

    while True:
        get_signal(coin_pairs, strategy_analyzer, notifier)
Exemple #11
0
    def __init__(self, config, exchange_interface, notifier):
        """Initializes DefaultBehaviour class.

        Args:
            indicator_conf (dict): A dictionary of configuration for this analyzer.
            exchange_interface (ExchangeInterface): Instance of the ExchangeInterface class for
                making exchange queries.
            notifier (Notifier): Instance of the notifier class for informing a user when a
                threshold has been crossed.
        """

        self.logger = structlog.get_logger()
        self.indicator_conf = config.indicators
        self.informant_conf = config.informants
        self.crossover_conf = config.crossovers
        self.exchange_interface = exchange_interface
        self.strategy_analyzer = StrategyAnalyzer()
        self.notifier = notifier

        output_interface = Output()
        self.output = output_interface.dispatcher
Exemple #12
0
def main():
    # Load settings and create the config object

    config = conf.Configuration()
    settings = config.fetch_settings()
    exchange_config = config.fetch_exchange_config()
    notifier_config = config.fetch_notifier_config()
    behaviour_config = config.fetch_behaviour_config()

    # Set up logger
    logs.configure_logging(settings['loglevel'], settings['app_mode'])

    exchange_interface = ExchangeInterface(exchange_config)
    strategy_analyzer = StrategyAnalyzer(exchange_interface)
    notifier = Notifier(notifier_config)
    behaviour_manager = Behaviour(behaviour_config)

    behaviour = behaviour_manager.get_behaviour(settings['selected_task'])

    behaviour.run(settings['symbol_pairs'], settings['update_interval'],
                  exchange_interface, strategy_analyzer, notifier)
Exemple #13
0
    def __configure_rsi_bot(self, behaviour_config):
        """Configures and returns the rsi bot behaviour class.

        Args:
            behaviour_config (dict): A dictionary of configuration values pertaining to the
                behaviour.

        Returns:
            RSIBot: A class of functionality for the rsi bot behaviour.
        """

        exchange_interface = ExchangeInterface(
            self.config.get_exchange_config())
        strategy_analyzer = StrategyAnalyzer(exchange_interface)
        notifier = Notifier(self.config.get_notifier_config())
        db_handler = DatabaseHandler(self.config.get_database_config())

        behaviour = RSIBot(behaviour_config, exchange_interface,
                           strategy_analyzer, notifier, db_handler)

        return behaviour
Exemple #14
0
def main():
    """Initializes the application
    """
    # Load settings and create the config object
    config = Configuration()
    settings = config.settings

    # Set up logger
    logs.configure_logging(settings['log_level'], settings['log_mode'])
    logger = structlog.get_logger()

    # Configure and run configured behaviour.
    exchange_interface = ExchangeInterface(config.exchanges)
    strategy_analyzer = StrategyAnalyzer()
    notifier = Notifier(config.notifiers)

    behaviour = Behaviour(config.behaviour, exchange_interface,
                          strategy_analyzer, notifier)

    while True:
        behaviour.run(settings['market_pairs'], settings['output_mode'])
        logger.info("Sleeping for %s seconds", settings['update_interval'])
        time.sleep(settings['update_interval'])
Exemple #15
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')
Exemple #16
0
def main():
    # Load settings and create the config object
    secrets = {}
    if os.path.isfile('secrets.json'):
        secrets = json.load(open('secrets.json'))
    config = json.load(open('default-config.json'))

    config.update(secrets)

    config['settings']['market_pairs'] = os.environ.get(
        'MARKET_PAIRS', config['settings']['market_pairs'])
    config['settings']['loglevel'] = os.environ.get('LOGLEVEL', logging.INFO)
    config['exchanges']['bittrex']['required']['key'] = os.environ.get(
        'BITTREX_KEY', config['exchanges']['bittrex']['required']['key'])
    config['exchanges']['bittrex']['required']['secret'] = os.environ.get(
        'BITTREX_SECRET', config['exchanges']['bittrex']['required']['secret'])
    config['notifiers']['twilio']['required']['key'] = os.environ.get(
        'TWILIO_KEY', config['notifiers']['twilio']['required']['key'])
    config['notifiers']['twilio']['required']['secret'] = os.environ.get(
        'TWILIO_SECRET', config['notifiers']['twilio']['required']['secret'])
    config['notifiers']['twilio']['required'][
        'sender_number'] = os.environ.get(
            'TWILIO_SENDER_NUMBER',
            config['notifiers']['twilio']['required']['sender_number'])
    config['notifiers']['twilio']['required'][
        'receiver_number'] = os.environ.get(
            'TWILIO_RECEIVER_NUMBER',
            config['notifiers']['twilio']['required']['receiver_number'])
    config['notifiers']['gmail']['required']['username'] = os.environ.get(
        'GMAIL_USERNAME', config['notifiers']['gmail']['required']['username'])
    config['notifiers']['gmail']['required']['password'] = os.environ.get(
        'GMAIL_PASSWORD', config['notifiers']['gmail']['required']['password'])
    config['notifiers']['gmail']['required'][
        'destination_emails'] = os.environ.get(
            'GMAIL_DESTINATION_EMAILS',
            config['notifiers']['gmail']['required']['destination_emails'])

    # Set up logger
    LOGGER = logging.getLogger(__name__)
    LOGGER.setLevel(config['settings']['loglevel'])

    LOG_FORMAT = logging.Formatter(
        '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    LOG_HANDLE = logging.StreamHandler()
    LOG_HANDLE.setLevel(logging.DEBUG)
    LOG_HANDLE.setFormatter(LOG_FORMAT)
    LOGGER.addHandler(LOG_HANDLE)

    exchange_interface = ExchangeInterface(config)
    strategy_analyzer = StrategyAnalyzer(config)
    notifier = Notifier(config)

    # The coin pairs
    coin_pairs = []
    if config['settings']['market_pairs']:
        coin_pairs = config['settings']['market_pairs'].translate(
            str.maketrans('', '', whitespace)).split(",")
    else:
        user_markets = exchange_interface.get_user_markets()
        for user_market in user_markets['info']:
            if 'BTC' in user_market['Currency']:
                continue
            market_pair = user_market['Currency'] + '/BTC'
            coin_pairs.append(market_pair)
    LOGGER.debug(coin_pairs)

    while True:
        get_signal(coin_pairs, strategy_analyzer, notifier)
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()
Exemple #18
0
class Behaviour():
    """Default analyzer which gives users basic trading information.
    """
    def __init__(self, config, exchange_interface, notifier):
        """Initializes DefaultBehaviour class.

        Args:
            indicator_conf (dict): A dictionary of configuration for this analyzer.
            exchange_interface (ExchangeInterface): Instance of the ExchangeInterface class for
                making exchange queries.
            notifier (Notifier): Instance of the notifier class for informing a user when a
                threshold has been crossed.
        """

        self.logger = structlog.get_logger()
        self.indicator_conf = config.indicators
        self.informant_conf = config.informants
        self.crossover_conf = config.crossovers
        self.exchange_interface = exchange_interface
        self.strategy_analyzer = StrategyAnalyzer()
        self.notifier = notifier

        output_interface = Output()
        self.output = output_interface.dispatcher

    def run(self, market_pairs, output_mode):
        """The analyzer entrypoint

        Args:
            market_pairs (list): List of symbol pairs to operate on, if empty get all pairs.
            output_mode (str): Which console output mode to use.
        """

        self.logger.info("Starting default analyzer...")

        # if market_pairs:
        #     self.logger.info("Found configured markets: %s", market_pairs)
        # else:
        #     self.logger.info("No configured markets, using all available on exchange.")

        market_data = self.exchange_interface.get_exchange_markets(
            markets=market_pairs)

        self.logger.info("Using the following exchange(s): %s",
                         list(market_data.keys()))

        new_result = self._test_strategies(market_data, output_mode)

        # self.notifier.notify_all(new_result)

    def _test_strategies(self, market_data, output_mode):
        """Test the strategies and perform notifications as required

        Args:
            market_data (dict): A dictionary containing the market data of the symbols to analyze.
            output_mode (str): Which console output mode to use.
        """

        new_result = dict()

        for exchange in market_data:

            self.logger.info("Beginning analysis of %s", exchange)
            if exchange not in new_result:
                new_result[exchange] = dict()

            for market_pair in market_data[exchange]:
                My_new_result = dict()
                My_new_result[exchange] = dict()

                self.logger.info("Beginning analysis of %s", market_pair)
                if market_pair not in new_result[exchange]:
                    new_result[exchange][market_pair] = dict()
                    My_new_result[exchange][market_pair] = dict()

                new_result[exchange][market_pair][
                    'indicators'] = self._get_indicator_results(
                        exchange, market_pair)

                new_result[exchange][market_pair][
                    'informants'] = self._get_informant_results(
                        exchange, market_pair)

                new_result[exchange][market_pair][
                    'crossovers'] = self._get_crossover_results(
                        new_result[exchange][market_pair])

                if output_mode in self.output:
                    output_data = deepcopy(new_result[exchange][market_pair])
                    # print(
                    #    self.output[output_mode](output_data, market_pair),
                    #    end=''
                    # )
                else:
                    self.logger.warn()

                My_new_result[exchange][market_pair] = new_result[exchange][
                    market_pair]

                self.notifier.notify_all_new(My_new_result)
        # Print an empty line when complete
        print()
        return new_result

    def _get_indicator_results(self, exchange, market_pair):
        """Execute the indicator analysis on a particular exchange and pair.

        Args:
            exchange (str): The exchange to get the indicator results for.
            market_pair (str): The pair to get the market pair results for.

        Returns:
            list: A list of dictinaries containing the results of the analysis.
        """

        indicator_dispatcher = self.strategy_analyzer.indicator_dispatcher()
        results = {
            indicator: list()
            for indicator in self.indicator_conf.keys()
        }
        historical_data_cache = dict()

        for indicator in self.indicator_conf:
            if indicator not in indicator_dispatcher:
                self.logger.warn("No such indicator %s, skipping.", indicator)
                continue

            for indicator_conf in self.indicator_conf[indicator]:
                if indicator_conf['enabled']:
                    candle_period = indicator_conf['candle_period']
                else:
                    self.logger.debug("%s is disabled, skipping.", indicator)
                    continue

                if candle_period not in historical_data_cache:
                    historical_data_cache[
                        candle_period] = self._get_historical_data(
                            market_pair, exchange, candle_period)

                if historical_data_cache[candle_period]:
                    analysis_args = {
                        'historical_data':
                        historical_data_cache[candle_period],
                        'signal': indicator_conf['signal'],
                        'hot_thresh': indicator_conf['hot'],
                        'cold_thresh': indicator_conf['cold']
                    }

                    if 'period_count' in indicator_conf:
                        analysis_args['period_count'] = indicator_conf[
                            'period_count']

                    results[indicator].append({
                        'result':
                        self._get_analysis_result(indicator_dispatcher,
                                                  indicator, analysis_args,
                                                  market_pair),
                        'config':
                        indicator_conf
                    })
        return results

    def _get_informant_results(self, exchange, market_pair):
        """Execute the informant analysis on a particular exchange and pair.

        Args:
            exchange (str): The exchange to get the indicator results for.
            market_pair (str): The pair to get the market pair results for.

        Returns:
            list: A list of dictinaries containing the results of the analysis.
        """

        informant_dispatcher = self.strategy_analyzer.informant_dispatcher()
        results = {
            informant: list()
            for informant in self.informant_conf.keys()
        }
        historical_data_cache = dict()

        for informant in self.informant_conf:
            if informant not in informant_dispatcher:
                self.logger.warn("No such informant %s, skipping.", informant)
                continue

            for informant_conf in self.informant_conf[informant]:
                if informant_conf['enabled']:
                    candle_period = informant_conf['candle_period']
                else:
                    self.logger.debug("%s is disabled, skipping.", informant)
                    continue

                if candle_period not in historical_data_cache:
                    historical_data_cache[
                        candle_period] = self._get_historical_data(
                            market_pair, exchange, candle_period)

                if historical_data_cache[candle_period]:
                    analysis_args = {
                        'historical_data': historical_data_cache[candle_period]
                    }

                    if 'period_count' in informant_conf:
                        analysis_args['period_count'] = informant_conf[
                            'period_count']

                    results[informant].append({
                        'result':
                        self._get_analysis_result(informant_dispatcher,
                                                  informant, analysis_args,
                                                  market_pair),
                        'config':
                        informant_conf
                    })
        return results

    def _get_crossover_results(self, new_result):
        """Execute crossover analysis on the results so far.

        Args:
            new_result (dict): A dictionary containing the results of the informant and indicator
                analysis.

        Returns:
            list: A list of dictinaries containing the results of the analysis.
        """

        crossover_dispatcher = self.strategy_analyzer.crossover_dispatcher()
        results = {
            crossover: list()
            for crossover in self.crossover_conf.keys()
        }

        for crossover in self.crossover_conf:
            if crossover not in crossover_dispatcher:
                self.logger.warn("No such crossover %s, skipping.", crossover)
                continue

            for crossover_conf in self.crossover_conf[crossover]:
                if not crossover_conf['enabled']:
                    self.logger.debug("%s is disabled, skipping.", crossover)
                    continue

                try:
                    key_indicator = new_result[
                        crossover_conf['key_indicator_type']][
                            crossover_conf['key_indicator']][
                                crossover_conf['key_indicator_index']]
                    crossed_indicator = \
                    new_result[crossover_conf['crossed_indicator_type']][crossover_conf['crossed_indicator']][
                        crossover_conf['crossed_indicator_index']]
                except:
                    self.logger.error(
                        "IndexError: list index out of range, %s", crossover)
                    continue

                dispatcher_args = {
                    'key_indicator':
                    key_indicator['result'],
                    'key_signal':
                    crossover_conf['key_signal'],
                    'key_indicator_index':
                    crossover_conf['key_indicator_index'],
                    'crossed_indicator':
                    crossed_indicator['result'],
                    'crossed_signal':
                    crossover_conf['crossed_signal'],
                    'crossed_indicator_index':
                    crossover_conf['crossed_indicator_index']
                }

                results[crossover].append({
                    'result':
                    crossover_dispatcher[crossover](**dispatcher_args),
                    'config':
                    crossover_conf
                })
        return results

    def _get_historical_data(self, market_pair, exchange, candle_period):
        """Gets a list of OHLCV data for the given pair and exchange.

        Args:
            market_pair (str): The market pair to get the OHLCV data for.
            exchange (str): The exchange to get the OHLCV data for.
            candle_period (str): The timeperiod to collect for the given pair and exchange.

        Returns:
            list: A list of OHLCV data.
        """

        historical_data = list()
        try:
            historical_data = self.exchange_interface.get_historical_data(
                market_pair, exchange, candle_period)
        except RetryError:
            self.logger.error(
                'Too many retries fetching information for pair %s, skipping',
                market_pair)
        except ExchangeError:
            self.logger.error(
                'Exchange supplied bad data for pair %s, skipping',
                market_pair)
        except ValueError as e:
            self.logger.error(e)
            self.logger.error(
                'Invalid data encountered while processing pair %s, skipping',
                market_pair)
            self.logger.debug(traceback.format_exc())
        except AttributeError:
            self.logger.error(
                'Something went wrong fetching data for %s, skipping',
                market_pair)
            self.logger.debug(traceback.format_exc())
        return historical_data

    def _get_analysis_result(self, dispatcher, indicator, dispatcher_args,
                             market_pair):
        """Get the results of performing technical analysis

        Args:
            dispatcher (dict): A dictionary of functions for performing TA.
            indicator (str): The name of the desired indicator.
            dispatcher_args (dict): A dictionary of arguments to provide the analyser
            market_pair (str): The market pair to analyse

        Returns:
            pandas.DataFrame: Returns a pandas.DataFrame of results or an empty string.
        """

        try:
            results = dispatcher[indicator](**dispatcher_args)
        except TypeError:
            self.logger.info(
                'Invalid type encountered while processing pair %s for indicator %s, skipping',
                market_pair, indicator)
            self.logger.info(traceback.format_exc())
            results = str()
        return results
Exemple #19
0
class Behaviour():
    """Default analyzer which gives users basic trading information.
    """

    def __init__(self, config, exchange_interface, notifier):
        """Initializes DefaultBehaviour class.

        Args:
            indicator_conf (dict): A dictionary of configuration for this analyzer.
            exchange_interface (ExchangeInterface): Instance of the ExchangeInterface class for
                making exchange queries.
            notifier (Notifier): Instance of the notifier class for informing a user when a
                threshold has been crossed.
        """

        self.logger = structlog.get_logger()
        self.indicator_conf = config.indicators
        self.informant_conf = config.informants
        self.crossover_conf = config.crossovers
        self.exchange_interface = exchange_interface
        self.strategy_analyzer = StrategyAnalyzer()
        self.notifier = notifier

        output_interface = Output()
        self.output = output_interface.dispatcher


    def run(self, market_pairs, output_mode):
        """The analyzer entrypoint

        Args:
            market_pairs (list): List of symbol pairs to operate on, if empty get all pairs.
            output_mode (str): Which console output mode to use.
        """

        self.logger.info("Starting default analyzer...")

        if market_pairs:
            self.logger.info("Found configured markets: %s", market_pairs)
        else:
            self.logger.info("No configured markets, using all available on exchange.")

        market_data = self.exchange_interface.get_exchange_markets(markets=market_pairs)

        self.logger.info("Using the following exchange(s): %s", list(market_data.keys()))

        new_result = self._test_strategies(market_data, output_mode)

        self.notifier.notify_all(new_result)


    def _test_strategies(self, market_data, output_mode):
        """Test the strategies and perform notifications as required

        Args:
            market_data (dict): A dictionary containing the market data of the symbols to analyze.
            output_mode (str): Which console output mode to use.
        """

        new_result = dict()
        for exchange in market_data:
            self.logger.info("Beginning analysis of %s", exchange)
            if exchange not in new_result:
                new_result[exchange] = dict()

            for market_pair in market_data[exchange]:
                self.logger.info("Beginning analysis of %s", market_pair)
                if market_pair not in new_result[exchange]:
                    new_result[exchange][market_pair] = dict()

                new_result[exchange][market_pair]['indicators'] = self._get_indicator_results(
                    exchange,
                    market_pair
                )

                new_result[exchange][market_pair]['informants'] = self._get_informant_results(
                    exchange,
                    market_pair
                )

                new_result[exchange][market_pair]['crossovers'] = self._get_crossover_results(
                    new_result[exchange][market_pair]
                )

                if output_mode in self.output:
                    output_data = deepcopy(new_result[exchange][market_pair])
                    print(
                        self.output[output_mode](output_data, market_pair),
                        end=''
                    )
                else:
                    self.logger.warn()

        # Print an empty line when complete
        print()
        return new_result


    def _get_indicator_results(self, exchange, market_pair):
        """Execute the indicator analysis on a particular exchange and pair.

        Args:
            exchange (str): The exchange to get the indicator results for.
            market_pair (str): The pair to get the market pair results for.

        Returns:
            list: A list of dictinaries containing the results of the analysis.
        """

        indicator_dispatcher = self.strategy_analyzer.indicator_dispatcher()
        results = { indicator: list() for indicator in self.indicator_conf.keys() }
        historical_data_cache = dict()

        for indicator in self.indicator_conf:
            if indicator not in indicator_dispatcher:
                self.logger.warn("No such indicator %s, skipping.", indicator)
                continue

            for indicator_conf in self.indicator_conf[indicator]:
                if indicator_conf['enabled']:
                    candle_period = indicator_conf['candle_period']
                else:
                    self.logger.debug("%s is disabled, skipping.", indicator)
                    continue

                if candle_period not in historical_data_cache:
                    historical_data_cache[candle_period] = self._get_historical_data(
                        market_pair,
                        exchange,
                        candle_period
                    )

                if historical_data_cache[candle_period]:
                    analysis_args = {
                        'historical_data': historical_data_cache[candle_period],
                        'signal': indicator_conf['signal'],
                        'hot_thresh': indicator_conf['hot'],
                        'cold_thresh': indicator_conf['cold']
                    }

                    if 'period_count' in indicator_conf:
                        analysis_args['period_count'] = indicator_conf['period_count']

                    results[indicator].append({
                        'result': self._get_analysis_result(
                            indicator_dispatcher,
                            indicator,
                            analysis_args,
                            market_pair
                        ),
                        'config': indicator_conf
                    })
        return results


    def _get_informant_results(self, exchange, market_pair):
        """Execute the informant analysis on a particular exchange and pair.

        Args:
            exchange (str): The exchange to get the indicator results for.
            market_pair (str): The pair to get the market pair results for.

        Returns:
            list: A list of dictinaries containing the results of the analysis.
        """

        informant_dispatcher = self.strategy_analyzer.informant_dispatcher()
        results = { informant: list() for informant in self.informant_conf.keys() }
        historical_data_cache = dict()

        for informant in self.informant_conf:
            if informant not in informant_dispatcher:
                self.logger.warn("No such informant %s, skipping.", informant)
                continue

            for informant_conf in self.informant_conf[informant]:
                if informant_conf['enabled']:
                    candle_period = informant_conf['candle_period']
                else:
                    self.logger.debug("%s is disabled, skipping.", informant)
                    continue

                if candle_period not in historical_data_cache:
                    historical_data_cache[candle_period] = self._get_historical_data(
                        market_pair,
                        exchange,
                        candle_period
                    )

                if historical_data_cache[candle_period]:
                    analysis_args = {
                        'historical_data': historical_data_cache[candle_period]
                    }

                    if 'period_count' in informant_conf:
                        analysis_args['period_count'] = informant_conf['period_count']

                    results[informant].append({
                        'result': self._get_analysis_result(
                            informant_dispatcher,
                            informant,
                            analysis_args,
                            market_pair
                        ),
                        'config': informant_conf
                    })
        return results


    def _get_crossover_results(self, new_result):
        """Execute crossover analysis on the results so far.

        Args:
            new_result (dict): A dictionary containing the results of the informant and indicator
                analysis.

        Returns:
            list: A list of dictinaries containing the results of the analysis.
        """

        crossover_dispatcher = self.strategy_analyzer.crossover_dispatcher()
        results = { crossover: list() for crossover in self.crossover_conf.keys() }

        for crossover in self.crossover_conf:
            if crossover not in crossover_dispatcher:
                self.logger.warn("No such crossover %s, skipping.", crossover)
                continue

            for crossover_conf in self.crossover_conf[crossover]:
                if not crossover_conf['enabled']:
                    self.logger.debug("%s is disabled, skipping.", crossover)
                    continue

                key_indicator = new_result[crossover_conf['key_indicator_type']][crossover_conf['key_indicator']][crossover_conf['key_indicator_index']]
                crossed_indicator = new_result[crossover_conf['crossed_indicator_type']][crossover_conf['crossed_indicator']][crossover_conf['crossed_indicator_index']]

                dispatcher_args = {
                    'key_indicator': key_indicator['result'],
                    'key_signal': crossover_conf['key_signal'],
                    'key_indicator_index': crossover_conf['key_indicator_index'],
                    'crossed_indicator': crossed_indicator['result'],
                    'crossed_signal': crossover_conf['crossed_signal'],
                    'crossed_indicator_index': crossover_conf['crossed_indicator_index']
                }

                results[crossover].append({
                    'result': crossover_dispatcher[crossover](**dispatcher_args),
                    'config': crossover_conf
                })
        return results


    def _get_historical_data(self, market_pair, exchange, candle_period):
        """Gets a list of OHLCV data for the given pair and exchange.

        Args:
            market_pair (str): The market pair to get the OHLCV data for.
            exchange (str): The exchange to get the OHLCV data for.
            candle_period (str): The timeperiod to collect for the given pair and exchange.

        Returns:
            list: A list of OHLCV data.
        """

        historical_data = list()
        try:
            historical_data = self.exchange_interface.get_historical_data(
                market_pair,
                exchange,
                candle_period
            )
        except RetryError:
            self.logger.error(
                'Too many retries fetching information for pair %s, skipping',
                market_pair
            )
        except ExchangeError:
            self.logger.error(
                'Exchange supplied bad data for pair %s, skipping',
                market_pair
            )
        except ValueError as e:
            self.logger.error(e)
            self.logger.error(
                'Invalid data encountered while processing pair %s, skipping',
                market_pair
            )
            self.logger.debug(traceback.format_exc())
        except AttributeError:
            self.logger.error(
                'Something went wrong fetching data for %s, skipping',
                market_pair
            )
            self.logger.debug(traceback.format_exc())
        return historical_data


    def _get_analysis_result(self, dispatcher, indicator, dispatcher_args, market_pair):
        """Get the results of performing technical analysis

        Args:
            dispatcher (dict): A dictionary of functions for performing TA.
            indicator (str): The name of the desired indicator.
            dispatcher_args (dict): A dictionary of arguments to provide the analyser
            market_pair (str): The market pair to analyse

        Returns:
            pandas.DataFrame: Returns a pandas.DataFrame of results or an empty string.
        """

        try:
            results = dispatcher[indicator](**dispatcher_args)
        except TypeError:
            self.logger.info(
                'Invalid type encountered while processing pair %s for indicator %s, skipping',
                market_pair,
                indicator
            )
            self.logger.info(traceback.format_exc())
            results = str()
        return results
Exemple #20
0
class Behaviour():
    """Default analyzer which gives users basic trading information.
    """
    def __init__(self, config, exchange_interface, notifier):
        """Initializes DefaultBehaviour class.

        Args:
            indicator_conf (dict): A dictionary of configuration for this analyzer.
            exchange_interface (ExchangeInterface): Instance of the ExchangeInterface class for
                making exchange queries.
            notifier (Notifier): Instance of the notifier class for informing a user when a
                threshold has been crossed.
        """

        self.logger = structlog.get_logger()
        self.indicator_conf = config.indicators
        self.informant_conf = config.informants
        self.crossover_conf = config.crossovers
        self.exchange_interface = exchange_interface
        self.strategy_analyzer = StrategyAnalyzer()
        self.notifier = notifier
        self.all_historical_data = dict()

        output_interface = Output()
        self.output = output_interface.dispatcher

        self.enable_charts = config.settings['enable_charts']
        self.timezone = config.settings['timezone']

    def run(self, market_data, output_mode):
        """The analyzer entrypoint

        Args:
            market_data (dict): Dict of exchanges and symbol pairs to operate on.
            output_mode (str): Which console output mode to use.
        """

        self.logger.info("Starting default analyzer...")

        self.logger.info("Using the following exchange(s): %s",
                         list(market_data.keys()))

        self.all_historical_data = self.get_all_historical_data(market_data)

        new_result = self._test_strategies(market_data, output_mode)

        self.notifier.set_timezone(self.timezone)

        if self.enable_charts:
            self.notifier.set_enable_charts(True)
            self.notifier.set_all_historical_data(self.all_historical_data)

        self.notifier.notify_all(new_result)

    def get_all_historical_data(self, market_data):
        """Get historical data for each exchange/market pair/candle period

        Args:
            market_data (dict): A dictionary containing the market data of the symbols to get data.
        """

        indicator_dispatcher = self.strategy_analyzer.indicator_dispatcher()
        informant_dispatcher = self.strategy_analyzer.informant_dispatcher()

        data = dict()

        for exchange in market_data:
            self.logger.info("Getting data for %s",
                             list(market_data[exchange].keys()))
            if exchange not in data:
                data[exchange] = dict()

            for market_pair in market_data[exchange]:
                if market_pair not in data[exchange]:
                    data[exchange][market_pair] = dict()

                for indicator in self.indicator_conf:
                    if indicator not in indicator_dispatcher:
                        self.logger.warn("No such indicator %s, skipping.",
                                         indicator)
                        continue

                    for indicator_conf in self.indicator_conf[indicator]:
                        if indicator_conf['enabled']:
                            candle_period = indicator_conf['candle_period']

                            if candle_period not in data[exchange][
                                    market_pair]:
                                data[exchange][market_pair][
                                    candle_period] = self._get_historical_data(
                                        market_pair, exchange, candle_period)

                for informant in self.informant_conf:
                    if informant not in informant_dispatcher:
                        self.logger.warn("No such informant %s, skipping.",
                                         informant)
                        continue

                    for informant_conf in self.informant_conf[informant]:
                        if informant_conf['enabled']:
                            candle_period = informant_conf['candle_period']

                            if candle_period not in data[exchange][
                                    market_pair]:
                                data[exchange][market_pair][
                                    candle_period] = self._get_historical_data(
                                        market_pair, exchange, candle_period)

        return data

    def _test_strategies(self, market_data, output_mode):
        """Test the strategies and perform notifications as required

        Args:
            market_data (dict): A dictionary containing the market data of the symbols to analyze.
            output_mode (str): Which console output mode to use.
        """

        new_result = dict()
        for exchange in market_data:
            self.logger.info("Beginning analysis of %s", exchange)
            if exchange not in new_result:
                new_result[exchange] = dict()

            for market_pair in market_data[exchange]:
                self.logger.info("Beginning analysis of %s", market_pair)
                if market_pair not in new_result[exchange]:
                    new_result[exchange][market_pair] = dict()

                new_result[exchange][market_pair][
                    'indicators'] = self._get_indicator_results(
                        exchange, market_pair)

                new_result[exchange][market_pair][
                    'informants'] = self._get_informant_results(
                        exchange, market_pair)

                new_result[exchange][market_pair][
                    'crossovers'] = self._get_crossover_results(
                        new_result[exchange][market_pair])

                if output_mode in self.output:
                    output_data = deepcopy(new_result[exchange][market_pair])
                    print(self.output[output_mode](output_data, market_pair),
                          end='')
                else:
                    self.logger.warn()

        # Print an empty line when complete
        print()
        return new_result

    def _get_indicator_results(self, exchange, market_pair):
        """Execute the indicator analysis on a particular exchange and pair.

        Args:
            exchange (str): The exchange to get the indicator results for.
            market_pair (str): The pair to get the market pair results for.

        Returns:
            list: A list of dictinaries containing the results of the analysis.
        """

        indicator_dispatcher = self.strategy_analyzer.indicator_dispatcher()
        results = {
            indicator: list()
            for indicator in self.indicator_conf.keys()
        }
        historical_data_cache = self.all_historical_data[exchange][market_pair]

        for indicator in self.indicator_conf:
            if indicator not in indicator_dispatcher:
                self.logger.warn("No such indicator %s, skipping.", indicator)
                continue

            for indicator_conf in self.indicator_conf[indicator]:
                if not indicator_conf['enabled']:
                    continue

                candle_period = indicator_conf['candle_period']

                #Exchange doesnt support such candle period
                if candle_period not in historical_data_cache:
                    continue

                if historical_data_cache[candle_period]:
                    analysis_args = {
                        'historical_data':
                        historical_data_cache[candle_period],
                        'signal':
                        indicator_conf['signal'],
                        'hot_thresh':
                        indicator_conf['hot']
                        if 'hot' in indicator_conf else 0,
                        'cold_thresh':
                        indicator_conf['cold']
                        if 'cold' in indicator_conf else 0
                    }

                    if 'period_count' in indicator_conf:
                        analysis_args['period_count'] = indicator_conf[
                            'period_count']

                    if indicator == 'rsi' and 'lrsi_filter' in indicator_conf:
                        analysis_args['lrsi_filter'] = indicator_conf[
                            'lrsi_filter']

                    if indicator == 'ma_ribbon':
                        analysis_args['pval_th'] = indicator_conf[
                            'pval_th'] if 'pval_th' in indicator_conf else 20
                        if 'ma_series' in indicator_conf:
                            series = indicator_conf['ma_series']
                            analysis_args['ma_series'] = [
                                int(i)
                                for i in series.replace(' ', '').split(',')
                            ]
                        else:
                            analysis_args['ma_series'] = [5, 15, 25, 35, 45]

                    if indicator == 'ma_crossover':
                        analysis_args['exponential'] = indicator_conf[
                            'exponential'] if 'exponential' in indicator_conf else False
                        analysis_args['ma_fast'] = indicator_conf[
                            'ma_fast'] if 'ma_fast' in indicator_conf else 13
                        analysis_args['ma_slow'] = indicator_conf[
                            'ma_slow'] if 'ma_slow' in indicator_conf else 30

                    results[indicator].append({
                        'result':
                        self._get_analysis_result(indicator_dispatcher,
                                                  indicator, analysis_args,
                                                  market_pair),
                        'config':
                        indicator_conf
                    })
        return results

    def _get_informant_results(self, exchange, market_pair):
        """Execute the informant analysis on a particular exchange and pair.

        Args:
            exchange (str): The exchange to get the indicator results for.
            market_pair (str): The pair to get the market pair results for.

        Returns:
            list: A list of dictinaries containing the results of the analysis.
        """

        informant_dispatcher = self.strategy_analyzer.informant_dispatcher()
        results = {
            informant: list()
            for informant in self.informant_conf.keys()
        }
        historical_data_cache = self.all_historical_data[exchange][market_pair]

        for informant in self.informant_conf:

            if informant not in informant_dispatcher:
                self.logger.warn("No such informant %s, skipping.", informant)
                continue

            for informant_conf in self.informant_conf[informant]:
                if not informant_conf['enabled']:
                    continue

                candle_period = informant_conf['candle_period']

                #Exchange doesnt support such candle period
                if candle_period not in historical_data_cache:
                    continue

                if historical_data_cache[candle_period]:
                    analysis_args = {
                        'historical_data': historical_data_cache[candle_period]
                    }

                    if 'period_count' in informant_conf:
                        analysis_args['period_count'] = informant_conf[
                            'period_count']

                    results[informant].append({
                        'result':
                        self._get_analysis_result(informant_dispatcher,
                                                  informant, analysis_args,
                                                  market_pair),
                        'config':
                        informant_conf
                    })
        return results

    def _get_crossover_results(self, new_result):
        """Execute crossover analysis on the results so far.

        Args:
            new_result (dict): A dictionary containing the results of the informant and indicator
                analysis.

        Returns:
            list: A list of dictinaries containing the results of the analysis.
        """

        crossover_dispatcher = self.strategy_analyzer.crossover_dispatcher()
        results = {
            crossover: list()
            for crossover in self.crossover_conf.keys()
        }

        for crossover in self.crossover_conf:
            if crossover not in crossover_dispatcher:
                self.logger.warn("No such crossover %s, skipping.", crossover)
                continue

            for crossover_conf in self.crossover_conf[crossover]:
                if not crossover_conf['enabled']:
                    self.logger.debug("%s is disabled, skipping.", crossover)
                    continue

                key_indicator = new_result[crossover_conf[
                    'key_indicator_type']][crossover_conf['key_indicator']][
                        crossover_conf['key_indicator_index']]
                crossed_indicator = new_result[
                    crossover_conf['crossed_indicator_type']][
                        crossover_conf['crossed_indicator']][
                            crossover_conf['crossed_indicator_index']]

                crossover_conf[
                    'candle_period'] = crossover_conf['key_indicator'] + str(
                        crossover_conf['key_indicator_index'])

                dispatcher_args = {
                    'key_indicator':
                    key_indicator['result'],
                    'key_signal':
                    crossover_conf['key_signal'],
                    'key_indicator_index':
                    crossover_conf['key_indicator_index'],
                    'crossed_indicator':
                    crossed_indicator['result'],
                    'crossed_signal':
                    crossover_conf['crossed_signal'],
                    'crossed_indicator_index':
                    crossover_conf['crossed_indicator_index']
                }

                results[crossover].append({
                    'result':
                    crossover_dispatcher[crossover](**dispatcher_args),
                    'config':
                    crossover_conf
                })
        return results

    def _get_historical_data(self, market_pair, exchange, candle_period):
        """Gets a list of OHLCV data for the given pair and exchange.

        Args:
            market_pair (str): The market pair to get the OHLCV data for.
            exchange (str): The exchange to get the OHLCV data for.
            candle_period (str): The timeperiod to collect for the given pair and exchange.

        Returns:
            list: A list of OHLCV data.
        """

        historical_data = list()
        try:
            historical_data = self.exchange_interface.get_historical_data(
                market_pair, exchange, candle_period)
        except RetryError:
            self.logger.error(
                'Too many retries fetching information for pair %s, skipping',
                market_pair)
        except ExchangeError:
            self.logger.error(
                'Exchange supplied bad data for pair %s, skipping',
                market_pair)
        except ValueError as e:
            self.logger.error(e)
            self.logger.error(
                'Invalid data encountered while processing pair %s, skipping',
                market_pair)
            self.logger.debug(traceback.format_exc())
        except AttributeError:
            self.logger.error(
                'Something went wrong fetching data for %s, skipping',
                market_pair)
            self.logger.debug(traceback.format_exc())
        return historical_data

    def _get_analysis_result(self, dispatcher, indicator, dispatcher_args,
                             market_pair):
        """Get the results of performing technical analysis

        Args:
            dispatcher (dict): A dictionary of functions for performing TA.
            indicator (str): The name of the desired indicator.
            dispatcher_args (dict): A dictionary of arguments to provide the analyser
            market_pair (str): The market pair to analyse

        Returns:
            pandas.DataFrame: Returns a pandas.DataFrame of results or an empty string.
        """

        try:
            results = dispatcher[indicator](**dispatcher_args)
        except TypeError:
            self.logger.info(
                'Invalid type encountered while processing pair %s for indicator %s, skipping',
                market_pair, indicator)
            self.logger.info(traceback.format_exc())
            results = str()
        return results
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
Exemple #22
0
class Behaviour(IndicatorUtils):
    """Default analyzer which gives users basic trading information.
    """
    def __init__(self, config, exchange_interface):
        """Initializes DefaultBehaviour class.

        Args:
            indicator_conf (dict): A dictionary of configuration for this analyzer.
            exchange_interface (ExchangeInterface): Instance of the ExchangeInterface class for
                making exchange queries.
        """

        self.logger = structlog.get_logger()
        self.indicator_conf = config.indicators
        self.informant_conf = config.informants
        self.crossover_conf = config.crossovers
        self.notifiers_conf = config.notifiers
        self.exchange_interface = exchange_interface
        self.strategy_analyzer = StrategyAnalyzer()

        self.all_historical_data = dict()
        self.last_analysis = dict()
        self.timezone = config.settings['timezone']

        output_interface = Output()
        self.output = output_interface.dispatcher

    def run(self, exchange, market_data, fibonacci, output_mode):
        """The analyzer entrypoint

        Args:
            market_data (dict): Dict of exchanges and symbol pairs to operate on.
            fibonacci (dict): Dict with Fibonacci levels
            output_mode (str): Which console output mode to use.
        """

        self.logger.info("Starting default analyzer for %s ...", exchange)

        self.all_historical_data = self.get_all_historical_data(market_data)

        new_analysis = self._test_strategies(market_data, output_mode)

        template = self.notifiers_conf['telegram']['optional']['template']

        indicator_messages = self.get_indicator_messages(
            new_analysis, market_data, template)

        self._create_charts(exchange, indicator_messages, fibonacci)

        return indicator_messages

    def get_all_historical_data(self, market_data):
        """Get historical data for each exchange/market pair/candle period

        Args:
            market_data (dict): A dictionary containing the market data of the symbols to get data.
        """

        indicator_dispatcher = self.strategy_analyzer.indicator_dispatcher()
        data = dict()

        for exchange in market_data:
            self.logger.info("Getting candle data of %s", exchange)
            if exchange not in data:
                data[exchange] = dict()

            for market_pair in market_data[exchange]:
                if market_pair not in data[exchange]:
                    data[exchange][market_pair] = dict()

                for indicator in self.indicator_conf:
                    if indicator not in indicator_dispatcher:
                        self.logger.warn("No such indicator %s, skipping.",
                                         indicator)
                        continue

                    for indicator_conf in self.indicator_conf[indicator]:
                        if indicator_conf['enabled']:
                            candle_period = indicator_conf['candle_period']

                            if candle_period not in data[exchange][
                                    market_pair]:
                                candle_data = self._get_historical_data(
                                    market_pair, exchange, candle_period)

                                if len(candle_data) == 0:
                                    self.logger.warn(
                                        'No candle data for %s %s on %s',
                                        market_pair, candle_period, exchange)
                                    continue

                                data[exchange][market_pair][
                                    candle_period] = candle_data

        #Return after iterate all exchanges
        return data

    def _test_strategies(self, market_data, output_mode):
        """Test the strategies and perform notifications as required

        Args:
            market_data (dict): A dictionary containing the market data of the symbols to analyze.
            output_mode (str): Which console output mode to use.
        """

        new_result = dict()
        for exchange in market_data:

            if exchange not in new_result:
                new_result[exchange] = dict()

            for market_pair in market_data[exchange]:
                self.logger.info("Beginning analysis of %s on %s" %
                                 (market_pair, exchange))

                if market_pair not in new_result[exchange]:
                    new_result[exchange][market_pair] = dict()

                new_result[exchange][market_pair][
                    'indicators'] = self._get_indicator_results(
                        exchange, market_pair)

                new_result[exchange][market_pair][
                    'informants'] = self._get_informant_results(
                        exchange, market_pair)

                new_result[exchange][market_pair][
                    'crossovers'] = self._get_crossover_results(
                        new_result[exchange][market_pair])

                if output_mode in self.output:
                    output_data = deepcopy(new_result[exchange][market_pair])
                    print(self.output[output_mode](output_data, market_pair),
                          end='')
                else:
                    self.logger.warn()

        # Print an empty line when complete
        print()
        return new_result

    def _get_indicator_results(self, exchange, market_pair):
        """Execute the indicator analysis on a particular exchange and pair.

        Args:
            exchange (str): The exchange to get the indicator results for.
            market_pair (str): The pair to get the market pair results for.

        Returns:
            list: A list of dictinaries containing the results of the analysis.
        """

        indicator_dispatcher = self.strategy_analyzer.indicator_dispatcher()
        results = {
            indicator: list()
            for indicator in self.indicator_conf.keys()
        }
        historical_data_cache = self.all_historical_data[exchange][market_pair]

        for indicator in self.indicator_conf:
            if indicator not in indicator_dispatcher:
                self.logger.warn("No such indicator %s, skipping.", indicator)
                continue

            for indicator_conf in self.indicator_conf[indicator]:
                if not indicator_conf['enabled']:
                    continue

                candle_period = indicator_conf['candle_period']

                #Exchange doesnt support such candle period
                if candle_period not in historical_data_cache:
                    continue

                if historical_data_cache[candle_period]:
                    analysis_args = {
                        'historical_data':
                        historical_data_cache[candle_period],
                        'signal': indicator_conf['signal'],
                        'hot_thresh': indicator_conf['hot'],
                        'cold_thresh': indicator_conf['cold']
                    }

                    if 'period_count' in indicator_conf:
                        analysis_args['period_count'] = indicator_conf[
                            'period_count']

                    if indicator == 'rsi' and 'lrsi_filter' in indicator_conf:
                        analysis_args['lrsi_filter'] = indicator_conf[
                            'lrsi_filter']

                    results[indicator].append({
                        'result':
                        self._get_analysis_result(indicator_dispatcher,
                                                  indicator, analysis_args,
                                                  market_pair),
                        'config':
                        indicator_conf
                    })
        return results

    def _get_informant_results(self, exchange, market_pair):
        """Execute the informant analysis on a particular exchange and pair.

        Args:
            exchange (str): The exchange to get the indicator results for.
            market_pair (str): The pair to get the market pair results for.

        Returns:
            list: A list of dictinaries containing the results of the analysis.
        """

        informant_dispatcher = self.strategy_analyzer.informant_dispatcher()
        results = {
            informant: list()
            for informant in self.informant_conf.keys()
        }
        #historical_data_cache = dict()
        historical_data_cache = self.all_historical_data[exchange][market_pair]

        for informant in self.informant_conf:
            if informant not in informant_dispatcher:
                self.logger.warn("No such informant %s, skipping.", informant)
                continue

            for informant_conf in self.informant_conf[informant]:
                if not informant_conf['enabled']:
                    continue

                candle_period = informant_conf['candle_period']

                #Exchange doesnt support such candle period
                if candle_period not in historical_data_cache:
                    continue

                if historical_data_cache[candle_period]:
                    analysis_args = {
                        'historical_data': historical_data_cache[candle_period]
                    }

                    if 'period_count' in informant_conf:
                        analysis_args['period_count'] = informant_conf[
                            'period_count']

                    results[informant].append({
                        'result':
                        self._get_analysis_result(informant_dispatcher,
                                                  informant, analysis_args,
                                                  market_pair),
                        'config':
                        informant_conf
                    })
        return results

    def _get_crossover_results(self, new_result):
        """Execute crossover analysis on the results so far.

        Args:
            new_result (dict): A dictionary containing the results of the informant and indicator
                analysis.

        Returns:
            list: A list of dictinaries containing the results of the analysis.
        """

        crossover_dispatcher = self.strategy_analyzer.crossover_dispatcher()
        results = {
            crossover: list()
            for crossover in self.crossover_conf.keys()
        }

        for crossover in self.crossover_conf:
            if crossover not in crossover_dispatcher:
                self.logger.warn("No such crossover %s, skipping.", crossover)
                continue

            for crossover_conf in self.crossover_conf[crossover]:
                if not crossover_conf['enabled']:
                    self.logger.debug("%s is disabled, skipping.", crossover)
                    continue

                key_indicator = new_result[crossover_conf[
                    'key_indicator_type']][crossover_conf['key_indicator']][
                        crossover_conf['key_indicator_index']]
                crossed_indicator = new_result[
                    crossover_conf['crossed_indicator_type']][
                        crossover_conf['crossed_indicator']][
                            crossover_conf['crossed_indicator_index']]

                dispatcher_args = {
                    'key_indicator':
                    key_indicator['result'],
                    'key_signal':
                    crossover_conf['key_signal'],
                    'key_indicator_index':
                    crossover_conf['key_indicator_index'],
                    'crossed_indicator':
                    crossed_indicator['result'],
                    'crossed_signal':
                    crossover_conf['crossed_signal'],
                    'crossed_indicator_index':
                    crossover_conf['crossed_indicator_index']
                }

                results[crossover].append({
                    'result':
                    crossover_dispatcher[crossover](**dispatcher_args),
                    'config':
                    crossover_conf
                })
        return results

    def _get_historical_data(self, market_pair, exchange, candle_period):
        """Gets a list of OHLCV data for the given pair and exchange.

        Args:
            market_pair (str): The market pair to get the OHLCV data for.
            exchange (str): The exchange to get the OHLCV data for.
            candle_period (str): The timeperiod to collect for the given pair and exchange.

        Returns:
            list: A list of OHLCV data.
        """

        historical_data = list()
        try:
            historical_data = self.exchange_interface.get_historical_data(
                market_pair, exchange, candle_period)
        except RetryError:
            self.logger.error(
                'Too many retries fetching information for pair %s, skipping',
                market_pair)
        except ExchangeError:
            self.logger.error(
                'Exchange supplied bad data for pair %s, skipping',
                market_pair)
        except ValueError as e:
            self.logger.error(e)
            self.logger.error(
                'Invalid data encountered while processing pair %s, skipping',
                market_pair)
            self.logger.debug(traceback.format_exc())
        except AttributeError:
            self.logger.error(
                'Something went wrong fetching data for %s, skipping',
                market_pair)
            self.logger.debug(traceback.format_exc())
        return historical_data

    def _get_analysis_result(self, dispatcher, indicator, dispatcher_args,
                             market_pair):
        """Get the results of performing technical analysis

        Args:
            dispatcher (dict): A dictionary of functions for performing TA.
            indicator (str): The name of the desired indicator.
            dispatcher_args (dict): A dictionary of arguments to provide the analyser
            market_pair (str): The market pair to analyse

        Returns:
            pandas.DataFrame: Returns a pandas.DataFrame of results or an empty string.
        """

        try:
            results = dispatcher[indicator](**dispatcher_args)
        except TypeError:
            self.logger.info(
                'Invalid type encountered while processing pair %s for indicator %s, skipping',
                market_pair, indicator)
            self.logger.info(traceback.format_exc())
            results = str()
        return results

    def _create_charts(self, exchange, indicator_messages, fibonacci):
        """Create charts for each market_pair/candle_period

        Args:
            market_data (dict): A dictionary containing the market data of the symbols
        """

        charts_dir = './charts'

        now = datetime.now(timezone(self.timezone))
        creation_date = now.strftime("%Y-%m-%d %H:%M:%S")

        if not os.path.exists(charts_dir):
            os.mkdir(charts_dir)

        for market_pair in indicator_messages[exchange]:

            candle_messages = indicator_messages[exchange][market_pair]

            #indicators = new_analysis[exchange][market_pair]['indicators']

            #OBV indicators
            #obv = dict()
            #for index, analysis in enumerate(indicators['obv']):
            #if analysis['result'].shape[0] == 0:
            #continue

            #obv[analysis['config']['candle_period']] = analysis['result']

            historical_data = self.all_historical_data[exchange][market_pair]

            fibonacci_levels = fibonacci[exchange][market_pair]

            for candle_period in candle_messages:
                if len(candle_messages[candle_period]) == 0:
                    continue

                candles_data = historical_data[candle_period]
                self.logger.info('Creating chart for %s %s %s', exchange,
                                 market_pair, candle_period)

                self._create_chart(exchange, market_pair, candle_period,
                                   candles_data, fibonacci_levels, charts_dir,
                                   creation_date)

    def _create_chart(self, exchange, market_pair, candle_period, candles_data,
                      fibonacci_levels, charts_dir, creation_date):

        df = self.convert_to_dataframe(candles_data)

        plt.rc('axes', grid=True)
        plt.rc('grid',
               color='#b0b0b0',
               linestyle='-',
               linewidth=0.2,
               alpha=0.6)

        left, width = 0.1, 0.8
        rect1 = [left, 0.7, width, 0.25]
        rect2 = [left, 0.5, width, 0.2]
        rect3 = [left, 0.3, width, 0.2]
        rect4 = [left, 0.1, width, 0.2]

        fig = plt.figure(facecolor='white')
        fig.set_size_inches(10, 16, forward=True)
        axescolor = '#f6f6f6'  # the axes background color

        ax1 = fig.add_axes(rect1,
                           facecolor=axescolor)  # left, bottom, width, height
        ax2 = fig.add_axes(rect2, facecolor=axescolor, sharex=ax1)
        ax3 = fig.add_axes(rect3, facecolor=axescolor, sharex=ax1)
        ax4 = fig.add_axes(rect4, facecolor=axescolor, sharex=ax1)

        if fibonacci_levels['0.00'] > 0 and fibonacci_levels[
                '0.00'] > fibonacci_levels['100.00']:
            ax1.axhline(fibonacci_levels['0.00'],
                        color='steelblue',
                        linestyle='-',
                        alpha=0.3)
            ax1.axhspan(fibonacci_levels['23.60'],
                        fibonacci_levels['0.00'],
                        alpha=0.2,
                        color='steelblue')
            ax1.axhspan(fibonacci_levels['38.20'],
                        fibonacci_levels['23.60'],
                        alpha=0.3,
                        color='steelblue')
            ax1.axhspan(fibonacci_levels['50.00'],
                        fibonacci_levels['38.20'],
                        alpha=0.2,
                        color='steelblue')
            ax1.axhspan(fibonacci_levels['61.80'],
                        fibonacci_levels['50.00'],
                        alpha=0.3,
                        color='steelblue')
            ax1.axhspan(fibonacci_levels['78.60'],
                        fibonacci_levels['61.80'],
                        alpha=0.2,
                        color='steelblue')
            ax1.axhspan(fibonacci_levels['100.00'],
                        fibonacci_levels['78.60'],
                        alpha=0.3,
                        color='steelblue')

        #Plot Candles chart
        self.plot_candlestick(ax1, df, candle_period)

        #Plot RSI (14)
        self.plot_rsi(ax2, df)

        #Plot OBV indicator with data of last analysis
        #if candle_period in obv :
        self.plot_obv(ax3, candles_data, candle_period)

        # Calculate and plot MACD
        self.plot_macd(ax4, df, candle_period)

        for ax in ax1, ax2, ax3, ax4:
            if ax != ax4:
                for label in ax.get_xticklabels():
                    label.set_visible(False)
            else:
                for label in ax.get_xticklabels():
                    label.set_rotation(30)
                    label.set_horizontalalignment('right')

            ax.xaxis.set_major_locator(mticker.MaxNLocator(10))
            ax.xaxis.set_major_formatter(DateFormatter('%d/%b'))
            ax.xaxis.set_tick_params(which='major', pad=15)

        fig.autofmt_xdate()

        title = '{} {} {} - {}'.format(exchange, market_pair, candle_period,
                                       creation_date).upper()
        fig.suptitle(title, fontsize=14)

        market_pair = market_pair.replace('/', '_').lower()
        chart_file = '{}/{}_{}_{}.png'.format(charts_dir, exchange,
                                              market_pair, candle_period)

        plt.savefig(chart_file)
        plt.close(fig)

    def candlestick_ohlc(self,
                         ax,
                         quotes,
                         width=0.2,
                         colorup='k',
                         colordown='r',
                         alpha=1.0,
                         ochl=False):
        """
        Plot the time, open, high, low, close as a vertical line ranging
        from low to high.  Use a rectangular bar to represent the
        open-close span.  If close >= open, use colorup to color the bar,
        otherwise use colordown

        Parameters
        ----------
        ax : `Axes`
            an Axes instance to plot to
        quotes : sequence of quote sequences
            data to plot.  time must be in float date format - see date2num
            (time, open, high, low, close, ...) vs
            (time, open, close, high, low, ...)
            set by `ochl`
        width : float
            fraction of a day for the rectangle width
        colorup : color
            the color of the rectangle where close >= open
        colordown : color
            the color of the rectangle where close <  open
        alpha : float
            the rectangle alpha level
        ochl: bool
            argument to select between ochl and ohlc ordering of quotes

        Returns
        -------
        ret : tuple
            returns (lines, patches) where lines is a list of lines
            added and patches is a list of the rectangle patches added

        """

        OFFSET = width / 2.0

        lines = []
        patches = []
        for q in quotes:
            if ochl:
                t, open, close, high, low = q[:5]
            else:
                t, open, high, low, close = q[:5]

            if close >= open:
                color = colorup
                lower = open
                height = close - open
            else:
                color = colordown
                lower = close
                height = open - close

            vline = Line2D(
                xdata=(t, t),
                ydata=(low, high),
                color=color,
                linewidth=0.5,
                antialiased=False,
            )

            rect = Rectangle(xy=(t - OFFSET, lower),
                             width=width,
                             height=height,
                             facecolor=color,
                             edgecolor=None,
                             antialiased=False,
                             alpha=1.0)

            lines.append(vline)
            patches.append(rect)
            ax.add_line(vline)
            ax.add_patch(rect)

        ax.autoscale_view()

        return lines, patches

    def plot_candlestick(self, ax, df, candle_period):
        textsize = 11

        _time = mdates.date2num(df.index.to_pydatetime())
        min_x = np.nanmin(_time)
        max_x = np.nanmax(_time)

        stick_width = ((max_x - min_x) / _time.size)

        prices = df["close"]

        ax.set_ymargin(0.2)
        ax.ticklabel_format(axis='y', style='plain')

        self.candlestick_ohlc(ax,
                              zip(_time, df['open'], df['high'], df['low'],
                                  df['close']),
                              width=stick_width,
                              colorup='olivedrab',
                              colordown='crimson')

        ma25 = self.moving_average(prices, 25, type='simple')
        ma7 = self.moving_average(prices, 7, type='simple')

        ax.plot(df.index, ma25, color='indigo', lw=0.6, label='MA (25)')
        ax.plot(df.index, ma7, color='orange', lw=0.6, label='MA (7)')

        ax.text(0.04,
                0.94,
                'MA (7, close, 0)',
                color='orange',
                transform=ax.transAxes,
                fontsize=textsize,
                va='top')
        ax.text(0.24,
                0.94,
                'MA (25, close, 0)',
                color='indigo',
                transform=ax.transAxes,
                fontsize=textsize,
                va='top')

    def plot_rsi(self, ax, df):
        textsize = 11
        fillcolor = 'darkmagenta'

        rsi = self.relative_strength(df["close"])

        ax.plot(df.index, rsi, color=fillcolor, linewidth=0.5)
        ax.axhline(70, color='darkmagenta', linestyle='dashed', alpha=0.6)
        ax.axhline(30, color='darkmagenta', linestyle='dashed', alpha=0.6)
        ax.fill_between(df.index,
                        rsi,
                        70,
                        where=(rsi >= 70),
                        facecolor=fillcolor,
                        edgecolor=fillcolor)
        ax.fill_between(df.index,
                        rsi,
                        30,
                        where=(rsi <= 30),
                        facecolor=fillcolor,
                        edgecolor=fillcolor)
        ax.set_ylim(0, 100)
        ax.set_yticks([30, 70])
        ax.text(0.024,
                0.94,
                'RSI (14)',
                va='top',
                transform=ax.transAxes,
                fontsize=textsize)

    def plot_macd(self, ax, df, candle_period):
        textsize = 11

        df = StockDataFrame.retype(df)
        df['macd'] = df.get('macd')

        min_y = df.macd.min()
        max_y = df.macd.max()

        #Reduce Macd Histogram values to have a better visualization
        macd_h = df.macdh * 0.5

        if (macd_h.min() < min_y):
            min_y = macd_h.min()

        if (macd_h.max() > max_y):
            max_y = macd_h.max()

        #Adding some extra space at bottom/top
        min_y = min_y * 1.2
        max_y = max_y * 1.2

        #Define candle bar width
        _time = mdates.date2num(df.index.to_pydatetime())
        min_x = np.nanmin(_time)
        max_x = np.nanmax(_time)

        bar_width = ((max_x - min_x) / _time.size) * 0.8

        ax.bar(x=_time,
               bottom=[0 for _ in macd_h.index],
               height=macd_h,
               width=bar_width,
               color="red",
               alpha=0.4)
        ax.plot(_time, df.macd, color='blue', lw=0.6)
        ax.plot(_time, df.macds, color='red', lw=0.6)
        ax.set_ylim((min_y, max_y))

        ax.yaxis.set_major_locator(mticker.MaxNLocator(nbins=5, prune='upper'))
        ax.text(0.024,
                0.94,
                'MACD (12, 26, close, 9)',
                va='top',
                transform=ax.transAxes,
                fontsize=textsize)

    def plot_obv(self, ax, candles_data, candle_period):

        obv_df = OBV().analyze(candles_data)

        textsize = 11

        min_y = obv_df.obv.min()
        max_y = obv_df.obv.max()

        #Adding some extra space at bottom/top
        min_y = min_y * 1.2
        max_y = max_y * 1.2

        _time = mdates.date2num(obv_df.index.to_pydatetime())

        ax.plot(_time, obv_df.obv, color='blue', lw=0.6)

        ax.set_ylim((min_y, max_y))

        ax.yaxis.set_major_locator(mticker.MaxNLocator(nbins=5, prune='upper'))
        ax.text(0.024,
                0.94,
                'OBV',
                va='top',
                transform=ax.transAxes,
                fontsize=textsize)

    def relative_strength(self, prices, n=14):
        """
        compute the n period relative strength indicator
        http://stockcharts.com/school/doku.php?id=chart_school:glossary_r#relativestrengthindex
        http://www.investopedia.com/terms/r/rsi.asp
        """

        deltas = np.diff(prices)
        seed = deltas[:n + 1]
        up = seed[seed >= 0].sum() / n
        down = -seed[seed < 0].sum() / n
        rs = up / down
        rsi = np.zeros_like(prices)
        rsi[:n] = 100. - 100. / (1. + rs)

        for i in range(n, len(prices)):
            delta = deltas[i - 1]  # cause the diff is 1 shorter

            if delta > 0:
                upval = delta
                downval = 0.
            else:
                upval = 0.
                downval = -delta

            up = (up * (n - 1) + upval) / n
            down = (down * (n - 1) + downval) / n

            rs = up / down
            rsi[i] = 100. - 100. / (1. + rs)

        return rsi

    def moving_average(self, x, n, type='simple'):
        """
        compute an n period moving average.

        type is 'simple' | 'exponential'

        """
        x = np.asarray(x)
        if type == 'simple':
            weights = np.ones(n)
        else:
            weights = np.exp(np.linspace(-1., 0., n))

        weights /= weights.sum()

        a = np.convolve(x, weights, mode='full')[:len(x)]
        a[:n] = a[n]
        return a

    def get_indicator_messages(self, new_analysis, market_data, template):
        """Creates a message list from a user defined template

        Args:
            new_analysis (dict): A dictionary of data related to the analysis to send a message about.
            template (str): A Jinja formatted message template.

        Returns:
            list: A list with the templated messages for the notifier.
        """

        if not self.last_analysis:
            self.last_analysis = new_analysis

        message_template = Template(template)

        new_messages = dict()
        ohlcv_values = dict()
        lrsi_values = dict()

        for exchange in new_analysis:

            new_messages[exchange] = dict()
            ohlcv_values[exchange] = dict()
            lrsi_values[exchange] = dict()

            for market_pair in new_analysis[exchange]:

                new_messages[exchange][market_pair] = dict()
                ohlcv_values[exchange][market_pair] = dict()
                lrsi_values[exchange][market_pair] = dict()

                #Getting price values for each market pair and candle period
                for indicator_type in new_analysis[exchange][market_pair]:
                    if indicator_type == 'informants':
                        for index, analysis in enumerate(
                                new_analysis[exchange][market_pair]
                            ['informants']['ohlcv']):
                            values = dict()
                            for signal in analysis['config']['signal']:
                                values[signal] = analysis['result'].iloc[-1][
                                    signal]
                                ohlcv_values[exchange][market_pair][analysis[
                                    'config']['candle_period']] = values

                        for index, analysis in enumerate(
                                new_analysis[exchange][market_pair]
                            ['informants']['lrsi']):
                            values = dict()
                            for signal in analysis['config']['signal']:
                                values[signal] = analysis['result'].iloc[-1][
                                    signal]

                            lrsi_values[exchange][market_pair][
                                analysis['config']['candle_period']] = values

                for indicator_type in new_analysis[exchange][market_pair]:
                    if indicator_type == 'informants':
                        continue

                    for indicator in new_analysis[exchange][market_pair][
                            indicator_type]:
                        for index, analysis in enumerate(
                                new_analysis[exchange][market_pair]
                            [indicator_type][indicator]):
                            if analysis['result'].shape[0] == 0:
                                continue

                            values = dict()
                            if 'candle_period' in analysis['config']:
                                candle_period = analysis['config'][
                                    'candle_period']
                                new_messages[exchange][market_pair][
                                    candle_period] = list()

                            if indicator_type == 'indicators':
                                #Check for any user config
                                #if candle_period not in user_indicators[indicator]:
                                #self.logger.info('###Skipping %s %s ' % (indicator, candle_period))
                                #continue

                                for signal in analysis['config']['signal']:
                                    latest_result = analysis['result'].iloc[-1]

                                    values[signal] = analysis['result'].iloc[
                                        -1][signal]
                                    if isinstance(values[signal], float):
                                        values[signal] = format(
                                            values[signal], '.2f')
                            elif indicator_type == 'crossovers':
                                latest_result = analysis['result'].iloc[-1]

                                key_signal = '{}_{}'.format(
                                    analysis['config']['key_signal'],
                                    analysis['config']['key_indicator_index'])

                                crossed_signal = '{}_{}'.format(
                                    analysis['config']['crossed_signal'],
                                    analysis['config']
                                    ['crossed_indicator_index'])

                                values[key_signal] = analysis['result'].iloc[
                                    -1][key_signal]
                                if isinstance(values[key_signal], float):
                                    values[key_signal] = format(
                                        values[key_signal], '.2f')

                                values[crossed_signal] = analysis[
                                    'result'].iloc[-1][crossed_signal]
                                if isinstance(values[crossed_signal], float):
                                    values[crossed_signal] = format(
                                        values[crossed_signal], '.2f')

                            status = 'neutral'
                            if latest_result['is_hot']:
                                status = 'hot'
                            elif latest_result['is_cold']:
                                status = 'cold'

                            # Save status of indicator's new analysis
                            new_analysis[exchange][market_pair][
                                indicator_type][indicator][index][
                                    'status'] = status

                            if latest_result['is_hot'] or latest_result[
                                    'is_cold']:
                                try:
                                    last_status = self.last_analysis[exchange][
                                        market_pair][indicator_type][
                                            indicator][index]['status']
                                except:
                                    last_status = str()

                                should_alert = True

                                if analysis['config'][
                                        'alert_frequency'] == 'once':
                                    if last_status == status:
                                        should_alert = False

                                if not analysis['config']['alert_enabled']:
                                    should_alert = False

                                if should_alert:
                                    base_currency, quote_currency = market_pair.split(
                                        '/')
                                    precision = market_data[exchange][
                                        market_pair]['precision']
                                    decimal_format = '.{}f'.format(
                                        precision['price'])

                                    candle_period = analysis['config'][
                                        'candle_period']

                                    prices = ''
                                    candle_values = ohlcv_values[exchange][
                                        market_pair]

                                    if candle_period in candle_values:
                                        for key, value in candle_values[
                                                candle_period].items():
                                            value = format(
                                                value, decimal_format)
                                            prices = '{} {}: {}'.format(
                                                prices, key.title(), value)

                                    lrsi = ''
                                    if candle_period in lrsi_values[exchange][
                                            market_pair]:
                                        lrsi = lrsi_values[exchange][
                                            market_pair][candle_period]['lrsi']

                                    new_message = message_template.render(
                                        values=values,
                                        exchange=exchange,
                                        market=market_pair,
                                        base_currency=base_currency,
                                        quote_currency=quote_currency,
                                        indicator=indicator,
                                        indicator_number=index,
                                        analysis=analysis,
                                        status=status,
                                        last_status=last_status,
                                        prices=prices,
                                        lrsi=lrsi)

                                    new_messages[exchange][market_pair][
                                        candle_period].append(new_message)

        # Merge changes from new analysis into last analysis
        self.last_analysis = {**self.last_analysis, **new_analysis}
        return new_messages