Esempio n. 1
0
def gemini_symbol_to_base_quote(symbol: str) -> Tuple[Asset, Asset]:
    """Turns a gemini symbol product into a base/quote asset tuple

    - Can raise UnprocessableTradePair if symbol is in unexpected format
    - Case raise UnknownAsset if any of the pair assets are not known to rotki
    """
    five_letter_assets = ('sushi', '1inch', 'storj', 'matic')
    if len(symbol) == 6:
        base_asset = asset_from_gemini(symbol[:3].upper())
        quote_asset = asset_from_gemini(symbol[3:].upper())
    elif len(symbol) == 7:
        try:
            base_asset = asset_from_gemini(symbol[:4].upper())
            quote_asset = asset_from_gemini(symbol[4:].upper())
        except UnknownAsset:
            base_asset = asset_from_gemini(symbol[:3].upper())
            quote_asset = asset_from_gemini(symbol[3:].upper())
    elif len(symbol) == 8:
        if any([asset in symbol for asset in five_letter_assets]):
            base_asset = asset_from_gemini(symbol[:5].upper())
            quote_asset = asset_from_gemini(symbol[5:].upper())
        else:
            base_asset = asset_from_gemini(symbol[:4].upper())
            quote_asset = asset_from_gemini(symbol[4:].upper())
    else:
        raise UnprocessableTradePair(symbol)

    return base_asset, quote_asset
Esempio n. 2
0
    def query_online_deposits_withdrawals(
            self,
            start_ts: Timestamp,
            end_ts: Timestamp,
    ) -> List[AssetMovement]:
        result = self._get_paginated_query(
            endpoint='transfers',
            start_ts=start_ts,
            end_ts=end_ts,
        )
        movements = []
        for entry in result:
            try:
                timestamp = deserialize_timestamp(entry['timestampms'])
                timestamp = Timestamp(int(timestamp / 1000))
                asset = asset_from_gemini(entry['currency'])

                movement = AssetMovement(
                    location=Location.GEMINI,
                    category=deserialize_asset_movement_category(entry['type']),
                    address=deserialize_asset_movement_address(entry, 'destination', asset),
                    transaction_id=get_key_if_has_val(entry, 'txHash'),
                    timestamp=timestamp,
                    asset=asset,
                    amount=deserialize_asset_amount_force_positive(entry['amount']),
                    fee_asset=asset,
                    # Gemini does not include withdrawal fees neither in the API nor in their UI
                    fee=Fee(ZERO),
                    link=str(entry['eid']),
                )
            except UnknownAsset as e:
                self.msg_aggregator.add_warning(
                    f'Found gemini deposit/withdrawal with unknown asset '
                    f'{e.asset_name}. Ignoring it.',
                )
                continue
            except UnsupportedAsset as e:
                self.msg_aggregator.add_warning(
                    f'Found gemini deposit/withdrawal with unsupported asset '
                    f'{e.asset_name}. Ignoring it.',
                )
                continue
            except (DeserializationError, KeyError) as e:
                msg = str(e)
                if isinstance(e, KeyError):
                    msg = f'Missing key entry for {msg}.'
                self.msg_aggregator.add_error(
                    'Error processing a gemini deposit/withdrawal. Check logs '
                    'for details. Ignoring it.',
                )
                log.error(
                    'Error processing a gemini deposit_withdrawal',
                    asset_movement=entry,
                    error=msg,
                )
                continue

            movements.append(movement)

        return movements
Esempio n. 3
0
    def query_balances(self) -> ExchangeQueryBalances:
        try:
            balances = self._private_api_query('balances')
            balances.extend(self._private_api_query('balances/earn'))
        except (GeminiPermissionError, RemoteError) as e:
            msg = f'Gemini API request failed. {str(e)}'
            log.error(msg)
            return None, msg

        returned_balances: DefaultDict[Asset, Balance] = defaultdict(Balance)
        for entry in balances:
            try:
                balance_type = entry['type']
                if balance_type == 'exchange':
                    amount = deserialize_asset_amount(entry['amount'])
                else:  # should be 'Earn'
                    amount = deserialize_asset_amount(entry['balance'])
                # ignore empty balances
                if amount == ZERO:
                    continue

                asset = asset_from_gemini(entry['currency'])
                try:
                    usd_price = Inquirer().find_usd_price(asset=asset)
                except RemoteError as e:
                    self.msg_aggregator.add_error(
                        f'Error processing gemini {balance_type} balance result due to '
                        f'inability to query USD price: {str(e)}. Skipping balance entry',
                    )
                    continue

                returned_balances[asset] += Balance(
                    amount=amount,
                    usd_value=amount * usd_price,
                )
            except UnknownAsset as e:
                self.msg_aggregator.add_warning(
                    f'Found gemini balance result with unknown asset '
                    f'{e.asset_name}. Ignoring it.', )
                continue
            except UnsupportedAsset as e:
                self.msg_aggregator.add_warning(
                    f'Found gemini {balance_type} balance result with unsupported '
                    f'asset {e.asset_name}. Ignoring it.', )
                continue
            except (DeserializationError, KeyError) as e:
                msg = str(e)
                if isinstance(e, KeyError):
                    msg = f'Missing key entry for {msg}.'
                self.msg_aggregator.add_error(
                    f'Error processing a gemini {balance_type} balance. Check logs '
                    f'for details. Ignoring it.', )
                log.error(
                    f'Error processing a gemini {balance_type} balance',
                    error=msg,
                )
                continue

        return returned_balances, ''
Esempio n. 4
0
    def query_online_trade_history(
            self,
            start_ts: Timestamp,
            end_ts: Timestamp,
    ) -> Tuple[List[Trade], Tuple[Timestamp, Timestamp]]:
        """Queries gemini for trades
        """
        log.debug('Query gemini trade history', start_ts=start_ts, end_ts=end_ts)
        trades = []
        gemini_trades = []
        for symbol in self.symbols:
            gemini_trades = self._get_trades_for_symbol(
                symbol=symbol,
                start_ts=start_ts,
                end_ts=end_ts,
            )
            for entry in gemini_trades:
                try:
                    timestamp = deserialize_timestamp(entry['timestamp'])
                    if timestamp > end_ts:
                        break

                    base, quote = gemini_symbol_to_base_quote(symbol)
                    trades.append(Trade(
                        timestamp=timestamp,
                        location=Location.GEMINI,
                        base_asset=base,
                        quote_asset=quote,
                        trade_type=TradeType.deserialize(entry['type']),
                        amount=deserialize_asset_amount(entry['amount']),
                        rate=deserialize_price(entry['price']),
                        fee=deserialize_fee(entry['fee_amount']),
                        fee_currency=asset_from_gemini(entry['fee_currency']),
                        link=str(entry['tid']),
                        notes='',
                    ))
                except UnprocessableTradePair as e:
                    self.msg_aggregator.add_warning(
                        f'Found unprocessable Gemini pair {e.pair}. Ignoring the trade.',
                    )
                    continue
                except UnknownAsset as e:
                    self.msg_aggregator.add_warning(
                        f'Found unknown Gemini asset {e.asset_name}. '
                        f'Ignoring the trade.',
                    )
                    continue
                except (DeserializationError, KeyError) as e:
                    msg = str(e)
                    if isinstance(e, KeyError):
                        msg = f'Missing key entry for {msg}.'
                    self.msg_aggregator.add_error(
                        'Failed to deserialize a gemini trade. '
                        'Check logs for details. Ignoring it.',
                    )
                    log.error(
                        'Error processing a gemini trade.',
                        raw_trade=entry,
                        error=msg,
                    )
                    continue

        return trades, (start_ts, end_ts)