Esempio n. 1
0
    def __init__(self, data_directory, history_date_start=DEFAULT_START_DATE):
        self.data_directory = data_directory
        # get the start date for historical data
        self.historical_data_start = createTimeStamp(history_date_start, formatstr="%d/%m/%Y")

        self.price_history = dict()
        # TODO: Check if historical data is after the requested start date
        # Check the data folder and load any cached history
        prefix = os.path.join(self.data_directory, 'price_history_')
        regex = re.compile(prefix + '(.*)\.json')
        files_list = glob.glob(prefix + '*.json')
        for file_ in files_list:
            match = regex.match(file_)
            assert match
            cache_key = match.group(1)
            with open(file_, 'rb') as f:
                data = rlk_jsonloads(f.read())
                self.price_history[cache_key] = data

        # Get coin list of crypto compare
        invalidate_cache = True
        coinlist_cache_path = os.path.join(self.data_directory, 'cryptocompare_coinlist.json')
        if os.path.isfile(coinlist_cache_path):
            with open(coinlist_cache_path, 'rb') as f:
                try:
                    data = rlk_jsonloads(f.read())
                    now = ts_now()
                    invalidate_cache = False

                    # If we got a cache and its' over a month old then requery cryptocompare
                    if data['time'] < now and now - data['time'] > 2629800:
                        invalidate_cache = True
                        data = data['data']
                except JSONDecodeError:
                    invalidate_cache = True

        if invalidate_cache:
            query_string = 'https://www.cryptocompare.com/api/data/coinlist/'
            resp = urlopen(Request(query_string))
            resp = rlk_jsonloads(resp.read())
            if 'Response' not in resp or resp['Response'] != 'Success':
                error_message = 'Failed to query cryptocompare for: "{}"'.format(query_string)
                if 'Message' in resp:
                    error_message += ". Error: {}".format(resp['Message'])
                raise ValueError(error_message)
            data = resp['Data']

            # Also save the cache
            with open(coinlist_cache_path, 'w') as f:
                write_data = {'time': ts_now(), 'data': data}
                f.write(rlk_jsondumps(write_data))
        else:
            # in any case take the data
            data = data['data']

        self.cryptocompare_coin_list = data
        # For some reason even though price for the following assets is returned
        # it's not in the coinlist so let's add them here.
        self.cryptocompare_coin_list['DAO'] = object()
        self.cryptocompare_coin_list['USDT'] = object()
Esempio n. 2
0
    def _api_query(self, command: str, req: Optional[Dict] = None) -> Dict:
        if req is None:
            req = {}

        if command == "returnTicker":
            log.debug('Querying poloniex for returnTicker')
            ret = self.session.get(self.public_uri + command)
        else:
            req['command'] = command

            with self.lock:
                # Protect this region with a lock since poloniex will reject
                # non-increasing nonces. So if two greenlets come in here at
                # the same time one of them will fail
                req['nonce'] = int(time.time() * 1000)
                post_data = str.encode(urlencode(req))

                sign = hmac.new(self.secret, post_data,
                                hashlib.sha512).hexdigest()
                self.session.headers.update({'Sign': sign})

                log.debug(
                    'Poloniex private API query',
                    command=command,
                    post_data=req,
                )
                ret = self.session.post('https://poloniex.com/tradingApi', req)

            result = rlk_jsonloads(ret.text)
            return self.post_process(result)

        return rlk_jsonloads(ret.text)
