diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 8e518cf1c..a9cc8cd1f 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -92,100 +92,105 @@ class FreqtradeBot(LoggingMixin): exchange_config: ExchangeConfig = deepcopy(config["exchange"]) # Remove credentials from original exchange config to avoid accidental credential exposure remove_exchange_credentials(config["exchange"], True) - - self.exchange = ExchangeResolver.load_exchange( - self.config, exchange_config=exchange_config, load_leverage_tiers=True - ) - - self.strategy: IStrategy = StrategyResolver.load_strategy(self.config) - - # Check config consistency here since strategies can set certain options - validate_config_consistency(config) - # Re-validate exchange compatibility - self.exchange.validate_config(self.config) - - init_db(self.config["db_url"]) - - self.wallets = Wallets(self.config, self.exchange) - - PairLocks.timeframe = self.config["timeframe"] - - self.trading_mode: TradingMode = self.config.get("trading_mode", TradingMode.SPOT) - self.margin_mode: MarginMode = self.config.get("margin_mode", MarginMode.NONE) - self.last_process: datetime | None = None - - # RPC runs in separate threads, can start handling external commands just after - # initialization, even before Freqtradebot has a chance to start its throttling, - # so anything in the Freqtradebot instance should be ready (initialized), including - # the initial state of the bot. - # Keep this at the end of this initialization method. - self.rpc: RPCManager = RPCManager(self) - - self.dataprovider = DataProvider(self.config, self.exchange, rpc=self.rpc) - self.pairlists = PairListManager(self.exchange, self.config, self.dataprovider) - - self.dataprovider.add_pairlisthandler(self.pairlists) - - # Attach Dataprovider to strategy instance - self.strategy.dp = self.dataprovider - # Attach Wallets to strategy instance - self.strategy.wallets = self.wallets - - # Init ExternalMessageConsumer if enabled - self.emc: ExternalMessageConsumer | None = ( - ExternalMessageConsumer(self.config, self.dataprovider) - if self.config.get("external_message_consumer", {}).get("enabled", False) - else None - ) - - logger.info("Starting initial pairlist refresh") - with MeasureTime( - lambda duration, _: logger.info(f"Initial Pairlist refresh took {duration:.2f}s"), 0 - ): - self.active_pair_whitelist = self._refresh_active_whitelist() - - # Set initial bot state from config - initial_state = self.config.get("initial_state") - self.state = State[initial_state.upper()] if initial_state else State.STOPPED - - # Protect exit-logic from forcesell and vice versa - self._exit_lock = Lock() - timeframe_secs = timeframe_to_seconds(self.strategy.timeframe) - self._exit_reason_cache = PeriodicCache(100, ttl=timeframe_secs) - LoggingMixin.__init__(self, logger, timeframe_secs) - - self._schedule = Scheduler() - - if self.trading_mode == TradingMode.FUTURES: - - def update(): - self.update_funding_fees() - self.update_all_liquidation_prices() - self.wallets.update() - - # This would be more efficient if scheduled in utc time, and performed at each - # funding interval, specified by funding_fee_times on the exchange classes - # However, this reduces the precision - and might therefore lead to problems. - for time_slot in range(0, 24): - for minutes in [1, 31]: - t = str(time(time_slot, minutes, 2)) - self._schedule.every().day.at(t).do(update) - - self._schedule.every().day.at("00:02").do(self.exchange.ws_connection_reset) - - self.strategy.ft_bot_start() - # Initialize protections AFTER bot start - otherwise parameters are not loaded. - self.protections = ProtectionManager(self.config, self.strategy.protections) - - def log_took_too_long(duration: float, time_limit: float): - logger.warning( - f"Strategy analysis took {duration:.2f}s, more than 25% of the timeframe " - f"({time_limit:.2f}s). This can lead to delayed orders and missed signals." - "Consider either reducing the amount of work your strategy performs " - "or reduce the amount of pairs in the Pairlist." + try: + self.exchange = ExchangeResolver.load_exchange( + self.config, exchange_config=exchange_config, load_leverage_tiers=True ) - self._measure_execution = MeasureTime(log_took_too_long, timeframe_secs * 0.25) + self.strategy: IStrategy = StrategyResolver.load_strategy(self.config) + + # Check config consistency here since strategies can set certain options + validate_config_consistency(config) + # Re-validate exchange compatibility + self.exchange.validate_config(self.config) + + init_db(self.config["db_url"]) + + self.wallets = Wallets(self.config, self.exchange) + + PairLocks.timeframe = self.config["timeframe"] + + self.trading_mode: TradingMode = self.config.get("trading_mode", TradingMode.SPOT) + self.margin_mode: MarginMode = self.config.get("margin_mode", MarginMode.NONE) + self.last_process: datetime | None = None + + # RPC runs in separate threads, can start handling external commands just after + # initialization, even before Freqtradebot has a chance to start its throttling, + # so anything in the Freqtradebot instance should be ready (initialized), including + # the initial state of the bot. + # Keep this at the end of this initialization method. + self.rpc: RPCManager = RPCManager(self) + + self.dataprovider = DataProvider(self.config, self.exchange, rpc=self.rpc) + self.pairlists = PairListManager(self.exchange, self.config, self.dataprovider) + + self.dataprovider.add_pairlisthandler(self.pairlists) + + # Attach Dataprovider to strategy instance + self.strategy.dp = self.dataprovider + # Attach Wallets to strategy instance + self.strategy.wallets = self.wallets + + # Init ExternalMessageConsumer if enabled + self.emc: ExternalMessageConsumer | None = ( + ExternalMessageConsumer(self.config, self.dataprovider) + if self.config.get("external_message_consumer", {}).get("enabled", False) + else None + ) + + logger.info("Starting initial pairlist refresh") + with MeasureTime( + lambda duration, _: logger.info(f"Initial Pairlist refresh took {duration:.2f}s"), 0 + ): + self.active_pair_whitelist = self._refresh_active_whitelist() + + # Set initial bot state from config + initial_state = self.config.get("initial_state") + self.state = State[initial_state.upper()] if initial_state else State.STOPPED + + # Protect exit-logic from forcesell and vice versa + self._exit_lock = Lock() + timeframe_secs = timeframe_to_seconds(self.strategy.timeframe) + self._exit_reason_cache = PeriodicCache(100, ttl=timeframe_secs) + LoggingMixin.__init__(self, logger, timeframe_secs) + + self._schedule = Scheduler() + + if self.trading_mode == TradingMode.FUTURES: + + def update(): + self.update_funding_fees() + self.update_all_liquidation_prices() + self.wallets.update() + + # This would be more efficient if scheduled in utc time, and performed at each + # funding interval, specified by funding_fee_times on the exchange classes + # However, this reduces the precision - and might therefore lead to problems. + for time_slot in range(0, 24): + for minutes in [1, 31]: + t = str(time(time_slot, minutes, 2)) + self._schedule.every().day.at(t).do(update) + + self._schedule.every().day.at("00:02").do(self.exchange.ws_connection_reset) + + self.strategy.ft_bot_start() + # Initialize protections AFTER bot start - otherwise parameters are not loaded. + self.protections = ProtectionManager(self.config, self.strategy.protections) + + def log_took_too_long(duration: float, time_limit: float): + logger.warning( + f"Strategy analysis took {duration:.2f}s, more than 25% of the timeframe " + f"({time_limit:.2f}s). This can lead to delayed orders and missed signals." + "Consider either reducing the amount of work your strategy performs " + "or reduce the amount of pairs in the Pairlist." + ) + + self._measure_execution = MeasureTime(log_took_too_long, timeframe_secs * 0.25) + + except Exception as e: + # Graceful shutdown in case of failed initialization. + self.cleanup() + raise e from e def notify_status(self, msg: str, msg_type=RPCMessageType.STATUS) -> None: """