Exemple #1
0
def deserialize_asset_amount_force_positive(
        amount: AcceptableFValInitInput) -> AssetAmount:
    """Acts exactly like deserialize_asset_amount but also forces the number to be positive

    Is needed for some places like some exchanges that list the withdrawal amounts as
    negative numbers because it's a withdrawal"""
    result = deserialize_asset_amount(amount)
    if result < ZERO:
        result = AssetAmount(abs(result))
    return result
Exemple #2
0
    def _deserialize_trade(self, raw_result: List[Any]) -> Trade:
        """Process a trade result from Bitfinex and deserialize it.

        The base and quote assets are instantiated using the `fee_currency_symbol`
        (from raw_result[10]) over the pair (from raw_result[1]).

        Known pairs format: 'tETHUST', 'tETH:UST'.

        Can raise DeserializationError.

        Schema reference in:
        https://docs.bitfinex.com/reference#rest-auth-trades
        """
        amount = deserialize_asset_amount(raw_result[4])
        trade_type = TradeType.BUY if amount >= ZERO else TradeType.SELL
        bfx_pair = self._process_bfx_pair(raw_result[1])
        if bfx_pair not in self.pair_bfx_symbols_map:
            raise DeserializationError(
                f'Could not deserialize bitfinex trade pair {raw_result[1]}. '
                f'Raw trade: {raw_result}',
            )

        bfx_base_asset_symbol, bfx_quote_asset_symbol = self.pair_bfx_symbols_map[bfx_pair]
        try:
            base_asset = asset_from_bitfinex(
                bitfinex_name=bfx_base_asset_symbol,
                currency_map=self.currency_map,
            )
            quote_asset = asset_from_bitfinex(
                bitfinex_name=bfx_quote_asset_symbol,
                currency_map=self.currency_map,
            )
            fee_asset = asset_from_bitfinex(
                bitfinex_name=raw_result[10],
                currency_map=self.currency_map,
            )
        except (UnknownAsset, UnsupportedAsset) as e:
            asset_tag = 'Unknown' if isinstance(e, UnknownAsset) else 'Unsupported'
            raise DeserializationError(
                f'{asset_tag} {e.asset_name} found while processing trade pair due to: {str(e)}',
            ) from e

        trade = Trade(
            timestamp=Timestamp(int(raw_result[2] / 1000)),
            location=Location.BITFINEX,
            pair=TradePair(f'{base_asset.identifier}_{quote_asset.identifier}'),
            trade_type=trade_type,
            amount=AssetAmount(abs(amount)),
            rate=deserialize_price(raw_result[5]),
            fee=Fee(abs(deserialize_fee(raw_result[9]))),
            fee_currency=fee_asset,
            link=str(raw_result[0]),
            notes='',
        )
        return trade
Exemple #3
0
    def add_ledger_action(
        self,
        action: LedgerAction,
        profit_loss_in_profit_currency: FVal,
    ) -> None:
        if not self.create_csv:
            return

        self.ledger_actions_csv.append({
            'time':
            self.timestamp_to_date(action.timestamp),
            'type':
            str(action.action_type),
            'location':
            str(action.location),
            'asset':
            str(action.asset),
            'amount':
            str(action.amount),
            f'profit_loss_in_{self.profit_currency.symbol}':
            profit_loss_in_profit_currency,
        })

        paid_asset: Optional[Asset]
        received_asset: Optional[Asset]
        if action.is_profitable():
            paid_in_profit_currency = ZERO
            paid_in_asset = ZERO
            paid_asset = None
            received_asset = action.asset
            received_in_asset = action.amount
            received_in_profit_currency = profit_loss_in_profit_currency
        else:
            paid_in_profit_currency = profit_loss_in_profit_currency
            paid_in_asset = action.amount
            paid_asset = action.asset
            received_asset = None
            received_in_asset = AssetAmount(ZERO)
            received_in_profit_currency = ZERO

        self.add_to_allevents(
            event_type=EV_LEDGER_ACTION,
            location=action.location,
            paid_in_profit_currency=paid_in_profit_currency,
            paid_asset=paid_asset,
            paid_in_asset=paid_in_asset,
            received_asset=received_asset,
            received_in_asset=received_in_asset,
            taxable_received_in_profit_currency=received_in_profit_currency,
            total_received_in_profit_currency=received_in_profit_currency,
            timestamp=action.timestamp,
            link=action.link,
            notes=action.notes,
        )
Exemple #4
0
def trade_from_conversion(trade_a: Dict[str, Any], trade_b: Dict[str, Any]) -> Optional[Trade]:
    """Turn information from a conversion into a trade

    Mary raise:
    - UnknownAsset due to Asset instantiation
    - DeserializationError due to unexpected format of dict entries
    - KeyError due to dict entires missing an expected entry
    """
    # Check that the status is complete
    if trade_a['status'] != 'completed':
        return None

    # Trade b will represent the asset we are converting to
    if trade_b['amount']['amount'].startswith('-'):
        trade_a, trade_b = trade_b, trade_a

    timestamp = deserialize_timestamp_from_date(trade_a['updated_at'], 'iso8601', 'coinbase')
    trade_type = deserialize_trade_type('sell')
    tx_amount = AssetAmount(abs(deserialize_asset_amount(trade_a['amount']['amount'])))
    tx_asset = asset_from_coinbase(trade_a['amount']['currency'], time=timestamp)
    native_amount = deserialize_asset_amount(trade_b['amount']['amount'])
    native_asset = asset_from_coinbase(trade_b['amount']['currency'], time=timestamp)
    amount = tx_amount
    # The rate is how much you get/give in quotecurrency if you buy/sell 1 unit of base currency
    rate = Price(native_amount / tx_amount)

    # Obtain fee amount in the native currency using data from both trades
    amount_after_fee = deserialize_asset_amount(trade_b['native_amount']['amount'])
    amount_before_fee = deserialize_asset_amount(trade_a['native_amount']['amount'])
    # amount_after_fee + amount_before_fee is a negative amount and the fee needs to be positive
    conversion_native_fee_amount = abs(amount_after_fee + amount_before_fee)
    if ZERO not in (tx_amount, conversion_native_fee_amount, amount_before_fee):
        # We have the fee amount in the native currency. To get it in the
        # converted asset we have to get the rate
        asset_native_rate = tx_amount / abs(amount_before_fee)
        fee_amount = Fee(conversion_native_fee_amount / asset_native_rate)
    else:
        fee_amount = Fee(ZERO)
    fee_asset = asset_from_coinbase(trade_a['amount']['currency'], time=timestamp)

    return Trade(
        timestamp=timestamp,
        location=Location.COINBASE,
        # in coinbase you are buying/selling tx_asset for native_asset
        base_asset=tx_asset,
        quote_asset=native_asset,
        trade_type=trade_type,
        amount=amount,
        rate=rate,
        fee=fee_amount,
        fee_currency=fee_asset,
        link=str(trade_a['trade']['id']),
    )
Exemple #5
0
    def add_ledger_action(
        self,
        action: LedgerAction,
        profit_loss_in_profit_currency: FVal,
    ) -> None:
        if not self.create_csv:
            return

        self.ledger_actions_csv.append({
            'time':
            timestamp_to_date(action.timestamp, formatstr='%d/%m/%Y %H:%M:%S'),
            'type':
            str(action.action_type),
            'location':
            str(action.location),
            'asset':
            str(action.asset),
            'amount':
            str(action.amount),
            f'profit_loss_in_{self.profit_currency.identifier}':
            profit_loss_in_profit_currency,
        })

        paid_asset: Union[EmptyStr, Asset]
        received_asset: Union[EmptyStr, Asset]
        if action.is_profitable():
            paid_in_profit_currency = ZERO
            paid_in_asset = ZERO
            paid_asset = S_EMPTYSTR
            received_asset = action.asset
            received_in_asset = action.amount
            received_in_profit_currency = profit_loss_in_profit_currency
        else:
            paid_in_profit_currency = profit_loss_in_profit_currency
            paid_in_asset = action.amount
            paid_asset = action.asset
            received_asset = S_EMPTYSTR
            received_in_asset = AssetAmount(ZERO)
            received_in_profit_currency = ZERO

        self.add_to_allevents(
            event_type=EV_LEDGER_ACTION,
            location=action.location,
            paid_in_profit_currency=paid_in_profit_currency,
            paid_asset=paid_asset,
            paid_in_asset=paid_in_asset,
            received_asset=received_asset,
            received_in_asset=received_in_asset,
            taxable_received_in_profit_currency=received_in_profit_currency,
            timestamp=action.timestamp,
        )
