Пример #1
0
    def test_get_snapshot(self, mock_get):
        """
        To validate the response from aiohttp request contains the same payload
        as the final result
        """
        loop = asyncio.get_event_loop()

        # Mock aiohttp response
        f = asyncio.Future()
        f.set_result(FixtureLiquid.SNAPSHOT_1)
        mock_get.return_value = f

        # Instantiate class instance
        liquid_data_source = LiquidAPIOrderBookDataSource()

        liquid_data_source.trading_pair_id_conversion_dict = {'BTC-ETH': 27}

        snapshot = loop.run_until_complete(
            liquid_data_source.get_snapshot(client=aiohttp.ClientSession(),
                                            trading_pair='BTC-ETH',
                                            full=1))

        self.assertEqual(
            list(snapshot.keys()),
            ['buy_price_levels', 'sell_price_levels', 'trading_pair'])
        self.assertEqual(len(snapshot['buy_price_levels']), 2)
        self.assertEqual(len(snapshot['sell_price_levels']), 20)
Пример #2
0
    def test_get_trading_pairs(self, mock_get_exchange_markets_data):
        """
        Test the logic where extracts trading pairs as well as the part
        trading_pair and id mapping is formed
        """
        loop = asyncio.get_event_loop()

        # Mock Future() object return value as the request response
        f = asyncio.Future()
        f.set_result(FixtureLiquid.EXCHANGE_MARKETS_DATA)
        mock_get_exchange_markets_data.return_value = f

        # Instantiate class instance
        liquid_data_source = LiquidAPIOrderBookDataSource()

        trading_pairs = loop.run_until_complete(
            liquid_data_source.get_trading_pairs())

        # Check trading pairs and their order
        self.assertListEqual(
            trading_pairs, ['BTC-USD', 'ETH-USD', 'BTC-USDC', 'ETH-USDC', 'LCX-BTC', 'STAC-ETH', 'WLO-BTC'])

        # Check derived trading_pair and id conversion dict keys and their corresponding values
        self.assertDictEqual(
            liquid_data_source.trading_pair_id_conversion_dict,
            {
                'BTC-USD': '1',
                'BTC-USDC': '443',
                'ETH-USD': '27',
                'ETH-USDC': '444',
                'LCX-BTC': '538',
                'STAC-ETH': '206',
                'WLO-BTC': '506'
            }
        )
 def test_api_get_last_traded_prices(self):
     prices = self.ev_loop.run_until_complete(
         LiquidAPIOrderBookDataSource.get_last_traded_prices(["BTC-USD", "ETH-USD"]))
     for key, value in prices.items():
         print(f"{key} last_trade_price: {value}")
     self.assertGreater(prices["BTC-USD"], 1000)
     self.assertLess(prices["ETH-USD"], 1000)
Пример #4
0
 def __init__(self, trading_pairs: List[str]):
     super().__init__(LiquidAPIOrderBookDataSource(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))
Пример #5
0
    def test_get_tracking_pairs(self, mock_get_trading_pairs, mock_get_snapshot):
        """
        Example output of tracking pairs
        {
            'BTC-USD': OrderBookTrackerEntry(
                trading_pair = 'BTC-USD',
                timestamp = '1573021425.445617',
                order_book = '<hummingbot.core.data_type.order_book.OrderBook object at 0x11fa72328>'
            ),
            'ETH-USDC': OrderBookTrackerEntry(
                trading_pair = 'ETH-USDC',
                timestamp = '1573021426.4484851',
                order_book = '<hummingbot.core.data_type.order_book.OrderBook object at 0x11fa723c0>'
            ),
            'BTC-USDC': OrderBookTrackerEntry(
                trading_pair = 'BTC-USDC',
                timestamp = '1573021427.4509811',
                order_book = '<hummingbot.core.data_type.order_book.OrderBook object at 0x11fa72458>'
            )
        }
        """
        loop = asyncio.get_event_loop()

        # Mock Future() object return value as the request response
        # For this particular test, the return value from get_snapshot is not relevant, therefore
        # setting it with a random snapshot from fixture
        f = asyncio.Future()
        f.set_result(FixtureLiquid.SNAPSHOT_2)
        mock_get_snapshot.return_value = f

        # Mock get trading pairs
        mocked_trading_pairs = ['BTC-USD', 'ETH-USDC', 'BTC-USDC']

        f = asyncio.Future()
        f.set_result(mocked_trading_pairs)
        mock_get_trading_pairs.return_value = f

        # Getting returned tracking pairs
        tracking_pairs = loop.run_until_complete(
            LiquidAPIOrderBookDataSource().get_tracking_pairs())

        # Validate the number of tracking pairs is equal to the number of trading pairs received
        self.assertEqual(len(mocked_trading_pairs), len(tracking_pairs))

        # Make sure the entry key in tracking pairs matches with what's in the trading pairs
        for trading_pair, tracking_pair_obj in zip(mocked_trading_pairs, list(tracking_pairs.keys())):
            self.assertEqual(trading_pair, tracking_pair_obj)

        # Validate the data type for each tracking pair value is OrderBookTrackerEntry
        for order_book_tracker_entry in tracking_pairs.values():
            self.assertIsInstance(order_book_tracker_entry, OrderBookTrackerEntry)

        # Validate the order book tracker entry trading_pairs are valid
        for trading_pair, order_book_tracker_entry in zip(mocked_trading_pairs, tracking_pairs.values()):
            self.assertEqual(order_book_tracker_entry.trading_pair, trading_pair)
