bmoscon / cryptofeed

Cryptocurrency Exchange Websocket Data Feed Handler
Other
2.14k stars 666 forks source link

Kucoin - Unauthenticated subscription disconnects after 24 hours, fails to refresh token, and then cannot reconnect #1023

Open vincentmele opened 3 months ago

vincentmele commented 3 months ago

Describe the bug Using an unauthenticated subscription to Kucoin's trade feed for a period of time (about 24 hours), Kucoin disconnects the websocket and then cryptofeed is unable to reconnect due to cryptofeed not refreshing the connection token.

To Reproduce Steps to reproduce the behavior:

2024-04-05 10:47:36,818 : WARNING : KUCOIN: error from exchange {'id': '660fd6c84d0eab582c237b51', 'type': 'error', 'code': 401, 'data': 'token is expired'}
2024-04-05 10:47:36,819 : WARNING : KUCOIN.ws.3: encountered connection issue no close frame received or sent - reconnecting in 1.0 seconds...

Expected behavior Cryptofeed should renew the token, reconnect, and continue the channel subscriptions.

Operating System:

Cryptofeed Version

I see that the Binance exchange code has functions to refresh the auth token, I'll take a look and see if I can get something similar to work with KuCoin and submit a PR if successful.

vincentmele commented 2 months ago

Further investigation:

This appears to be because the KuCoin Feed object (exchanges/kucoin.py) only obtains the connection token once upon its creation in the "address_info" object.

    def __init__(self, **kwargs):
        address_info = self.http_sync.write('https://api.kucoin.com/api/v1/bullet-public', json=True)
        token = address_info['data']['token']
        address = address_info['data']['instanceServers'][0]['endpoint']
        address = f"{address}?token={token}"
        self.websocket_endpoints = [WebsocketEndpoint(address, options={'ping_interval': address_info['data']['instanceServers'][0]['pingInterval'] / 2000})]
        super().__init__(**kwargs)
        if any([len(self.subscription[chan]) > 300 for chan in self.subscription]):
            raise ValueError("Kucoin has a limit of 300 symbols per connection")
        self.__reset()

Then, when the connection is kicked about 24 hours later, connection_handler.py tries to reconnect without reinitializing the KuCoin Feed object.

In essence, the feed itself needs to be recreated, not just the connection being retried under the same feed. In an ideal world, the code would know that the token timeout is 24 hours, refresh it ahead of time, then restart the connection using the new token.

I've worked around this by using a process manager that restarts the feed after a set interval.