Exemple #6
0
    def _read_asset_movements(self, filepath: str) -> List[AssetMovement]:
        """Reads a csv account type report and extracts the AssetMovements"""
        with open(filepath, newline='') as csvfile:
            reader = csv.DictReader(csvfile)
            movements = []
            for row in reader:
                try:
                    if row['type'] in ('withdrawal', 'deposit'):
                        timestamp = deserialize_timestamp_from_date(
                            row['time'],
                            'iso8601',
                            'coinbasepro',
                        )
                        amount = deserialize_asset_amount(row['amount'])
                        if row['type'] == 'withdrawal':
                            # For withdrawals the withdraw amount is negative
                            amount = AssetAmount(amount * FVal('-1'))
                        asset = Asset(row['amount/balance unit'])
                        movements.append(
                            AssetMovement(
                                location=Location.COINBASEPRO,
                                category=deserialize_asset_movement_category(
                                    row['type']),
                                timestamp=timestamp,
                                asset=asset,
                                amount=amount,
                                fee_asset=asset,
                                # I don't see any fee in deposit withdrawals in coinbasepro
                                fee=Fee(ZERO),
                                link=str(row['transfer id']),
                            ))
                except UnknownAsset as e:
                    self.msg_aggregator.add_warning(
                        f'Found unknown Coinbasepro asset {e.asset_name}. '
                        f'Ignoring its deposit/withdrawal.', )
                    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 Coinbasepro deposit/withdrawal. '
                        'Check logs for details. Ignoring it.', )
                    log.error(
                        'Error processing a coinbasepro  deposit/withdrawal.',
                        raw_asset_movement=row,
                        error=msg,
                    )
                    continue

        return movements
Exemple #7
0
def _deserialize_transaction(grant_id: int, rawtx: Dict[str,
                                                        Any]) -> LedgerAction:
    """May raise:
    - DeserializationError
    - KeyError
    - UnknownAsset
    """
    timestamp = deserialize_timestamp_from_date(
        date=rawtx['timestamp'],
        formatstr='%Y-%m-%dT%H:%M:%S',
        location='Gitcoin API',
        skip_milliseconds=True,
    )
    asset = get_gitcoin_asset(symbol=rawtx['asset'],
                              token_address=rawtx['token_address'])
    raw_amount = deserialize_int_from_str(symbol=rawtx['amount'],
                                          location='gitcoin api')
    amount = asset_normalized_value(raw_amount, asset)
    if amount == ZERO:
        raise ZeroGitcoinAmount()

    # let's use gitcoin's calculated rate for now since they include it in the response
    usd_value = Price(
        ZERO) if rawtx['usd_value'] is None else deserialize_price(
            rawtx['usd_value'])  # noqa: E501
    rate = Price(ZERO) if usd_value == ZERO else Price(usd_value / amount)
    raw_txid = rawtx['tx_hash']
    tx_type, tx_id = process_gitcoin_txid(key='tx_hash', entry=rawtx)
    # until we figure out if we can use it https://github.com/gitcoinco/web/issues/9255#issuecomment-874537144  # noqa: E501
    clr_round = _calculate_clr_round(timestamp, rawtx)
    notes = f'Gitcoin grant {grant_id} event' if not clr_round else f'Gitcoin grant {grant_id} event in clr_round {clr_round}'  # noqa: E501
    return LedgerAction(
        identifier=1,  # whatever -- does not end up in the DB
        timestamp=timestamp,
        action_type=LedgerActionType.DONATION_RECEIVED,
        location=Location.GITCOIN,
        amount=AssetAmount(amount),
        asset=asset,
        rate=rate,
        rate_asset=A_USD,
        link=raw_txid,
        notes=notes,
        extra_data=GitcoinEventData(
            tx_id=tx_id,
            grant_id=grant_id,
            clr_round=clr_round,
            tx_type=tx_type,
        ),
    )
Exemple #8
0
def test_export_import_db(data_dir, username):
    """Create a DB, write some data and then after export/import confirm it's there"""
    msg_aggregator = MessagesAggregator()
    data = DataHandler(data_dir, msg_aggregator)
    data.unlock(username, '123', create_new=True)
    data.set_fiat_balances({A_EUR: AssetAmount(FVal('10'))})

    encoded_data, _ = data.compress_and_encrypt_db('123')

    # The server would return them decoded
    encoded_data = encoded_data.decode()
    data.decompress_and_decrypt_db('123', encoded_data)
    fiat_balances = data.get_fiat_balances()
    assert len(fiat_balances) == 1
    assert int(fiat_balances[A_EUR]) == 10
Exemple #9
0
def test_get_trades_from_tx_swaps_1():
    """Single swap"""
    trades = get_trades_from_tx_swaps(TEST_SWAPS_TX_1)
    expected_trades = [
        AMMTrade(
            trade_type=TradeType.BUY,
            base_asset=EthereumToken('AMPL'),
            quote_asset=EthereumToken('USDC'),
            amount=AssetAmount(FVal('14285.153512382')),
            rate=Price(FVal('1.151914744569445582155783413')),
            trade_index=0,
            swaps=TEST_SWAPS_TX_1,
        ),
    ]
    assert trades == expected_trades
Exemple #10
0
def test_get_trades_from_tx_swaps_2():
    """Two swaps that can be aggregated"""
    trades = get_trades_from_tx_swaps(TEST_SWAPS_TX_2)
    expected_trades = [
        AMMTrade(
            trade_type=TradeType.BUY,
            base_asset=A_WETH,
            quote_asset=A_USDC,
            amount=AssetAmount(FVal('8.05955457343053505')),
            rate=Price(FVal('1538.698513176199118305140044')),
            trade_index=0,
            swaps=TEST_SWAPS_TX_2,
        ),
    ]
    assert trades == expected_trades
Exemple #11
0
def test_data_set_fiat_balances(data_dir, username):
    msg_aggregator = MessagesAggregator()
    data = DataHandler(data_dir, msg_aggregator)
    data.unlock(username, '123', create_new=True)

    amount_eur = AssetAmount(FVal('100'))
    amount_cny = AssetAmount(FVal('500'))

    data.set_fiat_balances({A_EUR: amount_eur})
    data.set_fiat_balances({A_CNY: amount_cny})
    balances = data.get_fiat_balances()
    assert len(balances) == 2
    assert FVal(balances[A_EUR]) == amount_eur
    assert FVal(balances[A_CNY]) == amount_cny

    data.set_fiat_balances({A_EUR: ZERO})
    balances = data.get_fiat_balances()
    assert len(balances) == 1
    assert FVal(balances[A_CNY]) == amount_cny

    # also check that all the fiat assets in the fiat table are in
    # all_assets.json
    for fiat_asset in FIAT_CURRENCIES:
        assert fiat_asset.is_fiat()
Exemple #12
0
def test_get_trades_from_tx_swaps_1():
    """Single swap"""
    trades = get_trades_from_tx_swaps(TEST_SWAPS_TX_1)
    expected_trades = [
        AMMTrade(
            trade_type=TradeType.BUY,
            base_asset=A_AMPL,
            quote_asset=A_USDC,
            amount=AssetAmount(FVal('14285.153512382')),
            rate=Price(FVal('0.8681198020203941533693340847')),
            trade_index=0,
            swaps=TEST_SWAPS_TX_1,
        ),
    ]
    assert trades == expected_trades
