import tkinter as tk
from tkinter import ttk, messagebox
import threading
import time
import logging
from datetime import datetime
from threading import Lock
from typing import Dict, Any
import os
import queue
from pybit.unified_trading import HTTP

# Создание директории для логов, если она не существует
log_directory = "logs"
if not os.path.exists(log_directory):
    os.makedirs(log_directory)

# Настройка логирования с использованием абсолютного пути и кодировки utf-8
logging.basicConfig(
    filename=os.path.join(log_directory, f'trading_bot_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log'),
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    encoding='utf-8'
)

class TradingBot:
    def __init__(self):
        self.running = False
        self.session = None
        self.MIN_ORDER_SIZE = 0.001
        self.active_buy_orders = {}
        self.active_sell_orders = {}
        self.orders_lock = Lock()
        self._shutdown_event = threading.Event()
        self.gui = None
        self.params = {}
        self.symbol_info = {}
        self.position_size = 0

    def set_gui(self, gui):
        """Установка ссылки на GUI"""
        self.gui = gui

    def initialize_session(self, api_key: str, api_secret: str, use_testnet: bool = False, exchange: str = "Bybit") -> bool:
        """Инициализация сессии с биржей"""
        try:
            if exchange.lower() == "bybit":
                self.session = HTTP(
                    testnet=use_testnet,
                    api_key=api_key,
                    api_secret=api_secret
                )
            else:
                # Здесь можно добавить инициализацию для других бирж (MEXC, Binance, Huobi)
                # Например, с использованием различных библиотек или API
                raise NotImplementedError(f"Выбор биржи '{exchange}' еще не реализован.")
            
            # Проверка подключения и учетных данных
            server_time = self.session.get_server_time()
            logging.info(f"Сессия успешно инициализирована. Время сервера: {server_time}")
            if self.gui:
                self.gui.queue.put((f"Сессия успешно инициализирована. Время сервера: {server_time}", False))
            return True
        except NotImplementedError as nie:
            logging.error(str(nie))
            if self.gui:
                self.gui.queue.put((str(nie), True))
            raise
        except Exception as e:
            logging.error(f"Ошибка при инициализации сессии: {str(e)}")
            if self.gui:
                self.gui.queue.put((f"Ошибка при инициализации сессии: {str(e)}", True))
            raise Exception(f"Ошибка при инициализации сессии: {str(e)}")

    def validate_inputs(self, pair: str, grid_lower: float, grid_upper: float, 
                       margin: float, quantity: float) -> bool:
        """Улучшенная валидация входных данных"""
        if not pair or not isinstance(pair, str):
            raise ValueError("Неверный формат торговой пары")

        if grid_upper <= grid_lower:
            raise ValueError("Верхний уровень сетки должен быть больше нижнего")

        if quantity < self.MIN_ORDER_SIZE:
            raise ValueError(f"Размер ордера меньше минимального: {self.MIN_ORDER_SIZE}")

        if margin <= 0:
            raise ValueError("Маржа должна быть положительным числом")

        # Проверка существования пары на бирже
        try:
            markets = self.session.get_symbols_list()
            available_pairs = [market['symbol'] for market in markets['result']['list']]
            if pair not in available_pairs:
                raise ValueError(f"Торговая пара {pair} не найдена на бирже")
        except Exception as e:
            raise ValueError(f"Ошибка проверки торговой пары: {str(e)}")

        return True

    def check_balance(self, required_margin: float) -> bool:
        """Улучшенная проверка баланса"""
        if required_margin <= 0:
            raise ValueError("Требуемая маржа должна быть положительным числом")

        try:
            balance_response = self.session.get_wallet_balance(accountType="UNIFIED", coin="USDT")
            available_balance = float(balance_response['result']['list'][0]['availableBalance'])

            if available_balance < required_margin:
                raise ValueError(
                    f"Недостаточно средств. Требуется: {required_margin}, "
                    f"Доступно: {available_balance}"
                )
            logging.info(f"Доступный баланс: {available_balance} USDT")
            if self.gui:
                self.gui.queue.put((f"Доступный баланс: {available_balance} USDT", False))
            return True
        except Exception as e:
            logging.error(f"Ошибка проверки баланса: {str(e)}")
            if self.gui:
                self.gui.queue.put((f"Ошибка проверки баланса: {str(e)}", True))
            raise

    def get_current_price(self, symbol: str) -> float:
        """Получение текущей цены"""
        try:
            ticker_response = self.session.get_tickers(category="linear", symbol=symbol)
            current_price = float(ticker_response['result']['list'][0]['lastPrice'])
            logging.info(f"Текущая цена {symbol}: {current_price}")
            return current_price
        except Exception as e:
            logging.error(f"Ошибка получения текущей цены: {str(e)}")
            if self.gui:
                self.gui.queue.put((f"Ошибка получения текущей цены: {str(e)}", True))
            raise

    def place_order(self, symbol: str, side: str, qty: float, price: float = None, 
                   order_type="Limit", params=None) -> Dict[str, Any]:
        """Размещение ордера на бирже"""
        if params is None:
            params = {}
        for attempt in range(3):  # Попытки переподключения
            try:
                order_params = {
                    "category": "linear",
                    "symbol": symbol,
                    "side": side.capitalize(),  # 'Buy' или 'Sell'
                    "orderType": order_type,
                    "qty": str(qty),
                    "timeInForce": "GoodTillCancel",
                }

                if order_type == "Limit":
                    if price is None:
                        raise ValueError("Цена должна быть указана для Limit ордера.")
                    order_params["price"] = str(price)
                elif order_type == "Market":
                    pass  # Цена не нужна для Market ордера
                else:
                    raise ValueError(f"Неизвестный тип ордера: {order_type}")

                # Добавляем дополнительные параметры, если они есть
                order_params.update(params)

                response = self.session.place_order(**order_params)
                order = response['result']

                if 'orderId' not in order:
                    raise Exception(f"Не удалось разместить ордер: {order.get('ret_msg', 'Нет сообщения')}")

                # Сохранение ордера в активные
                with self.orders_lock:
                    if side.lower() == 'buy':
                        self.active_buy_orders[order['orderId']] = {
                            'symbol': symbol,
                            'price': price,
                            'quantity': qty
                        }
                    else:
                        self.active_sell_orders[order['orderId']] = {
                            'symbol': symbol,
                            'price': price,
                            'quantity': qty
                        }

                logging.info(f"Размещен ордер: {side.capitalize()} {qty} @ {price if price else 'Market'}")
                if self.gui:
                    self.gui.queue.put((f"Размещен ордер: {side.capitalize()} {qty} @ {price if price else 'Market'}", False))
                return order

            except Exception as e:
                logging.error(f"Ошибка размещения ордера (попытка {attempt + 1}/3): {str(e)}")
                if attempt < 2:
                    time.sleep(2 ** attempt)  # Экспоненциальная задержка
                    continue
                if self.gui:
                    self.gui.queue.put((f"Ошибка размещения ордера: {str(e)}", True))
                raise

    def run_bot(self, **params):
        """Основная логика работы бота"""
        self.running = True
        self.params = params
        try:
            # Валидация входных данных
            self.validate_inputs(
                params['pair'],
                params['grid_lower'],
                params['grid_upper'],
                params['margin'],
                0  # Позже будет рассчитано
            )

            # Проверка баланса
            self.check_balance(params['margin'])

            # Установка плеча
            try:
                self.session.set_leverage(
                    category="linear",
                    symbol=params['pair'],
                    buyLeverage=str(params['leverage']),
                    sellLeverage=str(params['leverage'])
                )
                logging.info(f"Установлено плечо: {params['leverage']} для пары {params['pair']}")
                if self.gui:
                    self.gui.queue.put((f"Установлено плечо: {params['leverage']} для пары {params['pair']}", False))
            except Exception as e:
                logging.error(f"Не удалось установить плечо: {str(e)}")
                if self.gui:
                    self.gui.queue.put((f"Не удалось установить плечо: {str(e)}", True))

            # Получение текущей цены
            current_price = self.get_current_price(params['pair'])

            # Расчет начального количества
            initial_qty = (params['margin'] * params['leverage']) / current_price
            initial_qty = round(initial_qty, 8)  # Округление до 8 знаков
            logging.info(f"Расчитанное начальное количество: {initial_qty}")
            self.position_size += initial_qty

            # Размещение начальной рыночной покупки
            initial_order = self.place_order(
                symbol=params['pair'],
                side='Buy',
                qty=initial_qty,
                order_type="Market"
            )
            logging.info(f"Выполнен начальный рыночный ордер покупки: {initial_order}")
            # Ожидание исполнения ордера может потребоваться здесь

            # Получение фактической цены покупки
            filled_qty = float(initial_order.get('cumExecQty', '0'))
            if filled_qty < initial_qty:
                logging.warning(f"Не всю позицию удалось купить. Куплено: {filled_qty} из {initial_qty}")
                # Здесь можно реализовать дополнительную логику для докупки
                # Для простоты предполагаем, что весь ордер исполнен
            filled_price = float(initial_order.get('avgPrice', current_price))
            logging.info(f"Фактическая цена покупки: {filled_price}")

            # Размещение сетки ордеров на продажу (Take-Profit)
            grid_orders = params['grid_orders']
            grid_step = (params['grid_upper'] - params['grid_lower']) / grid_orders
            sell_qty = initial_qty / grid_orders
            sell_qty = round(sell_qty, 8)

            for i in range(grid_orders):
                sell_price = filled_price + (i + 1) * grid_step
                sell_order = self.place_order(
                    symbol=params['pair'],
                    side='Sell',
                    qty=sell_qty,
                    price=round(sell_price, 2),  # Округление до 2 знаков, измените при необходимости
                    order_type="Limit"
                )
                logging.info(f"Размещен Take-Profit ордер на продажу: {sell_order}")

            # Запуск мониторинга исполнения ордеров
            self.monitor_thread = threading.Thread(target=self.monitor_orders, daemon=True)
            self.monitor_thread.start()

        except Exception as e:
            logging.error(f"Ошибка в работе бота: {str(e)}")
            if self.gui:
                self.gui.queue.put((f"Ошибка: {str(e)}", True))
        finally:
            if not self.running:
                logging.info("Бот остановлен")
                if self.gui:
                    self.gui.queue.put(("Бот остановлен", False))

    def monitor_orders(self):
        """Мониторинг исполнения ордеров и размещение новых ордеров"""
        while self.running and not self._shutdown_event.is_set():
            try:
                # Проверка исполненных ордеров на продажу
                filled_sell_orders = self.session.get_orders(
                    category="linear",
                    symbol=self.params['pair'],
                    orderStatus="Filled",
                    limit=50
                )
                for order in filled_sell_orders['result']['list']:
                    order_id = order['orderId']
                    with self.orders_lock:
                        if order_id in self.active_sell_orders:
                            # Удаление исполненного ордера из активных
                            sell_order = self.active_sell_orders.pop(order_id)
                            logging.info(f"Take-Profit ордер исполнен: {order_id}")

                            # Размещение нового усредняющего ордера на покупку ниже текущей цены
                            new_buy_price = sell_order['price'] * (1 - self.params['avg_order_percent'] / 100)
                            new_buy_qty = sell_order['quantity']
                            new_buy_price = round(new_buy_price, 2)  # Округление до 2 знаков

                            buy_order = self.place_order(
                                symbol=self.params['pair'],
                                side='Buy',
                                qty=new_buy_qty,
                                price=new_buy_price,
                                order_type="Limit"
                            )
                            logging.info(f"Размещен усредняющий ордер на покупку: {buy_order}")

                # Проверка исполненных усредняющих ордеров на покупку
                filled_buy_orders = self.session.get_orders(
                    category="linear",
                    symbol=self.params['pair'],
                    orderStatus="Filled",
                    limit=50
                )
                for order in filled_buy_orders['result']['list']:
                    order_id = order['orderId']
                    with self.orders_lock:
                        if order_id in self.active_buy_orders:
                            # Удаление исполненного ордера из активных
                            buy_order = self.active_buy_orders.pop(order_id)
                            logging.info(f"Усредняющий ордер на покупку исполнен: {order_id}")

                            # Размещение нового Take-Profit ордера на продажу выше текущей цены
                            new_sell_price = buy_order['price'] * (1 + self.params['avg_take_profit_percent'] / 100)
                            new_sell_qty = buy_order['quantity']
                            new_sell_price = round(new_sell_price, 2)  # Округление до 2 знаков

                            sell_order = self.place_order(
                                symbol=self.params['pair'],
                                side='Sell',
                                qty=new_sell_qty,
                                price=new_sell_price,
                                order_type="Limit"
                            )
                            logging.info(f"Размещен новый Take-Profit ордер на продажу: {sell_order}")

                time.sleep(5)  # Интервал проверок
            except Exception as e:
                logging.error(f"Ошибка при мониторинге ордеров: {str(e)}")
                if self.gui:
                    self.gui.queue.put((f"Ошибка при мониторинге ордеров: {str(e)}", True))
                time.sleep(5)

    def stop_bot(self):
        """Грациозная остановка бота"""
        if not self.running:
            return

        self.running = False
        self._shutdown_event.set()

        try:
            with self.orders_lock:
                # Отмена активных ордеров на продажу
                for order_id, order_details in list(self.active_sell_orders.items()):
                    try:
                        self.session.cancel_order(
                            category="linear",
                            symbol=order_details['symbol'],
                            orderId=order_id
                        )
                        logging.info(f"Ордер на продажу {order_id} отменен")
                        if self.gui:
                            self.gui.queue.put((f"Ордер на продажу {order_id} отменен", False))
                        del self.active_sell_orders[order_id]
                    except Exception as e:
                        logging.error(f"Ошибка отмены ордера на продажу {order_id}: {str(e)}")
                        if self.gui:
                            self.gui.queue.put((f"Ошибка отмены ордера на продажу {order_id}: {str(e)}", True))

                # Отмена активных ордеров на покупку
                for order_id, order_details in list(self.active_buy_orders.items()):
                    try:
                        self.session.cancel_order(
                            category="linear",
                            symbol=order_details['symbol'],
                            orderId=order_id
                        )
                        logging.info(f"Ордер на покупку {order_id} отменен")
                        if self.gui:
                            self.gui.queue.put((f"Ордер на покупку {order_id} отменен", False))
                        del self.active_buy_orders[order_id]
                    except Exception as e:
                        logging.error(f"Ошибка отмены ордера на покупку {order_id}: {str(e)}")
                        if self.gui:
                            self.gui.queue.put((f"Ошибка отмены ордера на покупку {order_id}: {str(e)}", True))

            logging.info("Все активные ордера отменены")
            if self.gui:
                self.gui.queue.put(("Все активные ордера отменены", False))

        except Exception as e:
            logging.error(f"Ошибка при остановке бота: {str(e)}")
            if self.gui:
                self.gui.queue.put((f"Ошибка при остановке бота: {str(e)}", True))
        finally:
            logging.info("Бот остановлен")
            if self.gui:
                self.gui.queue.put(("Бот остановлен", False))

