Esempio n. 1
0
    def test_010_getNotExists(self):
        cache = Cache()

        with self.assertRaises(Exception) as context:
            cache.get("Bank1", "key1")

        self.assertTrue("Key key1 doesn't exist" in str(context.exception))
Esempio n. 2
0
    def test_070_timeout(self):
        cache = Cache()
        cache.put("Bank1", CacheItem("key1", "val1", 2))
        time.sleep(1)
        cache.touch("Bank1", "key1")
        time.sleep(1)
        self.assertIsNotNone(cache.get("Bank1", "key1"))
        time.sleep(1)

        with self.assertRaises(Exception) as context:
            cache.get("Bank1", "key1")

        self.assertTrue("Key key1 doesn't exist" in str(context.exception))
Esempio n. 3
0
    def test_cache_delete_least_popular_elements(self):
        cache = Cache(3, 1)
        cache.put('key1', 'value1')
        cache.put('key2', 'value2')
        cache.put('key3', 'value3')

        hits_plan = {'key1': 2, 'key2': 6, 'key3': 3}

        for key, value in hits_plan.items():
            for _ in range(value):
                cache.get(key)

        cache.put('key4', 'value4')

        self.assertFalse(cache.is_key('key1'))
        self.assertTrue(cache.is_key('key4'))
Esempio n. 4
0
    def test_080_incr(self):
        cache = Cache()
        cache.put("BankNum", CacheItem("key1", 1))
        self.assertEqual(cache.get("BankNum", "key1").value, 1)
        self.assertEqual(cache.incr("BankNum", "key1", 1).value, 2)
        self.assertEqual(cache.incr("BankNum", "key1", -3).value, -1)

        cache.put("BankNum", CacheItem("key2", "1"))
        self.assertEqual(cache.incr("BankNum", "key2", 1).value, 2)

        cache.put("BankNum", CacheItem("key3", "1a"))
        with self.assertRaises(Exception) as context:
            cache.incr("BankNum", "key3", 1)

        cache.put("BankNum", CacheItem("key4", 1.3))
        self.assertEqual(cache.get("BankNum", "key4").value, 1.3)
        self.assertEqual(cache.incr("BankNum", "key4", 1.2).value, 2.5)
Esempio n. 5
0
    def test_cache_store_elements_hits(self):
        cache = Cache(5, 1)
        cache.put('key1', 'value1')
        cache.put('key2', 'value2')
        cache.put('key3', 'value3')
        cache.put('key4', 'value4')
        cache.put('key5', 'value5')

        hits_plan = {'key1': 5, 'key2': 4, 'key3': 3, 'key4': 2, 'key5': 1}

        for key, value in hits_plan.items():
            for _ in range(value):
                cache.get(key)

        self.assertEqual(5, cache.hits[3])
        self.assertEqual(4, cache.hits[4])
        self.assertEqual(3, cache.hits[0])
        self.assertEqual(2, cache.hits[1])
        self.assertEqual(1, cache.hits[2])
Esempio n. 6
0
class Manager(object):
    def __init__(self, basedir=None):
        self.cache = Cache(basedir)
        self.auto_tagger = AutoTagger(self.get('auto-tags'))

    def __backup_thread(self):
        while True:
            self.cache.dump()
            time.sleep(self.get('settings')['backup']['interval'])

    def __sync_thread(self):
        while True:
            try:
                resources_tags = self.auto_tagger.process(
                    Crawler(
                        self.get('black-list'),
                        self.get('white-list'),
                        self.get('crawled-resources'),
                    ).crawl())

                SyncAgent(
                    self.get('settings')['server'],
                    self.get('settings')['user-token'],
                    self.get('settings')['device-token'],
                ).sync(resources_tags)

            except Exception as new_exception:
                print('[ERROR]: When trying to sync: {0}'.format(
                    new_exception.message))

            else:
                self.get('crawled-resources').update(
                    set(resource for resource, _ in resources_tags))

            time.sleep(self.get('settings')['sync']['interval'])

    def add_to_black_list(self, directory):
        return Crawler.add_to_dirlist(self.get('black-list'), directory)

    def add_to_white_list(self, directory):
        return Crawler.add_to_dirlist(self.get('white-list'), directory)

    def get(self, key):
        return self.cache.get(key)

    def start_backup_daemon(self):
        DaemonThread(target=self.__backup_thread).start()

    def start_sync_daemon(self):
        DaemonThread(target=self.__sync_thread).start()
Esempio n. 7
0
class Manager(object):
    def __init__(self, basedir=None):
        self.cache = Cache(basedir)
        self.auto_tagger = AutoTagger(self.get('auto-tags'))

    def __backup_thread(self):
        while True:
            self.cache.dump()
            time.sleep(self.get('settings')['backup']['interval'])

    def __sync_thread(self):
        while True:
            try:
                resources_tags = self.auto_tagger.process(
                    Crawler(
                        self.get('black-list'),
                        self.get('white-list'),
                        self.get('crawled-resources'),
                    ).crawl()
                )

                SyncAgent(
                    self.get('settings')['server'],
                    self.get('settings')['user-token'],
                    self.get('settings')['device-token'],
                ).sync(resources_tags)

            except Exception as new_exception:
                print('[ERROR]: When trying to sync: {0}'.format(new_exception.message))

            else:
                self.get('crawled-resources').update(set(resource for resource, _ in resources_tags))

            time.sleep(self.get('settings')['sync']['interval'])

    def add_to_black_list(self, directory):
        return Crawler.add_to_dirlist(self.get('black-list'), directory)

    def add_to_white_list(self, directory):
        return Crawler.add_to_dirlist(self.get('white-list'), directory)

    def get(self, key):
        return self.cache.get(key)

    def start_backup_daemon(self):
        DaemonThread(target=self.__backup_thread).start()

    def start_sync_daemon(self):
        DaemonThread(target=self.__sync_thread).start()
Esempio n. 8
0
class CacheTest(unittest.TestCase):

    #===============Initial==========================
    def setUp(self):
        self.c = Cache(5, 1)
        keys = list(map(lambda x: chr(x), range(97, 102)))
        values = list(range(2, 7))
        for i in range(5):
            self.c.put(keys[i], values[i])

    def tearDown(self):
        del self.c

    #==============Testing===================
    def test_request_increment(self):
        id = self.c.find("a", self.c.keys)
        before = self.c.hits[id]
        self.c.get("a")
        after = self.c.hits[id]
        self.assertTrue(after == before + 1)

    def test_put(self):
        x = Cache(5, 1)
        x.put("key", "value")
        self.assertTrue(x.get("key") == "value")

    def test_remove(self):
        self.c.get("a")
        self.c.get("b")
        self.c.get("c")
        self.c.get("e")
        self.c.remove()
        self.assertEqual(self.c.get("d"), None)

    def test_put_in_full(self):
        self.c.get("a")
        self.c.get("b")
        self.c.get("c")
        self.c.get("e")
        self.assertTrue(self.c.get("key") == None)
        self.c.put("key", "value")
        self.assertFalse(self.c.get("key") == None)
        self.assertTrue(self.c.get("d") == None)
Esempio n. 9
0
 def test_put(self):
     x = Cache(5, 1)
     x.put("key", "value")
     self.assertTrue(x.get("key") == "value")
from Cache import Cache
import sys

parameters = sys.argv[1].split(':')
if((parameters[0] == '') and (parameters[1] == '') and (parameters[0] == '')):
	cache = Cache()
else:	
	cache = Cache(nsets=int(parameters[0]),bsize=int(parameters[1]),assoc=int(parameters[2]))
file = open(sys.argv[2], 'r')
in_address = file.readlines()
i = 0
for address in in_address:
	cache.get(address)
	i += 1
	if i % (len(in_address)/4) == 0:
		print('Processing...')
cache.statistic()
file.close()
Esempio n. 11
0
class DetailProcessor(object):
    coinGecko = CoinGeckoAPI()

    def __init__(self):
        self.isServiceAvailable = True
        signal.signal(signal.SIGINT, self.exit_gracefully)
        signal.signal(signal.SIGTERM, self.exit_gracefully)

        self.logging = error_reporting.Client()
        self.cache = Cache(ttl=60)

        context = zmq.Context.instance()
        self.socket = context.socket(zmq.ROUTER)
        self.socket.bind("tcp://*:6900")

        print("[Startup]: Detail Server is online")

    def exit_gracefully(self):
        print("[Startup]: Detail Server is exiting")
        self.socket.close()
        self.isServiceAvailable = False

    def run(self):
        while self.isServiceAvailable:
            try:
                response = None, None
                origin, delimeter, clientId, service, request = self.socket.recv_multipart(
                )
                request = pickle.loads(zlib.decompress(request))
                if request.timestamp + 30 < time.time(): continue

                if service == b"detail":
                    response = self.request_detail(request)

            except (KeyboardInterrupt, SystemExit):
                return
            except Exception:
                print(traceback.format_exc())
                if os.environ["PRODUCTION_MODE"]:
                    self.logging.report_exception()
            finally:
                try:
                    self.socket.send_multipart([
                        origin, delimeter,
                        zlib.compress(pickle.dumps(response, -1))
                    ])
                except:
                    pass

    def request_detail(self, request):
        payload, tradeMessage, updatedTradeMessage = None, None, None

        for platform in request.platforms:
            request.set_current(platform=platform)
            hashCode = hash(request.requests[platform])
            fromCache = False

            if request.can_cache() and self.cache.has(hashCode):
                payload, updatedQuoteMessage = self.cache.get(hashCode), None
                fromCache = True
            elif platform == "CoinGecko":
                payload, updatedQuoteMessage = self.request_coingecko_details(
                    request)
            elif platform == "IEXC":
                payload, updatedQuoteMessage = self.request_iexc_details(
                    request)

            if payload is not None:
                if request.can_cache() and not fromCache:
                    self.cache.set(hashCode, payload)
                if request.authorId != 401328409499664394 and request.requests[
                        platform].ticker.base is not None:
                    database.document("dataserver/statistics/{}/{}".format(
                        platform, str(uuid.uuid4()))).set({
                            "timestamp":
                            time.time(),
                            "authorId":
                            str(request.authorId),
                            "ticker": {
                                "base": request.requests[platform].ticker.base,
                                "quote":
                                request.requests[platform].ticker.quote,
                                "id": request.requests[platform].ticker.id,
                                "bias": request.parserBias
                            },
                            "exchange":
                            None if request.requests[platform].exchange is None
                            else request.requests[platform].exchange.id
                        })
                return payload, updatedTradeMessage
            elif updatedTradeMessage is not None:
                tradeMessage = updatedTradeMessage

        return None, tradeMessage

    def request_coingecko_details(self, request):
        ticker = request.get_ticker()

        try:
            try:
                companyData = self.coinGecko.get_coin_by_id(
                    id=ticker.symbol,
                    localization="false",
                    tickers=False,
                    market_data=True,
                    community_data=True,
                    developer_data=True)
            except:
                return None, None

            description = md(companyData["description"].get(
                "en", "No description"))
            descriptionParagraphs = description.split("\r\n\r\n")
            textLength = [len(descriptionParagraphs[0])]
            for i in range(1, len(descriptionParagraphs)):
                nextLength = textLength[-1] + len(descriptionParagraphs[i])
                if nextLength > 1000: break
                textLength.append(nextLength)
            description = "\n".join(
                descriptionParagraphs[:len(textLength)]
            ) + "\n[Read more on CoinGecko](https://www.coingecko.com/coins/{})".format(
                ticker.symbol)

            payload = {
                "name":
                "{} ({})".format(companyData["name"], ticker.base),
                "description":
                description,
                "url":
                None if companyData["links"]["homepage"][0] == "" else
                companyData["links"]["homepage"][0],
                "rank":
                companyData["market_data"]["market_cap_rank"],
                "image":
                companyData["image"]["large"],
                "marketcap":
                None if companyData["market_data"]["market_cap"] is None else
                companyData["market_data"]["market_cap"].get("usd"),
                "volume":
                None if companyData["market_data"]["total_volume"] is None else
                companyData["market_data"]["total_volume"].get("usd"),
                "industry":
                None,
                "info":
                None,
                "supply": {
                    "total":
                    None if companyData["market_data"]["total_supply"] is None
                    else companyData["market_data"]["total_supply"],
                    "circulating":
                    None
                    if companyData["market_data"]["circulating_supply"] is None
                    else companyData["market_data"]["circulating_supply"]
                },
                "score": {
                    "developer": companyData["developer_score"],
                    "community": companyData["community_score"],
                    "liquidity": companyData["liquidity_score"],
                    "public interest": companyData["public_interest_score"]
                },
                "price": {
                    "current":
                    companyData["market_data"]["current_price"].get("usd"),
                    "ath":
                    companyData["market_data"]["ath"].get("usd"),
                    "atl":
                    companyData["market_data"]["atl"].get("usd"),
                    "per":
                    None
                },
                "change": {
                    "past day":
                    companyData["market_data"]
                    ["price_change_percentage_24h_in_currency"].get("usd"),
                    "past month":
                    companyData["market_data"]
                    ["price_change_percentage_30d_in_currency"].get("usd"),
                    "past year":
                    companyData["market_data"]
                    ["price_change_percentage_1y_in_currency"].get("usd")
                },
                "sourceText":
                "from CoinGecko",
                "platform":
                "CoinGecko",
            }
            return payload, None
        except Exception:
            print(traceback.format_exc())
            if os.environ["PRODUCTION_MODE"]: self.logging.report_exception()
            return None, None

    def request_iexc_details(self, request):
        ticker = request.get_ticker()

        try:
            try:
                stock = Stock(ticker.id, token=os.environ["IEXC_KEY"])
                companyData = stock.get_company().loc[ticker.id]
                rawData = stock.get_quote().loc[ticker.id]
            except:
                return None, None

            try:
                coinThumbnail = stock.get_logo().loc[ticker.id]["url"]
            except:
                coinThumbnail = None

            payload = {
                "name":
                "{} ({})".format(companyData["companyName"],
                                 companyData["symbol"]),
                "description":
                companyData["description"],
                "url":
                companyData["website"],
                "rank":
                None,
                "image":
                coinThumbnail,
                "marketcap":
                rawData["marketCap"],
                "volume":
                None,
                "industry":
                companyData["industry"],
                "info": {
                    "location":
                    "{}{}, {}, {}, {}".format(
                        companyData["address"],
                        "" if companyData["address2"] is None else ", " +
                        companyData["address2"], companyData["city"],
                        companyData["state"], companyData["country"]),
                    "employees":
                    companyData["employees"]
                },
                "supply":
                None,
                "score":
                None,
                "price": {
                    "current":
                    rawData["delayedPrice"] if rawData["latestPrice"] is None
                    else rawData["latestPrice"],
                    "ath":
                    None,
                    "atl":
                    None,
                    "per":
                    rawData["peRatio"]
                },
                "change": {
                    "past day": rawData["changePercent"],
                    "past month": None,
                    "past year": None
                },
                "sourceText":
                "provided by IEX Cloud",
                "platform":
                "IEX Cloud",
            }
            return payload, None
        except Exception:
            print(traceback.format_exc())
            if os.environ["PRODUCTION_MODE"]: self.logging.report_exception()
            return None, None
