def app(config: dict) -> None: """ Main loop which handles the application state :param config: config as dict :return: None """ logger.info('Starting freqtrade %s', __version__) init(config) try: old_state = get_state() logger.info('Initial State: %s', old_state) telegram.send_msg('*Status:* `{}`'.format(old_state.name.lower())) while True: new_state = get_state() # Log state transition if new_state != old_state: telegram.send_msg('*Status:* `{}`'.format( new_state.name.lower())) logging.info('Changing state to: %s', new_state.name) if new_state == State.STOPPED: time.sleep(1) elif new_state == State.RUNNING: _process() # We need to sleep here because otherwise we would run into bittrex rate limit time.sleep(exchange.EXCHANGE.sleep_time) old_state = new_state except RuntimeError: telegram.send_msg('*Status:* Got RuntimeError: ```\n{}\n```'.format( traceback.format_exc())) logger.exception('RuntimeError. Trader stopped!') finally: telegram.send_msg('*Status:* `Trader has stopped`')
def main(): """ Loads and validates the config and handles the main loop :return: None """ logger.info('Starting freqtrade %s', __version__) global _CONF with open('config.json') as file: _CONF = json.load(file) logger.info('Validating configuration ...') validate(_CONF, CONF_SCHEMA) init(_CONF) old_state = get_state() logger.info('Initial State: %s', old_state) telegram.send_msg('*Status:* `{}`'.format(old_state.name.lower())) while True: new_state = get_state() # Log state transition if new_state != old_state: telegram.send_msg('*Status:* `{}`'.format(new_state.name.lower())) logging.info('Changing state to: %s', new_state.name) if new_state == State.STOPPED: time.sleep(1) elif new_state == State.RUNNING: _process() # We need to sleep here because otherwise we would run into bittrex rate limit time.sleep(exchange.get_sleep_time()) old_state = new_state
def _forcesell(bot: Bot, update: Update) -> None: """ Handler for /forcesell <id>. Sells the given trade at current price :param bot: telegram bot :param update: message update :return: None """ if get_state() != State.RUNNING: send_msg('`trader is not running`', bot=bot) return trade_id = update.message.text.replace('/forcesell', '').strip() if trade_id == 'all': # Execute sell for all open orders for trade in Trade.query.filter(Trade.is_open.is_(True)).all(): # Get current rate current_rate = exchange.get_ticker(trade.pair)['bid'] from freqtrade.main import execute_sell execute_sell(trade, current_rate) return # Query for trade trade = Trade.query.filter( and_(Trade.id == trade_id, Trade.is_open.is_(True))).first() if not trade: send_msg('Invalid argument. See `/help` to view usage') logger.warning('/forcesell: Invalid argument received') return # Get current rate current_rate = exchange.get_ticker(trade.pair)['bid'] from freqtrade.main import execute_sell execute_sell(trade, current_rate)
def _performance(bot: Bot, update: Update) -> None: """ Handler for /performance. Shows a performance statistic from finished trades :param bot: telegram bot :param update: message update :return: None """ if get_state() != State.RUNNING: send_msg('`trader is not running`', bot=bot) return pair_rates = Trade.session.query(Trade.pair, func.sum(Trade.close_profit).label('profit_sum'), func.count(Trade.pair).label('count')) \ .filter(Trade.is_open.is_(False)) \ .group_by(Trade.pair) \ .order_by(text('profit_sum DESC')) \ .all() stats = '\n'.join('{index}.\t<code>{pair}\t{profit:.2f}% ({count})</code>'.format( index=i + 1, pair=pair, profit=round(rate * 100, 2), count=count ) for i, (pair, rate, count) in enumerate(pair_rates)) message = '<b>Performance:</b>\n{}'.format(stats) logger.debug(message) send_msg(message, parse_mode=ParseMode.HTML)
def _status(bot: Bot, update: Update) -> None: """ Handler for /status. Returns the current TradeThread status :param bot: telegram bot :param update: message update :return: None """ # Check if additional parameters are passed params = update.message.text.replace('/status', '').split(' ') \ if update.message.text else [] if 'table' in params: _status_table(bot, update) return # Fetch open trade trades = Trade.query.filter(Trade.is_open.is_(True)).all() if get_state() != State.RUNNING: send_msg('*Status:* `trader is not running`', bot=bot) elif not trades: send_msg('*Status:* `no active trade`', bot=bot) else: for trade in trades: order = None if trade.open_order_id: order = exchange.get_order(trade.open_order_id) # calculate profit and send message to user current_rate = exchange.get_ticker(trade.pair, False)['bid'] current_profit = trade.calc_profit_percent(current_rate) fmt_close_profit = '{:.2f}%'.format( round(trade.close_profit * 100, 2) ) if trade.close_profit else None message = """ *Trade ID:* `{trade_id}` *Current Pair:* [{pair}]({market_url}) *Open Since:* `{date}` *Amount:* `{amount}` *Open Rate:* `{open_rate:.8f}` *Close Rate:* `{close_rate}` *Current Rate:* `{current_rate:.8f}` *Close Profit:* `{close_profit}` *Current Profit:* `{current_profit:.2f}%` *Open Order:* `{open_order}` """.format( trade_id=trade.id, pair=trade.pair, market_url=exchange.get_pair_detail_url(trade.pair), date=arrow.get(trade.open_date).humanize(), open_rate=trade.open_rate, close_rate=trade.close_rate, current_rate=current_rate, amount=round(trade.amount, 8), close_profit=fmt_close_profit, current_profit=round(current_profit * 100, 2), open_order='({} rem={:.8f})'.format( order['type'], order['remaining'] ) if order else None, ) send_msg(message, bot=bot)
def _status_table(bot: Bot, update: Update) -> None: """ Handler for /status table. Returns the current TradeThread status in table format :param bot: telegram bot :param update: message update :return: None """ # Fetch open trade trades = Trade.query.filter(Trade.is_open.is_(True)).all() if get_state() != State.RUNNING: send_msg('*Status:* `trader is not running`', bot=bot) elif not trades: send_msg('*Status:* `no active order`', bot=bot) else: trades_list = [] for trade in trades: # calculate profit and send message to user current_rate = exchange.get_ticker(trade.pair)['bid'] trades_list.append([ trade.id, trade.pair, shorten_date( arrow.get(trade.open_date).humanize(only_distance=True)), '{:.2f}'.format(100 * trade.calc_profit(current_rate)) ]) columns = ['ID', 'Pair', 'Since', 'Profit'] df_statuses = DataFrame.from_records(trades_list, columns=columns) df_statuses = df_statuses.set_index(columns[0]) message = tabulate(df_statuses, headers='keys', tablefmt='simple') message = "<pre>{}</pre>".format(message) send_msg(message, parse_mode=ParseMode.HTML)
def _forcesell(bot: Bot, update: Update) -> None: """ Handler for /forcesell <id>. Sells the given trade at current price :param bot: telegram bot :param update: message update :return: None """ if get_state() != State.RUNNING: send_msg('`trader is not running`', bot=bot) return trade_id = update.message.text.replace('/forcesell', '').strip() if trade_id == 'all': # Execute sell for all open orders for trade in Trade.query.filter(Trade.is_open.is_(True)).all(): _exec_forcesell(trade) return # Query for trade trade = Trade.query.filter(and_( Trade.id == trade_id, Trade.is_open.is_(True) )).first() if not trade: send_msg('Invalid argument. See `/help` to view usage') logger.warning('/forcesell: Invalid argument received') return _exec_forcesell(trade)
def _status_table(bot: Bot, update: Update) -> None: """ Handler for /status table. Returns the current TradeThread status in table format :param bot: telegram bot :param update: message update :return: None """ # Fetch open trade trades = Trade.query.filter(Trade.is_open.is_(True)).all() if get_state() != State.RUNNING: send_msg('*Status:* `trader is not running`', bot=bot) elif not trades: send_msg('*Status:* `no active order`', bot=bot) else: trades_list = [] for trade in trades: # calculate profit and send message to user current_rate = exchange.get_ticker(trade.pair, False)['bid'] trades_list.append([ trade.id, trade.pair, shorten_date(arrow.get(trade.open_date).humanize(only_distance=True)), '{:.2f}%'.format(100 * trade.calc_profit_percent(current_rate)) ]) columns = ['ID', 'Pair', 'Since', 'Profit'] df_statuses = DataFrame.from_records(trades_list, columns=columns) df_statuses = df_statuses.set_index(columns[0]) message = tabulate(df_statuses, headers='keys', tablefmt='simple') message = "<pre>{}</pre>".format(message) send_msg(message, parse_mode=ParseMode.HTML)
def test_start_handle(default_conf, update, mocker): mocker.patch.dict('freqtrade.main._CONF', default_conf) msg_mock = MagicMock() mocker.patch.multiple('freqtrade.rpc.telegram', _CONF=default_conf, init=MagicMock(), send_msg=msg_mock) mocker.patch.multiple('freqtrade.main.exchange', _CONF=default_conf, init=MagicMock()) init(default_conf, create_engine('sqlite://')) update_state(State.STOPPED) assert get_state() == State.STOPPED _start(bot=MagicMock(), update=update) assert get_state() == State.RUNNING assert msg_mock.call_count == 0
def _performance(bot: Bot, update: Update) -> None: """ Handler for /performance. Shows a performance statistic from finished trades :param bot: telegram bot :param update: message update :return: None """ if get_state() != State.RUNNING: send_msg('`trader is not running`', bot=bot) return pair_rates = Trade.session.query(Trade.pair, func.sum(Trade.close_profit).label('profit_sum')) \ .filter(Trade.is_open.is_(False)) \ .group_by(Trade.pair) \ .order_by(text('profit_sum DESC')) \ .all() stats = '\n'.join('{index}.\t<code>{pair}\t{profit:.2f}%</code>'.format( index=i + 1, pair=pair, profit=round(rate * 100, 2)) for i, (pair, rate) in enumerate(pair_rates)) message = '<b>Performance:</b>\n{}'.format(stats) logger.debug(message) send_msg(message, parse_mode=ParseMode.HTML)
def _status(bot: Bot, update: Update) -> None: """ Handler for /status. Returns the current TradeThread status :param bot: telegram bot :param update: message update :return: None """ # Check if additional parameters are passed params = update.message.text.replace('/status', '').split(' ') \ if update.message.text else [] if 'table' in params: _status_table(bot, update) return # Fetch open trade trades = Trade.query.filter(Trade.is_open.is_(True)).all() if get_state() != State.RUNNING: send_msg('*Status:* `trader is not running`', bot=bot) elif not trades: send_msg('*Status:* `no active trade`', bot=bot) else: for trade in trades: order = None if trade.open_order_id: order = exchange.get_order(trade.open_order_id) # calculate profit and send message to user current_rate = exchange.get_ticker(trade.pair)['bid'] current_profit = trade.calc_profit(current_rate) fmt_close_profit = '{:.2f}%'.format( round(trade.close_profit * 100, 2) ) if trade.close_profit else None message = """ *Trade ID:* `{trade_id}` *Current Pair:* [{pair}]({market_url}) *Open Since:* `{date}` *Amount:* `{amount}` *Open Rate:* `{open_rate:.8f}` *Close Rate:* `{close_rate}` *Current Rate:* `{current_rate:.8f}` *Close Profit:* `{close_profit}` *Current Profit:* `{current_profit:.2f}%` *Open Order:* `{open_order}` """.format( trade_id=trade.id, pair=trade.pair, market_url=exchange.get_pair_detail_url(trade.pair), date=arrow.get(trade.open_date).humanize(), open_rate=trade.open_rate, close_rate=trade.close_rate, current_rate=current_rate, amount=round(trade.amount, 8), close_profit=fmt_close_profit, current_profit=round(current_profit * 100, 2), open_order='{} ({})'.format( order['remaining'], order['type'] ) if order else None, ) send_msg(message, bot=bot)
def test_process_runtime_error(default_conf, ticker, health, mocker): msg_mock = MagicMock() mocker.patch.dict('freqtrade.main._CONF', default_conf) mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=msg_mock) mocker.patch('freqtrade.main.get_signal', side_effect=lambda s, t: True) mocker.patch.multiple('freqtrade.main.exchange', validate_pairs=MagicMock(), get_ticker=ticker, get_wallet_health=health, buy=MagicMock(side_effect=RuntimeError)) init(default_conf, create_engine('sqlite://')) assert get_state() == State.RUNNING result = _process() assert result is False assert get_state() == State.STOPPED assert 'RuntimeError' in msg_mock.call_args_list[-1][0][0]
def test_stop_handle_already_stopped(default_conf, update, mocker): mocker.patch.dict('freqtrade.main._CONF', default_conf) msg_mock = MagicMock() mocker.patch.multiple('freqtrade.rpc.telegram', _CONF=default_conf, init=MagicMock(), send_msg=msg_mock) mocker.patch.multiple('freqtrade.main.exchange', _CONF=default_conf, init=MagicMock()) init(default_conf, create_engine('sqlite://')) update_state(State.STOPPED) assert get_state() == State.STOPPED _stop(bot=MagicMock(), update=update) assert get_state() == State.STOPPED assert msg_mock.call_count == 1 assert 'already stopped' in msg_mock.call_args_list[0][0][0]
def test_start_handle_already_running(default_conf, update, mocker): mocker.patch.dict('freqtrade.main._CONF', default_conf) msg_mock = MagicMock() mocker.patch.multiple('freqtrade.main.telegram', _CONF=default_conf, init=MagicMock(), send_msg=msg_mock) mocker.patch.multiple('freqtrade.main.exchange', _CONF=default_conf, init=MagicMock()) init(default_conf, create_engine('sqlite://')) update_state(State.RUNNING) assert get_state() == State.RUNNING _start(bot=MagicMock(), update=update) assert get_state() == State.RUNNING assert msg_mock.call_count == 1 assert 'already running' in msg_mock.call_args_list[0][0][0]
def test_stop_handle(conf, update, mocker): mocker.patch.dict('freqtrade.main._CONF', conf) msg_mock = MagicMock() mocker.patch.multiple('freqtrade.main.telegram', _CONF=conf, init=MagicMock(), send_msg=msg_mock) mocker.patch.multiple('freqtrade.main.exchange', _CONF=conf, init=MagicMock()) init(conf, 'sqlite://') update_state(State.RUNNING) assert get_state() == State.RUNNING _stop(bot=MagicBot(), update=update) assert get_state() == State.STOPPED assert msg_mock.call_count == 1 assert 'Stopping trader' in msg_mock.call_args_list[0][0][0]
def _start(bot: Bot, update: Update) -> None: """ Handler for /start. Starts TradeThread :param bot: telegram bot :param update: message update :return: None """ if get_state() == State.RUNNING: send_msg('*Status:* `already running`', bot=bot) else: update_state(State.RUNNING)
def _status(bot: Bot, update: Update) -> None: """ Handler for /status. Returns the current TradeThread status :param bot: telegram bot :param update: message update :return: None """ # Fetch open trade trades = Trade.query.filter(Trade.is_open.is_(True)).all() if get_state() != State.RUNNING: send_msg('*Status:* `trader is not running`', bot=bot) elif not trades: send_msg('*Status:* `no active order`', bot=bot) else: for trade in trades: # calculate profit and send message to user current_rate = exchange.get_ticker(trade.pair)['bid'] current_profit = 100 * ( (current_rate - trade.open_rate) / trade.open_rate) orders = exchange.get_open_orders(trade.pair) orders = [o for o in orders if o['id'] == trade.open_order_id] order = orders[0] if orders else None fmt_close_profit = '{:.2f}%'.format(round( trade.close_profit, 2)) if trade.close_profit else None message = """ *Trade ID:* `{trade_id}` *Current Pair:* [{pair}]({market_url}) *Open Since:* `{date}` *Amount:* `{amount}` *Open Rate:* `{open_rate}` *Close Rate:* `{close_rate}` *Current Rate:* `{current_rate}` *Close Profit:* `{close_profit}` *Current Profit:* `{current_profit:.2f}%` *Open Order:* `{open_order}` """.format( trade_id=trade.id, pair=trade.pair, market_url=exchange.get_pair_detail_url(trade.pair), date=arrow.get(trade.open_date).humanize(), open_rate=trade.open_rate, close_rate=trade.close_rate, current_rate=current_rate, amount=round(trade.amount, 8), close_profit=fmt_close_profit, current_profit=round(current_profit, 2), open_order='{} ({})'.format(order['remaining'], order['type']) if order else None, ) send_msg(message, bot=bot)
def _stop(bot: Bot, update: Update) -> None: """ Handler for /stop. Stops TradeThread :param bot: telegram bot :param update: message update :return: None """ if get_state() == State.RUNNING: send_msg('`Stopping trader ...`', bot=bot) update_state(State.STOPPED) else: send_msg('*Status:* `already stopped`', bot=bot)
def main(): """ Loads and validates the config and handles the main loop :return: None """ global _CONF args = build_arg_parser().parse_args() # Initialize logger logging.basicConfig( level=args.loglevel, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', ) logger.info('Starting freqtrade %s (loglevel=%s)', __version__, logging.getLevelName(args.loglevel)) # Load and validate configuration with open(args.config) as file: _CONF = json.load(file) if 'internals' not in _CONF: _CONF['internals'] = {} logger.info('Validating configuration ...') validate(_CONF, CONF_SCHEMA) # Initialize all modules and start main loop if args.dynamic_whitelist: logger.info( 'Using dynamically generated whitelist. (--dynamic-whitelist detected)' ) init(_CONF) old_state = None while True: new_state = get_state() # Log state transition if new_state != old_state: telegram.send_msg('*Status:* `{}`'.format(new_state.name.lower())) logger.info('Changing state to: %s', new_state.name) if new_state == State.STOPPED: time.sleep(1) elif new_state == State.RUNNING: throttle( _process, min_secs=_CONF['internals'].get('process_throttle_secs', 10), dynamic_whitelist=args.dynamic_whitelist, ) old_state = new_state
def _forcesell(bot: Bot, update: Update) -> None: """ Handler for /forcesell <id>. Sells the given trade at current price :param bot: telegram bot :param update: message update :return: None """ if get_state() != State.RUNNING: send_msg('`trader is not running`', bot=bot) return try: trade_id = int(update.message.text .replace('/forcesell', '') .strip()) # Query for trade trade = Trade.query.filter(and_( Trade.id == trade_id, Trade.is_open.is_(True) )).first() if not trade: send_msg('There is no open trade with ID: `{}`'.format(trade_id)) return # Get current rate current_rate = exchange.get_ticker(trade.pair)['bid'] # Get available balance currency = trade.pair.split('_')[1] balance = exchange.get_balance(currency) # Execute sell profit = trade.exec_sell_order(current_rate, balance) message = '*{}:* Selling [{}]({}) at rate `{:f} (profit: {}%)`'.format( trade.exchange, trade.pair.replace('_', '/'), exchange.get_pair_detail_url(trade.pair), trade.close_rate, round(profit, 2) ) logger.info(message) send_msg(message) except ValueError: send_msg('Invalid argument. Usage: `/forcesell <trade_id>`') logger.warning('/forcesell: Invalid argument received')
def _count(bot: Bot, update: Update) -> None: """ Handler for /count. Returns the number of trades running :param bot: telegram bot :param update: message update :return: None """ if get_state() != State.RUNNING: send_msg('`trader is not running`', bot=bot) return trades = Trade.query.filter(Trade.is_open.is_(True)).all() message = tabulate({ 'current': [len(trades)], 'max': [_CONF['max_open_trades']] }, headers=['current', 'max'], tablefmt='simple') message = "<pre>{}</pre>".format(message) logger.debug(message) send_msg(message, parse_mode=ParseMode.HTML)
def main(sysargv=sys.argv[1:]) -> None: """ Loads and validates the config and handles the main loop :return: None """ global _CONF args = parse_args( sysargv, 'Simple High Frequency Trading Bot for crypto currencies') # A subcommand has been issued if hasattr(args, 'func'): args.func(args) exit(0) # Initialize logger logging.basicConfig( level=args.loglevel, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', ) logger.info('Starting freqtrade %s (loglevel=%s)', __version__, logging.getLevelName(args.loglevel)) # Load and validate configuration _CONF = load_config(args.config) # Initialize all modules and start main loop if args.dynamic_whitelist: logger.info( 'Using dynamically generated whitelist. (--dynamic-whitelist detected)' ) # If the user ask for Dry run with a local DB instead of memory if args.dry_run_db: if _CONF.get('dry_run', False): _CONF.update({'dry_run_db': True}) logger.info( 'Dry_run will use the DB file: "tradesv3.dry_run.sqlite". (--dry_run_db detected)' ) else: logger.info('Dry run is disabled. (--dry_run_db ignored)') try: init(_CONF) old_state = None while True: new_state = get_state() # Log state transition if new_state != old_state: rpc.send_msg('*Status:* `{}`'.format(new_state.name.lower())) logger.info('Changing state to: %s', new_state.name) if new_state == State.STOPPED: time.sleep(1) elif new_state == State.RUNNING: throttle( _process, min_secs=_CONF['internals'].get('process_throttle_secs', 10), nb_assets=args.dynamic_whitelist, ) old_state = new_state except KeyboardInterrupt: logger.info('Got SIGINT, aborting ...') except BaseException: logger.exception('Got fatal exception!') finally: cleanup()
def main(sysargv=sys.argv[1:]) -> None: """ Loads and validates the config and handles the main loop :return: None """ global _CONF args = parse_args(sysargv, 'Simple High Frequency Trading Bot for crypto currencies') # A subcommand has been issued if hasattr(args, 'func'): args.func(args) exit(0) # Initialize logger logging.basicConfig( level=args.loglevel, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', ) logger.info( 'Starting freqtrade %s (loglevel=%s)', __version__, logging.getLevelName(args.loglevel) ) # Load and validate configuration _CONF = load_config(args.config) # Initialize all modules and start main loop if args.dynamic_whitelist: logger.info('Using dynamically generated whitelist. (--dynamic-whitelist detected)') # If the user ask for Dry run with a local DB instead of memory if args.dry_run_db: if _CONF.get('dry_run', False): _CONF.update({'dry_run_db': True}) logger.info( 'Dry_run will use the DB file: "tradesv3.dry_run.sqlite". (--dry_run_db detected)' ) else: logger.info('Dry run is disabled. (--dry_run_db ignored)') try: init(_CONF) old_state = None while True: new_state = get_state() # Log state transition if new_state != old_state: rpc.send_msg('*Status:* `{}`'.format(new_state.name.lower())) logger.info('Changing state to: %s', new_state.name) if new_state == State.STOPPED: time.sleep(1) elif new_state == State.RUNNING: throttle( _process, min_secs=_CONF['internals'].get('process_throttle_secs', 10), nb_assets=args.dynamic_whitelist, ) old_state = new_state except KeyboardInterrupt: logger.info('Got SIGINT, aborting ...') except BaseException: logger.exception('Got fatal exception!') finally: cleanup()
def main(config_suffix=None): """ Loads and validates the config and handles the main loop :return: None """ logger.info('Starting freqtrade %s', __version__) global _CONF with open('config' + config_suffix + '.json') as file: _CONF = json.load(file) logger.info('Validating configuration ...') validate(_CONF, CONF_SCHEMA) init(_CONF) # get trade interval from config for main loop trade_interval = _CONF['trade_interval'] # get analyze_method from config for buy signals analyze_method = _CONF['analyze_method'] global analyzer if analyze_method == 'danml': analyzer = DanML() elif analyze_method == 'cryptoml': analyzer = CrytpoML(whitelist=_CONF['exchange']['pair_whitelist']) else: assert analyzer is not None old_state = get_state() logger.info('Initial State: %s', old_state) telegram.send_msg('*Status:* `{}`'.format(old_state.name.lower())) while True: s = datetime.utcnow().second m = datetime.utcnow().minute h = datetime.utcnow().hour if s == 0 and m % trade_interval == 0: new_state = get_state() # Log state transition if new_state != old_state: telegram.send_msg('*Status:* `{}`'.format( new_state.name.lower())) logging.info('Changing state to: %s', new_state.name) if new_state == State.STOPPED: time.sleep(1) elif new_state == State.RUNNING: _process(buying=True) # We need to sleep here because otherwise we would run into bittrex rate limit # time.sleep(exchange.get_sleep_time()) old_state = new_state if s == 30: new_state = get_state() # Log state transition if new_state != old_state: telegram.send_msg('*Status:* `{}`'.format( new_state.name.lower())) logging.info('Changing state to: %s', new_state.name) if new_state == State.STOPPED: time.sleep(1) elif new_state == State.RUNNING: _process(buying=False) # We need to sleep here because otherwise we would run into bittrex rate limit # time.sleep(exchange.get_sleep_time()) old_state = new_state if h == 0: if analyzer.name == 'cryptoml': analyzer.reset_buffer() else: time.sleep(0.1)