1
0

Compare commits

...

2 Commits

Author SHA1 Message Date
39cee8c601
Ruff
Some checks failed
Python Ruff Code Quality / ruff (push) Failing after 23s
Run Python tests (through Pytest) / Test (push) Successful in 28s
Verify Python project can be installed, loaded and have version checked / Test (push) Successful in 24s
2024-11-28 00:45:46 +01:00
c1ae958458
Laying groundwork for both tax accounting and for repository history 2024-11-28 00:15:57 +01:00
4 changed files with 127 additions and 67 deletions

View File

@ -6,7 +6,7 @@ from decimal import Decimal
from typing import TypeVar from typing import TypeVar
import enforce_typing import enforce_typing
from fin_defs import Asset from fin_defs import Asset, AssetAmount
@enforce_typing.enforce_types @enforce_typing.enforce_types
@ -124,40 +124,36 @@ class TradeOrderDetails:
- `raw_order_details`: For storing arbitrary unstructured data from backend. - `raw_order_details`: For storing arbitrary unstructured data from backend.
""" """
input_asset: Asset input: AssetAmount
input_amount: Decimal output: AssetAmount
output_asset: Asset fee: AssetAmount
output_amount: Decimal
fee_asset: Asset
fee_amount: Decimal
executed_time: datetime.datetime executed_time: datetime.datetime
order_id: object order_id: object
raw_order_details: object raw_order_details: object
@enforce_typing.enforce_types @enforce_typing.enforce_types
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
class WithdrawalDetails: class WithdrawalDetails:
withdrawn_asset: Asset withdrawn: AssetAmount
withdrawn_amount: Decimal fee: AssetAmount
fee_asset: Asset
fee_amount: Decimal
executed_time: datetime.datetime executed_time: datetime.datetime
raw_details: object raw_details: object
@enforce_typing.enforce_types @enforce_typing.enforce_types
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
class DepositDetails: class DepositDetails:
deposit_asset: Asset deposit: AssetAmount
deposit_amount: Decimal fee: AssetAmount
fee_asset: Asset
fee_amount: Decimal
executed_time: datetime.datetime executed_time: datetime.datetime
raw_details: object raw_details: object
@enforce_typing.enforce_types
@dataclasses.dataclass(frozen=True)
class DoubleRegister:
input: AssetAmount | None
output: AssetAmount | None
executed_time: datetime.datetime

View File

