import ccxt
import tkinter as tk
from tkinter import messagebox
import logging
from datetime import datetime
import pandas as pd

# Логирование
logging.basicConfig(filename='trading_bot.log', level=logging.INFO)

# Инициализация биржи MEXC с расширенными опциями
exchange = ccxt.mexc({
    'apiKey': 'mx0vgl9KtaKYrVqgxi',
    'secret': '067d53047cf245f0b5e6b2f9e987c4c6',
    'options': {
        'createMarketBuyOrderRequiresPrice': False,
        'recvWindow': 10000,  # Увеличение recvWindow до 10000 миллисекунд (10 секунд)
    }
})

# Функция для синхронизации времени (это можно делать периодически)
def synchronize_time():
    try:
        exchange.load_time_difference()
        logging.info("Синхронизация времени с биржей.")
    except Exception as e:
        logging.error("Ошибка синхронизации времени: %s", e)

# Синхронизируем время перед выполнением торговых операций
synchronize_time()

# Настройка для работы с MEXC
exchange.options['createMarketBuyOrderRequiresPrice'] = False

# Проверка подключения к бирже
def check_connection():
    try:
        exchange.fetch_time()
        return True
    except Exception as e:
        logging.error("Ошибка подключения к бирже: %s", e)
        return False

connected = check_connection()

# Загрузка доступных рынков
try:
    markets = exchange.load_markets()
    symbols = list(markets.keys())
    logging.info(f"Загружено {len(symbols)} символов")
except Exception as e:
    logging.error("Ошибка при загрузке рынков: %s", e)
    messagebox.showerror("Ошибка", "Не удалось загрузить рынки с биржи - проверьте подключение к сети или конфигурацию API.")
    symbols = []

# Глобальные переменные
open_positions = []
session_profit = 0.0
session_start_time = None
trading_active = False
paused = False
trade_history = []

# Функция для фильтрации символов
def filter_symbols():
    search_text = entry_search.get().lower()
    filtered_symbols = [symbol for symbol in symbols if search_text in symbol.lower()]
    menu = symbol_menu['menu']
    menu.delete(0, 'end')
    for symbol in filtered_symbols:
        menu.add_command(label=symbol, command=lambda value=symbol: selected_symbol.set(value) or update_units())
    if filtered_symbols:
        selected_symbol.set(filtered_symbols[0])
        update_units()

# Функция для обновления доступных единиц торговли
def update_units():
    symbol = selected_symbol.get()
    base, quote = symbol.split('/')
    unit_menu['menu'].delete(0, 'end')
    unit_menu['menu'].add_command(label=base, command=lambda value=base: selected_unit.set(value))
    unit_menu['menu'].add_command(label=quote, command=lambda value=quote: selected_unit.set(value))
    selected_unit.set(base)
    update_balance_display(base, quote)
    update_min_trade_volume(symbol)

# Функция для обновления информации о балансе
def update_balance_display(base, quote):
    try:
        balance = exchange.fetch_balance()
        base_balance = balance['total'].get(base, 0)
        quote_balance = balance['total'].get(quote, 0)
        label_balance.config(text=f"Баланс {base}: {base_balance:.4f}, {quote}: {quote_balance:.4f}")
    except Exception as e:
        logging.error("Ошибка получения баланса: %s", e)
        label_balance.config(text="Ошибка получения баланса")

# Функция для обновления минимального объема торговли
def update_min_trade_volume(symbol):
    try:
        market = exchange.market(symbol)
        min_volume = market['limits']['amount']['min']
        label_min_volume.config(text=f"Мин. объем для {symbol}: {min_volume}")
    except Exception as e:
        logging.error("Ошибка получения минимального объема: %s", e)
        label_min_volume.config(text="Ошибка получения минимального объема")

# Функция для обновления UI
def update_ui():
    symbol = selected_symbol.get()
    current_price = get_current_price(symbol)
    if current_price:
        label_current_price.config(text=f"Текущая цена: {current_price:.2f}")

    if session_start_time:
        elapsed_time = datetime.now() - session_start_time
        label_session_time.config(text=f"Время сессии: {str(elapsed_time).split('.')[0]}")
    
    connection_status.config(bg="green" if connected else "red")
    update_open_positions_display()
    root.after(1000, update_ui)

# Функция для получения текущей цены
def get_current_price(symbol):
    try:
        ticker = exchange.fetch_ticker(symbol)
        return ticker['last']
    except Exception as e:
        logging.error("Ошибка при получении текущей цены: %s", e)
        return None