Exemple #13
0
def test_get_trades_from_tx_swaps_2():
    """Two swaps that can be aggregated"""
    trades = get_trades_from_tx_swaps(TEST_SWAPS_TX_2)
    expected_trades = [
        AMMTrade(
            trade_type=TradeType.BUY,
            base_asset=EthereumToken('WETH'),
            quote_asset=EthereumToken('USDC'),
            amount=AssetAmount(FVal('8.05955457343053505')),
            rate=Price(FVal('0.0006498998936028010652666316885')),
            trade_index=0,
            swaps=TEST_SWAPS_TX_2,
        ),
    ]
    assert trades == expected_trades
Exemple #14
0
def trade_from_bittrex(bittrex_trade: Dict[str, Any]) -> Trade:
    """Turn a bittrex trade returned from bittrex trade history to our common trade
    history format

    Throws:
        - UnknownAsset/UnsupportedAsset due to bittrex_pair_to_world()
        - DeserializationError due to unexpected format of dict entries
        - KeyError due to dict entries missing an expected entry
    """
    amount = AssetAmount(
        deserialize_asset_amount(bittrex_trade['Quantity']) -
        deserialize_asset_amount(bittrex_trade['QuantityRemaining']), )
    timestamp = deserialize_timestamp_from_bittrex_date(
        bittrex_trade['TimeStamp'])
    rate = deserialize_price(bittrex_trade['PricePerUnit'])
    order_type = deserialize_trade_type(bittrex_trade['OrderType'])
    bittrex_price = deserialize_price(bittrex_trade['Price'])
    fee = deserialize_fee(bittrex_trade['Commission'])
    pair = bittrex_pair_to_world(bittrex_trade['Exchange'])
    quote_currency = get_pair_position_asset(pair, 'second')

    log.debug(
        'Processing bittrex Trade',
        sensitive_log=True,
        amount=amount,
        rate=rate,
        order_type=order_type,
        price=bittrex_price,
        fee=fee,
        bittrex_pair=bittrex_trade['Exchange'],
        pair=pair,
    )

    return Trade(
        timestamp=timestamp,
        location=Location.BITTREX,
        pair=pair,
        trade_type=order_type,
        amount=amount,
        rate=rate,
        fee=fee,
        fee_currency=quote_currency,
        link=str(bittrex_trade['OrderUuid']),
    )
Exemple #15
0
def mock_exchange_data_in_db(exchange_locations, rotki) -> None:
    db = rotki.data.db
    for exchange_location in exchange_locations:
        db.add_trades([Trade(
            timestamp=Timestamp(1),
            location=exchange_location,
            base_asset=A_BTC,
            quote_asset=A_ETH,
            trade_type=TradeType.BUY,
            amount=AssetAmount(FVal(1)),
            rate=Price(FVal(1)),
            fee=Fee(FVal('0.1')),
            fee_currency=A_ETH,
            link='foo',
            notes='boo',
        )])
        db.update_used_query_range(name=f'{str(exchange_location)}_trades', start_ts=0, end_ts=9999)  # noqa: E501
        db.update_used_query_range(name=f'{str(exchange_location)}_margins', start_ts=0, end_ts=9999)  # noqa: E501
        db.update_used_query_range(name=f'{str(exchange_location)}_asset_movements', start_ts=0, end_ts=9999)  # noqa: E501
Exemple #16
0
    def _deserialize_trade(
            self,
            raw_trade: Dict[str, Any],
    ) -> Trade:
        """Process a trade user transaction from Bitstamp and deserialize it.

        Can raise DeserializationError.
        """
        timestamp = deserialize_timestamp_from_bitstamp_date(raw_trade['datetime'])
        trade_pair_data = self._get_trade_pair_data_from_transaction(raw_trade)
        base_asset_amount = deserialize_asset_amount(
            raw_trade[trade_pair_data.base_asset_symbol],
        )
        quote_asset_amount = deserialize_asset_amount(
            raw_trade[trade_pair_data.quote_asset_symbol],
        )
        rate = deserialize_price(raw_trade[trade_pair_data.pair])
        fee_currency = trade_pair_data.quote_asset
        if base_asset_amount >= ZERO:
            trade_type = TradeType.BUY
        else:
            if quote_asset_amount < 0:
                raise DeserializationError(
                    f'Unexpected bitstamp trade format. Both base and quote '
                    f'amounts are negative: {raw_trade}',
                )
            trade_type = TradeType.SELL

        trade = Trade(
            timestamp=timestamp,
            location=Location.BITSTAMP,
            base_asset=trade_pair_data.base_asset,
            quote_asset=trade_pair_data.quote_asset,
            trade_type=trade_type,
            amount=AssetAmount(abs(base_asset_amount)),
            rate=rate,
            fee=deserialize_fee(raw_trade['fee']),
            fee_currency=fee_currency,
            link=str(raw_trade['id']),
            notes='',
        )
        return trade
Exemple #17
0
def assert_blockfi_trades_import_results(rotki: Rotkehlchen):
    """A utility function to help assert on correctness of importing trades data from blockfi"""
    trades = rotki.data.db.get_trades()
    warnings = rotki.msg_aggregator.consume_warnings()
    errors = rotki.msg_aggregator.consume_errors()
    assert len(errors) == 0
    assert len(warnings) == 0

    expected_trades = [Trade(
        timestamp=Timestamp(1612051199),
        location=Location.BLOCKFI,
        base_asset=symbol_to_asset_or_token('USDC'),
        quote_asset=symbol_to_asset_or_token('LTC'),
        trade_type=TradeType.BUY,
        amount=AssetAmount(FVal('6404.6')),
        rate=Price(FVal('151.6283999982779809352223797')),
        fee=None,
        fee_currency=None,
        link='',
        notes='One Time',
    )]
    assert trades == expected_trades
Exemple #18
0
def test_deserialize_v2_trade_sell(mock_kucoin):
    raw_result = {
        'symbol': 'BCHSV-USDT',
        'tradeId': '601da995e0ee8b00063a075c',
        'orderId': '601da9950c92050006bd45c5',
        'counterOrderId': '601da9950c92050006bd457d',
        'side': 'sell',
        'liquidity': 'taker',
        'forceTaker': True,
        'price': '37624.4',
        'size': '0.0013',
        'funds': '48.91172',
        'fee': '0.034238204',
        'feeRate': '0.0007',
        'feeCurrency': 'USDT',
        'stop': '',
        'tradeType': 'TRADE',
        'type': 'market',
        'createdAt': 1612556794259,
    }
    expected_trade = Trade(
        timestamp=Timestamp(1612556794),
        location=Location.KUCOIN,
        base_asset=A_BSV,
        quote_asset=A_USDT,
        trade_type=TradeType.SELL,
        amount=AssetAmount(FVal('0.0013')),
        rate=Price(FVal('37624.4')),
        fee=Fee(FVal('0.034238204')),
        fee_currency=A_USDT,
        link='601da995e0ee8b00063a075c',
        notes='',
    )
    trade = mock_kucoin._deserialize_trade(
        raw_result=raw_result,
        case=KucoinCase.TRADES,
    )
    assert trade == expected_trade
