sammchardy / python-binance

Binance Exchange API python implementation for automated trading
https://python-binance.readthedocs.io/en/latest/
MIT License
5.89k stars 2.2k forks source link

Websockets RuntimeError "This event loop is already running" #1354

Open savelyevlad opened 10 months ago

savelyevlad commented 10 months ago

When I'm trying to run a websocket, then in some time stop it, and run a new websocket, the following error occurs:

Exception in thread Thread-2: Traceback (most recent call last):

File "D:\python\Python39\lib\threading.py", line 950, in _bootstrap_inner self.run() File "D:\python\Python39\lib\site-packages\binance\threaded_stream.py", line 59, in run self._loop.run_until_complete(self.socket_listener()) File "D:\python\Python39\lib\asyncio\base_events.py", line 618, in run_until_complete self._check_running() File "D:\python\Python39\lib\asyncio\base_events.py", line 578, in _check_running raise RuntimeError('This event loop is already running') RuntimeError: This event loop is already running D:\python\Python39\lib\threading.py:952: RuntimeWarning: coroutine 'ThreadedApiManager.socket_listener' was never awaited self._invoke_excepthook(self) RuntimeWarning: Enable tracemalloc to get the object allocation traceback

This is a part of code which I use to run websocket: twm = ThreadedWebsocketManager(self.main_window.api_key, self.main_window.api_secret) twm.start()

current_candle_websocket = twm.start_kline_futures_socket(callback=self.handle_candle_message, symbol=self.symbol, interval=Client.KLINE_INTERVAL_5MINUTE)

This is a part of code which I use to stop websocket: twm.stop_socket(current_candle_websocket) twm.stop() twm = ''

I use Python 3.9. The error didn't occur on python-binance 1.0.15, but since some API features are retired I can no longer use this version and updated python-binance to 1.0.19, and after that I am getting this error.

alexrmacleod commented 10 months ago

getting the same error, running in notebook this error only happens on junypter notebook runs fine as a python conda script

can get it working with python-binance 1.0.15 but need to use 1.0.19 for the new futures endpoints tried on Python 3.9 & Python 3.7 same issue

trying to use 1.0.19 version to get new futures endpoints but getting the below error

Exception in thread Thread-4: Traceback (most recent call last): File "/Users/alex/opt/anaconda3/envs/trader/lib/python3.7/threading.py", line 926, in _bootstrap_inner self.run() File "/Users/alex/opt/anaconda3/envs/trader/lib/python3.7/site-packages/binance/threaded_stream.py", line 59, in run self._loop.run_until_complete(self.socket_listener()) File "/Users/alex/opt/anaconda3/envs/trader/lib/python3.7/asyncio/base_events.py", line 563, in run_until_complete self._check_runnung() File "/Users/alex/opt/anaconda3/envs/trader/lib/python3.7/asyncio/base_events.py", line 523, in _check_runnung raise RuntimeError('This event loop is already running') RuntimeError: This event loop is already running

/Users/alex/opt/anaconda3/envs/trader/lib/python3.7/threading.py:960: RuntimeWarning: coroutine 'ThreadedApiManager.socket_listener' was never awaited del exc_type, exc_value, exc_tb RuntimeWarning: Enable tracemalloc to get the object allocation traceback

code:

def start_trading(self, historical_days):

        client.futures_change_leverage(symbol = self.symbol, leverage = self.leverage) # NEW

        self.twm = ThreadedWebsocketManager(testnet = False) # testnet
        self.twm.start()

        if self.bar_length in self.available_intervals:
            self.get_most_recent(symbol = self.symbol, interval = self.bar_length, days = historical_days)
            self.twm.start_kline_futures_socket(callback = self.stream_candles, symbol = self.symbol, interval = self.bar_length) # Adj: start_kline_futures_socket
            self.twm.join() # for script only not notebook

@sammchardy 🙏

wanglili-dartmouth commented 9 months ago