Esempio n. 3
0
    def _api_query(self, command, req={}):
        if command == "returnTicker" or command == "return24Volume":
            ret = self.session.get(self.public_uri + command)
        elif (command == "returnOrderBook"):
            ret = self.session.get(self.public_uri + command +
                                   '&currencyPair=' + str(req['currencyPair']))
        elif (command == "returnMarketTradeHistory"):
            ret = self.session.get(self.public_uri + 'returnTradeHistory' +
                                   '&currencyPair=' + str(req['currencyPair']))
        elif (command == "returnLoanOrders"):
            ret = self.session.get(self.public_uri + 'returnLoanOrders' +
                                   '&currency=' + str(req['currency']))
        else:
            req['command'] = command

            with self.lock:
                # Protect this region with a lock since poloniex will reject
                # non-increasing nonces. So if two greenlets come in here at
                # the same time one of them will fail
                req['nonce'] = int(time.time() * 1000)
                post_data = str.encode(urlencode(req))

                sign = hmac.new(self.secret, post_data,
                                hashlib.sha512).hexdigest()
                self.session.headers.update({'Sign': sign})
                ret = self.session.post('https://poloniex.com/tradingApi', req)

            result = rlk_jsonloads(ret.text)
            return self.post_process(result)

        return rlk_jsonloads(ret.text)
Esempio n. 4
0
def do_read_manual_margin_positions(data_directory):
    manual_margin_path = os.path.join(data_directory, MANUAL_MARGINS_LOGFILE)
    if os.path.isfile(manual_margin_path):
        with open(manual_margin_path, 'r') as f:
            margin_data = rlk_jsonloads(f.read())
    else:
        margin_data = []
        logger.info('Could not find manual margins log file at {}'.format(
            manual_margin_path))

    # Now turn the manual margin data to our MarginPosition format
    # The poloniex manual data format is:
    # { "open_time": unix_timestamp, "close_time": unix_timestamp,
    #   "btc_profit_loss": floating_point_number for profit or loss,
    #   "notes": "optional string with notes on the margin position"
    # }
    margin_positions = list()
    for position in margin_data:
        margin_positions.append(
            MarginPosition(
                exchange='poloniex',
                open_time=position['open_time'],
                close_time=position['close_time'],
                profit_loss=FVal(position['btc_profit_loss']),
                pl_currency=NonEthTokenBlockchainAsset('BTC'),
                notes=position['notes'],
            ))

    return margin_positions
Esempio n. 5
0
    def __init__(self, ethrpc_port):
        self.connected = True
        # Note that you should create only one RPCProvider per
        # process, as it recycles underlying TCP/IP network connections between
        # your process and Ethereum node
        self.web3 = Web3(
            HTTPProvider('http://localhost:{}'.format(ethrpc_port)))
        try:
            self.web3.eth.blockNumber
            dir_path = os.path.dirname(os.path.realpath(__file__))
            with open(os.path.join(dir_path, 'data', 'token_abi.json'),
                      'r') as f:
                self.token_abi = rlk_jsonloads(f.read())

            # Also make sure we are actually connected to the Ethereum mainnet
            genesis_hash = self.web3.eth.getBlock(0)['hash']
            target = '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3'
            if genesis_hash != target:
                logger.warn(
                    'Connected to a local ethereum node but it is not on the ethereum mainnet'
                )
                self.connected = False
        except ConnectionError:
            logger.warn(
                'Could not connect to a local ethereum node. Will use etherscan only'
            )
            self.connected = False
Esempio n. 6
0
def test_decoding():
    strdata = """
    {"a": 3.14, "b":5, "c": "foo", "d": "5.42323143", "e": { "u1": "3.221"}, "f": [2.1, "boo", 3, "4.2324"]}"""

    data = rlk_jsonloads(strdata)
    assert isinstance(data['a'], FVal)
    assert data['a'] == FVal('3.14')
    assert isinstance(data['b'], int)
    assert data['b'] == 5
    assert isinstance(data['c'], (str, bytes))
    assert data['c'] == 'foo'
    assert isinstance(data['d'], FVal)
    assert data['d'] == FVal('5.42323143')

    assert isinstance(data['e']['u1'], FVal)
    assert data['e']['u1'] == FVal('3.221')

    assert isinstance(data['f'][0], FVal)
    assert data['f'][0] == FVal('2.1')
    assert isinstance(data['f'][1], (str, bytes))
    assert data['f'][1] == "boo"
    assert isinstance(data['f'][2], int)
    assert data['f'][2] == 3
    assert isinstance(data['f'][3], FVal)
    assert data['f'][3] == FVal('4.2324')