Esempio n. 12
0
class ChainDb(object):
	def __init__(self, settings, datadir, log, mempool, netmagic,
		     readonly=False, fast_dbm=False):
		self.settings = settings
		self.log = log
		self.mempool = mempool
		self.readonly = readonly
		self.netmagic = netmagic
		self.fast_dbm = fast_dbm
		self.blk_cache = Cache(750)
		self.orphans = {}
		self.orphan_deps = {}
		if readonly:
			mode_str = 'r'
		else:
			mode_str = 'c'
			if fast_dbm:
				self.log.write("Opening database in fast mode")
				mode_str += 'f'
		self.misc = gdbm.open(datadir + '/misc.dat', mode_str)
		self.blocks = gdbm.open(datadir + '/blocks.dat', mode_str)
		self.height = gdbm.open(datadir + '/height.dat', mode_str)
		self.blkmeta = gdbm.open(datadir + '/blkmeta.dat', mode_str)
		self.tx = gdbm.open(datadir + '/tx.dat', mode_str)

		if 'height' not in self.misc:
			self.log.write("INITIALIZING EMPTY BLOCKCHAIN DATABASE")
			self.misc['height'] = str(-1)
			self.misc['msg_start'] = self.netmagic.msg_start
			self.misc['tophash'] = ser_uint256(0L)
			self.misc['total_work'] = hex(0L)

		if 'msg_start' not in self.misc or (self.misc['msg_start'] != self.netmagic.msg_start):
			self.log.write("Database magic number mismatch. Data corruption or incorrect network?")
			raise RuntimeError

	def dbsync(self):
		self.misc.sync()
		self.blocks.sync()
		self.height.sync()
		self.blkmeta.sync()
		self.tx.sync()

	def puttxidx(self, txhash, txidx):
		ser_txhash = ser_uint256(txhash)

		if ser_txhash in self.tx:
			old_txidx = self.gettxidx(txhash)
			self.log.write("WARNING: overwriting duplicate TX %064x, height %d, oldblk %064x, oldspent %x, newblk %064x" % (txhash, self.getheight(), old_txidx.blkhash, old_txidx.spentmask, txidx.blkhash))

		self.tx[ser_txhash] = (hex(txidx.blkhash) + ' ' +
				       hex(txidx.spentmask))

		return True

	def gettxidx(self, txhash):
		ser_txhash = ser_uint256(txhash)
		if ser_txhash not in self.tx:
			return None

		ser_value = self.tx[ser_txhash]
		pos = string.find(ser_value, ' ')

		txidx = TxIdx()
		txidx.blkhash = long(ser_value[:pos], 16)
		txidx.spentmask = long(ser_value[pos+1:], 16)

		return txidx

	def gettx(self, txhash):
		txidx = self.gettxidx(txhash)
		if txidx is None:
			return None

		block = self.getblock(txidx.blkhash)
		for tx in block.vtx:
			tx.calc_sha256()
			if tx.sha256 == txhash:
				return tx

		self.log.write("ERROR: Missing TX %064x in block %064x" % (txhash, txidx.blkhash))
		return None

	def haveblock(self, blkhash, checkorphans):
		if self.blk_cache.exists(blkhash):
			return True
		if checkorphans and blkhash in self.orphans:
			return True
		ser_hash = ser_uint256(blkhash)
		if ser_hash in self.blocks:
			return True
		return False

	def have_prevblock(self, block):
		if self.getheight() < 0 and block.sha256 == self.netmagic.block0:
			return True
		if self.haveblock(block.hashPrevBlock, False):
			return True
		return False

	def getblock(self, blkhash):
		block = self.blk_cache.get(blkhash)
		if block is not None:
			return block

		ser_hash = ser_uint256(blkhash)
		if ser_hash not in self.blocks:
			return None

		f = cStringIO.StringIO(self.blocks[ser_hash])
		block = CBlock()
		block.deserialize(f)

		self.blk_cache.put(blkhash, block)

		return block

	def spend_txout(self, txhash, n_idx):
		txidx = self.gettxidx(txhash)
		if txidx is None:
			return False

		txidx.spentmask |= (1L << n_idx)
		self.puttxidx(txhash, txidx)

		return True

	def clear_txout(self, txhash, n_idx):
		txidx = self.gettxidx(txhash)
		if txidx is None:
			return False

		txidx.spentmask &= ~(1L << n_idx)
		self.puttxidx(txhash, txidx)

		return True

	def unique_outpts(self, block):
		outpts = {}
		txmap = {}
		for tx in block.vtx:
			if tx.is_coinbase:
				continue
			txmap[tx.sha256] = tx
			for txin in tx.vin:
				v = (txin.prevout.hash, txin.prevout.n)
				if v in outs:
					return None

				outpts[v] = False

		return (outpts, txmap)

	def spent_outpts(self, block):
		# list of outpoints this block wants to spend
		l = self.unique_outpts(block)
		if l is None:
			return None
		outpts = l[0]
		txmap = l[1]
		spendlist = {}

		# pass 1: if outpoint in db, make sure it is unspent
		for k in outpts.iterkeys():
			txidx = self.gettxidx(k[0])
			if txidx is None:
				continue

			if k[1] > 100000:	# outpoint index sanity check
				return None

			if txidx.spentmask & (1L << k[1]):
				return None

			outpts[k] = True	# skip in pass 2

		# pass 2: remaining outpoints must exist in this block
		for k, v in outpts.iteritems():
			if v:
				continue

			if k[0] not in txmap:	# validate txout hash
				return None

			tx = txmap[k[0]]	# validate txout index (n)
			if k[1] >= len(tx.vout):
				return None

			# outpts[k] = True	# not strictly necessary

		return outpts.keys()
Esempio n. 13
0
 def test_000_basic(self):
     cache = Cache()
     cache.put("Bank1", CacheItem("key1", "val1"))
     cache.put("Bank2", CacheItem("key1", "val2"))
     self.assertEqual(cache.get("Bank1", "key1").value, 'val1')
     self.assertEqual(cache.get("Bank2", "key1").value, 'val2')
Esempio n. 14
0
def run(movie_dir, html_output_flag, limit):
    """This is the real entry point for the program"""

    #A class to help lookup movie titles
    movielookup = MovieLookup()

    #Match files in a given directory
    matcher = Matcher(Config.movie_match_regex, Config.allowed_file_types)

    #Used to find an imdb id from movie filename
    id_finder = IdFinder()

    #Used for caching movie data
    movie_cache = Cache(Config.movie_cache_file)

    #First, let's match files which match the regex and have the
    #required file extensions in the given directory
    matcher.find_in_directory(movie_dir)
    movie_matches = matcher.get_matches()
    unmatched = matcher.get_ignored()

    #normalise the matches (the filenames will be used as movie titles)
    normalised_movie_matches = Normaliser\
        .normalise_list_and_remove_trailing_number(movie_matches)

    #Now we lookup successful matches, first in the cache, then online
    movie_data = {}      #successful lookup data will go here
    failed_lookups = []  #we will do something with failed lookups later...

    count = 0   #used to limit the number of lookups we will do
    for title in normalised_movie_matches:
        count += 1
        if count >= limit:#check that we don't go over the arbitrary limit
            break

        #Check if the movie is in our cache
        cached_movie = movie_cache.get(title)
        if cached_movie:
            movie_data[title] = cached_movie
        #Otherwise, lookup using API
        else:
            #look up each movie in the list
            lookup_data = movielookup.lookup_by_title(title)

            #check if we found a movie
            if MovieDataUtil.is_valid_lookup_result(lookup_data):
                movie_data[title] = lookup_data
                #great, let's also add it to the cache
                movie_cache.add_to_cache(title, lookup_data)
            else:
                failed_lookups.append(title)

    #now we will try to correct the failed lookups
    #by using google to find each imdb id
    id_lookup_dict = id_finder.find_id_by_title_list(failed_lookups)

    #reset the failed lookups
    failed_lookups = []      #there should be a lot less now...
    title_corrections = 0    #count how many corrections we actually found

    #Now lookup using the new ids which we found
    for title, found_id in id_lookup_dict.items():
        if found_id != None:
            #we found an id, now let's look the movie up by its id
            lookup_data = movielookup.lookup_by_id(found_id)

            #theoretically this should always be true
            #unless we got an invalid id somehow...
            if MovieDataUtil.is_valid_lookup_result(lookup_data):
                movie_data[title] = lookup_data
                title_corrections += 1
                #great, let's also add it to the cache
                movie_cache.add_to_cache(title, lookup_data)
            else:
                failed_lookups.append(title)
        else:
            failed_lookups.append(title)

    #Save the updated cache
    movie_cache.save_cache_to_disk()

    #sort the data by imdb id
    movie_data = MovieDataUtil.sort_movie_data(movie_data)

    #Output the data
    if html_output_flag:
        logging.debug('Loading template from: %s', Config.template_directory)
        template_environment = Environment( \
                        loader=FileSystemLoader( \
                        Config.template_directory), trim_blocks=True)
        print template_environment.get_template('main.html').render(
            movie_lookup_data=movie_data,
            failed_lookups=failed_lookups,
            unmatched=unmatched,
            title_corrections=title_corrections,
            datetime=time.strftime("%c"),
            version=__version__,
            author=__author__,
            cache_stats=movie_cache.cache_stats(),
        )
    else:
        simple_output(movie_data, failed_lookups, unmatched)
Esempio n. 15
0
# and open the template in the editor.

__author__ = "ilausuch"
__date__ = "$13-jun-2017 20:33:29$"

import sys
import os
sys.path.append(os.path.abspath("../Core"))
sys.path.append(os.path.abspath("../Addons"))

from Timer import Timer
from Cache import Cache, CacheItem

if __name__ == "__main__":
    cache = Cache()

    count = 100000
    timer = Timer()

    for i in range(0, count):
        cache.put("Bank1", CacheItem(i, i))

    print("{0} puts in {1} seconds".format(count, timer.end()))

    timer = Timer()

    for i in range(0, count):
        cache.get("Bank1", i)

    print("{0} gets in {1} seconds".format(count, timer.end()))
Esempio n. 16
0
class ChainDb(object):
	def __init__(self, settings, datadir, log, mempool, netmagic,
		     readonly=False, fast_dbm=False):
		self.settings = settings
		self.log = log
		self.mempool = mempool
		self.readonly = readonly
		self.netmagic = netmagic
		self.fast_dbm = fast_dbm
		self.blk_cache = Cache(750)
		self.orphans = {}
		self.orphan_deps = {}

		# LevelDB to hold:
		#    tx:*      transaction outputs
		#    misc:*    state
		#    height:*  list of blocks at height h
		#    blkmeta:* block metadata
		#    blocks:*  block seek point in stream
		self.blk_write = io.BufferedWriter(io.FileIO(datadir + '/blocks.dat','ab'))
		self.blk_read = io.BufferedReader(io.FileIO(datadir + '/blocks.dat','rb'))
		self.db = leveldb.LevelDB(datadir + '/leveldb')

		try:
			self.db.Get('misc:height')
		except KeyError:
			self.log.write("INITIALIZING EMPTY BLOCKCHAIN DATABASE")
			batch = leveldb.WriteBatch()
			batch.Put('misc:height', str(-1))
			batch.Put('misc:msg_start', self.netmagic.msg_start)
			batch.Put('misc:tophash', ser_uint256(0L))
			batch.Put('misc:total_work', hex(0L))
			self.db.Write(batch)

		try:
			start = self.db.Get('misc:msg_start')
			if start != self.netmagic.msg_start: raise KeyError
		except KeyError:
			self.log.write("Database magic number mismatch. Data corruption or incorrect network?")
			raise RuntimeError

	def puttxidx(self, txhash, txidx, batch=None):
		ser_txhash = ser_uint256(txhash)


		try:
			self.db.Get('tx:'+ser_txhash)
			old_txidx = self.gettxidx(txhash)
			self.log.write("WARNING: overwriting duplicate TX %064x, height %d, oldblk %064x, oldspent %x, newblk %064x" % (txhash, self.getheight(), old_txidx.blkhash, old_txidx.spentmask, txidx.blkhash))
		except KeyError:
			pass
		batch = self.db if batch is not None else batch
		batch.Put('tx:'+ser_txhash, hex(txidx.blkhash) + ' ' +
					       hex(txidx.spentmask))

		return True

	def gettxidx(self, txhash):
		ser_txhash = ser_uint256(txhash)
		try:
			ser_value = self.db.Get('tx:'+ser_txhash)
		except KeyError:
			return None

		pos = string.find(ser_value, ' ')

		txidx = TxIdx()
		txidx.blkhash = long(ser_value[:pos], 16)
		txidx.spentmask = long(ser_value[pos+1:], 16)

		return txidx

	def gettx(self, txhash):
		txidx = self.gettxidx(txhash)
		if txidx is None:
			return None

		block = self.getblock(txidx.blkhash)
		for tx in block.vtx:
			tx.calc_sha256()
			if tx.sha256 == txhash:
				return tx

		self.log.write("ERROR: Missing TX %064x in block %064x" % (txhash, txidx.blkhash))
		return None

	def haveblock(self, blkhash, checkorphans):
		if self.blk_cache.exists(blkhash):
			return True
		if checkorphans and blkhash in self.orphans:
			return True
		ser_hash = ser_uint256(blkhash)
		try: 
			self.db.Get('blocks:'+ser_hash)
			return True
		except KeyError:
			return False

	def have_prevblock(self, block):
		if self.getheight() < 0 and block.sha256 == self.netmagic.block0:
			return True
		if self.haveblock(block.hashPrevBlock, False):
			return True
		return False

	def getblock(self, blkhash):
		block = self.blk_cache.get(blkhash)
		if block is not None:
			return block

		ser_hash = ser_uint256(blkhash)
		try:
			# Lookup the block index, seek in the file
			fpos = long(self.db.Get('blocks:'+ser_hash))
			self.blk_read.seek(fpos)

			# read and decode "block" msg
			msg = message_read(self.netmagic, self.blk_read)
			if msg is None:
				return None
			block = msg.block
		except KeyError:
			return None

		self.blk_cache.put(blkhash, block)

		return block

	def spend_txout(self, txhash, n_idx, batch=None):
		txidx = self.gettxidx(txhash)
		if txidx is None:
			return False

		txidx.spentmask |= (1L << n_idx)
		self.puttxidx(txhash, txidx, batch)

		return True

	def clear_txout(self, txhash, n_idx, batch=None):
		txidx = self.gettxidx(txhash)
		if txidx is None:
			return False

		txidx.spentmask &= ~(1L << n_idx)
		self.puttxidx(txhash, txidx, batch)

		return True

	def unique_outpts(self, block):
		outpts = {}
		txmap = {}
		for tx in block.vtx:
			if tx.is_coinbase:
				continue
			txmap[tx.sha256] = tx
			for txin in tx.vin:
				v = (txin.prevout.hash, txin.prevout.n)
				if v in outs:
					return None

				outpts[v] = False

		return (outpts, txmap)

	def spent_outpts(self, block):
		# list of outpoints this block wants to spend
		l = self.unique_outpts(block)
		if l is None:
			return None
		outpts = l[0]
		txmap = l[1]
		spendlist = {}

		# pass 1: if outpoint in db, make sure it is unspent
		for k in outpts.iterkeys():
			txidx = self.gettxidx(k[0])
			if txidx is None:
				continue

			if k[1] > 100000:	# outpoint index sanity check
				return None

			if txidx.spentmask & (1L << k[1]):
				return None

			outpts[k] = True	# skip in pass 2

		# pass 2: remaining outpoints must exist in this block
		for k, v in outpts.iteritems():
			if v:
				continue

			if k[0] not in txmap:	# validate txout hash
				return None

			tx = txmap[k[0]]	# validate txout index (n)
			if k[1] >= len(tx.vout):
				return None

			# outpts[k] = True	# not strictly necessary

		return outpts.keys()
