import logging from datetime import datetime from threading import Thread from time import sleep import pytz from websocket import create_connection from core.constants import HTTP_HEADERS from spotproviders.spot_provider import SpotProvider class WebsocketSpotProvider(SpotProvider): """Spot provider using websockets.""" def __init__(self, provider_config, url): super().__init__(provider_config) self._url = url self._ws = None self._thread = None self._stopped = False self._last_event_id = None def start(self): logging.info("Set up websocket connection to " + self.name + " spot API.") self._stopped = False self._thread = Thread(target=self._run) self._thread.daemon = True self._thread.start() def stop(self): self._stopped = True if self._ws: self._ws.close() if self._thread: self._thread.join() def _on_open(self): self.status = "Waiting for Data" def _on_error(self): self.status = "Connecting" def _run(self): while not self._stopped: try: logging.debug("Connecting to " + self.name + " spot API...") self.status = "Connecting" self._ws = create_connection(self._url, header=HTTP_HEADERS) self.status = "Connected" data = self._ws.recv() if data: try: new_spot = self._ws_message_to_spot(data) if new_spot: self._submit(new_spot) self.status = "OK" self.last_update_time = datetime.now(pytz.UTC) logging.debug("Received data from " + self.name + " spot API.") except Exception: logging.exception( "Exception processing message from Websocket Spot Provider (" + self.name + ")") except Exception as e: self.status = "Error" logging.exception("Exception in Websocket Spot Provider (" + self.name + ")", e) else: self.status = "Disconnected" sleep(5) # Wait before trying to reconnect def _ws_message_to_spot(self, b): """Convert a WS message received from the API into a spot. The exact message data (in bytes) is provided here so the subclass implementations can handle the message as string, JSON, XML, whatever the API actually provides.""" raise NotImplementedError("Subclasses must implement this method")