Esempio n. 7
0
    def get_multieth_balance(self, accounts):
        """Returns a dict with keys being accounts and balances in ETH"""
        balances = {}
        if not self.connected:
            # TODO: accounts.length should be less than 20. If more we gotta do
            # multiple calls
            eth_resp = urlopen(
                Request(
                    'https://api.etherscan.io/api?module=account&action=balancemulti&address=%s'
                    % ','.join(accounts)))
            eth_resp = rlk_jsonloads(eth_resp.read())
            if eth_resp['status'] != 1:
                raise ValueError(
                    'Failed to query etherscan for accounts balance')
            eth_accounts = eth_resp['result']
            for account_entry in eth_accounts:
                amount = FVal(account_entry['balance'])
                balances[account_entry['account']] = from_wei(amount)

        else:
            for account in accounts:
                amount = FVal(self.web3.eth.getBalance(account))
                balances[account] = from_wei(amount)

        return balances
Esempio n. 8
0
    def find_usd_price(self, asset, asset_btc_price=None):
        if self.kraken and self.kraken.first_connection_made and asset_btc_price is not None:
            return self.query_kraken_for_price(asset, asset_btc_price)

        # Adjust some ETH tokens to how cryptocompare knows them
        if asset == 'RDN':
            asset = 'RDN*'  # temporary
        if asset == 'DATAcoin':
            asset = 'DATA'
        resp = retry_calls(
            5,
            'find_usd_price',
            'urllib2.urlopen',
            urlopen,
            Request(
                u'https://min-api.cryptocompare.com/data/price?fsym={}&tsyms=USD'.format(
                    asset
                ))
        )

        resp = rlk_jsonloads(resp.read())

        # If there is an error in the response skip this token
        if 'USD' not in resp:
            if resp['Response'] == 'Error':
                print('Could not query USD price for {}. Error: "{}"'.format(
                    asset,
                    resp['Message']),
                )
            else:
                print('Could not query USD price for {}'.format(asset))
            return FVal(0)

        return FVal(resp['USD'])
Esempio n. 9
0
    def check_and_get_response(self, response, method):
        if response.status_code in (520, 525, 504):
            raise RecoverableRequestError('kraken', 'Usual kraken 5xx shenanigans')
        elif response.status_code != 200:
            raise ValueError(
                'Kraken API request {} for {} failed with HTTP status '
                'code: {}'.format(
                    response.url,
                    method,
                    response.status_code,
                ))

        result = rlk_jsonloads(response.text)
        if result['error']:
            if isinstance(result['error'], list):
                error = result['error'][0]
            else:
                error = result['error']

            if 'Rate limit exceeded' in error:
                raise RecoverableRequestError('kraken', 'Rate limited exceeded')
            else:
                raise ValueError(error)

        return result['result']
Esempio n. 10
0
    def find_usd_price(self, asset, asset_btc_price=None):
        if self.kraken and self.kraken.first_connection_made and asset_btc_price is not None:
            return self.query_kraken_for_price(asset, asset_btc_price)

        # Adjust some ETH tokens to how cryptocompare knows them
        if asset == 'RDN':
            asset = 'RDN*'  # temporary
        if asset == 'DATAcoin':
            asset = 'DATA'
        resp = retry_calls(
            5, 'find_usd_price', 'requests.get', requests.get,
            u'https://min-api.cryptocompare.com/data/price?'
            'fsym={}&tsyms=USD'.format(asset))

        if resp.status_code != 200:
            raise RemoteError(
                'Cant reach cryptocompare to get USD value of {}'.format(
                    asset))

        resp = rlk_jsonloads(resp.text)

        # If there is an error in the response skip this token
        if 'USD' not in resp:
            if resp['Response'] == 'Error':
                print(
                    'Could not query USD price for {}. Error: "{}"'.format(
                        asset, resp['Message']), )
            else:
                print('Could not query USD price for {}'.format(asset))
            return FVal(0)

        return FVal(resp['USD'])