Esempio n. 17
0
class Core(object):
    """ Starts the :class:`Cache`

    Arguments
    -----------
    db : :data:`bfly.Butterfly._db_type`
        A fully-loaded database

    Attributes
    ------------
    _db: :data:`bfly.Butterfly._db_type`
        Taken from first argument ``db``
    _cache: :class:`Cache`
        Able to store images and metadata using \
:class:`UtilityLayer.RUNTIME` instance \
from ``db`` argument
    """
    def __init__(self, db):
        # Get DB Terms
        self._db = db
        RUNTIME = db.RUNTIME

        # Make Cache with keywords
        self._cache = Cache(RUNTIME)

    #####
    # All methods to load data
    #     1. get_info answers an InfoQuery i_query.
    #     2. get_data answers a DataQuery d_query.
    #
    # Both get_info or get_data call update_query.
    #
    # To give answers , update_query uses _cache or:
    #     1. make_data_query if only i_query given.
    #     2. make_tile_query with new or given d_query.
    #####

    @staticmethod
    def get_groups(i_query):
        """ dumps group list for ``i_query`` as a string

        Arguments
        ----------
        i_query: :class:`QueryLayer.InfoQuery`
            A request for a list of groups

        Returns
        --------
        str
            A list of all groups for the ``i_query``
        """
        return i_query.dump

    def get_edits(self, i_query, msg={}):
        """ dumps websocket updates to ``i_query`` as a string

        Calls :meth:`update_query` with more information\
from the cache or from the properties of a tile.

        Arguments
        ----------
        i_query: :class:`QueryLayer.InfoQuery`
            A request for information

        Returns
        --------
        str
            Wesocket info for :class:`QueryLayer.InfoQuery`
        """
        keywords = self.update_query(i_query)
        # Update current query with preloaded terms
        i_query.update_source(keywords)
        # Execute weboscket command if needed
        changed = i_query.websocket_edit(msg)
        # Add to cache and query
        if len(changed):
            keywords.update(changed)
            i_query.update_source(keywords)
            self._cache.set(i_query.key, keywords)

        # Return the i_query info
        return i_query.dump

    def get_info(self, i_query):
        """ dumps answer to ``i_query`` as a string

        Calls :meth:`update_query` with more information\
from the cache or from the properties of a tile.

        Arguments
        ----------
        i_query: :class:`QueryLayer.InfoQuery`
            A request for information

        Returns
        --------
        str
            Channel info for :class:`QueryLayer.InfoQuery`
        """
        keywords = self.update_query(i_query)
        # Update current query with preloaded terms
        i_query.update_source(keywords)
        # Return the i_query info
        return i_query.dump

    def get_dataset(self, i_query):
        """ dumps dataset from ``i_query`` as a string

        Calls :meth:`update_query` with more information\
from the cache or from the properties of a tile.

        Arguments
        ----------
        i_query: :class:`QueryLayer.InfoQuery`
            A request for information

        Returns
        --------
        str
            Dataset info for :class:`QueryLayer.InfoQuery`
        """
        all_channels = []
        # Update all the channels in the dataset
        for channel in i_query.channels:
            # Set the i_query to a given channel
            i_query.set_channel(channel)
            # Update the info for the channel
            keywords = self.update_query(i_query)
            # Add additionl keywords if needed
            channel_key = i_query.OUTPUT.INFO.CHANNEL.NAME
            keywords[channel_key] = channel[channel_key]
            # Add to list of channels
            all_channels.append(keywords)
        # Clear the channel data
        i_query.set_channel({})
        # Update current query with preloaded terms
        return i_query.dump_dataset(all_channels)

    def get_data(self, d_query):
        """ dumps answer to ``d_query`` as a string

        Calls :meth:`update_query` with more information\
from the cache or from the properties of a tile. \
Also calls :meth:`find_tiles` to get the complete\
image needed to answer the ``d_query``.

        Arguments
        ----------
        i_query: :class:`QueryLayer.InfoQuery`
            A request for information

        Returns
        --------
        str
            Answer for the :class:`QueryLayer.InfoQuery`
        """

        keywords = self.update_query(d_query)
        # Update current query with preloaded terms
        d_query.update_source(keywords)
        # Get the image for the d_query
        image = self.find_tiles(d_query)
        return self.write_image(d_query, image)

    @staticmethod
    def make_data_query(i_query):
        """ Make a data query from an info query

        Arguments
        ----------
        i_query: :class:`InfoQuery`
            only needs ``PATH`` set in :data:`OUTPUT.INFO`

        Returns
        --------
        :class:`DataQuery`
            takes only the `PATH` from ``i_query``

        """
        # Begin building needed keywords
        i_path = i_query.OUTPUT.INFO.PATH

        return DataQuery(**{
            i_query.INPUT.METHODS.NAME: 'data',
            i_path.NAME: i_path.VALUE
        })

    @staticmethod
    def make_tile_query(d_query, t_index=np.uint32([0, 0, 0])):
        """ Make a :class:`TileQuery` from :class:`DataQuery`

        Arguments
        ----------
        d_query: :class:`DataQuery`
            only needs ``PATH`` set in :data:`OUTPUT.INFO`
        t_index: numpy.ndarray
            The 3x1 count of tiles form the origin

        Returns
        --------
        :class:`TileQuery`
            One tile request in the given data request
        """
        tile_crop = d_query.all_in_some(t_index)
        return TileQuery(d_query, t_index, tile_crop)

    def update_query(self, query):
        """ Finds missing query details from cache or tile

        Makes ``keywords`` from either the :data:`_cache` or \
from a new :class:`TileQuery` to update the given ``query``

        Arguments
        ----------
        query: :class:`Query`
            Either an :class:`InfoQuery` or a :class:`DataQuery`

        Returns
        --------
        keywords: dict
            Can pass to :meth:`Query.update_source` or combine \
to pass to :meth:`Query.update_dataset`.
        """
        keywords = self._cache.get(query.key)
        if not len(keywords):
            d_query = query
            # Create a preparatory data_query
            if not isinstance(query, DataQuery):
                d_query = self.make_data_query(query)
            # Create a preparatory tile_query
            t0_query = self.make_tile_query(d_query)
            # Update keywords and set the cache
            keywords = t0_query.preload_source
            self._cache.set(query.key, keywords)
        # Return the updated keywords
        return keywords

    #####
    # Image Specific Methods
    #####

    def find_tiles(self, d_query):
        """ Load the requested image for a :class:`DataQuery`

        Arguments
        ----------
        d_query: :class:`DataQuery`
            Request for a scaled subvolume of a source image

        Returns
        numpy.ndarray
            The full image data for the requested region
        """
        first_tile_index = d_query.tile_bounds[0]
        all_tiles = np.argwhere(np.ones(d_query.tile_shape))
        cutout = np.zeros(d_query.target_shape, d_query.dtype)
        tiles_needed = first_tile_index + all_tiles

        for t_index in tiles_needed:
            # Make a query for the given tile
            t_query = self.make_tile_query(d_query, t_index)
            tile = self.load_tile(t_query)
            if not len(tile):
                continue
            # Fill the tile into the full cutout
            to_cut = [t_query.target_origin, tile.shape]
            [Z0, Y0, X0], [Z1, Y1, X1] = d_query.some_in_all(*to_cut)
            cutout[Z0:Z1, Y0:Y1, X0:X1] = tile

        return cutout

    def find_unique(self, d_query):
        """ Get unique values for a :class:`DataQuery`

        Arguments
        ----------
        d_query: :class:`DataQuery`
            Request for a scaled subvolume of a source image

        Returns
        set
            The set of unique values for the request
        """
        first_tile_index = d_query.tile_bounds[0]
        all_tiles = np.argwhere(np.ones(d_query.tile_shape))
        tiles_needed = first_tile_index + all_tiles
        # Set of all unique values
        unique = set()

        for t_index in tiles_needed:
            # Make a query for the given tile
            t_query = self.make_tile_query(d_query, t_index)
            tile = self.load_tile(t_query)
            # Union of unique values with the full set
            tile_bins = np.bincount(tile.flatten()) > 0
            unique = unique | set(np.where(tile_bins)[0])

        return unique

    def load_tile(self, t_query):
        """ Load a single tile from the cache or from disk

        Arguments
        ----------
        t_query: :class:`TileQuery`
            With tile coordinates and volume within the tile

        Returns
        --------
        numpy.ndarray
            The subregion image data for the requested tile
        """
        # grab request size for query
        t_bounds = t_query.target_bounds
        t_origin = t_query.target_tile_bounds[0]
        (K0, J0, I0), (K1, J1, I1) = t_bounds - t_origin

        # Load from cache or from disk if needed
        cache_tile = self._cache.get(t_query.key)
        if len(cache_tile):
            return cache_tile[K0:K1, J0:J1, I0:I1]
        # Load from disk
        tile = t_query.tile
        if not len(tile):
            return []

        self._cache.set(t_query.key, tile)

        return tile[K0:K1, J0:J1, I0:I1]

    @staticmethod
    def view_volume(view, vol):
        """ Display a volume in color or grayscale

        Arguments
        ----------
        view: str
            The requested color or gray view of the data
        vol: str
            Raw volume from :class:`Cache` / :class:`Datasource`

        Returns
        --------
        numpy.ndarray
            Colorized or original raw volume
        """

        # Set up a colormap
        def id_to_color(vol):
            colors = np.zeros((3, ) + vol.shape).astype(np.uint8)
            colors[0] = np.mod(107 * vol, 700).astype(np.uint8)
            colors[1] = np.mod(509 * vol, 900).astype(np.uint8)
            colors[2] = np.mod(200 * vol, 777).astype(np.uint8)
            return np.moveaxis(colors, 0, -1)

        # Colormap if a colormap view
        if view.VALUE == view.COLOR.NAME:
            return id_to_color(vol)
        return vol

    def write_image(self, d_query, volume):
        """ Format a volume for a given :class:`DataQuery`

        Arguments
        ----------
        d_query: :class:`DataQuery`
            With the format and view for the requested volume
        volume: numpy.ndarray
            Raw volume from :class:`Cache` / :class:`Datasource`

        Returns
        --------
        str:
            The image response as a formatted bytestring
        """
        img_format = d_query.INPUT.IMAGE.FORMAT
        img_view = d_query.INPUT.IMAGE.VIEW
        img_type = d_query.OUTPUT.INFO.TYPE

        # Only if grayscale view is set
        if img_view.VALUE == img_view.GRAY.NAME:
            # set the view based on the format
            is_big_int = img_type.VALUE in img_type.ID_LIST
            no_big_int_gray = img_format.VALUE in img_format.COLOR_LIST
            # If big integers must not be grayscale, try colormap
            if is_big_int and no_big_int_gray:
                img_view.VALUE = img_view.COLOR.NAME

        # If Multiple slices cannot be formatted
        if img_format.VALUE not in img_format.VOL_LIST:
            shape = volume.shape
            if shape[0] > 1:
                # Flatten the volume to image
                volume = volume.reshape(1, -1, shape[-1])
        # Use colormap / RGB style encoding of ID data
        vol = self.view_volume(img_view, volume)

        if img_format.VALUE in ['raw']:
            output = StringIO.StringIO()
            output.write(vol.tobytes())
            vol_string = output.getvalue()
            return vol_string

        if img_format.VALUE in ['npz']:
            output = StringIO.StringIO()
            np.save(output, vol[np.newaxis])
            vol_string = output.getvalue()
            return zlib.compress(vol_string)

        if img_format.VALUE in img_format.ZIP_LIST:
            output = StringIO.StringIO()
            volstring = vol[0].T.tostring('F')
            output.write(zlib.compress(volstring))
            return output.getvalue()

        if img_format.VALUE in img_format.TIF_LIST:
            output = StringIO.StringIO()
            tiffvol = vol[0]
            tifffile.imsave(output, tiffvol)
            return output.getvalue()

        filetype = "." + img_format.VALUE
        image = cv2.imencode(filetype, vol[0])
        return image[1].tostring()
Esempio n. 18
0
def run(movie_dir, html_output_flag, limit):
    """This is the real entry point for the program"""

    #A class to help lookup movie titles
    movielookup = MovieLookup()

    #Match files in a given directory
    matcher = Matcher(Config.movie_match_regex, Config.allowed_file_types)

    #Used to find an imdb id from movie filename
    id_finder = IdFinder()

    #Used for caching movie data
    movie_cache = Cache(Config.movie_cache_file)

    #First, let's match files which match the regex and have the
    #required file extensions in the given directory
    matcher.find_in_directory(movie_dir)
    movie_matches = matcher.get_matches()
    unmatched = matcher.get_ignored()

    #normalise the matches (the filenames will be used as movie titles)
    normalised_movie_matches = Normaliser\
        .normalise_list_and_remove_trailing_number(movie_matches)

    #Now we lookup successful matches, first in the cache, then online
    movie_data = {}  #successful lookup data will go here
    failed_lookups = []  #we will do something with failed lookups later...

    count = 0  #used to limit the number of lookups we will do
    for title in normalised_movie_matches:
        count += 1
        if count >= limit:  #check that we don't go over the arbitrary limit
            break

        #Check if the movie is in our cache
        cached_movie = movie_cache.get(title)
        if cached_movie:
            movie_data[title] = cached_movie
        #Otherwise, lookup using API
        else:
            #look up each movie in the list
            lookup_data = movielookup.lookup_by_title(title)

            #check if we found a movie
            if MovieDataUtil.is_valid_lookup_result(lookup_data):
                movie_data[title] = lookup_data
                #great, let's also add it to the cache
                movie_cache.add_to_cache(title, lookup_data)
            else:
                failed_lookups.append(title)

    #now we will try to correct the failed lookups
    #by using google to find each imdb id
    id_lookup_dict = id_finder.find_id_by_title_list(failed_lookups)

    #reset the failed lookups
    failed_lookups = []  #there should be a lot less now...
    title_corrections = 0  #count how many corrections we actually found

    #Now lookup using the new ids which we found
    for title, found_id in id_lookup_dict.items():
        if found_id != None:
            #we found an id, now let's look the movie up by its id
            lookup_data = movielookup.lookup_by_id(found_id)

            #theoretically this should always be true
            #unless we got an invalid id somehow...
            if MovieDataUtil.is_valid_lookup_result(lookup_data):
                movie_data[title] = lookup_data
                title_corrections += 1
                #great, let's also add it to the cache
                movie_cache.add_to_cache(title, lookup_data)
            else:
                failed_lookups.append(title)
        else:
            failed_lookups.append(title)

    #Save the updated cache
    movie_cache.save_cache_to_disk()

    #sort the data by imdb id
    movie_data = MovieDataUtil.sort_movie_data(movie_data)

    #Output the data
    if html_output_flag:
        logging.debug('Loading template from: %s', Config.template_directory)
        template_environment = Environment( \
                        loader=FileSystemLoader( \
                        Config.template_directory), trim_blocks=True)
        print template_environment.get_template('main.html').render(
            movie_lookup_data=movie_data,
            failed_lookups=failed_lookups,
            unmatched=unmatched,
            title_corrections=title_corrections,
            datetime=time.strftime("%c"),
            version=__version__,
            author=__author__,
            cache_stats=movie_cache.cache_stats(),
        )
    else:
        simple_output(movie_data, failed_lookups, unmatched)