@ -3,13 +3,22 @@
import datetime import datetime
import logging import logging
import time import time
from collections.abc import Iterator
from decimal import Decimal from decimal import Decimal
import fin_defs import fin_defs
import kucoin.client import kucoin.client
from fin_defs import AssetAmount
from .data import (DepoFetcher, DepoGroup, DepoSingle, TradeOrderDetails, from .data import (
WithdrawalDetails, DepositDetails) DepoFetcher,
DepoGroup,
DepoSingle,
DepositDetails,
DoubleRegister,
TradeOrderDetails,
WithdrawalDetails,
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -20,6 +29,36 @@ def parse_asset_from_ticker(ticker: str) -> fin_defs.Asset:
return fin_defs.WELL_KNOWN_SYMBOLS[ticker] 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): class KucoinDepoFetcher(DepoFetcher):
"""`Depo` fetcher for [Kucoin](https://www.kucoin.com), the online crypto currency exchange. """`Depo` fetcher for [Kucoin](https://www.kucoin.com), the online crypto currency exchange.
@ -145,7 +184,7 @@ class KucoinDepoFetcher(DepoFetcher):
del symbol, side, size, funds, input_amount del symbol, side, size, funds, input_amount
# Determine order details # 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]: def _get_withdrawals(self) -> list[WithdrawalDetails]:
raw_details = self.kucoin_client.get_withdrawals() raw_details = self.kucoin_client.get_withdrawals()
@ -161,15 +200,19 @@ class KucoinDepoFetcher(DepoFetcher):
tz=datetime.UTC, tz=datetime.UTC,
) )
withdrawals.append(WithdrawalDetails(withdrawn_asset, withdrawals.append(
withdrawn_amount, fee_asset, WithdrawalDetails(
fee_amount, executed_time, AssetAmount(withdrawn_asset, withdrawn_amount),
item)) AssetAmount(fee_asset, fee_amount),
executed_time,
item,
),
)
del item del item
return withdrawals return withdrawals
def _get_deposits(self) -> list[WithdrawalDetails]: def _get_deposits(self) -> list[DepositDetails]:
raw_details = self.kucoin_client.get_deposits() raw_details = self.kucoin_client.get_deposits()
deposits = [] deposits = []
@ -183,19 +226,49 @@ class KucoinDepoFetcher(DepoFetcher):
tz=datetime.UTC, tz=datetime.UTC,
) )
deposits.append(DepositDetails(deposit_asset, deposits.append(
deposit_amount, fee_asset, DepositDetails(
fee_amount, executed_time, AssetAmount(deposit_asset, deposit_amount),
item)) AssetAmount(fee_asset, fee_amount),
executed_time,
item,
),
)
del item del item
return deposits 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( def _get_order_details(
self, self,
order_id: str, order_id: str,
input_asset: fin_defs.Asset,
output_asset: fin_defs.Asset,
) -> TradeOrderDetails: ) -> TradeOrderDetails:
"""Determine the order details for the order with the given id. """Determine the order details for the order with the given id.
@ -203,29 +276,7 @@ class KucoinDepoFetcher(DepoFetcher):
order through their systems. order through their systems.
""" """
order_details = self._get_order_with_retries(order_id, num_retries=10) 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( def _get_order_with_retries(
self, self,

View File

@ -1,7 +1,5 @@
import datetime import datetime
from decimal import Decimal
import fin_defs
import pytest import pytest
import fin_depo import fin_depo
@ -19,6 +17,7 @@ fin_depo.defi_kucoin.logger.setLevel('INFO')
NOW = datetime.datetime.now(tz=datetime.UTC) NOW = datetime.datetime.now(tz=datetime.UTC)
def get_fetcher() -> fin_depo.defi_kucoin.KucoinDepoFetcher: def get_fetcher() -> fin_depo.defi_kucoin.KucoinDepoFetcher:
return fin_depo.defi_kucoin.KucoinDepoFetcher( return fin_depo.defi_kucoin.KucoinDepoFetcher(
secrets.KUCOIN_KEY, secrets.KUCOIN_KEY,
@ -26,6 +25,7 @@ def get_fetcher() -> fin_depo.defi_kucoin.KucoinDepoFetcher:
secrets.KUCOIN_PASS, secrets.KUCOIN_PASS,
) )
@needs_secrets @needs_secrets
def test_get_depo(): def test_get_depo():
"""Can inspect kucoin depository.""" """Can inspect kucoin depository."""
@ -35,14 +35,28 @@ def test_get_depo():
for nested_depo in depo.nested: for nested_depo in depo.nested:
assert isinstance(nested_depo, fin_depo.data.DepoSingle) assert isinstance(nested_depo, fin_depo.data.DepoSingle)
@needs_secrets @needs_secrets
def test_get_withdrawals(): def test_get_withdrawals():
withdrawals = get_fetcher()._get_withdrawals() withdrawals = get_fetcher()._get_withdrawals()
print(withdrawals)
assert len(withdrawals) > 0 assert len(withdrawals) > 0
@needs_secrets @needs_secrets
def test_get_deposits(): def test_get_deposits():
deposits = get_fetcher()._get_deposits() deposits = get_fetcher()._get_deposits()
print(deposits)
assert len(deposits) > 0 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

View File

@ -106,4 +106,3 @@ def test_place_sell_side_order():
assert order_details.fee_amount is not None assert order_details.fee_amount is not None
assert NOW <= order_details.executed_time <= NOW + datetime.timedelta(minutes=10) assert NOW <= order_details.executed_time <= NOW + datetime.timedelta(minutes=10)