Пример #6
0
    def test_filter_market_data(self):
        """
        Test the logic to parse out market data from input exchange data,
        and make sure invalid fields and payload are all filtered out in
        this process.
        """
        market_data = LiquidAPIOrderBookDataSource.filter_market_data(
            exchange_markets_data=FixtureLiquid.EXCHANGE_MARKETS_DATA)

        # Check market data type
        self.assertIsInstance(market_data, list)

        # Check market data size
        self.assertEqual(len(market_data), 7)

        # Select and compare the first item with largest id from the list
        self.assertDictEqual(
            sorted(market_data, key=lambda x: x['id'], reverse=True)[0],
            {
                'id': '538',
                'product_type': 'CurrencyPair',
                'code': 'CASH',
                'name': None,
                'market_ask': 5e-08,
                'market_bid': 3e-08,
                'indicator': -1,
                'currency': 'BTC',
                'currency_pair_code': 'LCXBTC',
                'symbol': None,
                'btc_minimum_withdraw': None,
                'fiat_minimum_withdraw': None,
                'pusher_channel': 'product_cash_lcxbtc_538',
                'taker_fee': '0.001',
                'maker_fee': '0.001',
                'low_market_bid': '3.0e-08',
                'high_market_ask': '5.0e-08',
                'volume_24h': '628660.0',
                'last_price_24h': '0.00000003',
                'last_traded_price': '0.00000004',
                'last_traded_quantity': '4867.0',
                'quoted_currency': 'BTC',
                'base_currency': 'LCX',
                'disabled': False,
                'margin_enabled': False,
                'cfd_enabled': False,
                'last_event_timestamp': '1571979656.7983565'
            }
        )
        # Check market data trading pair and their sorting order
        self.assertListEqual(
            [market['currency_pair_code'] for market in market_data],
            ['WLOBTC', 'LCXBTC', 'STACETH', 'BTCUSDC', 'BTCUSD', 'ETHUSDC', 'ETHUSD']
        )
Пример #7
0
    def test_get_active_exchange_markets(self, mock_get_exchange_markets_data):
        """
        Test end to end flow from pinging Liquid API for markets and exchange data
        all the way to extract out needed information such as trading_pairs,
        prices, and volume information.
        """
        loop = asyncio.get_event_loop()

        # Mock Future() object return value as the request response
        f = asyncio.Future()
        f.set_result(FixtureLiquid.EXCHANGE_MARKETS_DATA)
        mock_get_exchange_markets_data.return_value = f

        all_markets_df = loop.run_until_complete(
            LiquidAPIOrderBookDataSource.get_active_exchange_markets())
        # loop.close()

        # Check DF type
        self.assertIsInstance(all_markets_df, pd.DataFrame)

        # Check DF dimension
        self.assertEqual(all_markets_df.shape,
                         (7, 29))  # (num of rows, num of cols)

        # Check DF indices
        self.assertListEqual(all_markets_df.index.to_list(), [
            'BTC-USD', 'ETH-USD', 'BTC-USDC', 'ETH-USDC', 'LCX-BTC',
            'STAC-ETH', 'WLO-BTC'
        ])

        # Check DF column names
        self.assertListEqual(sorted(all_markets_df.columns), [
            'USDVolume', 'base_currency', 'btc_minimum_withdraw',
            'cfd_enabled', 'code', 'currency', 'currency_pair_code',
            'disabled', 'fiat_minimum_withdraw', 'high_market_ask', 'id',
            'indicator', 'last_event_timestamp', 'last_price_24h',
            'last_traded_price', 'last_traded_quantity', 'low_market_bid',
            'maker_fee', 'margin_enabled', 'market_ask', 'market_bid', 'name',
            'product_type', 'pusher_channel', 'quoted_currency', 'symbol',
            'taker_fee', 'volume', 'volume_24h'
        ])

        # Check DF values
        self.assertEqual(all_markets_df.loc['BTC-USD'].last_traded_price,
                         '7470.49746')

        # Check DF order, make sure it's sorted by USDVolume col in desending order
        usd_volumes = all_markets_df.loc[:, 'USDVolume'].to_list()
        self.assertListEqual(
            usd_volumes, sorted(usd_volumes, reverse=True),
            "The output usd volumes should remain the same after being sorted again"
        )
