From 59cda73349e71df928efd543614c0767e8e2cff6 Mon Sep 17 00:00:00 2001 From: Jon Michael Aanes Date: Sun, 21 Jul 2024 14:56:57 +0200 Subject: [PATCH] Logging to file implemented. Still missing trades CSV. --- crypto_seller/__init__.py | 128 +++++++++++++++++++++++++++++++------- crypto_seller/__main__.py | 35 ++++++++--- 2 files changed, 131 insertions(+), 32 deletions(-) diff --git a/crypto_seller/__init__.py b/crypto_seller/__init__.py index a787dd1..1b6c1e4 100644 --- a/crypto_seller/__init__.py +++ b/crypto_seller/__init__.py @@ -1,18 +1,84 @@ """# Automatic Crypto Seller. -Description TODO. +This is a system for automatically trading individual financial assets a single +way, by using repeated trades over a period. + +The primary motivation is for trading low-volume crypto assets without +affecting the price too much. + +Supported sites: + +- [KuCoin](https://www.kucoin.com/): Online crypto-currency exchange. + +## Installation + +Install dependencies: + +```shell +pip install -r requirements.txt +``` + +## Usage + +Configure the sell rate: + +Todo: +---- +Run using from the top directory: + +```shell +python -m crypto_seller +``` + +The script will now automatically sell assets over time. The interval between +sell-offs are randomized to avoid front-running, and the amounts are also +randomized to avoid too consistent behaviour. + +The log will both be displayed in the shell, and be placed in a log file +`output/log.txt`. Every sell-off will be logged to the `output/trades.csv` +file, with as much information as possible. Keep both of these for tax +purposes, if relevant. + +Todo: +---- +## Auditing information + +The most relevant libraries for auditing are: + +- [`fin_depo`](https://gitfub.space/Jmaa/fin-depo): Library for programmatic + fetching of depository assets and in some cases allows for order placement. + This is the library that reads balances and places market orders. +- [`fin_defs`](https://gitfub.space/Jmaa/fin-defs): Definitions of financial + assets and instruments. Used by `fin_depo`. +- [`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 +cryptocurrencies, so it helps to keep track of the relevant trading +information. + +As mentioned above, keep (and backup) both the log file (`output/log.txt`) and +the trades file (`output/trades.csv`). + +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. """ __all__ = ['__version__', 'run_auto_sell'] -import datetime import dataclasses -import random -from typing import Any +import datetime import logging -from decimal import Decimal +import random import time - +from decimal import Decimal +from pathlib import Path +from typing import Any import fin_defs import fin_depo @@ -21,7 +87,17 @@ from . import config from ._version import __version__ logger = logging.getLogger(__name__) -logger.setLevel('INFO') + +################################################################################ +# Constants # + +PATH_OUTPUT = Path('./output').absolute() +PATH_LOG_FILE = PATH_OUTPUT / 'log.txt' +PATH_TRADES_FILE = PATH_OUTPUT / 'trades.csv' + +################################################################################ +# Main code # + @dataclasses.dataclass class AutoSellConfig: @@ -31,35 +107,41 @@ class AutoSellConfig: output_asset: fin_defs.Asset exit_when_empty: bool = True + def sample_from_range(rng: random.Random, range: tuple[Any, Any]) -> Any: multiplier = rng.random() if isinstance(range[0], Decimal): multiplier = Decimal(multiplier) return range[0] + (range[1] - range[0]) * multiplier + SELLER = fin_depo.defi_kucoin.KucoinDepoFetcher( - kucoin_secret = config.KUCOIN_SECRET, - kucoin_key = config.KUCOIN_KEY, - kucoin_pass = config.KUCOIN_PASS, + kucoin_secret=config.KUCOIN_SECRET, + kucoin_key=config.KUCOIN_KEY, + kucoin_pass=config.KUCOIN_PASS, ) + def determine_available_tokens(token: fin_defs.Asset) -> Decimal: return SELLER.get_depo().get_amount_of_asset(token) -def sell_tokens(input_asset: fin_defs.Asset, input_amount: Decimal, - output_asset: fin_defs.Asset) -> fin_depo.data.TradeOrderDetails: - #return SELLER.place_market_order(input_asset, input_amount, output_asset) + +def sell_tokens( + input_asset: fin_defs.Asset, input_amount: Decimal, output_asset: fin_defs.Asset, +) -> fin_depo.data.TradeOrderDetails: + # return SELLER.place_market_order(input_asset, input_amount, output_asset) return fin_depo.data.TradeOrderDetails( - input_asset= input_asset, - input_amount= input_amount, - output_asset= output_asset, - output_amount= input_amount, - fee_asset = input_asset, - fee_amount = input_amount, - order_id = 10001, - raw_order_details = {'TEST': 1, 'DATA': 2}, + input_asset=input_asset, + input_amount=input_amount, + output_asset=output_asset, + output_amount=input_amount, + fee_asset=input_asset, + fee_amount=input_amount, + order_id=10001, + raw_order_details={'TEST': 1, 'DATA': 2}, ) + def run_auto_sell(config: AutoSellConfig): rng = random.SystemRandom() @@ -73,7 +155,9 @@ def run_auto_sell(config: AutoSellConfig): amount_to_sell = min(input_amount_available, amount_to_sell) logger.info('Attempting to sell %s %s', amount_to_sell, config.input_asset) - order_details = sell_tokens(config.input_asset, amount_to_sell, config.output_asset) + order_details = sell_tokens( + config.input_asset, amount_to_sell, config.output_asset, + ) print(order_details) # TODO: Write order details to file. diff --git a/crypto_seller/__main__.py b/crypto_seller/__main__.py index bc07469..5d6dcee 100644 --- a/crypto_seller/__main__.py +++ b/crypto_seller/__main__.py @@ -1,26 +1,41 @@ import datetime import logging +import logging.handlers from decimal import Decimal + import fin_defs -from . import run_auto_sell, AutoSellConfig - -logging.basicConfig() +from . import PATH_LOG_FILE, AutoSellConfig, run_auto_sell +from . import logger as module_logger logger = logging.getLogger(__name__) -logger.setLevel('INFO') + def main(): + PATH_LOG_FILE.parent.mkdir(parents=True, exist_ok=True) + file_handler = logging.handlers.WatchedFileHandler(filename=PATH_LOG_FILE) + file_handler.setFormatter( + logging.Formatter( + '%(levelname)s:%(asctime)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S', + ), + ) + + logging.basicConfig(handlers=[logging.StreamHandler(), file_handler]) + + logger.setLevel('INFO') + module_logger.setLevel('INFO') + logger.info('Initializing crypto_seller') + config = AutoSellConfig( - input_amount_range= (Decimal('0.5'), Decimal('1')), - interval_range= (datetime.timedelta(seconds=2), - datetime.timedelta(seconds=6)), - input_asset = fin_defs.MPC, - output_asset = fin_defs.USDT, - exit_when_empty = True, + input_amount_range=(Decimal('0.5'), Decimal('1')), + interval_range=(datetime.timedelta(seconds=2), datetime.timedelta(seconds=6)), + input_asset=fin_defs.MPC, + output_asset=fin_defs.USDT, + exit_when_empty=True, ) run_auto_sell(config) + if __name__ == '__main__': main()