示例#1
0
def other_worker(bitshares, kh_worker_name, config_other_account):
    worker = StrategyBase(name=kh_worker_name,
                          config=config_other_account,
                          bitshares_instance=bitshares)
    yield worker
    worker.cancel_all_orders()
    time.sleep(1.1)
示例#2
0
def configure_worker(whiptail, worker_config):
    # By default always editing
    editing = True

    if not worker_config:
        editing = False

    default_strategy = worker_config.get('module',
                                         'dexbot.strategies.relative_orders')
    strategy_list = []

    for strategy in STRATEGIES:
        if default_strategy == strategy['class']:
            default_strategy = strategy['tag']

        # Add strategy tag and name pairs to a list
        strategy_list.append([strategy['tag'], strategy['name']])

    # Strategy selection
    worker_config['module'] = whiptail.radiolist(
        "Choose a worker strategy",
        select_choice(default_strategy, strategy_list))

    for strategy in STRATEGIES:
        if strategy['tag'] == worker_config['module']:
            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')

    # Check if strategy has changed and editing existing worker
    if editing and default_strategy != get_strategy_tag(
            worker_config['module']):
        new_worker_config = {}

        # If strategy has changed, create new config where base elements stay the same
        for config_item in StrategyBase.configure():
            key = config_item[0]
            new_worker_config[key] = worker_config[key]

        # Add module separately to the config
        new_worker_config['module'] = worker_config['module']
        worker_config = new_worker_config

    # Use class metadata for per-worker configuration
    config_elems = strategy_class.configure()
    if config_elems:
        # Strategy options
        for elem in config_elems:
            process_config_element(elem, whiptail, worker_config)
    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"
        )
    return worker_config
示例#3
0
def configure_dexbot(config, ctx):
    whiptail = get_whiptail('DEXBot configure')
    workers = config.get('workers', {})
    if not workers:
        while True:
            txt = whiptail.prompt("Your name for the worker")
            config['workers'] = {txt: configure_worker(whiptail, {})}
            if not whiptail.confirm(
                    "Set up another worker?\n(DEXBot can run multiple workers in one instance)"
            ):
                break
        setup_systemd(whiptail, config)
    else:
        bitshares_instance = ctx.bitshares
        action = whiptail.menu(
            "You have an existing configuration.\nSelect an action:",
            [('NEW', 'Create a new worker'), ('DEL', 'Delete a worker'),
             ('EDIT', 'Edit a worker'), ('CONF', 'Redo general config')])

        if action == 'EDIT':
            worker_name = whiptail.menu("Select worker to edit",
                                        [(index, index) for index in workers])
            config['workers'][worker_name] = configure_worker(
                whiptail, config['workers'][worker_name])

            strategy = StrategyBase(worker_name,
                                    bitshares_instance=bitshares_instance,
                                    config=config)
            strategy.clear_all_worker_data()
        elif action == 'DEL':
            worker_name = whiptail.menu("Select worker to delete",
                                        [(index, index) for index in workers])
            del config['workers'][worker_name]

            strategy = StrategyBase(worker_name,
                                    bitshares_instance=bitshares_instance,
                                    config=config)
            strategy.clear_all_worker_data()
        elif action == 'NEW':
            txt = whiptail.prompt("Your name for the new worker")
            config['workers'][txt] = configure_worker(whiptail, {})
        elif action == 'CONF':
            choice = whiptail.node_radiolist(
                msg="Choose 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)

            setup_systemd(whiptail, config)
    whiptail.clear()
    return config
示例#4
0
 def configure(cls, return_base_config=True):
     return StrategyBase.configure(return_base_config) + [
         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(
             'center_price', 'float', 0, 'Center price',
             'Fixed center price expressed in base asset: base/quote',
             (0, None, 8, '')),
         ConfigElement(
             'center_price_dynamic', 'bool', True,
             'Update center price from closest market orders',
             'Always calculate the middle from the closest market orders',
             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',
             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, ''))
     ]
示例#5
0
    def configure_details(cls, include_default_tabs=True):
        """ This function defines the tabs for detailed view of the worker. Further documentation is found in base.py

            :param include_default_tabs: If default tabs are included as well
            :return: List of DetailElement(s)

            NOTE: Add files to user data folders to see how they behave as an example.
        """
        return StrategyBase.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')
        ]
示例#6
0
 def configure(cls, return_base_config=True):
     return StrategyBase.configure(return_base_config) + [
         ConfigElement('external_price_source', 'choice', EXCHANGES[0], 'External price source',
                       'The bot will try to get price information from this source', EXCHANGES),
         ConfigElement('external_feed', 'bool', False, 'External price feed',
                       'Use external reference price instead of center price acquired from the market', None),
         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_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, ''))
     ]
示例#7
0
    def configure(cls, return_base_config=True):
        """ This function is used to auto generate fields for GUI

            :param return_base_config: If base config is used in addition to this configuration.
            :return: List of ConfigElement(s)
        """
        """ As a demonstration this template has two fields in the worker configuration. Upper and lower bound. 
            Documentation of ConfigElements can be found from base.py.
        """
        return StrategyBase.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, '')),
        ]