Пример #8
0
    def test_listen_for_order_book_diffs(self, mock_inner_messages):
        timeout = 2
        loop = asyncio.get_event_loop()

        q = asyncio.Queue()

        #  Socket events receiving in the order from top to bottom
        mocked_socket_responses = [
            FixtureLiquid.DIFF_BUY_1,
            FixtureLiquid.DIFF_SELL_2,
            FixtureLiquid.WS_PUSHER_SUBSCRIPTION_SUCCESS_RESPONSE,
            FixtureLiquid.WS_CLIENT_CONNECTION_SUCCESS_RESPONSE,
            FixtureLiquid.DIFF_BUY_2,
            FixtureLiquid.DIFF_SELL_1
        ]

        mock_inner_messages.return_value = self.AsyncIterator(seq=mocked_socket_responses)

        print('{class_name} test {test_name} is going to run for {timeout} seconds, starting now'.format(
            class_name=self.__class__.__name__,
            test_name=inspect.stack()[0][3],
            timeout=timeout))

        try:
            loop.run_until_complete(
                # Force exit from event loop after set timeout seconds
                asyncio.wait_for(
                    LiquidAPIOrderBookDataSource().listen_for_order_book_diffs(ev_loop=loop, output=q),
                    timeout=timeout
                )
            )
        except concurrent.futures.TimeoutError as e:
            print(e)

        first_event = q.get_nowait()
        second_event = q.get_nowait()
        third_event = q.get_nowait()
        fourth_event = q.get_nowait()

        recv_events = [first_event, second_event, third_event, fourth_event]

        for event in recv_events:
            # Validate the data inject into async queue is in Liquid order book message type
            self.assertIsInstance(event, LiquidOrderBookMessage)

            # Validate the event type is equal to DIFF
            self.assertEqual(event.type, OrderBookMessageType.DIFF)

            # Validate the actual content injected is dict type
            self.assertIsInstance(event.content, dict)
Пример #9
0
    def test_listen_for_order_book_snapshots(self, mock_get_trading_pairs, mock_get_snapshot):
        """
        Example order book message added to the queue:
        LiquidOrderBookMessage(
            type = < OrderBookMessageType.SNAPSHOT: 1 > ,
            content = {
                'buy_price_levels': [
                    ['181.95138', '0.69772000'],
                    ...
                ],
                'sell_price_levels': [
                    ['182.11620', '0.32400000'],
                    ...
                ],
                'trading_pair': 'BTC-USDC'
            },
            timestamp = 1573041256.2376761)
        """
        loop = asyncio.get_event_loop()

        # Instantiate empty async queue and make sure the initial size is 0
        q = asyncio.Queue()
        self.assertEqual(q.qsize(), 0)

        # Mock Future() object return value as the request response
        f1 = asyncio.Future()
        f1.set_result(
            {
                **FixtureLiquid.SNAPSHOT_2,
                'trading_pair': 'ETH-USD',
                'product_id': 27
            }
        )
        f2 = asyncio.Future()
        f2.set_result(
            {
                **FixtureLiquid.SNAPSHOT_1,
                'trading_pair': 'LCX-BTC',
                'product_id': 538
            }
        )

        mock_get_snapshot.side_effect = [f1, f2]

        # Mock get trading pairs
        mocked_trading_pairs = ['ETH-USD', 'LCX-BTC']

        f = asyncio.Future()
        f.set_result(mocked_trading_pairs)
        mock_get_trading_pairs.return_value = f

        # Listening for tracking pairs within the set timeout timeframe
        timeout = 6

        print('{class_name} test {test_name} is going to run for {timeout} seconds, starting now'.format(
            class_name=self.__class__.__name__,
            test_name=inspect.stack()[0][3],
            timeout=timeout))

        try:
            loop.run_until_complete(
                # Force exit from event loop after set timeout seconds
                asyncio.wait_for(
                    LiquidAPIOrderBookDataSource().listen_for_order_book_snapshots(ev_loop=loop, output=q),
                    timeout=timeout
                )
            )
        except concurrent.futures.TimeoutError as e:
            print(e)

        # Make sure that the number of items in the queue after certain seconds make sense
        # For instance, when the asyncio sleep time is set to 5 seconds in the method
        # If we configure timeout to be the same length, only 1 item has enough time to be received
        self.assertGreaterEqual(q.qsize(), 1)

        # Validate received response has correct data types
        first_item = q.get_nowait()
        self.assertIsInstance(first_item, LiquidOrderBookMessage)
        self.assertIsInstance(first_item.type, OrderBookMessageType)

        # Validate order book message type
        self.assertEqual(first_item.type, OrderBookMessageType.SNAPSHOT)

        # Validate snapshot received matches with the original snapshot received from API
        self.assertEqual(first_item.content['bids'], FixtureLiquid.SNAPSHOT_2['buy_price_levels'])
        self.assertEqual(first_item.content['asks'], FixtureLiquid.SNAPSHOT_2['sell_price_levels'])

        # Validate the rest of the content
        self.assertEqual(first_item.content['trading_pair'], mocked_trading_pairs[0])
        self.assertEqual(first_item.content['product_id'], 27)
