Search code examples
pythonwebsocketpython-binance

Websocket does not receive response when opened from a thread


When I open the websocket connection from a thread without even retrieving the latest_price_info from the websocket the websocket does not receive any messages, it only receives a first message and does not receive anything else. The following is the code I run:

import threading
import time
from binance_websocket import BinanceWebSocket  

lock = threading.Lock()

def manage_websocket(binance_ws):
    binance_ws.ws.run_forever()

if __name__ == "__main__":
    symbol = "BTCUSDT"
    interval = "1m"

    binance_ws = BinanceWebSocket(symbol, interval)
    
    websocket_thread = threading.Thread(target=manage_websocket, args=(binance_ws, ))
    websocket_thread.start()
    print("thread started")

    while True:
        print("while in icine girdi")
        time.sleep(10)

and the following is the websocket code:

import websocket
import json
import time
import threading

class BinanceWebSocket:

    def __init__(self, symbol, interval):
        self.symbol = symbol
        self.interval = interval
        self.ws_url = f"wss://stream.binance.com:9443/ws/{self.symbol}@kline_{self.interval}"
        self.ws = websocket.WebSocketApp(self.ws_url, on_message=self.on_message, on_error=self.on_error, on_close=self.on_close)
        self.ws.on_open = self.on_open
        self.latest_price_info = None
        self.lock = threading.Lock()  # Initialize the lock

    def set_latest_price_info(self, price):
        with self.lock:
            self.latest_price_info = price

    def get_latest_price_info(self):
        with self.lock:
            return self.latest_price_info

    def on_message(self, ws, message):
        print("The message is: ", message)
        data = json.loads(message)
        '''
        kline = data['k']
        close = kline['c']
        self.set_latest_price_info(close)  # Use the instance method to set the latest price
        '''
        print("Latest current price updated: ", self.get_latest_price_info())

    def on_error(self, ws, error):
        print(f"Error: {error}") 

    def on_close(self, ws, close_status_code, close_msg):
        print("WebSocket connection closed")
        self.reconnect()

    def on_open(self, ws):
        print("WebSocket connection opened")
        subscription_payload = {
            "method": "SUBSCRIBE",
            "params": [
                f"{self.symbol}@kline_{self.interval}"
            ],
            "id": 1
        }
        self.ws.send(json.dumps(subscription_payload))

    #Binance disconnects websocket connections every 24h, therefore reconnecting when disconnected
    def reconnect(self):
        while True:
            try:
                print("Reconnecting...")
                time.sleep(5)  # Delay before reconnecting
                self.ws = websocket.WebSocketApp(self.ws_url, on_message=self.on_message, on_error=self.on_error, on_close=self.on_close)
                self.ws.on_open = self.on_open
                self.ws.run_forever()
            except Exception as e:
                print(f"Reconnection failed: {e}")

For example when I open the websocket connection without a thread it is all okay and the self.latest_price_info attribute of BinanceWebSocket gets updated as intended if I run the following code, so opening the websocket connection in the main thread:

from binance_websocket import BinanceWebSocket
import time

symbol = "btcusdt"
interval = "1m"  # You can adjust the interval here

ws = BinanceWebSocket(symbol, interval)
ws.ws.run_forever()
try:
    while True:
        lastPriceFrom_ws = ws.latest_price_info
        if lastPriceFrom_ws is not None:
            print("Latest Current Price:", lastPriceFrom_ws)
        time.sleep(1)  # Adjust the sleep time if needed
except KeyboardInterrupt:
    print("WebSocket connection stopped.")

Initially I was trying to retrieve self.latest_price_info from the websocket object and I thought there must have been a deadlock situation where the main thread tries to read self.latest_price_info while the other that runs the websocket connection inside was trying to update it so I started use locks in the websocket class, but later I realized the problem was running the websocket connection in a thread. I even tried joining the other thread to the main thread, but it does not work and while I run the program the websocket just waits and does not receive any messages. It does not return any errors or behaves in any other way. SImply waits on messages but does not receive any. My main objective is to use the websocket to continuously get price data from Binance exchange. So what I need is to have the websocket continuously updating its attribute latest_price_info and I want to read the value. Also, if I open the websocket connection it blocks the execution of the main thread that is why I went for a solution by opening the websocket connection in a thread.


Solution

  • The issue was not about running a websocket connection in a thread, but the symbols. Binance websocket subscriptions require non-capitalized symbols such as "btcusdt" instead of "BTCUSDT" this was a detail I missed, when posting the question. SO, both implementation work if non-capitalized symbols are used.