From 8703f3ebe665183413780a16318e2c9efb6b0845 Mon Sep 17 00:00:00 2001 From: gcarq Date: Wed, 24 May 2017 21:52:41 +0200 Subject: [PATCH] implement StochRSI buy signal handler --- analyze.py | 127 +++++++++++++++++++++++++++++++++++++++++++++++ main.py | 17 +++++-- requirements.txt | 8 ++- 3 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 analyze.py diff --git a/analyze.py b/analyze.py new file mode 100644 index 000000000..212a26334 --- /dev/null +++ b/analyze.py @@ -0,0 +1,127 @@ +from datetime import timedelta +import time +import arrow +import matplotlib +import logging + +matplotlib.use("Qt5Agg") +import matplotlib.pyplot as plt +import requests +from pandas.io.json import json_normalize +from stockstats import StockDataFrame + +logging.basicConfig(level=logging.DEBUG, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') +logger = logging.getLogger(__name__) + + +def get_ticker_dataframe(pair): + """ + Analyses the trend for the given pair + :param pair: pair as str in format BTC_ETH or BTC-ETH + :return: StockDataFrame + """ + minimum_date = arrow.now() - timedelta(hours=2) + url = 'https://bittrex.com/Api/v2.0/pub/market/GetTicks' + params = { + 'marketName': pair.replace('_', '-'), + 'tickInterval': 'OneMin', + '_': minimum_date.timestamp * 1000 + } + data = requests.get(url, params).json() + if not data['success']: + raise RuntimeError('BITTREX: {}'.format(data['message'])) + + data = [{ + 'close': t['C'], + 'volume': t['V'], + 'open': t['O'], + 'high': t['H'], + 'low': t['L'], + 'date': t['T'], + } for t in data['result'] if arrow.get(t['T']) > minimum_date] + dataframe = StockDataFrame(json_normalize(data)) + + # calculate StochRSI + rsi = dataframe['rsi_{}'.format(14)] + rolling = rsi.rolling(window=14, center=False) + low = rolling.min() + high = rolling.max() + dataframe['stochrsi'] = (rsi - low) / (high - low) + return dataframe + + +def populate_trends(dataframe): + """ + Populates the trends for the given dataframe + :param dataframe: StockDataFrame + :return: StockDataFrame with populated trends + """ + #dataframe.loc[ + # (dataframe['stochrsi'] < 0.20) + # & (dataframe['close_12_ema'] <= dataframe['close_26_ema']), + # 'bullish' + #] = 1 + dataframe.loc[ + (dataframe['stochrsi'] < 0.20), 'underpriced' + ] = 1 + dataframe.loc[dataframe['underpriced'] == 1, 'buy'] = dataframe['close'] + return dataframe + + +def get_buy_signal(pair): + """ + Calculates a buy signal based on StochRSI indicator + :param pair: pair in format BTC_ANT or BTC-ANT + :return: True if pair is underpriced, False otherwise + """ + dataframe = get_ticker_dataframe(pair) + dataframe = populate_trends(dataframe) + latest = dataframe.iloc[-1] + signal = latest['underpriced'] == 1 + logger.debug('buy_trigger: {} (pair={}, signal={})'.format(latest['date'], pair, signal)) + return signal + + +def plot_dataframe(dataframe, pair): + """ + Plots the given dataframe + :param dataframe: StockDataFrame + :param pair: pair as str + :return: None + """ + + # Three subplots sharing x axe + f, (ax1, ax2, ax3) = plt.subplots(3, sharex=True) + f.suptitle(pair, fontsize=14, fontweight='bold') + ax1.plot(dataframe.index.values, dataframe['close'], label='close') + ax1.plot(dataframe.index.values, dataframe['close_12_ema'], label='EMA(12)') + ax1.plot(dataframe.index.values, dataframe['close_26_ema'], label='EMA(26)') + # ax1.plot(dataframe.index.values, dataframe['sell'], 'ro', label='sell') + ax1.plot(dataframe.index.values, dataframe['buy'], 'bo', label='buy') + ax1.legend() + + ax2.plot(dataframe.index.values, dataframe['macd'], label='MACD') + ax2.plot(dataframe.index.values, dataframe['macds'], label='MACDS') + ax2.plot(dataframe.index.values, dataframe['macdh'], label='MACD Histogram') + ax2.plot(dataframe.index.values, [0] * len(dataframe.index.values)) + ax2.legend() + + ax3.plot(dataframe.index.values, dataframe['stochrsi'], label='StochRSI') + ax3.plot(dataframe.index.values, [0.80] * len(dataframe.index.values)) + ax3.plot(dataframe.index.values, [0.20] * len(dataframe.index.values)) + ax3.legend() + + # Fine-tune figure; make subplots close to each other and hide x ticks for + # all but bottom plot. + f.subplots_adjust(hspace=0) + plt.setp([a.get_xticklabels() for a in f.axes[:-1]], visible=False) + plt.show() + + +if __name__ == '__main__': + while True: + pair = 'BTC_ANT' + for pair in ['BTC_ANT', 'BTC_ETH', 'BTC_GNT', 'BTC_ETC']: + get_buy_signal(pair) + time.sleep(60) diff --git a/main.py b/main.py index f4f3a1aeb..b6df1237f 100755 --- a/main.py +++ b/main.py @@ -11,6 +11,8 @@ from requests import ConnectionError from wrapt import synchronized +from analyze import get_buy_signal + logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) @@ -203,9 +205,18 @@ def create_trade(stake_amount: float, exchange): if not whitelist: raise ValueError('No pair in whitelist') - # Pick random pair and execute trade - idx = random.randint(0, len(whitelist) - 1) - pair = whitelist[idx] + ## Pick random pair and execute trade + #idx = random.randint(0, len(whitelist) - 1) + #pair = whitelist[idx] + pair = None + # Pick pair based on StochRSI buy signals + for p in whitelist: + if get_buy_signal(p): + pair = p + break + else: + raise ValueError('No buy signal from pairs: {}'.format(','.join(whitelist))) + open_rate = api_wrapper.get_ticker(pair)['ask'] amount = stake_amount / open_rate exchange = exchange diff --git a/requirements.txt b/requirements.txt index 6d1231b7b..882210315 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,10 @@ python-telegram-bot==5.3.1 arrow==0.10.0 requests==2.14.2 urllib3==1.20 -wrapt==1.10.10 \ No newline at end of file +wrapt==1.10.10 +pandas==0.20.1 +matplotlib==2.0.0 +PYQT5==5.8 +scikit-learn==0.18.1 +scipy==0.18.1 +stockstats==0.2.0 \ No newline at end of file