Пример #10
0
 def setUp(self) -> None:
     super().setUp()
     self.data_source = LiquidAPIOrderBookDataSource(
         trading_pairs=[self.trading_pair])
Пример #11
0
class LiquidAPIOrderBookDataSourceTest(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}"

    def setUp(self) -> None:
        super().setUp()
        self.data_source = LiquidAPIOrderBookDataSource(
            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_products_response_mock(self,
                                   trading_pair_exch_id: int = 659
                                   ) -> List[Dict[str, Any]]:
        products_mock = [{
            "id":
            str(trading_pair_exch_id),
            "product_type":
            "CurrencyPair",
            "code":
            "CASH",
            "name":
            None,
            "market_ask":
            12.5,
            "market_bid":
            12.04,
            "indicator":
            None,
            "currency":
            self.base_asset,
            "currency_pair_code":
            f"{self.quote_asset}{self.base_asset}",
            "symbol":
            None,
            "btc_minimum_withdraw":
            None,
            "fiat_minimum_withdraw":
            None,
            "pusher_channel":
            f"product_cash_{self.quote_asset}{self.base_asset}_659".lower(),
            "taker_fee":
            "0.0",
            "maker_fee":
            "0.0",
            "low_market_bid":
            "11.91",
            "high_market_ask":
            "12.5",
            "volume_24h":
            "2361.64741972",
            "last_price_24h":
            "11.92",
            "last_traded_price":
            "12.32",
            "last_traded_quantity":
            "2.00340964",
            "average_price":
            "12.33314",
            "quoted_currency":
            self.quote_asset,
            "base_currency":
            self.base_asset,
            "tick_size":
            "0.01",
            "disabled":
            False,
            "margin_enabled":
            False,
            "cfd_enabled":
            False,
            "perpetual_enabled":
            False,
            "last_event_timestamp":
            "1598864820.004941733",
            "timestamp":
            "1598864820.004941733",
            "multiplier_up":
            "1.4",
            "multiplier_down":
            "0.6",
            "average_time_interval":
            300
        }, {
            "id": "1",
            "product_type": "CurrencyPair",
            "code": "CASH",
            "name": " CASH Trading",
            "market_ask": 11628.9,
            "market_bid": 11619.42,
            "indicator": 1,
            "currency": "USD",
            "currency_pair_code": "BTCUSD",
            "symbol": "$",
            "btc_minimum_withdraw": None,
            "fiat_minimum_withdraw": None,
            "pusher_channel": "product_cash_btcusd_1",
            "taker_fee": "0.0",
            "maker_fee": "0.0",
            "low_market_bid": "11576.4",
            "high_market_ask": "11751.04",
            "volume_24h": "138.1747948",
            "last_price_24h": "11577.59",
            "last_traded_price": "11626.37",
            "last_traded_quantity": "0.1201",
            "average_price": "11628.69213",
            "quoted_currency": "USD",
            "base_currency": "BTC",
            "tick_size": "0.01",
            "disabled": True,
            "margin_enabled": True,
            "cfd_enabled": True,
            "perpetual_enabled": False,
            "last_event_timestamp": "1598865015.73273117",
            "timestamp": "1598865015.73273117",
            "multiplier_up": "1.4",
            "multiplier_down": "0.6",
            "average_time_interval": 300
        }]
        return products_mock

    @aioresponses()
    def test_get_trading_pairs(self, mocked_api):
        url = Constants.GET_EXCHANGE_MARKETS_URL
        resp = self.get_products_response_mock()
        mocked_api.get(url, body=json.dumps(resp))

        trading_pairs = self.async_run_with_timeout(
            self.data_source.get_trading_pairs())

        self.assertEqual(1, len(trading_pairs))

    @aioresponses()
    def test_get_trading_pairs_updates_conversion_dict(self, mocked_api):
        url = Constants.GET_EXCHANGE_MARKETS_URL
        trading_pair_exch_id = 659
        resp = self.get_products_response_mock(trading_pair_exch_id)
        mocked_api.get(url, body=json.dumps(resp))

        self.async_run_with_timeout(self.data_source.get_trading_pairs())

        self.assertIn(self.trading_pair,
                      self.data_source.trading_pair_id_conversion_dict)
        self.assertEqual(
            str(trading_pair_exch_id),
            self.data_source.trading_pair_id_conversion_dict[
                self.trading_pair])