async def create_exchanges(self): if not self.is_simulated: # create REST based on ccxt exchange self.exchange = RESTExchange(self.config, self.exchange_type, self) await self.exchange.initialize() self._load_constants() # create Websocket exchange if possible if not self.rest_only and self.check_web_socket_config(self.exchange.get_name()): for socket_manager in AbstractWebSocket.__subclasses__(): # add websocket exchange if available if socket_manager.get_name() == self.exchange.get_name(): self.exchange_web_socket = socket_manager.get_websocket_client(self.config, self) # init websocket self.exchange_web_socket.init_web_sockets(self.get_config_time_frame(), self.get_traded_pairs()) # start the websocket self.exchange_web_socket.start_sockets() # if simulated : create exchange simulator instance else: self.exchange = ExchangeSimulator(self.config, self.exchange_type, self) self.exchange_dispatcher = ExchangeDispatcher(self.config, self.exchange_type, self.exchange, self.exchange_web_socket) self.is_ready = True
class ExchangeManager(Initializable): WEB_SOCKET_RESET_MIN_INTERVAL = 15 def __init__(self, config, exchange_type, is_simulated=False, rest_only=False, ignore_config=False): super().__init__() self.config = config self.exchange_type = exchange_type self.rest_only = rest_only self.ignore_config = ignore_config self.logger = get_logger(self.__class__.__name__) self.is_ready = False self.is_simulated = is_simulated self.exchange = None self.exchange_web_socket = None self.exchange_dispatcher = None self.trader = None self.last_web_socket_reset = None self.client_symbols = [] self.client_time_frames = {} self.traded_pairs = [] self.time_frames = [] async def initialize_impl(self): await self.create_exchanges() def register_trader(self, trader): self.trader = trader def get_trader(self): return self.trader def _load_constants(self): self._load_config_symbols_and_time_frames() self._set_config_time_frame() self._set_config_traded_pairs() def websocket_available(self): return self.exchange_web_socket def need_user_stream(self): return self.config[CONFIG_TRADER][CONFIG_ENABLED_OPTION] async def create_exchanges(self): if not self.is_simulated: # create REST based on ccxt exchange self.exchange = RESTExchange(self.config, self.exchange_type, self) await self.exchange.initialize() self._load_constants() # create Websocket exchange if possible if not self.rest_only and self.check_web_socket_config(self.exchange.get_name()): for socket_manager in AbstractWebSocket.__subclasses__(): # add websocket exchange if available if socket_manager.get_name() == self.exchange.get_name(): self.exchange_web_socket = socket_manager.get_websocket_client(self.config, self) # init websocket self.exchange_web_socket.init_web_sockets(self.get_config_time_frame(), self.get_traded_pairs()) # start the websocket self.exchange_web_socket.start_sockets() # if simulated : create exchange simulator instance else: self.exchange = ExchangeSimulator(self.config, self.exchange_type, self) self.exchange_dispatcher = ExchangeDispatcher(self.config, self.exchange_type, self.exchange, self.exchange_web_socket) self.is_ready = True def did_not_just_try_to_reset_web_socket(self): if self.last_web_socket_reset is None: return True else: return time.time() - self.last_web_socket_reset > self.WEB_SOCKET_RESET_MIN_INTERVAL def reset_websocket_exchange(self): if self.exchange_web_socket and self.did_not_just_try_to_reset_web_socket(): # set web socket reset time self.last_web_socket_reset = time.time() # clear databases self.exchange_dispatcher.reset_symbols_data() self.exchange_dispatcher.reset_exchange_personal_data() # close and restart websockets self.exchange_web_socket.close_and_restart_sockets() # databases will be filled at the next calls similarly to bot startup process # should be used only in specific case def get_ccxt_exchange(self): return self.exchange def get_exchange(self): return self.exchange_dispatcher # Exchange configuration functions def check_config(self, exchange_name): if CONFIG_EXCHANGE_KEY not in self.config[CONFIG_EXCHANGES][exchange_name] \ or CONFIG_EXCHANGE_SECRET not in self.config[CONFIG_EXCHANGES][exchange_name]: return False else: return True def force_disable_web_socket(self, exchange_name): return CONFIG_EXCHANGE_WEB_SOCKET in self.config[CONFIG_EXCHANGES][exchange_name] \ and not self.config[CONFIG_EXCHANGES][exchange_name][CONFIG_EXCHANGE_WEB_SOCKET] def check_web_socket_config(self, exchange_name): return self.check_config(exchange_name) and not self.force_disable_web_socket(exchange_name) def enabled(self): # if we can get candlestick data if self.is_simulated or self.exchange.get_name() in self.config[CONFIG_EXCHANGES]: return True else: self.logger.warning(f"Exchange {self.exchange.get_name()} is currently disabled") return False def get_exchange_symbol_id(self, symbol, with_fixer=False): return self.exchange.get_market_status(symbol, with_fixer=with_fixer)["id"] def _set_config_time_frame(self): for time_frame in TimeFrameManager.get_config_time_frame(self.config): if self.time_frame_exists(time_frame.value): self.time_frames.append(time_frame) # add shortest timeframe for realtime evaluators client_shortest_time_frame = TimeFrameManager.find_min_time_frame( self.client_time_frames[CONFIG_WILDCARD], MIN_EVAL_TIME_FRAME) if client_shortest_time_frame not in self.time_frames: self.time_frames.append(client_shortest_time_frame) def get_config_time_frame(self): return self.time_frames def _set_config_traded_pairs(self): for cryptocurrency in self.config[CONFIG_CRYPTO_CURRENCIES]: for symbol in self.config[CONFIG_CRYPTO_CURRENCIES][cryptocurrency][CONFIG_CRYPTO_PAIRS]: if self.symbol_exists(symbol): self.traded_pairs.append(symbol) def get_traded_pairs(self): return self.traded_pairs def _load_config_symbols_and_time_frames(self): client = self.exchange.get_client() if client: self.client_symbols = client.symbols self.client_time_frames[CONFIG_WILDCARD] = client.timeframes if hasattr(client, "timeframes") else {} else: self.logger.error("Failed to load client from REST exchange") self._raise_exchange_load_error() def symbol_exists(self, symbol): if self.client_symbols is None: self.logger.error(f"Failed to load available symbols from REST exchange, impossible to check if " f"{symbol} exists on {self.exchange.get_name()}") return False return symbol in self.client_symbols def time_frame_exists(self, time_frame, symbol=None): if CONFIG_WILDCARD in self.client_time_frames or symbol is None: return time_frame in self.client_time_frames[CONFIG_WILDCARD] else: # should only happen in backtesting (or with an exchange with different timeframes per symbol) return time_frame in self.client_time_frames[symbol] def get_client_symbols(self): return self.client_symbols def get_client_timeframes(self): return self.client_time_frames def get_rate_limit(self): return self.exchange_type.rateLimit / 1000 # Getters def get_is_simulated(self): return self.is_simulated def get_symbol_data(self, symbol): return self.get_exchange().get_symbol_data(symbol) def get_symbol_available_time_frames(self, symbol): return self.get_exchange().get_symbol_data(symbol).get_available_time_frames() def get_personal_data(self): return self.get_exchange().get_exchange_personal_data() @staticmethod def need_to_uniformize_timestamp(timestamp): return not is_valid_timestamp(timestamp) def uniformize_candles_if_necessary(self, candle_or_candles): if candle_or_candles: if isinstance(candle_or_candles[0], list): if self.need_to_uniformize_timestamp(candle_or_candles[0][PriceIndexes.IND_PRICE_TIME.value]): self._uniformize_candles_timestamps(candle_or_candles) else: if self.need_to_uniformize_timestamp(candle_or_candles[PriceIndexes.IND_PRICE_TIME.value]): self._uniformize_candle_timestamps(candle_or_candles) def _uniformize_candles_timestamps(self, candles): for candle in candles: self._uniformize_candle_timestamps(candle) def _uniformize_candle_timestamps(self, candle): candle[PriceIndexes.IND_PRICE_TIME.value] = \ self.exchange_dispatcher.get_uniform_timestamp(candle[PriceIndexes.IND_PRICE_TIME.value]) # Exceptions def _raise_exchange_load_error(self): raise Exception(f"{self.exchange} - Failed to load exchange instances") def get_exchange_name(self): return self.exchange_type.__name__ def should_decrypt_token(self, logger): if ConfigManager.has_invalid_default_config_value( self.config[CONFIG_EXCHANGES][self.get_exchange_name()][CONFIG_EXCHANGE_KEY], self.config[CONFIG_EXCHANGES][self.get_exchange_name()][CONFIG_EXCHANGE_SECRET]): logger.warning("Exchange configuration tokens are not set yet, to use OctoBot's real trader's features, " "please enter your api tokens in exchange configuration") return False return True @staticmethod def handle_token_error(error, logger): logger.error(f"Exchange configuration tokens are invalid : please check your configuration ! " f"({error.__class__.__name__})")
class ExchangeManager: def __init__(self, config, exchange_type, is_simulated=False): self.config = config self.exchange_type = exchange_type self.logger = logging.getLogger(self.__class__.__name__) self.is_ready = False self.is_simulated = is_simulated self.exchange = None self.exchange_web_socket = None self.exchange_dispatcher = None self.client_symbols = [] self.client_time_frames = [] self.traded_pairs = [] self.time_frames = [] self.create_exchanges() def _load_constants(self): self._load_config_symbols_and_time_frames() self._set_config_time_frame() self._set_config_traded_pairs() def websocket_available(self): return self.exchange_web_socket def create_exchanges(self): if not self.is_simulated: # create REST based on ccxt exchange self.exchange = RESTExchange(self.config, self.exchange_type, self) self._load_constants() # create Websocket exchange if possible if self.check_web_socket_config(self.exchange.get_name()): for socket_manager in AbstractWebSocket.__subclasses__(): if socket_manager.get_name() == self.exchange.get_name(): self.exchange_web_socket = socket_manager.get_websocket_client(self.config, self) # init websocket self.exchange_web_socket.init_web_sockets(self.get_config_time_frame(), self.get_traded_pairs()) # start the websocket self.exchange_web_socket.start_sockets() # if simulated : create exchange simulator instance else: self.exchange = ExchangeSimulator(self.config, self.exchange_type, self) self.exchange_dispatcher = ExchangeDispatcher(self.config, self.exchange_type, self.exchange, self.exchange_web_socket) self.is_ready = True # should be used only in specific case def get_ccxt_exchange(self): return self.exchange def get_exchange(self): return self.exchange_dispatcher # Exchange configuration functions def check_config(self, exchange_name): if CONFIG_EXCHANGE_KEY not in self.config[CONFIG_EXCHANGES][exchange_name] \ or CONFIG_EXCHANGE_SECRET not in self.config[CONFIG_EXCHANGES][exchange_name]: return False else: return True def check_web_socket_config(self, exchange_name): return self.check_config(exchange_name) \ and CONFIG_EXCHANGE_WEB_SOCKET in self.config[CONFIG_EXCHANGES][exchange_name] \ and self.config[CONFIG_EXCHANGES][exchange_name][CONFIG_EXCHANGE_WEB_SOCKET] def enabled(self): # if we can get candlestick data if self.is_simulated or self.exchange.get_name() in self.config[CONFIG_EXCHANGES]: return True else: self.logger.warning("Exchange {0} is currently disabled".format(self.exchange.get_name())) return False def _set_config_time_frame(self): for time_frame in TimeFrameManager.get_config_time_frame(self.config): if self.time_frame_exists(time_frame.value): self.time_frames.append(time_frame) # add shortest timeframe for realtime evaluators client_shortest_time_frame = TimeFrameManager.find_min_time_frame(self.client_time_frames, MIN_EVAL_TIME_FRAME) if client_shortest_time_frame not in self.time_frames: self.time_frames.append(client_shortest_time_frame) def get_config_time_frame(self): return self.time_frames def _set_config_traded_pairs(self): for cryptocurrency in self.config[CONFIG_CRYPTO_CURRENCIES]: for symbol in self.config[CONFIG_CRYPTO_CURRENCIES][cryptocurrency][CONFIG_CRYPTO_PAIRS]: if self.symbol_exists(symbol): self.traded_pairs.append(symbol) def get_traded_pairs(self): return self.traded_pairs def _load_config_symbols_and_time_frames(self): client = self.exchange.get_client() if client: self.client_symbols = client.symbols self.client_time_frames = client.timeframes else: self.logger.error("Failed to load client from REST exchange") self._raise_exchange_load_error() def symbol_exists(self, symbol): return symbol in self.client_symbols def time_frame_exists(self, time_frame): return time_frame in self.client_time_frames def get_client_symbols(self): return self.client_symbols def get_client_timeframes(self): return self.client_time_frames def get_rate_limit(self): return self.exchange_type.rateLimit / 1000 # Getters def get_is_simulated(self): return self.is_simulated def get_symbol_data(self, symbol): return self.get_exchange().get_symbol_data(symbol) def get_personal_data(self): return self.get_exchange().get_exchange_personal_data() # Exceptions def _raise_exchange_load_error(self): raise Exception("{0} - Failed to load exchange instances".format(self.exchange))