示例#8
0
def configure_dexbot(config, ctx):
    """ Main `cli configure` entrypoint

        :param dexbot.config.Config config: dexbot config
    """
    whiptail = get_whiptail('DEXBot configure')
    workers = config.get('workers', {})
    bitshares_instance = ctx.bitshares
    validator = ConfigValidator(bitshares_instance)

    if not workers:
        while True:
            txt = whiptail.prompt("Your name for the worker")
            if len(txt) == 0:
                whiptail.alert("Worker name cannot be blank. ")
            else:
                config['workers'] = {
                    txt: configure_worker(whiptail, {}, bitshares_instance)
                }
                if not whiptail.confirm(
                        "Set up another worker?\n(DEXBot can run multiple workers in one instance)"
                ):
                    break
        setup_systemd(whiptail, config)
    else:
        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'), ('HELP', 'Where to get help'),
                 ('EXIT', 'Quit this application')])

            my_workers = [(index, index) for index in workers]

            if action == 'EXIT':
                # Cancel will also exit the application. but this is a clearer label
                # Todo: modify cancel to be "Quit" or "Exit" for the whiptail menu item.
                break
            elif action == 'LIST':
                if len(my_workers):
                    # List workers, then provide option to list config of workers
                    worker_name = whiptail.menu(
                        "List of Your Workers. Select to view Configuration.",
                        my_workers)
                    content = config['workers'][worker_name]
                    text = '\n'
                    for key, value in content.items():
                        text += '{}: {}\n'.format(key, value)
                    whiptail.view_text(text, pager=False)
                else:
                    whiptail.alert('No workers to view.')
            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)
                else:
                    whiptail.alert('No workers to edit.')
            elif action == 'DEL_WORKER':
                if len(my_workers):
                    worker_name = whiptail.menu("Select worker to delete",
                                                my_workers)
                    del config['workers'][worker_name]
                    # Pass ctx.config which is a loaded config (see ui.py configfile()), while `config` in a Config()
                    # instance, which is empty dict, but capable of returning keys via __getitem__(). We need to pass
                    # loaded config into StrategyBase to avoid loading a default config and preserve `--configfile`
                    # option
                    strategy = StrategyBase(
                        worker_name,
                        bitshares_instance=bitshares_instance,
                        config=ctx.config)
                    strategy.clear_all_worker_data()
                else:
                    whiptail.alert('No workers to delete.')
            elif action == 'NEW':
                worker_name = whiptail.prompt("Your name for the new worker. ")
                if not worker_name:
                    whiptail.alert("Worker name cannot be blank. ")
                elif not validator.validate_worker_name(worker_name):
                    whiptail.alert(
                        'Worker name needs to be unique. "{}" is already in use.'
                        .format(worker_name))
                else:
                    config['workers'][worker_name] = configure_worker(
                        whiptail, {}, bitshares_instance)
            elif action == 'ADD':
                add_account(whiptail, bitshares_instance)
            elif action == 'DEL_ACCOUNT':
                del_account(whiptail, bitshares_instance)
            elif action == 'SHOW':
                account_list = list_accounts(bitshares_instance)
                if account_list:
                    action = whiptail.menu(
                        "Bitshares Account List (Name - Type)", account_list)
                else:
                    whiptail.alert(
                        'You do not have any bitshares accounts in the wallet')
            elif action == 'ADD_NODE':
                txt = whiptail.prompt(
                    "Your name for the new node: e.g. wss://dexnode.net/ws")
                # Insert new node on top of the list
                config['node'].insert(0, txt)
            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']]))
                # Move selected node as first item in the config file's node list
                config['node'].remove(choice)
                config['node'].insert(0, choice)
                setup_systemd(whiptail, config)
            elif action == 'HELP':
                whiptail.alert(
                    "Please see https://github.com/Codaone/DEXBot/wiki")

    whiptail.clear()
    return config
示例#9
0
def configure_worker(whiptail, worker_config, bitshares_instance):
    """ Single worker configurator

        :param whiptail.Whiptail whiptail: instance of Whiptail or NoWhiptail
        :param collections.OrderedDict worker_config: the config dictionary for this worker
        :param bitshares.BitShares bitshares_instance: an instance of BitShares class
    """
    # By default always editing
    editing = True

    if not worker_config:
        editing = False

    default_strategy = worker_config.get('module',
                                         'dexbot.strategies.relative_orders')
    strategy_list = []

    for strategy in STRATEGIES:
        if default_strategy == strategy['class']:
            default_strategy = strategy['tag']

        # Add strategy tag and name pairs to a list
        strategy_list.append([strategy['tag'], strategy['name']])

    # Strategy selection
    worker_config['module'] = whiptail.radiolist(
        "Choose a worker strategy",
        select_choice(default_strategy, strategy_list))

    for strategy in STRATEGIES:
        if strategy['tag'] == worker_config['module']:
            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')

    # Check if strategy has changed and editing existing worker
    if editing and default_strategy != get_strategy_tag(
            worker_config['module']):
        new_worker_config = {}
        # If strategy has changed, create new config where base elements stay the same
        for config_item in StrategyBase.configure():
            try:
                key = config_item[0]
                new_worker_config[key] = worker_config[key]
            except KeyError:
                # In case using old configuration file and there are new fields, this passes missing key
                pass

        # Add module separately to the config
        new_worker_config['module'] = worker_config['module']
        worker_config = new_worker_config

    # Use class metadata for per-worker configuration
    config_elems = strategy_class.configure()

    if config_elems:
        # Strategy options
        for elem in config_elems:
            if not editing and (elem.key == "account"):
                # only allow WIF addition for new workers
                account_name = None
                # Query user until correct account and key provided
                while not account_name:
                    account_name = add_account(whiptail, bitshares_instance)
                worker_config[elem.key] = account_name
            else:  # account name only for edit worker
                process_config_element(elem, whiptail, worker_config)
    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"
        )

    return worker_config
