def update_notify(self): if not self.config['workers']: log.critical("No workers to launch, exiting") raise errors.NoWorkersAvailable() if self.notify: # Update the notification instance self.notify.reset_subscriptions(list(self.accounts), list(self.markets)) else: # Initialize the notification instance self.notify = Notify(markets=list(self.markets), accounts=list(self.accounts), on_market=self.on_market, on_account=self.on_account, on_block=self.on_block, bitshares_instance=self.bitshares)
def __init__( self, config, bitshares_instance=None, ): # BitShares instance self.bitshares = bitshares_instance or shared_bitshares_instance() self.config = config # Load all accounts and markets in use to subscribe to them accounts = set() markets = set() for botname, bot in config["bots"].items(): if "account" not in bot: raise ValueError("Bot %s has no account" % botname) if "market" not in bot: raise ValueError("Bot %s has no market" % botname) accounts.add(bot["account"]) markets.add(bot["market"]) # Create notification instance # Technically, this will multiplex markets and accounts and # we need to demultiplex the events after we have received them self.notify = Notify(markets=list(markets), accounts=list(accounts), on_market=self.on_market, on_account=self.on_account, on_block=self.on_block, bitshares_instance=self.bitshares) # Initialize bots: for botname, bot in config["bots"].items(): klass = getattr(importlib.import_module(bot["module"]), bot["bot"]) self.bots[botname] = klass(config=config, name=botname, bitshares_instance=self.bitshares)
def __init__( self, config, bitshares_instance=None, ): # BitShares instance self.bitshares = bitshares_instance or shared_bitshares_instance() self.config = config # Load all accounts and markets in use to subscribe to them accounts = set() markets = set() for botname, bot in config["bots"].items(): if "account" not in bot: raise ValueError("Bot %s has no account" % botname) if "market" not in bot: raise ValueError("Bot %s has no market" % botname) accounts.add(bot["account"]) markets.add(bot["market"]) # Create notification instance # Technically, this will multiplex markets and accounts and # we need to demultiplex the events after we have received them self.notify = Notify( markets=list(markets), accounts=list(accounts), on_market=self.on_market, on_account=self.on_account, on_block=self.on_block, bitshares_instance=self.bitshares ) # Initialize bots: for botname, bot in config["bots"].items(): klass = getattr( importlib.import_module(bot["module"]), bot["bot"] ) self.bots[botname] = klass( config=config, name=botname, bitshares_instance=self.bitshares )
class WorkerInfrastructure(threading.Thread): def __init__(self, config, bitshares_instance=None, view=None): super().__init__() # BitShares instance self.bitshares = bitshares_instance or shared_bitshares_instance() self.config = copy.deepcopy(config) self.view = view self.jobs = [] self.notify = None self.config_lock = threading.RLock() self.workers = {} self.accounts = set() self.markets = set() # Set the module search path user_worker_path = os.path.expanduser("~/bots") if os.path.exists(user_worker_path): sys.path.append(user_worker_path) def init_workers(self, config): """ Initialize the workers """ # set up reporting self.reporters = [] for reporter_params in self.config.get("reports", []): reporter_class = getattr( importlib.import_module(reporter_params['module']), reporter_params.get('class', 'Reporter')) reporter_params = reporter_params.copy() del reporter_params['module'] if 'class' in reporter_params: del reporter_params['class'] reporter_params['worker_infrastructure'] = self reporter_instance = reporter_class(**reporter_params) self.reporters.append(reporter_instance) # set up workers 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) }) 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) }) continue try: 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) 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) }) self.config_lock.release() def update_notify(self): if not self.config['workers']: log.critical("No workers configured to launch, exiting") raise errors.NoWorkersAvailable() if not self.workers: log.critical("No workers actually running") raise errors.NoWorkersAvailable() if self.notify: # Update the notification instance self.notify.reset_subscriptions(list(self.accounts), list(self.markets)) else: # Initialize the notification instance self.notify = Notify(markets=list(self.markets), accounts=list(self.accounts), on_market=self.on_market, on_account=self.on_account, on_block=self.on_block, bitshares_instance=self.bitshares) def shutdown(self): for i in self.reporters: i.shutdown() # Events def on_block(self, data): if self.jobs: try: for job in self.jobs: job() finally: self.jobs = [] self.config_lock.acquire() for reporter in self.reporters: reporter.ontick() for worker_name, worker in self.config["workers"].items(): if worker_name not in self.workers or self.workers[ worker_name].disabled: continue try: self.workers[worker_name].ontick(data) except Exception as e: self.workers[worker_name].log.exception("in ontick()") try: self.workers[worker_name].error_ontick(e) except Exception: self.workers[worker_name].log.exception( "in error_ontick()") self.config_lock.release() def on_market(self, data): if data.get("deleted", False): # No info available on deleted orders return self.config_lock.acquire() for worker_name, worker in self.config["workers"].items(): if self.workers[worker_name].disabled: self.workers[worker_name].log.debug( 'Worker "{}" is disabled'.format(worker_name)) continue if worker["market"] == data.market: try: self.workers[worker_name].onMarketUpdate(data) except Exception as e: self.workers[worker_name].log.exception( "in onMarketUpdate()") try: self.workers[worker_name].error_onMarketUpdate(e) except Exception: self.workers[worker_name].log.exception( "in error_onMarketUpdate()") self.config_lock.release() def on_account(self, account_update): self.config_lock.acquire() account = account_update.account for worker_name, worker in self.config["workers"].items(): if self.workers[worker_name].disabled: self.workers[worker_name].log.info( 'Worker "{}" is disabled'.format(worker_name)) continue if worker["account"] == account["name"]: try: self.workers[worker_name].onAccount(account_update) except Exception as e: self.workers[worker_name].log.exception( "in onAccountUpdate()") try: self.workers[worker_name].error_onAccount(e) except Exception: self.workers[worker_name].log.exception( "in error_onAccountUpdate()") self.config_lock.release() def add_worker(self, worker_name, config): with self.config_lock: self.config['workers'][worker_name] = config['workers'][ worker_name] self.init_workers(config) self.update_notify() def run(self): self.init_workers(self.config) self.update_notify() self.notify.listen() def stop(self, worker_name=None, pause=False): """ Used to stop the worker(s) :param str worker_name: name of the worker to stop :param bool pause: optional argument which tells worker if it was stopped or just paused """ if worker_name and len(self.workers) > 1: # Kill only the specified worker self.remove_market(worker_name) with self.config_lock: account = self.config['workers'][worker_name]['account'] self.config['workers'].pop(worker_name) self.accounts.remove(account) if pause: self.workers[worker_name].pause() self.workers.pop(worker_name, None) self.update_notify() else: # Kill all of the workers if pause: for worker in self.workers: self.workers[worker].pause() if self.notify: self.notify.websocket.close() self.shutdown() def remove_worker(self, worker_name=None): if worker_name: self.workers[worker_name].purge() else: for worker in self.workers: self.workers[worker].purge() def remove_market(self, worker_name): """ Remove the market only if the worker is the only one using it """ with self.config_lock: market = self.config['workers'][worker_name]['market'] for name, worker in self.config['workers'].items(): if market == worker['market']: break # Found the same market, do nothing else: # No markets found, safe to remove self.markets.remove(market) @staticmethod def remove_offline_worker(config, worker_name): # Initialize the base strategy to get control over the data bitshares_instance = BitShares(config['node']) strategy = BaseStrategy(worker_name, config, bitshares_instance=bitshares_instance) strategy.purge() @staticmethod def remove_offline_worker_data(worker_name): BaseStrategy.purge_worker_data(worker_name) def do_next_tick(self, job): """ Add a callable to be executed on the next tick """ self.jobs.append(job)
op['trxid'] = trxid if trxid == latest_order: new_orders.append(op) break if first_order == None: first_order = trxid if latest_order != None and len(new_orders): # Reversed sort order to get them in timeline order for op in new_orders[::-1]: # modify message send_msg = dataParser(op) send_tg_message('*' + username + '*: ' + send_msg.replace('sell', '_sold_')) if first_order: config.set(username, 'last_order_id', first_order) with open(conffile, 'w+') as configfile: config.write(configfile) bitshares = BitShares(node=conf.NODES) set_shared_bitshares_instance(bitshares) notify = Notify(on_block=on_block, bitshares_instance=bitshares) print("Listening..") notify.listen()
class BotInfrastructure(): bots = dict() def __init__( self, config, bitshares_instance=None, ): # BitShares instance self.bitshares = bitshares_instance or shared_bitshares_instance() self.config = config # Load all accounts and markets in use to subscribe to them accounts = set() markets = set() for botname, bot in config["bots"].items(): if "account" not in bot: raise ValueError("Bot %s has no account" % botname) if "market" not in bot: raise ValueError("Bot %s has no market" % botname) accounts.add(bot["account"]) markets.add(bot["market"]) # Create notification instance # Technically, this will multiplex markets and accounts and # we need to demultiplex the events after we have received them self.notify = Notify( markets=list(markets), accounts=list(accounts), on_market=self.on_market, on_account=self.on_account, on_block=self.on_block, bitshares_instance=self.bitshares ) # Initialize bots: for botname, bot in config["bots"].items(): klass = getattr( importlib.import_module(bot["module"]), bot["bot"] ) self.bots[botname] = klass( config=config, name=botname, bitshares_instance=self.bitshares ) # Events def on_block(self, data): for botname, bot in self.config["bots"].items(): if self.bots[botname].disabled: continue try: self.bots[botname].ontick(data) except Exception as e: self.bots[botname].error_ontick(e) log.error( "Error while processing {botname}.tick(): {exception}\n{stack}".format( botname=botname, exception=str(e), stack=traceback.format_exc() )) def on_market(self, data): if data.get("deleted", False): # no info available on deleted orders return for botname, bot in self.config["bots"].items(): if self.bots[botname].disabled: log.info("The bot %s has been disabled" % botname) continue if bot["market"] == data.market: try: self.bots[botname].onMarketUpdate(data) except Exception as e: self.bots[botname].error_onMarketUpdate(e) log.error( "Error while processing {botname}.onMarketUpdate(): {exception}\n{stack}".format( botname=botname, exception=str(e), stack=traceback.format_exc() )) def on_account(self, accountupdate): account = accountupdate.account for botname, bot in self.config["bots"].items(): if self.bots[botname].disabled: log.info("The bot %s has been disabled" % botname) continue if bot["account"] == account["name"]: try: self.bots[botname].onAccount(accountupdate) except Exception as e: self.bots[botname].error_onAccount(e) log.error( "Error while processing {botname}.onAccount(): {exception}\n{stack}".format( botname=botname, exception=str(e), stack=traceback.format_exc() )) def run(self): self.notify.listen()
class BotInfrastructure(): bots = dict() def __init__( self, config, bitshares_instance=None, ): # BitShares instance self.bitshares = bitshares_instance or shared_bitshares_instance() self.config = config # Load all accounts and markets in use to subscribe to them accounts = set() markets = set() for botname, bot in config["bots"].items(): if "account" not in bot: raise ValueError("Bot %s has no account" % botname) if "market" not in bot: raise ValueError("Bot %s has no market" % botname) accounts.add(bot["account"]) markets.add(bot["market"]) # Create notification instance # Technically, this will multiplex markets and accounts and # we need to demultiplex the events after we have received them self.notify = Notify(markets=list(markets), accounts=list(accounts), on_market=self.on_market, on_account=self.on_account, on_block=self.on_block, bitshares_instance=self.bitshares) # Initialize bots: for botname, bot in config["bots"].items(): klass = getattr(importlib.import_module(bot["module"]), bot["bot"]) self.bots[botname] = klass(config=config, name=botname, bitshares_instance=self.bitshares) # Events def on_block(self, data): for botname, bot in self.config["bots"].items(): if self.bots[botname].disabled: continue try: self.bots[botname].ontick(data) except Exception as e: self.bots[botname].error_ontick(e) log.error( "Error while processing {botname}.tick(): {exception}\n{stack}" .format(botname=botname, exception=str(e), stack=traceback.format_exc())) def on_market(self, data): if data.get("deleted", False): # no info available on deleted orders return for botname, bot in self.config["bots"].items(): if self.bots[botname].disabled: log.info("The bot %s has been disabled" % botname) continue if bot["market"] == data.market: try: self.bots[botname].onMarketUpdate(data) except Exception as e: self.bots[botname].error_onMarketUpdate(e) log.error( "Error while processing {botname}.onMarketUpdate(): {exception}\n{stack}" .format(botname=botname, exception=str(e), stack=traceback.format_exc())) def on_account(self, accountupdate): account = accountupdate.account for botname, bot in self.config["bots"].items(): if self.bots[botname].disabled: log.info("The bot %s has been disabled" % botname) continue if bot["account"] == account["name"]: try: self.bots[botname].onAccount(accountupdate) except Exception as e: self.bots[botname].error_onAccount(e) log.error( "Error while processing {botname}.onAccount(): {exception}\n{stack}" .format(botname=botname, exception=str(e), stack=traceback.format_exc())) def run(self): self.notify.listen()
def __init__(self, config, bitshares_instance=None, gui_data=None): super().__init__() # BitShares instance self.bitshares = bitshares_instance or shared_bitshares_instance() self.config = config # set the module search path user_bot_path = os.path.expanduser("~/bots") if os.path.exists(user_bot_path): sys.path.append(user_bot_path) # Load all accounts and markets in use to subscribe to them accounts = set() markets = set() # Initialize bots: for botname, bot in config["bots"].items(): if "account" not in bot: log_bots.critical("Bot has no account", extra={ 'botname': botname, 'account': 'unknown', 'market': 'unknown', 'is_dsabled': (lambda: True) }) continue if "market" not in bot: log_bots.critical("Bot has no market", extra={ 'botname': botname, 'account': bot['account'], 'market': 'unknown', 'is_disabled': (lambda: True) }) continue try: klass = getattr(importlib.import_module(bot["module"]), 'Strategy') self.bots[botname] = klass(config=config, name=botname, bitshares_instance=self.bitshares, gui_data=gui_data) markets.add(bot['market']) accounts.add(bot['account']) except: log_bots.exception("Bot initialisation", extra={ 'botname': botname, 'account': bot['account'], 'market': 'unknown', 'is_disabled': (lambda: True) }) if len(markets) == 0: log.critical("No bots to launch, exiting") sys.exit(70) # 70= "Software error" in /usr/include/sysexts.h # Create notification instance # Technically, this will multiplex markets and accounts and # we need to demultiplex the events after we have received them self.notify = Notify(markets=list(markets), accounts=list(accounts), on_market=self.on_market, on_account=self.on_account, on_block=self.on_block, bitshares_instance=self.bitshares)
class BotInfrastructure(Process): bots = dict() def __init__(self, config, bitshares_instance=None, gui_data=None): super().__init__() # BitShares instance self.bitshares = bitshares_instance or shared_bitshares_instance() self.config = config # set the module search path user_bot_path = os.path.expanduser("~/bots") if os.path.exists(user_bot_path): sys.path.append(user_bot_path) # Load all accounts and markets in use to subscribe to them accounts = set() markets = set() # Initialize bots: for botname, bot in config["bots"].items(): if "account" not in bot: log_bots.critical("Bot has no account", extra={ 'botname': botname, 'account': 'unknown', 'market': 'unknown', 'is_dsabled': (lambda: True) }) continue if "market" not in bot: log_bots.critical("Bot has no market", extra={ 'botname': botname, 'account': bot['account'], 'market': 'unknown', 'is_disabled': (lambda: True) }) continue try: klass = getattr(importlib.import_module(bot["module"]), 'Strategy') self.bots[botname] = klass(config=config, name=botname, bitshares_instance=self.bitshares, gui_data=gui_data) markets.add(bot['market']) accounts.add(bot['account']) except: log_bots.exception("Bot initialisation", extra={ 'botname': botname, 'account': bot['account'], 'market': 'unknown', 'is_disabled': (lambda: True) }) if len(markets) == 0: log.critical("No bots to launch, exiting") sys.exit(70) # 70= "Software error" in /usr/include/sysexts.h # Create notification instance # Technically, this will multiplex markets and accounts and # we need to demultiplex the events after we have received them self.notify = Notify(markets=list(markets), accounts=list(accounts), on_market=self.on_market, on_account=self.on_account, on_block=self.on_block, bitshares_instance=self.bitshares) # Events def on_block(self, data): for botname, bot in self.config["bots"].items(): if (not botname in self.bots) or self.bots[botname].disabled: continue try: self.bots[botname].ontick(data) except Exception as e: self.bots[botname].error_ontick(e) self.bots[botname].log.exception("in .tick()") def on_market(self, data): if data.get("deleted", False): # no info available on deleted orders return for botname, bot in self.config["bots"].items(): if self.bots[botname].disabled: self.bots[botname].log.warning("disabled") continue if bot["market"] == data.market: try: self.bots[botname].onMarketUpdate(data) except Exception as e: self.bots[botname].error_onMarketUpdate(e) self.bots[botname].log.exception(".onMarketUpdate()") def on_account(self, accountupdate): account = accountupdate.account for botname, bot in self.config["bots"].items(): if self.bots[botname].disabled: self.bots[botname].log.info("The bot %s has been disabled" % botname) continue if bot["account"] == account["name"]: try: self.bots[botname].onAccount(accountupdate) except Exception as e: self.bots[botname].error_onAccount(e) self.bots[botname].log.exception(".onAccountUpdate()") def run(self): self.notify.listen()
def init_bots(self): """Do the actual initialisation of bots Potentially quite slow (tens of seconds) So called as part of run() """ # set the module search path user_bot_path = os.path.expanduser("~/bots") if os.path.exists(user_bot_path): sys.path.append(user_bot_path) # Load all accounts and markets in use to subscribe to them accounts = set() markets = set() # Initialize bots: for botname, bot in self.config["bots"].items(): if "account" not in bot: log_bots.critical("Bot has no account", extra={ 'botname': botname, 'account': 'unknown', 'market': 'unknown', 'is_dsabled': (lambda: True) }) continue if "market" not in bot: log_bots.critical("Bot has no market", extra={ 'botname': botname, 'account': bot['account'], 'market': 'unknown', 'is_disabled': (lambda: True) }) continue try: klass = getattr(importlib.import_module(bot["module"]), 'Strategy') self.bots[botname] = klass(config=self.config, name=botname, bitshares_instance=self.bitshares, view=self.view) markets.add(bot['market']) accounts.add(bot['account']) except BaseException: log_bots.exception("Bot initialisation", extra={ 'botname': botname, 'account': bot['account'], 'market': 'unknown', 'is_disabled': (lambda: True) }) if len(markets) == 0: log.critical("No bots to launch, exiting") raise errors.NoBotsAvailable() # Create notification instance # Technically, this will multiplex markets and accounts and # we need to demultiplex the events after we have received them self.notify = Notify(markets=list(markets), accounts=list(accounts), on_market=self.on_market, on_account=self.on_account, on_block=self.on_block, bitshares_instance=self.bitshares) # set up reporting if "reports" in self.config: self.reporter = reports.Reporter(self.config['reports'], self.bots) else: self.reporter = None
class BotInfrastructure(threading.Thread): bots = dict() def __init__(self, config, bitshares_instance=None, view=None): """Initialise variables. But no bot setup, therefore fast""" super().__init__() # BitShares instance self.bitshares = bitshares_instance or shared_bitshares_instance() self.config = config self.view = view self.jobs = set() def init_bots(self): """Do the actual initialisation of bots Potentially quite slow (tens of seconds) So called as part of run() """ # set the module search path user_bot_path = os.path.expanduser("~/bots") if os.path.exists(user_bot_path): sys.path.append(user_bot_path) # Load all accounts and markets in use to subscribe to them accounts = set() markets = set() # Initialize bots: for botname, bot in self.config["bots"].items(): if "account" not in bot: log_bots.critical("Bot has no account", extra={ 'botname': botname, 'account': 'unknown', 'market': 'unknown', 'is_dsabled': (lambda: True) }) continue if "market" not in bot: log_bots.critical("Bot has no market", extra={ 'botname': botname, 'account': bot['account'], 'market': 'unknown', 'is_disabled': (lambda: True) }) continue try: klass = getattr(importlib.import_module(bot["module"]), 'Strategy') self.bots[botname] = klass(config=self.config, name=botname, bitshares_instance=self.bitshares, view=self.view) markets.add(bot['market']) accounts.add(bot['account']) except BaseException: log_bots.exception("Bot initialisation", extra={ 'botname': botname, 'account': bot['account'], 'market': 'unknown', 'is_disabled': (lambda: True) }) if len(markets) == 0: log.critical("No bots to launch, exiting") raise errors.NoBotsAvailable() # Create notification instance # Technically, this will multiplex markets and accounts and # we need to demultiplex the events after we have received them self.notify = Notify(markets=list(markets), accounts=list(accounts), on_market=self.on_market, on_account=self.on_account, on_block=self.on_block, bitshares_instance=self.bitshares) # set up reporting if "reports" in self.config: self.reporter = reports.Reporter(self.config['reports'], self.bots) else: self.reporter = None # Events def on_block(self, data): if self.jobs: try: for i in self.jobs: i() finally: self.jobs = set() if self.reporter is not None: self.reporter.ontick() for botname, bot in self.config["bots"].items(): if botname not in self.bots or self.bots[botname].disabled: continue try: self.bots[botname].ontick(data) except Exception as e: self.bots[botname].error_ontick(e) self.bots[botname].log.exception("in .tick()") def on_market(self, data): if data.get("deleted", False): # no info available on deleted orders return for botname, bot in self.config["bots"].items(): if self.bots[botname].disabled: continue if bot["market"] == data.market: try: self.bots[botname].onMarketUpdate(data) except Exception as e: self.bots[botname].error_onMarketUpdate(e) self.bots[botname].log.exception(".onMarketUpdate()") def on_account(self, accountupdate): account = accountupdate.account for botname, bot in self.config["bots"].items(): if self.bots[botname].disabled: self.bots[botname].log.info("bot disabled" % botname) continue if bot["account"] == account["name"]: try: self.bots[botname].onAccount(accountupdate) except Exception as e: self.bots[botname].error_onAccount(e) self.bots[botname].log.exception(".onAccountUpdate()") def run(self): self.init_bots() self.notify.listen() def stop(self, *args): self.notify.websocket.close() def remove_bot(self): for bot in self.bots: self.bots[bot].purge() @staticmethod def remove_offline_bot(config, bot_name): # Initialize the base strategy to get control over the data strategy = BaseStrategy(config, bot_name) strategy.purge() def report_now(self): """Force-generate a report if we can""" if self.reporter is not None: self.reporter.run_report_week() else: log.warn("No reporter available") def do_next_tick(self, job): """Add a callable to be executed on the next tick""" self.jobs.add(job) def reread_config(self): log.warn("reread_config not implemented yet")
class WorkerInfrastructure(threading.Thread): def __init__(self, config, bitshares_instance=None, view=None): super().__init__() # BitShares instance self.bitshares = bitshares_instance or shared_bitshares_instance() self.block_time = None self.config = copy.deepcopy(config) self.view = view self.jobs = set() self.notify = None self.config_lock = threading.RLock() self.workers = {} self.accounts = set() self.markets = set() # Set the module search path user_worker_path = os.path.expanduser("~/bots") if os.path.exists(user_worker_path): sys.path.append(user_worker_path) def init_workers(self, config): """Initialize the workers.""" 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), }, ) 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), }, ) continue try: 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) 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), }, ) self.config_lock.release() def update_notify(self): if not self.config['workers']: log.critical("No workers configured to launch, exiting") raise errors.NoWorkersAvailable() if not self.workers: log.critical("No workers actually running") raise errors.NoWorkersAvailable() if self.notify: # Update the notification instance self.notify.reset_subscriptions(list(self.accounts), list(self.markets)) else: # Initialize the notification instance self.notify = Notify( markets=list(self.markets), accounts=list(self.accounts), on_market=self.on_market, on_account=self.on_account, on_block=self.on_block, bitshares_instance=self.bitshares, ) # Events def on_block(self, data): if self.jobs: try: for job in self.jobs: job() finally: self.jobs = set() self.check_node_time() self.config_lock.acquire() for worker_name, worker in self.config["workers"].items(): if worker_name not in self.workers: continue elif self.workers[worker_name].disabled: self.workers[worker_name].log.error( 'Worker "{}" is disabled'.format(worker_name)) self.workers.pop(worker_name) continue try: self.workers[worker_name].ontick(data) except Exception as e: self.workers[worker_name].log.exception("in ontick()") try: self.workers[worker_name].error_ontick(e) except Exception: self.workers[worker_name].log.exception( "in error_ontick()") finally: self.bitshares.txbuffer.clear() self.config_lock.release() def on_market(self, data): if data.get("deleted", False): # No info available on deleted orders return self.config_lock.acquire() for worker_name, worker in self.config["workers"].items(): if worker_name not in self.workers: continue elif self.workers[worker_name].disabled: self.workers[worker_name].log.error( 'Worker "{}" is disabled'.format(worker_name)) self.workers.pop(worker_name) continue if worker["market"] == data.market: try: self.workers[worker_name].onMarketUpdate(data) except Exception as e: self.workers[worker_name].log.exception( "in onMarketUpdate()") try: self.workers[worker_name].error_onMarketUpdate(e) except Exception: self.workers[worker_name].log.exception( "in error_onMarketUpdate()") finally: self.bitshares.txbuffer.clear() self.config_lock.release() def on_account(self, account_update): self.config_lock.acquire() account = account_update.account for worker_name, worker in self.config["workers"].items(): if worker_name not in self.workers: continue elif self.workers[worker_name].disabled: self.workers[worker_name].log.error( 'Worker "{}" is disabled'.format(worker_name)) self.workers.pop(worker_name) continue if worker["account"] == account["name"]: try: self.workers[worker_name].onAccount(account_update) except Exception as e: self.workers[worker_name].log.exception( "in onAccountUpdate()") try: self.workers[worker_name].error_onAccount(e) except Exception: self.workers[worker_name].log.exception( "in error_onAccountUpdate()") finally: self.bitshares.txbuffer.clear() self.config_lock.release() def add_worker(self, worker_name, config): with self.config_lock: self.config['workers'][worker_name] = config['workers'][ worker_name] self.init_workers(config) self.update_notify() def run(self): self.init_workers(self.config) self.update_notify() self.notify.listen() def check_node_time(self): """Check that we're connected to synced node.""" props = self.bitshares.info() current_time = parse_time(props['time']) if not self.block_time: self.block_time = current_time elif current_time < self.block_time: current_node = self.bitshares.rpc.url self.bitshares.rpc.next() new_node = self.bitshares.rpc.url log.warning('Current node out of sync, switching: {} -> {}'.format( current_node, new_node)) return else: self.block_time = current_time def stop(self, worker_name=None, pause=False): """ Used to stop the worker(s) :param str worker_name: name of the worker to stop :param bool pause: optional argument which tells worker if it was stopped or just paused """ if worker_name: try: # Kill only the specified worker self.remove_market(worker_name) except KeyError: # Worker was not found meaning it does not exist or it is paused already return with self.config_lock: account = self.config['workers'][worker_name]['account'] self.config['workers'].pop(worker_name) # We should remove account subscription only if account is not used by another worker account_is_in_use = False for _, worker in self.workers.items(): if worker.account.name == account: account_is_in_use = True if not account_is_in_use: self.accounts.remove(account) if pause and worker_name in self.workers: self.workers[worker_name].pause() self.workers.pop(worker_name, None) else: # Kill all of the workers if pause: for worker in self.workers: self.workers[worker].pause() self.workers = [] # Update other workers if len(self.workers) > 0: self.update_notify() else: # No workers left, close websocket self.notify.websocket.close() def remove_worker(self, worker_name=None): if worker_name: self.workers[worker_name].purge() else: for worker in self.workers: self.workers[worker].purge() def remove_market(self, worker_name): """Remove the market only if the worker is the only one using it.""" with self.config_lock: market = self.config['workers'][worker_name]['market'] for name, worker in self.config['workers'].items(): if market == worker['market']: break # Found the same market, do nothing else: # No markets found, safe to remove self.markets.remove(market) @staticmethod 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() @staticmethod def remove_offline_worker_data(worker_name): StrategyBase.purge_all_local_worker_data(worker_name) def do_next_tick(self, job): """Add a callable to be executed on the next tick.""" self.jobs.add(job)