Esempio n. 19
0
class CandleProcessor(object):
    stableCoinTickers = [
        "USD", "USDT", "USDC", "DAI", "HUSD", "TUSD", "PAX", "USDK", "USDN",
        "BUSD", "GUSD", "USDS"
    ]

    def __init__(self):
        self.isServiceAvailable = True
        signal.signal(signal.SIGINT, self.exit_gracefully)
        signal.signal(signal.SIGTERM, self.exit_gracefully)

        self.logging = error_reporting.Client()
        self.cache = Cache(ttl=30)

        context = zmq.Context.instance()
        self.socket = context.socket(zmq.ROUTER)
        self.socket.bind("tcp://*:6900")

        print("[Startup]: Candle Server is online")

    def exit_gracefully(self):
        print("[Startup]: Candle Server is exiting")
        self.socket.close()
        self.isServiceAvailable = False

    def run(self):
        while self.isServiceAvailable:
            try:
                response = None, None
                origin, delimeter, clientId, service, request = self.socket.recv_multipart(
                )
                request = pickle.loads(zlib.decompress(request))
                if request.timestamp + 30 < time.time(): continue

                if service == b"candle":
                    response = self.request_candle(request)

            except (KeyboardInterrupt, SystemExit):
                return
            except Exception:
                print(traceback.format_exc())
                if os.environ["PRODUCTION_MODE"]:
                    self.logging.report_exception()
            finally:
                try:
                    self.socket.send_multipart([
                        origin, delimeter,
                        zlib.compress(pickle.dumps(response, -1))
                    ])
                except:
                    pass

    def request_candle(self, request):
        payload, candleMessage, updatedCandleMessage = None, None, None

        for platform in request.platforms:
            request.set_current(platform=platform)
            hashCode = hash(request.requests[platform])
            fromCache = False

            if request.can_cache() and self.cache.has(hashCode):
                payload, updatedCandleMessage = self.cache.get(hashCode), None
                fromCache = True
            elif platform == "CCXT":
                payload, updatedCandleMessage = self.request_ccxt_candles(
                    request)
            elif platform == "IEXC":
                payload, updatedCandleMessage = self.request_iexc_candles(
                    request)
            elif platform == "Quandl":
                pass

            if payload is not None:
                if request.can_cache() and not fromCache:
                    self.cache.set(hashCode, payload)
                return payload, updatedCandleMessage
            elif updatedCandleMessage is not None:
                candleMessage = updatedCandleMessage

        return None, candleMessage

    def request_ccxt_candles(self, request):
        ticker = request.get_ticker()
        exchange = request.get_exchange()

        try:
            if exchange is None: return None, None
            exchange = Exchange(exchange.id)

            try:
                rawData = exchange.properties.fetch_ohlcv(ticker.symbol,
                                                          timeframe="1m",
                                                          limit=60)
                if len(rawData) == 0 or rawData[-1][4] is None or rawData[0][
                        1] is None:
                    return None, None
            except:
                return None, None

            payload = {
                "candles": [],
                "title":
                ticker.name,
                "baseTicker":
                "USD" if ticker.base in CandleProcessor.stableCoinTickers else
                ticker.base,
                "quoteTicker":
                "USD" if ticker.quote in CandleProcessor.stableCoinTickers else
                ticker.quote,
                "sourceText":
                "on {}".format(exchange.name),
                "platform":
                "CCXT"
            }

            for e in rawData:
                timestamp = e[0] / 1000
                if ticker.isReversed:
                    payload["candles"].append(
                        [timestamp, 1 / e[1], 1 / e[2], 1 / e[3], 1 / e[4]])
                else:
                    payload["candles"].append(
                        [timestamp, e[1], e[2], e[3], e[4]])

            return payload, None
        except Exception:
            print(traceback.format_exc())
            if os.environ["PRODUCTION_MODE"]: self.logging.report_exception()
            return None, None

    def request_iexc_candles(self, request):
        ticker = request.get_ticker()
        exchange = request.get_exchange()

        try:
            try:
                stock = Stock(ticker.id, token=os.environ["IEXC_KEY"])
                rawData = stock.get_quote().loc[ticker.id]
                if rawData is None: return None, None
            except:
                return None, None

            latestPrice = rawData["delayedPrice"] if rawData[
                "latestPrice"] is None else rawData["latestPrice"]
            lastPrice = float(
                latestPrice if "isUSMarketOpen" not in rawData or
                rawData["isUSMarketOpen"] or "extendedPrice" not in rawData or
                rawData["extendedPrice"] is None else rawData["extendedPrice"])
            openPrice, highPrice, lowPrice = None, None, None
            if "open" in rawData: openPrice = rawData["open"]
            if "high" in rawData: highPrice = rawData["high"]
            if "low" in rawData: lowPrice = rawData["low"]
            if openPrice is None: openPrice = lastPrice
            if highPrice is None: highPrice = lastPrice
            if lowPrice is None: lowPrice = lastPrice

            payload = {
                "candles": [[
                    float(rawData["latestUpdate"]) / 1000,
                    float(openPrice),
                    float(highPrice),
                    float(lowPrice), lastPrice
                ]],
                "title":
                ticker.name,
                "baseTicker":
                "shares",
                "quoteTicker":
                ticker.quote,
                "sourceText":
                "provided by IEX Cloud ● {} on {}".format(
                    rawData["latestSource"], rawData["primaryExchange"]),
                "platform":
                "IEXC",
            }

            return payload, None
        except Exception:
            print(traceback.format_exc())
            if os.environ["PRODUCTION_MODE"]: self.logging.report_exception()
            return None, None
Esempio n. 20
0
class ChainDb(object):
	def __init__(self, settings, datadir, log, mempool, netmagic,
		     readonly=False, fast_dbm=False,compression=False):
		self.settings = settings
		self.log = log
		self.mempool = mempool
		self.readonly = readonly
		self.netmagic = netmagic
		self.fast_dbm = fast_dbm
		self.blk_cache = Cache(1000)
		self.orphans = {}
		self.orphan_deps = {}
		self.compress_on_write = compression

		# LevelDB to hold:
		#    tx:*      transaction outputs
		#    misc:*    state
		#    height:*  list of blocks at height h
		#    blkmeta:* block metadata
		#    blocks:*  block seek point in stream
		self.blk_write = io.BufferedWriter(io.FileIO(datadir + '/blocks.dat','ab'))
		self.blk_read = io.BufferedReader(io.FileIO(datadir + '/blocks.dat','rb'))
		self.db = leveldb.LevelDB(datadir + '/leveldb')

		try:
			self.db.Get('misc:height')
		except KeyError:
			self.log.write("INITIALIZING EMPTY BLOCKCHAIN DATABASE")
			batch = leveldb.WriteBatch()
			batch.Put('misc:height', str(-1))
			batch.Put('misc:msg_start', self.netmagic.msg_start)
			batch.Put('misc:tophash', ser_uint256(0L))
			batch.Put('misc:total_work', hex(0L))
			self.db.Write(batch)

		try:
			start = self.db.Get('misc:msg_start')
			if start != self.netmagic.msg_start: raise KeyError
		except KeyError:
			self.log.write("Database magic number mismatch. Data corruption or incorrect network?")
			raise RuntimeError

	def puttxidx(self, txhash, txidx, batch=None):
		ser_txhash = ser_uint256(txhash)


		try:
			self.db.Get('tx:'+ser_txhash)
			old_txidx = self.gettxidx(txhash)
			self.log.write("WARNING: overwriting duplicate TX %064x, height %d, oldblk %064x, oldspent %x, newblk %064x" % (txhash, self.getheight(), old_txidx.blkhash, old_txidx.spentmask, txidx.blkhash))
		except KeyError:
			pass
		batch = self.db if batch is not None else batch
		batch.Put('tx:'+ser_txhash, hex(txidx.blkhash) + ' ' +
					       hex(txidx.spentmask))

		return True

	def gettxidx(self, txhash):
		ser_txhash = ser_uint256(txhash)
		try:
			ser_value = self.db.Get('tx:'+ser_txhash)
		except KeyError:
			return None

		pos = string.find(ser_value, ' ')

		txidx = TxIdx()
		txidx.blkhash = long(ser_value[:pos], 16)
		txidx.spentmask = long(ser_value[pos+1:], 16)

		return txidx

	def gettx(self, txhash):
		txidx = self.gettxidx(txhash)
		if txidx is None:
			return None

		block = self.getblock(txidx.blkhash)
		if block:
			for tx in block.vtx:
				tx.calc_sha256()
				if tx.sha256 == txhash:
					return tx

		self.log.write("ERROR: Missing TX %064x in block %064x" % (txhash, txidx.blkhash))
		return None

	def haveblock(self, blkhash, checkorphans):
		if self.blk_cache.exists(blkhash):
			return True
		if checkorphans and blkhash in self.orphans:
			return True
		ser_hash = ser_uint256(blkhash)
		try: 
			self.db.Get('blocks:'+ser_hash)
			return True
		except KeyError:
			return False

	def have_prevblock(self, block):
		if self.getheight() < 0 and block.sha256 == self.netmagic.block0:
			return True
		if self.haveblock(block.hashPrevBlock, False):
			return True
		return False

	def getblock(self, blkhash):
		block = self.blk_cache.get(blkhash)
		if block is not None:
			return block

		ser_hash = ser_uint256(blkhash)
		try:
			# Lookup the block index, seek in the file
			fpos = long(self.db.Get('blocks:'+ser_hash))
			self.blk_read.seek(fpos)

			# read and decode "block" msg

			recvbuf = self.blk_read.read(4+4)
			if recvbuf[:4] == 'ZLIB':
				msg_len = int(recvbuf[4:8].encode('hex'),16)
				recvbuf = self.blk_read.read(msg_len)
			
				f = cStringIO.StringIO(zlib.decompress(recvbuf))
				msg = message_read(self.netmagic, f)
			else:	
				self.blk_read.seek(fpos)
				msg = message_read(self.netmagic, self.blk_read)
			
			
			if msg is None:
				return None
			block = msg.block
		except KeyError:
			return None

		self.blk_cache.put(blkhash, block)

		return block

	def spend_txout(self, txhash, n_idx, batch=None):
		txidx = self.gettxidx(txhash)
		if txidx is None:
			return False

		txidx.spentmask |= (1L << n_idx)
		self.puttxidx(txhash, txidx, batch)

		return True

	def clear_txout(self, txhash, n_idx, batch=None):
		txidx = self.gettxidx(txhash)
		if txidx is None:
			return False

		txidx.spentmask &= ~(1L << n_idx)
		self.puttxidx(txhash, txidx, batch)

		return True

	def unique_outpts(self, block):
		outpts = {}
		txmap = {}
		for tx in block.vtx:
			if tx.is_coinbase:
				continue
			txmap[tx.sha256] = tx
			for txin in tx.vin:
				v = (txin.prevout.hash, txin.prevout.n)
				if v in outs:
					return None

				outpts[v] = False

		return (outpts, txmap)

	def txout_spent(self, txout):
		txidx = self.gettxidx(txout.hash)
		if txidx is None:
			return None

		if txout.n > 100000:	# outpoint index sanity check
			return None

		if txidx.spentmask & (1L << txout.n):
			return True

		return False
