class TestScraperCoinmarketcap(unittest.TestCase): """ Tests for Coinmarketcap Api commands. These will fail in the absence of an internet connection or if Coinmarketcap API goes down. """ def setUp(self): self.coinmarketcap = Pymarketcap() def test_endpoints(self): from requests import get endpoints = [ 'currencies/%s/' % config.COIN_NAME, 'gainers-losers/', 'currencies/%s/historical-data/'\ % config.COIN_NAME, 'new', 'exchanges/%s/' % config.EXCHANGE, 'exchanges/volume/24-hour/all/' ] base_url = self.coinmarketcap.web_url for e in endpoints: _status_code = get(base_url + e).status_code self.assertEqual(_status_code, 200) def test_markets(self): actual = self.coinmarketcap.markets(config.COIN) value_types = { 'price_usd': Decimal, '24h_volume_usd': int, 'percent_volume': Decimal, 'pair': str, 'exchange': str } self.assertIs(type(actual), list) self.assertIs(len(actual) > 0, True) for source in actual: self.assertIs(type(source), dict) for key, value in source.items(): self.assertIs(type(value), value_types[key]) def test_ranks(self): temps = ['1h', '24h', '7d'] queries = ['gainers', 'losers'] value_types = { 'percent_change': Decimal, '24h_volume_usd': int, 'symbol': str, 'price_usd': Decimal, 'name': str } actual = self.coinmarketcap.ranks() self.assertIs(type(actual), dict) for q, temp in actual.items(): self.assertIn(q, queries) self.assertIs(type(temp), dict) for t, data in temp.items(): self.assertIn(t, temps) self.assertIs(type(data), list) self.assertIs(len(data) > 0, True) for d in data: self.assertIs(type(d), dict) for key, value in d.items(): self.assertIs(type(value), value_types[key]) # Test invalid argument with self.assertRaises(AttributeError): self.coinmarketcap.ranks('8d') def test_historical(self): from datetime import datetime value_types = { 'close': Decimal, 'low': Decimal, 'usd_volume': int, 'open': Decimal, 'usd_market_cap': int, 'high': Decimal, 'date': datetime } actual = self.coinmarketcap.historical(config.COIN, datetime(2017, 9, 30), datetime(2017, 10, 10)) self.assertIs(type(actual), list) for tick in actual: self.assertIs(type(tick), dict) for key, value in tick.items(): self.assertIs(type(value), value_types[key]) def test_recently(self): actual = self.coinmarketcap.recently() value_types = { 'price_usd': Decimal, 'mineable': bool, 'symbol': str, 'usd_market_cap': [str, int], 'circulating_supply': [str, int], 'volume_24h_usd': [str, int], 'days_ago': [str, int], 'name': str } self.assertIs(type(actual), list) for c in actual: self.assertIs(type(c), dict) for key, value in c.items(): if type(value_types[key]) is list: self.assertIn(type(value), value_types[key]) else: self.assertIs(type(value), value_types[key]) def test_exchange(self): actual = self.coinmarketcap.exchange(config.EXCHANGE) value_types = { 'market': str, 'price_usd': Decimal, 'rank': int, 'volume_24h_usd': int, 'name': str, 'perc_volume': Decimal } self.assertIs(type(actual), list) for market in actual: self.assertIs(type(market), dict) for key, value in market.items(): self.assertIs(type(value), value_types[key]) def test_exchanges(self): actual = self.coinmarketcap.exchanges() value_types = { 'market': str, 'price_usd': Decimal, 'rank': int, 'volume_24h_usd': int, 'name': str, 'perc_volume': Decimal, 'perc_change': Decimal } self.assertIs(type(actual), list) for exch in actual: self.assertIs(type(exch), dict) for key, value in exch.items(): if key in ('rank', 'volume_usd'): self.assertIs(type(value), int) elif key == 'name': self.assertIs(type(value), str) elif key == 'markets': self.assertIs(type(value), list) for m in value: self.assertIs(type(m), dict) for _key, _value in m.items(): self.assertIs(type(_value), value_types[_key]) def test_exchange_names(self): actual = self.coinmarketcap.exchange_names self.assertIs(type(actual), list)
class coinmarkets(object): def __init__(self, app): self.app = app self.webhook = "YOUR SLACK WEBHOOK" self.coinmarketcap = Pymarketcap() self.bittrex = ccxt.bittrex() self.poloniex = ccxt.poloniex() self.quadraigacx = ccxt.poloniex() self.exchanges = { 'bittrex': self.bittrex, 'poloniex': self.poloniex, 'quadraigacx': self.quadraigacx, 'coinmarketcap': self.coinmarketcap } self.loadMarkets() self.dispatch_map = { 'showalert': self.showAlert, 'removealert': self.removeAlert, 'alert': self.createAlert, 'topten': self.topten, 'updatecoin': self.updatecoin, 'gainers': self.gainers, 'losers': self.losers, 'symbols': self.symbols } alertFile = Path("alert.p") if alertFile.is_file(): self.alerts = pickle.load(open('alert.p', "rb")) else: self.alerts = [] self.symbols = [] self.timeframe = ['7d', '24h', '1h'] self.symbols = self.coinmarketcap.symbols self.coinmarket_latestinfo = [] self.bittrex_latestinfo = [] self.poloniex_latestinfo = [] self.quadraigacx_latestinfo = [] scheduler = BackgroundScheduler() scheduler.add_job(self.refreshinfo, 'interval', seconds=20) logging.basicConfig() scheduler.start() def parseCommand(self, command, text, responseUrl, username): args = tuple(text.split()) #method = getattr(self, method_name, lambda: "nothing") print(text) print(args) print(command) if args: self.dispatch_map[command](args, responseUrl, username) else: self.dispatch_map[command](["empty"], responseUrl, username) return "processing request" def loadMarkets(self): self.bittrex.loadMarkets() self.poloniex.loadMarkets() self.quadraigacx.loadMarkets() def showAlert(self, args, responseUrl, username): if args[0] == "empty": results = tasks.search(self.alerts, 'username', username) if not results: tasks.sendTextResponse(responseUrl, "no alerts found for " + username, "ephemeral") else: data = simplejson.dumps(self.alerts, use_decimal=True) tasks.sendTextResponse(responseUrl, data, "ephemeral") else: if args[0] == "all": data = simplejson.dumps(self.alerts, use_decimal=True) tasks.sendTextResponse(responseUrl, data, "ephemeral") #tasks.sendJsonResponse(responseUrl, "all alerts" , data, "ephemeral") else: tasks.sendTextResponse(responseUrl, "invalid command", "ephemeral") def createAlert(self, args, responseUrl, username): # args = [cointype,price,market] btcUSD = 0 timestamp = str(int(time.time())) if len(args) < 3: return "invalid command" else: market = args[2] if args[0] in self.symbols: symbol = str(args[0]) if (symbol == "BTC"): pair = "BTC/USDT" exchange = self.exchanges[args[2]] currentPrice = exchange.fetch_ticker(pair)['last'] else: pair = symbol + "/BTC" exchange = self.exchanges[args[2]] results = exchange.fetch_ticker(pair) if not results: return tasks.sendTextResponse( responseUrl, "currency not found in " + market, "ephemeral") else: btcUSD = exchange.fetch_ticker('BTC/USDT')['last'] currentPrice = float(results['last'] * btcUSD) if ('%' in args[1]): change = args[1].replace('%', ' ') if ('-' in change): change = float(change.replace('-', ' ')) percentage = float((100 - change) / 100) setPrice = float(percentage * float(currentPrice)) else: percentage = float((100 + float(change)) / 100) setPrice = float(percentage * float(currentPrice)) else: setPrice = float(args[1]) if float(currentPrice) < setPrice: alertType = "high" elif float(currentPrice) > setPrice: alertType = 'low' #alert when the price is lower than the setPrice else: return tasks.sendTextResponse( responseUrl, "error: current price == set price", "ephemeral") alert = { 'symbol': symbol, 'setPrice': setPrice, 'market': market, 'originalPrice': currentPrice, 'type': alertType, 'username': username, "timestamp": timestamp } self.alerts.append(alert) pickle.dump(self.alerts, open("alert.p", "wb")) tasks.createAlertResponse(alert, str(currentPrice), "Alert Created", responseUrl) return tasks.sendTextResponse(responseUrl, "command received", "ephemeral") else: return tasks.sendTextResponse(responseUrl, "invalid command", "ephemeral") def topten(self, args1, responseUrl, username): results = self.coinmarketcap.ticker(limit=10) results = results[0] print(results) return json.dumps(results, default=decimal_default) def updatecoin(self, args, responseUrl, username): if len(args) < 2: market = self.coinmarketcap.ticker(args[0], convert='USD') percentChange = " | 1h: " + str( market['percent_change_1h']) + " % " + " | 24h: " + str( market['percent_change_24h']) + " % " + " | 7d: " + str( market['percent_change_7d']) + " % " text = str("Current Price for " + str(args[0]) + ": " + str(market['price_usd']) + percentChange + " | " + "coinmarketcap") return tasks.sendTextResponse(responseUrl, text, "in_channel") elif args[1] == "all": data = self.coinmarketcap.markets(args[0]) tasks.updateAllCoinHelper.delay(args, data, responseUrl) else: #data = self.coinmarketcap.exchange(args[1]) exchange = self.exchanges[args[1]] tasks.updateCoinHelper( args, exchange, responseUrl, ) return tasks.sendTextResponse(responseUrl, "updatecoin received", "ephemeral") return "command received" def symbols(self, args1, responseUrl, username): print(args1) print(self.coinmarketcap.symbols) return json.dumps(self.coinmarketcap.symbols) def gainers(self, args, responseUrl, username): if args[0] == "empty" or args[0] not in self.timeframe or args[ 0] == "1h": args[0] = '1h' json_data = (self.coinmarketcap.ranks('gainers', args[0])[args[0]]) elif args[0] == '24h': json_data = (self.coinmarketcap.ranks('gainers', args[0])[args[0]]) elif args[0] == '7d': json_data = (self.coinmarketcap.ranks('gainers', args[0])[args[0]]) else: return tasks.sendTextResponse(responseUrl, "invalid timeframe", "ephemeral") tasks.gainerLoserHelper.delay(args, responseUrl, json_data, "gainers") return tasks.sendTextResponse(responseUrl, "top gainers received", "ephemeral") def losers(self, args, responseUrl, username): if args[0] == "empty" or args[0] not in self.timeframe or args[ 0] == "1h": json_data = (self.coinmarketcap.ranks('losers', '1h')['1h']) elif args[0] == '24h': json_data = (self.coinmarketcap.ranks('losers', args[0])[args[0]]) elif args[0] == '7d': json_data = (self.coinmarketcap.ranks('losers', args[0])[args[0]]) else: return tasks.sendTextResponse(responseUrl, "invalid timeframe", "ephemeral") tasks.gainerLoserHelper.delay(args, responseUrl, json_data, "losers") return tasks.sendTextResponse(responseUrl, "top losers received", "ephemeral") def refreshinfo(self): #self.coinmarket_latestinfo = self.coinmarketcap.ticker(limit=20) self.bittrex_latestinfo = self.bittrex.fetch_tickers() self.poloniex_latestinfo = self.poloniex.fetch_tickers() self.quadrigacx_latestinfo = self.quadraigacx.fetch_tickers() self.evaluateAlert(self.alerts) def removeAlert(self, args, responseUrl, username): for i, alert in enumerate(self.alerts): if alert['timestamp'] == args[0]: self.alerts.pop(i) pickle.dump(self.alerts, open("alert.p", "wb")) print("alerts list") print(*self.alerts) if responseUrl != "empty": return tasks.sendTextResponse( responseUrl, "alert " + alert['timestamp'] + " removed", "ephemeral") return "alert " + alert['timestamp'] + " removed" if responseUrl != "empty": return tasks.sendTextResponse(responseUrl, "alert not found", "ephemeral") return "alert not found" def evaluateAlert(self, alerts): if not alerts: return else: for i, alert in enumerate(alerts): alertType = alert["type"] if alert['market'] == 'coinmarketcap': currentPrice = float( self.coinmarketcap.ticker(alert['symbol'], convert='USD')['price_usd']) if alertType == 'low': if (currentPrice <= float(alert['setPrice'])): data = tasks.createAlertResponse( alert, currentPrice, "Price reached", self.webhook) requests.post(self.webhook, json=data) alerts.pop(i) elif alertType == 'high': if (currentPrice >= float(alert['setPrice'])): data = tasks.createAlertResponse( alert, currentPrice, "Price reached", self.webhook) requests.post(self.webhook, json=data) alerts.pop(i) elif alert['market'] == 'bittrex': tasks.sendAlert.delay(alerts, alert, self.webhook, self.bittrex_latestinfo) elif alert['market'] == 'quadrigacx': tasks.sendAlert.delay(alerts, alert, self.webhook, self.quadrigacx_latestinfo) elif alert['market'] == 'poloniex': tasks.sendAlert.delay(alerts, alert, self.webhook, self.poloniex_latestinfo)
#!/usr/bin/env python3 # -*- coding: utf-8 -*- from pymarketcap import Pymarketcap pym = Pymarketcap() currencies = pym.ranks() def assert_any_field_in_response(field, readable_field): found = False for rank in ["gainers", "losers"]: for period in ["1h", "24h", "7d"]: for curr in currencies[rank][period]: value = curr[field] if isinstance(value, (int, float)): found = True elif isinstance(value, str): if value != "": found = True elif value is None: continue else: msg = "Case not anticipated. 'type(value) == %r'" raise NotImplementedError(msg % type(value)) try: assert found == True except AssertionError as err: print("Any %s found searching all currencies." % readable_field \ + "Check 'processer:ranks()' function.")
class TestScraperCoinmarketcap(unittest.TestCase): """ Tests for Coinmarketcap Api commands. These will fail in the absence of an internet connection or if coinmarketcap.com goes down. """ def __init__(self, *args, **kwargs): super(TestScraperCoinmarketcap, self).__init__(*args, **kwargs) self.coinmarketcap = Pymarketcap() self.config = ConfigTest() def tearDown(self): # Prevent to many requests error in tests time.sleep(.25) def test_markets(self): actual = self.coinmarketcap.markets(self.config.COIN) value_types = { 'price_usd': Decimal, '24h_volume_usd': int, 'percent_volume': Decimal, 'pair': str, 'exchange': str, 'updated': bool } self.assertIs(type(actual), list) self.assertIs(len(actual) > 0, True) for source in actual: self.assertIs(type(source), dict) for key, value in source.items(): self.assertIs(type(value), value_types[key]) def test_ranks(self): temps = ['1h', '24h', '7d'] queries = ['gainers', 'losers'] value_types = { 'percent_change': Decimal, '24h_volume_usd': int, 'symbol': str, 'price_usd': Decimal, 'name': str } actual = self.coinmarketcap.ranks() self.assertIs(type(actual), dict) for q, temp in actual.items(): self.assertIn(q, queries) self.assertIs(type(temp), dict) for t, data in temp.items(): self.assertIn(t, temps) self.assertIs(type(data), list) self.assertIs(len(data) > 0, True) for d in data: self.assertIs(type(d), dict) for key, value in d.items(): self.assertIs(type(value), value_types[key]) # Test invalid argument with self.assertRaises(AttributeError): self.coinmarketcap.ranks('8d') def test_historical(self): from datetime import datetime value_types = { 'close': Decimal, 'low': Decimal, 'usd_volume': int, 'open': Decimal, 'usd_market_cap': int, 'high': Decimal, 'date': datetime } actual = self.coinmarketcap.historical(self.config.COIN, datetime(2017, 9, 30), datetime(2017, 10, 10)) self.assertIs(type(actual), list) for tick in actual: self.assertIs(type(tick), dict) for key, value in tick.items(): self.assertIs(type(value), value_types[key]) def test_recently(self): actual = self.coinmarketcap.recently() value_types = { 'price_usd': [Decimal, str], 'mineable': bool, 'symbol': str, 'usd_market_cap': [str, int], 'circulating_supply': [str, int], 'volume_24h_usd': [str, int], 'days_ago': [str, int], 'name': str, } self.assertIs(type(actual), list) for c in actual: self.assertIs(type(c), dict) for key, value in c.items(): if type(value_types[key]) is list: self.assertIn(type(value), value_types[key]) else: self.assertIs(type(value), value_types[key]) def test_exchange(self): actual = self.coinmarketcap.exchange(self.config.EXCHANGE) value_types = { 'market': str, 'price_usd': Decimal, 'rank': int, 'volume_24h_usd': int, 'name': str, 'perc_volume': Decimal } self.assertIs(type(actual), list) for market in actual: self.assertIs(type(market), dict) for key, value in market.items(): self.assertIs(type(value), value_types[key]) def test_exchanges(self): actual = self.coinmarketcap.exchanges() value_types = { 'market': str, 'price_usd': Decimal, 'rank': int, 'volume_24h_usd': int, 'name': str, 'perc_volume': Decimal, 'perc_change': Decimal } self.assertIs(type(actual), list) for exch in actual: self.assertIs(type(exch), dict) for key, value in exch.items(): if key in ('rank', 'volume_usd'): self.assertIs(type(value), int) elif key == 'name': self.assertIs(type(value), str) elif key == 'markets': self.assertIs(type(value), list) for m in value: self.assertIs(type(m), dict) for _key, _value in m.items(): self.assertIs(type(_value), value_types[_key]) def test_exchange_names(self): actual = self.coinmarketcap.exchange_names self.assertIs(type(actual), list) def _assert_graphs_data_structure(self, data): self.assertIs(type(data), dict) for key, value in data.items(): self.assertIs(type(value), list) for timestamp in value: self.assertIs(type(timestamp), list) for _value in timestamp: self.assertIn(type(_value), (float, int)) def test_graphs_currency(self): actual = self.coinmarketcap.graphs.currency(self.config.COIN) self._assert_graphs_data_structure(actual) def test_graphs_global_cap(self): actual = self.coinmarketcap.graphs.global_cap(bitcoin=True) self._assert_graphs_data_structure(actual) def test_graphs_dominance(self): actual = self.coinmarketcap.graphs.dominance() self._assert_graphs_data_structure(actual) def test_download_logo(self): filename = self.coinmarketcap.download_logo(self.config.COIN) self.assertNotEqual(filename, None) if filename: os.remove(filename)
class CryptoBacktester: def __init__(self, allowed_exchanges): self.allowed_exchanges = allowed_exchanges self.cmc = Pymarketcap() self.indicator_calc = IndicatorCalc() def filter_markets(self): ranks_filtered = { 'gainers': { '1h': [], '24h': [], '7d': [] }, 'losers': { '1h': [], '24h': [], '7d': [] } } failed_products = { 'gainers': { '1h': [], '24h': [], '7d': [] }, 'losers': { '1h': [], '24h': [], '7d': [] } } time_bins = ['1h', '24h', '7d'] ranks = self.cmc.ranks() gainers = ranks['gainers'] losers = ranks['losers'] for bin in time_bins: logger.debug('bin: ' + str(bin)) for mkt in gainers[bin]: logger.debug('[gainers] mkt: ' + str(mkt)) try: markets = self.cmc.markets(mkt['website_slug']) for exch in markets['markets']: if exch['source'].lower() in self.allowed_exchanges: ranks_filtered['gainers'][bin].append((mkt, exch)) except Exception as e: logger.exception(e) failed_products['gainers'][bin].append(mkt) for mkt in losers[bin]: logger.debug('[losers] mkt: ' + str(mkt)) try: markets = self.cmc.markets(mkt['website_slug']) for exch in markets['markets']: if exch['source'].lower() in self.allowed_exchanges: ranks_filtered['losers'][bin].append((mkt, exch)) except Exception as e: logger.exception(e) failed_products['losers'][bin].append(mkt) return ranks_filtered, failed_products def get_best_pairs(self, ranked_products): best_pairs = {'success': True, 'result': {}} #conversion_currencies = ['BTC', 'ETH', 'USD'] try: for rank_type in ranked_products: logger.debug('rank_type: ' + rank_type) best_pairs['result'][rank_type] = {} for time_bin in ranked_products[rank_type]: logger.debug('time_bin: ' + time_bin) best_pairs['result'][rank_type][time_bin] = {} for product in ranked_products[rank_type][time_bin]: logger.debug('product: ' + str(product)) if product[0]['symbol'] not in best_pairs['result'][ rank_type][time_bin]: best_pairs['result'][rank_type][time_bin][ product[0]['symbol']] = {} if product[1]['pair'].split('/')[1] not in best_pairs[ 'result'][rank_type][time_bin][product[0] ['symbol']]: best_pairs['result'][rank_type][time_bin][ product[0]['symbol']][product[1]['pair'].split( '/')[1]] = self.cmc.ticker( currency=product[0]['website_slug'], convert=product[1]['pair'].split('/') [1])['data']['quotes'][ product[1]['pair'].split('/')[1]] time.sleep(2) except Exception as e: logger.exception('Exception raised in get_best_pairs().') logger.exception(e) best_pairs['success'] = False finally: return best_pairs def get_candles(self, exchange, market, interval=0): candles = {'success': True, 'result': {}} #try: logger.debug('exchange: ' + exchange) logger.debug('market: ' + market) logger.debug('interval: ' + interval) valid_intervals = [ 60, 180, 300, 900, 1800, 3600, 7200, 14400, 21600, 43200, 86400, 259200, 604800 ] endpoint = '/markets/' + exchange.lower() + '/' + market.lower( ) + '/ohlc' url = 'https://api.cryptowat.ch' + endpoint url_params = {} if interval == 0: pass else: candle_url_param = str(int(round(float(interval), 0))) url_params['periods'] = candle_url_param if interval not in valid_intervals: logger.error( 'Invalid interval passed to get_candles(). Exiting.') sys.exit(1) try: r = requests.get(url, params=url_params) time.sleep(request_delay) results = r.json() if 'result' not in results or 'allowance' not in results: logger.debug( '[get_candles()] Failed to acquire valid candles.') candles['success'] = False if 'error' in results: logger.error('Error while calling Cryptowat.ch API.') logger.error(results['error']) if results['error'] == 'Out of allowance': allowance_remaining = 0 else: allowance_remaining = results['allowance']['remaining'] allowance_cost = results['allowance']['cost'] #allowance_avg_cost = average_api_cost(allowance_cost) if candles['success'] == True: for time_bin in results['result']: data = results['result'][time_bin] np_historical = np.array(data, dtype='f8') candles[time_bin] = {} candles[time_bin]['close_time'] = np_historical[:, 0] candles[time_bin]['open'] = np_historical[:, 1] candles[time_bin]['high'] = np_historical[:, 2] candles[time_bin]['low'] = np_historical[:, 3] candles[time_bin]['close'] = np_historical[:, 4] candles[time_bin]['volume'] = np_historical[:, 5] except requests.exceptions.RequestException as e: logger.exception('RequestException while retrieving candles.') logger.exception(e) #candle_data['RequestException'] = True candles['success'] = False except requests.exceptions.ConnectionError as e: logger.error('ConnectionError while retrieving candles.') logger.error(e) #candle_data['Error'] = True candles['success'] = False except json.JSONDecodeError as e: logger.error('JSONDecodeError while retrieving candles.') logger.error(e) #candle_data['Error'] = True candles['success'] = False except Exception as e: logger.exception('Uncaught exception while retrieving candles.') logger.exception(e) #candle_data['Exception'] = True candles['success'] = False finally: return candles