@sammchardy same here, happened in python 3.7, 3.8, 3.9,3.10. If changed to python 3.5 or 3.6, another error will occur [Python3.6 AttributeError: module 'asyncio' has no attribute 'run']

OpenCoderX commented 9 months ago

I get the same error, it's block me from upgrading.

OpenCoderX commented 8 months ago

I would pay to have this fixed.

LoveBloodAndDiamonds commented 7 months ago

i have same problem, but i fix it.

Problem was in what i create twm instance in Thread-N, and try to use it in Thread-N+1

twm = ThreadedWebsocketManager(self.main_window.api_key, self.main_window.api_secret)

looks like you write some desktop app, so try watch what with threads there

WonniPooh commented 5 months ago

Please correct me if I am wrong, but as I see from the code - the ThreadedWebsocketManager is inherited from ThreadedApiManager, which is also inherited, fromthreading.Thread.

So on creation of ThreadedWebsocketManager a constructor of ThreadedApiManager is called, that calls

class ThreadedApiManager(threading.Thread):

    def __init__(
        self, api_key: Optional[str] = None, api_secret: Optional[str] = None,
        requests_params: Optional[Dict[str, Any]] = None, tld: str = 'com',
        testnet: bool = False, session_params: Optional[Dict[str, Any]] = None
    ):
        """Initialise the BinanceSocketManager

        """
        super().__init__()
        self._loop: asyncio.AbstractEventLoop = get_loop()
def get_loop():
    """check if there is an event loop in the current thread, if not create one
    inspired by https://stackoverflow.com/questions/46727787/runtimeerror-there-is-no-current-event-loop-in-thread-in-async-apscheduler
    """
    try:
        loop = asyncio.get_event_loop()
        return loop
    except RuntimeError as e:
        if str(e).startswith("There is no current event loop in thread"):
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            return loop
        else:
            raise

And then when we call ThreadedWebsocketManager.start(), the run() method of ThreadedApiManager is invoked:

    def run(self):
        self._loop.run_until_complete(self.socket_listener())

Before the changes in commit https://github.com/sammchardy/python-binance/commit/77266dafdd952b467759310044c1cd6cf593ef9d

a new event loop was created, what was perfectly logical, though get_loop() is called in a ThreadedApiManager constructor, which is called BEFORE starting the ThreadedWebsocketManager thread. So we will get the event_loop of our main thread. And if I am using asyncio - having an event loop in current thread I am creating ThreadedWebsocketManager from, that running loop is returned to be used by that new instance of ThreadedWebsocketManager in a new loop!

And if, in some case, I want to create 2 ThreadedWebsocketManager classes, I will get an error of

Exception in thread Thread-5:
Traceback (most recent call last):
  File "/usr/lib64/python3.12/threading.py", line 1073, in _bootstrap_inner
    self.run()
  File "/home/oserbin/.local/lib/python3.12/site-packages/binance/threaded_stream.py", line 59, in run
    self._loop.run_until_complete(self.socket_listener())
  File "/usr/lib64/python3.12/asyncio/base_events.py", line 660, in run_until_complete
    self._check_running()
  File "/usr/lib64/python3.12/asyncio/base_events.py", line 619, in _check_running
    raise RuntimeError('This event loop is already running')
RuntimeError: This event loop is already running

And printing event loop object addresses of 2 separate objects of ThreadedWebsocketManager that what I get: 0x7f2e3a90d070 0x7f2e3a90d070

So I may misunderstand something, please correct me if I am wrong and there is no bug in it.

WonniPooh commented 5 months ago

I would pay to have this fixed.

I don't know what is your situation exactly, but the problem described by me above could be solved by replacing

self._loop: asyncio.AbstractEventLoop = get_loop() to self._loop: asyncio.AbstractEventLoop = asyncio.new_event_loop() in file https://github.com/sammchardy/python-binance/blob/master/binance/threaded_stream.py