1
0

Laying groundwork for both tax accounting and for repository history

This commit is contained in:
Jon Michael Aanes 2024-11-28 00:15:57 +01:00
parent 678390e881
commit c1ae958458
Signed by: Jmaa
SSH Key Fingerprint: SHA256:Ab0GfHGCblESJx7JRE4fj4bFy/KRpeLhi41y4pF3sNA
3 changed files with 87 additions and 57 deletions

View File

@ -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

View File

@ -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,

View File

@ -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