1
0

Logging to file implemented. Still missing trades CSV.
Some checks failed
Test Python / Test (push) Failing after 24s

This commit is contained in:
Jon Michael Aanes 2024-07-21 14:56:57 +02:00
parent 113f1ca158
commit 59cda73349
Signed by: Jmaa
SSH Key Fingerprint: SHA256:Ab0GfHGCblESJx7JRE4fj4bFy/KRpeLhi41y4pF3sNA
2 changed files with 131 additions and 32 deletions

View File

@ -1,18 +1,84 @@
"""# Automatic Crypto Seller. """# 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'] __all__ = ['__version__', 'run_auto_sell']
import datetime
import dataclasses import dataclasses
import random import datetime
from typing import Any
import logging import logging
from decimal import Decimal import random
import time import time
from decimal import Decimal
from pathlib import Path
from typing import Any
import fin_defs import fin_defs
import fin_depo import fin_depo
@ -21,7 +87,17 @@ from . import config
from ._version import __version__ from ._version import __version__
logger = logging.getLogger(__name__) 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 @dataclasses.dataclass
class AutoSellConfig: class AutoSellConfig:
@ -31,23 +107,28 @@ class AutoSellConfig:
output_asset: fin_defs.Asset output_asset: fin_defs.Asset
exit_when_empty: bool = True exit_when_empty: bool = True
def sample_from_range(rng: random.Random, range: tuple[Any, Any]) -> Any: def sample_from_range(rng: random.Random, range: tuple[Any, Any]) -> Any:
multiplier = rng.random() multiplier = rng.random()
if isinstance(range[0], Decimal): if isinstance(range[0], Decimal):
multiplier = Decimal(multiplier) multiplier = Decimal(multiplier)
return range[0] + (range[1] - range[0]) * multiplier return range[0] + (range[1] - range[0]) * multiplier
SELLER = fin_depo.defi_kucoin.KucoinDepoFetcher( SELLER = fin_depo.defi_kucoin.KucoinDepoFetcher(
kucoin_secret=config.KUCOIN_SECRET, kucoin_secret=config.KUCOIN_SECRET,
kucoin_key=config.KUCOIN_KEY, kucoin_key=config.KUCOIN_KEY,
kucoin_pass=config.KUCOIN_PASS, kucoin_pass=config.KUCOIN_PASS,
) )
def determine_available_tokens(token: fin_defs.Asset) -> Decimal: def determine_available_tokens(token: fin_defs.Asset) -> Decimal:
return SELLER.get_depo().get_amount_of_asset(token) 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: 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 SELLER.place_market_order(input_asset, input_amount, output_asset)
return fin_depo.data.TradeOrderDetails( return fin_depo.data.TradeOrderDetails(
input_asset=input_asset, input_asset=input_asset,
@ -60,6 +141,7 @@ def sell_tokens(input_asset: fin_defs.Asset, input_amount: Decimal,
raw_order_details={'TEST': 1, 'DATA': 2}, raw_order_details={'TEST': 1, 'DATA': 2},
) )
def run_auto_sell(config: AutoSellConfig): def run_auto_sell(config: AutoSellConfig):
rng = random.SystemRandom() rng = random.SystemRandom()
@ -73,7 +155,9 @@ def run_auto_sell(config: AutoSellConfig):
amount_to_sell = min(input_amount_available, amount_to_sell) amount_to_sell = min(input_amount_available, amount_to_sell)
logger.info('Attempting to sell %s %s', amount_to_sell, config.input_asset) 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) print(order_details)
# TODO: Write order details to file. # TODO: Write order details to file.

View File

@ -1,26 +1,41 @@
import datetime import datetime
import logging import logging
import logging.handlers
from decimal import Decimal from decimal import Decimal
import fin_defs import fin_defs
from . import run_auto_sell, AutoSellConfig from . import PATH_LOG_FILE, AutoSellConfig, run_auto_sell
from . import logger as module_logger
logging.basicConfig()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logger.setLevel('INFO')
def main(): 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') logger.info('Initializing crypto_seller')
config = AutoSellConfig( config = AutoSellConfig(
input_amount_range=(Decimal('0.5'), Decimal('1')), input_amount_range=(Decimal('0.5'), Decimal('1')),
interval_range= (datetime.timedelta(seconds=2), interval_range=(datetime.timedelta(seconds=2), datetime.timedelta(seconds=6)),
datetime.timedelta(seconds=6)),
input_asset=fin_defs.MPC, input_asset=fin_defs.MPC,
output_asset=fin_defs.USDT, output_asset=fin_defs.USDT,
exit_when_empty=True, exit_when_empty=True,
) )
run_auto_sell(config) run_auto_sell(config)
if __name__ == '__main__': if __name__ == '__main__':
main() main()