Esempio n. 11
0
    def api_query(self, method, options=None):
        """
        Queries Bittrex with given method and options
        """
        if not options:
            options = {}
        nonce = str(int(time.time() * 1000))
        method_type = 'public'

        if method in BITTREX_MARKET_METHODS:
            method_type = 'market'
        elif method in BITTREX_ACCOUNT_METHODS:
            method_type = 'account'

        request_url = self.uri + method_type + '/' + method + '?'

        if method_type != 'public':
            request_url += 'apikey=' + self.api_key.decode() + "&nonce=" + nonce + '&'

        request_url += urlencode(options)
        signature = hmac.new(
            self.secret,
            request_url.encode(),
            hashlib.sha512
        ).hexdigest()
        self.session.headers.update({'apisign': signature})
        response = self.session.get(request_url)
        json_ret = rlk_jsonloads(response.text)
        if json_ret['success'] is not True:
            raise ValueError(json_ret['message'])
        return json_ret['result']
Esempio n. 12
0
    def api_query(self,
                  method: str,
                  options: Optional[Dict] = None) -> Union[List, Dict]:
        if not options:
            options = {}

        with self.lock:
            # Protect this region with a lock since binance will reject
            # non-increasing nonces. So if two greenlets come in here at
            # the same time one of them will fail
            if method in V3_ENDPOINTS:
                api_version = 3
                # Recommended recvWindows is 5000 but we get timeouts with it
                options['recvWindow'] = 10000
                options['timestamp'] = str(int(time.time() * 1000))
                signature = hmac.new(self.secret,
                                     urlencode(options).encode('utf-8'),
                                     hashlib.sha256).hexdigest()
                options['signature'] = signature
            elif method in V1_ENDPOINTS:
                api_version = 1
            else:
                raise ValueError(
                    'Unexpected binance api method {}'.format(method))

            request_url = self.uri + 'v' + str(
                api_version) + '/' + method + '?'
            request_url += urlencode(options)

            log.debug('Binance API request', request_url=request_url)

            response = self.session.get(request_url)

        if response.status_code != 200:
            result = rlk_jsonloads(response.text)
            raise RemoteError(
                'Binance API request {} for {} failed with HTTP status '
                'code: {}, error code: {} and error message: {}'.format(
                    response.url,
                    method,
                    response.status_code,
                    result['code'],
                    result['msg'],
                ))

        json_ret = rlk_jsonloads(response.text)
        return json_ret
Esempio n. 13
0
    def __init__(self, data_directory):

        self.data_directory = data_directory
        self.db = None
        self.eth_tokens = []
        dir_path = os.path.dirname(os.path.realpath(__file__))
        with open(os.path.join(dir_path, 'data', 'eth_tokens.json'), 'r') as f:
            self.eth_tokens = rlk_jsonloads(f.read())
Esempio n. 14
0
    def query_private(self, method: str, req: Optional[dict] = None) -> dict:
        self.first_connection()
        if method == 'Balance':
            return generate_random_kraken_balance_response()
        elif method == 'TradesHistory':
            trades_num = random.randint(1, 49)
            start = req['start']
            end = req['end']

            # Trades is a dict with txid as the key
            trades = {}
            for _ in range(trades_num):
                trade = generate_random_kraken_trade_data(
                    list(self.tradeable_pairs.keys()),
                    start,
                    end,
                )
                trades[trade['ordertxid']] = trade

            response_str = json.dumps({'trades': trades, 'count': trades_num})
            return rlk_jsonloads(response_str)
        elif method == 'Ledgers':
            ledgers_num = random.randint(1, 49)
            start = req['start']
            end = req['end']
            ledger_type = req['type']
            # Ledgers is a dict with txid as the key
            ledgers = {}
            for _ in range(ledgers_num):
                ledger = generate_random_kraken_ledger_data(
                    start_ts=start,
                    end_ts=end,
                    ledger_type=ledger_type,
                )
                ledgers[ledger['refid']] = ledger

            response_str = json.dumps({
                'ledger': ledgers,
                'count': ledgers_num
            })
            return rlk_jsonloads(response_str)

        return super().query_private(method, req)
