diff --git a/fin_depo/data.py b/fin_depo/data.py index 9ee66fa..f8b2056 100644 --- a/fin_depo/data.py +++ b/fin_depo/data.py @@ -6,7 +6,7 @@ from decimal import Decimal from typing import TypeVar import enforce_typing -from fin_defs import Asset +from fin_defs import Asset, AssetAmount @enforce_typing.enforce_types @@ -124,12 +124,9 @@ class TradeOrderDetails: - `raw_order_details`: For storing arbitrary unstructured data from backend. """ - input_asset: Asset - input_amount: Decimal - output_asset: Asset - output_amount: Decimal - fee_asset: Asset - fee_amount: Decimal + input: AssetAmount + output: AssetAmount + fee: AssetAmount executed_time: datetime.datetime order_id: object @@ -138,26 +135,22 @@ class TradeOrderDetails: @enforce_typing.enforce_types @dataclasses.dataclass(frozen=True) class WithdrawalDetails: - withdrawn_asset: Asset - withdrawn_amount: Decimal - - fee_asset: Asset - fee_amount: Decimal - + withdrawn: AssetAmount + fee: AssetAmount executed_time: datetime.datetime - raw_details: object @enforce_typing.enforce_types @dataclasses.dataclass(frozen=True) class DepositDetails: - deposit_asset: Asset - deposit_amount: Decimal - - fee_asset: Asset - fee_amount: Decimal - + deposit: AssetAmount + fee: AssetAmount executed_time: datetime.datetime - raw_details: object +@enforce_typing.enforce_types +@dataclasses.dataclass(frozen=True) +class DoubleRegister: + input: AssetAmount | None + output: AssetAmount | None + executed_time: datetime.datetime diff --git a/fin_depo/defi_kucoin.py b/fin_depo/defi_kucoin.py index b3b7e55..c99c8e2 100644 --- a/fin_depo/defi_kucoin.py +++ b/fin_depo/defi_kucoin.py @@ -3,13 +3,15 @@ import datetime import logging import time +from collections.abc import Iterator from decimal import Decimal import fin_defs +from fin_defs import AssetAmount import kucoin.client from .data import (DepoFetcher, DepoGroup, DepoSingle, TradeOrderDetails, - WithdrawalDetails, DepositDetails) + WithdrawalDetails, DepositDetails, DoubleRegister) logger = logging.getLogger(__name__) @@ -19,6 +21,33 @@ def parse_asset_from_ticker(ticker: str) -> fin_defs.Asset: return fin_defs.CryptoCurrency('KCS', coingecko_id='kucoin-shares') return fin_defs.WELL_KNOWN_SYMBOLS[ticker] +def order_from_json(order_details: dict): + (asset, base_asset) = [parse_asset_from_ticker(a) for a in order_details['symbol'].split('-')] + + # Convert from kucoin results + if order_details['side'] == 'buy': + input_asset, output_asset = base_asset, asset + input_amount_final = Decimal(order_details['dealFunds']) + output_amount_final = Decimal(order_details['dealSize']) + else: + input_asset, output_asset = asset, base_asset + output_amount_final = Decimal(order_details['dealFunds']) + input_amount_final = Decimal(order_details['dealSize']) + + fee_asset = parse_asset_from_ticker(order_details['feeCurrency']) + + return TradeOrderDetails( + input=AssetAmount(input_asset, input_amount_final), + output=AssetAmount(output_asset, output_amount_final), + fee=AssetAmount(fee_asset, Decimal(order_details['fee'])), + executed_time=datetime.datetime.fromtimestamp( + order_details['createdAt'] / 1000, + tz=datetime.UTC, + ), + order_id=order_details['id'], + raw_order_details=order_details, + ) + class KucoinDepoFetcher(DepoFetcher): """`Depo` fetcher for [Kucoin](https://www.kucoin.com), the online crypto currency exchange. @@ -145,7 +174,7 @@ class KucoinDepoFetcher(DepoFetcher): del symbol, side, size, funds, input_amount # Determine order details - return self._get_order_details(response['orderId'], input_asset, output_asset) + return self._get_order_details(response['orderId']) def _get_withdrawals(self) -> list[WithdrawalDetails]: raw_details = self.kucoin_client.get_withdrawals() @@ -161,15 +190,15 @@ class KucoinDepoFetcher(DepoFetcher): tz=datetime.UTC, ) - withdrawals.append(WithdrawalDetails(withdrawn_asset, - withdrawn_amount, fee_asset, - fee_amount, executed_time, + withdrawals.append(WithdrawalDetails(AssetAmount(withdrawn_asset, withdrawn_amount), + AssetAmount(fee_asset, fee_amount), + executed_time, item)) del item return withdrawals - def _get_deposits(self) -> list[WithdrawalDetails]: + def _get_deposits(self) -> list[DepositDetails]: raw_details = self.kucoin_client.get_deposits() deposits = [] @@ -183,19 +212,38 @@ class KucoinDepoFetcher(DepoFetcher): tz=datetime.UTC, ) - deposits.append(DepositDetails(deposit_asset, - deposit_amount, fee_asset, - fee_amount, executed_time, - item)) + deposits.append(DepositDetails(AssetAmount(deposit_asset, deposit_amount), + AssetAmount(fee_asset, fee_amount), + executed_time, item)) del item return deposits + def _get_historic_spot_orders(self) -> Iterator[TradeOrderDetails]: + end_time = datetime.datetime.now(tz=datetime.UTC) + for _weeks_back in range(20): + end_time = end_time - datetime.timedelta(days=7) + timestamp = int(end_time.timestamp()*1000) + print(timestamp) + raw_details = self.kucoin_client.get_orders(end=timestamp) + yield from (order_from_json(item) for item in raw_details['items']) + del _weeks_back, raw_details + + def _get_double_registers(self) -> list[DoubleRegister]: + deposits = self._get_deposits() + withdrawals = self._get_withdrawals() + spot_trades = self._get_historic_spot_orders() + + double_registers = [] + double_registers += [DoubleRegister(d.deposit, None, d.executed_time) for d in deposits] + double_registers += [DoubleRegister(None, d.withdrawn, d.executed_time) for d in withdrawals] + double_registers += [DoubleRegister(d.input, d.output, d.executed_time) for d in spot_trades] + double_registers.sort(key=lambda x:x.executed_time) + return double_registers + def _get_order_details( self, order_id: str, - input_asset: fin_defs.Asset, - output_asset: fin_defs.Asset, ) -> TradeOrderDetails: """Determine the order details for the order with the given id. @@ -203,29 +251,8 @@ class KucoinDepoFetcher(DepoFetcher): order through their systems. """ order_details = self._get_order_with_retries(order_id, num_retries=10) + return order_from_json(order_details) - # Convert from kucoin results - if input_asset == fin_defs.USDT: - input_amount_final = Decimal(order_details['dealFunds']) - output_amount_final = Decimal(order_details['dealSize']) - else: - output_amount_final = Decimal(order_details['dealFunds']) - input_amount_final = Decimal(order_details['dealSize']) - - return TradeOrderDetails( - input_asset=input_asset, - input_amount=input_amount_final, - output_asset=output_asset, - output_amount=output_amount_final, - fee_asset=parse_asset_from_ticker(order_details['feeCurrency']), - fee_amount=Decimal(order_details['fee']), - executed_time=datetime.datetime.fromtimestamp( - order_details['createdAt'] / 1000, - tz=datetime.UTC, - ), - order_id=order_id, - raw_order_details=order_details, - ) def _get_order_with_retries( self, diff --git a/test/test_kucoin.py b/test/test_kucoin.py index e8923ff..613c85c 100644 --- a/test/test_kucoin.py +++ b/test/test_kucoin.py @@ -38,11 +38,21 @@ def test_get_depo(): @needs_secrets def test_get_withdrawals(): withdrawals = get_fetcher()._get_withdrawals() - print(withdrawals) assert len(withdrawals) > 0 @needs_secrets def test_get_deposits(): deposits = get_fetcher()._get_deposits() - print(deposits) assert len(deposits) > 0 + +@needs_secrets +def test_get_historic_spot_orders(): + orders = get_fetcher()._get_historic_spot_orders() + assert next(orders) + +@needs_secrets +def test_get_double_registers(): + double_registers = get_fetcher()._get_double_registers() + print(double_registers) + assert len(double_registers) > 0 + assert False