def application(argv): fix_thread_set_name() # init terminal displayer Terminal() options = { 'working-path': os.getcwd(), 'identity': 'real', 'config-path': './user/config', 'log-path': './user/log', 'reports-path': './user/reports', 'markets-path': './user/markets', 'log-name': 'siis.log', } # create initial siis data structure if necessary install(options) siis_log = SiisLog(options, Terminal().inst().style()) siis_logger = logging.getLogger('siis') # parse process command line if len(argv) > 1: options['livemode'] = True # utc or local datetime ? for arg in argv: if arg.startswith('--'): if arg == '--paper-mode': # livemode but in paper-mode options['paper-mode'] = True elif arg == '--fetch': # use the fetcher options['fetch'] = True elif arg == '--binarize': # use the binarizer options['binarize'] = True elif arg == '--optimize': # use the optimizer options['optimize'] = True elif arg == '--sync': # use the syncer options['sync'] = True elif arg == '--rebuild': # use the rebuilder options['rebuild'] = True elif arg == '--install-market': options['install-market'] = True elif arg == '--initial-fetch': # do the initial OHLC fetch for watchers options['initial-fetch'] = True elif arg == '--backtest': # backtest mean always paper-mode options['paper-mode'] = True options['backtesting'] = True elif arg.startswith('--timestep='): # backesting timestep, default is 60 second options['timestep'] = float(arg.split('=')[1]) elif arg.startswith('--time-factor='): # backtesting time-factor options['time-factor'] = float(arg.split('=')[1]) elif arg.startswith('--from='): # if backtest from date (if ommited use whoole data) date format is "yyyy-mm-dd-hh:mm:ss", fetch, binarize, optimize to date options['from'] = datetime.strptime( arg.split('=')[1], '%Y-%m-%dT%H:%M:%S').replace(tzinfo=UTC()) elif arg.startswith('--to='): # if backtest to date (can be ommited), fetch, binarize, optimize to date options['to'] = datetime.strptime( arg.split('=')[1], '%Y-%m-%dT%H:%M:%S').replace(tzinfo=UTC()) elif arg.startswith('--last='): # fetch the last n data history options['last'] = int(arg.split('=')[1]) elif arg.startswith('--market='): # fetch, binarize, optimize the data history for this market options['market'] = arg.split('=')[1] elif arg.startswith('--spec='): # fetcher data history option options['option'] = arg.split('=')[1] elif arg.startswith('--broker='): # broker name for fetcher, watcher, optimize, binarize options['broker'] = arg.split('=')[1] elif arg.startswith('--timeframe='): # fetch, binarize, optimize base timeframe options['timeframe'] = arg.split('=')[1] elif arg.startswith('--cascaded='): # fetch cascaded ohlc generation options['cascaded'] = arg.split('=')[1] elif arg == '--watcher-only': # feed only with live data (not compatible with --read-only) options['watcher-only'] = True elif arg == '--read-only': # does not write to the database (not compatible with --watcher-only) options['read-only'] = True elif arg == '--check-data': # check DB ohlc data (@todo) options['check-data'] = True elif arg.startswith('--profile='): # appliances profile name options['profile'] = arg.split('=')[1] elif arg == '--version': Terminal.inst().info('%s %s' % (APP_SHORT_NAME, '.'.join( [str(x) for x in APP_VERSION]))) sys.exit(0) elif arg == '--help' or '-h': display_cli_help() sys.exit(0) else: options['identity'] = argv[1] # watcher-only read-only mutual exclusion if options.get('watcher-only') and options.get('read-only'): Terminal.inst().error( "Options --watcher-only and --read-only are mutually exclusive !" ) sys.exit(-1) # backtesting if options.get('backtesting', False): if options.get('from') is None or options.get('to') is None: del options['backtesting'] Terminal.inst().error( "Backtesting need from= and to= date time") sys.exit(-1) if options['identity'].startswith('-'): Terminal.inst().error("First option must be the identity name") # # binarizer mode # if options.get('binarize'): if options.get('market') and options.get('from') and options.get( 'to') and options.get('broker'): from tools.binarizer import do_binarizer do_binarizer(options) else: display_cli_help() sys.exit(0) # # fetcher mode # if options.get('fetch'): if options.get('market') and options.get('broker'): from tools.fetcher import do_fetcher do_fetcher(options) else: display_cli_help() sys.exit(0) # # optimizer mode # if options.get('optimize'): if options.get('market') and options.get('from') and options.get( 'to') and options.get('broker'): from tools.optimizer import do_optimizer do_optimizer(options) else: display_cli_help() sys.exit(0) # # sync mode # if options.get('sync'): if options.get('market') and options.get('broker'): from tools.syncer import do_syncer do_syncer(options) else: display_cli_help() sys.exit(0) # # rebuilder mode # if options.get('rebuild'): if options.get('market') and options.get('from') and options.get( 'to') and options.get('broker') and options.get('timeframe'): from tools.rebuilder import do_rebuilder do_rebuilder(options) else: display_cli_help() sys.exit(0) # # normal mode # Terminal.inst().info("Starting SIIS using %s identity..." % options['identity']) Terminal.inst().action("- (Press 'q' twice to terminate)") Terminal.inst().action("- (Press 'h' for help)") Terminal.inst().flush() if options.get('backtesting'): Terminal.inst().notice("Process a backtesting.") if options.get('paper-mode'): Terminal.inst().notice("- Using paper-mode trader.") else: Terminal.inst().notice("- Using live-mode trader.") signal.signal(signal.SIGINT, signal_handler) # monitoring service Terminal.inst().info("Starting monitor service...") monitor_service = MonitorService(options) # desktop notifier desktop_service = DesktopNotifier() # discord_service = DiscordNotifier() view_service = ViewService() # database manager Database.create(options) Database.inst().setup(options) # watcher service Terminal.inst().info("Starting watcher's service...") watcher_service = WatcherService(options) watcher_service.start(options) # trader service Terminal.inst().info("Starting trader's service...") trader_service = TraderService(watcher_service, monitor_service, options) trader_service.start(options) # want to display desktop notification and update views watcher_service.add_listener(desktop_service) watcher_service.add_listener(view_service) # want to display desktop notification and update views trader_service.add_listener(desktop_service) trader_service.add_listener(view_service) # trader service listen to watcher service and update views watcher_service.add_listener(trader_service) # strategy service Terminal.inst().info("Starting strategy's service...") strategy_service = StrategyService(watcher_service, trader_service, monitor_service, options) strategy_service.start(options) # strategy service listen to watcher service watcher_service.add_listener(strategy_service) # strategy service listen to trader service trader_service.add_listener(strategy_service) # want to display desktop notification, update view and notify on discord # strategy_service.add_listener(notifier_service) # @todo add notifier service and replace desktop service as desktop notifier into this service same for discord... strategy_service.add_listener(desktop_service) strategy_service.add_listener(view_service) # for display stats (@todo move to views) desktop_service.strategy_service = strategy_service desktop_service.trader_service = trader_service # register terminal commands commands_handler = CommandsHandler() commands_handler.init(options) # cli commands registration register_general_commands(commands_handler) register_trading_commands(commands_handler, trader_service, strategy_service, monitor_service) register_region_commands(commands_handler, strategy_service) setup_views(siis_logger, view_service, watcher_service, trader_service, strategy_service) # setup and start the monitor service monitor_service.setup(watcher_service, trader_service, strategy_service) monitor_service.start() Terminal.inst().message("Running main loop...") Terminal.inst().upgrade() Terminal.inst().message("Steady...", view='notice') display_welcome() LOOP_SLEEP = 0.016 # in second MAX_CMD_ALIVE = 15 # in second running = True value = None value_changed = False command_timeout = 0 prev_timestamp = 0 try: while running: # keyboard input commands try: c = Terminal.inst().read() key = Terminal.inst().key() if c: # split the commande line args = [ arg for arg in (value[1:].split(' ') if value and value.startswith(':') else []) if arg ] if value and value[-1] == ' ': args.append('') # update the current type command commands_handler.process_char(c, args) if key: if key == 'KEY_ESCAPE': # cancel command value = None value_changed = True command_timeout = 0 # use command mode Terminal.inst().set_mode(Terminal.MODE_DEFAULT) # split the commande line args = [ arg for arg in (value[1:].split(' ') if value and value.startswith(':') else []) if arg ] if value and value[-1] == ' ': args.append('') # process on the arguments args = commands_handler.process_key( key, args, Terminal.inst().mode == Terminal.MODE_COMMAND) if args: # regen the updated commande ligne value = ":" + ' '.join(args) value_changed = True command_timeout = 0 desktop_service.on_key_pressed(key) # @todo move the rest to command_handler if c: if value and value[0] == ':': if c == '\b': # backspace, erase last command char value = value[:-1] if value else None value_changed = True command_timeout = time.time() elif c != '\n': # append to the advanced command value value += c value_changed = True command_timeout = time.time() elif c == '\n': result = commands_handler.process_cli(value) command_timeout = 0 if not result: # maybe an application level command if value == ':q' or value == ':quit': running = False elif value.startswith(':x '): # manually exit position at market @todo move as command target = value[2:] if target == "all" or target == "ALL": Terminal.inst().action( "Send close to market command for any positions", view='status') trader_service.command( Trader.COMMAND_CLOSE_ALL_MARKET, {}) else: Terminal.inst().action( "Send close to market command for position %s" % (target, ), view='status') trader_service.command( Trader.COMMAND_CLOSE_MARKET, {'key': target}) elif value.startswith(':d '): # @deprecated manually duplicate a position entry or exit must be associated to social strategy # @todo move as command target = value[2:] Terminal.inst().action( "Send replicate to market command for position %s" % (target, ), view='status') trader_service.command( Trader.COMMAND_TRIGGER, {'key': target}) # clear command value value_changed = True value = None # use default mode Terminal.inst().set_mode(Terminal.MODE_DEFAULT) elif c != '\n': # initial command value value = "" + c value_changed = True command_timeout = time.time() if value and value[0] == ':': # use command mode Terminal.inst().set_mode(Terminal.MODE_COMMAND) if value and value[0] != ':': # direct key # use default mode Terminal.inst().set_mode(Terminal.MODE_DEFAULT) try: result = commands_handler.process_accelerator(key) # @todo convert to Command object accelerator if not result: result = True # @todo might be replaced by views if value == 'p': trader_service.command( Trader.COMMAND_LIST_POSITIONS, {}) elif value == 'o': trader_service.command( Trader.COMMAND_LIST_ORDERS, {}) elif value == 'g': trader_service.command( Trader.COMMAND_SHOW_PERFORMANCE, {}) # display views elif value == 'C': Terminal.inst().clear_content() elif value == 'D': Terminal.inst().switch_view('debug') elif value == 'I': Terminal.inst().switch_view('content') elif value == 'F': Terminal.inst().switch_view('strategy') elif value == 'S': Terminal.inst().switch_view('stats') elif value == 'P': Terminal.inst().switch_view('perf') elif value == 'T': Terminal.inst().switch_view('ticker') elif value == 'A': Terminal.inst().switch_view('account') elif value == 'M': Terminal.inst().switch_view('market') elif value == 'Q': Terminal.inst().switch_view('asset') elif value == 'N': Terminal.inst().switch_view('signal') elif value == '?': # ping services and workers watcher_service.ping() trader_service.ping() strategy_service.ping() monitor_service.ping() elif value == ' ': # a simple mark on the terminal Terminal.inst().notice( "Trading time %s" % (datetime.fromtimestamp( strategy_service.timestamp). strftime('%Y-%m-%d %H:%M:%S')), view='status') elif value == 'a': desktop_service.audible = not desktop_service.audible Terminal.inst().action( "Audible notification are now %s" % ("actives" if desktop_service.audible else "disabled", ), view='status') elif value == 'n': desktop_service.popups = not desktop_service.popups Terminal.inst().action( "Desktop notification are now %s" % ("actives" if desktop_service.popups else "disabled", ), view='status') elif value == 'e': desktop_service.discord = not desktop_service.discord Terminal.inst().action( "Discord notification are now %s" % ("actives" if desktop_service.discord else "disabled", ), view='status') else: result = False if result: value = None value_changed = True command_timeout = 0 except Exception as e: has_exception(siis_logger, e) except IOError: pass except Exception as e: has_exception(siis_logger, e) # display advanced command only if value_changed: if value and value.startswith(':'): Terminal.inst().action("Command: %s" % value[1:], view='command') else: Terminal.inst().message("", view='command') # clear input if no char hit during the last MAX_CMD_ALIVE if value and not value.startswith(':'): if (command_timeout > 0) and (time.time() - command_timeout >= MAX_CMD_ALIVE): value = None value_changed = True Terminal.inst().info("Current typing canceled", view='status') try: # display strategy tarding time (update max once per second) if strategy_service.timestamp - prev_timestamp >= 1.0: mode = "live" if trader_service.backtesting: mode = "backtesting" elif trader_service.paper_mode: mode = "paper-mode" Terminal.inst().message( "%s - %s" % (mode, datetime.fromtimestamp(strategy_service.timestamp). strftime('%Y-%m-%d %H:%M:%S')), view='notice') prev_timestamp = strategy_service.timestamp # synchronous operations here watcher_service.sync() trader_service.sync() strategy_service.sync() monitor_service.sync() desktop_service.sync() view_service.sync() Terminal.inst().update() except BaseException as e: siis_logger.error(traceback.format_exc()) Terminal.inst().error(repr(e)) # don't waste CPU time on main thread time.sleep(LOOP_SLEEP) finally: Terminal.inst().restore_term() Terminal.inst().info("Terminate...") Terminal.inst().flush() commands_handler.terminate(options) commands_handler = None # service terminate monitor_service.terminate() strategy_service.terminate() trader_service.terminate() watcher_service.terminate() desktop_service.terminate() # discord_service.terminate() view_service.terminate() Terminal.inst().info("Saving database...") Terminal.inst().flush() Database.terminate() Terminal.inst().info("Bye!") Terminal.inst().flush() Terminal.terminate()
def application(argv): fix_thread_set_name() # init terminal displayer Terminal() options = {'log-path': './user/log', 'log-name': 'client.log'} # create initial siis data structure if necessary install(options) siis_log = SiisLog(options) logger = logging.getLogger('siis.client') stream = "" rpc = "" fifo = -1 fifo_rpc = -1 if len(sys.argv) > 1: stream = sys.argv[1] if len(sys.argv) > 2: rpc = sys.argv[2] if not stream: Terminal.inst().error("- Missing stream url !") if not rpc: Terminal.inst().error("- Missing RPC url !") try: fifo = os.open(stream, os.O_NONBLOCK | posix.O_RDONLY) except Exception as e: Terminal.inst().error(repr(e)) if not fifo: Terminal.inst().error("- Cannot open the stream !") try: fifo_rpc = os.open(rpc, os.O_NONBLOCK | posix.O_WRONLY) except Exception as e: Terminal.inst().error(repr(e)) if not fifo_rpc: Terminal.inst().error("- Cannot open the RPC fifo !") Terminal.inst().info("Starting SIIS simple chart client...") Terminal.inst().flush() try: Charting.inst().start() except Exception as e: has_exception(e) dispatcher = Dispatcher() running = True Terminal.inst().message("Running main loop...") size = 32768 buf = [] content = "" cur = bytearray() if not Charting.inst().visible: if not Charting.inst().running: # charting service try: Charting.inst().start() except Exception as e: has_exception(e) if Charting.inst().running: Charting.inst().show() Terminal.inst().action("Charting is now shown") while running: # read from fifo try: buf = os.read(fifo, size) if buf: for n in buf: if n == 10: # new line as message termination try: msg = json.loads(cur.decode('utf8')) dispatcher.on_message(msg) except Exception as e: logger.error(repr(e)) cur = bytearray() else: cur.append(n) except (BrokenPipeError, IOError): pass if not Charting.inst().has_charts(): running = False time.sleep(0.01) # close message messages = dispatcher.close() if fifo: os.close(fifo) fifo = -1 if fifo_rpc: for msg in messages: try: # write to fifo posix.write(fifo_rpc, (json.dumps(msg) + '\n').encode('utf8')) except (BrokenPipeError, IOError) as e: logger.error(repr(e)) except (TypeError, ValueError) as e: logger.error("Error sending message : %s" % repr(c)) fifo_rpc.flush() # os.flush(fifo_rpc) os.close(fifo_rpc) fifo_rpc = -1 Terminal.inst().info("Terminate...") Terminal.inst().flush() # terminate charting singleton Charting.terminate() Terminal.inst().info("Bye!") Terminal.inst().flush() Terminal.terminate()
def application(argv): fix_thread_set_name() # init terminal displayer Terminal() options = { 'working-path': os.getcwd(), 'identity': 'real', 'config-path': './user/config', 'log-path': './user/log', 'reports-path': './user/reports', 'markets-path': './user/markets', 'log-name': 'siis.log', } # create initial siis data structure if necessary install(options) siis_log = SiisLog(options, Terminal().inst().style()) siis_logger = logging.getLogger('siis') traceback_logger = logging.getLogger('siis.traceback') # parse process command line if len(argv) > 1: options['livemode'] = True # utc or local datetime ? for arg in argv: if arg.startswith('--'): if arg == '--paper-mode': # livemode but in paper-mode options['paper-mode'] = True elif arg == '--fetch': # use the fetcher options['tool'] = "fetcher" elif arg == '--binarize': # use the binarizer options['tool'] = "binarize" elif arg == '--optimizer': # use the optimizer options['tool'] = "optimizer" elif arg == '--sync': # use the syncer options['tool'] = "syncer" elif arg == '--rebuild': # use the rebuilder options['tool'] = "rebuilder" elif arg == '--export': # use the exporter options['tool'] = "exporter" elif arg == '--import': # use the importer options['tool'] = "importer" elif arg == '--clean': # use the cleaner options['tool'] = "cleaner" elif arg.startswith("--tool="): # use a named tool options['tool'] = arg.split('=')[1] elif arg == '--no-conf': options['no-conf'] = True elif arg == '--zip': options['zip'] = True elif arg == '--install-market': options['install-market'] = True elif arg == '--initial-fetch': # do the initial OHLC fetch for watchers options['initial-fetch'] = True elif arg == '--backtest': # backtest mean always paper-mode options['paper-mode'] = True options['backtesting'] = True elif arg.startswith('--timestep='): # backesting timestep, default is 60 second options['timestep'] = float(arg.split('=')[1]) elif arg.startswith('--time-factor='): # backtesting time-factor options['time-factor'] = float(arg.split('=')[1]) elif arg.startswith('--filename='): # used with import or export options['filename'] = arg.split('=')[1] elif arg.startswith('--from='): # if backtest from date and tools options['from'] = parse_datetime(arg.split('=')[1]) if not options['from']: Terminal.inst().error("Invalid 'from' datetime format") sys.exit(-1) elif arg.startswith('--to='): # if backtest to date and tools options['to'] = parse_datetime(arg.split('=')[1]) if not options['to']: Terminal.inst().error("Invalid 'to' datetime format") sys.exit(-1) elif arg.startswith('--last='): # fetch the last n data history options['last'] = int(arg.split('=')[1]) if options['last'] <= 0: Terminal.inst().error( "Invalid 'last' value. Must be at least 1") sys.exit(-1) elif arg.startswith('--market='): # fetch, binarize, optimize the data history for this market options['market'] = arg.split('=')[1] elif arg.startswith('--spec='): # fetcher data history option options['option'] = arg.split('=')[1] elif arg.startswith('--broker='): # broker name for fetcher, watcher, optimize, binarize options['broker'] = arg.split('=')[1] elif arg.startswith('--timeframe='): # fetch, binarize, optimize base timeframe options['timeframe'] = arg.split('=')[1] elif arg.startswith('--cascaded='): # fetch cascaded ohlc generation options['cascaded'] = arg.split('=')[1] elif arg.startswith('--target='): # target ohlc generation options['target'] = arg.split('=')[1] elif arg == '--watcher-only': # feed only with live data (not compatible with --read-only) options['watcher-only'] = True elif arg == '--read-only': # does not write to the database (not compatible with --watcher-only) options['read-only'] = True elif arg.startswith('--profile='): # appliances profile name options['profile'] = arg.split('=')[1] elif arg == '--version': Terminal.inst().info('%s %s' % (APP_SHORT_NAME, '.'.join( [str(x) for x in APP_VERSION]))) sys.exit(0) elif arg == '--help' or '-h': display_cli_help() sys.exit(0) else: options['identity'] = argv[1] # watcher-only read-only mutual exclusion if options.get('watcher-only') and options.get('read-only'): Terminal.inst().error( "Options --watcher-only and --read-only are mutually exclusive !" ) sys.exit(-1) # backtesting if options.get('backtesting', False): if options.get('from') is None or options.get('to') is None: del options['backtesting'] Terminal.inst().error( "Backtesting need from= and to= date time") sys.exit(-1) # # binarizer mode # if options.get('tool') == "binarizer": if options.get('market') and options.get('from') and options.get( 'to') and options.get('broker'): from tools.binarizer import do_binarizer do_binarizer(options) else: display_cli_help() sys.exit(0) # # fetcher mode # if options.get('tool') == "fetcher": if options.get('market') and options.get('broker'): from tools.fetcher import do_fetcher do_fetcher(options) else: display_cli_help() sys.exit(0) # # optimizer mode # if options.get('tool') == "optimizer": if options.get('market') and options.get('from') and options.get( 'broker'): from tools.optimizer import do_optimizer do_optimizer(options) else: display_cli_help() sys.exit(0) # # rebuilder mode # if options.get('tool') == "rebuilder": if options.get('market') and options.get('from') and options.get( 'broker') and options.get('timeframe'): from tools.rebuilder import do_rebuilder do_rebuilder(options) else: display_cli_help() sys.exit(0) # # exporter mode # if options.get('tool') == "exporter": if options.get('market') and options.get('from') and options.get( 'broker') and options.get('filename'): from tools.exporter import do_exporter do_exporter(options) else: display_cli_help() sys.exit(0) # # importer mode # if options.get('tool') == "importer": if options.get('filename'): from tools.importer import do_importer do_importer(options) else: display_cli_help() sys.exit(0) # # tool mode # if options.get('tool'): ToolClazz = Tool.load_tool(options.get('tool')) if ToolClazz: if ToolClazz.need_identity(): if options['identity'].startswith('-'): Terminal.inst().error( "First option must be the identity name") Terminal.inst().flush() sys.exit(-1) tool = ToolClazz(options) if not tool.check_options(options): display_cli_help() sys.exit(-1) if ToolClazz.need_identity(): Terminal.inst().info( "Starting SIIS %s using %s identity..." % (options.get('tool'), options['identity'])) else: Terminal.inst().info("Starting SIIS %s..." % options.get('tool')) Terminal.inst().flush() tool.execute(options) Terminal.inst().info( "%s done!" % (ToolClazz.alias() or options.get('tool')).capitalize()) Terminal.inst().flush() Terminal.terminate() sys.exit(0) else: sys.exit(-1) if options['identity'].startswith('-'): Terminal.inst().error("First option must be the identity name") # # normal mode # Terminal.inst().info("Starting SIIS using %s identity..." % options['identity']) Terminal.inst().action("- type ':q<Enter> or :quit<Enter>' to terminate") Terminal.inst().action( "- type ':h<Enter> or :help<Enter>' to display help") Terminal.inst().flush() if options.get('backtesting'): Terminal.inst().notice("Process a backtesting.") if options.get('paper-mode'): Terminal.inst().notice("- Using paper-mode trader.") else: Terminal.inst().notice("- Using live-mode trader.") signal.signal(signal.SIGINT, signal_handler) # # application # watchdog_service = WatchdogService(options) watchdog_service.start(options) # application services view_service = None notifier_service = None watcher_service = None trader_service = None strategy_service = None # monitoring service Terminal.inst().info("Starting monitor service...") monitor_service = MonitorService(options) # notifier service try: notifier_service = NotifierService(options) notifier_service.start(options) except Exception as e: Terminal.inst().error(str(e)) terminate(watchdog_service, watcher_service, trader_service, strategy_service, monitor_service, view_service, notifier_service) sys.exit(-1) # view service try: view_service = ViewService(options) watchdog_service.add_service(view_service) except Exception as e: Terminal.inst().error(str(e)) terminate(watchdog_service, watcher_service, trader_service, strategy_service, monitor_service, view_service, notifier_service) sys.exit(-1) # database manager try: Database.create(options) Database.inst().setup(options) except Exception as e: Terminal.inst().error(str(e)) terminate(watchdog_service, watcher_service, trader_service, strategy_service, monitor_service, view_service, notifier_service) sys.exit(-1) # watcher service Terminal.inst().info("Starting watcher's service...") try: watcher_service = WatcherService(options) watcher_service.start(options) watchdog_service.add_service(watcher_service) except Exception as e: Terminal.inst().error(str(e)) terminate(watchdog_service, watcher_service, trader_service, strategy_service, monitor_service, view_service, notifier_service) sys.exit(-1) # trader service Terminal.inst().info("Starting trader's service...") try: trader_service = TraderService(watcher_service, monitor_service, options) trader_service.start(options) watchdog_service.add_service(trader_service) except Exception as e: Terminal.inst().error(str(e)) terminate(watchdog_service, watcher_service, trader_service, strategy_service, monitor_service, view_service, notifier_service) sys.exit(-1) # want to display desktop notification and update views watcher_service.add_listener(view_service) # want to display desktop notification and update views trader_service.add_listener(view_service) # trader service listen to watcher service and update views watcher_service.add_listener(trader_service) # strategy service Terminal.inst().info("Starting strategy's service...") try: strategy_service = StrategyService(watcher_service, trader_service, monitor_service, options) strategy_service.start(options) watchdog_service.add_service(strategy_service) except Exception as e: Terminal.inst().error(str(e)) terminate(watchdog_service, watcher_service, trader_service, strategy_service, monitor_service, view_service, notifier_service) sys.exit(-1) # wan't to be notifier of system errors watchdog_service.add_listener(notifier_service) # strategy service listen to watcher service watcher_service.add_listener(strategy_service) # strategy service listen to trader service trader_service.add_listener(strategy_service) # want to display desktop notification, update view and notify on discord strategy_service.add_listener(notifier_service) strategy_service.add_listener(view_service) # want signal and important notifications notifier_service.set_strategy_service(strategy_service) notifier_service.set_trader_service(trader_service) # register terminal commands commands_handler = CommandsHandler() commands_handler.init(options) # cli commands registration register_general_commands(commands_handler) register_trading_commands(commands_handler, trader_service, strategy_service, monitor_service, notifier_service) register_region_commands(commands_handler, strategy_service) # setup and start the monitor service monitor_service.setup(watcher_service, trader_service, strategy_service) try: monitor_service.start() watchdog_service.add_service(monitor_service) except Exception as e: Terminal.inst().error(str(e)) terminate(watchdog_service, watcher_service, trader_service, strategy_service, monitor_service, view_service, notifier_service) sys.exit(-1) Terminal.inst().message("Running main loop...") Terminal.inst().upgrade() Terminal.inst().message("Steady...", view='notice') if view_service: # setup the default views try: setup_default_views(view_service, watcher_service, trader_service, strategy_service) except Exception as e: Terminal.inst().error(str(e)) terminate(watchdog_service, watcher_service, trader_service, strategy_service, monitor_service, view_service, notifier_service) sys.exit(-1) display_welcome() LOOP_SLEEP = 0.016 # in second MAX_CMD_ALIVE = 15 # in second running = True value = None value_changed = False command_timeout = 0 prev_timestamp = 0 try: while running: # keyboard input commands try: c = Terminal.inst().read() key = Terminal.inst().key() if c: # split the commande line args = [ arg for arg in (value[1:].split(' ') if value and value.startswith(':') else []) if arg ] if value and value[-1] == ' ': args.append('') # update the current type command commands_handler.process_char(c, args) if key: if key == 'KEY_ESCAPE': # cancel command value = None value_changed = True command_timeout = 0 # split the commande line args = [ arg for arg in (value[1:].split(' ') if value and value.startswith(':') else []) if arg ] if value and value[-1] == ' ': args.append('') # process on the arguments args = commands_handler.process_key( key, args, Terminal.inst().mode == Terminal.MODE_COMMAND) if args: # regen the updated commande ligne value = ":" + ' '.join(args) value_changed = True command_timeout = 0 view_service.on_key_pressed(key) if key == 'KEY_ESCAPE': # was in command me, now in default mode Terminal.inst().set_mode(Terminal.MODE_DEFAULT) # @todo move the rest to command_handler if c: if value and value[0] == ':': if c == '\b': # backspace, erase last command char value = value[:-1] if value else None value_changed = True command_timeout = time.time() elif c != '\n': # append to the advanced command value value += c value_changed = True command_timeout = time.time() elif c == '\n': result = commands_handler.process_cli(value) command_timeout = 0 if not result: # maybe an application level command if value == ':q' or value == ':quit': running = False elif value.startswith(':x '): # manually exit position at market # @todo move as command target = value[3:] if target == "all" or target == "ALL": Terminal.inst().action( "Send close to market command for any positions", view='status') trader_service.command( Trader.COMMAND_CLOSE_ALL_MARKET, {}) else: Terminal.inst().action( "Send close to market command for position %s" % (target, ), view='status') trader_service.command( Trader.COMMAND_CLOSE_MARKET, {'key': target}) # clear command value value_changed = True value = None # use default mode Terminal.inst().set_mode(Terminal.MODE_DEFAULT) elif c != '\n': # initial command value value = "" + c value_changed = True command_timeout = time.time() if value and value[0] == ':': # use command mode Terminal.inst().set_mode(Terminal.MODE_COMMAND) if value and value[0] != ':': # direct key # use default mode Terminal.inst().set_mode(Terminal.MODE_DEFAULT) try: result = commands_handler.process_accelerator(key) # @todo convert to Command object accelerator if not result: result = True # display views @todo must be managed by view_service if value == 'C': Terminal.inst().clear_content() elif value == 'D': Terminal.inst().switch_view('debug') elif value == 'I': Terminal.inst().switch_view('content') elif value == 'F': Terminal.inst().switch_view('strategy') elif value == 'S': Terminal.inst().switch_view('stats') elif value == 'P': Terminal.inst().switch_view('perf') elif value == 'X': Terminal.inst().switch_view('position') elif value == 'O': Terminal.inst().switch_view('order') elif value == 'T': Terminal.inst().switch_view('ticker') elif value == 'A': Terminal.inst().switch_view('account') elif value == 'M': Terminal.inst().switch_view('market') elif value == 'Q': Terminal.inst().switch_view('asset') elif value == 'N': Terminal.inst().switch_view('signal') elif value == '?': # ping services and workers watchdog_service.ping(1.0) elif value == ' ': # a simple mark on the terminal Terminal.inst().notice( "Trading time %s" % (datetime.fromtimestamp( strategy_service.timestamp). strftime('%Y-%m-%d %H:%M:%S')), view='status') elif value == 'a': if notifier_service: notifier_service.command( Notifier.COMMAND_TOGGLE, { 'notifier': "desktop", 'value': "audible" }) elif value == 'n': if notifier_service: notifier_service.command( Notifier.COMMAND_TOGGLE, { 'notifier': "desktop", 'value': "popup" }) elif value == '%': if view_service: view_service.toggle_percent() elif value == ',': if view_service: view_service.toggle_group() elif value == '!': if view_service: view_service.toggle_datetime_format() else: result = False if result: value = None value_changed = True command_timeout = 0 except Exception as e: siis_logger.error(repr(e)) traceback_logger.error(traceback.format_exc()) except IOError: pass except Exception as e: siis_logger.error(repr(e)) traceback_logger.error(traceback.format_exc()) # display advanced command only if value_changed: if value and value.startswith(':'): Terminal.inst().message("Command: %s" % value[1:], view='command') else: Terminal.inst().message("", view='command') # clear input if no char hit during the last MAX_CMD_ALIVE if value and not value.startswith(':'): if (command_timeout > 0) and (time.time() - command_timeout >= MAX_CMD_ALIVE): value = None value_changed = True Terminal.inst().info("Current typing canceled", view='status') try: # display strategy tarding time (update max once per second) if strategy_service.timestamp - prev_timestamp >= 1.0: mode = "live" if trader_service.backtesting: mode = "backtesting" elif trader_service.paper_mode: mode = "paper-mode" Terminal.inst().message( "%s - %s" % (mode, datetime.fromtimestamp(strategy_service.timestamp). strftime('%Y-%m-%d %H:%M:%S')), view='notice') prev_timestamp = strategy_service.timestamp # synchronous operations here watcher_service.sync() trader_service.sync() strategy_service.sync() if monitor_service: monitor_service.sync() if view_service: view_service.sync() if notifier_service: notifier_service.sync() Terminal.inst().update() except BaseException as e: siis_logger.error(repr(e)) traceback_logger.error(traceback.format_exc()) # don't waste CPU time on main thread time.sleep(LOOP_SLEEP) finally: Terminal.inst().restore_term() Terminal.inst().info("Terminate...") Terminal.inst().flush() commands_handler.terminate(options) if commands_handler else None commands_handler = None # service terminate monitor_service.terminate() if monitor_service else None strategy_service.terminate() if strategy_service else None trader_service.terminate() if trader_service else None watcher_service.terminate() if watcher_service else None view_service.terminate() if view_service else None notifier_service.terminate() if notifier_service else None Terminal.inst().info("Saving database...") Terminal.inst().flush() Database.terminate() watchdog_service.terminate() if watchdog_service else None Terminal.inst().info("Bye!") Terminal.inst().flush() Terminal.terminate()
def application(argv): fix_thread_set_name() options = { 'identity': 'real', 'config-path': './user/config', 'connectors-path': './user/config/connectors', 'log-path': './user/log', 'reports-path': './user/reports', 'markets-path': './user/markets', 'log-name': 'connector.log', 'default': "connector.json", 'connector-config': "", 'database': { 'name': "siis", 'type': "pgsql", 'host': "127.0.0.1", 'port': 5432, 'user': "******", 'password': "******" }, 'cache': { 'name': "siis", 'type': "redis", 'host': "127.0.0.1", 'port': 6379, 'user': "******", 'password': "******" }, 'strategy': { 'protocol': "tcp", 'host': "127.0.0.1", 'port': 5600 }, 'connector': { 'name': "", 'host': "" }, 'markets': {}, 'connectors': { 'binance.com': { 'classpath': 'connectors.binance.connector.BinanceConnector', }, 'bitmex.com': { 'classpath': 'connectors.bitmex.connector.BitMexConnector', }, 'ig.com': { 'classpath': 'connectors.ig.connector.IGConnector', }, } } # create initial siis data structure if necessary install(options) siis_log = SiisLog(options, "uterm") siis_logger = logging.getLogger('siis.connector') if len(argv) > 1: # utc or local datetime ? for n, arg in enumerate(argv): if arg.startswith('-'): if (arg == '--fetch' or arg == '-F'): # use the fetcher options['fetch'] = True elif (arg == '--spec' or arg == '-S') and n + 1 < len(argv): # fetcher data history option options['option'] = argv[n + 1] elif arg == '--binarize': # use the binarizer options['binarize'] = True elif arg == '--sync': # use the syncer options['sync'] = True elif (arg == '--connector' or arg == '-c') and n + 1 < len(argv): # connector conf filename options['connector-config'] = argv[n + 1] elif (arg == '--from' or arg == '-f') and n + 1 < len(argv): # if backtest from date (if ommited use whoole data) date format is "yyyy-mm-dd-hh:mm:ss", fetch, binarize to date options['from'] = datetime.datetime.strptime( argv[n + 1], '%Y-%m-%dT%H:%M:%S').replace(tzinfo=UTC()) elif (arg == '--to' or arg == '-t') and n + 1 < len(argv): # if backtest to date (can be ommited), fetch, binarize to date options['to'] = datetime.datetime.strptime( argv[n + 1], '%Y-%m-%dT%H:%M:%S').replace(tzinfo=UTC()) elif (arg == '--last' or arg == '-l') and n + 1 < len(argv): # fetch the last n data history options['last'] = int(argv[n + 1]) elif (arg == '--market' or arg == '-m') and n + 1 < len(argv): # fetch, binarize the data history for this market options['market'] = argv[n + 1] elif (arg == '--timeframe' or arg == '-s') and n + 1 < len(argv): # fetch, binarize base timeframe options['timeframe'] = argv[n + 1] elif (arg == '--cascaded=' or arg == '-C') and n + 1 < len(argv): # fetch cascaded ohlc generation options['cascaded'] = argv[n + 1] elif arg == '--read-only': options['read-only'] = True elif arg == '--check-data': options['check-data'] = True elif arg == '--version' or arg == '-v': print('%s %s' % (APP_SHORT_NAME, '.'.join( [str(x) for x in APP_VERSION]))) sys.exit(0) elif arg == '--help' or arg == '-h': display_cmd_line_help() sys.exit(0) # replay if options.get('replay', False): if options.get('from') is None or options.get('to') is None: del options['replay'] print("Replay need from= and to= date time") sys.exit(-1) if not options['connector-config']: print("Connector configuration filename must be specified") sys.exit(-1) # # config # parse_config(siis_logger, options) parse_connector_spec(siis_logger, options) siis_log.upgrade(options) # # binarizer # if options.get('binarize'): if options.get('market') and options.get('from') and options.get( 'to') and options.get('connector'): do_binarizer(options, siis_logger) else: display_cmd_line_help() sys.exit(0) # # fetcher mode # if options.get('fetch'): if options.get('market') and options.get('connector') and options.get( 'timeframe'): do_fetcher(options, siis_logger) else: display_cmd_line_help() sys.exit(0) # # running mode # print("Starting SIIS simple runner using %s..." % options['connector-config']) print("Hit CTRL-C twice to terminate") if options.get('replay'): print("Process a replay.") # monitoring service # print("Starting monitor service...") # monitor_service = MonitorService(options) # monitor_service.start() # database manager Database.create(options) Database.inst().setup(options) print("Starting connector handler...") handler = DefaultHandler(options) handler.init(options) handler.start() run(siis_logger, handler) print("Terminate...") handler.stop() handler.terminate() handler = None print("Saving database...") Database.terminate() print("Bye!")