Esempio n. 21
0
class ChainDb(object):
    def __init__(self,
                 settings,
                 datadir,
                 log,
                 mempool,
                 netmagic,
                 readonly=False,
                 fast_dbm=False):
        self.settings = settings
        self.log = log
        self.mempool = mempool
        self.readonly = readonly
        self.netmagic = netmagic
        self.fast_dbm = fast_dbm
        self.blk_cache = Cache(750)
        self.orphans = {}
        self.orphan_deps = {}

        # LevelDB to hold:
        #    tx:*      transaction outputs
        #    misc:*    state
        #    height:*  list of blocks at height h
        #    blkmeta:* block metadata
        #    blocks:*  block seek point in stream
        self.blk_write = io.BufferedWriter(
            io.FileIO(datadir + '/blocks.dat', 'ab'))
        self.blk_read = io.BufferedReader(
            io.FileIO(datadir + '/blocks.dat', 'rb'))
        self.db = leveldb.LevelDB(datadir + '/leveldb')

        try:
            self.db.Get('misc:height')
        except KeyError:
            self.log.write("INITIALIZING EMPTY BLOCKCHAIN DATABASE")
            batch = leveldb.WriteBatch()
            batch.Put('misc:height', str(-1))
            batch.Put('misc:msg_start', self.netmagic.msg_start)
            batch.Put('misc:tophash', ser_uint256(0L))
            batch.Put('misc:total_work', hex(0L))
            self.db.Write(batch)

        try:
            start = self.db.Get('misc:msg_start')
            if start != self.netmagic.msg_start: raise KeyError
        except KeyError:
            self.log.write(
                "Database magic number mismatch. Data corruption or incorrect network?"
            )
            raise RuntimeError

    def puttxidx(self, txhash, txidx, batch=None):
        ser_txhash = ser_uint256(txhash)

        try:
            self.db.Get('tx:' + ser_txhash)
            old_txidx = self.gettxidx(txhash)
            self.log.write(
                "WARNING: overwriting duplicate TX %064x, height %d, oldblk %064x, oldspent %x, newblk %064x"
                % (txhash, self.getheight(), old_txidx.blkhash,
                   old_txidx.spentmask, txidx.blkhash))
        except KeyError:
            pass
        batch = self.db if batch is not None else batch
        batch.Put('tx:' + ser_txhash,
                  hex(txidx.blkhash) + ' ' + hex(txidx.spentmask))

        return True

    def gettxidx(self, txhash):
        ser_txhash = ser_uint256(txhash)
        try:
            ser_value = self.db.Get('tx:' + ser_txhash)
        except KeyError:
            return None

        pos = string.find(ser_value, ' ')

        txidx = TxIdx()
        txidx.blkhash = long(ser_value[:pos], 16)
        txidx.spentmask = long(ser_value[pos + 1:], 16)

        return txidx

    def gettx(self, txhash):
        txidx = self.gettxidx(txhash)
        if txidx is None:
            return None

        block = self.getblock(txidx.blkhash)
        for tx in block.vtx:
            tx.calc_sha256()
            if tx.sha256 == txhash:
                return tx

        self.log.write("ERROR: Missing TX %064x in block %064x" %
                       (txhash, txidx.blkhash))
        return None

    def haveblock(self, blkhash, checkorphans):
        if self.blk_cache.exists(blkhash):
            return True
        if checkorphans and blkhash in self.orphans:
            return True
        ser_hash = ser_uint256(blkhash)
        try:
            self.db.Get('blocks:' + ser_hash)
            return True
        except KeyError:
            return False

    def have_prevblock(self, block):
        if self.getheight() < 0 and block.sha256 == self.netmagic.block0:
            return True
        if self.haveblock(block.hashPrevBlock, False):
            return True
        return False

    def getblock(self, blkhash):
        block = self.blk_cache.get(blkhash)
        if block is not None:
            return block

        ser_hash = ser_uint256(blkhash)
        try:
            # Lookup the block index, seek in the file
            fpos = long(self.db.Get('blocks:' + ser_hash))
            self.blk_read.seek(fpos)

            # read and decode "block" msg
            msg = message_read(self.netmagic, self.blk_read)
            if msg is None:
                return None
            block = msg.block
        except KeyError:
            return None

        self.blk_cache.put(blkhash, block)

        return block

    def spend_txout(self, txhash, n_idx, batch=None):
        txidx = self.gettxidx(txhash)
        if txidx is None:
            return False

        txidx.spentmask |= (1L << n_idx)
        self.puttxidx(txhash, txidx, batch)

        return True

    def clear_txout(self, txhash, n_idx, batch=None):
        txidx = self.gettxidx(txhash)
        if txidx is None:
            return False

        txidx.spentmask &= ~(1L << n_idx)
        self.puttxidx(txhash, txidx, batch)

        return True

    def unique_outpts(self, block):
        outpts = {}
        txmap = {}
        for tx in block.vtx:
            if tx.is_coinbase:
                continue
            txmap[tx.sha256] = tx
            for txin in tx.vin:
                v = (txin.prevout.hash, txin.prevout.n)
                if v in outs:
                    return None

                outpts[v] = False

        return (outpts, txmap)

    def spent_outpts(self, block):
        # list of outpoints this block wants to spend
        l = self.unique_outpts(block)
        if l is None:
            return None
        outpts = l[0]
        txmap = l[1]
        spendlist = {}

        # pass 1: if outpoint in db, make sure it is unspent
        for k in outpts.iterkeys():
            txidx = self.gettxidx(k[0])
            if txidx is None:
                continue

            if k[1] > 100000:  # outpoint index sanity check
                return None

            if txidx.spentmask & (1L << k[1]):
                return None

            outpts[k] = True  # skip in pass 2

        # pass 2: remaining outpoints must exist in this block
        for k, v in outpts.iteritems():
            if v:
                continue

            if k[0] not in txmap:  # validate txout hash
                return None

            tx = txmap[k[0]]  # validate txout index (n)
            if k[1] >= len(tx.vout):
                return None

            # outpts[k] = True	# not strictly necessary

        return outpts.keys()
        try:
            message = udp_connection_client.blocked_recv() # Get message from the client
        except ConnectionResetError:
            continue
        dns_message.parse_dns(message) # Parse the message received
        search_key = (dns_message.QueriesList[0].QNAME, dns_message.QueriesList[0].QTYPE) # the search key is for query the cache: qname and qtype
        transaction_id = dns_message.Header.TransactionID # The transaction ID of the query from client

        if Config.VERBOSE:
            print("Transaction ID:{}. Search Key: {}".format(transaction_id, search_key))
        """
        Find Answer in cache
        """
        if Config.VERBOSE:
            print("Cache: {}".format(cache.itemdict.keys()))
        cache_msg = cache.get(search_key) # Find the answer in the cache
        if cache_msg != Cache.ANSWER_EXPIRE and cache_msg != Cache.ANSWER_NOT_FOUND: # The answer is in the cache and it is not out of date
            if Config.CACHE_VERBOSE:
                print("Cache hit.")
            udp_connection_client.sendto(dns_message.changeTransactionID(cache_msg, transaction_id)) # Send the answer to the client directly
            continue
        else: # The answer is not found or out of date
            if Config.CACHE_VERBOSE:
                print("Cache miss :", cache_msg)
            """
            Forward query to upper level DNS sever
            """
            udp_connection_DNS = UDPConnection() # Connect to the upper level server
            udp_connection_DNS.sendto_server(message, Config.DNS_server_IPaddr, Config.DNS_server_Port) # Forward the query to the server
            try:
                message = udp_connection_DNS.blocked_recv() # Receive the response from the server