示例#10
0
 def configure_details(cls, include_default_tabs=True):
     return StrategyBase.configure_details(include_default_tabs) + []
示例#11
0
def other_worker(ro_worker_name, config_other_account):
    worker = StrategyBase(name=ro_worker_name, config=config_other_account)
    yield worker
    worker.cancel_all_orders()
    time.sleep(1.1)
示例#12
0
 def remove_offline_worker_data(worker_name):
     StrategyBase.purge_all_local_worker_data(worker_name)
示例#13
0
 def remove_offline_worker(config, worker_name, bitshares_instance):
     # Initialize the base strategy to get control over the data
     strategy = StrategyBase(worker_name, config, bitshares_instance=bitshares_instance)
     strategy.clear_all_worker_data()
示例#14
0
    def __init__(self, *args, **kwargs):
        # Initializes StrategyBase class
        StrategyBase.__init__(self, *args, **kwargs)

        self.log.info("Initializing {}...".format(STRATEGY_NAME))

        # Tick counter
        self.counter = 0

        # Define Callbacks
        self.onMarketUpdate += self.maintain_strategy
        self.ontick += self.tick

        self.error_ontick = self.error
        self.error_onMarketUpdate = self.error
        self.error_onAccount = self.error

        # Get view
        self.view = kwargs.get('view')
        self.worker_name = kwargs.get('name')

        # Worker params
        self.buy_distance = self.worker.get('buy_distance', 3) / 100
        self.sell_distance = self.worker.get('sell_distance', 3) / 100
        self.buy_orders = self.worker.get('buy_orders', '6-4')
        self.sell_orders = self.worker.get('sell_orders', '4-6')
        self.buy_increment_step = self.worker.get('buy_increment_step',
                                                  2) / 100
        self.sell_increment_step = self.worker.get('sell_increment_step',
                                                   2) / 100
        self.buy_stop_ratio = self.worker.get('buy_stop_ratio', 50) / 100
        self.sell_stop_ratio = self.worker.get('sell_stop_ratio', 50) / 100
        self.external_feed = self.worker.get('external_feed', False)
        self.external_price_source = self.worker.get('external_price_source',
                                                     'gecko')
        self.external_market = self.worker.get('external_market',
                                               self.market.get_string('/'))
        self.center_price_depth = self.worker.get('center_price_depth', 0)
        self.cp_from_last_trade = self.worker.get(
            'center_price_from_last_trade', False)
        self.is_reset_on_partial_fill = self.worker.get(
            'reset_on_partial_fill', True)
        self.partial_fill_threshold = self.worker.get('partial_fill_threshold',
                                                      90) / 100
        self.is_reset_on_price_change = self.worker.get(
            'reset_on_price_change', False)
        self.price_change_threshold = self.worker.get('price_change_threshold',
                                                      0.5) / 100
        # Our center price is always dynamic
        self.is_center_price_dynamic = True

        self.buy_orders_percentages = self.validate_orders(self.buy_orders)
        self.sell_orders_percentages = self.validate_orders(self.sell_orders)

        # Set last check in the past to get immediate check at startup
        self.last_check = datetime(2000, 1, 1)
        self.min_check_interval = 6

        if self.view:
            self.update_gui_slider()

        self.log.info("{} initialized.".format(STRATEGY_NAME))
示例#15
0
def other_orders(bitshares, kh_worker_name, config_other_account):
    """ Place some orders from second account to simulate foreign trader
    """
    worker = StrategyBase(name=kh_worker_name,
                          config=config_other_account,
                          bitshares_instance=bitshares)
    worker.place_market_buy_order(10, 0.9)
    worker.place_market_buy_order(10, 1)
    worker.place_market_sell_order(10, 2.1)
    worker.place_market_sell_order(10, 2)

    yield worker
    worker.cancel_all_orders()
    time.sleep(1.1)
示例#16
0
文件: conftest.py 项目: tryiou/DEXBot
def strategybase(worker_name, config, bitshares):
    worker = StrategyBase(worker_name, config=config, bitshares_instance=bitshares)
    yield worker
    worker.cancel_all_orders()
    time.sleep(1.1)