evgrmn / tmatic

Tmatic is a cryptocurrency platform designed for automated trading on the Bitmex, Bybit and Deribit crypto exchanges.
https://www.tmatic.org
GNU General Public License v3.0
25 stars 6 forks source link

When computer's local clock is not synchronized causing error, than Tmatic shouldn't go further, i.g., to load trading history #241

Closed nikolayromenskiy closed 2 months ago

nikolayromenskiy commented 3 months ago

2024-06-08 15:03:20,856 - api.bybit.ws - INFO - Bybit - Websocket closed 2024-06-08 15:03:20,857 - api.bybit.agent - INFO - Sending get_instruments_info() - category - spot 2024-06-08 15:03:20,859 - api.bybit.agent - INFO - Sending get_instruments_info() - category - inverse 2024-06-08 15:03:20,860 - api.bybit.agent - INFO - Sending get_instruments_info() - category - option 2024-06-08 15:03:20,862 - api.bybit.agent - INFO - Sending get_instruments_info() - category - linear 2024-06-08 15:03:21,383 - api.bybit.agent - INFO - Sending open_orders() - parameters - {'openOnly': 0, 'limit': 50, 'category': 'spot', 'cursor': 'no'} 2024-06-08 15:03:21,384 - api.bybit.agent - INFO - Sending open_orders() - parameters - {'openOnly': 0, 'limit': 50, 'category': 'linear', 'settleCoin': 'USDT', 'cursor': 'no'} 2024-06-08 15:03:22,040 - api.bybit.agent - INFO - Sending open_orders() - parameters - {'openOnly': 0, 'limit': 50, 'category': 'linear', 'settleCoin': 'USDT', 'cursor': 'ce655e2e-02da-45ed-a3fd-13861dd5f4ae%3A1717858200151%2Caa2205dd-6474-4199-9540-67c7e709a1d7%3A1716477047327'} 2024-06-08 15:03:22,261 - api.bybit.ws - INFO - Connecting to websocket 2024-06-08 15:03:22,262 - api.bybit.agent - INFO - Sending get_uid_wallet_type() 2024-06-08 15:03:22,262 - api.bybit.agent - INFO - Sending get_wallet_balance() - accountType - UNIFIED 2024-06-08 15:03:22,265 - api.bybit.pybit._websocket_stream - INFO - WebSocket Unified V5 attempting connection... 2024-06-08 15:03:22,266 - api.bybit.agent - INFO - Sending get_positions() - category - linear - settlCurrency - USDT 2024-06-08 15:03:22,266 - api.bybit.pybit._websocket_stream - INFO - WebSocket Unified V5 (Auth) attempting connection... 2024-06-08 15:03:22,971 - api.bybit.agent - INFO - Sending get_wallet_balance() - accountType - CONTRACT 2024-06-08 15:03:23,712 - websocket - INFO - Websocket connected 2024-06-08 15:03:23,712 - api.bybit.pybit._websocket_stream - INFO - WebSocket Unified V5 (Auth) connected 2024-06-08 15:03:23,740 - websocket - INFO - Websocket connected 2024-06-08 15:03:23,742 - api.bybit.pybit._websocket_stream - INFO - WebSocket Unified V5 connected 2024-06-08 15:03:23,742 - api.bybit.ws - INFO - ws subscription - orderbook_stream - category - linear - symbol - ('BTCUSDT', 'linear', 'Bybit') 2024-06-08 15:03:23,742 - api.bybit.ws - INFO - ws subscription - ticker_stream - category - linear - symbol - ('BTCUSDT', 'linear', 'Bybit') 2024-06-08 15:03:23,751 - api.bybit.agent - INFO - Sending get_executions() - category - spot - startTime - 2024-06-05 08:04:40+00:00 2024-06-08 15:03:23,751 - api.bybit.agent - INFO - Sending get_executions() - category - inverse - startTime - 2024-06-05 08:04:40+00:00 2024-06-08 15:03:23,753 - api.bybit.agent - INFO - Sending get_executions() - category - option - startTime - 2024-06-05 08:04:40+00:00 2024-06-08 15:03:23,753 - api.bybit.agent - INFO - Sending get_executions() - category - linear - startTime - 2024-06-05 08:04:40+00:00 ___Unexpected Bybit error: Exception Traceback (most recent call last): File "/home/rmn/tmatic_240608/api/bybit/errors.py", line 16, in decorator return method(*args, **kwargs) File "/home/rmn/tmatic_240608/api/bybit/ws.py", line 416, in _on_message self.callback(message) File "/home/rmn/tmatic_240608/api/bybit/pybit/_websocket_stream.py", line 458, in _handle_incoming_message self._process_auth_message(message) File "/home/rmn/tmatic_240608/api/bybit/pybit/_websocket_stream.py", line 402, in _process_auth_message raise Exception( Exception: Authorization for Unified V5 (Auth) failed. Please check your API keys and resync your system time. Raw error: {'success': False, 'ret_msg': 'Params Error', 'op': 'auth', 'conn_id': 'cno3fi5daugt7mgdqvtg-2yz96'} 2024-06-08 15:03:23,955 - api.bybit.errors - ERROR - Exception - Authorization for Unified V5 (Auth) failed. Please check your API keys and resync your system time. Raw error: {'success': False, 'ret_msg': 'Params Error', 'op': 'auth', 'conn_id': 'cno3fi5daugt7mgdqvtg-2yz96'} 2024-06-08 15:03:23,956 - websocket - ERROR - error from callback <function _WebSocketManager._connect.. at 0x782840198dc0>: 'WebSocket' object has no attribute 'name' 2024-06-08 15:03:23,989 - api.bybit.agent - INFO - Sending get_executions() - category - linear - startTime - 2024-06-05 08:04:40+00:00 2024-06-08 15:03:24,171 - websocket - ERROR - error from callback <function _WebSocketManager._connect.. at 0x782840198d30>: 'WebSocket' object has no attribute 'name' 2024-06-08 15:03:24,171 - websocket - INFO - tearing down on exception 'WebSocket' object has no attribute 'name' 2024-06-08 15:03:24,402 - api.bybit.agent - INFO - Bybit - loading trading history, startTime=2024-06-05 08:04:40+00:00, received: 1 records. 2024-06-08 15:03:24,602 - common.init - INFO - Robot EMI=Btc1. Adding to 'robots' with STATUS='NOT DEFINED' 2024-06-08 15:03:24,602 - common.init - INFO - Robot EMI=Btc5. Adding to 'robots' with STATUS='NOT DEFINED' __Tmatic: Bybit API is still pinging, but the websocket is already closed __Tmatic: Bybit API is still pinging, but the websocket is already closed

evgrmn commented 2 months ago

Fixed in the commit 75d1f36. When loading the trading history in the file api/bybit/agent.py in the trading_history() function, the sign of correct exit from the thread has been changed, explicitly specified as “success” at the exit point from get_in_thread(). Thus, if the get_in_thread() function does not complete due to a Bybit API error, the thread completion flag will remain by default as None, which will be regarded by the program as an unsuccessful attempt to load the trading history, and therefore will be a signal to completely reload the data from Bybit.

See comments in the code below:

def trading_history(self, histCount: int, time=None) -> list:
    if time:
        trade_history = []
        utc = datetime.now(timezone.utc)
        if utc - time > timedelta(days=729):
            self.logger.info(
                "Bybit only allows you to query trading history for the last 2 years. Check the History.ini file."
            )
            time = utc - timedelta(days=729)
            self.logger.info("Time changed to " + str(time))
        startTime = service.time_converter(time)
        limit = min(100, histCount)

        def get_in_thread(category, startTime, limit, success, num):
            nonlocal trade_history
            cursor = "no"
            while cursor:
                self.logger.info(
                    "Sending get_executions() - category - "
                    + category
                    + " - startTime - "
                    + str(service.time_converter(startTime / 1000))
                )
                result = self.session.get_executions(
                    category=category,
                    startTime=startTime,
                    limit=limit,
                    cursor=cursor,
                )
                cursor = result["result"]["nextPageCursor"]
                res = result["result"]["list"]
                if isinstance(result["result"]["list"], list):
                    for row in res:
                        row["symbol"] = (row["symbol"], category, self.name)
                        row["execID"] = row["execId"]
                        row["orderID"] = row["orderId"]
                        row["category"] = category
                        row["lastPx"] = float(row["execPrice"])
                        row["leavesQty"] = float(row["leavesQty"])
                        row["transactTime"] = service.time_converter(
                            time=int(row["execTime"]) / 1000, usec=True
                        )
                        row["commission"] = float(row["feeRate"])
                        if row["orderLinkId"]:
                            row["clOrdID"] = row["orderLinkId"]
                        row["price"] = float(row["execPrice"])
                        if category == "spot":
                            row["settlCurrency"] = (row["feeCurrency"], self.name)
                        else:
                            row["settlCurrency"] = self.Instrument[
                                row["symbol"]
                            ].settlCurrency
                        row["lastQty"] = float(row["execQty"])
                        row["market"] = self.name
                        if row["execType"] == "Funding":
                            if row["side"] == "Sell":
                                row["lastQty"] = -row["lastQty"]
                        row["execFee"] = float(row["execFee"])
                    trade_history += res
                    ####################################################
                    # success[num] is explicitly specified as “success”
                    ####################################################
                    success[num] = "success"
                else:
                    return

    while startTime < service.time_converter(datetime.now(tz=timezone.utc)):
        threads, success = [], []
        for category in self.categories:
            ####################################################
            # success[num] defaults to None
            ####################################################
            success.append(None)
            t = threading.Thread(
                target=get_in_thread,
                args=(category, startTime, limit, success, len(success) - 1),
            )
            threads.append(t)
            t.start()
        [thread.join() for thread in threads]
        ####################################################
        # All threads must succeed, otherwise None is returned
        ####################################################
        for s in success:
            if not s:
                return
        message = (
            "Bybit - loading trading history, startTime="
            + str(service.time_converter(startTime / 1000))
            + ", received: "
            + str(len(trade_history))
            + " records."
        )
        self.logger.info(message)
        if len(trade_history) > histCount:
            break
        startTime += 604800000  # +7 days
    trade_history.sort(key=lambda x: x["transactTime"])

    return trade_history