def setUp(self) -> None:
     super().setUp()
     self.mocking_assistant = NetworkMockingAssistant()
     self.throttler = AsyncThrottler(
         build_rate_limits_by_tier(self.api_tier))
     self.data_source = KrakenAPIOrderBookDataSource(
         self.throttler, trading_pairs=[self.trading_pair])
 def __init__(self,
              trading_pairs: List[str],
              throttler: Optional[AsyncThrottler] = None,
              ):
     super().__init__(KrakenAPIOrderBookDataSource(throttler, trading_pairs), trading_pairs)
     self._order_book_diff_stream: asyncio.Queue = asyncio.Queue()
     self._order_book_snapshot_stream: asyncio.Queue = asyncio.Queue()
     self._ev_loop: asyncio.BaseEventLoop = asyncio.get_event_loop()
     self._saved_message_queues: Dict[str, Deque[OrderBookMessage]] = defaultdict(lambda: deque(maxlen=1000))
    def test_fetch_trading_pairs(self, mocked_api):
        url = f"{CONSTANTS.BASE_URL}{CONSTANTS.ASSET_PAIRS_PATH_URL}"
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        resp = self.get_public_asset_pair_mock()
        mocked_api.get(regex_url, body=json.dumps(resp))

        resp = self.async_run_with_timeout(
            KrakenAPIOrderBookDataSource.fetch_trading_pairs())

        self.assertTrue(len(resp) == 1)
        self.assertIn(self.trading_pair, resp)
    def test_get_last_traded_prices(self, mocked_api):
        url = f"{CONSTANTS.BASE_URL}{CONSTANTS.TICKER_PATH_URL}"
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        last_traded_price = Decimal("52641.10000")
        resp = self.get_last_traded_prices_mock(
            last_trade_close=last_traded_price)
        mocked_api.get(regex_url, body=json.dumps(resp))

        ret = self.async_run_with_timeout(
            KrakenAPIOrderBookDataSource.get_last_traded_prices(
                trading_pairs=[self.trading_pair], throttler=self.throttler))

        self.assertIn(self.trading_pair, ret)
        self.assertEqual(float(last_traded_price), ret[self.trading_pair])
class KrakenAPIOrderBookDataSourceTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()
        cls.ev_loop = asyncio.get_event_loop()
        cls.base_asset = "COINALPHA"
        cls.quote_asset = "HBOT"
        cls.trading_pair = f"{cls.base_asset}-{cls.quote_asset}"
        cls.api_tier = KrakenAPITier.STARTER

    def setUp(self) -> None:
        super().setUp()
        self.mocking_assistant = NetworkMockingAssistant()
        self.throttler = AsyncThrottler(
            build_rate_limits_by_tier(self.api_tier))
        self.data_source = KrakenAPIOrderBookDataSource(
            self.throttler, trading_pairs=[self.trading_pair])

    def async_run_with_timeout(self, coroutine: Awaitable, timeout: int = 1):
        ret = self.ev_loop.run_until_complete(
            asyncio.wait_for(coroutine, timeout))
        return ret

    def get_last_traded_prices_mock(self, last_trade_close: Decimal) -> Dict:
        last_traded_prices = {
            "error": [],
            "result": {
                f"X{self.base_asset}{self.quote_asset}": {
                    "a": ["52609.60000", "1", "1.000"],
                    "b": ["52609.50000", "1", "1.000"],
                    "c": [str(last_trade_close), "0.00080000"],
                    "v": ["1920.83610601", "7954.00219674"],
                    "p": ["52389.94668", "54022.90683"],
                    "t": [23329, 80463],
                    "l": ["51513.90000", "51513.90000"],
                    "h": ["53219.90000", "57200.00000"],
                    "o": "52280.40000"
                }
            }
        }
        return last_traded_prices

    def get_depth_mock(self) -> Dict:
        depth = {
            "error": [],
            "result": {
                f"X{self.base_asset}{self.quote_asset}": {
                    "asks": [["52523.00000", "1.199", 1616663113],
                             ["52536.00000", "0.300", 1616663112]],
                    "bids": [["52522.90000", "0.753", 1616663112],
                             ["52522.80000", "0.006", 1616663109]]
                }
            }
        }
        return depth

    def get_public_asset_pair_mock(self) -> Dict:
        asset_pairs = {
            "error": [],
            "result": {
                f"X{self.base_asset}{self.quote_asset}": {
                    "altname": f"{self.base_asset}{self.quote_asset}",
                    "wsname": f"{self.base_asset}/{self.quote_asset}",
                    "aclass_base": "currency",
                    "base": self.base_asset,
                    "aclass_quote": "currency",
                    "quote": self.quote_asset,
                    "lot": "unit",
                    "pair_decimals": 5,
                    "lot_decimals": 8,
                    "lot_multiplier": 1,
                    "leverage_buy": [2, 3, 4, 5],
                    "leverage_sell": [2, 3, 4, 5],
                    "fees": [
                        [0, 0.26],
                        [50000, 0.24],
                    ],
                    "fees_maker": [
                        [0, 0.16],
                        [50000, 0.14],
                    ],
                    "fee_volume_currency": "ZUSD",
                    "margin_call": 80,
                    "margin_stop": 40,
                    "ordermin": "0.005"
                },
            }
        }
        return asset_pairs

    def get_trade_data_mock(self) -> List:
        trade_data = [
            0,
            [["5541.20000", "0.15850568", "1534614057.321597", "s", "l", ""],
             ["6060.00000", "0.02455000", "1534614057.324998", "b", "l", ""]],
            "trade", f"{self.base_asset}/{self.quote_asset}"
        ]
        return trade_data

    @aioresponses()
    def test_get_last_traded_prices(self, mocked_api):
        url = f"{CONSTANTS.BASE_URL}{CONSTANTS.TICKER_PATH_URL}"
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        last_traded_price = Decimal("52641.10000")
        resp = self.get_last_traded_prices_mock(
            last_trade_close=last_traded_price)
        mocked_api.get(regex_url, body=json.dumps(resp))

        ret = self.async_run_with_timeout(
            KrakenAPIOrderBookDataSource.get_last_traded_prices(
                trading_pairs=[self.trading_pair], throttler=self.throttler))

        self.assertIn(self.trading_pair, ret)
        self.assertEqual(float(last_traded_price), ret[self.trading_pair])

    @aioresponses()
    def test_get_new_order_book(self, mocked_api):
        url = f"{CONSTANTS.BASE_URL}{CONSTANTS.SNAPSHOT_PATH_URL}"
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        resp = self.get_depth_mock()
        mocked_api.get(regex_url, body=json.dumps(resp))

        ret = self.async_run_with_timeout(
            self.data_source.get_new_order_book(self.trading_pair))

        self.assertTrue(isinstance(ret, OrderBook))

        bids_df, asks_df = ret.snapshot
        pair_data = resp["result"][f"X{self.base_asset}{self.quote_asset}"]
        first_bid_price = float(pair_data["bids"][0][0])
        first_ask_price = float(pair_data["asks"][0][0])

        self.assertEqual(first_bid_price, bids_df.iloc[0]["price"])
        self.assertEqual(first_ask_price, asks_df.iloc[0]["price"])

    @aioresponses()
    def test_fetch_trading_pairs(self, mocked_api):
        url = f"{CONSTANTS.BASE_URL}{CONSTANTS.ASSET_PAIRS_PATH_URL}"
        regex_url = re.compile(f"^{url}".replace(".",
                                                 r"\.").replace("?", r"\?"))
        resp = self.get_public_asset_pair_mock()
        mocked_api.get(regex_url, body=json.dumps(resp))

        resp = self.async_run_with_timeout(
            KrakenAPIOrderBookDataSource.fetch_trading_pairs())

        self.assertTrue(len(resp) == 1)
        self.assertIn(self.trading_pair, resp)

    @patch("aiohttp.ClientSession.ws_connect", new_callable=AsyncMock)
    def test_listen_for_trades(self, ws_connect_mock):
        ws_connect_mock.return_value = self.mocking_assistant.create_websocket_mock(
        )
        resp = self.get_trade_data_mock()
        self.mocking_assistant.add_websocket_aiohttp_message(
            websocket_mock=ws_connect_mock.return_value,
            message=json.dumps(resp))
        output_queue = asyncio.Queue()

        self.ev_loop.create_task(
            self.data_source.listen_for_trades(self.ev_loop, output_queue))
        self.mocking_assistant.run_until_all_aiohttp_messages_delivered(
            websocket_mock=ws_connect_mock.return_value)

        self.assertTrue(not output_queue.empty())
        msg = output_queue.get_nowait()
        self.assertTrue(isinstance(msg, OrderBookMessage))
        first_trade_price = resp[1][0][0]
        self.assertEqual(msg.content["price"], first_trade_price)

        self.assertTrue(not output_queue.empty())
        msg = output_queue.get_nowait()
        self.assertTrue(isinstance(msg, OrderBookMessage))
        second_trade_price = resp[1][1][0]
        self.assertEqual(msg.content["price"], second_trade_price)
 def data_source(self) -> OrderBookTrackerDataSource:
     if not self._data_source:
         self._data_source = KrakenAPIOrderBookDataSource(trading_pairs=self._trading_pairs)
     return self._data_source
예제 #7
0
 def setUpClass(cls):
     cls.ev_loop: asyncio.BaseEventLoop = asyncio.get_event_loop()
     cls.order_book_data_source: KrakenAPIOrderBookDataSource = KrakenAPIOrderBookDataSource(["ETHUSDC", "XBTUSDC", "ETHDAI"])
 def setUp(self) -> None:
     super().setUp()
     self.mocking_assistant = NetworkMockingAssistant()
     self.throttler = AsyncThrottler(CONSTANTS.RATE_LIMITS)
     self.data_source = KrakenAPIOrderBookDataSource(
         self.throttler, trading_pairs=[self.trading_pair])