How do you close a websocket connection if the API page is invalid

Viewed 505

I am working with the Binance API. I am connecting to their API and trying to assess if Binance has a list of assets on their platform or not. The list of assets is seen below:

assets = ['tribe', 'pax']

I pass this through to their API by inserting the name of the assets into the SOCKET link:

SOCKET = f"wss://stream.binance.com:9443/ws/{asset}usdt@ticker"

I know the asset does exist on their website if on_message is called, because then I have accomplished a consistent connection with their API and it will keep printing messages unless I close the connection (which I do). However, if no message is received in n time I know they do not have the asset I am looking for. In this case Binance does have tribe, but not pax. I want to close the connection if the asset is not on their website after n time, how do I do this?

import ssl
import websocket

def on_open(ws):
    print('connection: successful')


def on_close(ws, *args):
    print('connection: lost')
    print("---------------------------------------------------")
    ws.close()


def on_message(ws, message):
    print("message received")
    print()
    ws.close()


def on_error(ws, message):
    print(message)
    print()


assets = ['tribe', 'pax']
for asset in assets:
    print(asset)
    SOCKET = f"wss://stream.binance.com:9443/ws/{asset}usdt@ticker"


    ws = websocket.WebSocketApp(SOCKET, on_open=on_open, on_close=on_close, on_message=on_message,
                                on_error=on_error)

    ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})

I have attempted to write:

if on_message == False:
    ws.close()

however this does not work because on_message is not even being called as far as my knowledge goes.

Here is the Binance API documentation: https://github.com/binance/binance-spot-api-docs/blob/master/web-socket-streams.md#klinecandlestick-streams Here is the websocket-client documentation: https://websocket-client.readthedocs.io/en/latest/examples.html

3 Answers

Try this:-

import websocket
import ssl
import time
from threading import Thread


class Binance():
    def __init__(self, asset, timeout=5):
        self.url = f'wss://stream.binance.com:9443/ws/{asset}usdt@ticker'
        self.ws = None
        self.mr = False
        self.timeout = timeout

    def start(self):
        self.ws = websocket.WebSocketApp(self.url, on_message=self.on_message)
        Thread(target=self.monitor).start()
        self.ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})

    def on_message(self, ws, message):
        self.mr = True
        print(message)

    def stop(self):
        if self.ws:
            self.ws.close()
            self.ws = None

    def monitor(self):
        while True:
            time.sleep(self.timeout)
            if not self.mr:
                self.stop()
                break
            self.mr = False


def runner(asset):
    Binance(asset).start()


for asset in ['pax', 'tribe']:
    Thread(target=runner, args=(asset,)).start()

This is rough, but it works


import ssl
import websocket
import requests
import json
import pprint

def on_open(ws):
    print('connection: successful')


def on_close(ws, *args):
    print('connection: lost')
    print()
    ws.close()


def on_message(ws, message):
    json_message = json.loads(message)
    pprint.pprint(json_message)
    ws.close()


def on_error(ws, message):
    print("error")
    print()


assets = ['TRIBE', 'PAX', 'OGN', 'RAI', 'QNT', 'BTC']
for asset in assets:
    url = f"https://www.binance.com/en/trade/{asset}_USDT"
    soup = requests.get(url).text

    if "https://bin.bnbstatic.com/static/images/electron/404-error.png" in soup:
        print(f"{asset} not on coinbase")


    else:
        print(asset, "on coinbase")
        SOCKET = f"wss://stream.binance.com:9443/ws/{asset.lower()}usdt@ticker"
        print(SOCKET)

        ws = websocket.WebSocketApp(SOCKET, on_open=on_open, on_close=on_close, on_message=on_message, on_error=on_error)
        ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})

    print("------------------------------")
    print()


Also, this does not use the method you were thinking of but it does work like you want it too (I think).

I came across this thread while researching a ticker feed to an ML engine. I originally thought the example used thewebsocket package, which is really old (2010?) and not maintained anymore, but it probably uses the websock-client package, which is current. Both are named websock. I have not investigated either in depth but the newer package seems fruitful.

An alternative is to use the new websockets package. Here's a ticker feed from coinbase using a single thread of coroutines (asyncio). Note that the request is sent once on all the assets/products and then the ticker is received in a loop. If anything fails, the try-catch will be run.

def run_feed():
    '''
    simple test for pro.coinbase ticker feed
    See https://websockets.readthedocs.io/en/stable/    
    '''
    feed_done = False

    def sig_handler(signo, frame):
        nonlocal feed_done
        feed_done = True

    # url="wss://ws-feed.pro.coinbase.com"
    url="wss://ws-feed.pro.coinbase1.com"    
    prodlist = ['ETH-USD', 'DOT-USD']
    json_req = json.dumps(
        {
            "type": "subscribe",
            "product_ids": prodlist,
            "channels": ["matches"],
        }
    )
    
    async def call_api(request):
        nonlocal feed_done
        try:
            async with websockets.connect(url) as ws:
                await ws.send(request)
                while not feed_done:
                    recv = await ws.recv()
                    print_recv(json.loads(recv))
                    print(f'exit feed loop and close websocket')
                    await ws.close()
        except Exception as e:
            print(f'exception: doc={e.__doc__} str={e}')

    signal.signal(signal.SIGINT, sig_handler)                
    asyncio.get_event_loop().run_until_complete(call_api(json_req))
Related