Compare commits
No commits in common. "9709c9b68aff72a11df0b90aa660c4e40bc47465" and "c8ce3837516aac85ffc3c1a8ef3c60be2fba9aa9" have entirely different histories.
9709c9b68a
...
c8ce383751
|
@ -95,20 +95,14 @@ most mature on the danish market, and does support KuCoin.
|
|||
|
||||
## TODO
|
||||
|
||||
- [ ] Allow multiple secrets configs
|
||||
- [ ] Allow multiple output directories
|
||||
- [ ] Fix `TimeoutError` issue occuring from slow Kucoin endpoint. Might
|
||||
require implementing own kucoin backend in `fin_depo`.
|
||||
- [ ] Present an overview of what the script will be doing
|
||||
* Sell what for what? How much, how often?
|
||||
* Give an estimate of how long it will take.
|
||||
* Wait 20 seconds before starting, to allow the user to review.
|
||||
- [ ] Ensure that a failure during selling results in a safe winding down of the system.
|
||||
* Catch runtime errors when selling
|
||||
* Show status
|
||||
* Show errors to log.
|
||||
* Stop loop and exit with results, and error indicator.
|
||||
- [X] Present an overview of what the script will be doing
|
||||
* Sell what for what? How much, how often?
|
||||
* Give an estimate of how long it will take.
|
||||
* Wait 20 seconds before starting, to allow the user to review.
|
||||
- [X] Document command-line arguments
|
||||
* Catch runtime errors when selling
|
||||
* Show errors to log.
|
||||
* Stop loop and exit with results, and error indicator.
|
||||
- [X] Document configuration
|
||||
- [X] Document code auditing
|
||||
- [X] Parse configuration from json.
|
||||
|
@ -125,7 +119,7 @@ import datetime
|
|||
import logging
|
||||
import random
|
||||
from collections.abc import Callable
|
||||
from decimal import ROUND_DOWN, Decimal
|
||||
from decimal import Decimal, ROUND_DOWN
|
||||
from typing import Any
|
||||
|
||||
import fin_defs
|
||||
|
@ -201,10 +195,7 @@ def sample_from_range(rng: random.Random, rang: tuple[Any, Any]) -> Any:
|
|||
ROUND_TO_WHOLE = Decimal('1')
|
||||
|
||||
|
||||
def run_auto_sell(
|
||||
config: AutoSellConfig,
|
||||
initial_rounds_to_skip: int = 0,
|
||||
) -> AutoSellRunResults:
|
||||
def run_auto_sell(config: AutoSellConfig, skip_first:int=0) -> AutoSellRunResults:
|
||||
"""Executes the sell-off.
|
||||
|
||||
Sell-offs are performed in rounds of sizes and with intervals randomly
|
||||
|
@ -221,22 +212,15 @@ def run_auto_sell(
|
|||
input_amount_available = config.seller.get_depo().get_amount_of_asset(
|
||||
config.input_asset,
|
||||
)
|
||||
logger.info(
|
||||
'Currently own %s %s',
|
||||
input_amount_available,
|
||||
config.input_asset.raw_short_name(),
|
||||
)
|
||||
logger.info('Currently own %s %s', input_amount_available, config.input_asset.raw_short_name())
|
||||
|
||||
if initial_rounds_to_skip > 0:
|
||||
initial_rounds_to_skip -= 1
|
||||
logger.info('skipping this round')
|
||||
if skip_first > 0:
|
||||
skip_first -= 1
|
||||
logger.info('Skipping this round')
|
||||
elif 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)
|
||||
amount_to_sell = amount_to_sell.quantize(
|
||||
ROUND_TO_WHOLE,
|
||||
rounding=ROUND_DOWN,
|
||||
)
|
||||
amount_to_sell = amount_to_sell.quantize(ROUND_TO_WHOLE, rounding=ROUND_DOWN)
|
||||
logger.info('Attempting to sell %s %s', amount_to_sell, config.input_asset)
|
||||
|
||||
order_details = config.seller.place_market_order(
|
||||
|
@ -281,12 +265,8 @@ def log_estimates(config: AutoSellConfig):
|
|||
expected_num_sell_offs = current_balance / average_amount
|
||||
expected_duration = average_sleep * float(expected_num_sell_offs)
|
||||
|
||||
fastest_duration = config.interval_range[0] * float(
|
||||
current_balance / config.input_amount_range[1],
|
||||
)
|
||||
slowest_duration = config.interval_range[1] * float(
|
||||
current_balance / config.input_amount_range[0],
|
||||
)
|
||||
fastest_duration = config.interval_range[0] * float(current_balance / config.input_amount_range[1])
|
||||
slowest_duration = config.interval_range[1] * float(current_balance / config.input_amount_range[0])
|
||||
|
||||
logger.info('Welcome to crypto seller!')
|
||||
config.sleep(1)
|
||||
|
@ -297,19 +277,11 @@ def log_estimates(config: AutoSellConfig):
|
|||
config.sleep(1)
|
||||
logger.info('')
|
||||
config.sleep(1)
|
||||
logger.info(
|
||||
'- Current balance: %s %s',
|
||||
current_balance,
|
||||
config.input_asset.raw_short_name(),
|
||||
)
|
||||
logger.info('- Current balance: %s %s', current_balance, config.input_asset.raw_short_name())
|
||||
config.sleep(1)
|
||||
logger.info('- Average sleep: %s seconds', average_sleep)
|
||||
config.sleep(1)
|
||||
logger.info(
|
||||
'- Average amount: %s %s',
|
||||
average_amount,
|
||||
config.input_asset.raw_short_name(),
|
||||
)
|
||||
logger.info('- Average amount: %s %s', average_amount, config.input_asset.raw_short_name())
|
||||
config.sleep(1)
|
||||
logger.info('- Expected counts: %s', expected_num_sell_offs)
|
||||
config.sleep(1)
|
||||
|
|
|
@ -58,14 +58,7 @@ def setup_logging():
|
|||
|
||||
|
||||
CLI_DESCRIPTION = """
|
||||
Script to automatically and with little effort sell financial assets from
|
||||
online accounts.
|
||||
""".strip()
|
||||
|
||||
CLI_EPILOG = """
|
||||
Author : Jon Michael Aanes (jonjmaa@gmail.com)
|
||||
Website : https://gitfub.space/Jmaa/crypto-seller
|
||||
License : MIT License (see website for full text)
|
||||
Sells financial assets from an online account.
|
||||
""".strip()
|
||||
|
||||
|
||||
|
@ -103,25 +96,9 @@ def load_config(config_path: Path) -> AutoSellConfig:
|
|||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
prog='crypto_seller',
|
||||
description=CLI_DESCRIPTION,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog=CLI_EPILOG,
|
||||
)
|
||||
parser.add_argument(
|
||||
'--config',
|
||||
type=Path,
|
||||
dest='config_file',
|
||||
required=True,
|
||||
help='Trading configuration file',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--wait-before-first',
|
||||
action='store_true',
|
||||
dest='wait_before_first',
|
||||
help='Skip the first sell-off round, and wait for the next.',
|
||||
)
|
||||
parser = argparse.ArgumentParser('crypto_seller', description=CLI_DESCRIPTION)
|
||||
parser.add_argument('--config', type=Path, dest='config_file', required=True)
|
||||
parser.add_argument('--skip-first', action='store_true', dest='skip_first')
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
|
@ -139,10 +116,7 @@ def main():
|
|||
log_estimates(auto_sell_config)
|
||||
|
||||
# Run auto sell
|
||||
results = run_auto_sell(
|
||||
auto_sell_config,
|
||||
initial_rounds_to_skip=args.wait_before_first and 1 or 0,
|
||||
)
|
||||
results = run_auto_sell(auto_sell_config, skip_first=args.skip_first and 1 or 0)
|
||||
|
||||
# Display results
|
||||
logging.info('Sell-offs complete')
|
||||
|
|
|
@ -8,6 +8,7 @@ import fin_depo
|
|||
import crypto_seller
|
||||
import crypto_seller.order_csv
|
||||
|
||||
import pytest
|
||||
|
||||
class SellerMock(fin_depo.data.DepoFetcher):
|
||||
def __init__(self, asset: fin_defs.Asset, initial_amount: Decimal):
|
||||
|
|
Loading…
Reference in New Issue
Block a user