async def get_new_order_book(self, trading_pair: str) -> OrderBook:
     async with aiohttp.ClientSession() as client:
         snapshot: Dict[str, any] = await self.get_snapshot(
             client, trading_pair)
         snapshot_timestamp: float = time.time()
         snapshot_msg: OrderBookMessage = CoinbaseProOrderBook.snapshot_message_from_exchange(
             snapshot,
             snapshot_timestamp,
             metadata={"trading_pair": trading_pair})
         active_order_tracker: CoinbaseProActiveOrderTracker = CoinbaseProActiveOrderTracker(
         )
         bids, asks = active_order_tracker.convert_snapshot_message_to_order_book_row(
             snapshot_msg)
         order_book = self.order_book_create_function()
         order_book.apply_snapshot(bids, asks, snapshot_msg.update_id)
         return order_book
Esempio n. 2
0
 async def listen_for_order_book_diffs(self, ev_loop: asyncio.AbstractEventLoop, output: asyncio.Queue):
     """
     *required
     Subscribe to diff channel via web socket, and keep the connection open for incoming messages
     :param ev_loop: ev_loop to execute this function in
     :param output: an async queue where the incoming messages are stored
     """
     while True:
         try:
             trading_pairs: List[str] = self._trading_pairs
             ws_assistant = await self._web_assistants_factory.get_ws_assistant()
             await ws_assistant.connect(CONSTANTS.WS_URL, message_timeout=CONSTANTS.WS_MESSAGE_TIMEOUT)
             subscribe_payload = {
                 "type": "subscribe",
                 "product_ids": trading_pairs,
                 "channels": [CONSTANTS.FULL_CHANNEL_NAME]
             }
             subscribe_request = WSRequest(payload=subscribe_payload)
             await ws_assistant.subscribe(subscribe_request)
             async for msg in self._iter_messages(ws_assistant):
                 msg_type: str = msg.get("type", None)
                 if msg_type is None:
                     raise ValueError(f"Coinbase Pro Websocket message does not contain a type - {msg}")
                 elif msg_type == "error":
                     raise ValueError(f"Coinbase Pro Websocket received error message - {msg['message']}")
                 elif msg_type in ["open", "match", "change", "done"]:
                     if msg_type == "done" and "price" not in msg:
                         # done messages with no price are completed market orders which can be ignored
                         continue
                     order_book_message: OrderBookMessage = CoinbaseProOrderBook.diff_message_from_exchange(msg)
                     output.put_nowait(order_book_message)
                 elif msg_type in ["received", "activate", "subscriptions"]:
                     # these messages are not needed to track the order book
                     continue
                 else:
                     raise ValueError(f"Unrecognized Coinbase Pro Websocket message received - {msg}")
         except asyncio.CancelledError:
             raise
         except Exception:
             self.logger().network(
                 "Unexpected error with WebSocket connection.",
                 exc_info=True,
                 app_warning_msg=f"Unexpected error with WebSocket connection."
                                 f" Retrying in {CONSTANTS.REST_API_LIMIT_COOLDOWN} seconds."
                                 f" Check network connection."
             )
             await self._sleep(CONSTANTS.WS_RECONNECT_COOLDOWN)
 async def listen_for_order_book_diffs(self, ev_loop: asyncio.BaseEventLoop, output: asyncio.Queue):
     """
     *required
     Subscribe to diff channel via web socket, and keep the connection open for incoming messages
     :param ev_loop: ev_loop to execute this function in
     :param output: an async queue where the incoming messages are stored
     """
     while True:
         try:
             trading_pairs: List[str] = self._trading_pairs
             async with websockets.connect(COINBASE_WS_FEED) as ws:
                 ws: websockets.WebSocketClientProtocol = ws
                 subscribe_request: Dict[str, Any] = {
                     "type": "subscribe",
                     "product_ids": trading_pairs,
                     "channels": ["full"]
                 }
                 await ws.send(ujson.dumps(subscribe_request))
                 async for raw_msg in self._inner_messages(ws):
                     msg = ujson.loads(raw_msg)
                     msg_type: str = msg.get("type", None)
                     if msg_type is None:
                         raise ValueError(f"Coinbase Pro Websocket message does not contain a type - {msg}")
                     elif msg_type == "error":
                         raise ValueError(f"Coinbase Pro Websocket received error message - {msg['message']}")
                     elif msg_type in ["open", "match", "change", "done"]:
                         if msg_type == "done" and "price" not in msg:
                             # done messages with no price are completed market orders which can be ignored
                             continue
                         order_book_message: OrderBookMessage = CoinbaseProOrderBook.diff_message_from_exchange(msg)
                         output.put_nowait(order_book_message)
                     elif msg_type in ["received", "activate", "subscriptions"]:
                         # these messages are not needed to track the order book
                         continue
                     else:
                         raise ValueError(f"Unrecognized Coinbase Pro Websocket message received - {msg}")
         except asyncio.CancelledError:
             raise
         except Exception:
             self.logger().network(
                 f'{"Unexpected error with WebSocket connection."}',
                 exc_info=True,
                 app_warning_msg=f'{"Unexpected error with WebSocket connection. Retrying in 30 seconds. "}'
                                 f'{"Check network connection."}'
             )
             await asyncio.sleep(30.0)
    async def get_tracking_pairs(self) -> Dict[str, OrderBookTrackerEntry]:
        """
        *required
        Initializes order books and order book trackers for the list of trading pairs
        returned by `self.get_trading_pairs`
        :returns: A dictionary of order book trackers for each trading pair
        """
        # Get the currently active markets
        async with aiohttp.ClientSession() as client:
            trading_pairs: List[str] = self._trading_pairs
            retval: Dict[str, OrderBookTrackerEntry] = {}

            number_of_pairs: int = len(trading_pairs)
            for index, trading_pair in enumerate(trading_pairs):
                try:
                    snapshot: Dict[str, any] = await self.get_snapshot(client, trading_pair)
                    snapshot_timestamp: float = time.time()
                    snapshot_msg: OrderBookMessage = CoinbaseProOrderBook.snapshot_message_from_exchange(
                        snapshot,
                        snapshot_timestamp,
                        metadata={"trading_pair": trading_pair}
                    )
                    order_book: OrderBook = self.order_book_create_function()
                    active_order_tracker: CoinbaseProActiveOrderTracker = CoinbaseProActiveOrderTracker()
                    bids, asks = active_order_tracker.convert_snapshot_message_to_order_book_row(snapshot_msg)
                    order_book.apply_snapshot(bids, asks, snapshot_msg.update_id)

                    retval[trading_pair] = CoinbaseProOrderBookTrackerEntry(
                        trading_pair,
                        snapshot_timestamp,
                        order_book,
                        active_order_tracker
                    )
                    self.logger().info(f"Initialized order book for {trading_pair}. "
                                       f"{index+1}/{number_of_pairs} completed.")
                    await asyncio.sleep(0.6)
                except IOError:
                    self.logger().network(
                        f"Error getting snapshot for {trading_pair}.",
                        exc_info=True,
                        app_warning_msg=f"Error getting snapshot for {trading_pair}. Check network connection."
                    )
                except Exception:
                    self.logger().error(f"Error initializing order book for {trading_pair}. ", exc_info=True)
            return retval