Esempio n. 15
0
def do_read_manual_margin_positions(data_directory):
    manual_margin_path = os.path.join(data_directory, MANUAL_MARGINS_LOGFILE)
    if os.path.isfile(manual_margin_path):
        with open(manual_margin_path, 'r') as f:
            margin_data = rlk_jsonloads(f.read())
    else:
        margin_data = []
        logger.error('Could not find manual margins log file at {}'.format(
            manual_margin_path))
    return margin_data
Esempio n. 16
0
    def __init__(self, data_directory):

        self.data_directory = data_directory

        try:
            with open(os.path.join(self.data_directory, 'settings.json')) as f:
                self.settings = rlk_jsonloads(f.read())
        except JSONDecodeError as e:
            logger.critical(
                'settings.json file could not be decoded and is corrupt: {}'.
                format(e))
            self.settings = empty_settings
        except FileNotFoundError:
            self.settings = empty_settings

        self.db = None
        self.eth_tokens = []
        dir_path = os.path.dirname(os.path.realpath(__file__))
        with open(os.path.join(dir_path, 'data', 'eth_tokens.json'), 'r') as f:
            self.eth_tokens = rlk_jsonloads(f.read())
Esempio n. 17
0
    def __init__(self, data_dir, kraken=None):
        self.kraken = kraken
        self.session = requests.session()
        self.data_directory = data_dir

        filename = os.path.join(self.data_directory, 'price_history_forex.json')
        try:
            with open(filename, 'rb') as f:
                data = rlk_jsonloads(f.read())
                self.cached_forex_data = data
        except (OSError, IOError, JSONDecodeError):
            self.cached_forex_data = dict()
Esempio n. 18
0
    def attempt_connect(self,
                        ethrpc_port,
                        mainnet_check=True) -> Tuple[bool, str]:
        if self.rpc_port == ethrpc_port and self.connected:
            # We are already connected
            return True, 'Already connected to an ethereum node'

        if self.web3:
            del self.web3

        try:
            self.web3 = Web3(
                HTTPProvider('http://localhost:{}'.format(ethrpc_port)))
        except ConnectionError:
            logger.warn(
                'Could not connect to a local ethereum node. Will use etherscan only'
            )
            self.connected = False
            return False, 'Failed to connect to ethereum node at port {}'.format(
                ethrpc_port)

        if self.web3.isConnected():
            dir_path = os.path.dirname(os.path.realpath(__file__))
            with open(os.path.join(dir_path, 'data', 'token_abi.json'),
                      'r') as f:
                self.token_abi = rlk_jsonloads(f.read())

            # Also make sure we are actually connected to the Ethereum mainnet
            if mainnet_check:
                genesis_hash = self.web3.eth.getBlock(0)['hash'].hex()
                target = '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3'
                if genesis_hash != target:
                    logger.warn(
                        'Connected to a local ethereum node but it is not on the ethereum mainnet'
                    )
                    self.connected = False
                    message = (
                        'Connected to ethereum node at port {} but it is not on '
                        'the ethereum mainnet'.format(ethrpc_port))
                    return False, message

            self.connected = True
            return True, ''
        else:
            logger.warn(
                'Could not connect to a local ethereum node. Will use etherscan only'
            )
            self.connected = False
            message = 'Failed to connect to ethereum node at port {}'.format(
                ethrpc_port)

        # If we get here we did not connnect
        return False, message
