def rsi(symbol): URL = "https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=" + symbol + "&apikey=" + config.alphaadvantage_api_key_id r = requests.get(url=URL) data = r.json() days = data["Time Series (Daily)"] closingPrices = [] for day in days: closingPrices.append(float(days[day]['4. close'])) cleanedData = [] # print (closingPrices) srsi = stochrsi(closingPrices, 28) for dataPoint in srsi: if (math.isnan(dataPoint) == False): cleanedData.append(dataPoint) #above 70 buy no matter what if (cleanedData[0] > 60): return 1 if (cleanedData[0] <= 40): return 0 #above 30 buy if on upward trend for last 3 if (cleanedData[0] > 40): if (cleanedData[0] > cleanedData[1]): return 1 # if its not in buy range or above sell range and on upper trajectory we should sell return 0
def loop(self): timeout = 3 / float(len(self.products)) counter = 0 by_volume = [] # array of dicts # TODO: for coins that are in the balance, sell once order book is tending towards selling side # maybe also last 10 trades could be checked and if most are sell, then sell as well. # TODO: move checker for coins in balance into a separate, faster loop. while self._running: self._balances = self.get_balance() # refresh balance product = self.products[counter] ticker_symbol = product['symbol'] if ticker_symbol.endswith('BTC'): idx = ticker_symbol.index('BTC') elif ticker_symbol.endswith('USDT'): idx = ticker_symbol.index('USDT') coin1 = ticker_symbol[:idx] coin2 = ticker_symbol[idx:] if self.is_coin_blacklisted(coin1): logging.info("{}: blacklisted, skipping.".format(coin1)) else: #logging.info("Check {}...".format(ticker_symbol)) try: # get klines since close new_klines = self.client.get_klines( symbol=ticker_symbol, interval='5m', startTime=self._klines[ticker_symbol][-1][0]) new_klines2 = self.client.get_klines( symbol=ticker_symbol, interval='1h', startTime=self._klines2[ticker_symbol][-1][0]) for kline_group in [{ 'new': new_klines, 'old': self._klines }, { 'new': new_klines2, 'old': self._klines2 }]: for kline in kline_group['new']: if kline[0] != kline_group['old'][ticker_symbol][ -1][0]: kline_group['old'][ticker_symbol].append(kline) else: kline_group['old'][ticker_symbol][-1] = kline if len(kline_group['old'] [ticker_symbol]) > self._start_size: kline_group['old'][ticker_symbol].pop(0) # short term volumes = list( map(lambda item: float(item[5]), self._klines[ticker_symbol][-30:])) volumes_sorted = sorted(volumes) median_volume = 0 mean_volume = reduce(lambda x, y: x + y, volumes_sorted) / len(volumes_sorted) if len(volumes_sorted) % 2 != 0: mid = len(volumes_sorted) / 2 f = floor(mid) c = ceil(mid) assert f < c, "floor should be less than ceiling ({} vs {})".format( f, c) median_volume = (volumes_sorted[f] + volumes_sorted[c]) / 2 else: median_volume = volumes_sorted[len(volumes_sorted) // 2] # divide median by mean med_mean = median_volume / mean_volume last_volume = volumes[-1] last_volume_mean = last_volume / mean_volume ticker_data = self.client.get_ticker(symbol=ticker_symbol) volume_btc = float(ticker_data['volume']) * float( ticker_data['lastPrice']) if float(self._klines[ticker_symbol][-2][1]) <= float( self._klines[ticker_symbol][-2][4]): if (coin1 not in self._balances or self._balances[coin1] == 0) and (coin2 in self._balances and self._balances[coin2] > 0): price_closes = list( map(lambda item: float(item[4]), self._klines[ticker_symbol])) sma7 = sma(price_closes, 7) sma20 = sma(price_closes, 20) logging.info("{}: SMA7: {}, SMA20: {}".format( ticker_symbol, sma7[-1], sma20[-1])) depth = self.client.get_order_book( symbol=ticker_symbol, limit=10) bid_sum = reduce( lambda x, y: x + y, list( map(lambda item: float(item[1]), depth['bids']))) ask_sum = reduce( lambda x, y: x + y, list( map(lambda item: float(item[1]), depth['asks']))) logging.info("{} bid sum: {}, ask sum: {}".format( ticker_symbol, bid_sum, ask_sum)) if (volume_btc > 200) and ( last_volume_mean / med_mean > 2) and (price_closes[-1] > sma7[-1]) and ( sma7[-1] > sma20[-1]): price_closes_lt = list( map(lambda kline: float(kline[4]), self._klines2[ticker_symbol])) stoch_rsi_results = stochrsi( price_closes_lt, 14) logging.info("{} SRSI : {}".format( ticker_symbol, stoch_rsi_results[-1])) if stoch_rsi_results[-1] >= 60: logging.info( "skip {}, already pumped".format( ticker_symbol)) elif bid_sum < ask_sum: logging.info( "Not buying {} because bid sum ({}) < ask sum ({})" .format(ticker_symbol, bid_sum, ask_sum)) else: self.buy(symbol=ticker_symbol, coin1=coin1, coin2=coin2) self.blacklist_coin( best_coin['coin1'], THIRTY_MINUTES) # by_volume.append({ # 'symbol': ticker_symbol, # 'coin1': coin1, # 'coin2': coin2, # 'value': last_volume_mean / med_mean, # 'sma7': sma7 # }) # if last_volume_mean / med_mean > 2: # logging.info("\n\n!! {} Abnormal volume = {} / {}\n\n".format(ticker_symbol, last_volume_mean, med_mean)) # if float(self._klines[ticker_symbol][-2][1]) > float(self._klines[ticker_symbol][-2][4]): # logging.info("{} is likely falling (red candle), not buying.".format(ticker_symbol)) # else: # self.buy(ticker_symbol, coin1, coin2) coin_amount_held = 0 coin_is_dust = True trailing_stop_triggered = False if coin1 in self._balances: coin_amount_held = self._balances[coin1] coin_price = float(self._klines[ticker_symbol][-1][4]) if coin2 == 'BTC': if coin_price * coin_amount_held >= 0.0005: # lt ~$5 worth of bitcoin coin_is_dust = False elif coin2 == 'USDT': if coin_price * coin_amount_held >= 5: # lt ~$5 coin_is_dust = False else: raise Exception( "Unsure how to handle coin2 type: {}".format( coin2)) if coin_is_dust: self._balances[coin1] = 0 # just set to zero else: if ticker_symbol in self._trailing_stops: if coin_price <= self._trailing_stops[ ticker_symbol]: logging.info( "{}: Trailing stop triggered @ {}".format( ticker_symbol, coin_price)) self.sell(ticker_symbol, coin1, coin2) self.blacklist_coin(coin1, TWO_HOURS) trailing_stop_triggered = True else: self.update_trailing_stop_for(ticker_symbol) except Exception as ex: logging.error("Error: {}".format(ex)) counter += 1 counter %= len(self.products) if False and counter == 0: by_volume_sorted = sorted(by_volume, key=lambda item: item['value']) best_coin = None for i in range(len(by_volume_sorted) - 1, -1, -1): item = by_volume_sorted[i] last_sma7 = item['sma7'][-1] # find a recent candle with most volume, determine red or green last_klines = self._klines[item['symbol']][-4:] last_kline = self._klines[item['symbol']][-1] top_kline = None for kline in last_klines: if top_kline is None or (float(kline[5]) > float( top_kline[5])): top_kline = kline assert top_kline is not None logging.info("{} top_kline = {}".format( item['symbol'], top_kline)) # determine red or green if float(top_kline[1]) > float(top_kline[4]) or float( last_kline[1]) > float(last_kline[4]): logging.info( "skip {} because dump determined ({} > {})".format( item['symbol'], top_kline[1], top_kline[4])) else: price_closes = list( map(lambda kline: float(kline[4]), self._klines[item['symbol']])) stoch_rsi_results = stochrsi(price_closes, 14) logging.info("{} SRSI : {}".format( item['symbol'], stoch_rsi_results[-1])) if stoch_rsi_results[-1] >= 60: logging.info("skip {}, already pumped".format( item['symbol'])) else: if float(last_klines[-2][4]) > last_sma7: best_coin = item break else: logging.info("{} is below sma ({}, {})".format( item['symbol'], last_sma7, last_klines[-2][4])) assert best_coin is not None logging.info("by_volume_sorted = {}".format(by_volume_sorted)) #logging.info("\nbest_coin = {}\n".format(best_coin)) self.buy(symbol=best_coin['symbol'], coin1=best_coin['coin1'], coin2=best_coin['coin2']) self.blacklist_coin(best_coin['coin1'], THIRTY_MINUTES) by_volume = [] time.sleep(timeout)
def test_stochrsi_invalid_period(self): period = 128 with self.assertRaises(Exception) as cm: stochrsi.stochrsi(self.data, period) expected = "Error: data_len < period" self.assertEqual(str(cm.exception), expected)
def test_stochrsi_period_10(self): period = 10 sr = stochrsi.stochrsi(self.data, period) np.testing.assert_array_equal(sr, self.stochrsi_period_10_expected)
def loop(self): timeout = self.get_timeout() counter = 0 oversold_ary = [] # array of dicts while self._running: self._balances = self.get_balance() # refresh balance product = self.products[counter] ticker_symbol = product['symbol'] logging.info("Check {}...".format(ticker_symbol)) try: # get klines since close new_klines = self.client.get_klines(symbol=ticker_symbol, interval='15m', startTime=self._klines[ticker_symbol][-1][0]) new_klines2 = self.client.get_klines(symbol=ticker_symbol, interval='1h', startTime=self._klines2[ticker_symbol][-1][0]) for kline_group in [{ 'new': new_klines, 'old': self._klines }, { 'new': new_klines2, 'old': self._klines2 }]: for kline in kline_group['new']: if kline[0] != kline_group['old'][ticker_symbol][-1][0]: kline_group['old'][ticker_symbol].append(kline) else: kline_group['old'][ticker_symbol][-1] = kline if len(kline_group['old'][ticker_symbol]) > self._start_size: kline_group['old'][ticker_symbol].pop(0) # short term price_closes = list(map(lambda item: float(item[4]), self._klines[ticker_symbol][:-1])) stoch_rsi_results = stochrsi(price_closes, 14) # long term price_closes_lt = list(map(lambda item: float(item[4]), self._klines2[ticker_symbol])) stoch_rsi_results_lt = stochrsi(price_closes_lt, 14) if ticker_symbol.endswith('BTC'): idx = ticker_symbol.index('BTC') elif ticker_symbol.endswith('USDT'): idx = ticker_symbol.index('USDT') coin1 = ticker_symbol[:idx] coin2 = ticker_symbol[idx:] ticker_data = self.client.get_ticker(symbol=ticker_symbol) volume_btc = float(ticker_data['volume']) * float(ticker_data['lastPrice']) coin_amount_held = 0 coin_is_dust = True trailing_stop_triggered = False if coin1 in self._balances: coin_amount_held = self._balances[coin1] coin_price = float(self._klines[ticker_symbol][-1][4]) if coin2 == 'BTC': if coin_price * coin_amount_held >= 0.0005: # lt ~$5 worth of bitcoin coin_is_dust = False elif coin2 == 'USDT': if coin_price * coin_amount_held >= 5: # lt ~$5 coin_is_dust = False else: raise Exception("Unsure how to handle coin2 type: {}".format(coin2)) if coin_is_dust: self._balances[coin1] = 0 # just set to zero else: if ticker_symbol in self._trailing_stops: if coin_price <= self._trailing_stops[ticker_symbol]: logging.info("{}: Trailing stop triggered @ {}".format(ticker_symbol, coin_price)) self.sell(ticker_symbol, coin1, coin2) self.blacklist_coin(coin1, TWO_HOURS) trailing_stop_triggered = True else: self.update_trailing_stop_for(ticker_symbol) if not trailing_stop_triggered: if stoch_rsi_results[-1] >= 60: if coin1 in self._balances and self._balances[coin1]: if coin1 != 'BNB': # don't sell BNB if coin1 != 'BTC' or self._balances['USDT'] == 0: # don't trade all btc at once, so we can keep trading alts logging.info("{}: overbought ({}) @ {}".format(coin1, stoch_rsi_results[-1], self._klines[ticker_symbol][-1][4])) self.sell(ticker_symbol, coin1, coin2) elif stoch_rsi_results[-1] <= 20: if volume_btc >= 100: oversold_ary.append({ 'symbol': ticker_symbol, 'volume': volume_btc, 'coin1': coin1, 'coin2': coin2, 'srsi': stoch_rsi_results[-1], 'srsi2': stoch_rsi_results_lt[-1] }) logging.info("{}: oversold ({}) @ {} :: (rsi: {}, long term: {})".format(coin1, stoch_rsi_results[-1], self._klines[ticker_symbol][-1][4], stoch_rsi_results[-1], stoch_rsi_results_lt[-1])) #if (coin1 not in self._balances or self._balances[coin1] == 0) and (coin2 in self._balances and self._balances[coin2] > 0): # print("{} oversold ({}) @ {}".format(coin1, stoch_rsi_results[-1], self._klines[symbol][-1][4])) # self.buy(symbol, coin1, coin2) else: logging.info("Volume not high enough for {} ({} BTC), skipping...".format(ticker_symbol, volume_btc)) except Exception as ex: logging.error("Error: {}".format(ex)) counter += 1 counter %= len(self.products) if counter == 0: # sort oversold by lowest rsi # oversold_by_score = [[] * 10] # for item in oversold_ary: # score = round(item['srsi'] / 10) # oversold_by_score[score].append(item) # for sub in oversold_by_score: oversold_sorted = sorted(oversold_ary, key=lambda item: item['srsi']) oversold_final = [] for item in oversold_sorted: coin1 = item['coin1'] coin2 = item['coin2'] ticker_symbol = item['symbol'] srsi = item['srsi'] srsi2 = item['srsi2'] # check if the coin has possibly been dumped by calculating the median volume volumes = map(lambda kline: float(kline[5]), self._klines[item['symbol']]) #sorted(key=lambda item: float(item[5])) volumes_sorted = sorted(volumes) median_volume = 0 mean_volume = reduce(lambda x, y: x + y, volumes_sorted) / len(volumes_sorted) if len(volumes_sorted) % 2 != 0: mid = len(volumes_sorted) / 2 f = floor(mid) c = ceil(mid) assert f < c, "floor should be less than ceiling ({} vs {})".format(f, c) median_volume = (volumes_sorted[f] + volumes_sorted[c]) / 2 else: median_volume = volumes_sorted[len(volumes_sorted) // 2] # divide median by mean med_mean = median_volume / mean_volume if med_mean < 0.7: logging.info("{} has likely been dumped, skipping...".format(coin1)) else: # prevent from buying already pumped coins #if stoch_rsi_results_lt[-1] != 100: # print("{} has likely already been pumped, not buying.".format(coin1)) #else: if (coin1 not in self._balances or self._balances[coin1] == 0) and (coin2 in self._balances and self._balances[coin2] > 0): if self.is_coin_blacklisted(coin1): logging.info("{}: blacklisted, skipping.".format(coin1)) else: oversold_final.append(item) #self.buy(ticker_symbol, coin1, coin2) logging.info("FINAL: {}".format(oversold_final)) for item in oversold_final: coin1 = item['coin1'] coin2 = item['coin2'] ticker_symbol = item['symbol'] srsi = item['srsi'] srsi2 = item['srsi2'] self.buy(ticker_symbol, coin1, coin2) oversold_ary = [] time.sleep(timeout)