Esempio n. 23
0
class QuoteProcessor(object):
    imageOverlays = {
        "Alpha depth":
        Image.open("app/assets/overlays/quotes/depth.png").convert("RGBA")
    }

    stableCoinTickers = [
        "USD", "USDT", "USDC", "DAI", "HUSD", "TUSD", "PAX", "USDK", "USDN",
        "BUSD", "GUSD", "USDS"
    ]
    lastBitcoinQuote = {}

    def __init__(self):
        self.isServiceAvailable = True
        signal.signal(signal.SIGINT, self.exit_gracefully)
        signal.signal(signal.SIGTERM, self.exit_gracefully)

        self.logging = error_reporting.Client()
        self.cache1 = Cache(ttl=5)
        self.cache2 = Cache(ttl=5)

        self.coinGecko = CoinGeckoAPI()
        self.lastBitcoinQuote = {
            "quotePrice": [0],
            "quoteVolume":
            None,
            "ticker":
            Ticker("BTCUSD", "BTCUSD", "BTC", "USD", "BTC/USD",
                   hasParts=False),
            "exchange":
            None,
            "timestamp":
            time.time()
        }

        try:
            rawData = self.coinGecko.get_coin_by_id(id="bitcoin",
                                                    localization="false",
                                                    tickers=False,
                                                    market_data=True,
                                                    community_data=False,
                                                    developer_data=False)
            self.lastBitcoinQuote["quotePrice"] = [
                rawData["market_data"]["current_price"]["usd"]
            ]
            self.lastBitcoinQuote["quoteVolume"] = rawData["market_data"][
                "total_volume"]["usd"]
        except:
            pass

        context = zmq.Context.instance()
        self.socket = context.socket(zmq.ROUTER)
        self.socket.bind("tcp://*:6900")

        print("[Startup]: Quote Server is online")

    def exit_gracefully(self):
        print("[Startup]: Quote Server is exiting")
        self.socket.close()
        self.isServiceAvailable = False

    def run(self):
        while self.isServiceAvailable:
            try:
                response = None, None
                origin, delimeter, clientId, service, request = self.socket.recv_multipart(
                )
                request = pickle.loads(zlib.decompress(request))
                if request.timestamp + 30 < time.time(): continue

                if service == b"quote":
                    response = self.request_quote(request)
                elif service == b"depth":
                    response = self.request_depth(request)

            except (KeyboardInterrupt, SystemExit):
                return
            except Exception:
                print(traceback.format_exc())
                if os.environ["PRODUCTION_MODE"]:
                    self.logging.report_exception()
            finally:
                try:
                    self.socket.send_multipart([
                        origin, delimeter,
                        zlib.compress(pickle.dumps(response, -1))
                    ])
                except:
                    pass

    def request_depth(self, request):
        payload, quoteMessage, updatedQuoteMessage = None, None, None

        for platform in request.platforms:
            request.set_current(platform=platform)
            hashCode = hash(request.requests[platform])
            fromCache = False

            if request.can_cache() and self.cache1.has(hashCode):
                payload, updatedQuoteMessage = self.cache1.get(hashCode), None
                fromCache = True
            elif platform == "CCXT":
                payload, updatedQuoteMessage = self.request_ccxt_depth(request)
            elif platform == "IEXC":
                payload, updatedQuoteMessage = self.request_iexc_depth(request)

            if payload is not None:
                if request.can_cache() and not fromCache:
                    self.cache1.set(hashCode, payload)
                if request.authorId != 401328409499664394 and request.requests[
                        platform].ticker.base is not None and request.authorId not in constants.satellites:
                    database.document("dataserver/statistics/{}/{}".format(
                        platform, str(uuid.uuid4()))).set({
                            "timestamp":
                            time.time(),
                            "authorId":
                            str(request.authorId),
                            "ticker": {
                                "base": request.requests[platform].ticker.base,
                                "quote":
                                request.requests[platform].ticker.quote,
                                "id": request.requests[platform].ticker.id,
                                "bias": request.parserBias
                            },
                            "exchange":
                            None if request.requests[platform].exchange is None
                            else request.requests[platform].exchange.id
                        })
                return payload, updatedQuoteMessage
            elif updatedQuoteMessage is not None:
                quoteMessage = updatedQuoteMessage

        return None, quoteMessage

    def request_quote(self, request):
        payload, quoteMessage, updatedQuoteMessage = None, None, None

        for platform in request.platforms:
            request.set_current(platform=platform)
            hashCode = hash(request.requests[platform])
            fromCache = False

            if request.can_cache() and self.cache2.has(hashCode):
                payload, updatedQuoteMessage = self.cache2.get(hashCode), None
                fromCache = True
            elif platform == "Alternative.me":
                payload, updatedQuoteMessage = self.request_fear_greed_index(
                    request)
            elif platform == "LLD":
                payload, updatedQuoteMessage = self.request_lld_quote(request)
            elif platform == "CoinGecko":
                payload, updatedQuoteMessage = self.request_coingecko_quote(
                    request)
            elif platform == "CCXT":
                payload, updatedQuoteMessage = self.request_ccxt_quote(request)
            elif platform == "IEXC":
                payload, updatedQuoteMessage = self.request_iexc_quote(request)
            elif platform == "Quandl":
                pass

            if payload is not None:
                if request.can_cache() and not fromCache:
                    self.cache2.set(hashCode, payload)
                if request.authorId != 401328409499664394 and request.requests[
                        platform].ticker.base is not None and request.authorId not in constants.satellites:
                    database.document("dataserver/statistics/{}/{}".format(
                        platform, str(uuid.uuid4()))).set({
                            "timestamp":
                            time.time(),
                            "authorId":
                            str(request.authorId),
                            "ticker": {
                                "base": request.requests[platform].ticker.base,
                                "quote":
                                request.requests[platform].ticker.quote,
                                "id": request.requests[platform].ticker.id,
                                "bias": request.parserBias
                            },
                            "exchange":
                            None if request.requests[platform].exchange is None
                            else request.requests[platform].exchange.id
                        })
                return payload, updatedQuoteMessage
            elif updatedQuoteMessage is not None:
                quoteMessage = updatedQuoteMessage

        return None, quoteMessage

    def request_coingecko_quote(self, request):
        ticker = request.get_ticker()
        exchange = request.get_exchange()

        try:
            try:
                rawData = self.coinGecko.get_coin_by_id(id=ticker.symbol,
                                                        localization="false",
                                                        tickers=False,
                                                        market_data=True,
                                                        community_data=False,
                                                        developer_data=False)
            except:
                return None, None

            if ticker.quote.lower() not in rawData["market_data"][
                    "current_price"] or ticker.quote.lower(
                    ) not in rawData["market_data"]["total_volume"]:
                return None, "Requested price for `{}` is not available.".format(
                    ticker.name)

            price = rawData["market_data"]["current_price"][
                ticker.quote.lower()]
            if ticker.isReversed: price = 1 / price
            volume = rawData["market_data"]["total_volume"][
                ticker.quote.lower()]
            priceChange = rawData["market_data"][
                "price_change_percentage_24h_in_currency"][ticker.quote.lower(
                )] if ticker.quote.lower() in rawData["market_data"][
                    "price_change_percentage_24h_in_currency"] else 0
            if ticker.isReversed:
                priceChange = (1 / (priceChange / 100 + 1) - 1) * 100

            payload = {
                "quotePrice":
                ("{:,.%df}" %
                 (4 if TickerParser.check_if_fiat(ticker.quote)[0]
                  and not ticker.isReversed else 8)).format(price),
                "quoteVolume":
                volume,
                "quoteConvertedPrice":
                None if ticker.quote == "USD" else "≈ ${:,.6f}".format(
                    rawData["market_data"]["current_price"]["usd"]),
                "quoteConvertedVolume":
                None if ticker.quote == "USD" else "≈ ${:,.4f}".format(
                    rawData["market_data"]["total_volume"]["usd"]),
                "title":
                ticker.name,
                "baseTicker":
                ticker.base if ticker.base.lower()
                in rawData["market_data"]["current_price"] else "BTC",
                "quoteTicker":
                ticker.quote if ticker.quote.lower()
                in rawData["market_data"]["current_price"] else "BTC",
                "change":
                priceChange,
                "thumbnailUrl":
                TickerParser.get_coingecko_image(ticker.base),
                "messageColor":
                "amber" if priceChange == 0 else
                ("green" if priceChange > 0 else "red"),
                "sourceText":
                "from CoinGecko",
                "platform":
                "CoinGecko",
                "raw": {
                    "quotePrice": [price],
                    "quoteVolume": volume,
                    "ticker": ticker,
                    "exchange": exchange,
                    "timestamp": time.time()
                }
            }
            if ticker == Ticker("BTCUSD",
                                "BTCUSD",
                                "BTC",
                                "USD",
                                "BTC/USD",
                                hasParts=False):
                self.lastBitcoinQuote = payload["raw"]
            return payload, None
        except Exception:
            print(traceback.format_exc())
            if os.environ["PRODUCTION_MODE"]: self.logging.report_exception()
            return None, None

    def request_ccxt_quote(self, request):
        ticker = request.get_ticker()
        exchange = request.get_exchange()

        try:
            if exchange is None: return None, None
            exchange = Exchange(exchange.id)

            tf, limitTimestamp, candleOffset = Utils.get_highest_supported_timeframe(
                exchange.properties,
                datetime.datetime.now().astimezone(pytz.utc))
            try:
                rawData = exchange.properties.fetch_ohlcv(ticker.symbol,
                                                          timeframe=tf.lower(),
                                                          since=limitTimestamp,
                                                          limit=300)
                if len(rawData) == 0 or rawData[-1][4] is None or rawData[0][
                        1] is None:
                    return None, None
            except:
                return None, None

            price = [rawData[-1][4], rawData[0][1]
                     ] if len(rawData) < candleOffset else [
                         rawData[-1][4], rawData[-candleOffset][1]
                     ]
            if ticker.isReversed: price = [1 / price[0], 1 / price[1]]
            volume = None if price[0] is None else sum([
                candle[5] for candle in rawData if int(candle[0] / 1000) >=
                int(exchange.properties.milliseconds() / 1000) - 86400
            ]) / (
                price[0] if exchange.id in ["bitmex", "binancefutures"] else 1)
            priceChange = 0 if tf == "1m" or price[1] == 0 else (
                price[0] / price[1]) * 100 - 100

            payload = {
                "quotePrice":
                "{:,.8f}".format(price[0])
                if ticker.isReversed else Utils.format_price(
                    exchange.properties, ticker.symbol, price[0]),
                "quoteVolume":
                volume,
                "quoteConvertedPrice":
                "≈ ${:,.6f}".format(price[0] *
                                    self.lastBitcoinQuote["quotePrice"][0])
                if ticker.quote == "BTC" else None,
                "quoteConvertedVolume":
                "≈ ${:,.4f}".format(volume *
                                    self.lastBitcoinQuote["quotePrice"][0])
                if ticker.quote == "BTC" else None,
                "title":
                ticker.name,
                "baseTicker":
                "USD" if ticker.base in QuoteProcessor.stableCoinTickers else
                ticker.base,
                "quoteTicker":
                "USD" if ticker.quote in QuoteProcessor.stableCoinTickers else
                ticker.quote,
                "change":
                priceChange,
                "thumbnailUrl":
                TickerParser.get_coingecko_image(ticker.base),
                "messageColor":
                "amber" if priceChange == 0 else
                ("green" if priceChange > 0 else "red"),
                "sourceText":
                "on {}".format(exchange.name),
                "platform":
                "CCXT",
                "raw": {
                    "quotePrice": [price[0]] if tf == "1m" else price[:1],
                    "quoteVolume": volume,
                    "ticker": ticker,
                    "exchange": exchange,
                    "timestamp": time.time()
                }
            }
            return payload, None
        except Exception:
            print(traceback.format_exc())
            if os.environ["PRODUCTION_MODE"]: self.logging.report_exception()
            return None, None

    def request_lld_quote(self, request):
        ticker = request.get_ticker()
        exchange = request.get_exchange()
        filters = request.get_filters()
        action = request.find_parameter_in_list("lld", filters)

        try:
            if exchange is not None: exchange = Exchange(exchange.id)

            if action == "funding":
                if exchange.id in ["bitmex"]:
                    try:
                        rawData = exchange.properties.public_get_instrument(
                            {"symbol": ticker.id})[0]
                    except:
                        return None, "Requested funding data for `{}` is not available.".format(
                            ticker.name)

                    if rawData["fundingTimestamp"] is None:
                        fundingDate = datetime.datetime.strptime(
                            rawData["fundingTimestamp"],
                            "%Y-%m-%dT%H:%M:00.000Z").replace(tzinfo=pytz.utc)
                    else:
                        fundingDate = datetime.datetime.now().replace(
                            tzinfo=pytz.utc)
                    indicativeFundingTimestamp = datetime.datetime.timestamp(
                        fundingDate) + 28800
                    indicativeFundingDate = datetime.datetime.utcfromtimestamp(
                        indicativeFundingTimestamp).replace(tzinfo=pytz.utc)
                    deltaFunding = fundingDate - datetime.datetime.now(
                    ).astimezone(pytz.utc)
                    deltaIndicative = indicativeFundingDate - datetime.datetime.now(
                    ).astimezone(pytz.utc)

                    hours1, seconds1 = divmod(
                        deltaFunding.days * 86400 + deltaFunding.seconds, 3600)
                    minutes1 = int(seconds1 / 60)
                    hoursFunding = "{:d} {} ".format(
                        hours1, "hours"
                        if hours1 > 1 else "hour") if hours1 > 0 else ""
                    minutesFunding = "{:d} {}".format(
                        minutes1 if hours1 > 0 or minutes1 > 0 else seconds1,
                        "{}".format("minute" if minutes1 == 1 else "minutes")
                        if hours1 > 0 or minutes1 > 0 else
                        ("second" if seconds1 == 1 else "seconds"))
                    deltaFundingText = "{}{}".format(hoursFunding,
                                                     minutesFunding)

                    hours2, seconds2 = divmod(
                        deltaIndicative.days * 86400 + deltaIndicative.seconds,
                        3600)
                    minutes2 = int(seconds2 / 60)
                    hoursIndicative = "{:d} {} ".format(
                        hours2, "hours"
                        if hours2 > 1 else "hour") if hours2 > 0 else ""
                    minutesIndicative = "{:d} {}".format(
                        minutes2 if hours2 > 0 or minutes2 > 0 else seconds2,
                        "{}".format("minute" if minutes2 == 1 else "minutes")
                        if hours2 > 0 or minutes2 > 0 else
                        ("second" if seconds2 == 1 else "seconds"))
                    deltaIndicativeText = "{}{}".format(
                        hoursIndicative, minutesIndicative)

                    fundingRate = rawData["fundingRate"] * 100
                    predictedFundingRate = rawData[
                        "indicativeFundingRate"] * 100
                    averageFundingRate = (fundingRate +
                                          predictedFundingRate) / 2

                    payload = {
                        "quotePrice":
                        "Funding Rate: {:+.4f} % *(in {})*\nPredicted Rate: {:+.4f} % *(in {})*"
                        .format(fundingRate, deltaFundingText,
                                predictedFundingRate, deltaIndicativeText),
                        "quoteVolume":
                        None,
                        "quoteConvertedPrice":
                        None,
                        "quoteConvertedVolume":
                        None,
                        "title":
                        ticker.name,
                        "baseTicker":
                        ticker.base,
                        "quoteTicker":
                        ticker.quote,
                        "change":
                        None,
                        "thumbnailUrl":
                        TickerParser.get_coingecko_image(ticker.base),
                        "messageColor":
                        "yellow" if averageFundingRate == 0.01 else
                        ("light green"
                         if averageFundingRate < 0.01 else "deep orange"),
                        "sourceText":
                        "Contract details on {}".format(exchange.name),
                        "platform":
                        "LLD",
                        "raw": {
                            "quotePrice": [fundingRate, predictedFundingRate],
                            "quoteVolume": None,
                            "ticker": ticker,
                            "exchange": exchange,
                            "timestamp": time.time()
                        }
                    }
                    return payload, None
                return None, "Funding data is only available on BitMEX."
            elif action == "oi":
                if exchange.id in ["bitmex"]:
                    try:
                        rawData = exchange.properties.public_get_instrument(
                            {"symbol": ticker.id})[0]
                    except:
                        return None, "Requested open interest data for `{}` is not available.".format(
                            ticker.name)

                    payload = {
                        "quotePrice":
                        "Open interest: {:,.0f} {}\nOpen value: {:,.4f} XBT".
                        format(rawData["openInterest"],
                               "USD" if ticker.id == "XBTUSD" else "contracts",
                               rawData["openValue"] / 100000000),
                        "quoteVolume":
                        None,
                        "quoteConvertedPrice":
                        None,
                        "quoteConvertedVolume":
                        None,
                        "title":
                        ticker.name,
                        "baseTicker":
                        ticker.base,
                        "quoteTicker":
                        ticker.quote,
                        "change":
                        None,
                        "thumbnailUrl":
                        TickerParser.get_coingecko_image(ticker.base),
                        "messageColor":
                        "deep purple",
                        "sourceText":
                        "Contract details on {}".format(exchange.name),
                        "platform":
                        "LLD",
                        "raw": {
                            "quotePrice": [
                                rawData["openInterest"],
                                rawData["openValue"] / 100000000
                            ],
                            "quoteVolume":
                            None,
                            "ticker":
                            ticker,
                            "exchange":
                            exchange,
                            "timestamp":
                            time.time()
                        }
                    }
                    return payload, None
                return None, "Open interest and open value data is only available on BitMEX."
            elif action == "ls":
                if exchange.id in ["bitfinex2"]:
                    try:
                        longs = exchange.properties.publicGetStats1KeySizeSymbolLongLast(
                            {
                                "key": "pos.size",
                                "size": "1m",
                                "symbol": "t{}".format(ticker.id),
                                "side": "long",
                                "section": "last"
                            })
                        shorts = exchange.properties.publicGetStats1KeySizeSymbolShortLast(
                            {
                                "key": "pos.size",
                                "size": "1m",
                                "symbol": "t{}".format(ticker.id),
                                "side": "long",
                                "section": "last"
                            })
                        ratio = longs[1] / (longs[1] + shorts[1]) * 100
                    except:
                        return None, None

                    payload = {
                        "quotePrice":
                        "{:.1f} % longs / {:.1f} % shorts".format(
                            ratio, 100 - ratio),
                        "quoteVolume":
                        None,
                        "quoteConvertedPrice":
                        None,
                        "quoteConvertedVolume":
                        None,
                        "title":
                        "{} longs/shorts ratio".format(ticker.name),
                        "baseTicker":
                        ticker.base,
                        "quoteTicker":
                        ticker.quote,
                        "change":
                        None,
                        "thumbnailUrl":
                        TickerParser.get_coingecko_image(ticker.base),
                        "messageColor":
                        "deep purple",
                        "sourceText":
                        "Data on {}".format(exchange.name),
                        "platform":
                        "LLD",
                        "raw": {
                            "quotePrice": [longs[1], shorts[1]],
                            "quoteVolume": None,
                            "ticker": ticker,
                            "exchange": exchange,
                            "timestamp": time.time()
                        }
                    }
                    return payload, None
                return None, "Longs and shorts data is only available on Bitfinex."
            elif action == "sl":
                if exchange.id in ["bitfinex2"]:
                    try:
                        longs = exchange.properties.publicGetStats1KeySizeSymbolLongLast(
                            {
                                "key": "pos.size",
                                "size": "1m",
                                "symbol": "t{}".format(ticker.id),
                                "side": "short",
                                "section": "last"
                            })
                        shorts = exchange.properties.publicGetStats1KeySizeSymbolShortLast(
                            {
                                "key": "pos.size",
                                "size": "1m",
                                "symbol": "t{}".format(ticker.id),
                                "side": "short",
                                "section": "last"
                            })
                        ratio = shorts[1] / (longs[1] + shorts[1]) * 100
                    except:
                        return None, None

                    payload = {
                        "quotePrice":
                        "{:.1f} % shorts / {:.1f} % longs".format(
                            ratio, 100 - ratio),
                        "quoteVolume":
                        None,
                        "quoteConvertedPrice":
                        None,
                        "quoteConvertedVolume":
                        None,
                        "title":
                        "{} shorts/longs ratio".format(ticker.name),
                        "baseTicker":
                        ticker.base,
                        "quoteTicker":
                        ticker.quote,
                        "change":
                        None,
                        "thumbnailUrl":
                        TickerParser.get_coingecko_image(ticker.base),
                        "messageColor":
                        "deep purple",
                        "sourceText":
                        "Data on {}".format(exchange.name),
                        "platform":
                        "LLD",
                        "raw": {
                            "quotePrice": [longs[1], shorts[1]],
                            "quoteVolume": None,
                            "ticker": ticker,
                            "exchange": exchange,
                            "timestamp": time.time()
                        }
                    }
                    return payload, None
                return None, "Longs and shorts data is only available on Bitfinex."
            elif action == "dom":
                try:
                    rawData = self.coinGecko.get_global()
                except:
                    return None, "Requested dominance data for `{}` is not available.".format(
                        ticker.name)
                if ticker.base.lower() not in rawData["market_cap_percentage"]:
                    return None, "Dominance for {} does not exist.".format(
                        ticker.base)
                coinDominance = rawData["market_cap_percentage"][
                    ticker.base.lower()]

                payload = {
                    "quotePrice":
                    "{} dominance: {:,.2f} %".format(ticker.base,
                                                     coinDominance),
                    "quoteVolume":
                    None,
                    "quoteConvertedPrice":
                    None,
                    "quoteConvertedVolume":
                    None,
                    "title":
                    "Market Dominance",
                    "baseTicker":
                    ticker.base,
                    "quoteTicker":
                    ticker.quote,
                    "change":
                    None,
                    "thumbnailUrl":
                    TickerParser.get_coingecko_image(ticker.base),
                    "messageColor":
                    "deep purple",
                    "sourceText":
                    "Market information from CoinGecko",
                    "platform":
                    "LLD",
                    "raw": {
                        "quotePrice": coinDominance,
                        "quoteVolume": None,
                        "ticker": ticker,
                        "exchange": None,
                        "timestamp": time.time()
                    }
                }
                return payload, None
            else:
                return None, None
        except Exception:
            print(traceback.format_exc())
            if os.environ["PRODUCTION_MODE"]: self.logging.report_exception()
            return None, None

    def request_iexc_quote(self, request):
        payload, quoteMessage, updatedQuoteMessage = None, None, None

        for platform in ["Stocks", "Forex"]:
            if platform == "Stocks":
                payload, updatedQuoteMessage = self.request_iexc_stocks(
                    request)
            elif platform == "Forex":
                payload, updatedQuoteMessage = self.request_iexc_forex(request)

            if payload is not None:
                return payload, updatedQuoteMessage
            elif updatedQuoteMessage is not None:
                quoteMessage = updatedQuoteMessage

        return None, quoteMessage

    def request_iexc_stocks(self, request):
        ticker = request.get_ticker()
        exchange = request.get_exchange()

        try:
            try:
                stock = Stock(ticker.id, token=os.environ["IEXC_KEY"])
                rawData = stock.get_quote().loc[ticker.id]
                if ticker.quote is None and exchange is not None:
                    return None, "Price for `{}` is only available on `{}`.".format(
                        ticker.id, rawData["primaryExchange"])
                if rawData is None or (rawData["latestPrice"] is None
                                       and rawData["delayedPrice"] is None):
                    return None, None
            except:
                return None, None

            try:
                coinThumbnail = stock.get_logo().loc[ticker.id]["url"]
            except:
                coinThumbnail = static_storage.icon

            latestPrice = rawData["delayedPrice"] if rawData[
                "latestPrice"] is None else rawData["latestPrice"]
            price = float(latestPrice if "isUSMarketOpen" not in rawData or
                          rawData["isUSMarketOpen"] or "extendedPrice" not in
                          rawData or rawData["extendedPrice"] is None else
                          rawData["extendedPrice"])
            if ticker.isReversed: price = 1 / price
            volume = float(rawData["latestVolume"])
            priceChange = (1 / rawData["change"]
                           if ticker.isReversed else rawData["change"]
                           ) / price * 100 if "change" in rawData and rawData[
                               "change"] is not None else 0

            payload = {
                "quotePrice":
                "{:,.5f}".format(price)
                if ticker.isReversed else "{}".format(price),
                "quoteVolume":
                volume,
                "quoteConvertedPrice":
                None,
                "quoteConvertedVolume":
                None,
                "title":
                ticker.name,
                "baseTicker":
                "contracts",
                "quoteTicker":
                ticker.quote,
                "change":
                priceChange,
                "thumbnailUrl":
                coinThumbnail,
                "messageColor":
                "amber" if priceChange == 0 else
                ("green" if priceChange > 0 else "red"),
                "sourceText":
                "provided by IEX Cloud",
                "platform":
                "IEXC",
                "raw": {
                    "quotePrice": [price],
                    "quoteVolume": volume,
                    "ticker": ticker,
                    "exchange": exchange,
                    "timestamp": time.time()
                }
            }
            return payload, None
        except Exception:
            print(traceback.format_exc())
            if os.environ["PRODUCTION_MODE"]: self.logging.report_exception()
            return None, None

    def request_iexc_forex(self, request):
        ticker = request.get_ticker()
        exchange = request.get_exchange()

        try:
            try:
                if exchange is not None: return None, None
                rawData = requests.get(
                    "https://cloud.iexapis.com/stable/fx/latest?symbols={}&token={}"
                    .format(ticker.id, os.environ["IEXC_KEY"])).json()
                if rawData is None or type(rawData) is not list or len(
                        rawData) == 0:
                    return None, None
            except:
                return None, None

            price = rawData[0]["rate"]
            if ticker.isReversed: price = 1 / price

            payload = {
                "quotePrice": "{:,.5f}".format(price),
                "quoteVolume": None,
                "quoteConvertedPrice": None,
                "quoteConvertedVolume": None,
                "title": ticker.name,
                "baseTicker": ticker.base,
                "quoteTicker": ticker.quote,
                "change": None,
                "thumbnailUrl": static_storage.icon,
                "messageColor": "deep purple",
                "sourceText": "provided by IEX Cloud",
                "platform": "IEXC",
                "raw": {
                    "quotePrice": [price],
                    "quoteVolume": None,
                    "ticker": ticker,
                    "exchange": exchange,
                    "timestamp": time.time()
                }
            }
            return payload, None
        except Exception:
            print(traceback.format_exc())
            if os.environ["PRODUCTION_MODE"]: self.logging.report_exception()
            return None, None

    def request_fear_greed_index(self, request):
        try:
            requestUrl, _ = request.build_url()
            r = requests.get(requestUrl).json()
            greedIndex = int(r["data"][0]["value"])

            payload = {
                "quotePrice":
                greedIndex,
                "quoteVolume":
                None,
                "quoteConvertedPrice":
                "≈ {}".format(r["data"][0]["value_classification"].lower()),
                "quoteConvertedVolume":
                None,
                "title":
                "Fear & Greed Index",
                "baseTicker":
                None,
                "quoteTicker":
                None,
                "change":
                greedIndex - int(r["data"][1]["value"]),
                "thumbnailUrl":
                static_storage.icon,
                "messageColor":
                "deep purple",
                "sourceText":
                "Data provided by Alternative.me",
                "platform":
                "Alternative.me",
                "raw": {
                    "quotePrice": greedIndex,
                    "quoteVolume": None,
                    "ticker": request.get_ticker(),
                    "exchange": None,
                    "timestamp": time.time()
                }
            }
            return payload, None
        except Exception:
            print(traceback.format_exc())
            if os.environ["PRODUCTION_MODE"]: self.logging.report_exception()
            return None, None

    def request_ccxt_depth(self, request):
        ticker = request.get_ticker()
        exchange = request.get_exchange()

        try:
            if exchange is None: return None, None
            exchange = Exchange(exchange.id)

            try:
                depthData = exchange.properties.fetch_order_book(ticker.symbol)
                bestBid = depthData["bids"][0]
                bestAsk = depthData["asks"][0]
                lastPrice = (bestBid[0] + bestAsk[0]) / 2
            except:
                return None, None

            imageData = self.generate_depth_image(depthData, bestBid, bestAsk,
                                                  lastPrice)

            return imageData, None
        except Exception:
            print(traceback.format_exc())
            if os.environ["PRODUCTION_MODE"]: self.logging.report_exception()
            return None, None

    def request_iexc_depth(self, request):
        ticker = request.get_ticker()
        exchange = request.get_exchange()

        try:
            try:
                stock = Stock(ticker.id, token=os.environ["IEXC_KEY"])
                depthData = stock.get_book()[ticker.id]
                rawData = stock.get_quote().loc[ticker.id]
                if ticker.quote is None and exchange is not None:
                    return None, "Orderbook visualization for `{}` is only available on `{}`.".format(
                        ticker.id, rawData["primaryExchange"])
                bestBid = depthData["bids"][0]
                bestAsk = depthData["asks"][0]
                lastPrice = (bestBid[0] + bestAsk[0]) / 2
            except:
                return None, None

            imageData = self.generate_depth_image(depthData, bestBid, bestAsk,
                                                  lastPrice)

            return imageData, None
        except Exception:
            print(traceback.format_exc())
            if os.environ["PRODUCTION_MODE"]: self.logging.report_exception()
            return None, None

    def generate_depth_image(self, depthData, bestBid, bestAsk, lastPrice):
        bidTotal = 0
        xBids = [bestBid[0]]
        yBids = [0]
        for bid in depthData['bids']:
            if len(xBids) < 10 or bid[0] > lastPrice * 0.9:
                bidTotal += bid[1]
                xBids.append(bid[0])
                yBids.append(bidTotal)

        askTotal = 0
        xAsks = [bestAsk[0]]
        yAsks = [0]
        for ask in depthData['asks']:
            if len(xAsks) < 10 or ask[0] < lastPrice * 1.1:
                askTotal += ask[1]
                xAsks.append(ask[0])
                yAsks.append(askTotal)

        fig = plt.figure(facecolor="#131722")
        ax = fig.add_subplot(1, 1, 1)
        ax.tick_params(color="#787878", labelcolor="#D9D9D9")
        ax.step(xBids, yBids, where="post", color="#27A59A")
        ax.step(xAsks, yAsks, where="post", color="#EF534F")
        ax.fill_between(xBids,
                        yBids,
                        0,
                        facecolor="#27A59A",
                        interpolate=True,
                        step="post",
                        alpha=0.33,
                        zorder=2)
        ax.fill_between(xAsks,
                        yAsks,
                        0,
                        facecolor="#EF534F",
                        interpolate=True,
                        step="post",
                        alpha=0.33,
                        zorder=2)
        plt.axvline(x=lastPrice, color="#758696", linestyle="--")

        ax.set_facecolor("#131722")
        for spine in ax.spines.values():
            spine.set_edgecolor("#787878")
        ax.autoscale(enable=True, axis="both", tight=True)

        def on_draw(event):
            bboxes = []
            for label in ax.get_yticklabels():
                bbox = label.get_window_extent()
                bboxi = bbox.transformed(fig.transFigure.inverted())
                bboxes.append(bboxi)

            bbox = mtransforms.Bbox.union(bboxes)
            if fig.subplotpars.left < bbox.width:
                fig.subplots_adjust(left=1.1 * bbox.width)
                fig.canvas.draw()
            return False

        ax.yaxis.set_major_formatter(
            tkr.FuncFormatter(lambda x, p: format(int(x), ',')))
        plt.setp(ax.get_xticklabels(),
                 rotation=45,
                 horizontalalignment='right')
        lastPriceLabel = bestAsk[0] if bestAsk[1] >= bestBid[1] else bestBid[0]
        xLabels = list(plt.xticks()[0][1:])
        yLabels = list(plt.yticks()[0][1:])
        for label in xLabels:
            plt.axvline(x=label, color="#363C4F", linewidth=1, zorder=1)
        for label in yLabels:
            plt.axhline(y=label, color="#363C4F", linewidth=1, zorder=1)
        diffLabels = 1 - xLabels[0] / xLabels[1]
        bottomBound, topBound = lastPriceLabel * (
            1 - diffLabels * (1 / 4)), lastPriceLabel * (1 + diffLabels *
                                                         (1 / 4))
        xLabels = [l for l in xLabels if not (bottomBound <= l <= topBound)]

        plt.xticks(xLabels + [lastPriceLabel])
        plt.yticks(yLabels)
        ax.set_xlim([xBids[-1], xAsks[-1]])
        ax.set_ylim([0, max(bidTotal, askTotal)])

        fig.canvas.mpl_connect("draw_event", on_draw)
        plt.tight_layout()

        rawImageData = BytesIO()
        plt.savefig(rawImageData, format="png", edgecolor="none")
        rawImageData.seek(0)

        imageBuffer = BytesIO()
        chartImage = Image.new("RGBA", (1600, 1200))
        chartImage.paste(Image.open(rawImageData))
        chartImage = Image.alpha_composite(chartImage,
                                           self.imageOverlays["Alpha depth"])
        chartImage.save(imageBuffer, format="png")
        imageData = base64.b64encode(imageBuffer.getvalue())
        imageBuffer.close()

        return imageData