Esempio n. 5
0
 async def listen_for_order_book_snapshots(self, ev_loop: asyncio.BaseEventLoop, output: asyncio.Queue):
     """
     *required
     Fetches order book snapshots for each trading pair, and use them to update the local order book
     :param ev_loop: ev_loop to execute this function in
     :param output: an async queue where the incoming messages are stored
     """
     while True:
         try:
             trading_pairs: List[str] = self._trading_pairs
             rest_assistant = await self._get_rest_assistant()
             for trading_pair in trading_pairs:
                 try:
                     snapshot: Dict[str, any] = await self.get_snapshot(rest_assistant, trading_pair)
                     snapshot_timestamp: float = time.time()
                     snapshot_msg: OrderBookMessage = CoinbaseProOrderBook.snapshot_message_from_exchange(
                         snapshot,
                         snapshot_timestamp,
                         metadata={"product_id": trading_pair}
                     )
                     output.put_nowait(snapshot_msg)
                     self.logger().debug(f"Saved order book snapshot for {trading_pair}")
                     # Be careful not to go above API rate limits.
                     await self._sleep(CONSTANTS.REST_API_LIMIT_COOLDOWN)
                 except asyncio.CancelledError:
                     raise
                 except Exception:
                     self.logger().network(
                         "Unexpected error with WebSocket connection.",
                         exc_info=True,
                         app_warning_msg=f"Unexpected error with WebSocket connection."
                                         f" Retrying in {CONSTANTS.REST_API_LIMIT_COOLDOWN} seconds."
                                         f" Check network connection."
                     )
                     await self._sleep(CONSTANTS.REST_API_LIMIT_COOLDOWN)
             this_hour: pd.Timestamp = pd.Timestamp.utcnow().replace(minute=0, second=0, microsecond=0)
             next_hour: pd.Timestamp = this_hour + pd.Timedelta(hours=1)
             delta: float = next_hour.timestamp() - time.time()
             await self._sleep(delta)
         except asyncio.CancelledError:
             raise
         except Exception:
             self.logger().error("Unexpected error.", exc_info=True)
             await self._sleep(CONSTANTS.REST_API_LIMIT_COOLDOWN)
