Compare commits
No commits in common. "92c7f3e5414a046defbc7745458859e6aa47a460" and "9709c9b68aff72a11df0b90aa660c4e40bc47465" have entirely different histories.
92c7f3e541
...
9709c9b68a
|
@ -216,65 +216,50 @@ def run_auto_sell(
|
||||||
all_executed_orders: list[fin_depo.data.TradeOrderDetails] = []
|
all_executed_orders: list[fin_depo.data.TradeOrderDetails] = []
|
||||||
total_sleep_duration = datetime.timedelta(seconds=0)
|
total_sleep_duration = datetime.timedelta(seconds=0)
|
||||||
|
|
||||||
try:
|
while True:
|
||||||
while True:
|
# Check that account has tokens.
|
||||||
# Check that account has tokens.
|
input_amount_available = config.seller.get_depo().get_amount_of_asset(
|
||||||
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(),
|
||||||
|
)
|
||||||
|
|
||||||
|
if initial_rounds_to_skip > 0:
|
||||||
|
initial_rounds_to_skip -= 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,
|
||||||
|
)
|
||||||
|
logger.info('Attempting to sell %s %s', amount_to_sell, config.input_asset)
|
||||||
|
|
||||||
|
order_details = config.seller.place_market_order(
|
||||||
config.input_asset,
|
config.input_asset,
|
||||||
)
|
amount_to_sell,
|
||||||
logger.info(
|
config.output_asset,
|
||||||
'Currently own %s %s',
|
|
||||||
input_amount_available,
|
|
||||||
config.input_asset.raw_short_name(),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if initial_rounds_to_skip > 0:
|
config.log_order_to_csv(order_details)
|
||||||
initial_rounds_to_skip -= 1
|
all_executed_orders.append(order_details)
|
||||||
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,
|
|
||||||
)
|
|
||||||
logger.info(
|
|
||||||
'Attempting to sell %s %s',
|
|
||||||
amount_to_sell,
|
|
||||||
config.input_asset,
|
|
||||||
)
|
|
||||||
|
|
||||||
order_details = config.seller.place_market_order(
|
del amount_to_sell
|
||||||
config.input_asset,
|
elif config.exit_when_empty:
|
||||||
amount_to_sell,
|
break
|
||||||
config.output_asset,
|
|
||||||
)
|
|
||||||
|
|
||||||
config.log_order_to_csv(order_details)
|
# Time out
|
||||||
all_executed_orders.append(order_details)
|
time_to_sleep = sample_from_range(rng, config.interval_range)
|
||||||
|
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 amount_to_sell
|
del input_amount_available
|
||||||
elif config.exit_when_empty:
|
|
||||||
break
|
|
||||||
|
|
||||||
# Time out
|
|
||||||
time_to_sleep = sample_from_range(rng, config.interval_range)
|
|
||||||
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
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
logger.warning('Manual interrupt')
|
|
||||||
except RuntimeError:
|
|
||||||
logger.exception(
|
|
||||||
'A unexpected and serious error occured. The program will be winding down',
|
|
||||||
)
|
|
||||||
logger.fatal('Please send the above error message to: mailto:jonjmaa@gmail.com')
|
|
||||||
logger.fatal(
|
|
||||||
'He will attempt to diagnosticate the problem, and help with recovery',
|
|
||||||
)
|
|
||||||
|
|
||||||
time_end = datetime.datetime.now(tz=datetime.UTC)
|
time_end = datetime.datetime.now(tz=datetime.UTC)
|
||||||
|
|
||||||
|
@ -303,45 +288,41 @@ def log_estimates(config: AutoSellConfig):
|
||||||
current_balance / config.input_amount_range[0],
|
current_balance / config.input_amount_range[0],
|
||||||
)
|
)
|
||||||
|
|
||||||
minisleep = 0.1
|
|
||||||
|
|
||||||
logger.info('')
|
|
||||||
logger.info('Welcome to crypto seller!')
|
logger.info('Welcome to crypto seller!')
|
||||||
config.sleep(3)
|
config.sleep(1)
|
||||||
logger.info('')
|
logger.info('')
|
||||||
config.sleep(minisleep)
|
config.sleep(1)
|
||||||
logger.info('I, the great crypto seller, your humble servant, will')
|
logger.info('I, the great crypto seller, your humble servant, will')
|
||||||
config.sleep(minisleep)
|
|
||||||
logger.info('now analyse your configuration and divine some estimates!')
|
logger.info('now analyse your configuration and divine some estimates!')
|
||||||
config.sleep(3)
|
config.sleep(1)
|
||||||
logger.info('')
|
logger.info('')
|
||||||
config.sleep(minisleep)
|
config.sleep(1)
|
||||||
logger.info(
|
logger.info(
|
||||||
'- Current balance: %s %s',
|
'- Current balance: %s %s',
|
||||||
current_balance,
|
current_balance,
|
||||||
config.input_asset.raw_short_name(),
|
config.input_asset.raw_short_name(),
|
||||||
)
|
)
|
||||||
config.sleep(minisleep)
|
config.sleep(1)
|
||||||
logger.info('- Average sleep: %s seconds', average_sleep)
|
logger.info('- Average sleep: %s seconds', average_sleep)
|
||||||
config.sleep(minisleep)
|
config.sleep(1)
|
||||||
logger.info(
|
logger.info(
|
||||||
'- Average amount: %s %s',
|
'- Average amount: %s %s',
|
||||||
average_amount,
|
average_amount,
|
||||||
config.input_asset.raw_short_name(),
|
config.input_asset.raw_short_name(),
|
||||||
)
|
)
|
||||||
config.sleep(minisleep)
|
config.sleep(1)
|
||||||
logger.info('- Expected counts: %s', expected_num_sell_offs)
|
logger.info('- Expected counts: %s', expected_num_sell_offs)
|
||||||
config.sleep(minisleep)
|
config.sleep(1)
|
||||||
logger.info('- Expected time: %s', expected_duration)
|
logger.info('- Expected time: %s', expected_duration)
|
||||||
config.sleep(minisleep)
|
config.sleep(1)
|
||||||
logger.info('- Fastest time: %s', fastest_duration)
|
logger.info('- Fastest time: %s', fastest_duration)
|
||||||
config.sleep(minisleep)
|
config.sleep(1)
|
||||||
logger.info('- Slowest time: %s', slowest_duration)
|
logger.info('- Slowest time: %s', slowest_duration)
|
||||||
config.sleep(minisleep)
|
config.sleep(1)
|
||||||
logger.info('')
|
logger.info('')
|
||||||
config.sleep(minisleep)
|
config.sleep(1)
|
||||||
logger.info('Do you still want to proceed?')
|
logger.info('Do you still want to proceed?')
|
||||||
config.sleep(minisleep)
|
config.sleep(1)
|
||||||
logger.info('If not, press CTRL+C within the next 10 seconds...')
|
logger.info('If not, press CTRL+C within the next 10 seconds...')
|
||||||
config.sleep(10)
|
config.sleep(10)
|
||||||
logger.info('')
|
logger.info('')
|
||||||
|
|
|
@ -3,7 +3,6 @@ import datetime
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import logging.handlers
|
import logging.handlers
|
||||||
import sys
|
|
||||||
import time
|
import time
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -25,18 +24,18 @@ logger = logging.getLogger(__name__)
|
||||||
################################################################################
|
################################################################################
|
||||||
# Constants #
|
# Constants #
|
||||||
|
|
||||||
PATH_FILE_LOG = 'log.txt'
|
PATH_OUTPUT = Path('./output').absolute()
|
||||||
PATH_FILE_TRADES = 'trades.csv'
|
PATH_LOG_FILE = PATH_OUTPUT / 'log.txt'
|
||||||
|
PATH_TRADES_FILE = PATH_OUTPUT / 'trades.csv'
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# Application Setup #
|
# Application Setup #
|
||||||
|
|
||||||
|
|
||||||
def setup_logging(output_directory: Path):
|
def setup_logging():
|
||||||
"""Enables logging for the terminal and to a log file."""
|
"""Enables logging for the terminal and to a log file."""
|
||||||
path_log_file = output_directory / PATH_FILE_LOG
|
PATH_LOG_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||||
path_log_file.parent.mkdir(parents=True, exist_ok=True)
|
file_handler = logging.handlers.WatchedFileHandler(filename=PATH_LOG_FILE)
|
||||||
file_handler = logging.handlers.WatchedFileHandler(filename=path_log_file)
|
|
||||||
file_handler.setFormatter(
|
file_handler.setFormatter(
|
||||||
logging.Formatter(
|
logging.Formatter(
|
||||||
'%(levelname)s:%(asctime)s: %(message)s',
|
'%(levelname)s:%(asctime)s: %(message)s',
|
||||||
|
@ -70,7 +69,7 @@ License : MIT License (see website for full text)
|
||||||
""".strip()
|
""".strip()
|
||||||
|
|
||||||
|
|
||||||
def load_config(config_path: Path, output_directory: Path) -> AutoSellConfig:
|
def load_config(config_path: Path) -> AutoSellConfig:
|
||||||
logger.info('Loading configuration')
|
logger.info('Loading configuration')
|
||||||
|
|
||||||
from . import secrets_config
|
from . import secrets_config
|
||||||
|
@ -99,7 +98,7 @@ def load_config(config_path: Path, output_directory: Path) -> AutoSellConfig:
|
||||||
exit_when_empty=True,
|
exit_when_empty=True,
|
||||||
seller=seller_backend,
|
seller=seller_backend,
|
||||||
sleep=time.sleep,
|
sleep=time.sleep,
|
||||||
log_order_to_csv=order_csv.CsvFileLogger(output_directory / PATH_FILE_TRADES),
|
log_order_to_csv=order_csv.CsvFileLogger(PATH_TRADES_FILE),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,13 +116,6 @@ def parse_args():
|
||||||
required=True,
|
required=True,
|
||||||
help='Trading configuration file',
|
help='Trading configuration file',
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
|
||||||
'--output',
|
|
||||||
type=Path,
|
|
||||||
dest='output_directory',
|
|
||||||
required=True,
|
|
||||||
help='Directory for outputing logs and trades list',
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--wait-before-first',
|
'--wait-before-first',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
|
@ -135,23 +127,16 @@ def parse_args():
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Initializes the program."""
|
"""Initializes the program."""
|
||||||
args = parse_args()
|
setup_logging()
|
||||||
|
|
||||||
setup_logging(output_directory=args.output_directory)
|
|
||||||
|
|
||||||
logger.info('Initializing crypto_seller')
|
logger.info('Initializing crypto_seller')
|
||||||
|
args = parse_args()
|
||||||
|
|
||||||
# Load config
|
# Load config
|
||||||
auto_sell_config = load_config(
|
auto_sell_config = load_config(args.config_file)
|
||||||
args.config_file,
|
|
||||||
output_directory=args.output_directory,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Display estimates
|
# Display estimates
|
||||||
try:
|
log_estimates(auto_sell_config)
|
||||||
log_estimates(auto_sell_config)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# Run auto sell
|
# Run auto sell
|
||||||
results = run_auto_sell(
|
results = run_auto_sell(
|
||||||
|
|
|
@ -4,7 +4,7 @@ from secret_loader import SecretLoader
|
||||||
|
|
||||||
__all__ = ['KUCOIN_KEY', 'KUCOIN_SECRET', 'KUCOIN_PASS']
|
__all__ = ['KUCOIN_KEY', 'KUCOIN_SECRET', 'KUCOIN_PASS']
|
||||||
|
|
||||||
secret_loader = SecretLoader(ENV_KEY_PREFIX='CS')
|
secret_loader = SecretLoader()
|
||||||
|
|
||||||
KUCOIN_KEY = secret_loader.load_or_fail('KUCOIN_KEY')
|
KUCOIN_KEY = secret_loader.load_or_fail('KUCOIN_KEY')
|
||||||
KUCOIN_SECRET = secret_loader.load_or_fail('KUCOIN_SECRET')
|
KUCOIN_SECRET = secret_loader.load_or_fail('KUCOIN_SECRET')
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
pytest
|
|
|
@ -65,7 +65,7 @@ def test_auto_run():
|
||||||
exit_when_empty=True,
|
exit_when_empty=True,
|
||||||
seller=seller_mock,
|
seller=seller_mock,
|
||||||
log_order_to_csv=crypto_seller.order_csv.CsvFileLogger(
|
log_order_to_csv=crypto_seller.order_csv.CsvFileLogger(
|
||||||
Path('test/output/test-trades.csv'),
|
Path('output/test-trades.csv'),
|
||||||
),
|
),
|
||||||
sleep=sleep_mock,
|
sleep=sleep_mock,
|
||||||
)
|
)
|
||||||
|
@ -82,4 +82,4 @@ def test_auto_run():
|
||||||
|
|
||||||
# Check mocks agree
|
# Check mocks agree
|
||||||
assert seller_mock.amount_left == 0
|
assert seller_mock.amount_left == 0
|
||||||
assert sleep_mock.time_slept == 1000 + 17.2
|
assert sleep_mock.time_slept == 1000 + 23
|
||||||
|
|
Loading…
Reference in New Issue
Block a user