Exemple #19
0
def test_deserialize_v2_trade_buy(mock_kucoin):
    raw_result = {
        'symbol': 'KCS-USDT',
        'tradeId': '601da9faf1297d0007efd712',
        'orderId': '601da9fa0c92050006bd83be',
        'counterOrderId': '601bad620c9205000642300f',
        'side': 'buy',
        'liquidity': 'taker',
        'forceTaker': True,
        'price': 1000,
        'size': '0.2',
        'funds': 200,
        'fee': '0.14',
        'feeRate': '0.0007',
        'feeCurrency': 'USDT',
        'stop': '',
        'tradeType': 'TRADE',
        'type': 'market',
        'createdAt': 1612556794259,
    }
    expected_trade = Trade(
        timestamp=Timestamp(1612556794),
        location=Location.KUCOIN,
        base_asset=A_KCS,
        quote_asset=A_USDT,
        trade_type=TradeType.BUY,
        amount=AssetAmount(FVal('0.2')),
        rate=Price(FVal('1000')),
        fee=Fee(FVal('0.14')),
        fee_currency=A_USDT,
        link='601da9faf1297d0007efd712',
        notes='',
    )
    trade = mock_kucoin._deserialize_trade(
        raw_result=raw_result,
        case=KucoinCase.TRADES,
    )
    assert trade == expected_trade
Exemple #20
0
def trade_from_bitmex(bitmex_trade: Dict) -> MarginPosition:
    """Turn a bitmex trade returned from bitmex trade history to our common trade
    history format. This only returns margin positions as bitmex only deals in
    margin trading. May raise:

    - KeyError
    - DeserializationError
    """
    close_time = iso8601ts_to_timestamp(bitmex_trade['transactTime'])
    profit_loss = AssetAmount(
        satoshis_to_btc(deserialize_asset_amount(bitmex_trade['amount'])))
    currency = bitmex_to_world(bitmex_trade['currency'])
    fee = deserialize_fee(bitmex_trade['fee'])
    notes = bitmex_trade['address']
    assert currency == A_BTC, 'Bitmex trade should only deal in BTC'

    log.debug(
        'Processing Bitmex Trade',
        sensitive_log=True,
        timestamp=close_time,
        profit_loss=profit_loss,
        currency=currency,
        fee=fee,
        notes=notes,
    )

    return MarginPosition(
        location=Location.BITMEX,
        open_time=None,
        close_time=close_time,
        profit_loss=profit_loss,
        pl_currency=currency,
        fee=fee,
        fee_currency=A_BTC,
        notes=notes,
        link=str(bitmex_trade['transactID']),
    )
Exemple #21
0
def trade_from_bitcoinde(raw_trade: Dict) -> Trade:

    try:
        timestamp = deserialize_timestamp_from_date(
            raw_trade['successfully_finished_at'],
            'iso8601',
            'bitcoinde',
        )
    except KeyError:
        # For very old trades (2013) bitcoin.de does not return 'successfully_finished_at'
        timestamp = deserialize_timestamp_from_date(
            raw_trade['trade_marked_as_paid_at'],
            'iso8601',
            'bitcoinde',
        )

    trade_type = deserialize_trade_type(raw_trade['type'])
    tx_amount = AssetAmount(FVal(raw_trade['amount_currency_to_trade']))
    native_amount = FVal(raw_trade['volume_currency_to_pay'])
    tx_asset, native_asset = bitcoinde_pair_to_world(raw_trade['trading_pair'])
    pair = TradePair(f'{tx_asset.identifier}_{native_asset.identifier}')
    amount = tx_amount
    rate = Price(native_amount / tx_amount)
    fee_amount = deserialize_fee(raw_trade['fee_currency_to_pay'])
    fee_asset = Asset('EUR')

    return Trade(
        timestamp=timestamp,
        location=Location.BITCOINDE,
        pair=pair,
        trade_type=trade_type,
        amount=amount,
        rate=rate,
        fee=fee_amount,
        fee_currency=fee_asset,
        link=str(raw_trade['trade_id']),
    )
Exemple #22
0
                token0=A_WBTC,
                token1=A_WETH,
                amount0_in=AssetAmount(FVal('0.01562514')),
                amount1_in=AssetAmount(ZERO),
                amount0_out=AssetAmount(ZERO),
                amount1_out=AssetAmount(FVal('0.205421420618533148')),
            ),
        ],
    ).serialize()