Esempio n. 6
0
    def test_diff_msg_get_added_to_order_book(self):
        test_active_order_tracker = self.order_book_tracker._active_order_trackers[
            "BTC-USD"]

        price = "200"
        order_id = "test_order_id"
        product_id = "BTC-USD"
        remaining_size = "1.00"

        # Test open message diff
        raw_open_message = {
            "type": "open",
            "time": datetime.now().isoformat(),
            "product_id": product_id,
            "sequence": 20000000000,
            "order_id": order_id,
            "price": price,
            "remaining_size": remaining_size,
            "side": "buy"
        }
        open_message = CoinbaseProOrderBook.diff_message_from_exchange(
            raw_open_message)
        self.order_book_tracker._order_book_diff_stream.put_nowait(
            open_message)
        self.run_parallel(asyncio.sleep(5))

        test_order_book_row = test_active_order_tracker.active_bids[Decimal(
            price)]
        self.assertEqual(test_order_book_row[order_id]["remaining_size"],
                         remaining_size)

        # Test change message diff
        new_size = "2.00"
        raw_change_message = {
            "type": "change",
            "time": datetime.now().isoformat(),
            "product_id": product_id,
            "sequence": 20000000001,
            "order_id": order_id,
            "price": price,
            "new_size": new_size,
            "old_size": remaining_size,
            "side": "buy",
        }
        change_message = CoinbaseProOrderBook.diff_message_from_exchange(
            raw_change_message)
        self.order_book_tracker._order_book_diff_stream.put_nowait(
            change_message)
        self.run_parallel(asyncio.sleep(5))

        test_order_book_row = test_active_order_tracker.active_bids[Decimal(
            price)]
        self.assertEqual(test_order_book_row[order_id]["remaining_size"],
                         new_size)

        # Test match message diff
        match_size = "0.50"
        raw_match_message = {
            "type": "match",
            "trade_id": 10,
            "sequence": 20000000002,
            "maker_order_id": order_id,
            "taker_order_id": "test_order_id_2",
            "time": datetime.now().isoformat(),
            "product_id": "BTC-USD",
            "size": match_size,
            "price": price,
            "side": "buy"
        }
        match_message = CoinbaseProOrderBook.diff_message_from_exchange(
            raw_match_message)
        self.order_book_tracker._order_book_diff_stream.put_nowait(
            match_message)
        self.run_parallel(asyncio.sleep(5))

        test_order_book_row = test_active_order_tracker.active_bids[Decimal(
            price)]
        self.assertEqual(
            Decimal(test_order_book_row[order_id]["remaining_size"]),
            Decimal(new_size) - Decimal(match_size))

        # Test done message diff
        raw_done_message = {
            "type": "done",
            "time": datetime.now().isoformat(),
            "product_id": "BTC-USD",
            "sequence": 20000000003,
            "price": price,
            "order_id": order_id,
            "reason": "filled",
            "remaining_size": 0,
            "side": "buy",
        }
        done_message = CoinbaseProOrderBook.diff_message_from_exchange(
            raw_done_message)
        self.order_book_tracker._order_book_diff_stream.put_nowait(
            done_message)
        self.run_parallel(asyncio.sleep(5))

        test_order_book_row = test_active_order_tracker.active_bids[Decimal(
            price)]
        self.assertTrue(order_id not in test_order_book_row)