Esempio n. 24
0
class DetailProcessor(object):
    coinGecko = CoinGeckoAPI()

    def __init__(self):
        self.isServiceAvailable = True
        signal.signal(signal.SIGINT, self.exit_gracefully)
        signal.signal(signal.SIGTERM, self.exit_gracefully)

        self.logging = error_reporting.Client()
        self.cache = Cache()

        context = zmq.Context.instance()
        self.socket = context.socket(zmq.ROUTER)
        self.socket.bind("tcp://*:6900")

        print("[Startup]: Detail Server is online")

    def exit_gracefully(self):
        print("[Startup]: Detail Server is exiting")
        self.socket.close()
        self.isServiceAvailable = False

    def run(self):
        while self.isServiceAvailable:
            try:
                response = None, None
                origin, delimeter, clientId, service, request = self.socket.recv_multipart(
                )
                request = pickle.loads(zlib.decompress(request))
                if request.timestamp + 30 < time.time(): continue

                if service == b"detail":
                    response = self.request_detail(request)

            except (KeyboardInterrupt, SystemExit):
                return
            except Exception:
                print(traceback.format_exc())
                if os.environ["PRODUCTION_MODE"]:
                    self.logging.report_exception()
            finally:
                try:
                    self.socket.send_multipart([
                        origin, delimeter,
                        zlib.compress(pickle.dumps(response, -1))
                    ])
                except:
                    pass

    def request_detail(self, request):
        payload, tradeMessage, updatedTradeMessage = None, None, None

        for platform in request.platforms:
            request.set_current(platform=platform)
            hashCode = hash(request.requests[platform])
            fromCache = False

            if request.can_cache() and self.cache.has(hashCode):
                payload, updatedQuoteMessage = self.cache.get(hashCode), None
                fromCache = True
            elif platform == "CoinGecko":
                payload, updatedQuoteMessage = self.request_coingecko_details(
                    request)

            if payload is not None:
                if request.can_cache() and not fromCache:
                    self.cache.set(hashCode, payload)
                if request.authorId != 401328409499664394 and request.requests[
                        platform].ticker.base is not None:
                    database.document("dataserver/statistics/{}/{}".format(
                        platform, str(uuid.uuid4()))).set({
                            "timestamp":
                            time.time(),
                            "authorId":
                            str(request.authorId),
                            "ticker": {
                                "base": request.requests[platform].ticker.base,
                                "quote":
                                request.requests[platform].ticker.quote,
                                "id": request.requests[platform].ticker.id,
                                "bias": request.parserBias
                            },
                            "exchange":
                            None if request.requests[platform].exchange is None
                            else request.requests[platform].exchange.id
                        })
                return payload, updatedTradeMessage
            elif updatedTradeMessage is not None:
                tradeMessage = updatedTradeMessage

        return None, tradeMessage

    def request_coingecko_details(self, request):
        ticker = request.get_ticker()

        try:
            try:
                rawData = self.coinGecko.get_coin_by_id(id=ticker.symbol,
                                                        localization="false",
                                                        tickers=False,
                                                        market_data=True,
                                                        community_data=True,
                                                        developer_data=True)
            except:
                return None, None

            payload = {
                "name":
                "{} ({})".format(rawData["name"], ticker.base),
                "rank":
                rawData["market_data"]["market_cap_rank"],
                "image":
                rawData["image"]["large"],
                "marketcap":
                "" if rawData["market_data"]["market_cap"] is None else
                rawData["market_data"]["market_cap"].get("usd", ""),
                "volume":
                None if rawData["market_data"]["total_volume"] is None else
                rawData["market_data"]["total_volume"].get("usd"),
                "supply": {
                    "total":
                    None if rawData["market_data"]["total_supply"] is None else
                    rawData["market_data"]["total_supply"],
                    "circulating":
                    None
                    if rawData["market_data"]["circulating_supply"] is None
                    else rawData["market_data"]["circulating_supply"]
                },
                "score": {
                    "developer": rawData["developer_score"],
                    "community": rawData["community_score"],
                    "liquidity": rawData["liquidity_score"],
                    "public interest": rawData["public_interest_score"]
                },
                "price": {
                    "current":
                    rawData["market_data"]["current_price"].get("usd"),
                    "ath": rawData["market_data"]["ath"].get("usd"),
                    "atl": rawData["market_data"]["atl"].get("usd")
                },
                "change": {
                    "past day":
                    rawData["market_data"]
                    ["price_change_percentage_24h_in_currency"].get("usd"),
                    "past month":
                    rawData["market_data"]
                    ["price_change_percentage_30d_in_currency"].get("usd"),
                    "past year":
                    rawData["market_data"]
                    ["price_change_percentage_1y_in_currency"].get("usd")
                },
                "sourceText":
                "from CoinGecko",
                "platform":
                "CoinGecko",
            }
            return payload, None
        except Exception:
            print(traceback.format_exc())
            if os.environ["PRODUCTION_MODE"]: self.logging.report_exception()
            return None, None
Esempio n. 25
0
                print("ERR ERR wrong number of arguments for 'set' command")
                continue
            key = command[0]
            attributes = {}
            for i in range(0, len(command), 2):
                attributes[command[i]] = command[i + 1]

            # print(attributes)
            print(cache.set(key, attributes[key], attributes))

        elif option == "GET":
            if len(command) != 1:
                print("ERR ERR wrong number of arguments for 'get' command")
                continue
            key = command[0]
            print(cache.get(key))

        elif option == "EXPIRE":
            if len(command) != 2:
                print("ERR ERR wrong number of arguments for 'expire' command")
                continue
            key = command[0]
            time = int(command[1])
            print(cache.set_expire(key, time))

        elif option == "TTL":
            if len(command) != 1:
                print("ERR ERR wrong number of arguments for 'ttl' command")
                continue
            key = command[0]
            print(cache.get_TTL(key))
