Traceback (most recent call last):
  File "C:\Users\user\PycharmProjects\RATE AND UI\test_UI.py", line 167, in <module>
    app.run()
  File "C:\Users\user\PycharmProjects\RATE AND UI\test_UI.py", line 162, in run
    self.root.mainloop()
AttributeError: 'FundingRateApp' object has no attribute 'root' такая ошибка в коде import tkinter as tk
from tkinter import ttk
import asyncio
import threading
import ccxt
import logging
from main import main as fetch_funding_rates_main  # Импорт функции main из первого скрипта

# Настройка логгирования
logging.basicConfig(level=logging.DEBUG)


class FundingRateApp:
    def init(self):
        # Инициализация интерфейса tkinter
        self.root = tk.Tk()
        self.root.title('Funding Rate Comparison')
        self.configure_interface()

        # Запуск цикла событий в отдельном потоке
        self.loop = asyncio.new_event_loop()
        self.thread = threading.Thread(target=self.run_event_loop, daemon=True)
        self.thread.start()

    def configure_interface(self):
        self.frame = ttk.Frame(self.root, padding='10')
        self.frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        self.scrollbar = ttk.Scrollbar(self.frame, orient=tk.VERTICAL)
        self.scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))

        self.listbox = tk.Listbox(self.frame, yscrollcommand=self.scrollbar.set, width=100, height=20)
        self.listbox.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        self.scrollbar.config(command=self.listbox.yview)

        self.button = ttk.Button(self.root, text='Load Data', command=self.start_loading)
        self.button.grid(row=1, column=0, pady=10)

        self.listbox.bind('<<ListboxSelect>>', lambda event: self.show_prices())

    def run_event_loop(self):
        asyncio.set_event_loop(self.loop)
        self.loop.run_forever()

    def start_loading(self):
        asyncio.run_coroutine_threadsafe(self.load_data(), self.loop)

    def parse_data(self, data):
        parsed_data = {}
        for line in data:
            try:
                parts = line.split(', ')
                if len(parts) != 3:
                    logging.warning(f"Skipping line due to incorrect format: {line}")
                    continue

                symbol_part, rate_part, exchange_part = parts
                symbol = symbol_part.split(': ')[1]
                rate = float(rate_part.split(': ')[1])
                exchange = exchange_part.split(': ')[1]
                base_currency = symbol.split(' / ')[-1].split(':')[0]
                pair = symbol.replace(':USDT', '')

                if 'USDT' in base_currency:
                    if pair not in parsed_data:
                        parsed_data[pair] = []
                    parsed_data[pair].append((rate, exchange))
            except (IndexError, ValueError) as e:
                logging.error(f"Error processing line: {line}, error: {e}")
                continue
        return parsed_data

    def filter_data(self, parsed_data):
        filtered = []
        for pair, rates in parsed_data.items():
            if len(rates) > 1:
                for i in range(len(rates)):
                    for j in range(i + 1, len(rates)):
                        rate_i, exchange_i = rates[i]
                        rate_j, exchange_j = rates[j]
                        if abs(rate_i - rate_j) >= 0.002:
                            filtered.append(f'{pair}: {rate_i}(EX: {exchange_i}), {rate_j}(EX: {exchange_j})')
        return filtered

    async def fetch_data(self):
        data = await fetch_funding_rates_main()
        logging.debug(f"Fetched data: {data}")
        parsed_data = self.parse_data(data)
        logging.debug(f"Parsed data: {parsed_data}")
        return self.filter_data(parsed_data)

    def display_data(self, filtered_data):
        self.listbox.delete(0, tk.END)
        for item in filtered_data:
            self.listbox.insert(tk.END, item)

    async def load_data(self):
        filtered_data = await self.fetch_data()
        self.display_data(filtered_data)

    def show_prices(self):
        if self.listbox.curselection():
            selected_item = self.listbox.get(self.listbox.curselection()[0])
            pair = selected_item.split(":")[0]
            exchanges = [part.split("EX: ")[1].strip(')') for part in selected_item.split(", ") if "EX: " in part]
            self.create_price_window(pair, exchanges)
        else:
            logging.info("No item selected")

    def create_price_window(self, pair, exchanges):
        new_window = tk.Toplevel(self.root)
        new_window.title(f'Prices for {pair}')

        label = ttk.Label(new_window, text=f'Prices for {pair}')
        label.grid(row=0, column=0, pady=10)

        prices_frame = ttk.Frame(new_window)
        prices_frame.grid(row=1, column=0, pady=10)

        price_labels = {exchange: ttk.Label(prices_frame, text="") for exchange in exchanges}
        for idx, (exchange, label) in enumerate(price_labels.items()):
            ttk.Label(prices_frame, text=exchange).grid(row=idx, column=0, padx=10, pady=5)
            label.grid(row=idx, column=1, padx=10, pady=5)

        async def update_prices():
            while True:
                prices = await self.fetch_current_prices(pair, exchanges)
                if prices:
                    for exchange, price in prices.items():
                        if isinstance(price, dict):
                            price_text = f"Long: {price['long']} / Short: {price['short']}"
                        else:
                            price_text = price
                        price_labels[exchange].config(text=price_text)
                await asyncio.sleep(2)

        asyncio.run_coroutine_threadsafe(update_prices(), self.loop)
        buy_button = ttk.Button(new_window, text='Buy', command=lambda: self.buy_on_exchanges(pair, exchanges))
        buy_button.grid(row=2, column=0, pady=10)

    async def fetch_current_prices(self, pair, exchanges):
        prices = {}
        for ex in exchanges:
            try:
                logging.debug(f"Fetching current prices for {pair} on {ex}")
                exchange_class = getattr(ccxt, ex.lower())
                exchange = exchange_class({
                    'enableRateLimit': True,
                    'options': {'defaultType': 'future'},
                })
                market = f'{pair}:USDT' if ' / ' not in pair else f'{pair.replace("/", "")}T'
                ticker = await self.loop.run_in_executor(None, exchange.fetch_ticker, market)
                prices[ex] = {'long': ticker['ask'], 'short': ticker['bid']}
            except Exception as e:
                logging.error(f"Error fetching prices for {pair} on {ex}: {str(e)}")
                prices[ex] = f"Error: {str(e)}"
        return prices

    def buy_on_exchanges(self, pair, exchanges):
        logging.info(f"Buying {pair} on selected exchanges {exchanges} (Placeholder)")

    def run(self):
        self.root.mainloop()


# Запуск приложения
if __name__ == '__main__':
    app = FundingRateApp()
    app.run()
 что деать пояснения на русском