Esempio n. 19
0
 def get_eth_balance(self, account):
     if not self.connected:
         eth_resp = urlopen(
             Request(
                 'https://api.etherscan.io/api?module=account&action=balance&address=%s'
                 % account))
         eth_resp = rlk_jsonloads(eth_resp.read())
         if eth_resp['status'] != 1:
             raise ValueError(
                 'Failed to query etherscan for accounts balance')
         amount = FVal(eth_resp['result'])
         return from_wei(amount)
     else:
         return from_wei(self.web3.eth.getBalance(account))
Esempio n. 20
0
    def check_trades_cache(self, start_ts, end_ts, special_name=None):
        trades_file = self._get_cachefile_name(special_name)
        trades = dict()
        if os.path.isfile(trades_file):
            with open(trades_file, 'r') as f:
                try:
                    trades = rlk_jsonloads(f.read())
                except JSONDecodeError:
                    pass

                # no need to query again
                if data_up_todate(trades, start_ts, end_ts):
                    return trades['data']

        return None
Esempio n. 21
0
    def process_response(self, response):
        result_or_error = ''
        success = False
        if response.status_code not in HANDLABLE_STATUS_CODES:
            result_or_error = (
                'Unexpected status response({}) from rotkehlchen server'.
                format(response.status_code))
        else:
            result_or_error = rlk_jsonloads(response.text)
            if 'error' in result_or_error:
                result_or_error = result_or_error['error']
            else:
                success = True

        return success, result_or_error
Esempio n. 22
0
    def got_cached_price(self, cache_key, timestamp):
        """Check if we got a price history for the timestamp cached"""
        if cache_key in self.price_history_file:
            if cache_key not in self.price_history:
                try:
                    with open(self.price_history_file[cache_key], 'rb') as f:
                        data = rlk_jsonloads(f.read())
                        self.price_history[cache_key] = data
                except (OSError, IOError, JSONDecodeError):
                    return False

            in_range = (
                self.price_history[cache_key]['start_time'] <= timestamp
                and self.price_history[cache_key]['end_time'] > timestamp)
            if in_range:
                return True

        return False
Esempio n. 23
0
def _process_response(
        response: requests.Response) -> Tuple[bool, Union[Dict, List, str]]:
    """Processess a response returned from the Rotkehlchen server and returns
    the result for success or the error string if an error happened"""
    result_or_error: Union[Dict, List, str] = ''
    success = False
    if response.status_code not in HANDLABLE_STATUS_CODES:
        result_or_error = (
            'Unexpected status response({}) from rotkehlchen server'.format(
                response.status_code))
    else:
        result_or_error = rlk_jsonloads(response.text)
        if isinstance(result_or_error, Dict) and 'error' in result_or_error:
            result_or_error = result_or_error['error']
        else:
            success = True

    return success, result_or_error
Esempio n. 24
0
def query_txlist(address, internal, from_block=None, to_block=None):
    result = list()
    if internal:
        reqstring = ('https://api.etherscan.io/api?module=account&action='
                     'txlistinternal&address={}'.format(address))
    else:
        reqstring = ('https://api.etherscan.io/api?module=account&action='
                     'txlist&address={}'.format(address))
    if from_block:
        reqstring += '&startblock={}'.format(from_block)
    if to_block:
        reqstring += '&endblock={}'.format(to_block)

    resp = urlopen(Request(reqstring))
    resp = rlk_jsonloads(resp.read())

    if 'status' not in resp or convert_to_int(resp['status']) != 1:
        status = convert_to_int(resp['status'])
        if status == 0 and resp['message'] == 'No transactions found':
            return list()

        # else unknown error
        raise ValueError(
            'Failed to query txlist from etherscan with query: {} . '
            'Response was: {}'.format(reqstring, resp))

    for v in resp['result']:
        # internal tx list contains no gasprice
        gas_price = -1 if internal else FVal(v['gasPrice'])
        result.append(
            EthereumTransaction(
                timestamp=convert_to_int(v['timeStamp']),
                block_number=convert_to_int(v['blockNumber']),
                hash=v['hash'],
                from_address=v['from'],
                to_address=v['to'],
                value=FVal(v['value']),
                gas=FVal(v['gas']),
                gas_price=gas_price,
                gas_used=FVal(v['gasUsed']),
            ))

    return result