Esempio n. 26
0
class ChainDb(object):
    def __init__(self,
                 settings,
                 datadir,
                 log,
                 mempool,
                 netmagic,
                 readonly=False,
                 fast_dbm=False,
                 compression=False):
        self.settings = settings
        self.log = log
        self.mempool = mempool
        self.readonly = readonly
        self.netmagic = netmagic
        self.fast_dbm = fast_dbm
        self.blk_cache = Cache(1000)
        self.orphans = {}
        self.orphan_deps = {}
        self.compress_on_write = compression

        # LevelDB to hold:
        #    tx:*      transaction outputs
        #    misc:*    state
        #    height:*  list of blocks at height h
        #    blkmeta:* block metadata
        #    blocks:*  block seek point in stream
        self.blk_write = io.BufferedWriter(
            io.FileIO(datadir + '/blocks.dat', 'ab'))
        self.blk_read = io.BufferedReader(
            io.FileIO(datadir + '/blocks.dat', 'rb'))
        self.db = leveldb.LevelDB(datadir + '/leveldb')

        try:
            self.db.Get('misc:height')
        except KeyError:
            self.log.write("INITIALIZING EMPTY BLOCKCHAIN DATABASE")
            batch = leveldb.WriteBatch()
            batch.Put('misc:height', str(-1))
            batch.Put('misc:msg_start', self.netmagic.msg_start)
            batch.Put('misc:tophash', ser_uint256(0L))
            batch.Put('misc:total_work', hex(0L))
            self.db.Write(batch)

        try:
            start = self.db.Get('misc:msg_start')
            if start != self.netmagic.msg_start: raise KeyError
        except KeyError:
            self.log.write(
                "Database magic number mismatch. Data corruption or incorrect network?"
            )
            raise RuntimeError

    def puttxidx(self, txhash, txidx, batch=None):
        ser_txhash = ser_uint256(txhash)

        try:
            self.db.Get('tx:' + ser_txhash)
            old_txidx = self.gettxidx(txhash)
            self.log.write(
                "WARNING: overwriting duplicate TX %064x, height %d, oldblk %064x, oldspent %x, newblk %064x"
                % (txhash, self.getheight(), old_txidx.blkhash,
                   old_txidx.spentmask, txidx.blkhash))
        except KeyError:
            pass
        batch = self.db if batch is not None else batch
        batch.Put('tx:' + ser_txhash,
                  hex(txidx.blkhash) + ' ' + hex(txidx.spentmask))

        return True

    def gettxidx(self, txhash):
        ser_txhash = ser_uint256(txhash)
        try:
            ser_value = self.db.Get('tx:' + ser_txhash)
        except KeyError:
            return None

        pos = string.find(ser_value, ' ')

        txidx = TxIdx()
        txidx.blkhash = long(ser_value[:pos], 16)
        txidx.spentmask = long(ser_value[pos + 1:], 16)

        return txidx

    def gettx(self, txhash):
        txidx = self.gettxidx(txhash)
        if txidx is None:
            return None

        block = self.getblock(txidx.blkhash)
        if block:
            for tx in block.vtx:
                tx.calc_sha256()
                if tx.sha256 == txhash:
                    return tx

        self.log.write("ERROR: Missing TX %064x in block %064x" %
                       (txhash, txidx.blkhash))
        return None

    def haveblock(self, blkhash, checkorphans):
        if self.blk_cache.exists(blkhash):
            return True
        if checkorphans and blkhash in self.orphans:
            return True
        ser_hash = ser_uint256(blkhash)
        try:
            self.db.Get('blocks:' + ser_hash)
            return True
        except KeyError:
            return False

    def have_prevblock(self, block):
        if self.getheight() < 0 and block.sha256 == self.netmagic.block0:
            return True
        if self.haveblock(block.hashPrevBlock, False):
            return True
        return False

    def getblock(self, blkhash):
        block = self.blk_cache.get(blkhash)
        if block is not None:
            return block

        ser_hash = ser_uint256(blkhash)
        try:
            # Lookup the block index, seek in the file
            fpos = long(self.db.Get('blocks:' + ser_hash))
            self.blk_read.seek(fpos)

            # read and decode "block" msg

            recvbuf = self.blk_read.read(4 + 4)
            if recvbuf[:4] == 'ZLIB':
                msg_len = int(recvbuf[4:8].encode('hex'), 16)
                recvbuf = self.blk_read.read(msg_len)

                f = cStringIO.StringIO(zlib.decompress(recvbuf))
                msg = message_read(self.netmagic, f)
            else:
                self.blk_read.seek(fpos)
                msg = message_read(self.netmagic, self.blk_read)

            if msg is None:
                return None
            block = msg.block
        except KeyError:
            return None

        self.blk_cache.put(blkhash, block)

        return block

    def spend_txout(self, txhash, n_idx, batch=None):
        txidx = self.gettxidx(txhash)
        if txidx is None:
            return False

        txidx.spentmask |= (1L << n_idx)
        self.puttxidx(txhash, txidx, batch)

        return True

    def clear_txout(self, txhash, n_idx, batch=None):
        txidx = self.gettxidx(txhash)
        if txidx is None:
            return False

        txidx.spentmask &= ~(1L << n_idx)
        self.puttxidx(txhash, txidx, batch)

        return True

    def unique_outpts(self, block):
        outpts = {}
        txmap = {}
        for tx in block.vtx:
            if tx.is_coinbase:
                continue
            txmap[tx.sha256] = tx
            for txin in tx.vin:
                v = (txin.prevout.hash, txin.prevout.n)
                if v in outs:
                    return None

                outpts[v] = False

        return (outpts, txmap)

    def txout_spent(self, txout):
        txidx = self.gettxidx(txout.hash)
        if txidx is None:
            return None

        if txout.n > 100000:  # outpoint index sanity check
            return None

        if txidx.spentmask & (1L << txout.n):
            return True

        return False
Esempio n. 27
0
class Core(object):
    """ Starts the :class:`Cache`

    Arguments
    -----------
    db : :data:`bfly.Butterfly._db_type`
        A fully-loaded database

    Attributes
    ------------
    _db: :data:`bfly.Butterfly._db_type`
        Taken from first argument ``db``
    _cache: :class:`Cache`
        Able to store images and metadata using \
:class:`UtilityLayer.RUNTIME` instance \
from ``db`` argument
    """
    def __init__(self, db):
        # Get DB Terms
        self._db = db
        RUNTIME = db.RUNTIME

        # Make Cache with keywords
        self._cache = Cache(RUNTIME)

    #####
    # All methods to load data
    #     1. get_info answers an InfoQuery i_query.
    #     2. get_data answers a DataQuery d_query.
    #
    # Both get_info or get_data call update_query.
    #
    # To give answers , update_query uses _cache or:
    #     1. make_data_query if only i_query given.
    #     2. make_tile_query with new or given d_query.
    #####

    @staticmethod
    def get_groups(i_query):
        """ dumps group list for ``i_query`` as a string

        Arguments
        ----------
        i_query: :class:`QueryLayer.InfoQuery`
            A request for a list of groups

        Returns
        --------
        str
            A list of all groups for the ``i_query``
        """
        return i_query.dump

    def get_edits(self, i_query, msg={}):
        """ dumps websocket updates to ``i_query`` as a string

        Calls :meth:`update_query` with more information\
from the cache or from the properties of a tile.

        Arguments
        ----------
        i_query: :class:`QueryLayer.InfoQuery`
            A request for information

        Returns
        --------
        str
            Wesocket info for :class:`QueryLayer.InfoQuery`
        """
        keywords = self.update_query(i_query)
        # Update current query with preloaded terms
        i_query.update_source(keywords)
        # Execute weboscket command if needed
        changed = i_query.websocket_edit(msg)
        # Add to cache and query
        if len(changed):
            keywords.update(changed)
            i_query.update_source(keywords)
            self._cache.set(i_query.key, keywords)

        # Return the i_query info
        return i_query.dump

    def get_info(self, i_query):
        """ dumps answer to ``i_query`` as a string

        Calls :meth:`update_query` with more information\
from the cache or from the properties of a tile.

        Arguments
        ----------
        i_query: :class:`QueryLayer.InfoQuery`
            A request for information

        Returns
        --------
        str
            Channel info for :class:`QueryLayer.InfoQuery`
        """
        keywords = self.update_query(i_query)
        # Update current query with preloaded terms
        i_query.update_source(keywords)
        # Return the i_query info
        return i_query.dump

    def get_dataset(self, i_query):
        """ dumps dataset from ``i_query`` as a string

        Calls :meth:`update_query` with more information\
from the cache or from the properties of a tile.

        Arguments
        ----------
        i_query: :class:`QueryLayer.InfoQuery`
            A request for information

        Returns
        --------
        str
            Dataset info for :class:`QueryLayer.InfoQuery`
        """
        all_channels = []
        # Update all the channels in the dataset
        for channel in i_query.channels:
            # Set the i_query to a given channel
            i_query.set_channel(channel)
            # Update the info for the channel
            keywords = self.update_query(i_query)
            # Add additionl keywords if needed
            channel_key = i_query.OUTPUT.INFO.CHANNEL.NAME
            keywords[channel_key] = channel[channel_key]
            # Add to list of channels
            all_channels.append(keywords)
        # Clear the channel data
        i_query.set_channel({})
        # Update current query with preloaded terms
        return i_query.dump_dataset(all_channels)

    def get_data(self, d_query):
        """ dumps answer to ``d_query`` as a string

        Calls :meth:`update_query` with more information\
from the cache or from the properties of a tile. \
Also calls :meth:`find_tiles` to get the complete\
image needed to answer the ``d_query``.

        Arguments
        ----------
        i_query: :class:`QueryLayer.InfoQuery`
            A request for information

        Returns
        --------
        str
            Answer for the :class:`QueryLayer.InfoQuery`
        """

        keywords = self.update_query(d_query)
        # Update current query with preloaded terms
        d_query.update_source(keywords)
        # Get the image for the d_query
        image = self.find_tiles(d_query)
        return self.write_image(d_query, image)

    @staticmethod
    def make_data_query(i_query):
        """ Make a data query from an info query

        Arguments
        ----------
        i_query: :class:`InfoQuery`
            only needs ``PATH`` set in :data:`OUTPUT.INFO`

        Returns
        --------
        :class:`DataQuery`
            takes only the `PATH` from ``i_query``

        """
        # Begin building needed keywords
        i_path = i_query.OUTPUT.INFO.PATH

        return DataQuery(**{
            i_query.INPUT.METHODS.NAME: 'data',
            i_path.NAME: i_path.VALUE
        })

    @staticmethod
    def make_tile_query(d_query, t_index=np.uint32([0,0,0])):
        """ Make a :class:`TileQuery` from :class:`DataQuery`

        Arguments
        ----------
        d_query: :class:`DataQuery`
            only needs ``PATH`` set in :data:`OUTPUT.INFO`
        t_index: numpy.ndarray
            The 3x1 count of tiles form the origin

        Returns
        --------
        :class:`TileQuery`
            One tile request in the given data request
        """
        tile_crop = d_query.all_in_some(t_index)
        return TileQuery(d_query, t_index, tile_crop)

    def update_query(self, query):
        """ Finds missing query details from cache or tile

        Makes ``keywords`` from either the :data:`_cache` or \
from a new :class:`TileQuery` to update the given ``query``

        Arguments
        ----------
        query: :class:`Query`
            Either an :class:`InfoQuery` or a :class:`DataQuery`

        Returns
        --------
        keywords: dict
            Can pass to :meth:`Query.update_source` or combine \
to pass to :meth:`Query.update_dataset`.
        """
        keywords = self._cache.get(query.key)
        if not len(keywords):
            d_query = query
            # Create a preparatory data_query
            if not isinstance(query, DataQuery):
                d_query = self.make_data_query(query)
            # Create a preparatory tile_query
            t0_query = self.make_tile_query(d_query)
            # Update keywords and set the cache
            keywords = t0_query.preload_source
            self._cache.set(query.key, keywords)
        # Return the updated keywords
        return keywords

    #####
    # Image Specific Methods
    #####

    def find_tiles(self, d_query):
        """ Load the requested image for a :class:`DataQuery`

        Arguments
        ----------
        d_query: :class:`DataQuery`
            Request for a scaled subvolume of a source image

        Returns
        numpy.ndarray
            The full image data for the requested region
        """
        first_tile_index = d_query.tile_bounds[0]
        all_tiles = np.argwhere(np.ones(d_query.tile_shape))
        cutout = np.zeros(d_query.target_shape, d_query.dtype)
        tiles_needed = first_tile_index + all_tiles

        for t_index in tiles_needed:
            # Make a query for the given tile
            t_query = self.make_tile_query(d_query, t_index)
            tile = self.load_tile(t_query)
            if not len(tile):
                continue
            # Fill the tile into the full cutout
            to_cut = [t_query.target_origin, tile.shape]
            [Z0,Y0,X0],[Z1,Y1,X1] = d_query.some_in_all(*to_cut)
            cutout[Z0:Z1,Y0:Y1,X0:X1] = tile

        return cutout

    def find_unique(self, d_query):
        """ Get unique values for a :class:`DataQuery`

        Arguments
        ----------
        d_query: :class:`DataQuery`
            Request for a scaled subvolume of a source image

        Returns
        set
            The set of unique values for the request
        """
        first_tile_index = d_query.tile_bounds[0]
        all_tiles = np.argwhere(np.ones(d_query.tile_shape))
        tiles_needed = first_tile_index + all_tiles
        # Set of all unique values
        unique = set()

        for t_index in tiles_needed:
            # Make a query for the given tile
            t_query = self.make_tile_query(d_query, t_index)
            tile = self.load_tile(t_query)
            # Union of unique values with the full set
            tile_bins = np.bincount(tile.flatten()) > 0
            unique = unique | set(np.where(tile_bins)[0])

        return unique

    def load_tile(self, t_query):
        """ Load a single tile from the cache or from disk

        Arguments
        ----------
        t_query: :class:`TileQuery`
            With tile coordinates and volume within the tile

        Returns
        --------
        numpy.ndarray
            The subregion image data for the requested tile
        """
        # grab request size for query
        t_bounds = t_query.target_bounds
        t_origin = t_query.target_tile_bounds[0]
        (K0,J0,I0),(K1,J1,I1) = t_bounds-t_origin

        # Load from cache or from disk if needed
        cache_tile = self._cache.get(t_query.key)
        if len(cache_tile):
            return cache_tile[K0:K1,J0:J1,I0:I1]
        # Load from disk
        tile = t_query.tile
        if not len(tile):
            return []

        self._cache.set(t_query.key, tile)

        return tile[K0:K1,J0:J1,I0:I1]

    @staticmethod
    def view_volume(view, vol):
        """ Display a volume in color or grayscale

        Arguments
        ----------
        view: str
            The requested color or gray view of the data
        vol: str
            Raw volume from :class:`Cache` / :class:`Datasource`

        Returns
        --------
        numpy.ndarray
            Colorized or original raw volume
        """
        # Set up a colormap
        def id_to_color(vol):
            colors = np.zeros((3,)+ vol.shape).astype(np.uint8)
            colors[0] = np.mod(107 * vol, 700).astype(np.uint8)
            colors[1] = np.mod(509 * vol, 900).astype(np.uint8)
            colors[2] = np.mod(200 * vol, 777).astype(np.uint8)
            return np.moveaxis(colors,0,-1)

        # Colormap if a colormap view
        if view.VALUE == view.COLOR.NAME:
            return id_to_color(vol)
        return vol

    def write_image(self, d_query, volume):
        """ Format a volume for a given :class:`DataQuery`

        Arguments
        ----------
        d_query: :class:`DataQuery`
            With the format and view for the requested volume
        volume: numpy.ndarray
            Raw volume from :class:`Cache` / :class:`Datasource`

        Returns
        --------
        str:
            The image response as a formatted bytestring
        """
        img_format = d_query.INPUT.IMAGE.FORMAT
        img_view = d_query.INPUT.IMAGE.VIEW
        img_type = d_query.OUTPUT.INFO.TYPE

        # Only if grayscale view is set
        if img_view.VALUE == img_view.GRAY.NAME:
            # set the view based on the format
            is_big_int = img_type.VALUE in img_type.ID_LIST
            no_big_int_gray = img_format.VALUE in img_format.COLOR_LIST
            # If big integers must not be grayscale, try colormap
            if is_big_int and no_big_int_gray:
                img_view.VALUE = img_view.COLOR.NAME

        # If Multiple slices cannot be formatted
        if img_format.VALUE not in img_format.VOL_LIST:
            shape = volume.shape
            if shape[0] > 1:
                # Flatten the volume to image
                volume = volume.reshape(1, -1, shape[-1])
        # Use colormap / RGB style encoding of ID data
        vol = self.view_volume(img_view, volume)

        if img_format.VALUE in ['raw']:
            output = StringIO.StringIO()
            output.write(vol.tobytes())
            vol_string = output.getvalue()
            return vol_string

        if img_format.VALUE in ['npz']:
            output = StringIO.StringIO()
            np.save(output, vol[np.newaxis])
            vol_string = output.getvalue()
            return zlib.compress(vol_string)

        if img_format.VALUE in img_format.ZIP_LIST:
            output = StringIO.StringIO()
            volstring = vol[0].T.tostring('F')
            output.write(zlib.compress(volstring))
            return output.getvalue()

        if img_format.VALUE in img_format.TIF_LIST:
            output = StringIO.StringIO()
            tiffvol = vol[0]
            tifffile.imsave(output, tiffvol)
            return output.getvalue()

        filetype = "." + img_format.VALUE
        image = cv2.imencode(filetype, vol[0])
        return image[1].tostring()