# Функция для покупки на заданную сумму в валюте
def buy(symbol, cost):
    try:
        # Проверка на то, что сумма положительна
        if cost <= 0:
            messagebox.showerror("Ошибка", "Сумма должна быть положительной.")
            return None
        
        current_price = get_current_price(symbol)
        # Проверка, что текущая цена корректна и положительна
        if current_price is None or current_price <= 0:
            messagebox.showerror("Ошибка", "Не удалось получить корректную текущую цену.")
            return None

        # Извлечение минимального объема торговли для проверки
        market = exchange.market(symbol)
        min_volume = market['limits']['amount']['min']

        # Рассчитываем количество единиц
        amount_units = cost / current_price

        # Проверка расчета объема
        if amount_units < min_volume:
            messagebox.showerror("Ошибка", f"Минимальный объем для торговли: {min_volume}. Невозможно купить на {amount_units}.")
            return None

        # Повторная проверка объема
        if amount_units <= 0:
            messagebox.showerror("Ошибка", "Количество единиц должно быть положительным.")
            return None

        # Выполняем ордер на покупку
        order = exchange.create_order(symbol, 'market', 'buy', amount_units)
        logging.info("Покупка: %s", order)
        save_trade_state('buy', order)
        open_positions.append(order)
        log_trade('buy', order)
        return order['price']
    except Exception as e:
        logging.error("Ошибка покупки: %s", e)
        messagebox.showerror("Ошибка", f"Не удалось купить: {e}")
        return None

# Функция для продажи на заданную сумму в валюте
def sell(symbol, cost):
    try:
        # Проверка на то, что сумма положительна
        if cost <= 0:
            messagebox.showerror("Ошибка", "Сумма должна быть положительной.")
            return None

        current_price = get_current_price(symbol)
        # Проверка, что текущая цена корректна и положительна
        if current_price is None or current_price <= 0:
            messagebox.showerror("Ошибка", "Не удалось получить корректную текущую цену.")
            return None

        # Извлечение минимального объема торговли для проверки
        market = exchange.market(symbol)
        min_volume = market['limits']['amount']['min']

        # Рассчитываем количество единиц
        amount_units = cost / current_price

        # Проверка расчета объема
        if amount_units < min_volume:
            messagebox.showerror("Ошибка", f"Минимальный объем для торговли: {min_volume}. Невозможно продать {amount_units}.")
            return None

        # Повторная проверка объема
        if amount_units <= 0:
            messagebox.showerror("Ошибка", "Количество единиц должно быть положительным.")
            return None

        # Выполняем ордер на продажу
        order = exchange.create_market_sell_order(symbol, amount_units)
        logging.info("Продажа: %s", order)
        if open_positions:
            last_buy_order = open_positions.pop()
            profit = (order['price'] - last_buy_order['price']) * amount_units
            save_trade_state('sell', order, profit)
            log_trade('sell', order, profit)
        return order['price']
    except Exception as e:
        logging.error("Ошибка продажи: %s", e)
        messagebox.showerror("Ошибка", f"Не удалось продать: {e}")
        return None

# Функция для логирования сделки
def log_trade(action, order, profit=0):
    trade_info = {
        'action': action,
        'symbol': order['symbol'],
        'amount': order['amount'],
        'price': order['price'],
        'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        'profit': profit,
    }
    trade_history.append(trade_info)
    update_trade_history_display()
    export_to_excel()

# Функция для сохранения состояния торговли
def save_trade_state(action, order, profit=0):
    with open('trading_bot_state.log', 'a') as f:
        f.write(f"{action},{order['symbol']},{order['amount']},{order['price']},{profit}\n")

# Загрузка состояния торговли из логов
def load_trade_state():
    global session_profit, open_positions
    try:
        with open('trading_bot_state.log', 'r') as f:
            lines = f.readlines()
            open_positions = []
            for line in lines:
                action, symbol, amount, price, profit = line.strip().split(',')
                if action == 'buy':
                    open_positions.append({'symbol': symbol, 'amount': float(amount), 'price': float(price)})
                session_profit += float(profit)
    except FileNotFoundError:
        logging.info("Лог состояния торговли не найден.")

# Экспорт истории торгов в Excel
def export_to_excel():
    df = pd.DataFrame(trade_history)
    df.to_excel("trade_history.xlsx", index=False)

# Функция для обновления отображения истории торгов
def update_trade_history_display():
    text_history.delete(1.0, tk.END)
    for trade in trade_history:
        text_history.insert(tk.END, f"{trade['timestamp']} {trade['action'].upper()} {trade['amount']} {trade['symbol']} @ {trade['price']} Profit: {trade['profit']}\n")

# Функция для обновления открытых позиций
def update_open_positions_display():
    open_orders_text.delete(1.0, tk.END)
    for position in open_positions:
        current_profit = calculate_current_profit(position)
        open_orders_text.insert(tk.END, f"{position['symbol']} {position['amount']} @ {position['price']} Current Profit: {current_profit:.2f}\n")

# Функция для расчета текущей прибыли
def calculate_current_profit(position):
    current_price = get_current_price(position['symbol'])
    profit = (current_price - position['price']) * position['amount']
    return profit