Esempio n. 25
0
    def check_trades_cache(
            self,
            start_ts: typing.Timestamp,
            end_ts: typing.Timestamp,
            special_name: str = None) -> Optional[Union[List, Dict]]:
        trades_file = self._get_cachefile_name(special_name)
        trades: dict = dict()
        if os.path.isfile(trades_file):
            with open(trades_file, 'r') as f:
                try:
                    trades = rlk_jsonloads(f.read())
                except JSONDecodeError:
                    pass

                # no need to query again
                if data_up_todate(trades, start_ts, end_ts):
                    return trades['data']

        return None
Esempio n. 26
0
    def _api_query(self,
                   command: str,
                   req: Optional[Dict] = None) -> Union[Dict, List]:
        if req is None:
            req = {}

        if command == "returnTicker":
            log.debug('Querying poloniex for returnTicker')
            ret = self.session.get(self.public_uri + command)
        else:
            req['command'] = command

            with self.lock:
                # Protect this region with a lock since poloniex will reject
                # non-increasing nonces. So if two greenlets come in here at
                # the same time one of them will fail
                req['nonce'] = int(time.time() * 1000)
                post_data = str.encode(urlencode(req))

                sign = hmac.new(self.secret, post_data,
                                hashlib.sha512).hexdigest()
                self.session.headers.update({'Sign': sign})

                log.debug(
                    'Poloniex private API query',
                    command=command,
                    post_data=req,
                )
                ret = self.session.post('https://poloniex.com/tradingApi', req)

            if ret.status_code != 200:
                raise RemoteError(
                    f'Poloniex query responded with error status code: {ret.status_code}'
                    f' and text: {ret.text}', )

            try:
                result = rlk_jsonloads_dict(ret.text)
            except JSONDecodeError:
                raise RemoteError(
                    f'Poloniex returned invalid JSON response: {ret.text}')
            return _post_process(result)

        return rlk_jsonloads(ret.text)
Esempio n. 27
0
    def find_usd_price(
        self,
        asset: typing.Asset,
        asset_btc_price: Optional[FVal] = None,
    ) -> FVal:
        if self.kraken and self.kraken.first_connection_made and asset_btc_price is not None:
            price = self.query_kraken_for_price(asset, asset_btc_price)
            log.debug('Get usd price from kraken', asset=asset, price=price)
            return price

        log.debug('Get usd price from cryptocompare', asset=asset)
        asset = world_to_cryptocompare(asset)
        resp = retry_calls(
            5, 'find_usd_price', 'requests.get', requests.get,
            u'https://min-api.cryptocompare.com/data/price?'
            'fsym={}&tsyms=USD'.format(asset))

        if resp.status_code != 200:
            raise RemoteError(
                'Cant reach cryptocompare to get USD value of {}'.format(
                    asset))

        resp = rlk_jsonloads(resp.text)

        # If there is an error in the response skip this token
        if 'USD' not in resp:
            error_message = ''
            if resp['Response'] == 'Error':
                error_message = resp['Message']

            log.error(
                'Cryptocompare usd price query failed',
                asset=asset,
                error=error_message,
            )
            return FVal(0)

        price = FVal(resp['USD'])
        log.debug('Got usd price from cryptocompare', asset=asset, price=price)
        return price
