fix: Graceful shutdown in case of failed initialization
This commit is contained in:
+97
-92
@@ -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:
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user