# Функция для начала/продолжения торговли
def execute_trade():
    global session_start_time, trading_active, paused
    if paused:
        paused = False
        button_trade.config(text="Торговля активна")
        return

    try:
        if not trading_active:
            session_start_time = datetime.now()
            button_trade.config(text="Торговля активна")
            trading_active = True

            symbol = selected_symbol.get()
            cost = float(entry_amount.get())
            buy(symbol, cost)

        symbol = selected_symbol.get()
        cost = float(entry_amount.get())
        dip_threshold = float(entry_dip.get()) / 100
        rise_threshold = float(entry_rise.get()) / 100

        current_price = get_current_price(symbol)
        if current_price is None:
            return

        if not hasattr(execute_trade, "baseline_price"):
            execute_trade.baseline_price = current_price

        # Покупка при просадке
        if current_price <= execute_trade.baseline_price * (1 - dip_threshold):
            new_buy_price = buy(symbol, cost)
            if new_buy_price:
                execute_trade.baseline_price = new_buy_price

        # Продажа при росте
        if current_price >= execute_trade.baseline_price * (1 + rise_threshold):
            new_sell_price = sell(symbol, cost)
            if new_sell_price:
                execute_trade.baseline_price = new_sell_price

        label_positions.config(text=f"Открытых сделок: {len(open_positions)}")
        label_profit.config(text=f"Прибыль сессии: {session_profit:.2f}")

    except ValueError:
        messagebox.showerror("Ошибка", "Некорректный ввод. Убедитесь, что все значения заданы корректно.")

# Пауза торговли
def pause_trade():
    global paused
    if trading_active:
        paused = True
        button_trade.config(text="Торговля на паузе")

# Создание интерфейса
root = tk.Tk()
root.title("MEXC Trading Bot")

# Инициализация состояния
load_trade_state()

# Элементы интерфейса
label_search = tk.Label(root, text='Поиск символа:')
label_search.pack()
entry_search = tk.Entry(root)
entry_search.pack()
entry_search.bind('<KeyRelease>', lambda event: filter_symbols())

label_symbol = tk.Label(root, text='Символ:')
label_symbol.pack()

selected_symbol = tk.StringVar(root)
if symbols:
    selected_symbol.set(symbols[0])
    symbol_menu = tk.OptionMenu(root, selected_symbol, *symbols, command=lambda _: update_units())
    symbol_menu.pack()
else:
    messagebox.showerror("Ошибка", "Список символов пуст. Проверьте соединение с биржей.")

label_unit = tk.Label(root, text='Торговая единица:')
label_unit.pack()
selected_unit = tk.StringVar(root)
unit_menu = tk.OptionMenu(root, selected_unit, "")
unit_menu.pack()

# Отображение баланса
label_balance = tk.Label(root, text="Баланс")
label_balance.pack()

# Отображение минимального объема торговли
label_min_volume = tk.Label(root, text="Мин. объем:")
label_min_volume.pack()

# Поля для ввода параметров трейдинга
label_amount = tk.Label(root, text='Сумма (в фиатной или криптовалюте):')
label_amount.pack()
entry_amount = tk.Entry(root)
entry_amount.pack()
entry_amount.insert(tk.END, '10')  # Пример значения суммы

label_dip = tk.Label(root, text='Покупка при просадке (%):')
label_dip.pack()
entry_dip = tk.Entry(root)
entry_dip.pack()
entry_dip.insert(tk.END, '0.5')

label_rise = tk.Label(root, text='Продажа при росте (%):')
label_rise.pack()
entry_rise = tk.Entry(root)
entry_rise.pack()
entry_rise.insert(tk.END, '1')

button_trade = tk.Button(root, text="Начать торговлю", command=execute_trade)
button_trade.pack()

button_continue = tk.Button(root, text="Продолжить", command=execute_trade)
button_continue.pack()

button_pause = tk.Button(root, text="Пауза", command=pause_trade)
button_pause.pack()

label_current_price = tk.Label(root, text="Текущая цена: ")
label_current_price.pack()

label_positions = tk.Label(root, text="Открытых сделок: 0")
label_positions.pack()

label_profit = tk.Label(root, text="Прибыль сессии: 0.00")
label_profit.pack()

label_session_time = tk.Label(root, text="Время сессии: 00:00:00")
label_session_time.pack()

# Поле для истории ордеров
label_trade_history = tk.Label(root, text="История ордеров:")
label_trade_history.pack()
text_history = tk.Text(root, height=10, width=50)
text_history.pack()

# Поле для текущих ордеров
label_open_orders = tk.Label(root, text="Текущие ордера:")
label_open_orders.pack()
open_orders_text = tk.Text(root, height=10, width=50)
open_orders_text.pack()

connection_status = tk.Label(root, text="Статус подключения", width=20, bg="red")
connection_status.pack(anchor='se')

update_ui()
root.mainloop() ERROR:root:Ошибка при получении текущей цены: mexc does not have market symbol 
ERROR:root:Ошибка при получении текущей цены: mexc does not have market symbol