Esempio n. 28
0
    def api_query(
        self,
        method: str,
        options: Optional[Dict] = None,
    ) -> Union[List, Dict]:
        """
        Queries Bittrex with given method and options
        """
        if not options:
            options = {}
        nonce = str(int(time.time() * 1000))
        method_type = 'public'

        if method in BITTREX_MARKET_METHODS:
            method_type = 'market'
        elif method in BITTREX_ACCOUNT_METHODS:
            method_type = 'account'

        request_url = self.uri + method_type + '/' + method + '?'

        if method_type != 'public':
            request_url += 'apikey=' + self.api_key.decode(
            ) + "&nonce=" + nonce + '&'

        request_url += urlencode(options)
        signature = hmac.new(self.secret, request_url.encode(),
                             hashlib.sha512).hexdigest()
        self.session.headers.update({'apisign': signature})
        log.debug('Bittrex API query', request_url=request_url)
        response = self.session.get(request_url)

        try:
            json_ret = rlk_jsonloads(response.text)
        except JSONDecodeError:
            raise RemoteError('Bittrex returned invalid JSON response')

        if json_ret['success'] is not True:
            raise RemoteError(json_ret['message'])
        return json_ret['result']
Esempio n. 29
0
    def get_multitoken_balance(self, token_symbol, token_address,
                               token_decimals, accounts):
        """Return a dictionary with keys being accounts and value balances of token
        Balance value is normalized through the token decimals.
        """
        balances = {}
        if self.connected:
            token_contract = self.web3.eth.contract(address=token_address,
                                                    abi=self.token_abi)
            for account in accounts:
                token_amount = FVal(token_contract.call().balanceOf(account))
                if token_amount != 0:
                    balances[account] = token_amount / (FVal(10)**
                                                        FVal(token_decimals))
        else:
            for account in accounts:
                print('Checking token {} for account {}'.format(
                    token_symbol, account))
                resp = urlopen(
                    Request(
                        'https://api.etherscan.io/api?module=account&action='
                        'tokenbalance&contractaddress={}&address={}'.format(
                            token_address,
                            account,
                        )))
                resp = rlk_jsonloads(resp.read())
                if resp['status'] != 1:
                    raise ValueError(
                        'Failed to query etherscan for {} token balance of {}'.
                        format(
                            token_symbol,
                            account,
                        ))
                token_amount = FVal(resp['result'])
                if token_amount != 0:
                    balances[account] = token_amount / (FVal(10)**
                                                        FVal(token_decimals))

        return balances
Esempio n. 30
0
    def find_usd_price(
        self,
        asset: typing.Asset,
        asset_btc_price: Optional[FVal] = None,
    ) -> FVal:
        if self.kraken and self.kraken.first_connection_made and asset_btc_price is not None:
            return self.query_kraken_for_price(asset, asset_btc_price)

        # Adjust some ETH tokens to how cryptocompare knows them
        if asset == S_RDN:
            # remove this if cryptocompare changes the symbol
            asset = cast(typing.EthToken, 'RDN*')
        if asset == S_DATACOIN:
            asset = cast(typing.NonEthTokenBlockchainAsset, 'DATA')
        resp = retry_calls(
            5, 'find_usd_price', 'requests.get', requests.get,
            u'https://min-api.cryptocompare.com/data/price?'
            'fsym={}&tsyms=USD'.format(asset))

        if resp.status_code != 200:
            raise RemoteError(
                'Cant reach cryptocompare to get USD value of {}'.format(
                    asset))

        resp = rlk_jsonloads(resp.text)

        # If there is an error in the response skip this token
        if 'USD' not in resp:
            if resp['Response'] == 'Error':
                print(
                    'Could not query USD price for {}. Error: "{}"'.format(
                        asset, resp['Message']), )
            else:
                print('Could not query USD price for {}'.format(asset))
            return FVal(0)

        return FVal(resp['USD'])