class TradingBotGUI(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.master = master
        self.bot = TradingBot()
        self.bot.set_gui(self)  # Установка ссылки на GUI в боте
        self.bot_thread = None
        self.queue = queue.Queue()  # Инициализация очереди для коммуникации между потоками
        self.setup_gui()
        self.setup_validation()
        self.master.protocol("WM_DELETE_WINDOW", self.on_closing)  # Обработка закрытия окна

    def setup_validation(self):
        """Настройка валидации полей ввода"""
        def validate_float(value):
            if value == "":
                return True
            try:
                float(value)
                return True
            except ValueError:
                return False

        def validate_int(value):
            if value == "":
                return True
            try:
                int(value)
                return True
            except ValueError:
                return False

        float_vcmd = (self.register(validate_float), '%P')
        int_vcmd = (self.register(validate_int), '%P')

        # Применение валидации к полям
        self.leverage_entry.config(validate="key", validatecommand=int_vcmd)
        self.grid_lower_entry.config(validate="key", validatecommand=float_vcmd)
        self.grid_upper_entry.config(validate="key", validatecommand=float_vcmd)
        self.margin_entry.config(validate="key", validatecommand=float_vcmd)
        self.stop_loss_entry.config(validate="key", validatecommand=float_vcmd)  # Не используется в текущей логике
        self.take_profit_entry.config(validate="key", validatecommand=float_vcmd)  # Не используется в текущей логике
        self.avg_order_percent_entry.config(validate="key", validatecommand=float_vcmd)
        self.avg_take_profit_percent_entry.config(validate="key", validatecommand=float_vcmd)
        self.grid_orders_entry.config(validate="key", validatecommand=int_vcmd)

    def update_status_threadsafe(self, message: str, is_error: bool = False):
        """Потокобезопасное обновление статуса в GUI"""
        self.status_label.config(
            text=f"Статус: {message}",
            foreground="red" if is_error else "black"
        )

    def validate_all_inputs(self) -> bool:
        """Проверка всех полей ввода"""
        required_fields = [
            (self.api_key_entry, "API Key"),
            (self.api_secret_entry, "API Secret"),
            (self.pair_entry, "Trading Pair"),
            (self.leverage_entry, "Leverage"),
            (self.grid_lower_entry, "Lower Grid Level"),
            (self.grid_upper_entry, "Upper Grid Level"),
            (self.margin_entry, "Margin"),
            (self.avg_order_percent_entry, "% Below for Averaging Order"),
            (self.avg_take_profit_percent_entry, "% Profit for New Order"),
            (self.grid_orders_entry, "Number of Grid Orders"),
        ]

        for entry, name in required_fields:
            if not entry.get().strip():
                messagebox.showerror("Ошибка", f"Поле '{name}' обязательно для заполнения")
                return False

        # Проверка корректности числовых значений и диапазонов
        try:
            leverage = int(self.leverage_entry.get())
            if leverage <= 0:
                raise ValueError("Плечо должно быть больше 0.")

            grid_lower = float(self.grid_lower_entry.get())
            grid_upper = float(self.grid_upper_entry.get())
            if grid_upper <= grid_lower:
                raise ValueError("Верхний уровень сетки должен быть больше нижнего.")

            margin = float(self.margin_entry.get())
            if margin <= 0:
                raise ValueError("Маржа должна быть положительным числом.")

            avg_order_percent = float(self.avg_order_percent_entry.get())
            if not (0 < avg_order_percent <= 100):
                raise ValueError("% ниже для докупки должен быть в диапазоне (0, 100].")

            avg_take_profit_percent = float(self.avg_take_profit_percent_entry.get())
            if not (0 < avg_take_profit_percent < 100):
                raise ValueError("% прибыли по новому ордеру должен быть в диапазоне (0, 100).")

            grid_orders = int(self.grid_orders_entry.get())
            if grid_orders <= 0:
                raise ValueError("Количество сеток должно быть положительным числом.")

        except ValueError as ve:
            messagebox.showerror("Ошибка", str(ve))
            return False
        except Exception:
            messagebox.showerror("Ошибка", "Пожалуйста, введите корректные числовые значения.")
            return False

        return True

    def setup_gui(self):
        frame = ttk.Frame(self.master, padding="10")
        frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        # API ключи
        ttk.Label(frame, text="API Key:").grid(row=0, column=0, sticky=tk.W, pady=2)
        self.api_key_entry = ttk.Entry(frame, width=50, show="*")
        self.api_key_entry.grid(row=0, column=1, pady=2)

        ttk.Label(frame, text="API Secret:").grid(row=1, column=0, sticky=tk.W, pady=2)
        self.api_secret_entry = ttk.Entry(frame, width=50, show="*")
        self.api_secret_entry.grid(row=1, column=1, pady=2)

        # Чекбокс для отображения API ключей
        self.show_keys_var = tk.BooleanVar()
        self.show_keys_check = ttk.Checkbutton(
            frame,
            text="Показать API ключи",
            variable=self.show_keys_var,
            command=self.toggle_api_keys
        )
        self.show_keys_check.grid(row=2, column=1, sticky=tk.W)

        # Параметры торговли
        entries = [
            ("Торговая пара (например, BTCUSDT):", "pair_entry", "BTCUSDT", 20),
            ("Плечо:", "leverage_entry", "10", 10),
            ("Нижний уровень сетки:", "grid_lower_entry", "1.1", 20),
            ("Верхний уровень сетки:", "grid_upper_entry", "2.0", 20),
            ("% ниже для докупки:", "avg_order_percent_entry", "5", 10),
            ("% прибыли по новому ордеру:", "avg_take_profit_percent_entry", "10", 10),
            ("Количество сеток:", "grid_orders_entry", "10", 10),
            ("Общая маржа позиции (USDT):", "margin_entry", "1000", 20),
        ]

        for i, (label, attr, default, width) in enumerate(entries, start=3):
            ttk.Label(frame, text=label).grid(row=i, column=0, sticky=tk.W, pady=2)
            entry = ttk.Entry(frame, width=width)
            entry.grid(row=i, column=1, pady=2, sticky=tk.W)
            if default:
                entry.insert(0, default)
            setattr(self, attr, entry)

        # Кнопки управления
        self.start_button = ttk.Button(frame, text="Запустить бота", command=self.start_bot)
        self.start_button.grid(row=len(entries)+3, column=0, pady=10)

        self.stop_button = ttk.Button(frame, text="Остановить бота", command=self.stop_bot, state=tk.DISABLED)
        self.stop_button.grid(row=len(entries)+3, column=1, pady=10)

        # Статус бота
        self.status_label = ttk.Label(frame, text="Статус: Ожидание")
        self.status_label.grid(row=len(entries)+4, column=0, columnspan=2, pady=5)

        # Настройка растягивания
        for i in range(len(entries) + 5):
            frame.rowconfigure(i, weight=1)
        frame.columnconfigure(1, weight=1)

    def toggle_api_keys(self):
        """Переключение отображения API ключей"""
        if self.show_keys_var.get():
            self.api_key_entry.config(show="")
            self.api_secret_entry.config(show="")
        else:
            self.api_key_entry.config(show="*")
            self.api_secret_entry.config(show="*")

    def start_bot(self):
        try:
            if self.bot.running:
                messagebox.showwarning("Предупреждение", "Бот уже запущен.")
                return

            if not self.validate_all_inputs():
                return

            # Сбор данных из полей ввода
            params = {
                'pair': self.pair_entry.get().upper(),
                'leverage': int(self.leverage_entry.get()),
                'grid_lower': float(self.grid_lower_entry.get()),
                'grid_upper': float(self.grid_upper_entry.get()),
                'avg_order_percent': float(self.avg_order_percent_entry.get()),
                'avg_take_profit_percent': float(self.avg_take_profit_percent_entry.get()),
                'grid_orders': int(self.grid_orders_entry.get()),
                'margin': float(self.margin_entry.get()),
                'use_testnet': False,  # Можно добавить опцию для тестовой сети
                'exchange': "Bybit"  # По умолчанию Bybit. При необходимости можно добавить выбор
            }

            # Инициализация сессии с обработкой исключений
            try:
                self.bot.initialize_session(
                    self.api_key_entry.get(),
                    self.api_secret_entry.get(),
                    use_testnet=params['use_testnet'],
                    exchange=params['exchange']
                )
            except Exception as e:
                raise Exception(str(e))

            # Запуск бота в отдельном потоке
            self.bot_thread = threading.Thread(
                target=self.bot.run_bot,
                kwargs=params,
                daemon=True  # Поток завершится при закрытии программы
            )
            self.bot_thread.start()

            # Обновление GUI
            self.start_button.config(state=tk.DISABLED)
            self.stop_button.config(state=tk.NORMAL)
            self.update_status_threadsafe("Работает")

        except Exception as e:
            messagebox.showerror("Ошибка", str(e))
            logging.error(f"Ошибка запуска бота: {str(e)}")
            self.update_status_threadsafe(f"Ошибка: {str(e)}", is_error=True)
            self.start_button.config(state=tk.NORMAL)
            self.stop_button.config(state=tk.DISABLED)

    def stop_bot(self):
        try:
            self.bot.stop_bot()

            if self.bot_thread and self.bot_thread.is_alive():
                self.bot_thread.join(timeout=5)  # Ждём завершения потока

            self.start_button.config(state=tk.NORMAL)
            self.stop_button.config(state=tk.DISABLED)
            self.update_status_threadsafe("Остановлен")

        except Exception as e:
            messagebox.showerror("Ошибка", f"Ошибка при остановке бота: {str(e)}")
            logging.error(f"Ошибка при остановке бота: {str(e)}")
            self.update_status_threadsafe(f"Ошибка: {str(e)}", is_error=True)

    def on_closing(self):
        """Обработка закрытия окна"""
        if self.bot.running:
            if messagebox.askokcancel("Выход", "Бот всё ещё работает. Вы действительно хотите выйти?"):
                self.stop_bot()
                self.master.destroy()
        else:
            self.master.destroy()

    def process_queue(self):
        """Обработка очереди сообщений из потока бота"""
        try:
            while True:
                message, is_error = self.queue.get_nowait()
                self.update_status_threadsafe(message, is_error)
        except queue.Empty:
            pass
        self.master.after(100, self.process_queue)

    def run(self):
        self.process_queue()  # Запуск обработки очереди
        self.master.mainloop()

if __name__ == "__main__":
    root = tk.Tk()
    root.title("Сеточный Торговый Бот для Bybit")
    gui = TradingBotGUI(master=root)
    gui.run() найди ошибки