2024-07-17 19:49:19 +00:00
|
|
|
"""# Automatic Crypto Seller.
|
|
|
|
|
2024-07-22 11:21:41 +00:00
|
|
|
This is a wrapper about [`fin_depo`] for automatically individual financial
|
|
|
|
assets a one way, by using repeated trades over a period.
|
2024-07-21 12:56:57 +00:00
|
|
|
|
2024-07-22 11:21:41 +00:00
|
|
|
The primary motivation is for trading low-volume crypto assets slowly without
|
2024-07-21 12:56:57 +00:00
|
|
|
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:
|
|
|
|
|
2024-07-22 11:21:41 +00:00
|
|
|
TODO
|
|
|
|
|
2024-07-21 12:56:57 +00:00
|
|
|
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.
|
|
|
|
|
|
|
|
## 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.
|
|
|
|
|
2024-07-22 11:36:52 +00:00
|
|
|
TODO
|
2024-07-21 12:56:57 +00:00
|
|
|
|
|
|
|
## 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.
|
2024-07-22 11:36:52 +00:00
|
|
|
|
2024-07-17 19:49:19 +00:00
|
|
|
"""
|
|
|
|
|
2024-07-17 20:21:58 +00:00
|
|
|
__all__ = ['__version__', 'run_auto_sell']
|
|
|
|
|
|
|
|
import dataclasses
|
2024-07-21 12:56:57 +00:00
|
|
|
import datetime
|
2024-07-17 20:21:58 +00:00
|
|
|
import logging
|
2024-07-21 12:56:57 +00:00
|
|
|
import random
|
2024-07-17 20:21:58 +00:00
|
|
|
import time
|
2024-07-22 11:36:52 +00:00
|
|
|
from collections.abc import Callable
|
2024-07-21 12:56:57 +00:00
|
|
|
from decimal import Decimal
|
|
|
|
from pathlib import Path
|
2024-07-22 11:36:52 +00:00
|
|
|
from typing import Any
|
2024-07-17 20:21:58 +00:00
|
|
|
|
|
|
|
import fin_defs
|
2024-07-21 10:00:55 +00:00
|
|
|
import fin_depo
|
2024-07-17 19:49:19 +00:00
|
|
|
|
|
|
|
from ._version import __version__
|
2024-07-17 20:21:58 +00:00
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
2024-07-21 12:56:57 +00:00
|
|
|
|
|
|
|
################################################################################
|
|
|
|
# Constants #
|
|
|
|
|
|
|
|
PATH_OUTPUT = Path('./output').absolute()
|
|
|
|
PATH_LOG_FILE = PATH_OUTPUT / 'log.txt'
|
|
|
|
PATH_TRADES_FILE = PATH_OUTPUT / 'trades.csv'
|
|
|
|
|
|
|
|
################################################################################
|
|
|
|
# Main code #
|
|
|
|
|
2024-07-17 20:21:58 +00:00
|
|
|
|
2024-07-22 11:36:52 +00:00
|
|
|
@dataclasses.dataclass(frozen=True)
|
2024-07-17 20:21:58 +00:00
|
|
|
class AutoSellConfig:
|
2024-07-21 10:00:55 +00:00
|
|
|
interval_range: tuple[datetime.timedelta, datetime.timedelta]
|
|
|
|
input_asset: fin_defs.Asset
|
|
|
|
input_amount_range: tuple[Decimal, Decimal]
|
|
|
|
output_asset: fin_defs.Asset
|
2024-07-22 11:31:53 +00:00
|
|
|
seller: fin_depo.defi_kucoin.KucoinDepoFetcher
|
2024-07-22 11:36:52 +00:00
|
|
|
sleep: Callable[[float], None]
|
2024-07-17 20:21:58 +00:00
|
|
|
exit_when_empty: bool = True
|
|
|
|
|
2024-07-22 11:36:52 +00:00
|
|
|
|
2024-07-17 20:21:58 +00:00
|
|
|
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
|
|
|
|
|
2024-07-22 11:36:52 +00:00
|
|
|
|
2024-07-22 11:31:53 +00:00
|
|
|
def run_auto_sell(config: AutoSellConfig):
|
2024-07-17 20:21:58 +00:00
|
|
|
rng = random.SystemRandom()
|
|
|
|
|
|
|
|
while True:
|
|
|
|
# Check that account has tokens.
|
2024-07-22 11:36:52 +00:00
|
|
|
input_amount_available = config.seller.get_depo().get_amount_of_asset(
|
|
|
|
config.input_asset,
|
|
|
|
)
|
2024-07-21 10:00:55 +00:00
|
|
|
logger.info('Currently own %s %s', input_amount_available, config.input_asset)
|
2024-07-17 20:21:58 +00:00
|
|
|
|
2024-07-21 10:00:55 +00:00
|
|
|
if input_amount_available > 0:
|
|
|
|
amount_to_sell = sample_from_range(rng, config.input_amount_range)
|
|
|
|
amount_to_sell = min(input_amount_available, amount_to_sell)
|
|
|
|
logger.info('Attempting to sell %s %s', amount_to_sell, config.input_asset)
|
2024-07-17 20:21:58 +00:00
|
|
|
|
2024-07-22 11:36:52 +00:00
|
|
|
order_details = config.seller.place_market_order(
|
|
|
|
config.input_asset, amount_to_sell, config.output_asset,
|
|
|
|
)
|
2024-07-22 11:21:41 +00:00
|
|
|
|
2024-07-21 10:00:55 +00:00
|
|
|
print(order_details)
|
|
|
|
# TODO: Write order details to file.
|
2024-07-17 20:21:58 +00:00
|
|
|
|
2024-07-21 10:00:55 +00:00
|
|
|
del amount_to_sell
|
2024-07-17 20:21:58 +00:00
|
|
|
elif config.exit_when_empty:
|
|
|
|
break
|
|
|
|
|
|
|
|
# Time out
|
2024-07-21 10:00:55 +00:00
|
|
|
time_to_sleep = sample_from_range(rng, config.interval_range)
|
2024-07-17 20:21:58 +00:00
|
|
|
time_to_sleep_secs = time_to_sleep.total_seconds()
|
|
|
|
logger.info('Sleeping %s (%d seconds)', time_to_sleep, time_to_sleep_secs)
|
2024-07-22 11:31:53 +00:00
|
|
|
config.sleep(time_to_sleep_secs)
|
2024-07-17 20:21:58 +00:00
|
|
|
|
2024-07-21 10:00:55 +00:00
|
|
|
del input_amount_available
|