diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index 7ebfb7420..725f7ddaa 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -3,5 +3,3 @@ name: Feature request
about: Suggest an idea for this project
---
-
-
diff --git a/.gitignore b/.gitignore
index 28f506e8a..828c12a91 100644
--- a/.gitignore
+++ b/.gitignore
@@ -91,3 +91,6 @@ archive
# Bandit config
!bandit.yml
+
+# pre-commit hooks
+!.pre-commit-config.yaml
diff --git a/.isort.cfg b/.isort.cfg
new file mode 100644
index 000000000..a7fcfaf1e
--- /dev/null
+++ b/.isort.cfg
@@ -0,0 +1,4 @@
+[settings]
+line_length = 120
+multi_line_output = 3
+include_trailing_comma = True
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 000000000..16b527296
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,27 @@
+# Read up on pre-commit
+# https://ljvmiranda921.github.io/notebook/2018/06/21/precommits-using-black-and-flake8/
+
+repos:
+
+- repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v2.4.0
+ hooks:
+ - id: trailing-whitespace
+ - id: check-docstring-first
+ - id: check-executables-have-shebangs
+ - id: check-json
+ - id: check-yaml
+ - id: end-of-file-fixer
+ - id: no-commit-to-branch
+ - id: flake8
+
+- repo: https://github.com/timothycrosley/isort
+ rev: 4.3.21
+ hooks:
+ - id: isort
+
+- repo: https://github.com/ambv/black
+ rev: 19.10b0
+ hooks:
+ - id: black
+ language_version: python3
diff --git a/MANIFEST.in b/MANIFEST.in
index d0d04a220..8ff719a92 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,2 +1,2 @@
include *.md
-include dexbot/resources/img/*
\ No newline at end of file
+include dexbot/resources/img/*
diff --git a/README.md b/README.md
index e58429a34..b7b4087d5 100644
--- a/README.md
+++ b/README.md
@@ -62,6 +62,13 @@ Join the [Telegram Chat for DEXBot](https://t.me/DEXBOTbts).
Install the software, use it and report any problems by creating a ticket.
+Before commiting any changes first time, make sure to install pre-commit hooks!
+
+```
+pip install -r requirements-dev.txt
+pre-commit install
+```
+
* [New Contributors Guide](https://github.com/Codaone/DEXBot/wiki/New-Contributors-Guide)
* [Git Workflow](https://github.com/Codaone/DEXBot/wiki/Git-Workflow)
diff --git a/dexbot/cli.py b/dexbot/cli.py
index d17d59f0a..2b3130d27 100755
--- a/dexbot/cli.py
+++ b/dexbot/cli.py
@@ -4,42 +4,31 @@
import os.path
import signal
import sys
+from multiprocessing import freeze_support
-from uptick.decorators import online
import bitshares.exceptions
+import click # noqa: E402
import graphenecommon.exceptions
from bitshares.market import Market
-
-from dexbot.config import Config, DEFAULT_CONFIG_FILE
from dexbot.cli_conf import SYSTEMD_SERVICE_NAME, get_whiptail, setup_systemd
-from dexbot.helper import initialize_orders_log, initialize_data_folders
-from dexbot.ui import (
- verbose,
- chain,
- unlock,
- configfile,
- reset_nodes
-)
+from dexbot.config import DEFAULT_CONFIG_FILE, Config
+from dexbot.helper import initialize_data_folders, initialize_orders_log
+from dexbot.ui import chain, configfile, reset_nodes, unlock, verbose
+from uptick.decorators import online
-from .worker import WorkerInfrastructure
+from . import errors, helper
from .cli_conf import configure_dexbot, dexbot_service_running
-from . import errors
-from . import helper
-from multiprocessing import freeze_support
+from .worker import WorkerInfrastructure
# We need to do this before importing click
if "LANG" not in os.environ:
os.environ['LANG'] = 'C.UTF-8'
-import click # noqa: E402
log = logging.getLogger(__name__)
# Initial logging before proper setup.
-logging.basicConfig(
- level=logging.INFO,
- format='%(asctime)s %(levelname)s %(message)s'
-)
+logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')
# Configure orders logging
initialize_orders_log()
@@ -50,39 +39,18 @@
@click.group()
@click.option(
- "--configfile",
- default=DEFAULT_CONFIG_FILE,
+ "--configfile", default=DEFAULT_CONFIG_FILE,
)
@click.option(
'--logfile',
default=None,
type=click.Path(dir_okay=False, writable=True),
- help='Override logfile location (example: ~/dexbot.log)'
-)
-@click.option(
- '--verbose',
- '-v',
- type=int,
- default=3,
- help='Verbosity (0-15)')
-@click.option(
- '--systemd/--no-systemd',
- '-d',
- default=False,
- help='Run as a daemon from systemd')
-@click.option(
- '--pidfile',
- '-p',
- type=click.Path(dir_okay=False, writable=True),
- default='',
- help='File to write PID')
-@click.option(
- '--sortnodes',
- '-s',
- type=int,
- default=-1,
- help='Sort nodes, w/max timeout in sec. [sec > 0]'
+ help='Override logfile location (example: ~/dexbot.log)',
)
+@click.option('--verbose', '-v', type=int, default=3, help='Verbosity (0-15)')
+@click.option('--systemd/--no-systemd', '-d', default=False, help='Run as a daemon from systemd')
+@click.option('--pidfile', '-p', type=click.Path(dir_okay=False, writable=True), default='', help='File to write PID')
+@click.option('--sortnodes', '-s', type=int, default=-1, help='Sort nodes, w/max timeout in sec. [sec > 0]')
@click.pass_context
def main(ctx, **kwargs):
ctx.obj = {}
@@ -131,6 +99,7 @@ def run(ctx):
if ctx.obj['systemd']:
try:
import sdnotify # A soft dependency on sdnotify -- don't crash on non-systemd systems
+
n = sdnotify.SystemdNotifier()
n.notify("READY=1")
except BaseException:
@@ -203,9 +172,7 @@ def cancel(ctx, market, account):
try:
my_market = Market(market)
ctx.bitshares.bundle = True
- my_market.cancel([
- x["id"] for x in my_market.accountopenorders(account)
- ], account=account)
+ my_market.cancel([x["id"] for x in my_market.accountopenorders(account)], account=account)
response = ctx.bitshares.txbuffer.broadcast()
log.info(response)
if response is not None:
diff --git a/dexbot/cli_conf.py b/dexbot/cli_conf.py
index b6421cb3d..23fa8818d 100644
--- a/dexbot/cli_conf.py
+++ b/dexbot/cli_conf.py
@@ -14,33 +14,24 @@
"""
import importlib
-import pathlib
import os
import os.path
-import sys
+import pathlib
import re
import subprocess
+import sys
+import dexbot.helper
from bitshares.account import Account
-
-from dexbot.whiptail import get_whiptail
-from dexbot.strategies.base import StrategyBase
from dexbot.config_validator import ConfigValidator
from dexbot.node_manager import get_sorted_nodelist
-
-import dexbot.helper
-
+from dexbot.strategies.base import StrategyBase
+from dexbot.whiptail import get_whiptail
STRATEGIES = [
- {'tag': 'relative',
- 'class': 'dexbot.strategies.relative_orders',
- 'name': 'Relative Orders'},
- {'tag': 'stagger',
- 'class': 'dexbot.strategies.staggered_orders',
- 'name': 'Staggered Orders'},
- {'tag': 'koth',
- 'class': 'dexbot.strategies.king_of_the_hill',
- 'name': 'King of the Hill'},
+ {'tag': 'relative', 'class': 'dexbot.strategies.relative_orders', 'name': 'Relative Orders'},
+ {'tag': 'stagger', 'class': 'dexbot.strategies.staggered_orders', 'name': 'Staggered Orders'},
+ {'tag': 'koth', 'class': 'dexbot.strategies.king_of_the_hill', 'name': 'King of the Hill'},
]
# Todo: tags must be unique. Are they really a tags?
@@ -50,13 +41,12 @@
# make sure tag is unique
i = 1
while tag in tags_so_far:
- tag = tag+str(i)
+ tag = tag + str(i)
i += 1
tags_so_far.add(tag)
STRATEGIES.append({'tag': tag, 'class': module, 'name': desc})
-SYSTEMD_SERVICE_NAME = os.path.expanduser(
- "~/.local/share/systemd/user/dexbot.service")
+SYSTEMD_SERVICE_NAME = os.path.expanduser("~/.local/share/systemd/user/dexbot.service")
SYSTEMD_SERVICE_FILE = """
[Unit]
@@ -78,8 +68,7 @@
def select_choice(current, choices):
""" For the radiolist, get us a list with the current value selected
"""
- return [(tag, text, (current == tag and "ON") or "OFF")
- for tag, text in choices]
+ return [(tag, text, (current == tag and "ON") or "OFF") for tag, text in choices]
def process_config_element(element, whiptail, worker_config):
@@ -99,9 +88,7 @@ def process_config_element(element, whiptail, worker_config):
if element.extra:
while not re.match(element.extra, txt):
whiptail.alert("The value is not valid")
- txt = whiptail.prompt(
- title, worker_config.get(
- element.key, element.default))
+ txt = whiptail.prompt(title, worker_config.get(element.key, element.default))
worker_config[element.key] = txt
if element.type == "bool":
@@ -132,8 +119,9 @@ def process_config_element(element, whiptail, worker_config):
worker_config[element.key] = val
if element.type == "choice":
- worker_config[element.key] = whiptail.radiolist(title, select_choice(
- worker_config.get(element.key, element.default), element.extra))
+ worker_config[element.key] = whiptail.radiolist(
+ title, select_choice(worker_config.get(element.key, element.default), element.extra)
+ )
def dexbot_service_running():
@@ -156,8 +144,7 @@ def setup_systemd(whiptail, config):
if not os.path.exists("/etc/systemd"):
return # No working systemd
- if not whiptail.confirm(
- "Do you want to run dexbot as a background (daemon) process?", default="no"):
+ if not whiptail.confirm("Do you want to run dexbot as a background (daemon) process?", default="no"):
config['systemd_status'] = 'disabled'
return
@@ -173,16 +160,13 @@ def setup_systemd(whiptail, config):
"The uptick wallet password\n"
"NOTE: this will be saved on disc so the worker can run unattended. "
"This means anyone with access to this computer's files can spend all your money",
- password=True)
+ password=True,
+ )
# Because we hold password be restrictive
fd = os.open(SYSTEMD_SERVICE_NAME, os.O_WRONLY | os.O_CREAT, 0o600)
with open(fd, "w") as fp:
- fp.write(
- SYSTEMD_SERVICE_FILE.format(
- exe=sys.argv[0],
- passwd=password,
- homedir=os.path.expanduser("~")))
+ fp.write(SYSTEMD_SERVICE_FILE.format(exe=sys.argv[0], passwd=password, homedir=os.path.expanduser("~")))
# The dexbot service file was edited, reload the daemon configs
os.system('systemctl --user daemon-reload')
@@ -228,8 +212,7 @@ def configure_worker(whiptail, worker_config, bitshares_instance):
# Strategy selection
worker_config['module'] = whiptail.radiolist(
- "Choose a worker strategy",
- select_choice(default_strategy, strategy_list)
+ "Choose a worker strategy", select_choice(default_strategy, strategy_list)
)
for strategy in STRATEGIES:
@@ -237,10 +220,7 @@ def configure_worker(whiptail, worker_config, bitshares_instance):
worker_config['module'] = strategy['class']
# Import the strategy class but we don't __init__ it here
- strategy_class = getattr(
- importlib.import_module(worker_config["module"]),
- 'Strategy'
- )
+ strategy_class = getattr(importlib.import_module(worker_config["module"]), 'Strategy')
# Check if strategy has changed and editing existing worker
if editing and default_strategy != get_strategy_tag(worker_config['module']):
@@ -276,7 +256,8 @@ def configure_worker(whiptail, worker_config, bitshares_instance):
else:
whiptail.alert(
"This worker type does not have configuration information. "
- "You will have to check the worker code and add configuration values to config.yml if required")
+ "You will have to check the worker code and add configuration values to config.yml if required"
+ )
return worker_config
@@ -305,19 +286,22 @@ def configure_dexbot(config, ctx):
while True:
action = whiptail.menu(
"You have an existing configuration.\nSelect an action:",
- [('LIST', 'List your workers'),
- ('NEW', 'Create a new worker'),
- ('EDIT', 'Edit a worker'),
- ('DEL_WORKER', 'Delete a worker'),
- ('ADD', 'Add a bitshares account'),
- ('DEL_ACCOUNT', 'Delete a bitshares account'),
- ('SHOW', 'Show bitshares accounts'),
- ('NODES', 'Edit Node Selection'),
- ('ADD_NODE', 'Add Your Node'),
- ('SORT_NODES', 'By latency (uses default list)'),
- ('DEL_NODE', 'Delete A Node'),
- ('HELP', 'Where to get help'),
- ('EXIT', 'Quit this application')])
+ [
+ ('LIST', 'List your workers'),
+ ('NEW', 'Create a new worker'),
+ ('EDIT', 'Edit a worker'),
+ ('DEL_WORKER', 'Delete a worker'),
+ ('ADD', 'Add a bitshares account'),
+ ('DEL_ACCOUNT', 'Delete a bitshares account'),
+ ('SHOW', 'Show bitshares accounts'),
+ ('NODES', 'Edit Node Selection'),
+ ('ADD_NODE', 'Add Your Node'),
+ ('SORT_NODES', 'By latency (uses default list)'),
+ ('DEL_NODE', 'Delete A Node'),
+ ('HELP', 'Where to get help'),
+ ('EXIT', 'Quit this application'),
+ ],
+ )
my_workers = [(index, index) for index in workers]
@@ -339,8 +323,9 @@ def configure_dexbot(config, ctx):
elif action == 'EDIT':
if len(my_workers):
worker_name = whiptail.menu("Select worker to edit", my_workers)
- config['workers'][worker_name] = configure_worker(whiptail, config['workers'][worker_name],
- bitshares_instance)
+ config['workers'][worker_name] = configure_worker(
+ whiptail, config['workers'][worker_name], bitshares_instance
+ )
else:
whiptail.alert('No workers to edit.')
elif action == 'DEL_WORKER':
@@ -380,8 +365,8 @@ def configure_dexbot(config, ctx):
elif action == 'NODES':
choice = whiptail.node_radiolist(
msg="Choose your preferred node",
- items=select_choice(config['node'][0],
- [(index, index) for index in config['node']]))
+ items=select_choice(config['node'][0], [(index, index) for index in config['node']]),
+ )
# Move selected node as first item in the config file's node list
config['node'].remove(choice)
config['node'].insert(0, choice)
@@ -393,8 +378,8 @@ def configure_dexbot(config, ctx):
elif action == 'DEL_NODE':
choice = whiptail.node_radiolist(
msg="Choose node to delete",
- items=select_choice(config['node'][0],
- [(index, index) for index in config['node']]))
+ items=select_choice(config['node'][0], [(index, index) for index in config['node']]),
+ )
config['node'].remove(choice)
# delete node permanently from config
setup_systemd(whiptail, config)
diff --git a/dexbot/config.py b/dexbot/config.py
index 338e66b42..fa54b8920 100644
--- a/dexbot/config.py
+++ b/dexbot/config.py
@@ -1,20 +1,17 @@
import os
import pathlib
+from collections import OrderedDict, defaultdict
+import appdirs
from dexbot import APP_NAME, AUTHOR
from dexbot.node_manager import get_sorted_nodelist
-
-
-import appdirs
from ruamel import yaml
-from collections import OrderedDict, defaultdict
DEFAULT_CONFIG_DIR = appdirs.user_config_dir(APP_NAME, appauthor=AUTHOR)
DEFAULT_CONFIG_FILE = os.path.join(DEFAULT_CONFIG_DIR, 'config.yml')
class Config(dict):
-
def __init__(self, config=None, path=None):
""" Creates or loads the config file based on if it exists.
:param dict config: data used to create the config file
@@ -165,9 +162,7 @@ def construct_mapping(mapping_loader, node):
mapping_loader.flatten_mapping(node)
return object_pairs_hook(mapping_loader.construct_pairs(node))
- OrderedLoader.add_constructor(
- yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
- construct_mapping)
+ OrderedLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, construct_mapping)
return yaml.load(stream, OrderedLoader)
@staticmethod
@@ -194,6 +189,7 @@ def assets_intersections(config):
}
}
"""
+
def update_data(asset, operational_percent):
if isinstance(data[account][asset]['sum_pct'], float):
# Existing dict key
@@ -210,8 +206,7 @@ def update_data(asset, operational_percent):
data[account][asset]['num_zero_workers'] = 1
if data[account][asset]['sum_pct'] > 1:
- raise ValueError('Operational percent for asset {} is more than 100%'
- .format(asset))
+ raise ValueError('Operational percent for asset {} is more than 100%'.format(asset))
def tree():
return defaultdict(tree)
diff --git a/dexbot/config_validator.py b/dexbot/config_validator.py
index 1d5fd1af7..c2dab4bf0 100644
--- a/dexbot/config_validator.py
+++ b/dexbot/config_validator.py
@@ -1,10 +1,9 @@
-from dexbot.config import Config
-
-from bitshares.instance import shared_bitshares_instance
from bitshares.account import Account
from bitshares.asset import Asset
-from bitshares.exceptions import KeyAlreadyInStoreException, AccountDoesNotExistsException, AssetDoesNotExistsException
+from bitshares.exceptions import AccountDoesNotExistsException, AssetDoesNotExistsException, KeyAlreadyInStoreException
+from bitshares.instance import shared_bitshares_instance
from bitsharesbase.account import PrivateKey
+from dexbot.config import Config
class ConfigValidator:
diff --git a/dexbot/controllers/main_controller.py b/dexbot/controllers/main_controller.py
index 86f07ea2b..1eeb962b4 100644
--- a/dexbot/controllers/main_controller.py
+++ b/dexbot/controllers/main_controller.py
@@ -1,22 +1,20 @@
-import os
import logging
+import os
import sys
import time
-from dexbot import VERSION, APP_NAME, AUTHOR
-from dexbot.helper import initialize_orders_log, initialize_data_folders
-from dexbot.worker import WorkerInfrastructure
-from dexbot.views.errors import PyQtHandler
-
from appdirs import user_data_dir
from bitshares.bitshares import BitShares
from bitshares.instance import set_shared_bitshares_instance
from bitsharesapi.bitsharesnoderpc import BitSharesNodeRPC
+from dexbot import APP_NAME, AUTHOR, VERSION
+from dexbot.helper import initialize_data_folders, initialize_orders_log
+from dexbot.views.errors import PyQtHandler
+from dexbot.worker import WorkerInfrastructure
from grapheneapi.exceptions import NumRetriesReached
class MainController:
-
def __init__(self, config):
self.bitshares_instance = None
self.config = config
@@ -26,7 +24,8 @@ def __init__(self, config):
data_dir = user_data_dir(APP_NAME, AUTHOR)
filename = os.path.join(data_dir, 'dexbot.log')
formatter = logging.Formatter(
- '%(asctime)s - %(worker_name)s using account %(account)s on %(market)s - %(levelname)s - %(message)s')
+ '%(asctime)s - %(worker_name)s using account %(account)s on %(market)s - %(levelname)s - %(message)s'
+ )
logger = logging.getLogger("dexbot.per_worker")
fh = logging.FileHandler(filename)
fh.setFormatter(formatter)
@@ -35,8 +34,10 @@ def __init__(self, config):
self.pyqt_handler = PyQtHandler()
self.pyqt_handler.setLevel(logging.INFO)
logger.addHandler(self.pyqt_handler)
- logger.info("DEXBot {} on python {} {}".format(VERSION, sys.version[:6], sys.platform), extra={
- 'worker_name': 'NONE', 'account': 'NONE', 'market': 'NONE'})
+ logger.info(
+ "DEXBot {} on python {} {}".format(VERSION, sys.version[:6], sys.platform),
+ extra={'worker_name': 'NONE', 'account': 'NONE', 'market': 'NONE'},
+ )
# Configure orders logging
initialize_orders_log()
diff --git a/dexbot/controllers/settings_controller.py b/dexbot/controllers/settings_controller.py
index 8ccc7baf3..3ca924109 100644
--- a/dexbot/controllers/settings_controller.py
+++ b/dexbot/controllers/settings_controller.py
@@ -1,11 +1,9 @@
from dexbot.config import Config
-
-from PyQt5.QtWidgets import QTreeWidgetItem
from PyQt5.QtCore import Qt
+from PyQt5.QtWidgets import QTreeWidgetItem
class SettingsController:
-
def __init__(self, view):
self.config = Config()
self.view = view
diff --git a/dexbot/controllers/strategy_controller.py b/dexbot/controllers/strategy_controller.py
index f11513870..2bca7c685 100644
--- a/dexbot/controllers/strategy_controller.py
+++ b/dexbot/controllers/strategy_controller.py
@@ -66,7 +66,7 @@ def elements(self):
QtWidgets.QLineEdit,
QtWidgets.QCheckBox,
QtWidgets.QComboBox,
- QtWidgets.QSlider
+ QtWidgets.QSlider,
)
for option in self.configure:
@@ -78,7 +78,6 @@ def elements(self):
class RelativeOrdersController(StrategyController):
-
def __init__(self, view, configure, worker_controller, worker_data):
# Check if there is worker data. This prevents error when multiplying None type when creating worker.
if worker_data:
@@ -254,7 +253,6 @@ def validation_errors(self):
class StaggeredOrdersController(StrategyController):
-
def __init__(self, view, configure, worker_controller, worker_data):
self.view = view
self.configure = configure
@@ -298,7 +296,6 @@ def validation_errors(self):
class KingOfTheHillController(StrategyController):
-
def __init__(self, view, configure, worker_controller, worker_data):
self.view = view
self.configure = configure
diff --git a/dexbot/controllers/wallet_controller.py b/dexbot/controllers/wallet_controller.py
index 3dec4ae7f..420274eec 100644
--- a/dexbot/controllers/wallet_controller.py
+++ b/dexbot/controllers/wallet_controller.py
@@ -2,7 +2,6 @@
class WalletController:
-
def __init__(self, bitshares_instance):
self.bitshares = bitshares_instance
diff --git a/dexbot/controllers/worker_controller.py b/dexbot/controllers/worker_controller.py
index 8e33d3d2d..aff6fb13b 100644
--- a/dexbot/controllers/worker_controller.py
+++ b/dexbot/controllers/worker_controller.py
@@ -1,20 +1,18 @@
import collections
import re
-from dexbot.views.errors import gui_error
+from bitshares.instance import shared_bitshares_instance
from dexbot.config import Config
from dexbot.config_validator import ConfigValidator
from dexbot.helper import find_external_strategies
-from dexbot.views.notice import NoticeDialog
from dexbot.views.confirmation import ConfirmationDialog
+from dexbot.views.errors import gui_error
+from dexbot.views.notice import NoticeDialog
from dexbot.views.strategy_form import StrategyFormWidget
-
-from bitshares.instance import shared_bitshares_instance
from PyQt5 import QtGui
class WorkerController:
-
def __init__(self, view, bitshares_instance, mode):
self.view = view
self.mode = mode
@@ -33,16 +31,10 @@ def strategies(self):
strategies = collections.OrderedDict()
strategies['dexbot.strategies.relative_orders'] = {
'name': 'Relative Orders',
- 'form_module': 'dexbot.views.ui.forms.relative_orders_widget_ui'
- }
- strategies['dexbot.strategies.staggered_orders'] = {
- 'name': 'Staggered Orders',
- 'form_module': ''
- }
- strategies['dexbot.strategies.king_of_the_hill'] = {
- 'name': 'King of the Hill',
- 'form_module': ''
+ 'form_module': 'dexbot.views.ui.forms.relative_orders_widget_ui',
}
+ strategies['dexbot.strategies.staggered_orders'] = {'name': 'Staggered Orders', 'form_module': ''}
+ strategies['dexbot.strategies.king_of_the_hill'] = {'name': 'King of the Hill', 'form_module': ''}
for desc, module in find_external_strategies():
strategies[module] = {'name': desc, 'form_module': module}
# if there is no UI form in the module then GUI will gracefully revert to auto-ui
@@ -96,8 +88,9 @@ def get_account(worker_data):
@staticmethod
def handle_save_dialog():
- dialog = ConfirmationDialog('Saving the worker will cancel all the current orders.\n'
- 'Are you sure you want to do this?')
+ dialog = ConfirmationDialog(
+ 'Saving the worker will cancel all the current orders.\n' 'Are you sure you want to do this?'
+ )
return dialog.exec_()
@gui_error
@@ -125,8 +118,7 @@ def validate_form(self):
old_worker_name = None if self.mode == 'add' else self.view.worker_name
if not self.validator.validate_worker_name(worker_name, old_worker_name):
- error_texts.append(
- 'Worker name needs to be unique. "{}" is already in use.'.format(worker_name))
+ error_texts.append('Worker name needs to be unique. "{}" is already in use.'.format(worker_name))
if not self.validator.validate_asset(base_asset):
error_texts.append('Field "Base Asset" does not have a valid asset.')
if not self.validator.validate_asset(quote_asset):
@@ -185,14 +177,13 @@ def handle_save(self):
'fee_asset': fee_asset,
'operational_percent_quote': operational_percent_quote,
'operational_percent_base': operational_percent_base,
- **self.view.strategy_widget.values
+ **self.view.strategy_widget.values,
}
self.view.worker_name = self.view.worker_name_input.text()
self.view.accept()
class UppercaseValidator(QtGui.QValidator):
-
@staticmethod
def validate(string, pos):
return QtGui.QValidator.Acceptable, string.upper(), pos
diff --git a/dexbot/controllers/worker_details_controller.py b/dexbot/controllers/worker_details_controller.py
index f6bfadda2..1791a1488 100644
--- a/dexbot/controllers/worker_details_controller.py
+++ b/dexbot/controllers/worker_details_controller.py
@@ -1,12 +1,11 @@
import csv
import os
-from PyQt5.QtWidgets import QTableWidgetItem
from PyQt5.QtGui import QTextCursor
+from PyQt5.QtWidgets import QTableWidgetItem
class WorkerDetailsController:
-
def __init__(self, view, worker_name, config):
""" Initializes controller
diff --git a/dexbot/errors.py b/dexbot/errors.py
index 8cd19736e..f6b46203d 100644
--- a/dexbot/errors.py
+++ b/dexbot/errors.py
@@ -1,11 +1,10 @@
import logging
+
log = logging.getLogger(__name__)
def InsufficientFundsError(amount):
- log.error(
- "[InsufficientFunds] Need {}".format(str(amount))
- )
+ log.error("[InsufficientFunds] Need {}".format(str(amount)))
class NoWorkersAvailable(Exception):
diff --git a/dexbot/gui.py b/dexbot/gui.py
index 900342b7a..7bc4fb1eb 100644
--- a/dexbot/gui.py
+++ b/dexbot/gui.py
@@ -3,7 +3,6 @@
from dexbot.config import Config
from dexbot.controllers.main_controller import MainController
from dexbot.views.worker_list import MainView
-
from PyQt5.QtWidgets import QApplication
diff --git a/dexbot/helper.py b/dexbot/helper.py
index 352616aa4..cc6827607 100644
--- a/dexbot/helper.py
+++ b/dexbot/helper.py
@@ -1,10 +1,10 @@
-import os
-import math
-import shutil
import errno
import logging
-from appdirs import user_data_dir
+import math
+import os
+import shutil
+from appdirs import user_data_dir
from dexbot import APP_NAME, AUTHOR
@@ -94,6 +94,7 @@ def find_external_strategies():
for entry_point in pkg_resources.iter_entry_points("dexbot.strategy"):
yield (entry_point.name, entry_point.module_name)
+
except ImportError:
# Our system doesn't have setuptools, so no way to find external strategies
def find_external_strategies():
diff --git a/dexbot/migrations/env.py b/dexbot/migrations/env.py
index be82b9ecc..131e3422a 100644
--- a/dexbot/migrations/env.py
+++ b/dexbot/migrations/env.py
@@ -1,7 +1,5 @@
-from sqlalchemy import engine_from_config
-from sqlalchemy import pool
-
from alembic import context
+from sqlalchemy import engine_from_config, pool
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
@@ -36,9 +34,7 @@ def run_migrations_offline():
"""
url = config.get_main_option("sqlalchemy.url")
- context.configure(
- url=url, target_metadata=target_metadata, literal_binds=True
- )
+ context.configure(url=url, target_metadata=target_metadata, literal_binds=True)
with context.begin_transaction():
context.run_migrations()
@@ -52,15 +48,11 @@ def run_migrations_online():
"""
connectable = engine_from_config(
- config.get_section(config.config_ini_section),
- prefix="sqlalchemy.",
- poolclass=pool.NullPool,
+ config.get_section(config.config_ini_section), prefix="sqlalchemy.", poolclass=pool.NullPool,
)
with connectable.connect() as connection:
- context.configure(
- connection=connection, target_metadata=target_metadata
- )
+ context.configure(connection=connection, target_metadata=target_metadata)
with context.begin_transaction():
context.run_migrations()
diff --git a/dexbot/migrations/versions/d1e6672520b2_extend_orders_table.py b/dexbot/migrations/versions/d1e6672520b2_extend_orders_table.py
index 732d5bfb1..84339fa32 100644
--- a/dexbot/migrations/versions/d1e6672520b2_extend_orders_table.py
+++ b/dexbot/migrations/versions/d1e6672520b2_extend_orders_table.py
@@ -5,9 +5,8 @@
Create Date: 2019-07-29 17:38:09.136485
"""
-from alembic import op
import sqlalchemy as sa
-
+from alembic import op
# revision identifiers, used by Alembic.
revision = 'd1e6672520b2'
diff --git a/dexbot/node_manager.py b/dexbot/node_manager.py
index 7ba64d849..6bbdc05b0 100644
--- a/dexbot/node_manager.py
+++ b/dexbot/node_manager.py
@@ -1,18 +1,15 @@
-from websocket import create_connection as wss_create
-from time import time
-from itertools import repeat
import logging
import multiprocessing as mp
-import subprocess
import platform
+import subprocess
+from itertools import repeat
+from time import time
+from websocket import create_connection as wss_create
log = logging.getLogger(__name__)
-logging.basicConfig(
- level=logging.INFO,
- format='%(asctime)s %(levelname)s %(message)s'
-)
+logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')
max_timeout = 2.0 # default ping time is set to 2s. use for internal testing.
host_ip = '8.8.8.8' # default host to ping to check internet
@@ -52,7 +49,7 @@ def wss_test(node, timeout):
try:
start = time()
wss_create(node, timeout=timeout)
- latency = (time() - start)
+ latency = time() - start
return latency
except Exception as e:
log.info('websocket test: {}'.format(e))
@@ -74,7 +71,7 @@ def get_sorted_nodelist(nodelist, timeout):
"""
print('get_sorted_nodelist max timeout: {}'.format(timeout))
- pool_size = mp.cpu_count()*2
+ pool_size = mp.cpu_count() * 2
with mp.Pool(processes=pool_size) as pool:
latency_info = pool.starmap(check_node, zip(nodelist, repeat(timeout)))
diff --git a/dexbot/orderengines/bitshares_engine.py b/dexbot/orderengines/bitshares_engine.py
index 38b36421a..133622a02 100644
--- a/dexbot/orderengines/bitshares_engine.py
+++ b/dexbot/orderengines/bitshares_engine.py
@@ -1,26 +1,22 @@
-import datetime
import copy
+import datetime
import logging
import time
-from dexbot.config import Config
-from dexbot.storage import Storage
-from dexbot.helper import truncate
-
import bitshares.exceptions
import bitsharesapi
import bitsharesapi.exceptions
-
from bitshares.amount import Amount, Asset
from bitshares.dex import Dex
from bitshares.instance import shared_bitshares_instance
from bitshares.market import Market
from bitshares.price import FilledOrder, Order, UpdateCallOrder
from bitshares.utils import formatTime
-
+from dexbot.config import Config
+from dexbot.helper import truncate
+from dexbot.storage import Storage
from events import Events
-
# Number of maximum retries used to retry action before failing
MAX_TRIES = 3
@@ -39,16 +35,18 @@ class BitsharesOrderEngine(Storage, Events):
"""
- def __init__(self,
- name,
- config=None,
- _account=None,
- _market=None,
- fee_asset_symbol=None,
- bitshares_instance=None,
- bitshares_bundle=None,
- *args,
- **kwargs):
+ def __init__(
+ self,
+ name,
+ config=None,
+ _account=None,
+ _market=None,
+ fee_asset_symbol=None,
+ bitshares_instance=None,
+ bitshares_bundle=None,
+ *args,
+ **kwargs
+ ):
# BitShares instance
self.bitshares = bitshares_instance or shared_bitshares_instance()
@@ -113,10 +111,7 @@ def _callbackPlaceFillOrders(self, d):
def _cancel_orders(self, orders):
try:
- self.retry_action(
- self.bitshares.cancel,
- orders, account=self._account, fee_asset=self.fee_asset['id']
- )
+ self.retry_action(self.bitshares.cancel, orders, account=self._account, fee_asset=self.fee_asset['id'])
except bitsharesapi.exceptions.UnhandledRPCError as exception:
if str(exception).startswith('Assert Exception: maybe_found != nullptr: Unable to find Object'):
# The order(s) we tried to cancel doesn't exist
@@ -157,9 +152,7 @@ def account_total_value(self, return_asset):
total_value += updated_order['base']['amount']
else:
total_value += self.convert_asset(
- updated_order['base']['amount'],
- updated_order['base']['symbol'],
- return_asset
+ updated_order['base']['amount'], updated_order['base']['symbol'], return_asset
)
return total_value
@@ -233,8 +226,11 @@ def cancel_all_orders(self, all_markets=False):
self.log.info('Canceling all account orders')
orders_to_cancel = self.all_own_orders
else:
- self.log.info('Canceling all orders on market {}/{}'
- .format(self.market['quote']['symbol'], self.market['base']['symbol']))
+ self.log.info(
+ 'Canceling all orders on market {}/{}'.format(
+ self.market['quote']['symbol'], self.market['base']['symbol']
+ )
+ )
orders_to_cancel = self.own_orders
if orders_to_cancel:
@@ -544,8 +540,9 @@ def place_market_buy_order(self, amount, price, return_none=False, *args, **kwar
self.disabled = True
return None
- self.log.info('Placing a buy order with {:.{prec}f} {} @ {:.8f}'
- .format(base_amount, symbol, price, prec=precision))
+ self.log.info(
+ 'Placing a buy order with {:.{prec}f} {} @ {:.8f}'.format(base_amount, symbol, price, prec=precision)
+ )
# Place the order
buy_transaction = self.retry_action(
@@ -600,8 +597,9 @@ def place_market_sell_order(self, amount, price, return_none=False, invert=False
self.disabled = True
return None
- self.log.info('Placing a sell order with {:.{prec}f} {} @ {:.8f}'
- .format(quote_amount, symbol, price, prec=precision))
+ self.log.info(
+ 'Placing a sell order with {:.{prec}f} {} @ {:.8f}'.format(quote_amount, symbol, price, prec=precision)
+ )
# Place the order
sell_transaction = self.retry_action(
@@ -662,13 +660,17 @@ def retry_action(self, action, *args, **kwargs):
elif "trx.expiration <= now + chain_parameters.maximum_time_until_expiration" in str(exception):
if tries > MAX_TRIES:
info = self.bitshares.info()
- raise Exception('Too much difference between node block time and trx expiration, please change '
- 'the node. Block time: {}, local time: {}'
- .format(info['time'], formatTime(datetime.datetime.utcnow())))
+ raise Exception(
+ 'Too much difference between node block time and trx expiration, please change '
+ 'the node. Block time: {}, local time: {}'.format(
+ info['time'], formatTime(datetime.datetime.utcnow())
+ )
+ )
else:
tries += 1
- self.log.warning('Too much difference between node block time and trx expiration, switching '
- 'node')
+ self.log.warning(
+ 'Too much difference between node block time and trx expiration, switching ' 'node'
+ )
self.bitshares.txbuffer.clear()
self.bitshares.rpc.next()
elif "Assert Exception: delta.amount > 0: Insufficient Balance" in str(exception):
@@ -845,7 +847,10 @@ def is_partially_filled(self, order, threshold=0.3):
diff_abs = order['base']['amount'] - order['for_sale']['amount']
diff_rel = diff_abs / order['base']['amount']
if diff_rel > threshold:
- self.log.debug('Partially filled {} order: {} {} @ {:.8f}, filled: {:.2%}'.format(
- order_type, order['base']['amount'], order['base']['symbol'], price, diff_rel))
+ self.log.debug(
+ 'Partially filled {} order: {} {} @ {:.8f}, filled: {:.2%}'.format(
+ order_type, order['base']['amount'], order['base']['symbol'], price, diff_rel
+ )
+ )
return True
return False
diff --git a/dexbot/pricefeeds/bitshares_feed.py b/dexbot/pricefeeds/bitshares_feed.py
index 2e273cdaf..42e63c0b4 100644
--- a/dexbot/pricefeeds/bitshares_feed.py
+++ b/dexbot/pricefeeds/bitshares_feed.py
@@ -15,9 +15,8 @@ class BitsharesPriceFeed:
- Buy orders reserve BASE
- Sell orders reserve QUOTE
"""
- def __init__(self,
- market,
- bitshares_instance=None):
+
+ def __init__(self, market, bitshares_instance=None):
self.market = market
self.ticker = self.market.ticker
@@ -28,9 +27,7 @@ def __init__(self,
# BitShares instance
self.bitshares = bitshares_instance or shared_bitshares_instance()
- self.log = logging.LoggerAdapter(
- logging.getLogger('dexbot.pricefeed_log'), {}
- )
+ self.log = logging.LoggerAdapter(logging.getLogger('dexbot.pricefeed_log'), {})
def get_limit_orders(self, depth=1):
""" Returns orders from the current market. Orders are sorted by price. Does not require account info.
diff --git a/dexbot/qt_queue/idle_queue.py b/dexbot/qt_queue/idle_queue.py
index 1dd980d84..d0a16ca62 100644
--- a/dexbot/qt_queue/idle_queue.py
+++ b/dexbot/qt_queue/idle_queue.py
@@ -6,4 +6,5 @@
def idle_add(func, *args, **kwargs):
def idle():
func(*args, **kwargs)
+
idle_loop.put(idle)
diff --git a/dexbot/qt_queue/queue_dispatcher.py b/dexbot/qt_queue/queue_dispatcher.py
index e8c972797..843b701b1 100644
--- a/dexbot/qt_queue/queue_dispatcher.py
+++ b/dexbot/qt_queue/queue_dispatcher.py
@@ -1,7 +1,6 @@
-from PyQt5.QtWidgets import QApplication
-from PyQt5.QtCore import QThread, QEvent
-
from dexbot.qt_queue.idle_queue import idle_loop
+from PyQt5.QtCore import QEvent, QThread
+from PyQt5.QtWidgets import QApplication
class ThreadDispatcher(QThread):
diff --git a/dexbot/resources/svg/dexbot-logo.svg b/dexbot/resources/svg/dexbot-logo.svg
index 6c1cc14d0..84444153d 100644
--- a/dexbot/resources/svg/dexbot-logo.svg
+++ b/dexbot/resources/svg/dexbot-logo.svg
@@ -142,4 +142,4 @@
id="tspan108"
y="0"
x="0 23.390959 46.781918 70.172874 93.563835 116.9548 140.34575 163.73671 187.12767 210.51863 233.90959 257.30054 280.6915 304.08246 327.47342 350.86438 374.25534">MARKET MAKING BOT
-
\ No newline at end of file
+
diff --git a/dexbot/resources/svg/dexbot.svg b/dexbot/resources/svg/dexbot.svg
index e011220cf..dfd8607a2 100644
--- a/dexbot/resources/svg/dexbot.svg
+++ b/dexbot/resources/svg/dexbot.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/dexbot/resources/svg/modifystrategy.svg b/dexbot/resources/svg/modifystrategy.svg
index 7e30ac73d..3342fa265 100644
--- a/dexbot/resources/svg/modifystrategy.svg
+++ b/dexbot/resources/svg/modifystrategy.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/dexbot/resources/svg/simplestrategy.svg b/dexbot/resources/svg/simplestrategy.svg
index 0a1819df2..17470e4d3 100644
--- a/dexbot/resources/svg/simplestrategy.svg
+++ b/dexbot/resources/svg/simplestrategy.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/dexbot/storage.py b/dexbot/storage.py
index 4072a3fa1..4b76152c0 100644
--- a/dexbot/storage.py
+++ b/dexbot/storage.py
@@ -1,22 +1,20 @@
+import json
import os
import os.path
+import queue
import sys
-import json
import threading
-import queue
import uuid
+
import alembic
import alembic.config
-
from appdirs import user_data_dir
-
-from . import helper
from dexbot import APP_NAME, AUTHOR
-
-from sqlalchemy import create_engine, Column, String, Integer, Float, Boolean
+from sqlalchemy import Boolean, Column, Float, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy.orm import sessionmaker, load_only
+from sqlalchemy.orm import load_only, sessionmaker
+from . import helper
Base = declarative_base()
@@ -227,6 +225,7 @@ def __init__(self, **kwargs):
migrations_dir = os.path.join(bundle_dir, 'migrations')
else:
from pkg_resources import resource_filename
+
migrations_dir = resource_filename('dexbot', 'migrations')
if os.path.exists(sqlite_file) and os.path.getsize(sqlite_file) > 0:
diff --git a/dexbot/strategies/base.py b/dexbot/strategies/base.py
index 13bbff7a5..0dc829059 100644
--- a/dexbot/strategies/base.py
+++ b/dexbot/strategies/base.py
@@ -2,20 +2,17 @@
import math
import time
-from dexbot.config import Config
-from dexbot.storage import Storage
-from dexbot.qt_queue.idle_queue import idle_add
-from dexbot.strategies.config_parts.base_config import BaseConfig
-
-from dexbot.orderengines.bitshares_engine import BitsharesOrderEngine
-from dexbot.pricefeeds.bitshares_feed import BitsharesPriceFeed
-
import bitshares.exceptions
-from bitshares.instance import shared_bitshares_instance
-from bitshares.amount import Asset
from bitshares.account import Account
+from bitshares.amount import Asset
+from bitshares.instance import shared_bitshares_instance
from bitshares.market import Market
-
+from dexbot.config import Config
+from dexbot.orderengines.bitshares_engine import BitsharesOrderEngine
+from dexbot.pricefeeds.bitshares_feed import BitsharesPriceFeed
+from dexbot.qt_queue.idle_queue import idle_add
+from dexbot.storage import Storage
+from dexbot.strategies.config_parts.base_config import BaseConfig
from events import Events
# Number of maximum retries used to retry action before failing
@@ -91,18 +88,20 @@ def configure_details(cls, include_default_tabs=True):
'error_ontick',
]
- def __init__(self,
- name,
- config=None,
- onAccount=None,
- onOrderMatched=None,
- onOrderPlaced=None,
- onMarketUpdate=None,
- onUpdateCallOrder=None,
- ontick=None,
- bitshares_instance=None,
- *args,
- **kwargs):
+ def __init__(
+ self,
+ name,
+ config=None,
+ onAccount=None,
+ onOrderMatched=None,
+ onOrderPlaced=None,
+ onMarketUpdate=None,
+ onUpdateCallOrder=None,
+ ontick=None,
+ bitshares_instance=None,
+ *args,
+ **kwargs
+ ):
# BitShares instance
self.bitshares = bitshares_instance or shared_bitshares_instance()
@@ -190,8 +189,8 @@ def __init__(self,
'worker_name': name,
'account': self.worker['account'],
'market': self.worker['market'],
- 'is_disabled': lambda: self.disabled
- }
+ 'is_disabled': lambda: self.disabled,
+ },
)
self.orders_log = logging.LoggerAdapter(logging.getLogger('dexbot.orders_log'), {})
@@ -256,16 +255,18 @@ def store_profit_estimation_data(self):
return None
timestamp = time.time()
- self.store_balance_entry(account, self.worker_name, base_amount, base_symbol,
- quote_amount, quote_symbol, center_price, timestamp)
+ self.store_balance_entry(
+ account, self.worker_name, base_amount, base_symbol, quote_amount, quote_symbol, center_price, timestamp
+ )
def get_profit_estimation_data(self, seconds):
""" Get balance history closest to the given time
:returns The data as dict from the first timestamp going backwards from seconds argument
"""
- return self.get_balance_history(self.config['workers'][self.worker_name].get('account'),
- self.worker_name, seconds)
+ return self.get_balance_history(
+ self.config['workers'][self.worker_name].get('account'), self.worker_name, seconds
+ )
def calc_profit(self):
""" Calculate relative profit for the current worker
@@ -276,8 +277,13 @@ def calc_profit(self):
timestamp = current_time - time_range
# Fetch the balance from history
- old_data = self.get_balance_history(self.config['workers'][self.worker_name].get('account'), self.worker_name,
- timestamp, self.base_asset, self.quote_asset)
+ old_data = self.get_balance_history(
+ self.config['workers'][self.worker_name].get('account'),
+ self.worker_name,
+ timestamp,
+ self.base_asset,
+ self.quote_asset,
+ )
if old_data:
earlier_base = old_data.base_total
earlier_quote = old_data.quote_total
diff --git a/dexbot/strategies/config_parts/base_config.py b/dexbot/strategies/config_parts/base_config.py
index 123ecf3c6..686f3fd05 100644
--- a/dexbot/strategies/config_parts/base_config.py
+++ b/dexbot/strategies/config_parts/base_config.py
@@ -47,7 +47,6 @@
class BaseConfig:
-
@classmethod
def configure(cls, return_base_config=True):
""" Return a list of ConfigElement objects defining the configuration values for this class.
@@ -64,19 +63,34 @@ def configure(cls, return_base_config=True):
# Common configs
base_config = [
- ConfigElement('account', 'string', '', 'Account',
- 'BitShares account name for the bot to operate with',
- ''),
- ConfigElement('market', 'string', 'BTS/USD', 'Market',
- 'BitShares market to operate on, in the format QUOTE/BASE, for example \"BTS/USD\"',
- r'[A-Z0-9\.]+[:\/][A-Z0-9\.]+'),
- ConfigElement('fee_asset', 'string', 'BTS', 'Fee asset',
- 'Asset to be used to pay transaction fees',
- r'[A-Z\.]+'),
- ConfigElement('operational_percent_quote', 'float', 0, 'QUOTE balance %',
- 'Max % of QUOTE asset available to this worker', (0, None, 2, '%')),
- ConfigElement('operational_percent_base', 'float', 0, 'BASE balance %',
- 'Max % of BASE asset available to this worker', (0, None, 2, '%')),
+ ConfigElement('account', 'string', '', 'Account', 'BitShares account name for the bot to operate with', ''),
+ ConfigElement(
+ 'market',
+ 'string',
+ 'BTS/USD',
+ 'Market',
+ 'BitShares market to operate on, in the format QUOTE/BASE, for example \"BTS/USD\"',
+ r'[A-Z0-9\.]+[:\/][A-Z0-9\.]+',
+ ),
+ ConfigElement(
+ 'fee_asset', 'string', 'BTS', 'Fee asset', 'Asset to be used to pay transaction fees', r'[A-Z\.]+'
+ ),
+ ConfigElement(
+ 'operational_percent_quote',
+ 'float',
+ 0,
+ 'QUOTE balance %',
+ 'Max % of QUOTE asset available to this worker',
+ (0, None, 2, '%'),
+ ),
+ ConfigElement(
+ 'operational_percent_base',
+ 'float',
+ 0,
+ 'BASE balance %',
+ 'Max % of BASE asset available to this worker',
+ (0, None, 2, '%'),
+ ),
]
if return_base_config:
diff --git a/dexbot/strategies/config_parts/relative_config.py b/dexbot/strategies/config_parts/relative_config.py
index 512ade064..f03cf3abc 100644
--- a/dexbot/strategies/config_parts/relative_config.py
+++ b/dexbot/strategies/config_parts/relative_config.py
@@ -2,7 +2,6 @@
class RelativeConfig(BaseConfig):
-
@classmethod
def configure(cls, return_base_config=True):
""" Return a list of ConfigElement objects defining the configuration values for this class.
@@ -24,57 +23,165 @@ def configure(cls, return_base_config=True):
('kraken', 'Kraken'),
('bitfinex', 'Bitfinex'),
('gdax', 'Gdax'),
- ('binance', 'Binance')
+ ('binance', 'Binance'),
]
relative_orders_config = [
- ConfigElement('external_feed', 'bool', False, 'External price feed',
- 'Use external reference price instead of center price acquired from the market', None),
- ConfigElement('external_price_source', 'choice', EXCHANGES[0][0], 'External price source',
- 'The bot will try to get price information from this source', EXCHANGES),
- ConfigElement('amount', 'float', 1, 'Amount',
- 'Fixed order size, expressed in quote asset, unless "relative order size" selected',
- (0, None, 8, '')),
- ConfigElement('relative_order_size', 'bool', False, 'Relative order size',
- 'Amount is expressed as a percentage of the account balance of quote/base asset', None),
- ConfigElement('spread', 'float', 5, 'Spread',
- 'The percentage difference between buy and sell', (0, 100, 2, '%')),
- ConfigElement('dynamic_spread', 'bool', False, 'Dynamic spread',
- 'Enable dynamic spread which overrides the spread field', None),
- ConfigElement('market_depth_amount', 'float', 0, 'Market depth',
- 'From which depth will market spread be measured? (QUOTE amount)',
- (0.00000001, 1000000000, 8, '')),
- ConfigElement('dynamic_spread_factor', 'float', 1, 'Dynamic spread factor',
- 'How many percent will own spread be compared to market spread?',
- (0.01, 1000, 2, '%')),
- ConfigElement('center_price', 'float', 0, 'Center price',
- 'Fixed center price expressed in base asset: base/quote', (0, None, 8, '')),
- ConfigElement('center_price_dynamic', 'bool', True, 'Measure center price from market orders',
- 'Estimate the center from closest opposite orders or from a depth', None),
- ConfigElement('center_price_depth', 'float', 0, 'Measurement depth',
- 'Cumulative quote amount from which depth center price will be measured',
- (0.00000001, 1000000000, 8, '')),
- ConfigElement('center_price_from_last_trade', 'bool', False, 'Last trade price as new center price',
- 'This will make orders move by half the spread at every fill', None),
- ConfigElement('center_price_offset', 'bool', False, 'Center price offset based on asset balances',
- 'Automatically adjust orders up or down based on the imbalance of your assets', None),
- ConfigElement('manual_offset', 'float', 0, 'Manual center price offset',
- "Manually adjust orders up or down. "
- "Works independently of other offsets and doesn't override them", (-50, 100, 2, '%')),
- ConfigElement('reset_on_partial_fill', 'bool', True, 'Reset orders on partial fill',
- 'Reset orders when buy or sell order is partially filled', None),
- ConfigElement('partial_fill_threshold', 'float', 30, 'Fill threshold',
- 'Order fill threshold to reset orders', (0, 100, 2, '%')),
- ConfigElement('reset_on_price_change', 'bool', False, 'Reset orders on center price change',
- 'Reset orders when center price is changed more than threshold '
- '(set False for external feeds)', None),
- ConfigElement('price_change_threshold', 'float', 2, 'Price change threshold',
- 'Define center price threshold to react on', (0, 100, 2, '%')),
- ConfigElement('custom_expiration', 'bool', False, 'Custom expiration',
- 'Override order expiration time to trigger a reset', None),
- ConfigElement('expiration_time', 'int', 157680000, 'Order expiration time',
- 'Define custom order expiration time to force orders reset more often, seconds',
- (30, 157680000, ''))
+ ConfigElement(
+ 'external_feed',
+ 'bool',
+ False,
+ 'External price feed',
+ 'Use external reference price instead of center price acquired from the market',
+ None,
+ ),
+ ConfigElement(
+ 'external_price_source',
+ 'choice',
+ EXCHANGES[0][0],
+ 'External price source',
+ 'The bot will try to get price information from this source',
+ EXCHANGES,
+ ),
+ ConfigElement(
+ 'amount',
+ 'float',
+ 1,
+ 'Amount',
+ 'Fixed order size, expressed in quote asset, unless "relative order size" selected',
+ (0, None, 8, ''),
+ ),
+ ConfigElement(
+ 'relative_order_size',
+ 'bool',
+ False,
+ 'Relative order size',
+ 'Amount is expressed as a percentage of the account balance of quote/base asset',
+ None,
+ ),
+ ConfigElement(
+ 'spread', 'float', 5, 'Spread', 'The percentage difference between buy and sell', (0, 100, 2, '%')
+ ),
+ ConfigElement(
+ 'dynamic_spread',
+ 'bool',
+ False,
+ 'Dynamic spread',
+ 'Enable dynamic spread which overrides the spread field',
+ None,
+ ),
+ ConfigElement(
+ 'market_depth_amount',
+ 'float',
+ 0,
+ 'Market depth',
+ 'From which depth will market spread be measured? (QUOTE amount)',
+ (0.00000001, 1000000000, 8, ''),
+ ),
+ ConfigElement(
+ 'dynamic_spread_factor',
+ 'float',
+ 1,
+ 'Dynamic spread factor',
+ 'How many percent will own spread be compared to market spread?',
+ (0.01, 1000, 2, '%'),
+ ),
+ ConfigElement(
+ 'center_price',
+ 'float',
+ 0,
+ 'Center price',
+ 'Fixed center price expressed in base asset: base/quote',
+ (0, None, 8, ''),
+ ),
+ ConfigElement(
+ 'center_price_dynamic',
+ 'bool',
+ True,
+ 'Measure center price from market orders',
+ 'Estimate the center from closest opposite orders or from a depth',
+ None,
+ ),
+ ConfigElement(
+ 'center_price_depth',
+ 'float',
+ 0,
+ 'Measurement depth',
+ 'Cumulative quote amount from which depth center price will be measured',
+ (0.00000001, 1000000000, 8, ''),
+ ),
+ ConfigElement(
+ 'center_price_from_last_trade',
+ 'bool',
+ False,
+ 'Last trade price as new center price',
+ 'This will make orders move by half the spread at every fill',
+ None,
+ ),
+ ConfigElement(
+ 'center_price_offset',
+ 'bool',
+ False,
+ 'Center price offset based on asset balances',
+ 'Automatically adjust orders up or down based on the imbalance of your assets',
+ None,
+ ),
+ ConfigElement(
+ 'manual_offset',
+ 'float',
+ 0,
+ 'Manual center price offset',
+ "Manually adjust orders up or down. " "Works independently of other offsets and doesn't override them",
+ (-50, 100, 2, '%'),
+ ),
+ ConfigElement(
+ 'reset_on_partial_fill',
+ 'bool',
+ True,
+ 'Reset orders on partial fill',
+ 'Reset orders when buy or sell order is partially filled',
+ None,
+ ),
+ ConfigElement(
+ 'partial_fill_threshold',
+ 'float',
+ 30,
+ 'Fill threshold',
+ 'Order fill threshold to reset orders',
+ (0, 100, 2, '%'),
+ ),
+ ConfigElement(
+ 'reset_on_price_change',
+ 'bool',
+ False,
+ 'Reset orders on center price change',
+ 'Reset orders when center price is changed more than threshold ' '(set False for external feeds)',
+ None,
+ ),
+ ConfigElement(
+ 'price_change_threshold',
+ 'float',
+ 2,
+ 'Price change threshold',
+ 'Define center price threshold to react on',
+ (0, 100, 2, '%'),
+ ),
+ ConfigElement(
+ 'custom_expiration',
+ 'bool',
+ False,
+ 'Custom expiration',
+ 'Override order expiration time to trigger a reset',
+ None,
+ ),
+ ConfigElement(
+ 'expiration_time',
+ 'int',
+ 157680000,
+ 'Order expiration time',
+ 'Define custom order expiration time to force orders reset more often, seconds',
+ (30, 157680000, ''),
+ ),
]
return BaseConfig.configure(return_base_config) + relative_orders_config
diff --git a/dexbot/strategies/config_parts/staggered_config.py b/dexbot/strategies/config_parts/staggered_config.py
index 6a97124a5..409d2d133 100644
--- a/dexbot/strategies/config_parts/staggered_config.py
+++ b/dexbot/strategies/config_parts/staggered_config.py
@@ -2,7 +2,6 @@
class StaggeredConfig(BaseConfig):
-
@classmethod
def configure(cls, return_base_config=True):
""" Modes description:
@@ -30,40 +29,72 @@ def configure(cls, return_base_config=True):
('neutral', 'Neutral'),
('valley', 'Valley'),
('buy_slope', 'Buy Slope'),
- ('sell_slope', 'Sell Slope')
+ ('sell_slope', 'Sell Slope'),
]
return BaseConfig.configure(return_base_config) + [
ConfigElement(
- 'mode', 'choice', 'neutral', 'Strategy mode',
- 'How to allocate funds and profits. Doesn\'t effect existing orders, only future ones', modes),
+ 'mode',
+ 'choice',
+ 'neutral',
+ 'Strategy mode',
+ 'How to allocate funds and profits. Doesn\'t effect existing orders, only future ones',
+ modes,
+ ),
ConfigElement(
- 'spread', 'float', 6, 'Spread',
- 'The percentage difference between buy and sell', (0, None, 2, '%')),
+ 'spread', 'float', 6, 'Spread', 'The percentage difference between buy and sell', (0, None, 2, '%')
+ ),
ConfigElement(
- 'increment', 'float', 4, 'Increment',
- 'The percentage difference between staggered orders', (0, None, 2, '%')),
+ 'increment',
+ 'float',
+ 4,
+ 'Increment',
+ 'The percentage difference between staggered orders',
+ (0, None, 2, '%'),
+ ),
ConfigElement(
- 'center_price_dynamic', 'bool', True, 'Market center price',
- 'Begin strategy with center price obtained from the market. Use with mature markets', None),
+ 'center_price_dynamic',
+ 'bool',
+ True,
+ 'Market center price',
+ 'Begin strategy with center price obtained from the market. Use with mature markets',
+ None,
+ ),
ConfigElement(
- 'center_price', 'float', 0, 'Manual center price',
+ 'center_price',
+ 'float',
+ 0,
+ 'Manual center price',
'In an immature market, give a center price manually to begin with. BASE/QUOTE',
- (0, 1000000000, 8, '')),
+ (0, 1000000000, 8, ''),
+ ),
ConfigElement(
- 'lower_bound', 'float', 1, 'Lower bound',
+ 'lower_bound',
+ 'float',
+ 1,
+ 'Lower bound',
'The lowest price (Quote/Base) in the range',
- (0, 1000000000, 8, '')),
+ (0, 1000000000, 8, ''),
+ ),
ConfigElement(
- 'upper_bound', 'float', 1000000, 'Upper bound',
+ 'upper_bound',
+ 'float',
+ 1000000,
+ 'Upper bound',
'The highest price (Quote/Base) in the range',
- (0, 1000000000, 8, '')),
+ (0, 1000000000, 8, ''),
+ ),
ConfigElement(
- 'instant_fill', 'bool', True, 'Allow instant fill',
- 'Allow to execute orders by market', None),
+ 'instant_fill', 'bool', True, 'Allow instant fill', 'Allow to execute orders by market', None
+ ),
ConfigElement(
- 'operational_depth', 'int', 10, 'Operational depth',
- 'Order depth to maintain on books', (2, 9999999, None))
+ 'operational_depth',
+ 'int',
+ 10,
+ 'Operational depth',
+ 'Order depth to maintain on books',
+ (2, 9999999, None),
+ ),
]
@classmethod
diff --git a/dexbot/strategies/config_parts/strategy_config.py b/dexbot/strategies/config_parts/strategy_config.py
index 5c961d3f7..cbf03eafa 100644
--- a/dexbot/strategies/config_parts/strategy_config.py
+++ b/dexbot/strategies/config_parts/strategy_config.py
@@ -17,12 +17,12 @@ def configure(cls, return_base_config=True):
Documentation of ConfigElements can be found from base.py.
"""
return BaseConfig.configure(return_base_config) + [
- ConfigElement('lower_bound', 'float', 1, 'Lower bound',
- 'The bottom price in the range',
- (0, 10000000, 8, '')),
- ConfigElement('upper_bound', 'float', 10, 'Upper bound',
- 'The top price in the range',
- (0, 10000000, 8, '')),
+ ConfigElement(
+ 'lower_bound', 'float', 1, 'Lower bound', 'The bottom price in the range', (0, 10000000, 8, '')
+ ),
+ ConfigElement(
+ 'upper_bound', 'float', 10, 'Upper bound', 'The top price in the range', (0, 10000000, 8, '')
+ ),
]
@classmethod
@@ -37,5 +37,5 @@ def configure_details(cls, include_default_tabs=True):
return BaseConfig.configure_details(include_default_tabs) + [
DetailElement('graph', 'Graph', 'Graph', 'graph.jpg'),
DetailElement('table', 'Orders', 'Data from csv file', 'example.csv'),
- DetailElement('text', 'Log', 'Log data', 'example.log')
+ DetailElement('text', 'Log', 'Log data', 'example.log'),
]
diff --git a/dexbot/strategies/external_feeds/ccxt_feed.py b/dexbot/strategies/external_feeds/ccxt_feed.py
index b3bcfe2c6..14009e4b1 100644
--- a/dexbot/strategies/external_feeds/ccxt_feed.py
+++ b/dexbot/strategies/external_feeds/ccxt_feed.py
@@ -22,14 +22,13 @@ async def fetch_ticker(exchange, symbol):
try:
ticker = await exchange.fetch_ticker(symbol.upper())
except Exception as exception:
- print(type(exception).__name__, exception.args,
- 'Exchange Error (ignoring)')
+ print(type(exception).__name__, exception.args, 'Exchange Error (ignoring)')
except accxt.RequestTimeout as exception:
- print(type(exception).__name__, exception.args,
- 'Request Timeout (ignoring)')
+ print(type(exception).__name__, exception.args, 'Request Timeout (ignoring)')
except accxt.ExchangeNotAvailable as exception:
- print(type(exception).__name__, exception.args,
- 'Exchange Not Available due to downtime or maintenance (ignoring)')
+ print(
+ type(exception).__name__, exception.args, 'Exchange Not Available due to downtime or maintenance (ignoring)'
+ )
await exchange.close()
return ticker
diff --git a/dexbot/strategies/external_feeds/gecko_feed.py b/dexbot/strategies/external_feeds/gecko_feed.py
index 21b9722ef..030950fff 100644
--- a/dexbot/strategies/external_feeds/gecko_feed.py
+++ b/dexbot/strategies/external_feeds/gecko_feed.py
@@ -1,7 +1,7 @@
-import requests
import asyncio
-from dexbot.strategies.external_feeds.process_pair import split_pair, debug
+import requests
+from dexbot.strategies.external_feeds.process_pair import debug, split_pair
""" To use Gecko API, note that gecko does not provide pairs by default.
For base/quote one must be listed as ticker and the other as fullname,
diff --git a/dexbot/strategies/external_feeds/price_feed.py b/dexbot/strategies/external_feeds/price_feed.py
old mode 100755
new mode 100644
index 06fa4b49b..2f83853ea
--- a/dexbot/strategies/external_feeds/price_feed.py
+++ b/dexbot/strategies/external_feeds/price_feed.py
@@ -2,9 +2,15 @@
from dexbot.strategies.external_feeds.ccxt_feed import get_ccxt_price
from dexbot.strategies.external_feeds.gecko_feed import get_gecko_price
+from dexbot.strategies.external_feeds.process_pair import (
+ debug,
+ filter_bit_symbol,
+ filter_prefix_symbol,
+ get_consolidated_pair,
+ join_pair,
+ split_pair,
+)
from dexbot.strategies.external_feeds.waves_feed import get_waves_price
-from dexbot.strategies.external_feeds.process_pair import split_pair, join_pair, filter_prefix_symbol, \
- filter_bit_symbol, get_consolidated_pair, debug
class PriceFeed:
diff --git a/dexbot/strategies/external_feeds/waves_feed.py b/dexbot/strategies/external_feeds/waves_feed.py
index 62bf0b9ed..91749d959 100644
--- a/dexbot/strategies/external_feeds/waves_feed.py
+++ b/dexbot/strategies/external_feeds/waves_feed.py
@@ -1,6 +1,7 @@
+import asyncio
+
import dexbot.strategies.external_feeds.process_pair
import requests
-import asyncio
WAVES_URL = 'https://marketdata.wavesplatform.com/api/'
SYMBOLS_URL = "/symbols"
diff --git a/dexbot/strategies/king_of_the_hill.py b/dexbot/strategies/king_of_the_hill.py
index eec01d719..9b4e6365a 100644
--- a/dexbot/strategies/king_of_the_hill.py
+++ b/dexbot/strategies/king_of_the_hill.py
@@ -1,6 +1,5 @@
# Python imports
import copy
-
from datetime import datetime, timedelta
from decimal import Decimal
@@ -8,7 +7,6 @@
from dexbot.strategies.base import StrategyBase
from dexbot.strategies.config_parts.koth_config import KothConfig
-
STRATEGY_NAME = 'King of the Hill'
diff --git a/dexbot/strategies/relative_orders.py b/dexbot/strategies/relative_orders.py
index cf7585d1c..797162e52 100644
--- a/dexbot/strategies/relative_orders.py
+++ b/dexbot/strategies/relative_orders.py
@@ -127,7 +127,7 @@ def tick(self, d):
""" Ticks come in on every block. We need to periodically check orders because cancelled orders
do not triggers a market_update event
"""
- if (self.is_reset_on_price_change and not self.counter % 8):
+ if self.is_reset_on_price_change and not self.counter % 8:
self.log.debug('Checking orders by tick threshold')
self.check_orders()
self.counter += 1
@@ -142,8 +142,10 @@ def amount_to_sell(self):
amount = quote_balance * (self.order_size / 100)
# Sell / receive amount should match x2 of minimal possible fraction of asset
- if (amount < 2 * 10 ** -self.market['quote']['precision'] or
- amount * self.sell_price < 2 * 10 ** -self.market['base']['precision']):
+ if (
+ amount < 2 * 10 ** -self.market['quote']['precision']
+ or amount * self.sell_price < 2 * 10 ** -self.market['base']['precision']
+ ):
amount = 0
return amount
@@ -158,8 +160,10 @@ def amount_to_buy(self):
amount = base_balance * (self.order_size / 100) / self.buy_price
# Sell / receive amount should match x2 of minimal possible fraction of asset
- if (amount < 2 * 10 ** -self.market['quote']['precision'] or
- amount * self.buy_price < 2 * 10 ** -self.market['base']['precision']):
+ if (
+ amount < 2 * 10 ** -self.market['quote']['precision']
+ or amount * self.buy_price < 2 * 10 ** -self.market['base']['precision']
+ ):
amount = 0
return amount
@@ -211,19 +215,20 @@ def calculate_order_prices(self):
self.log.warning('Failed to obtain last trade price')
try:
center_price = self.get_market_center_price()
- self.log.info('Using market center price (failed to obtain last trade): {:.8f}'
- .format(center_price))
+ self.log.info(
+ 'Using market center price (failed to obtain last trade): {:.8f}'.format(center_price)
+ )
except TypeError:
self.log.warning('Failed to obtain center price from market')
elif self.center_price_depth > 0:
# Calculate with quote amount if given
center_price = self.get_market_center_price(quote_amount=self.center_price_depth)
try:
- self.log.info('Using market center price: {:.8f} with depth: {:.{prec}f}'.format(
- center_price,
- self.center_price_depth,
- prec=self.market['quote']['precision']
- ))
+ self.log.info(
+ 'Using market center price: {:.8f} with depth: {:.{prec}f}'.format(
+ center_price, self.center_price_depth, prec=self.market['quote']['precision']
+ )
+ )
except TypeError:
self.log.warning('Failed to obtain depthted center price')
else:
@@ -234,20 +239,12 @@ def calculate_order_prices(self):
self.log.warning('Failed to obtain center price from market')
self.center_price = self.calculate_center_price(
- center_price,
- self.is_asset_offset,
- spread,
- self['order_ids'],
- self.manual_offset
+ center_price, self.is_asset_offset, spread, self['order_ids'], self.manual_offset
)
else:
# User has given center price to use, calculate offsets and spread
self.center_price = self.calculate_center_price(
- self.center_price,
- self.is_asset_offset,
- spread,
- self['order_ids'],
- self.manual_offset
+ self.center_price, self.is_asset_offset, spread, self['order_ids'], self.manual_offset
)
try:
@@ -461,24 +458,21 @@ def _calculate_center_price(self, suppress_errors=False):
if highest_bid is None or highest_bid == 0.0:
if not suppress_errors:
- self.log.critical(
- "Cannot estimate center price, there is no highest bid."
- )
+ self.log.critical("Cannot estimate center price, there is no highest bid.")
self.disabled = True
return None
elif lowest_ask is None or lowest_ask == 0.0:
if not suppress_errors:
- self.log.critical(
- "Cannot estimate center price, there is no lowest ask."
- )
+ self.log.critical("Cannot estimate center price, there is no lowest ask.")
self.disabled = True
return None
# Calculate center price between two closest orders on the market
return highest_bid * math.sqrt(lowest_ask / highest_bid)
- def calculate_center_price(self, center_price=None, asset_offset=False, spread=None,
- order_ids=None, manual_offset=0, suppress_errors=False):
+ def calculate_center_price(
+ self, center_price=None, asset_offset=False, spread=None, order_ids=None, manual_offset=0, suppress_errors=False
+ ):
""" Calculate center price which shifts based on available funds
"""
if center_price is None:
@@ -605,11 +599,7 @@ def check_orders(self, *args, **kwargs):
spread = self.get_market_spread(quote_amount=self.market_depth_amount) * self.dynamic_spread_factor
center_price = self.calculate_center_price(
- None,
- self.is_asset_offset,
- spread,
- self['order_ids'],
- self.manual_offset
+ None, self.is_asset_offset, spread, self['order_ids'], self.manual_offset
)
diff = abs((self.center_price - center_price) / self.center_price)
if diff >= self.price_change_threshold:
diff --git a/dexbot/strategies/staggered_orders.py b/dexbot/strategies/staggered_orders.py
index 85275bea2..fd9864074 100644
--- a/dexbot/strategies/staggered_orders.py
+++ b/dexbot/strategies/staggered_orders.py
@@ -1,12 +1,12 @@
-import time
import math
+import time
import uuid
-import bitsharesapi.exceptions
from datetime import datetime, timedelta
from functools import reduce
-from bitshares.dex import Dex
-from bitshares.amount import Amount
+import bitsharesapi.exceptions
+from bitshares.amount import Amount
+from bitshares.dex import Dex
from dexbot.strategies.base import StrategyBase
from dexbot.strategies.config_parts.staggered_config import StaggeredConfig
@@ -57,8 +57,10 @@ def __init__(self, *args, **kwargs):
fee_sum = self.market['base'].market_fee_percent + self.market['quote'].market_fee_percent
if self.target_spread - self.increment < fee_sum:
- self.log.error('Spread must be greater than increment by at least {}, refusing to work because worker'
- ' will make losses'.format(fee_sum))
+ self.log.error(
+ 'Spread must be greater than increment by at least {}, refusing to work because worker'
+ ' will make losses'.format(fee_sum)
+ )
self.disabled = True
if self.operational_depth < 2:
@@ -226,19 +228,24 @@ def maintain_strategy(self, *args, **kwargs):
# Greatly increase check interval to lower CPU load whether there is no funds to allocate or we cannot
# allocate funds for some reason
- if (self.current_check_interval == self.min_check_interval and
- self.base_balance_history[1] == self.base_balance_history[2] and
- self.quote_balance_history[1] == self.quote_balance_history[2]):
+ if (
+ self.current_check_interval == self.min_check_interval
+ and self.base_balance_history[1] == self.base_balance_history[2]
+ and self.quote_balance_history[1] == self.quote_balance_history[2]
+ ):
# Balance didn't changed, so we can reduce maintenance frequency
- self.log.debug('Raising check interval up to {} seconds to reduce CPU usage'.format(
- self.max_check_interval))
+ self.log.debug(
+ 'Raising check interval up to {} seconds to reduce CPU usage'.format(self.max_check_interval)
+ )
self.current_check_interval = self.max_check_interval
- elif (self.current_check_interval == self.max_check_interval and
- (self.base_balance_history[1] != self.base_balance_history[2] or
- self.quote_balance_history[1] != self.quote_balance_history[2])):
+ elif self.current_check_interval == self.max_check_interval and (
+ self.base_balance_history[1] != self.base_balance_history[2]
+ or self.quote_balance_history[1] != self.quote_balance_history[2]
+ ):
# Balance changed, increase maintenance frequency to allocate more quickly
- self.log.debug('Reducing check interval to {} seconds because of changed '
- 'balances'.format(self.min_check_interval))
+ self.log.debug(
+ 'Reducing check interval to {} seconds because of changed ' 'balances'.format(self.min_check_interval)
+ )
self.current_check_interval = self.min_check_interval
if previous_bootstrap_state is True and self['bootstrapping'] is False:
@@ -246,10 +253,12 @@ def maintain_strategy(self, *args, **kwargs):
self.dump_initial_orders()
# Do not continue whether balances are changing or bootstrap is on
- if (self['bootstrapping'] or
- self.base_balance_history[0] != self.base_balance_history[2] or
- self.quote_balance_history[0] != self.quote_balance_history[2] or
- trx_executed):
+ if (
+ self['bootstrapping']
+ or self.base_balance_history[0] != self.base_balance_history[2]
+ or self.quote_balance_history[0] != self.quote_balance_history[2]
+ or trx_executed
+ ):
self.last_check = datetime.now()
self.log_maintenance_time()
return
@@ -278,8 +287,10 @@ def maintain_strategy(self, *args, **kwargs):
return
elif self.buy_orders:
# If target spread is not reached and no balance to allocate, cancel lowest buy order
- self.log.info('Free balances are not changing, bootstrap is off and target spread is not reached. '
- 'Cancelling lowest buy order as a fallback')
+ self.log.info(
+ 'Free balances are not changing, bootstrap is off and target spread is not reached. '
+ 'Cancelling lowest buy order as a fallback'
+ )
self.cancel_orders_wrapper(self.buy_orders[-1])
self.last_check = datetime.now()
@@ -350,14 +361,24 @@ def refresh_balances(self, use_cached_orders=False):
op_percent_base = self.get_worker_share_for_asset(self.market['base']['symbol'])
if op_percent_quote < 1:
op_quote_balance *= op_percent_quote
- self.log.debug('Using {:.2%} of QUOTE balance ({:.{prec}f} {})'
- .format(op_percent_quote, op_quote_balance, self.market['quote']['symbol'],
- prec=self.market['quote']['precision']))
+ self.log.debug(
+ 'Using {:.2%} of QUOTE balance ({:.{prec}f} {})'.format(
+ op_percent_quote,
+ op_quote_balance,
+ self.market['quote']['symbol'],
+ prec=self.market['quote']['precision'],
+ )
+ )
if op_percent_base < 1:
op_base_balance *= op_percent_base
- self.log.debug('Using {:.2%} of BASE balance ({:.{prec}f} {})'
- .format(op_percent_base, op_base_balance, self.market['base']['symbol'],
- prec=self.market['base']['precision']))
+ self.log.debug(
+ 'Using {:.2%} of BASE balance ({:.{prec}f} {})'.format(
+ op_percent_base,
+ op_base_balance,
+ self.market['base']['symbol'],
+ prec=self.market['base']['precision'],
+ )
+ )
# Count balances allocated into virtual orders
virtual_orders_base_balance = 0
@@ -382,9 +403,7 @@ def refresh_balances(self, use_cached_orders=False):
# Calc avail balance; avail balances used in maintain_strategy to pass into allocate_asset
# avail = total - real_orders - virtual_orders
self.quote_balance['amount'] = (
- self.quote_total_balance
- - own_orders_balance['quote']
- - virtual_orders_quote_balance
+ self.quote_total_balance - own_orders_balance['quote'] - virtual_orders_quote_balance
)
self.base_balance['amount'] = self.base_total_balance - own_orders_balance['base'] - virtual_orders_base_balance
@@ -499,6 +518,7 @@ def restore_virtual_orders(self):
If we have both buy and sell real orders, restore both. If we have only one type of orders, restore
corresponding virtual orders and purge opposite orders.
"""
+
def place_further_buy_orders():
furthest_order = self.real_buy_orders[-1]
while furthest_order['price'] > self.lower_bound * (1 + self.increment):
@@ -686,8 +706,16 @@ def store_profit_estimation_data(self, force=False):
if need_store and self.market_center_price:
timestamp = time.time()
self.log.debug('Storing balance data at center price {:.8f}'.format(self.market_center_price))
- self.store_balance_entry(account, self.worker_name, self.base_total_balance, self.base_asset,
- self.quote_total_balance, self.quote_asset, self.market_center_price, timestamp)
+ self.store_balance_entry(
+ account,
+ self.worker_name,
+ self.base_total_balance,
+ self.base_asset,
+ self.quote_total_balance,
+ self.quote_asset,
+ self.market_center_price,
+ timestamp,
+ )
# Cache center price for later comparisons
self.old_center_price = self.market_center_price
@@ -763,13 +791,17 @@ def allocate_asset(self, asset, asset_balance):
self.replace_partially_filled_order(closest_own_order)
return
- if (self['bootstrapping'] and
- self.base_balance_history[2] == self.base_balance_history[0] and
- self.quote_balance_history[2] == self.quote_balance_history[0] and
- opposite_orders):
+ if (
+ self['bootstrapping']
+ and self.base_balance_history[2] == self.base_balance_history[0]
+ and self.quote_balance_history[2] == self.quote_balance_history[0]
+ and opposite_orders
+ ):
# Turn off bootstrap mode whether we're didn't allocated assets during previous 3 maintenance
- self.log.debug('Turning bootstrapping off: actual_spread > target_spread, we have free '
- 'balances and cannot allocate them normally 3 times in a row')
+ self.log.debug(
+ 'Turning bootstrapping off: actual_spread > target_spread, we have free '
+ 'balances and cannot allocate them normally 3 times in a row'
+ )
self['bootstrapping'] = False
""" Note: because we're using operations batching, there is possible a situation when we will have
@@ -782,8 +814,11 @@ def allocate_asset(self, asset, asset_balance):
"""
# Place order closer to the center price
- self.log.debug('Placing closer {} order; actual spread: {:.4%}, target + increment: {:.4%}'
- .format(order_type, self.actual_spread, self.target_spread + self.increment))
+ self.log.debug(
+ 'Placing closer {} order; actual spread: {:.4%}, target + increment: {:.4%}'.format(
+ order_type, self.actual_spread, self.target_spread + self.increment
+ )
+ )
if self['bootstrapping']:
self.place_closer_order(asset, closest_own_order)
elif opposite_orders and self.actual_spread - self.increment < self.target_spread + self.increment:
@@ -796,30 +831,49 @@ def allocate_asset(self, asset, asset_balance):
if self.mode == 'mountain':
opposite_asset_limit = closest_opposite_order['base']['amount'] * (1 + self.increment)
own_asset_limit = None
- self.log.debug('Limiting {} order by opposite order: {:.{prec}f} {}'.format(
- order_type, opposite_asset_limit, opposite_symbol, prec=opposite_precision))
- elif ((self.mode == 'buy_slope' and asset == 'base') or
- (self.mode == 'sell_slope' and asset == 'quote')):
+ self.log.debug(
+ 'Limiting {} order by opposite order: {:.{prec}f} {}'.format(
+ order_type, opposite_asset_limit, opposite_symbol, prec=opposite_precision
+ )
+ )
+ elif (self.mode == 'buy_slope' and asset == 'base') or (
+ self.mode == 'sell_slope' and asset == 'quote'
+ ):
opposite_asset_limit = None
own_asset_limit = closest_opposite_order['quote']['amount']
- self.log.debug('Limiting {} order by opposite order: {:.{prec}f} {}'
- .format(order_type, own_asset_limit, own_symbol, prec=own_precision))
+ self.log.debug(
+ 'Limiting {} order by opposite order: {:.{prec}f} {}'.format(
+ order_type, own_asset_limit, own_symbol, prec=own_precision
+ )
+ )
elif self.mode == 'neutral':
- opposite_asset_limit = closest_opposite_order['base']['amount'] * \
- math.sqrt(1 + self.increment)
+ opposite_asset_limit = closest_opposite_order['base']['amount'] * math.sqrt(1 + self.increment)
own_asset_limit = None
- self.log.debug('Limiting {} order by opposite order: {:.{prec}f} {}'.format(
- order_type, opposite_asset_limit, opposite_symbol, prec=opposite_precision))
- elif (self.mode == 'valley' or
- (self.mode == 'buy_slope' and asset == 'quote') or
- (self.mode == 'sell_slope' and asset == 'base')):
+ self.log.debug(
+ 'Limiting {} order by opposite order: {:.{prec}f} {}'.format(
+ order_type, opposite_asset_limit, opposite_symbol, prec=opposite_precision
+ )
+ )
+ elif (
+ self.mode == 'valley'
+ or (self.mode == 'buy_slope' and asset == 'quote')
+ or (self.mode == 'sell_slope' and asset == 'base')
+ ):
opposite_asset_limit = closest_opposite_order['base']['amount']
own_asset_limit = None
- self.log.debug('Limiting {} order by opposite order: {:.{prec}f} {}'.format(
- order_type, opposite_asset_limit, opposite_symbol, prec=opposite_precision))
+ self.log.debug(
+ 'Limiting {} order by opposite order: {:.{prec}f} {}'.format(
+ order_type, opposite_asset_limit, opposite_symbol, prec=opposite_precision
+ )
+ )
allow_partial = True if asset == 'quote' else False
- self.place_closer_order(asset, closest_own_order, own_asset_limit=own_asset_limit,
- opposite_asset_limit=opposite_asset_limit, allow_partial=allow_partial)
+ self.place_closer_order(
+ asset,
+ closest_own_order,
+ own_asset_limit=own_asset_limit,
+ opposite_asset_limit=opposite_asset_limit,
+ allow_partial=allow_partial,
+ )
else:
# Opposite side probably reached range bound, allow to place partial order
self.place_closer_order(asset, closest_own_order, allow_partial=True)
@@ -838,8 +892,10 @@ def allocate_asset(self, asset, asset_balance):
opposite order will be fully filled.
"""
funds_to_reserve = closest_own_order['base']['amount']
- self.log.debug('Partially filled order on own side, reserving funds to replace: '
- '{:.{prec}f} {}'.format(funds_to_reserve, own_symbol, prec=own_precision))
+ self.log.debug(
+ 'Partially filled order on own side, reserving funds to replace: '
+ '{:.{prec}f} {}'.format(funds_to_reserve, own_symbol, prec=own_precision)
+ )
asset_balance -= funds_to_reserve
if not self.check_partial_fill(closest_opposite_order, fill_threshold=0):
@@ -855,17 +911,17 @@ def allocate_asset(self, asset, asset_balance):
funds_to_reserve = closer_own_order['amount'] * closer_own_order['price'] * additional_reserve
elif asset == 'quote':
funds_to_reserve = closer_own_order['amount'] * additional_reserve
- self.log.debug('Partially filled order on opposite side, reserving funds for next {} order: '
- '{:.{prec}f} {}'.format(order_type, funds_to_reserve, own_symbol,
- prec=own_precision))
+ self.log.debug(
+ 'Partially filled order on opposite side, reserving funds for next {} order: '
+ '{:.{prec}f} {}'.format(order_type, funds_to_reserve, own_symbol, prec=own_precision)
+ )
asset_balance -= funds_to_reserve
if asset_balance > own_threshold:
# Allocate excess funds
- if ((asset == 'base' and furthest_own_order_price /
- (1 + self.increment) < self.lower_bound) or
- (asset == 'quote' and furthest_own_order_price *
- (1 + self.increment) > self.upper_bound)):
+ if (asset == 'base' and furthest_own_order_price / (1 + self.increment) < self.lower_bound) or (
+ asset == 'quote' and furthest_own_order_price * (1 + self.increment) > self.upper_bound
+ ):
# Lower/upper bound has been reached and now will start allocating rest of the balance.
self['bootstrapping'] = False
self.log.debug('Increasing sizes of {} orders'.format(order_type))
@@ -878,8 +934,11 @@ def allocate_asset(self, asset, asset_balance):
else:
increase_status = 'done'
- if (increase_status == 'done' and not self.check_partial_fill(closest_own_order)
- and not self.check_partial_fill(closest_opposite_order, fill_threshold=0)):
+ if (
+ increase_status == 'done'
+ and not self.check_partial_fill(closest_own_order)
+ and not self.check_partial_fill(closest_opposite_order, fill_threshold=0)
+ ):
""" Replace partially filled closest orders only when allocation of excess funds was finished. This
would prevent an abuse case when we are operating inactive market. An attacker can massively dump
the price and then he can buy back the asset cheaper. Similar case may happen on the "normal" market
@@ -895,8 +954,9 @@ def allocate_asset(self, asset, asset_balance):
# Refresh balances to make "reserved" funds available
self.refresh_balances(use_cached_orders=True)
self.replace_partially_filled_order(closest_own_order)
- elif (increase_status == 'done' and not self.check_partial_fill(closest_opposite_order, fill_threshold=(
- 1 - self.partial_fill_threshold))):
+ elif increase_status == 'done' and not self.check_partial_fill(
+ closest_opposite_order, fill_threshold=(1 - self.partial_fill_threshold)
+ ):
# Dust order on opposite side, cancel dust order and place closer order
# Require empty txbuffer to avoid rare condition when order may be already canceled from
# replace_partially_filled_order() call.
@@ -1431,8 +1491,11 @@ def check_partial_fill(self, order, fill_threshold=None):
diff_abs = order['base']['amount'] - order['for_sale']['amount']
diff_rel = diff_abs / order['base']['amount']
if diff_rel > fill_threshold:
- self.log.debug('Partially filled {} order: {} {} @ {:.8f}, filled: {:.2%}'.format(
- order_type, order['base']['amount'], order['base']['symbol'], price, diff_rel))
+ self.log.debug(
+ 'Partially filled {} order: {} {} @ {:.8f}, filled: {:.2%}'.format(
+ order_type, order['base']['amount'], order['base']['symbol'], price, diff_rel
+ )
+ )
return False
return True
@@ -1465,12 +1528,15 @@ def replace_partially_filled_order(self, order):
self.refresh_balances()
else:
needed = order['base']['amount'] - order['for_sale']['amount']
- self.log.debug('Unable to replace partially filled {} order: avail/needed: {:.{prec}f}/{:.{prec}f} {}'
- .format(order_type, asset_balance['amount'], needed, order['base']['symbol'],
- prec=precision))
+ self.log.debug(
+ 'Unable to replace partially filled {} order: avail/needed: {:.{prec}f}/{:.{prec}f} {}'.format(
+ order_type, asset_balance['amount'], needed, order['base']['symbol'], prec=precision
+ )
+ )
- def place_closer_order(self, asset, order, place_order=True, allow_partial=False, own_asset_limit=None,
- opposite_asset_limit=None):
+ def place_closer_order(
+ self, asset, order, place_order=True, allow_partial=False, own_asset_limit=None, opposite_asset_limit=None
+ ):
""" Place order closer to the center
:param asset:
@@ -1530,14 +1596,18 @@ def place_closer_order(self, asset, order, place_order=True, allow_partial=False
# Calculate new order amounts depending on mode
opposite_asset_amount = 0
own_asset_amount = 0
- if (self.mode == 'mountain' or
- (self.mode == 'buy_slope' and asset == 'quote') or
- (self.mode == 'sell_slope' and asset == 'base')):
+ if (
+ self.mode == 'mountain'
+ or (self.mode == 'buy_slope' and asset == 'quote')
+ or (self.mode == 'sell_slope' and asset == 'base')
+ ):
opposite_asset_amount = order['quote']['amount']
own_asset_amount = opposite_asset_amount * price
- elif (self.mode == 'valley' or
- (self.mode == 'buy_slope' and asset == 'base') or
- (self.mode == 'sell_slope' and asset == 'quote')):
+ elif (
+ self.mode == 'valley'
+ or (self.mode == 'buy_slope' and asset == 'base')
+ or (self.mode == 'sell_slope' and asset == 'quote')
+ ):
own_asset_amount = order['base']['amount']
opposite_asset_amount = own_asset_amount / price
elif self.mode == 'neutral':
@@ -1566,17 +1636,25 @@ def place_closer_order(self, asset, order, place_order=True, allow_partial=False
if balance < limiter:
if allow_partial or (
# Accept small inaccuracy for full-sized closer order
- place_order and not allow_partial and limiter - balance < 20 * 10 ** -precision
+ place_order
+ and not allow_partial
+ and limiter - balance < 20 * 10 ** -precision
):
- self.log.debug('Limiting {} order amount to available asset balance: {:.{prec}f} {}'
- .format(order_type, balance, symbol, prec=precision))
+ self.log.debug(
+ 'Limiting {} order amount to available asset balance: {:.{prec}f} {}'.format(
+ order_type, balance, symbol, prec=precision
+ )
+ )
if asset == 'base':
quote_amount = balance / price
elif asset == 'quote':
quote_amount = balance
elif place_order and not allow_partial:
- self.log.debug('Not enough balance to place closer {} order; need/avail: {:.{prec}f}/{:.{prec}f}'
- .format(order_type, limiter, balance, prec=precision))
+ self.log.debug(
+ 'Not enough balance to place closer {} order; need/avail: {:.{prec}f}/{:.{prec}f}'.format(
+ order_type, limiter, balance, prec=precision
+ )
+ )
place_order = False
# Make sure new order is bigger than allowed minimum
@@ -1592,8 +1670,11 @@ def place_closer_order(self, asset, order, place_order=True, allow_partial=False
elif asset == 'quote':
hard_limit = quote_amount
if balance < hard_limit:
- self.log.debug('Not enough balance to place minimal allowed order: {:.{prec}f}/{:.{prec}f} {}'
- .format(balance, hard_limit, symbol, prec=precision))
+ self.log.debug(
+ 'Not enough balance to place minimal allowed order: {:.{prec}f}/{:.{prec}f} {}'.format(
+ balance, hard_limit, symbol, prec=precision
+ )
+ )
place_order = False
if place_order and asset == 'base':
@@ -1652,14 +1733,18 @@ def place_further_order(self, asset, order, place_order=True, allow_partial=Fals
# Calculate new order amounts depending on mode
opposite_asset_amount = 0
own_asset_amount = 0
- if (self.mode == 'mountain' or
- (self.mode == 'buy_slope' and asset == 'quote') or
- (self.mode == 'sell_slope' and asset == 'base')):
+ if (
+ self.mode == 'mountain'
+ or (self.mode == 'buy_slope' and asset == 'quote')
+ or (self.mode == 'sell_slope' and asset == 'base')
+ ):
opposite_asset_amount = order['quote']['amount']
own_asset_amount = opposite_asset_amount * price
- elif (self.mode == 'valley' or
- (self.mode == 'buy_slope' and asset == 'base') or
- (self.mode == 'sell_slope' and asset == 'quote')):
+ elif (
+ self.mode == 'valley'
+ or (self.mode == 'buy_slope' and asset == 'base')
+ or (self.mode == 'sell_slope' and asset == 'quote')
+ ):
own_asset_amount = order['base']['amount']
opposite_asset_amount = own_asset_amount / price
elif self.mode == 'neutral':
@@ -1680,12 +1765,18 @@ def place_further_order(self, asset, order, place_order=True, allow_partial=Fals
# Check whether new order will exceed available balance
if balance < limiter:
if place_order and not allow_partial:
- self.log.debug('Not enough balance to place further {} order; need/avail: {:.{prec}f}/{:.{prec}f}'
- .format(order_type, limiter, balance, prec=precision))
+ self.log.debug(
+ 'Not enough balance to place further {} order; need/avail: {:.{prec}f}/{:.{prec}f}'.format(
+ order_type, limiter, balance, prec=precision
+ )
+ )
place_order = False
elif allow_partial:
- self.log.debug('Limiting {} order amount to available asset balance: {:.{prec}f} {}'
- .format(order_type, balance, symbol, prec=precision))
+ self.log.debug(
+ 'Limiting {} order amount to available asset balance: {:.{prec}f} {}'.format(
+ order_type, balance, symbol, prec=precision
+ )
+ )
if asset == 'base':
quote_amount = balance / price
elif asset == 'quote':
@@ -1704,8 +1795,11 @@ def place_further_order(self, asset, order, place_order=True, allow_partial=Fals
elif asset == 'quote':
hard_limit = quote_amount
if balance < hard_limit:
- self.log.debug('Not enough balance to place minimal allowed order: {:.{prec}f}/{:.{prec}f} {}'
- .format(balance, hard_limit, symbol, prec=precision))
+ self.log.debug(
+ 'Not enough balance to place minimal allowed order: {:.{prec}f}/{:.{prec}f} {}'.format(
+ balance, hard_limit, symbol, prec=precision
+ )
+ )
place_order = False
if place_order and asset == 'base':
@@ -1745,8 +1839,10 @@ def place_highest_sell_order(self, quote_balance, place_order=True, market_cente
if price > self.upper_bound:
self.log.info(
'Not placing highest sell order because price will exceed higher bound. Market center '
- 'price: {:.8f}, closest order price: {:.8f}, upper_bound: {:.8f}'
- .format(market_center_price, price, self.upper_bound))
+ 'price: {:.8f}, closest order price: {:.8f}, upper_bound: {:.8f}'.format(
+ market_center_price, price, self.upper_bound
+ )
+ )
return
sell_orders_count = self.calc_sell_orders_count(price, self.upper_bound)
@@ -1754,8 +1850,9 @@ def place_highest_sell_order(self, quote_balance, place_order=True, market_cente
if self.fee_asset['id'] == self.market['quote']['id']:
buy_orders_count = self.calc_buy_orders_count(price, self.lower_bound)
fee = self.get_order_creation_fee(self.fee_asset)
- real_orders_count = min(buy_orders_count, self.operational_depth) + min(sell_orders_count,
- self.operational_depth)
+ real_orders_count = min(buy_orders_count, self.operational_depth) + min(
+ sell_orders_count, self.operational_depth
+ )
# Exclude all further fees from avail balance
quote_balance = quote_balance - fee * real_orders_count
@@ -1868,8 +1965,10 @@ def place_lowest_buy_order(self, base_balance, place_order=True, market_center_p
if price < self.lower_bound:
self.log.info(
'Not placing lowest buy order because price will exceed lower bound. Market center price: '
- '{:.8f}, closest order price: {:.8f}, lower bound: {:.8f}'
- .format(market_center_price, price, self.lower_bound))
+ '{:.8f}, closest order price: {:.8f}, lower bound: {:.8f}'.format(
+ market_center_price, price, self.lower_bound
+ )
+ )
return
buy_orders_count = self.calc_buy_orders_count(price, self.lower_bound)
@@ -1877,8 +1976,9 @@ def place_lowest_buy_order(self, base_balance, place_order=True, market_center_p
if self.fee_asset['id'] == self.market['base']['id']:
fee = self.get_order_creation_fee(self.fee_asset)
sell_orders_count = self.calc_sell_orders_count(price, self.upper_bound)
- real_orders_count = min(buy_orders_count, self.operational_depth) + min(sell_orders_count,
- self.operational_depth)
+ real_orders_count = min(buy_orders_count, self.operational_depth) + min(
+ sell_orders_count, self.operational_depth
+ )
# Exclude all further fees from avail balance
base_balance = base_balance - fee * real_orders_count
@@ -1985,10 +2085,12 @@ def check_min_order_size(self, amount, price):
if not self.order_min_base or not self.order_min_quote:
self.calculate_min_amounts()
- if (amount < self.order_min_quote or
- amount * price < self.order_min_base):
- self.log.debug('Too small order, base: {:.8f}/{:.8f}, quote: {}/{}'
- .format(amount * price, self.order_min_base, amount, self.order_min_quote))
+ if amount < self.order_min_quote or amount * price < self.order_min_base:
+ self.log.debug(
+ 'Too small order, base: {:.8f}/{:.8f}, quote: {}/{}'.format(
+ amount * price, self.order_min_base, amount, self.order_min_quote
+ )
+ )
return max(self.order_min_quote, self.order_min_base / price)
return amount
@@ -2013,8 +2115,11 @@ def place_virtual_buy_order(self, amount, price):
order['for_sale'] = base_asset
order['price'] = precise_base_amount / precise_quote_amount
- self.log.info('Placing a virtual buy order with {:.{prec}f} {} @ {:.8f}'
- .format(order['base']['amount'], symbol, order['price'], prec=self.market['base']['precision']))
+ self.log.info(
+ 'Placing a virtual buy order with {:.{prec}f} {} @ {:.8f}'.format(
+ order['base']['amount'], symbol, order['price'], prec=self.market['base']['precision']
+ )
+ )
self.virtual_orders.append(order)
# Immediately lower avail balance
@@ -2045,8 +2150,11 @@ def place_virtual_sell_order(self, amount, price):
order['for_sale'] = base_asset
order['price'] = precise_base_amount / precise_quote_amount
- self.log.info('Placing a virtual sell order with {:.{prec}f} {} @ {:.8f}'
- .format(amount, symbol, order['price'], prec=self.market['quote']['precision']))
+ self.log.info(
+ 'Placing a virtual sell order with {:.{prec}f} {} @ {:.8f}'.format(
+ amount, symbol, order['price'], prec=self.market['quote']['precision']
+ )
+ )
self.virtual_orders.append(order)
# Immediately lower avail balance
@@ -2101,5 +2209,6 @@ def tick(self, d):
class VirtualOrder(dict):
""" Wrapper class to handle virtual orders comparison in list index() method
"""
+
def __float__(self):
return self['price']
diff --git a/dexbot/strategies/strategy_template.py b/dexbot/strategies/strategy_template.py
index f5ec6d426..a5c9ee3f9 100644
--- a/dexbot/strategies/strategy_template.py
+++ b/dexbot/strategies/strategy_template.py
@@ -40,6 +40,7 @@ class Strategy(StrategyBase):
NOTE: Change this comment section to describe the strategy.
"""
+
@classmethod
def configure(cls, return_base_config=True):
return StrategyConfig.configure(return_base_config)
diff --git a/dexbot/ui.py b/dexbot/ui.py
index 90d77ff75..244bdbb48 100644
--- a/dexbot/ui.py
+++ b/dexbot/ui.py
@@ -1,21 +1,19 @@
+import logging
+import logging.config
import os
import os.path
import sys
-import logging
-import logging.config
from functools import update_wrapper
import click
-from ruamel import yaml
from appdirs import user_data_dir
-
from bitshares import BitShares
-from bitshares.instance import set_shared_bitshares_instance
from bitshares.exceptions import WrongMasterPasswordException
-
-from dexbot import VERSION, APP_NAME, AUTHOR
+from bitshares.instance import set_shared_bitshares_instance
+from dexbot import APP_NAME, AUTHOR, VERSION
from dexbot.config import Config
from dexbot.node_manager import get_sorted_nodelist, ping
+from ruamel import yaml
log = logging.getLogger(__name__)
@@ -23,23 +21,24 @@
def verbose(f):
@click.pass_context
def new_func(ctx, *args, **kwargs):
- verbosity = [
- "critical", "error", "warn", "info", "debug"
- ][int(min(ctx.obj.get("verbose", 0), 4))]
+ verbosity = ["critical", "error", "warn", "info", "debug"][int(min(ctx.obj.get("verbose", 0), 4))]
if ctx.obj.get("systemd", False):
# Don't print the timestamps: systemd will log it for us
formatter1 = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
formatter2 = logging.Formatter(
- '%(worker_name)s using account %(account)s on %(market)s - %(levelname)s - %(message)s')
+ '%(worker_name)s using account %(account)s on %(market)s - %(levelname)s - %(message)s'
+ )
elif verbosity == "debug":
# When debugging: log where the log call came from
formatter1 = logging.Formatter('%(asctime)s (%(module)s:%(lineno)d) - %(levelname)s - %(message)s')
formatter2 = logging.Formatter(
- '%(asctime)s (%(module)s:%(lineno)d) - %(worker_name)s - %(levelname)s - %(message)s')
+ '%(asctime)s (%(module)s:%(lineno)d) - %(worker_name)s - %(levelname)s - %(message)s'
+ )
else:
formatter1 = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
formatter2 = logging.Formatter(
- '%(asctime)s - %(worker_name)s using account %(account)s on %(market)s - %(levelname)s - %(message)s')
+ '%(asctime)s - %(worker_name)s using account %(account)s on %(market)s - %(levelname)s - %(message)s'
+ )
# Use special format for special workers logger
logger = logging.getLogger("dexbot.per_worker")
@@ -73,22 +72,19 @@ def new_func(ctx, *args, **kwargs):
# GrapheneAPI logging
if ctx.obj["verbose"] > 4:
- verbosity = [
- "critical", "error", "warn", "info", "debug"
- ][int(min(ctx.obj.get("verbose", 4) - 4, 4))]
+ verbosity = ["critical", "error", "warn", "info", "debug"][int(min(ctx.obj.get("verbose", 4) - 4, 4))]
logger = logging.getLogger("grapheneapi")
logger.setLevel(getattr(logging, verbosity.upper()))
logger.addHandler(ch)
if ctx.obj["verbose"] > 8:
- verbosity = [
- "critical", "error", "warn", "info", "debug"
- ][int(min(ctx.obj.get("verbose", 8) - 8, 4))]
+ verbosity = ["critical", "error", "warn", "info", "debug"][int(min(ctx.obj.get("verbose", 8) - 8, 4))]
logger = logging.getLogger("graphenebase")
logger.setLevel(getattr(logging, verbosity.upper()))
logger.addHandler(ch)
return ctx.invoke(f, *args, **kwargs)
+
return update_wrapper(new_func, f)
@@ -113,14 +109,10 @@ def chain(f):
@click.pass_context
def new_func(ctx, *args, **kwargs):
nodelist = sort_nodes(ctx)
- ctx.bitshares = BitShares(
- nodelist,
- num_retries=-1,
- expiration=60,
- **ctx.obj
- )
+ ctx.bitshares = BitShares(nodelist, num_retries=-1, expiration=60, **ctx.obj)
set_shared_bitshares_instance(ctx.bitshares)
return ctx.invoke(f, *args, **kwargs)
+
return update_wrapper(new_func, f)
@@ -139,8 +131,7 @@ def new_func(ctx, *args, **kwargs):
# No user available to interact with
log.critical("Uptick Passphrase not available, exiting")
sys.exit(78) # 'configuration error' in sysexits.h
- pwd = click.prompt(
- "Current Uptick Wallet Passphrase", hide_input=True)
+ pwd = click.prompt("Current Uptick Wallet Passphrase", hide_input=True)
try:
ctx.bitshares.wallet.unlock(pwd)
except WrongMasterPasswordException:
@@ -151,15 +142,15 @@ def new_func(ctx, *args, **kwargs):
# No user available to interact with
log.critical("Uptick Wallet not installed, cannot run")
sys.exit(78)
- click.echo("No Uptick wallet installed yet. \n" +
- "This is a password for encrypting " +
- "the file that contains your private keys. Creating ...")
- pwd = click.prompt(
- "Uptick Wallet Encryption Passphrase",
- hide_input=True,
- confirmation_prompt=True)
+ click.echo(
+ "No Uptick wallet installed yet. \n"
+ + "This is a password for encrypting "
+ + "the file that contains your private keys. Creating ..."
+ )
+ pwd = click.prompt("Uptick Wallet Encryption Passphrase", hide_input=True, confirmation_prompt=True)
ctx.bitshares.wallet.create(pwd)
return ctx.invoke(f, *args, **kwargs)
+
return update_wrapper(new_func, f)
@@ -178,6 +169,7 @@ def new_func(ctx, *args, **kwargs):
with open(ctx.obj["configfile"], 'w') as file:
yaml.dump(ctx.config, file, default_flow_style=False)
return ctx.invoke(f, *args, **kwargs)
+
return update_wrapper(new_func, f)
@@ -188,6 +180,7 @@ def new_func(ctx, *args, **kwargs):
Config(path=ctx.obj['configfile'])
ctx.config = yaml.safe_load(open(ctx.obj["configfile"]))
return ctx.invoke(f, *args, **kwargs)
+
return update_wrapper(new_func, f)
@@ -211,35 +204,20 @@ def formatStd(f):
def warning(msg):
- click.echo(
- "[" +
- click.style("Warning", fg="yellow") +
- "] " + msg
- )
+ click.echo("[" + click.style("Warning", fg="yellow") + "] " + msg)
def confirmwarning(msg):
- return click.confirm(
- "[" +
- click.style("Warning", fg="yellow") +
- "] " + msg
- )
+ return click.confirm("[" + click.style("Warning", fg="yellow") + "] " + msg)
def alert(msg):
- click.echo(
- "[" +
- click.style("Alert", fg="red") +
- "] " + msg
- )
+ click.echo("[" + click.style("Alert", fg="red") + "] " + msg)
def confirmalert(msg):
- return click.confirm(
- "[" +
- click.style("Alert", fg="red") +
- "] " + msg
- )
+ return click.confirm("[" + click.style("Alert", fg="red") + "] " + msg)
+
# error message "translation"
# here we convert some of the cryptic Graphene API error messages into a longer sentence
@@ -248,8 +226,10 @@ def confirmalert(msg):
# it's here because both GUI and CLI might use it
-TRANSLATIONS = {'amount_to_sell.amount > 0': "You need to have sufficient buy and sell amounts in your account",
- 'now <= trx.expiration': "Your node has difficulty syncing to the blockchain, consider changing nodes"}
+TRANSLATIONS = {
+ 'amount_to_sell.amount > 0': "You need to have sufficient buy and sell amounts in your account",
+ 'now <= trx.expiration': "Your node has difficulty syncing to the blockchain, consider changing nodes",
+}
def translate_error(err):
diff --git a/dexbot/views/confirmation.py b/dexbot/views/confirmation.py
index f6af5d81c..31463f5a4 100644
--- a/dexbot/views/confirmation.py
+++ b/dexbot/views/confirmation.py
@@ -1,10 +1,9 @@
-from .ui.confirmation_window_ui import Ui_Dialog
-
from PyQt5 import QtWidgets
+from .ui.confirmation_window_ui import Ui_Dialog
-class ConfirmationDialog(QtWidgets.QDialog):
+class ConfirmationDialog(QtWidgets.QDialog):
def __init__(self, text):
super().__init__()
self.ui = Ui_Dialog()
diff --git a/dexbot/views/create_wallet.py b/dexbot/views/create_wallet.py
index 87a90d52d..5b341f49a 100644
--- a/dexbot/views/create_wallet.py
+++ b/dexbot/views/create_wallet.py
@@ -1,12 +1,10 @@
-from dexbot.views.ui.create_wallet_window_ui import Ui_Dialog
-from dexbot.views.notice import NoticeDialog
from dexbot.views.errors import gui_error
-
+from dexbot.views.notice import NoticeDialog
+from dexbot.views.ui.create_wallet_window_ui import Ui_Dialog
from PyQt5.QtWidgets import QDialog
class CreateWalletView(QDialog, Ui_Dialog):
-
def __init__(self, controller):
self.controller = controller
super().__init__()
diff --git a/dexbot/views/create_worker.py b/dexbot/views/create_worker.py
index 5ab9d6583..010246ded 100644
--- a/dexbot/views/create_worker.py
+++ b/dexbot/views/create_worker.py
@@ -1,11 +1,10 @@
-from .ui.create_worker_window_ui import Ui_Dialog
-from dexbot.controllers.worker_controller import WorkerController, UppercaseValidator
-
+from dexbot.controllers.worker_controller import UppercaseValidator, WorkerController
from PyQt5 import QtWidgets
+from .ui.create_worker_window_ui import Ui_Dialog
-class CreateWorkerView(QtWidgets.QDialog, Ui_Dialog):
+class CreateWorkerView(QtWidgets.QDialog, Ui_Dialog):
def __init__(self, bitshares_instance):
super().__init__()
self.strategy_widget = None
diff --git a/dexbot/views/edit_worker.py b/dexbot/views/edit_worker.py
index f0b43b204..a1d3a6edf 100644
--- a/dexbot/views/edit_worker.py
+++ b/dexbot/views/edit_worker.py
@@ -1,11 +1,10 @@
-from .ui.edit_worker_window_ui import Ui_Dialog
-from dexbot.controllers.worker_controller import WorkerController, UppercaseValidator
-
+from dexbot.controllers.worker_controller import UppercaseValidator, WorkerController
from PyQt5 import QtWidgets
+from .ui.edit_worker_window_ui import Ui_Dialog
-class EditWorkerView(QtWidgets.QDialog, Ui_Dialog):
+class EditWorkerView(QtWidgets.QDialog, Ui_Dialog):
def __init__(self, parent_widget, bitshares_instance, worker_name, config):
super().__init__()
self.worker_name = worker_name
diff --git a/dexbot/views/errors.py b/dexbot/views/errors.py
index bb56ce3f0..b4ac0f40b 100644
--- a/dexbot/views/errors.py
+++ b/dexbot/views/errors.py
@@ -1,11 +1,11 @@
import logging
import traceback
-from dexbot.ui import translate_error
-from .ui.error_dialog_ui import Ui_Dialog
from dexbot.qt_queue.idle_queue import idle_add
+from dexbot.ui import translate_error
+from PyQt5 import QtCore, QtWidgets
-from PyQt5 import QtWidgets, QtCore
+from .ui.error_dialog_ui import Ui_Dialog
class PyQtHandler(logging.Handler):
@@ -44,7 +44,6 @@ def set_info_handler(self, info_handler):
class ErrorDialog(QtWidgets.QDialog, Ui_Dialog):
-
def __init__(self, title, message, extra=None, detail=None):
super().__init__()
self.setupUi(self)
@@ -89,11 +88,12 @@ def hide_details_func(self):
def gui_error(func):
""" A decorator for GUI handler functions - traps all exceptions and displays the dialog
"""
+
def func_wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except BaseException as exc:
- show_dialog("DEXBot Error", "An error occurred with DEXBot: \n"+repr(exc), None, traceback.format_exc())
+ show_dialog("DEXBot Error", "An error occurred with DEXBot: \n" + repr(exc), None, traceback.format_exc())
return func_wrapper
diff --git a/dexbot/views/layouts/flow_layout.py b/dexbot/views/layouts/flow_layout.py
index 422a470ac..4945e6c33 100644
--- a/dexbot/views/layouts/flow_layout.py
+++ b/dexbot/views/layouts/flow_layout.py
@@ -3,7 +3,6 @@
class FlowLayout(QtWidgets.QLayout):
-
def __init__(self, parent=None, margin=0, spacing=-1):
super(FlowLayout, self).__init__(parent)
@@ -76,8 +75,7 @@ def _do_layout(self, rect, test_only):
line_height = 0
if not test_only:
- item.setGeometry(
- QtCore.QRect(QtCore.QPoint(x, y), item.sizeHint()))
+ item.setGeometry(QtCore.QRect(QtCore.QPoint(x, y), item.sizeHint()))
x = next_x
line_height = max(line_height, item.sizeHint().height())
diff --git a/dexbot/views/notice.py b/dexbot/views/notice.py
index 262d7f645..d2395f34e 100644
--- a/dexbot/views/notice.py
+++ b/dexbot/views/notice.py
@@ -1,10 +1,9 @@
-from .ui.notice_window_ui import Ui_Dialog
-
from PyQt5 import QtWidgets
+from .ui.notice_window_ui import Ui_Dialog
-class NoticeDialog(QtWidgets.QDialog, Ui_Dialog):
+class NoticeDialog(QtWidgets.QDialog, Ui_Dialog):
def __init__(self, text):
super().__init__()
self.setupUi(self)
diff --git a/dexbot/views/settings.py b/dexbot/views/settings.py
index aeebc7b43..d9847f87a 100644
--- a/dexbot/views/settings.py
+++ b/dexbot/views/settings.py
@@ -1,11 +1,9 @@
from dexbot.controllers.settings_controller import SettingsController
from dexbot.views.ui.settings_window_ui import Ui_settings_dialog
-
from PyQt5.QtWidgets import QDialog, QDialogButtonBox
class SettingsView(QDialog, Ui_settings_dialog):
-
def __init__(self):
super().__init__()
diff --git a/dexbot/views/strategy_form.py b/dexbot/views/strategy_form.py
index 9a0a5bfce..3f137eb4a 100644
--- a/dexbot/views/strategy_form.py
+++ b/dexbot/views/strategy_form.py
@@ -1,29 +1,21 @@
import importlib
import dexbot.controllers.strategy_controller
-
-from PyQt5 import QtWidgets, QtCore, QtGui
+from PyQt5 import QtCore, QtGui, QtWidgets
class StrategyFormWidget(QtWidgets.QWidget):
-
def __init__(self, controller, strategy_module, worker_config=None):
super().__init__()
self.controller = controller
self.module_name = strategy_module.split('.')[-1]
- strategy_class = getattr(
- importlib.import_module(strategy_module),
- 'Strategy'
- )
+ strategy_class = getattr(importlib.import_module(strategy_module), 'Strategy')
# For strategies uses autogeneration, we need the strategy configs without the defaults
configure = strategy_class.configure(return_base_config=False)
form_module = controller.strategies[strategy_module].get('form_module')
try:
- widget = getattr(
- importlib.import_module(form_module),
- 'Ui_Form'
- )
+ widget = getattr(importlib.import_module(form_module), 'Ui_Form')
self.strategy_widget = widget()
self.strategy_widget.setupUi(self)
except (ValueError, AttributeError):
@@ -37,23 +29,14 @@ def __init__(self, controller, strategy_module, worker_config=None):
try:
# Try to get the controller from the internal set
- strategy_controller = getattr(
- dexbot.controllers.strategy_controller,
- class_name
- )
+ strategy_controller = getattr(dexbot.controllers.strategy_controller, class_name)
except AttributeError:
try:
# look in the strategy module itself (external strategies may do this)
- strategy_controller = getattr(
- importlib.import_module(strategy_module),
- 'StrategyController'
- )
+ strategy_controller = getattr(importlib.import_module(strategy_module), 'StrategyController')
except AttributeError:
# The controller doesn't exist, use the default controller
- strategy_controller = getattr(
- dexbot.controllers.strategy_controller,
- 'StrategyController'
- )
+ strategy_controller = getattr(dexbot.controllers.strategy_controller, 'StrategyController')
self.strategy_controller = strategy_controller(self, configure, controller, worker_config)
@@ -98,11 +81,9 @@ def add_element(self, option):
extra = option.extra
if element_type == 'float':
- element = self.add_double_spin_box(
- key, title, extra[0], extra[1], extra[2], extra[3], description)
+ element = self.add_double_spin_box(key, title, extra[0], extra[1], extra[2], extra[3], description)
elif element_type == 'int':
- element = self.add_spin_box(
- key, title, extra[0], extra[1], extra[2], description)
+ element = self.add_spin_box(key, title, extra[0], extra[1], extra[2], description)
elif element_type == 'string':
element = self.add_line_edit(key, title, description)
elif element_type == 'bool':
diff --git a/dexbot/views/unlock_wallet.py b/dexbot/views/unlock_wallet.py
index f2ce65d05..90cfef318 100644
--- a/dexbot/views/unlock_wallet.py
+++ b/dexbot/views/unlock_wallet.py
@@ -1,12 +1,10 @@
-from dexbot.views.ui.unlock_wallet_window_ui import Ui_Dialog
-from dexbot.views.notice import NoticeDialog
from dexbot.views.errors import gui_error
-
+from dexbot.views.notice import NoticeDialog
+from dexbot.views.ui.unlock_wallet_window_ui import Ui_Dialog
from PyQt5.QtWidgets import QDialog
class UnlockWalletView(QDialog, Ui_Dialog):
-
def __init__(self, controller):
self.controller = controller
super().__init__()
diff --git a/dexbot/views/worker_details.py b/dexbot/views/worker_details.py
index 138fe0e3f..5b56a39b2 100644
--- a/dexbot/views/worker_details.py
+++ b/dexbot/views/worker_details.py
@@ -1,20 +1,17 @@
+import importlib
import os
from dexbot.controllers.worker_details_controller import WorkerDetailsController
from dexbot.helper import get_user_data_directory
-from dexbot.views.ui.worker_details_window_ui import Ui_details_dialog
from dexbot.views.ui.tabs.graph_tab_ui import Ui_Graph_Tab
from dexbot.views.ui.tabs.table_tab_ui import Ui_Table_Tab
from dexbot.views.ui.tabs.text_tab_ui import Ui_Text_Tab
-
+from dexbot.views.ui.worker_details_window_ui import Ui_details_dialog
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QWidget
-import importlib
-
class WorkerDetailsView(QtWidgets.QDialog, Ui_details_dialog, Ui_Graph_Tab, Ui_Table_Tab, Ui_Text_Tab):
-
def __init__(self, worker_name, config):
super().__init__()
diff --git a/dexbot/views/worker_item.py b/dexbot/views/worker_item.py
index e17810154..fcaaf7b37 100644
--- a/dexbot/views/worker_item.py
+++ b/dexbot/views/worker_item.py
@@ -1,18 +1,17 @@
import re
-from .ui.worker_item_widget_ui import Ui_widget
-from .confirmation import ConfirmationDialog
-from .worker_details import WorkerDetailsView
-from .edit_worker import EditWorkerView
-from dexbot.storage import db_worker
from dexbot.controllers.worker_controller import WorkerController
+from dexbot.storage import db_worker
from dexbot.views.errors import gui_error
-
from PyQt5 import QtCore, QtWidgets
+from .confirmation import ConfirmationDialog
+from .edit_worker import EditWorkerView
+from .ui.worker_item_widget_ui import Ui_widget
+from .worker_details import WorkerDetailsView
-class WorkerItemWidget(QtWidgets.QWidget, Ui_widget):
+class WorkerItemWidget(QtWidgets.QWidget, Ui_widget):
def __init__(self, worker_name, config, main_ctrl, view):
super().__init__()
@@ -55,7 +54,7 @@ def setup_ui_data(self, config):
self.set_worker_slider(50)
@gui_error
- def toggle_worker(self, ):
+ def toggle_worker(self,):
if self.horizontalLayout_5.alignment() != QtCore.Qt.AlignRight:
self.start_worker()
else:
@@ -127,7 +126,7 @@ def set_worker_slider(self, value):
margin_left = self.bar.layout().contentsMargins().left()
margin_right = self.bar.layout().contentsMargins().right()
total_padding = spacing + margin_left + margin_right
- usable_width = (bar_width - total_padding)
+ usable_width = bar_width - total_padding
# So we keep the roundness of bars.
# If bar width is less than 2 * border-radius, it squares the corners
@@ -142,8 +141,7 @@ def set_worker_slider(self, value):
@gui_error
def remove_widget_dialog(self):
- dialog = ConfirmationDialog(
- 'Are you sure you want to remove worker "{}"?'.format(self.worker_name))
+ dialog = ConfirmationDialog('Are you sure you want to remove worker "{}"?'.format(self.worker_name))
return_value = dialog.exec_()
if return_value:
self.remove_widget()
@@ -167,8 +165,9 @@ def handle_open_details(self):
@gui_error
def handle_edit_worker(self):
- edit_worker_dialog = EditWorkerView(self, self.main_ctrl.bitshares_instance,
- self.worker_name, self.worker_config)
+ edit_worker_dialog = EditWorkerView(
+ self, self.main_ctrl.bitshares_instance, self.worker_name, self.worker_config
+ )
return_value = edit_worker_dialog.exec_()
# User clicked save
@@ -176,9 +175,9 @@ def handle_edit_worker(self):
new_worker_name = edit_worker_dialog.worker_name
self.view.change_worker_widget_name(self.worker_name, new_worker_name)
self.main_ctrl.pause_worker(self.worker_name, config=self.worker_config)
- self.main_ctrl.config.replace_worker_config(self.worker_name,
- new_worker_name,
- edit_worker_dialog.worker_data)
+ self.main_ctrl.config.replace_worker_config(
+ self.worker_name, new_worker_name, edit_worker_dialog.worker_data
+ )
self.worker_name = new_worker_name
self.reload_widget(new_worker_name)
diff --git a/dexbot/views/worker_list.py b/dexbot/views/worker_list.py
index 18b54272d..7890709a2 100644
--- a/dexbot/views/worker_list.py
+++ b/dexbot/views/worker_list.py
@@ -1,10 +1,12 @@
import time
-from threading import Thread
import webbrowser
+from threading import Thread
from dexbot import __version__
from dexbot.config import Config
from dexbot.controllers.wallet_controller import WalletController
+from dexbot.qt_queue.idle_queue import idle_add
+from dexbot.qt_queue.queue_dispatcher import ThreadDispatcher
from dexbot.views.create_wallet import CreateWalletView
from dexbot.views.create_worker import CreateWorkerView
from dexbot.views.errors import gui_error
@@ -13,17 +15,13 @@
from dexbot.views.ui.worker_list_window_ui import Ui_MainWindow
from dexbot.views.unlock_wallet import UnlockWalletView
from dexbot.views.worker_item import WorkerItemWidget
-from dexbot.qt_queue.idle_queue import idle_add
-from dexbot.qt_queue.queue_dispatcher import ThreadDispatcher
-
+from grapheneapi.exceptions import NumRetriesReached
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtGui import QFontDatabase
from PyQt5.QtWidgets import QMainWindow
-from grapheneapi.exceptions import NumRetriesReached
class MainView(QMainWindow, Ui_MainWindow):
-
def __init__(self, main_controller):
super().__init__()
self.setupUi(self)
@@ -61,8 +59,10 @@ def connect_to_bitshares(self):
try:
self.main_controller.measure_latency(self.config['node'])
except NumRetriesReached:
- self.status_bar.showMessage('ver {} - Coudn\'t connect to Bitshares. '
- 'Please use different node(s) and retry.'.format(__version__))
+ self.status_bar.showMessage(
+ 'ver {} - Coudn\'t connect to Bitshares. '
+ 'Please use different node(s) and retry.'.format(__version__)
+ )
self.main_controller.set_bitshares_instance(None)
return False
@@ -71,8 +71,9 @@ def connect_to_bitshares(self):
return True
else:
# Config has no nodes in it
- self.status_bar.showMessage('ver {} - Node(s) not found. '
- 'Please add node(s) from settings.'.format(__version__))
+ self.status_bar.showMessage(
+ 'ver {} - Node(s) not found. ' 'Please add node(s) from settings.'.format(__version__)
+ )
return False
@pyqtSlot(name='handle_login')
diff --git a/dexbot/whiptail.py b/dexbot/whiptail.py
index 51d16eae7..d64fd3bdc 100644
--- a/dexbot/whiptail.py
+++ b/dexbot/whiptail.py
@@ -1,14 +1,15 @@
from __future__ import print_function
-import sys
-import shlex
-import shutil
+
import itertools
-import click
import os
+import shlex
+import shutil
+import sys
import tempfile
-from subprocess import Popen, PIPE
from collections import namedtuple
+from subprocess import PIPE, Popen
+import click
# whiptail.py - Use whiptail to display dialog boxes from shell scripts
# Copyright (C) 2013 Marwan Alsabbagh
@@ -23,9 +24,7 @@ def flatten(data):
class Whiptail:
-
- def __init__(self, title='', backtitle='', height=20, width=60,
- auto_exit=True):
+ def __init__(self, title='', backtitle='', height=20, width=60, auto_exit=True):
self.title = title
self.backtitle = backtitle
self.height = height
@@ -34,8 +33,15 @@ def __init__(self, title='', backtitle='', height=20, width=60,
def run(self, control, msg, extra=(), exit_on=(1, 255)):
cmd = [
- 'whiptail', '--title', self.title, '--backtitle', self.backtitle,
- '--' + control, msg, str(self.height), str(self.width)
+ 'whiptail',
+ '--title',
+ self.title,
+ '--backtitle',
+ self.backtitle,
+ '--' + control,
+ msg,
+ str(self.height),
+ str(self.width),
]
cmd += list(extra)
p = Popen(cmd, stderr=PIPE)
@@ -125,11 +131,7 @@ def confirm(self, msg, default='yes'):
return click.confirm(msg, default=(default == 'yes'))
def alert(self, msg):
- click.echo(
- "[" +
- click.style("alert", fg="yellow") +
- "] " + msg
- )
+ click.echo("[" + click.style("alert", fg="yellow") + "] " + msg)
def view_text(self, text, pager=True):
if pager:
diff --git a/dexbot/worker.py b/dexbot/worker.py
index c2584ee0e..1f7571353 100644
--- a/dexbot/worker.py
+++ b/dexbot/worker.py
@@ -1,15 +1,14 @@
+import copy
import importlib
-import sys
import logging
import os.path
+import sys
import threading
-import copy
import dexbot.errors as errors
-from dexbot.strategies.base import StrategyBase
-
-from bitshares.notify import Notify
from bitshares.instance import shared_bitshares_instance
+from bitshares.notify import Notify
+from dexbot.strategies.base import StrategyBase
log = logging.getLogger(__name__)
log_workers = logging.getLogger('dexbot.per_worker')
@@ -20,13 +19,7 @@
class WorkerInfrastructure(threading.Thread):
-
- def __init__(
- self,
- config,
- bitshares_instance=None,
- view=None
- ):
+ def __init__(self, config, bitshares_instance=None, view=None):
super().__init__()
# BitShares instance
@@ -52,35 +45,44 @@ def init_workers(self, config):
self.config_lock.acquire()
for worker_name, worker in config["workers"].items():
if "account" not in worker:
- log_workers.critical("Worker has no account", extra={
- 'worker_name': worker_name, 'account': 'unknown',
- 'market': 'unknown', 'is_disabled': (lambda: True)
- })
+ log_workers.critical(
+ "Worker has no account",
+ extra={
+ 'worker_name': worker_name,
+ 'account': 'unknown',
+ 'market': 'unknown',
+ 'is_disabled': (lambda: True),
+ },
+ )
continue
if "market" not in worker:
- log_workers.critical("Worker has no market", extra={
- 'worker_name': worker_name, 'account': worker['account'],
- 'market': 'unknown', 'is_disabled': (lambda: True)
- })
+ log_workers.critical(
+ "Worker has no market",
+ extra={
+ 'worker_name': worker_name,
+ 'account': worker['account'],
+ 'market': 'unknown',
+ 'is_disabled': (lambda: True),
+ },
+ )
continue
try:
- strategy_class = getattr(
- importlib.import_module(worker["module"]),
- 'Strategy'
- )
+ strategy_class = getattr(importlib.import_module(worker["module"]), 'Strategy')
self.workers[worker_name] = strategy_class(
- config=config,
- name=worker_name,
- bitshares_instance=self.bitshares,
- view=self.view
+ config=config, name=worker_name, bitshares_instance=self.bitshares, view=self.view
)
self.markets.add(worker['market'])
self.accounts.add(worker['account'])
except BaseException:
- log_workers.exception("Worker initialisation", extra={
- 'worker_name': worker_name, 'account': worker['account'],
- 'market': 'unknown', 'is_disabled': (lambda: True)
- })
+ log_workers.exception(
+ "Worker initialisation",
+ extra={
+ 'worker_name': worker_name,
+ 'account': worker['account'],
+ 'market': 'unknown',
+ 'is_disabled': (lambda: True),
+ },
+ )
self.config_lock.release()
def update_notify(self):
@@ -101,7 +103,7 @@ def update_notify(self):
on_market=self.on_market,
on_account=self.on_account,
on_block=self.on_block,
- bitshares_instance=self.bitshares
+ bitshares_instance=self.bitshares,
)
# Events
diff --git a/docs/conf.py b/docs/conf.py
index 57908ba21..7b5585876 100755
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -13,8 +13,8 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
-import sys
import os
+import sys
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
@@ -25,7 +25,7 @@
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
+# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
@@ -52,7 +52,7 @@
source_suffix = '.rst'
# The encoding of source files.
-#source_encoding = 'utf-8-sig'
+# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
@@ -80,9 +80,9 @@
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
-#today = ''
+# today = ''
# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
+# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
@@ -90,27 +90,27 @@
# The reST default role (used for this markup: `text`) to use for all
# documents.
-#default_role = None
+# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
-#show_authors = False
+# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+# modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
-#keep_warnings = False
+# keep_warnings = False
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
@@ -125,26 +125,26 @@
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
-#html_theme_options = {}
+# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
+# html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# " v documentation".
-#html_title = None
+# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
-#html_short_title = None
+# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
-#html_logo = None
+# html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
-#html_favicon = None
+# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
@@ -154,62 +154,62 @@
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
-#html_extra_path = []
+# html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
+# html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
-#html_use_smartypants = True
+# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
+# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
# If false, no module index is generated.
-#html_domain_indices = True
+# html_domain_indices = True
# If false, no index is generated.
-#html_use_index = True
+# html_use_index = True
# If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
+# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
+# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
+# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
+# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
+# html_file_suffix = None
# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja'
# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr'
-#html_search_language = 'en'
+# html_search_language = 'en'
# A dictionary with options for the search language support, empty by default.
# Now only 'ja' uses this config value
-#html_search_options = {'type': 'default'}
+# html_search_options = {'type': 'default'}
# The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used.
-#html_search_scorer = 'scorer.js'
+# html_search_scorer = 'scorer.js'
# Output file base name for HTML help builder.
htmlhelp_basename = 'DEXBotdoc'
@@ -217,59 +217,52 @@
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
-
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
-
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
-
-# Latex figure (float) alignment
-#'figure_align': 'htbp',
+ # The paper size ('letterpaper' or 'a4paper').
+ #'papersize': 'letterpaper',
+ # The font size ('10pt', '11pt' or '12pt').
+ #'pointsize': '10pt',
+ # Additional stuff for the LaTeX preamble.
+ #'preamble': '',
+ # Latex figure (float) alignment
+ #'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
- (master_doc, 'DEXBot.tex', 'DEXBot Documentation',
- 'DEXBot Team', 'manual'),
+ (master_doc, 'DEXBot.tex', 'DEXBot Documentation', 'DEXBot Team', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
-#latex_logo = None
+# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
-#latex_use_parts = False
+# latex_use_parts = False
# If true, show page references after internal links.
-#latex_show_pagerefs = False
+# latex_show_pagerefs = False
# If true, show URL addresses after external links.
-#latex_show_urls = False
+# latex_show_urls = False
# Documents to append as an appendix to all manuals.
-#latex_appendices = []
+# latex_appendices = []
# If false, no module index is generated.
-#latex_domain_indices = True
+# latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
-man_pages = [
- (master_doc, 'dexbot', 'DEXBot Documentation',
- [author], 1)
-]
+man_pages = [(master_doc, 'dexbot', 'DEXBot Documentation', [author], 1)]
# If true, show URL addresses after external links.
-#man_show_urls = False
+# man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
@@ -278,19 +271,25 @@
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
- (master_doc, 'DEXBot', 'DEXBot Documentation',
- author, 'DEXBot', 'One line description of project.',
- 'Miscellaneous'),
+ (
+ master_doc,
+ 'DEXBot',
+ 'DEXBot Documentation',
+ author,
+ 'DEXBot',
+ 'One line description of project.',
+ 'Miscellaneous',
+ ),
]
# Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
+# texinfo_appendices = []
# If false, no module index is generated.
-#texinfo_domain_indices = True
+# texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
+# texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
-#texinfo_no_detailmenu = False
+# texinfo_no_detailmenu = False
diff --git a/docs/configuration.rst b/docs/configuration.rst
index 2cbfe19ee..7ac764f65 100644
--- a/docs/configuration.rst
+++ b/docs/configuration.rst
@@ -5,19 +5,19 @@ The configuration consists of a series of questions about the bots you wish to c
1. The Bot Name.
-
+
Choose a unique name for your bot, DEXBot doesn't care what you call it.
It is used to identify the bot in the logs so should be fairly short.
2. The Bot Strategy
-
+
DEXBot provides a number of different bot strategies. They can be quite different in
how they behave (i.e. spend *your* money) so it is important you understand the strategy
before deploying a bot.
a. :doc:`echo` For testing this just logs events on a market, does no trading.
b. :doc:`follow_orders` My (Ian Haywood) main bot, an extension of stakemachine's `wall`,
- it has been used to provide liquidity on AUD:BTS.
+ it has been used to provide liquidity on AUD:BTS.
Does function but by no mean perfect, see caveats in the docs.
3. Strategy-specific questions
@@ -25,14 +25,14 @@ The configuration consists of a series of questions about the bots you wish to c
The questions that follow are determined by the strategy chosen, and each strategy will have its own questions around
amounts to trade, spreads etc. See the strategy documentations linked above. But the first two strategy questions
are nearly universal amongst the strategies so are documented here:
-
+
a. The Account.
This is the same account name as the one where you entered the keys into ``uptick`` earlier on: the bot must
always have the private key so it can execute trades.
b. The Market.
-
+
This is the main market the bot trade on. They are specified by the quote asset, a colon (:), and the base asset, for example
the market for BitShares priced in US dollars is called BTS:USD. BitShares always provides a "reverse" market so
there will be a USD:BTS with the same trades, the only difference is the prices will be the inverse (1/x) of BTS:USD.
diff --git a/docs/manual.rst b/docs/manual.rst
index c5492f92f..fd1589302 100644
--- a/docs/manual.rst
+++ b/docs/manual.rst
@@ -28,7 +28,7 @@ The config.yml file
NAME_OF_BOT:
# Python module to look for the strategy (can be custom)
- # dexbot will search in ~/bots as well as standard dirs
+ # dexbot will search in ~/bots as well as standard dirs
module: "dexbot.strategies.echo"
# The bot class in that module to use
@@ -49,6 +49,3 @@ Using the configuration in custom strategies
The bot's configuration is available to in each strategy as dictionary
in ``self.bot``. The whole configuration is avaialable in
``self.config``. The name of your bot can be found in ``self.name``.
-
-
-
diff --git a/docs/setup.rst b/docs/setup.rst
index 97ca940f3..58ecc8164 100644
--- a/docs/setup.rst
+++ b/docs/setup.rst
@@ -32,7 +32,7 @@ On CentOS/RedHat::
sudo yum install openssl-devel python34-pip python34-devel newt
On other distros you need to check the documentation for how to install these packages, the names should be very similar.
-
+
Installation
------------
@@ -54,8 +54,8 @@ bot's account into a local wallet. This can be done using
uptick addkey
You can get your private key from the BitShares Web Wallet: click the menu on the top right,
-then "Settings", "Accounts", "View keys", then tab "Owner Permissions", click
-on the public key, then "Show".
+then "Settings", "Accounts", "View keys", then tab "Owner Permissions", click
+on the public key, then "Show".
Look for the private key in Wallet Import Format (WIF), it's a "5" followed
by a long list of letters. Select, copy and paste this into the screen
@@ -76,4 +76,3 @@ Configuration
This will walk you through the configuration process.
Read more about this in the :doc:`configuration`.
-
diff --git a/hooks/rthook-Crypto.py b/hooks/rthook-Crypto.py
index e18378ec6..38a52b731 100644
--- a/hooks/rthook-Crypto.py
+++ b/hooks/rthook-Crypto.py
@@ -1,15 +1,18 @@
# Runtime hook for pycryptodome extensions
-import Crypto.Util._raw_api
import importlib.machinery
import os.path
import sys
+import Crypto.Util._raw_api
+
+
def load_raw_lib(name, cdecl):
- for ext in importlib.machinery.EXTENSION_SUFFIXES:
- try:
- return Crypto.Util._raw_api.load_lib(os.path.join(sys._MEIPASS, name + ext), cdecl)
- except OSError:
- pass
+ for ext in importlib.machinery.EXTENSION_SUFFIXES:
+ try:
+ return Crypto.Util._raw_api.load_lib(os.path.join(sys._MEIPASS, name + ext), cdecl)
+ except OSError:
+ pass
+
Crypto.Util._raw_api.load_pycryptodome_raw_lib = load_raw_lib
diff --git a/installer/windows/bundle/bundle.wixproj b/installer/windows/bundle/bundle.wixproj
index f3d5cd6ec..3cc387007 100644
--- a/installer/windows/bundle/bundle.wixproj
+++ b/installer/windows/bundle/bundle.wixproj
@@ -70,4 +70,4 @@
-->
-
\ No newline at end of file
+
diff --git a/installer/windows/bundle/bundle.wxs b/installer/windows/bundle/bundle.wxs
index 218609d98..e47ec408f 100644
--- a/installer/windows/bundle/bundle.wxs
+++ b/installer/windows/bundle/bundle.wxs
@@ -1,9 +1,9 @@
-
+
-
+
-
+
&Cancel
-
+
Please wait until the setup is completed...
Processing:
Initializing...
@@ -53,4 +53,4 @@
[WixBundleName] Installation
Click Install to install [WixBundleName] v[WixBundleVersion] on your computer.
By installing you accept these <a href="#">license terms</a>
-
\ No newline at end of file
+
diff --git a/installer/windows/bundle/resources/classic_theme.xml b/installer/windows/bundle/resources/classic_theme.xml
index 9df3457bf..159a649eb 100644
--- a/installer/windows/bundle/resources/classic_theme.xml
+++ b/installer/windows/bundle/resources/classic_theme.xml
@@ -56,4 +56,4 @@
-
\ No newline at end of file
+
diff --git a/installer/windows/msi/Product.wxs b/installer/windows/msi/Product.wxs
index 74dd75eff..dd2deb9fb 100644
--- a/installer/windows/msi/Product.wxs
+++ b/installer/windows/msi/Product.wxs
@@ -20,7 +20,7 @@
-
+
@@ -43,7 +43,7 @@
-
+
@@ -52,7 +52,7 @@
-
+
\ No newline at end of file
+
diff --git a/installer/windows/msi/README.txt b/installer/windows/msi/README.txt
index 342bd9cce..5d2e8ee3f 100644
--- a/installer/windows/msi/README.txt
+++ b/installer/windows/msi/README.txt
@@ -15,4 +15,4 @@ Website:
https://dexbot.info
Telegram:
-https://t.me/DEXBOTbts
\ No newline at end of file
+https://t.me/DEXBOTbts
diff --git a/installer/windows/msi/msi.wixproj b/installer/windows/msi/msi.wixproj
index ee290c915..03312094e 100644
--- a/installer/windows/msi/msi.wixproj
+++ b/installer/windows/msi/msi.wixproj
@@ -51,4 +51,4 @@
-->
-
\ No newline at end of file
+
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 000000000..f9f741f2a
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,15 @@
+[tool.black]
+line-length = 120
+skip-string-normalization = true
+include = '\.pyi?$'
+exclude = '''
+/(
+ \.git
+ | \.tox
+ | \.venv
+ | _build
+ | buck-out
+ | build
+ | dist
+)/
+'''
diff --git a/pyuic.json b/pyuic.json
index 266116fda..54aacc68b 100644
--- a/pyuic.json
+++ b/pyuic.json
@@ -10,4 +10,4 @@
"pyrcc_options": "",
"pyuic": "pyuic5",
"pyuic_options": "--import-from=dexbot.resources"
-}
\ No newline at end of file
+}
diff --git a/requirements-dev.txt b/requirements-dev.txt
index f37fb9cf1..3d59ffefa 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,3 +1,5 @@
# Packages needed for running testsuite
docker==3.7.2
pytest==4.4.0
+# Needed for development
+pre-commit==1.20.0
diff --git a/setup.py b/setup.py
index da61cad6c..9aab3a9c2 100755
--- a/setup.py
+++ b/setup.py
@@ -1,9 +1,10 @@
#!/usr/bin/env python3
-from dexbot import VERSION, APP_NAME
-
-from setuptools import setup, find_packages
from distutils.command import build as build_module
+from setuptools import find_packages, setup
+
+from dexbot import APP_NAME, VERSION
+
cmd_class = {}
console_scripts = ['dexbot-cli = dexbot.cli:main']
install_requires = []
@@ -17,10 +18,8 @@ def run(self):
try:
from pyqt_distutils.build_ui import build_ui
- cmd_class = {
- 'build_ui': build_ui,
- 'build': BuildCommand
- }
+
+ cmd_class = {'build_ui': build_ui, 'build': BuildCommand}
console_scripts.append('dexbot-gui = dexbot.gui:main')
install_requires.extend(["pyqt-distutils"])
except BaseException as e:
@@ -47,9 +46,7 @@ def run(self):
'Intended Audience :: Developers',
],
cmdclass=cmd_class,
- entry_points={
- 'console_scripts': console_scripts
- },
+ entry_points={'console_scripts': console_scripts},
install_requires=install_requires,
include_package_data=True,
)
diff --git a/tests/conftest.py b/tests/conftest.py
index 204c21c0c..3161c5895 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,18 +1,17 @@
-import uuid
-import docker
import os.path
-import pytest
-import socket
import random
+import socket
import time
+import uuid
+import docker
+import pytest
from bitshares import BitShares
-from bitshares.instance import set_shared_bitshares_instance
-from bitshares.genesisbalance import GenesisBalance
from bitshares.account import Account
from bitshares.asset import Asset
-from bitshares.exceptions import AssetDoesNotExistsException, AccountDoesNotExistsException
-
+from bitshares.exceptions import AccountDoesNotExistsException, AssetDoesNotExistsException
+from bitshares.genesisbalance import GenesisBalance
+from bitshares.instance import set_shared_bitshares_instance
from bitsharesbase.account import PublicKey
from bitsharesbase.chains import known_chains
diff --git a/tests/gecko_test.py b/tests/gecko_test.py
index 45a3a0d27..6ce64f441 100644
--- a/tests/gecko_test.py
+++ b/tests/gecko_test.py
@@ -1,12 +1,17 @@
import click
-from dexbot.styles import yellow
from dexbot.strategies.external_feeds.gecko_feed import get_gecko_price
from dexbot.strategies.external_feeds.process_pair import split_pair
+from dexbot.styles import yellow
def print_usage():
- print("Usage: python3 gecko_feed.py", yellow('[symbol]'),
- "Symbol is required, for example:", yellow('BTC/USD'), sep='')
+ print(
+ "Usage: python3 gecko_feed.py",
+ yellow('[symbol]'),
+ "Symbol is required, for example:",
+ yellow('BTC/USD'),
+ sep='',
+ )
# Unit tests
diff --git a/tests/migrations/conftest.py b/tests/migrations/conftest.py
index ce46044b6..2bc5108e1 100644
--- a/tests/migrations/conftest.py
+++ b/tests/migrations/conftest.py
@@ -1,14 +1,13 @@
+import logging
import os
-import pytest
import tempfile
-import logging
-from sqlalchemy import create_engine, Column, String, Integer, Float
+import pytest
+from dexbot.storage import DatabaseWorker
+from sqlalchemy import Column, Float, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
-from dexbot.storage import DatabaseWorker
-
log = logging.getLogger("dexbot")
log.setLevel(logging.DEBUG)
diff --git a/tests/migrations/test_migrations.py b/tests/migrations/test_migrations.py
index 2fbae51cb..e12520c20 100644
--- a/tests/migrations/test_migrations.py
+++ b/tests/migrations/test_migrations.py
@@ -1,5 +1,4 @@
import pytest
-
from dexbot.storage import DatabaseWorker
diff --git a/tests/process_pair_test.py b/tests/process_pair_test.py
index ae8f09897..7427d9d8e 100644
--- a/tests/process_pair_test.py
+++ b/tests/process_pair_test.py
@@ -1,5 +1,10 @@
-from dexbot.strategies.external_feeds.process_pair import split_pair, get_consolidated_pair, filter_prefix_symbol, \
- filter_bit_symbol
+from dexbot.strategies.external_feeds.process_pair import (
+ filter_bit_symbol,
+ filter_prefix_symbol,
+ get_consolidated_pair,
+ split_pair,
+)
+
"""
This is the unit test for filters in process_pair module.
@@ -24,9 +29,20 @@ def test_split_symbol():
def test_filters():
- test_symbols = ['USDT', 'bridge.USD', 'Rudex.USD', 'open.USD',
- 'GDEX.USD', 'Spark.USD', 'bridge.BTC', 'BTC', 'LTC',
- 'bitUSD', 'bitEUR', 'bitHKD']
+ test_symbols = [
+ 'USDT',
+ 'bridge.USD',
+ 'Rudex.USD',
+ 'open.USD',
+ 'GDEX.USD',
+ 'Spark.USD',
+ 'bridge.BTC',
+ 'BTC',
+ 'LTC',
+ 'bitUSD',
+ 'bitEUR',
+ 'bitHKD',
+ ]
print("Test Symbols", test_symbols, sep=":")
r = [filter_prefix_symbol(i) for i in test_symbols]
print("Filter prefix symbol", r, sep=":")
diff --git a/tests/storage/conftest.py b/tests/storage/conftest.py
index a790b8270..458643da7 100644
--- a/tests/storage/conftest.py
+++ b/tests/storage/conftest.py
@@ -1,6 +1,6 @@
-import pytest
import logging
+import pytest
from dexbot.storage import Storage
log = logging.getLogger("dexbot")
diff --git a/tests/storage/test_storage.py b/tests/storage/test_storage.py
index 30003202a..3eacb3777 100644
--- a/tests/storage/test_storage.py
+++ b/tests/storage/test_storage.py
@@ -1,4 +1,5 @@
import logging
+
import pytest
log = logging.getLogger("dexbot")
diff --git a/tests/strategies/king_of_the_hill/conftest.py b/tests/strategies/king_of_the_hill/conftest.py
index 694feab08..8519f3c82 100644
--- a/tests/strategies/king_of_the_hill/conftest.py
+++ b/tests/strategies/king_of_the_hill/conftest.py
@@ -1,10 +1,10 @@
-import pytest
-import time
+import copy
import logging
+import time
-from dexbot.strategies.king_of_the_hill import Strategy
+import pytest
from dexbot.strategies.base import StrategyBase
-import copy
+from dexbot.strategies.king_of_the_hill import Strategy
log = logging.getLogger("dexbot")
diff --git a/tests/strategies/king_of_the_hill/test_king_of_the_hill.py b/tests/strategies/king_of_the_hill/test_king_of_the_hill.py
index 9994e10d0..a25e9133d 100644
--- a/tests/strategies/king_of_the_hill/test_king_of_the_hill.py
+++ b/tests/strategies/king_of_the_hill/test_king_of_the_hill.py
@@ -1,6 +1,6 @@
import logging
-import pytest
+import pytest
log = logging.getLogger("dexbot")
log.setLevel(logging.DEBUG)
diff --git a/tests/strategies/relative_orders/conftest.py b/tests/strategies/relative_orders/conftest.py
index 6a481d34a..b500a476b 100644
--- a/tests/strategies/relative_orders/conftest.py
+++ b/tests/strategies/relative_orders/conftest.py
@@ -1,8 +1,9 @@
-import pytest
-import time
import copy
import logging
import random
+import time
+
+import pytest
from dexbot.strategies.base import StrategyBase
from dexbot.strategies.relative_orders import Strategy
diff --git a/tests/strategies/relative_orders/test_relative_orders.py b/tests/strategies/relative_orders/test_relative_orders.py
index ba5576ae0..ad63f21b0 100644
--- a/tests/strategies/relative_orders/test_relative_orders.py
+++ b/tests/strategies/relative_orders/test_relative_orders.py
@@ -1,8 +1,8 @@
-import math
-import pytest
import logging
+import math
import time
+import pytest
from bitshares.market import Market
# Turn on debug for dexbot logger
diff --git a/tests/strategies/staggered_orders/conftest.py b/tests/strategies/staggered_orders/conftest.py
index 90396f1fd..469a21c53 100644
--- a/tests/strategies/staggered_orders/conftest.py
+++ b/tests/strategies/staggered_orders/conftest.py
@@ -1,12 +1,11 @@
-import pytest
import copy
-import time
-import tempfile
-import os
import logging
+import os
+import tempfile
+import time
+import pytest
from bitshares.amount import Amount
-
from dexbot.strategies.staggered_orders import Strategy
log = logging.getLogger("dexbot")
diff --git a/tests/strategies/staggered_orders/test_staggered_orders_complex.py b/tests/strategies/staggered_orders/test_staggered_orders_complex.py
index 1c0b8c3d4..a669f7ef3 100644
--- a/tests/strategies/staggered_orders/test_staggered_orders_complex.py
+++ b/tests/strategies/staggered_orders/test_staggered_orders_complex.py
@@ -1,8 +1,8 @@
import logging
-import pytest
import math
-
from datetime import datetime
+
+import pytest
from bitshares.account import Account
from bitshares.amount import Amount
diff --git a/tests/strategies/staggered_orders/test_staggered_orders_highlevel.py b/tests/strategies/staggered_orders/test_staggered_orders_highlevel.py
index 5fa407e88..970b9d12f 100644
--- a/tests/strategies/staggered_orders/test_staggered_orders_highlevel.py
+++ b/tests/strategies/staggered_orders/test_staggered_orders_highlevel.py
@@ -1,7 +1,7 @@
import logging
import math
-import pytest
+import pytest
from dexbot.strategies.staggered_orders import VirtualOrder
# Turn on debug for dexbot logger
diff --git a/tests/strategies/staggered_orders/test_staggered_orders_init.py b/tests/strategies/staggered_orders/test_staggered_orders_init.py
index 01679c301..397211a03 100644
--- a/tests/strategies/staggered_orders/test_staggered_orders_init.py
+++ b/tests/strategies/staggered_orders/test_staggered_orders_init.py
@@ -1,7 +1,7 @@
import copy
import logging
-import pytest
+import pytest
from dexbot.strategies.staggered_orders import Strategy
# Turn on debug for dexbot logger
diff --git a/tests/strategies/staggered_orders/test_staggered_orders_mwsa.py b/tests/strategies/staggered_orders/test_staggered_orders_mwsa.py
index 9f4df474f..20f36f768 100644
--- a/tests/strategies/staggered_orders/test_staggered_orders_mwsa.py
+++ b/tests/strategies/staggered_orders/test_staggered_orders_mwsa.py
@@ -1,4 +1,5 @@
import logging
+
import pytest
# Turn on debug for dexbot logger
diff --git a/tests/styles_test.py b/tests/styles_test.py
index c3b7f5b7e..53e7a8bc6 100644
--- a/tests/styles_test.py
+++ b/tests/styles_test.py
@@ -1,5 +1,4 @@
-from dexbot.styles import green, blue, yellow, red, pink, bold, underline
-
+from dexbot.styles import blue, bold, green, pink, red, underline, yellow
if __name__ == '__main__':
# Test each text style
diff --git a/tests/test_measure_latency.py b/tests/test_measure_latency.py
index 1912c6e6c..3e86e74da 100644
--- a/tests/test_measure_latency.py
+++ b/tests/test_measure_latency.py
@@ -1,5 +1,4 @@
import pytest
-
from dexbot.controllers.main_controller import MainController
from grapheneapi.exceptions import NumRetriesReached
diff --git a/tests/test_prepared_testnet.py b/tests/test_prepared_testnet.py
index 51912f0f5..8fbf632c4 100644
--- a/tests/test_prepared_testnet.py
+++ b/tests/test_prepared_testnet.py
@@ -1,5 +1,4 @@
import pytest
-
from bitshares.account import Account
from bitshares.asset import Asset
diff --git a/tests/test_worker_infrastructure.py b/tests/test_worker_infrastructure.py
index afced72f8..314d0a286 100644
--- a/tests/test_worker_infrastructure.py
+++ b/tests/test_worker_infrastructure.py
@@ -1,8 +1,8 @@
-import threading
import logging
+import threading
import time
-import pytest
+import pytest
from dexbot.worker import WorkerInfrastructure
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')
diff --git a/tests/waves_test.py b/tests/waves_test.py
index 8410d862d..317eb6df0 100644
--- a/tests/waves_test.py
+++ b/tests/waves_test.py
@@ -1,4 +1,4 @@
-from dexbot.strategies.external_feeds.process_pair import split_pair, filter_prefix_symbol, filter_bit_symbol
+from dexbot.strategies.external_feeds.process_pair import filter_bit_symbol, filter_prefix_symbol, split_pair
from dexbot.strategies.external_feeds.waves_feed import get_waves_price
""" This is the unit test for getting external feed data from waves DEX.