From af48230498e5e909769be866373f9ac02f1d193a Mon Sep 17 00:00:00 2001 From: Jon Michael Aanes Date: Mon, 22 Jul 2024 14:12:05 +0200 Subject: [PATCH] Present some starts on completion. --- crypto_seller/__init__.py | 59 +++++++++++++++++++++++++++++++++------ crypto_seller/__main__.py | 6 ++-- test/test_auto_sell.py | 11 ++++++-- 3 files changed, 64 insertions(+), 12 deletions(-) diff --git a/crypto_seller/__init__.py b/crypto_seller/__init__.py index 51d3960..2705d42 100644 --- a/crypto_seller/__init__.py +++ b/crypto_seller/__init__.py @@ -20,10 +20,6 @@ pip install -r requirements.txt ## Usage -Configure the sell rate: - -TODO - Run using from the top directory: ```shell @@ -51,8 +47,6 @@ The most relevant libraries for auditing are: - [`python-kucoin`](https://python-kucoin.readthedocs.io/en/latest/) is used by `fin_depo` for providing KuCoin support. -TODO - ## Taxation Some parts of the world suffers from weird and draconian taxation rules on @@ -66,6 +60,14 @@ To help with tax reporting, it might be useful to sign up to tax oriented websites. For example, [CryptoSkat](https://cryptoskat.dk/) seems to be the most mature on the danish market, and does support KuCoin. +## TODO + +- [ ] Log all trades to CSV file. +- [ ] Parse configuration from json. +- [X] Collect information during the run and output after run +- [ ] Document configuration +- [ ] Document auditing + """ __all__ = ['__version__', 'run_auto_sell'] @@ -109,6 +111,19 @@ class AutoSellConfig: exit_when_empty: bool = True +@dataclasses.dataclass(frozen=True) +class AutoSellRunResults: + order_details: list[fin_depo.data.TradeOrderDetails] + total_duration: datetime.timedelta + total_sleep_duration: datetime.timedelta + + def total_input_amount(self) -> Decimal: + return sum(o.input_amount for o in self.order_details) + + def total_output_amount(self) -> Decimal: + return sum(o.output_amount for o in self.order_details) + + def sample_from_range(rng: random.Random, range: tuple[Any, Any]) -> Any: multiplier = rng.random() if isinstance(range[0], Decimal): @@ -116,9 +131,13 @@ def sample_from_range(rng: random.Random, range: tuple[Any, Any]) -> Any: return range[0] + (range[1] - range[0]) * multiplier -def run_auto_sell(config: AutoSellConfig): +def run_auto_sell(config: AutoSellConfig) -> AutoSellRunResults: rng = random.SystemRandom() + time_start = datetime.datetime.now(tz=datetime.UTC) + all_executed_orders: list[fin_depo.data.TradeOrderDetails] = [] + total_sleep_duration = datetime.timedelta(seconds=0) + while True: # Check that account has tokens. input_amount_available = config.seller.get_depo().get_amount_of_asset( @@ -132,10 +151,13 @@ def run_auto_sell(config: AutoSellConfig): logger.info('Attempting to sell %s %s', amount_to_sell, config.input_asset) order_details = config.seller.place_market_order( - config.input_asset, amount_to_sell, config.output_asset, + config.input_asset, + amount_to_sell, + config.output_asset, ) print(order_details) + all_executed_orders.append(order_details) # TODO: Write order details to file. del amount_to_sell @@ -147,5 +169,26 @@ def run_auto_sell(config: AutoSellConfig): time_to_sleep_secs = time_to_sleep.total_seconds() logger.info('Sleeping %s (%d seconds)', time_to_sleep, time_to_sleep_secs) config.sleep(time_to_sleep_secs) + total_sleep_duration += time_to_sleep del input_amount_available + + time_end = datetime.datetime.now(tz=datetime.UTC) + + return AutoSellRunResults( + all_executed_orders, + total_duration=time_end - time_start, + total_sleep_duration=total_sleep_duration, + ) + + +def log_results(results: AutoSellRunResults): + logger.info('Stats:') + logger.info('- Num orders: %s', len(results.order_details)) + logger.info('- Total sold: %s', results.total_input_amount()) + logger.info('- Total gained: %s', results.total_output_amount()) + logger.info( + '- Total duration: %s (%s spent sleeping)', + results.total_duration, + results.total_sleep_duration, + ) diff --git a/crypto_seller/__main__.py b/crypto_seller/__main__.py index ebbbda7..088f214 100644 --- a/crypto_seller/__main__.py +++ b/crypto_seller/__main__.py @@ -6,7 +6,7 @@ from decimal import Decimal import fin_defs -from . import PATH_LOG_FILE, AutoSellConfig, config, run_auto_sell +from . import PATH_LOG_FILE, AutoSellConfig, config, run_auto_sell, log_results from . import logger as module_logger logger = logging.getLogger(__name__) @@ -47,7 +47,9 @@ def main(): seller=seller_backend, sleep=time.sleep, ) - run_auto_sell(auto_sell_config) + results = run_auto_sell(auto_sell_config) + logging.info('Sell-offs complete') + log_results(results) if __name__ == '__main__': diff --git a/test/test_auto_sell.py b/test/test_auto_sell.py index 87b7c89..e28302e 100644 --- a/test/test_auto_sell.py +++ b/test/test_auto_sell.py @@ -32,7 +32,7 @@ class SellerMock(fin_depo.data.DepoFetcher): input_asset=input_asset, input_amount=input_amount, output_asset=output_asset, - output_amount=input_amount, # TODO? + output_amount=input_amount, fee_asset=input_asset, fee_amount=Decimal(0), order_id=10000000 - self.amount_left, @@ -62,7 +62,14 @@ def test_auto_run(): sleep=sleep_mock, ) - crypto_seller.run_auto_sell(config) + results = crypto_seller.run_auto_sell(config) + # Check results + assert len(results.order_details) == 1000 + assert results.total_sleep_duration == datetime.timedelta(seconds=1000) + assert results.total_input_amount() == 1000 + assert results.total_output_amount() == 1000 + + # Check mocks agree assert seller_mock.amount_left == 0 assert sleep_mock.time_slept == 1000