BALANCER_TEST_ADDR3_EXPECTED_HISTORY_POOL1 = (BalancerPoolEventsBalance(
    address=BALANCER_TEST_ADDR3,
    pool_address_token=BALANCER_TEST_ADDR3_POOL1,
    profit_loss_amounts=[
        AssetAmount(FVal('-0.039312851799093402')),
        AssetAmount(FVal('0.744372160905819159')),
    ],
    usd_profit_loss=FVal('-0.76584117161052920880190053'),
    events=[
        BalancerEvent(
            tx_hash=
            '0xb9dff9df4e3838c75d354d62c4596d94e5eb8904e07cee07a3b7ffa611c05544',
            log_index=331,
            address=BALANCER_TEST_ADDR3,
            timestamp=Timestamp(1597144247),
            event_type=BalancerBPTEventType.MINT,
            pool_address_token=BALANCER_TEST_ADDR3_POOL1,
            lp_balance=Balance(
                amount=FVal('0.042569019597126949'),
                usd_value=FVal('19.779488662371895'),
Exemple #23
0
def get_balancer_test_addr2_expected_trades():
    """In a function since the new(unknown) assets needs to have been loaded in the DB"""
    A_WCRES = EthereumToken.initialize(  # noqa: N806
        address=string_to_ethereum_address(
            '0xa0afAA285Ce85974c3C881256cB7F225e3A1178a'),
        decimals=18,
        symbol='wCRES',
    )
    return [
        AMMTrade(
            trade_type=TradeType.BUY,
            base_asset=A_WETH,
            quote_asset=A_AAVE,
            amount=AssetAmount(FVal('1.616934038985744521')),
            rate=Price(FVal('6.963972908793392530935439799')),
            trade_index=1,
            swaps=[
                AMMSwap(
                    tx_hash=
                    '0x3c457da9b541ae39a7dc781ab04a03938b98b5649512aec2a2d32635c9bbf589',  # noqa: E501
                    log_index=24,
                    address=string_to_ethereum_address(
                        '0x029f388aC4D5C8BfF490550ce0853221030E822b'
                    ),  # noqa: E501
                    from_address=string_to_ethereum_address(
                        '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56'
                    ),  # noqa: E501
                    to_address=string_to_ethereum_address(
                        '0x7c90a3cd7Ec80dd2F633ed562480AbbEEd3bE546'
                    ),  # noqa: E501
                    timestamp=Timestamp(1607008178),
                    location=Location.BALANCER,
                    token0=A_AAVE,
                    token1=A_WETH,
                    amount0_in=AssetAmount(FVal('11.260284842802604032')),
                    amount1_in=AssetAmount(ZERO),
                    amount0_out=AssetAmount(ZERO),
                    amount1_out=AssetAmount(FVal('1.616934038985744521')),
                ),
            ],
        ),
        AMMTrade(
            trade_type=TradeType.BUY,
            base_asset=A_AAVE,
            quote_asset=A_WETH,
            amount=AssetAmount(FVal('11.260286362820602094')),
            rate=Price(FVal('0.1416068599966922676173010716')),
            trade_index=0,
            swaps=[
                AMMSwap(
                    tx_hash=
                    '0x3c457da9b541ae39a7dc781ab04a03938b98b5649512aec2a2d32635c9bbf589',  # noqa: E501
                    log_index=18,
                    address=string_to_ethereum_address(
                        '0x029f388aC4D5C8BfF490550ce0853221030E822b'
                    ),  # noqa: E501
                    from_address=string_to_ethereum_address(
                        '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56'
                    ),  # noqa: E501
                    to_address=string_to_ethereum_address(
                        '0x70985E557aE0CD6dC88189a532e54FbC61927BAd'
                    ),  # noqa: E501
                    timestamp=Timestamp(1607008178),
                    location=Location.BALANCER,
                    token0=A_WETH,
                    token1=A_AAVE,
                    amount0_in=AssetAmount(FVal('1.594533794502600192')),
                    amount1_in=AssetAmount(ZERO),
                    amount0_out=AssetAmount(ZERO),
                    amount1_out=AssetAmount(FVal('11.260286362820602094')),
                ),
            ],
        ),
        AMMTrade(
            trade_type=TradeType.BUY,
            base_asset=A_WETH,
            quote_asset=A_SYN,
            amount=AssetAmount(FVal('1.352902561458047718')),
            rate=Price(FVal('724.4303350385182691258363763')),
            trade_index=0,
            swaps=[
                AMMSwap(
                    tx_hash=
                    '0x5e235216cb03e4eb234014f5ccf3efbfddd40c4576424e2a8204f1d12b96ed35',  # noqa: E501
                    log_index=143,
                    address=string_to_ethereum_address(
                        '0x029f388aC4D5C8BfF490550ce0853221030E822b'
                    ),  # noqa: E501
                    from_address=string_to_ethereum_address(
                        '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56'
                    ),  # noqa: E501
                    to_address=string_to_ethereum_address(
                        '0x8982E9bBf7AC6A49c434aD81D2fF8e16895318e5'
                    ),  # noqa: E501
                    timestamp=Timestamp(1607008218),
                    location=Location.BALANCER,
                    token0=A_SYN,
                    token1=A_WETH,
                    amount0_in=AssetAmount(FVal('980.08365587152306176')),
                    amount1_in=AssetAmount(ZERO),
                    amount0_out=AssetAmount(ZERO),
                    amount1_out=AssetAmount(FVal('1.352902561458047718')),
                ),
            ],
        ),
        AMMTrade(
            trade_type=TradeType.BUY,
            base_asset=A_WETH,
            quote_asset=A_WCRES,
            amount=AssetAmount(FVal('0.205709519074945018')),
            rate=Price(FVal('232.7409943164679514496089589')),
            trade_index=0,
            swaps=[
                AMMSwap(
                    tx_hash=
                    '0xf54be824b4619777f1db0e3da91b0cd52f6dba730c95a75644e2b085e6ab9824',  # noqa: E501
                    log_index=300,
                    address=string_to_ethereum_address(
                        '0x029f388aC4D5C8BfF490550ce0853221030E822b'
                    ),  # noqa: E501
                    from_address=string_to_ethereum_address(
                        '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56'
                    ),  # noqa: E501
                    to_address=string_to_ethereum_address(
                        '0x10996eC4f3E7A1b314EbD966Fa8b1ad0fE0f8307'
                    ),  # noqa: E501
                    timestamp=Timestamp(1607009877),
                    location=Location.BALANCER,
                    token0=A_WCRES,
                    token1=A_WETH,
                    amount0_in=AssetAmount(FVal('47.87703800986513408')),
                    amount1_in=AssetAmount(ZERO),
                    amount0_out=AssetAmount(ZERO),
                    amount1_out=AssetAmount(FVal('0.205709519074945018')),
                ),
            ],
        ),
        AMMTrade(
            trade_type=TradeType.BUY,
            base_asset=A_API3,
            quote_asset=A_WETH,
            amount=AssetAmount(FVal('295.881648100500428692')),
            rate=Price(FVal('0.003346787723157288562491614498')),
            trade_index=0,
            swaps=[
                AMMSwap(
                    tx_hash=
                    '0xfed4e15051e3ce4dc0d2816f719701e5920e40bf41614b5feaa3c5a6a0186c03',  # noqa: E501
                    log_index=22,
                    address=string_to_ethereum_address(
                        '0x029f388aC4D5C8BfF490550ce0853221030E822b'
                    ),  # noqa: E501
                    from_address=string_to_ethereum_address(
                        '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56'
                    ),  # noqa: E501
                    to_address=string_to_ethereum_address(
                        '0x997c0fc9578a8194EFDdE2E0cD7aa6A69cFCD7c1'
                    ),  # noqa: E501
                    timestamp=Timestamp(1607010888),
                    location=Location.BALANCER,
                    token0=A_WETH,
                    token1=A_API3,
                    amount0_in=AssetAmount(FVal('0.990253067370299904')),
                    amount1_in=AssetAmount(ZERO),
                    amount0_out=AssetAmount(ZERO),
                    amount1_out=AssetAmount(FVal('295.881648100500428692')),
                ),
            ],
        ),
        AMMTrade(
            trade_type=TradeType.BUY,
            base_asset=A_WETH,
            quote_asset=A_MFT,
            amount=AssetAmount(FVal('0.686544199299304057')),
            rate=Price(FVal('243775.0324093115004367119900')),
            trade_index=0,
            swaps=[
                AMMSwap(
                    tx_hash=
                    '0xf0147c4b81098676c08ae20ae5bf8f8b60d0ad79eec484f3f93ac6ab49a3c51c',  # noqa: E501
                    log_index=97,
                    address=string_to_ethereum_address(
                        '0x029f388aC4D5C8BfF490550ce0853221030E822b'
                    ),  # noqa: E501
                    from_address=string_to_ethereum_address(
                        '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56'
                    ),  # noqa: E501
                    to_address=string_to_ethereum_address(
                        '0x2Eb6CfbFFC8785Cd0D9f2d233d0a617bF4269eeF'
                    ),  # noqa: E501
                    timestamp=Timestamp(1607015059),
                    location=Location.BALANCER,
                    token0=A_MFT,
                    token1=A_WETH,
                    amount0_in=AssetAmount(FVal('167362.334434612660404224')),
                    amount1_in=AssetAmount(ZERO),
                    amount0_out=AssetAmount(ZERO),
                    amount1_out=AssetAmount(FVal('0.686544199299304057')),
                ),
            ],
        ),
        AMMTrade(
            trade_type=TradeType.BUY,
            base_asset=A_WETH,
            quote_asset=A_AAVE,
            amount=AssetAmount(FVal('3.055412574642681758')),
            rate=Price(FVal('6.916116208273240607778771150')),
            trade_index=1,
            swaps=[
                AMMSwap(
                    tx_hash=
                    '0x67c0e9a0fdd002d0b9d1cca0c8e4ca4d30435bbf57bbf0091396275efaea414b',  # noqa: E501
                    log_index=37,
                    address=string_to_ethereum_address(
                        '0x029f388aC4D5C8BfF490550ce0853221030E822b'
                    ),  # noqa: E501
                    from_address=string_to_ethereum_address(
                        '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56'
                    ),  # noqa: E501
                    to_address=string_to_ethereum_address(
                        '0x0E552307659E70bF61f918f96AA880Cdec40d7E2'
                    ),  # noqa: E501
                    timestamp=Timestamp(1607015339),
                    location=Location.BALANCER,
                    token0=A_AAVE,
                    token1=A_WETH,
                    amount0_in=AssetAmount(FVal('21.131588430448123904')),
                    amount1_in=AssetAmount(ZERO),
                    amount0_out=AssetAmount(ZERO),
                    amount1_out=AssetAmount(FVal('3.055412574642681758')),
                ),
            ],
        ),
        AMMTrade(
            trade_type=TradeType.BUY,
            base_asset=A_AAVE,
            quote_asset=A_WETH,
            amount=AssetAmount(FVal('21.131588567541018817')),
            rate=Price(FVal('0.1435213742524287826717337545')),
            trade_index=0,
            swaps=[
                AMMSwap(
                    tx_hash=
                    '0x67c0e9a0fdd002d0b9d1cca0c8e4ca4d30435bbf57bbf0091396275efaea414b',  # noqa: E501
                    log_index=31,
                    address=string_to_ethereum_address(
                        '0x029f388aC4D5C8BfF490550ce0853221030E822b'
                    ),  # noqa: E501
                    from_address=string_to_ethereum_address(
                        '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56'
                    ),  # noqa: E501
                    to_address=string_to_ethereum_address(
                        '0x7c90a3cd7Ec80dd2F633ed562480AbbEEd3bE546'
                    ),  # noqa: E501
                    timestamp=Timestamp(1607015339),
                    location=Location.BALANCER,
                    token0=A_WETH,
                    token1=A_AAVE,
                    amount0_in=AssetAmount(FVal('3.0328346313504')),
                    amount1_in=AssetAmount(ZERO),
                    amount0_out=AssetAmount(ZERO),
                    amount1_out=AssetAmount(FVal('21.131588567541018817')),
                ),
            ],
        ),
    ]
Exemple #24
0
        to_address=ETH_ADDRESS1,
        value=FVal('12323'),
        gas=FVal('5000000'),
        gas_price=FVal('2100000000'),
        gas_used=FVal('1900000'),
        input_data=DUMMY_HASH,
        nonce=3,
    ),
]

margin_history = [
    MarginPosition(  # before query period -- BTC/EUR: 422.90
        location=Location.POLONIEX,
        open_time=Timestamp(1463184190),  # 14/05/2016
        close_time=Timestamp(1464393600),  # 28/05/2016
        profit_loss=AssetAmount(FVal(0.05)),
        pl_currency=A_BTC,
        fee=Fee(ZERO),
        fee_currency=A_BTC,
        link='1',
        notes='margin1',
    ),
    MarginPosition(  # before query period -- BTC/EUR: 542.87
        location=Location.POLONIEX,
        open_time=Timestamp(1472428800),  # 29/08/2016
        close_time=Timestamp(1473897600),  # 15/09/2016
        profit_loss=AssetAmount(FVal('-0.042')),
        pl_currency=A_BTC,
        fee=Fee(FVal('0.0001')),
        fee_currency=A_BTC,
        link='2',
Exemple #25
0
def test_get_trade_with_1_token_pool(
        rotkehlchen_api_server,
        ethereum_accounts,  # pylint: disable=unused-argument
        rotki_premium_credentials,  # pylint: disable=unused-argument
        start_with_valid_premium,  # pylint: disable=unused-argument
):
    """
    Test the special case of a swap within an 1 token pool.
    This can probably happen if the controller has since removed tokens from the pool.
    """
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
    setup = setup_balances(
        rotki,
        ethereum_accounts=ethereum_accounts,
        btc_accounts=None,
        original_queries=['zerion', 'logs', 'blocknobytime'],
    )
    with ExitStack() as stack:
        # patch ethereum/etherscan to not autodetect tokens
        setup.enter_ethereum_patches(stack)
        response = requests.get(
            api_url_for(rotkehlchen_api_server,
                        'balancertradeshistoryresource'),
            json={
                'from_timestamp': 1621358338,
                'to_timestamp': 1621358340,
            },
        )
        result = assert_proper_response_with_result(response)

    db_trades = rotki.data.db.get_amm_swaps()
    assert len(db_trades) == 29

    address_trades = result[BALANCER_TEST_ADDR4]
    assert len(address_trades) == 1
    assert address_trades[0] == AMMTrade(
        trade_type=TradeType.BUY,
        base_asset=A_WETH,
        quote_asset=A_WBTC,
        amount=AssetAmount(FVal('0.205421420618533148')),
        rate=Price(FVal('0.07606382992071615428519015532')),
        trade_index=0,
        swaps=[
            AMMSwap(
                tx_hash=
                '0x4f9e0d8aa660a5d3db276a1ade038f7027f29838dd22d5276571d2e4ea7131ae',  # noqa: E501
                log_index=84,
                address=string_to_ethereum_address(
                    BALANCER_TEST_ADDR4),  # noqa: E501
                from_address=string_to_ethereum_address(
                    '0xFD3dFB524B2dA40c8a6D703c62BE36b5D8540626'
                ),  # noqa: E501
                to_address=string_to_ethereum_address(
                    '0x582818356331877553F3E9Cf9557b48e5DdbD54a'
                ),  # noqa: E501
                timestamp=Timestamp(1621358339),
                location=Location.BALANCER,
                token0=A_WBTC,
                token1=A_WETH,
                amount0_in=AssetAmount(FVal('0.01562514')),
                amount1_in=AssetAmount(ZERO),
                amount0_out=AssetAmount(ZERO),
                amount1_out=AssetAmount(FVal('0.205421420618533148')),
            ),
        ],
    ).serialize()
Exemple #26
0
    def _consume_cointracking_entry(self, csv_row: Dict[str, Any]) -> None:
        """Consumes a cointracking entry row from the CSV and adds it into the database
        Can raise:
            - DeserializationError if something is wrong with the format of the expected values
            - UnsupportedCointrackingEntry if importing of this entry is not supported.
            - IndexError if the CSV file is corrupt
            - KeyError if the an expected CSV key is missing
            - UnknownAsset if one of the assets founds in the entry are not supported
        """
        row_type = csv_row['Type']
        timestamp = deserialize_timestamp_from_date(
            date=csv_row['Date'],
            formatstr='%d.%m.%Y %H:%M:%S',
            location='cointracking.info',
        )
        notes = csv_row['Comment']
        location = exchange_row_to_location(csv_row['Exchange'])

        fee = Fee(ZERO)
        fee_currency = A_USD  # whatever (used only if there is no fee)
        if csv_row['Fee'] != '':
            fee = deserialize_fee(csv_row['Fee'])
            fee_currency = Asset(csv_row['Cur.Fee'])

        if row_type in ('Gift/Tip', 'Trade', 'Income'):
            base_asset = Asset(csv_row['Cur.Buy'])
            quote_asset = None if csv_row['Cur.Sell'] == '' else Asset(
                csv_row['Cur.Sell'])
            if quote_asset is None and row_type not in ('Gift/Tip', 'Income'):
                raise DeserializationError(
                    'Got a trade entry with an empty quote asset')

            if quote_asset is None:
                # Really makes no difference as this is just a gift and the amount is zero
                quote_asset = A_USD
            pair = TradePair(
                f'{base_asset.identifier}_{quote_asset.identifier}')
            base_amount_bought = deserialize_asset_amount(csv_row['Buy'])
            if csv_row['Sell'] != '-':
                quote_amount_sold = deserialize_asset_amount(csv_row['Sell'])
            else:
                quote_amount_sold = AssetAmount(ZERO)
            rate = Price(quote_amount_sold / base_amount_bought)

            trade = Trade(
                timestamp=timestamp,
                location=location,
                pair=pair,
                trade_type=TradeType.
                BUY,  # It's always a buy during cointracking import
                amount=base_amount_bought,
                rate=rate,
                fee=fee,
                fee_currency=fee_currency,
                link='',
                notes=notes,
            )
            self.db.add_trades([trade])
        elif row_type == 'Deposit' or row_type == 'Withdrawal':
            category = deserialize_asset_movement_category(row_type.lower())
            if category == AssetMovementCategory.DEPOSIT:
                amount = deserialize_asset_amount(csv_row['Buy'])
                asset = Asset(csv_row['Cur.Buy'])
            else:
                amount = deserialize_asset_amount_force_positive(
                    csv_row['Sell'])
                asset = Asset(csv_row['Cur.Sell'])

            asset_movement = AssetMovement(
                location=location,
                category=category,
                address=None,
                transaction_id=None,
                timestamp=timestamp,
                asset=asset,
                amount=amount,
                fee=fee,
                fee_asset=fee_currency,
                link='',
            )
            self.db.add_asset_movements([asset_movement])
        else:
            raise UnsupportedCointrackingEntry(
                f'Unknown entrype type "{row_type}" encountered during cointracking '
                f'data import. Ignoring entry', )
Exemple #27
0
def check_result_of_history_creation(
    start_ts: Timestamp,
    end_ts: Timestamp,
    trade_history: List[Trade],
    margin_history: List[Trade],
    loan_history: Dict,
    asset_movements: List[AssetMovement],
    eth_transactions: List[EthereumTransaction],
) -> Dict[str, Any]:
    """This function offers Some simple assertions on the result of the
    created history. The entire processing part of the history is mocked
    away by this checking function"""
    assert start_ts == 0, 'should be same as given to process_history'
    assert end_ts == TEST_END_TS, 'should be same as given to process_history'

    # TODO: Add more assertions/check for each action
    # OR instead do it in tests for conversion of actions(trades, loans, deposits e.t.c.)
    # from exchange to our format for each exchange
    assert len(trade_history) == 9
    assert trade_history[0].location == 'kraken'
    assert trade_history[0].pair == 'ETH_EUR'
    assert trade_history[0].trade_type == TradeType.BUY
    assert trade_history[1].location == 'kraken'
    assert trade_history[1].pair == 'BTC_EUR'
    assert trade_history[1].trade_type == TradeType.BUY
    assert trade_history[2].location == 'bittrex'
    assert trade_history[2].pair == 'LTC_BTC'
    assert trade_history[2].trade_type == TradeType.BUY
    assert trade_history[3].location == 'bittrex'
    assert trade_history[3].pair == 'LTC_ETH'
    assert trade_history[3].trade_type == TradeType.SELL
    assert trade_history[4].location == 'binance'
    assert trade_history[4].pair == 'ETH_BTC'
    assert trade_history[4].trade_type == TradeType.BUY
    assert trade_history[5].location == 'binance'
    assert trade_history[5].pair == 'RDN_ETH'
    assert trade_history[5].trade_type == TradeType.SELL
    assert trade_history[6].location == 'poloniex'
    assert trade_history[6].pair == 'ETH_BTC'
    assert trade_history[6].trade_type == TradeType.SELL
    assert trade_history[7].location == 'poloniex'
    assert trade_history[7].pair == 'ETH_BTC'
    assert trade_history[7].trade_type == TradeType.BUY
    assert trade_history[8].location == 'poloniex'
    assert trade_history[8].pair == 'XMR_ETH'
    assert trade_history[8].trade_type == TradeType.BUY

    assert len(loan_history) == 2
    assert loan_history[0].currency == A_ETH
    assert loan_history[0].earned == AssetAmount(FVal('0.00000001'))
    assert loan_history[1].currency == A_BTC
    assert loan_history[1].earned == AssetAmount(FVal('0.00000005'))

    assert len(asset_movements) == 8
    assert asset_movements[0].exchange == 'kraken'
    assert asset_movements[0].category == 'deposit'
    assert asset_movements[0].asset == A_BTC
    assert asset_movements[1].exchange == 'kraken'
    assert asset_movements[1].category == 'deposit'
    assert asset_movements[1].asset == A_ETH
    assert asset_movements[2].exchange == 'kraken'
    assert asset_movements[2].category == 'withdrawal'
    assert asset_movements[2].asset == A_BTC
    assert asset_movements[3].exchange == 'kraken'
    assert asset_movements[3].category == 'withdrawal'
    assert asset_movements[3].asset == A_ETH
    assert asset_movements[4].exchange == 'poloniex'
    assert asset_movements[4].category == 'withdrawal'
    assert asset_movements[4].asset == A_BTC
    assert asset_movements[5].exchange == 'poloniex'
    assert asset_movements[5].category == 'withdrawal'
    assert asset_movements[5].asset == A_ETH
    assert asset_movements[6].exchange == 'poloniex'
    assert asset_movements[6].category == 'deposit'
    assert asset_movements[6].asset == A_BTC
    assert asset_movements[7].exchange == 'poloniex'
    assert asset_movements[7].category == 'deposit'
    assert asset_movements[7].asset == A_ETH

    # The history creation for these is not yet tested
    assert len(margin_history) == 0
    assert len(eth_transactions) == 0

    return {}
Exemple #28
0
# Method: `_calculate_events_balances`

LP_1_EVENTS = [
    LiquidityPoolEvent(
        tx_hash=
        '0xa9ce328d0e2d2fa8932890bfd4bc61411abd34a4aaa48fc8b853c873a55ea824',
        log_index=263,
        address=TEST_ADDRESS_1,
        timestamp=Timestamp(1604273256),
        event_type=EventType.MINT,
        pool_address=deserialize_ethereum_address(
            "0x55111baD5bC368A2cb9ecc9FBC923296BeDb3b89"),  # noqa: E501
        token0=TOKEN_BASED,
        token1=ASSET_WETH,
        amount0=AssetAmount(FVal('605.773209925184996494')),
        amount1=AssetAmount(FVal('1.106631443395672732')),
        usd_price=Price(FVal('872.4689300619698095220125311431804')),
        lp_amount=AssetAmount(FVal('1.220680531244355402')),
    ),
    LiquidityPoolEvent(
        tx_hash=
        '0x27ddad4f187e965a3ee37257b75d297ff79b2663fd0a2d8d15f7efaccf1238fa',
        log_index=66,
        address=TEST_ADDRESS_1,
        timestamp=Timestamp(1604283808),
        event_type=EventType.BURN,
        pool_address=deserialize_ethereum_address(
            "0x55111baD5bC368A2cb9ecc9FBC923296BeDb3b89"),  # noqa: E501
        token0=TOKEN_BASED,
        token1=ASSET_WETH,
def test_ledger_actions_accounting(accountant):
    """Test for accounting for ledger actions

    Makes sure that Ledger actions are processed in accounting, range is respected
    and that they contribute to the "bought" amount per asset and that also if
    a rate is given then that is used instead of the queried price
    """
    ledger_actions_history = [
        LedgerAction(  # before range - read only for amount not profit
            identifier=1,
            timestamp=1435979735,  # 0.1 EUR per ETH
            action_type=LedgerActionType.INCOME,
            location=Location.EXTERNAL,
            asset=A_ETH,
            amount=AssetAmount(FVal(1)),
            rate=None,
            rate_asset=None,
            link=None,
            notes=None,
        ),
        LedgerAction(
            identifier=2,
            timestamp=1437279735,  # 250 EUR per BTC
            action_type=LedgerActionType.INCOME,
            location=Location.BLOCKCHAIN,
            asset=A_BTC,
            amount=AssetAmount(FVal(1)),
            rate=FVal('400'),
            rate_asset=A_EUR,
            link='foo',
            notes='we give a rate here',
        ),
        LedgerAction(
            identifier=3,
            timestamp=1447279735,  # 0.4 EUR per XMR
            action_type=LedgerActionType.DIVIDENDS_INCOME,
            location=Location.KRAKEN,
            asset=A_XMR,
            amount=AssetAmount(FVal(10)),
            rate=None,
            rate_asset=None,
            link=None,
            notes=None,
        ),
        LedgerAction(
            identifier=4,
            timestamp=1457279735,  # 1 EUR per ETH
            action_type=LedgerActionType.EXPENSE,
            location=Location.EXTERNAL,
            asset=A_ETH,
            amount=AssetAmount(FVal('0.1')),
            rate=None,
            rate_asset=None,
            link=None,
            notes=None,
        ),
        LedgerAction(
            identifier=5,
            timestamp=1467279735,  # 420 EUR per BTC
            action_type=LedgerActionType.LOSS,
            location=Location.EXTERNAL,
            asset=A_BTC,
            amount=AssetAmount(FVal('0.1')),
            rate=FVal(500),
            rate_asset=A_USD,
            link='foo2',
            notes='we give a rate here',
        ),
        LedgerAction(  # after range and should be completely ignored
            identifier=6,
            timestamp=1529693374,
            action_type=LedgerActionType.EXPENSE,
            location=Location.EXTERNAL,
            asset=A_ETH,
            amount=AssetAmount(FVal('0.5')),
            rate=FVal(400),
            rate_asset=A_EUR,
            link='foo3',
            notes='we give a rate here too but doesnt matter',
        )
    ]

    result = accounting_history_process(
        accountant=accountant,
        start_ts=1436979735,
        end_ts=1519693374,
        history_list=[],
        ledger_actions_list=ledger_actions_history,
    )
    assert accountant.events.cost_basis.get_calculated_asset_amount(
        A_BTC).is_close('0.9')
    assert accountant.events.cost_basis.get_calculated_asset_amount(
        A_ETH).is_close('0.9')
    assert accountant.events.cost_basis.get_calculated_asset_amount(
        A_XMR).is_close('10')
    # 400 * 1 + 0.4 * 10 - 1 * 0.1  - 500 * 0.9004 * 0.1 = 358.88
    expected_pnl = '358.88'
    assert FVal(result['overview']['ledger_actions_profit_loss']).is_close(
        expected_pnl)
    assert FVal(result['overview']['total_profit_loss']).is_close(expected_pnl)
    assert FVal(
        result['overview']['total_taxable_profit_loss']).is_close(expected_pnl)
Exemple #30
0
    def check_result_of_history_creation(
            start_ts: Timestamp,
            end_ts: Timestamp,
            trade_history: List[Union[Trade, MarginPosition]],
            loan_history: List[Loan],
            asset_movements: List[AssetMovement],
            eth_transactions: List[EthereumTransaction],
    ) -> Dict[str, Any]:
        """This function offers some simple assertions on the result of the
        created history. The entire processing part of the history is mocked
        away by this checking function"""
        if history_start_ts is None:
            assert start_ts == 0, 'if no start_ts is given it should be zero'
        else:
            assert start_ts == history_start_ts, 'should be same as given to process_history'
        if history_end_ts is not None:
            assert end_ts == history_end_ts, 'should be same as given to process_history'

        # TODO: Add more assertions/check for each action
        # OR instead do it in tests for conversion of actions(trades, loans, deposits e.t.c.)
        # from exchange to our format for each exchange
        assert len(trade_history) == 11
        assert isinstance(trade_history[0], Trade)
        assert trade_history[0].location == Location.KRAKEN
        assert trade_history[0].pair == 'ETH_EUR'
        assert trade_history[0].trade_type == TradeType.BUY
        assert isinstance(trade_history[1], Trade)
        assert trade_history[1].location == Location.KRAKEN
        assert trade_history[1].pair == 'BTC_EUR'
        assert trade_history[1].trade_type == TradeType.BUY
        assert isinstance(trade_history[2], Trade)
        assert trade_history[2].location == Location.BITTREX
        assert trade_history[2].pair == 'LTC_BTC'
        assert trade_history[2].trade_type == TradeType.BUY
        assert isinstance(trade_history[3], Trade)
        assert trade_history[3].location == Location.BITTREX
        assert trade_history[3].pair == 'LTC_ETH'
        assert trade_history[3].trade_type == TradeType.SELL
        assert isinstance(trade_history[4], MarginPosition)
        assert trade_history[4].profit_loss == FVal('0.05')
        assert isinstance(trade_history[5], Trade)
        assert trade_history[5].location == Location.BINANCE
        assert trade_history[5].pair == 'ETH_BTC'
        assert trade_history[5].trade_type == TradeType.BUY
        assert isinstance(trade_history[6], Trade)
        assert trade_history[6].location == Location.BINANCE
        assert trade_history[6].pair == 'RDN_ETH'
        assert trade_history[6].trade_type == TradeType.SELL
        assert isinstance(trade_history[7], Trade)
        assert trade_history[7].location == Location.POLONIEX
        assert trade_history[7].pair == 'ETH_BTC'
        assert trade_history[7].trade_type == TradeType.SELL
        assert isinstance(trade_history[8], Trade)
        assert trade_history[8].location == Location.POLONIEX
        assert trade_history[8].pair == 'ETH_BTC'
        assert trade_history[8].trade_type == TradeType.BUY
        assert isinstance(trade_history[9], Trade)
        assert trade_history[9].location == Location.POLONIEX
        assert trade_history[9].pair == 'XMR_ETH'
        assert trade_history[9].trade_type == TradeType.BUY
        assert isinstance(trade_history[10], MarginPosition)
        assert trade_history[10].profit_loss == FVal('5E-9')

        assert len(loan_history) == 2
        assert loan_history[0].currency == A_ETH
        assert loan_history[0].earned == AssetAmount(FVal('0.00000001'))
        assert loan_history[1].currency == A_BTC
        assert loan_history[1].earned == AssetAmount(FVal('0.00000005'))

        assert len(asset_movements) == 11
        assert asset_movements[0].location == Location.POLONIEX
        assert asset_movements[0].category == AssetMovementCategory.WITHDRAWAL
        assert asset_movements[0].asset == A_BTC
        assert asset_movements[1].location == Location.POLONIEX
        assert asset_movements[1].category == AssetMovementCategory.WITHDRAWAL
        assert asset_movements[1].asset == A_ETH
        assert asset_movements[2].location == Location.POLONIEX
        assert asset_movements[2].category == AssetMovementCategory.DEPOSIT
        assert asset_movements[2].asset == A_BTC
        assert asset_movements[3].location == Location.POLONIEX
        assert asset_movements[3].category == AssetMovementCategory.DEPOSIT
        assert asset_movements[3].asset == A_ETH
        assert asset_movements[4].location == Location.BITMEX
        assert asset_movements[4].category == AssetMovementCategory.DEPOSIT
        assert asset_movements[4].asset == A_BTC
        assert asset_movements[5].location == Location.BITMEX
        assert asset_movements[5].category == AssetMovementCategory.WITHDRAWAL
        assert asset_movements[5].asset == A_BTC
        assert asset_movements[6].location == Location.BITMEX
        assert asset_movements[6].category == AssetMovementCategory.WITHDRAWAL
        assert asset_movements[6].asset == A_BTC
        assert asset_movements[7].location == Location.KRAKEN
        assert asset_movements[7].category == AssetMovementCategory.DEPOSIT
        assert asset_movements[7].asset == A_BTC
        assert asset_movements[8].location == Location.KRAKEN
        assert asset_movements[8].category == AssetMovementCategory.DEPOSIT
        assert asset_movements[8].asset == A_ETH
        assert asset_movements[9].location == Location.KRAKEN
        assert asset_movements[9].category == AssetMovementCategory.WITHDRAWAL
        assert asset_movements[9].asset == A_BTC
        assert asset_movements[10].location == Location.KRAKEN
        assert asset_movements[10].category == AssetMovementCategory.WITHDRAWAL
        assert asset_movements[10].asset == A_ETH

        # The history creation for these is not yet tested
        assert len(eth_transactions) == 3
        assert eth_transactions[0].block_number == 54092
        assert eth_transactions[0].tx_hash == hexstring_to_bytes(TX_HASH_STR1)
        assert eth_transactions[0].from_address == ETH_ADDRESS1
        assert eth_transactions[0].to_address == ''
        assert eth_transactions[0].value == FVal('11901464239480000000000000')
        assert eth_transactions[0].input_data == MOCK_INPUT_DATA
        assert eth_transactions[1].block_number == 54093
        assert eth_transactions[1].tx_hash == hexstring_to_bytes(TX_HASH_STR2)
        assert eth_transactions[1].from_address == ETH_ADDRESS2
        assert eth_transactions[1].to_address == ETH_ADDRESS1
        assert eth_transactions[1].value == FVal('40000300')
        assert eth_transactions[1].input_data == MOCK_INPUT_DATA
        assert eth_transactions[2].block_number == 54094
        assert eth_transactions[2].tx_hash == hexstring_to_bytes(TX_HASH_STR3)
        assert eth_transactions[2].from_address == ETH_ADDRESS3
        assert eth_transactions[2].to_address == ETH_ADDRESS1
        assert eth_transactions[2].value == FVal('500520300')
        assert eth_transactions[2].input_data == MOCK_INPUT_DATA

        return {}