Websocket : (RuntimeError('cannot join current thread'),)

Seeing this error on the websocket error_handler().


2024-02-21 15:30:00,724 -[ trailing.p](http://trailing.py)y > error_handler() # 230 [ERROR] args: (RuntimeError('cannot join current thread'),)

2024-02-21 15:30:00,724 -[ trailing.p](http://trailing.py)y > error_handler() # 231 [ERROR] args: {}

2024-02-21 15:30:00,724 -[ trailing.p](http://trailing.py)y > close_handler() # 234 [ERROR] args: ({'code': 200, 'message': 'Connection Closed', 's': 'ok'},)

my web socket error handler looks like this:

    def error_handler(self, *args, **kwargs):
        log.error(f"args: {args}")
        log.error(f"args: {kwargs}")

This is causing the program to not terminate after the websocket closes at 3:30 PM

Not sure how to handle this on my end, any help will be welcome.

Hey @s13rw81

We need more information regarding the inputs given in the Websocket to check this , please refer to the docs :

https://myapi.fyers.in/docsv3

thank you for your prompt reply. The web socket is created, authenticated subscribed and data received without any issues the only problem that is happening is at 3.30 pm when the market is closing i am seeing this being passed as parameters to the error_handler of the websocket. I am ready to share my code with you if thats possible.

import sqlite3
from datetime import datetime
from auth.secret import get_token_and_client_id
from config import (
    DB_FILE,
    DELETE_TSL_QUERY,
    INSERT_TSL_QUERY,
    SELECT_TSL_QUERY,
    STOP_PERCENT,
    UPDATE_TSL_QUERY,
    WS_LOG_PATH,
    log,
)
from fyers_apiv3.FyersWebsocket import data_ws as ws
from fyers_apiv3 import fyersModel
from orders.order import Order
from reporting import report

class Stoploss:
    def __init__(self, fyers: fyersModel):
        self.holdings = None
        self.positions = None
        self.fs = None
        self.fyers = fyers
        self.order = Order(self.fyers)
        self.to_sub = list()

    def setup_trailing_stoploss(self):
        try:
            client_id, token = get_token_and_client_id()
            self.fs = ws.FyersDataSocket(access_token=f"{client_id}:{token}", litemode=True, log_path=WS_LOG_PATH,
                                         on_connect=self.start, on_close=self.close_handler,
                                         on_error=self.error_handler, on_message=self.quote_handler)
            self.fs.connect()
            log.debug("connected to fyers ws successfully.")

        except Exception as e:
            log.error(f"error setting up websocket connection to fyers : {e}")

    def get_positions(self):
        try:
            # get the current positions
            self.positions = self.fyers.positions()["netPositions"]
            # check for positions that are long
            if self.positions:
                self.positions = [
                    i for i in self.positions if i["side"] != "" and i["side"] > 0
                ]

                for position in self.positions:
                    self.to_sub.append(position["symbol"])
            log.debug(f"after appending open positions, subscription list is: {self.to_sub}")

        except Exception as e:
            log.error(f"error getting positions from fyers : {e}")

        try:
            conn = sqlite3.connect(DB_FILE)
            c = conn.cursor()
            for i in self.positions:
                try:
                    c.execute(
                        SELECT_TSL_QUERY,
                        (i["symbol"],),
                    )
                    result = c.fetchall()
                    if result:
                        i["stoploss"] = result[0][0]
                    else:
                        i["stoploss"] = i["buyAvg"] - (i["buyAvg"] * STOP_PERCENT / 100)
                        # add symbol to trailing_stoploss db:
                        c.execute(
                            INSERT_TSL_QUERY,
                            (i["symbol"], i["stoploss"]),
                        )
                        conn.commit()
                    log.debug(f"calculated and added stoploss = {i['stoploss']} for {i['symbol']}")

                except Exception as e:
                    log.error(f"error calculating and added stoploss for {i['symbol']}, {e}")
            c.close()
            conn.close()

        except Exception as e:
            log.error(f"error setting default sl for position : {e}")

    def get_holdings(self):
        try:
            # get the current holdings
            self.holdings = self.fyers.holdings()["holdings"]
            # check for holdings that are open
            if self.holdings:
                self.holdings = [i for i in self.holdings if i["remainingQuantity"] > 0]

                for holding in self.holdings:
                    self.to_sub.append(holding["symbol"])

            log.debug(f"after appending open positions, subscription list is: {self.to_sub}")

        except Exception as e:
            log.error(f"error getting positions from fyers : {e}")
        try:
            conn = sqlite3.connect(DB_FILE)
            c = conn.cursor()
            for i in self.holdings:
                try:
                    c.execute(
                        SELECT_TSL_QUERY,
                        (i["symbol"],),
                    )
                    result = c.fetchall()

                    if result:
                        i["stoploss"] = result[0][0]
                    else:
                        i["stoploss"] = i["costPrice"] - (
                                i["costPrice"] * STOP_PERCENT / 100
                        )
                        # add symbol to trailing_stoploss db:
                        c.execute(
                            INSERT_TSL_QUERY,
                            (i["symbol"], i["stoploss"]),
                        )
                        conn.commit()

                except Exception as e:
                    log.error(f"error calculating and added stoploss for {i['symbol']}, {e}")

            c.close()
            conn.close()

        except Exception as e:
            log.error(e)

    def start(self):
        # unsubscribe to any previous feeds
        if self.to_sub:
            self.fs.unsubscribe(self.to_sub)
            self.to_sub = [symbol.replace('&', '%26') for symbol in self.to_sub]

            self.fs.subscribe(symbols=self.to_sub, data_type="SymbolUpdate")
            log.debug("Subscribed to live feed.")
            self.fs.keep_running()
        else:
            log.debug("No holdings or positions. Nothing to subscribe.")
            # empty the trailing_stoploss table
            conn = sqlite3.connect(DB_FILE)
            c = conn.cursor()
            c.execute(DELETE_TSL_QUERY)
            conn.commit()
            c.close()
            conn.close()

    def quote_handler(self, quotes):
        # log.info(f'quotes received as is : {quotes}, data type {type(quotes)}')
        if quotes['type'] != 'sf':
            return

        if self.market_status() == "open":
            try:
                self.stoploss(quotes["symbol"], quotes["ltp"])
            except Exception as e:
                log.error(f"error in processing tick : {quotes}, {e}")
        else:
            self.complete()

    def stoploss(self, symbol, ltp):
        # holdings
        if self.holdings:
            for i in self.holdings:
                if symbol == i["symbol"]:
                    # check if SL breach
                    if ltp  i["stoploss"]:
                            i["stoploss"] = trailing_sl
                            conn = sqlite3.connect(DB_FILE)
                            c = conn.cursor()
                            c.execute(
                                UPDATE_TSL_QUERY,
                                (
                                    trailing_sl,
                                    i["symbol"],
                                ),
                            )
                            conn.commit()
                            c.close()
                            conn.close()

        # log.debug(f"quote received for {symbol} : {ltp}")
        # positions
        if self.positions:
            for i in self.positions:
                if symbol == i["symbol"]:
                    # check if SL breach
                    if ltp <= i["stoploss"]:
                        log.info(f'[POSITION] Stoploss Hit. for {i}, LTP is {ltp}, selling and unsubscribing to symbol')
                        # unsubscribe from the ticker
                        self.fs.unsubscribe([i["symbol"]])
                        # exit position
                        self.order.sell(position=i)

    def market_status(self):
        now = datetime.now()
        nine15 = now.replace(hour=9, minute=15, second=0, microsecond=0)
        three30 = now.replace(hour=15, minute=30, second=0, microsecond=0)
        if nine15 < now < three30:
            return "open"
        else:
            return "closed"

    def error_handler(self, *args, **kwargs):
        log.error(f"args: {args}")
        log.error(f"args: {kwargs}")

    def close_handler(self, *args, **kwargs):
        log.error(f"args: {args}")
        log.error(f"args: {kwargs}")
        if self.market_status() == "open":
            # get holdings again and start the ws
            self.get_positions()
            self.get_holdings()
            self.start()
        else:
            self.complete()
            report.push_report("🔵 Market is closed (close handler triggered).")

    def complete(self):
        if self.to_sub:
            self.fs.unsubscribe(self.to_sub)
        self.fs.close_connection()
        self.fs = None
        log.debug("trading completed for the day.")
        report.push_report("Trading completed for the day.")

kindly look at my code below and see if you can spot anything unusual.

import sqlite3
from datetime import datetime
from auth.secret import get_token_and_client_id
from config import (
    DB_FILE,
    DELETE_TSL_QUERY,
    INSERT_TSL_QUERY,
    SELECT_TSL_QUERY,
    STOP_PERCENT,
    UPDATE_TSL_QUERY,
    WS_LOG_PATH,
    log,
)
from fyers_apiv3.FyersWebsocket import data_ws as ws
from fyers_apiv3 import fyersModel
from orders.order import Order
from reporting import report

class Stoploss:
    def __init__(self, fyers: fyersModel):
        self.holdings = None
        self.positions = None
        self.fs = None
        self.fyers = fyers
        self.order = Order(self.fyers)
        self.to_sub = list()

    def setup_trailing_stoploss(self):
        try:
            client_id, token = get_token_and_client_id()
            self.fs = ws.FyersDataSocket(access_token=f"{client_id}:{token}", litemode=True, log_path=WS_LOG_PATH,
                                         on_connect=self.start, on_close=self.close_handler,
                                         on_error=self.error_handler, on_message=self.quote_handler)
            self.fs.connect()
            log.debug("connected to fyers ws successfully.")

        except Exception as e:
            log.error(f"error setting up websocket connection to fyers : {e}")

    def get_positions(self):
        try:
            # get the current positions
            self.positions = self.fyers.positions()["netPositions"]
            # check for positions that are long
            if self.positions:
                self.positions = [
                    i for i in self.positions if i["side"] != "" and i["side"] > 0
                ]

                for position in self.positions:
                    self.to_sub.append(position["symbol"])
            log.debug(f"after appending open positions, subscription list is: {self.to_sub}")

        except Exception as e:
            log.error(f"error getting positions from fyers : {e}")

        try:
            conn = sqlite3.connect(DB_FILE)
            c = conn.cursor()
            for i in self.positions:
                try:
                    c.execute(
                        SELECT_TSL_QUERY,
                        (i["symbol"],),
                    )
                    result = c.fetchall()
                    if result:
                        i["stoploss"] = result[0][0]
                    else:
                        i["stoploss"] = i["buyAvg"] - (i["buyAvg"] * STOP_PERCENT / 100)
                        # add symbol to trailing_stoploss db:
                        c.execute(
                            INSERT_TSL_QUERY,
                            (i["symbol"], i["stoploss"]),
                        )
                        conn.commit()
                    log.debug(f"calculated and added stoploss = {i['stoploss']} for {i['symbol']}")

                except Exception as e:
                    log.error(f"error calculating and added stoploss for {i['symbol']}, {e}")
            c.close()
            conn.close()

        except Exception as e:
            log.error(f"error setting default sl for position : {e}")

    def get_holdings(self):
        try:
            # get the current holdings
            self.holdings = self.fyers.holdings()["holdings"]
            # check for holdings that are open
            if self.holdings:
                self.holdings = [i for i in self.holdings if i["remainingQuantity"] > 0]

                for holding in self.holdings:
                    self.to_sub.append(holding["symbol"])

            log.debug(f"after appending open positions, subscription list is: {self.to_sub}")

        except Exception as e:
            log.error(f"error getting positions from fyers : {e}")
        try:
            conn = sqlite3.connect(DB_FILE)
            c = conn.cursor()
            for i in self.holdings:
                try:
                    c.execute(
                        SELECT_TSL_QUERY,
                        (i["symbol"],),
                    )
                    result = c.fetchall()

                    if result:
                        i["stoploss"] = result[0][0]
                    else:
                        i["stoploss"] = i["costPrice"] - (
                                i["costPrice"] * STOP_PERCENT / 100
                        )
                        # add symbol to trailing_stoploss db:
                        c.execute(
                            INSERT_TSL_QUERY,
                            (i["symbol"], i["stoploss"]),
                        )
                        conn.commit()

                except Exception as e:
                    log.error(f"error calculating and added stoploss for {i['symbol']}, {e}")

            c.close()
            conn.close()

        except Exception as e:
            log.error(e)

    def start(self):
        # unsubscribe to any previous feeds
        if self.to_sub:
            self.fs.unsubscribe(self.to_sub)
            self.to_sub = [symbol.replace('&', '%26') for symbol in self.to_sub]

            self.fs.subscribe(symbols=self.to_sub, data_type="SymbolUpdate")
            log.debug("Subscribed to live feed.")
            self.fs.keep_running()
        else:
            log.debug("No holdings or positions. Nothing to subscribe.")
            # empty the trailing_stoploss table
            conn = sqlite3.connect(DB_FILE)
            c = conn.cursor()
            c.execute(DELETE_TSL_QUERY)
            conn.commit()
            c.close()
            conn.close()

    def quote_handler(self, quotes):
        # log.info(f'quotes received as is : {quotes}, data type {type(quotes)}')
        if quotes['type'] != 'sf':
            return

        if self.market_status() == "open":
            try:
                self.stoploss(quotes["symbol"], quotes["ltp"])
            except Exception as e:
                log.error(f"error in processing tick : {quotes}, {e}")
        else:
            self.complete()

    def stoploss(self, symbol, ltp):
        # holdings
        if self.holdings:
            for i in self.holdings:
                if symbol == i["symbol"]:
                    # check if SL breach
                    if ltp  i["stoploss"]:
                            i["stoploss"] = trailing_sl
                            conn = sqlite3.connect(DB_FILE)
                            c = conn.cursor()
                            c.execute(
                                UPDATE_TSL_QUERY,
                                (
                                    trailing_sl,
                                    i["symbol"],
                                ),
                            )
                            conn.commit()
                            c.close()
                            conn.close()

        # log.debug(f"quote received for {symbol} : {ltp}")
        # positions
        if self.positions:
            for i in self.positions:
                if symbol == i["symbol"]:
                    # check if SL breach
                    if ltp <= i["stoploss"]:
                        log.info(f'[POSITION] Stoploss Hit. for {i}, LTP is {ltp}, selling and unsubscribing to symbol')
                        # unsubscribe from the ticker
                        self.fs.unsubscribe([i["symbol"]])
                        # exit position
                        self.order.sell(position=i)

    def market_status(self):
        now = datetime.now()
        nine15 = now.replace(hour=9, minute=15, second=0, microsecond=0)
        three30 = now.replace(hour=15, minute=30, second=0, microsecond=0)
        if nine15 < now < three30:
            return "open"
        else:
            return "closed"

    def error_handler(self, *args, **kwargs):
        log.error(f"args: {args}")
        log.error(f"args: {kwargs}")

    def close_handler(self, *args, **kwargs):
        log.error(f"args: {args}")
        log.error(f"args: {kwargs}")
        if self.market_status() == "open":
            # get holdings again and start the ws
            self.get_positions()
            self.get_holdings()
            self.start()
        else:
            self.complete()
            report.push_report("🔵 Market is closed (close handler triggered).")

    def complete(self):
        if self.to_sub:
            self.fs.unsubscribe(self.to_sub)
        self.fs.close_connection()
        self.fs = None
        log.debug("trading completed for the day.")
        report.push_report("Trading completed for the day.")

@s13rw81 Could you let us know the version of API v3 used by you ?

@WWhYB2qrsx i am using the 3.0.5 version


Name: fyers-apiv3

Version: 3.0.5

Summary: Fyers trading APIs.

Home-page: [https://github.com/FyersDev/fyers-api-py](https://github.com/FyersDev/fyers-api-py)

Author: Fyers-Tech

Author-email: [[email protected]](mailto:[email protected])

License:

Location: /home/ubuntu/.local/lib/python3.10/site-packages

Requires: aiohttp, asyncio, aws-lambda-powertools, requests, websocket-client

Required-by:

ubuntu@s13rw81-oracle:~$```