Мы с тобой в прошлом диалоге писали программу на python с веб-интерфейсом для мониторинга плотностей по криптовалютам на binance и торговли через плотности. Последнее что мы сделали, у нас не отображались плотности по спотовому рынку. Я попросил тебя сделать так чтобы запрашивались книги ордеров для монет на глубину 5% от текущей рыночной цены и  обновлялись раз в 1 минуту, а текущие цены обновлялись бы раз в секунду, так  как они очень важны для оперативных данных. Вот код который мы с тобой написали для файлов python и html:

import os
import sys
import asyncio
import threading
import time
import traceback
from datetime import datetime, timezone, timedelta
from binance import AsyncClient, BinanceSocketManager, Client
from binance.exceptions import BinanceAPIException
from flask import Flask, render_template, request, redirect, url_for, jsonify
import schedule

# Параметры бота
MAX_OPEN_POSITIONS = 5       # Максимальное количество открытых позиций
POSITION_SIZE_USDT = 10      # Размер позиции в USDT
TRADING_ALLOWED = False      # Разрешена ли торговля (по умолчанию False)
monitoring_active = False    # Флаг активности мониторинга
min_density_volume = 1000    # Минимальный объём плотности в USDT
min_consumption_time = 0.1   # Минимальное время разъедания в минутах

# Инициализация глобальных переменных
selected_symbols = []        # Список выбранных пользователем символов
symbols_to_monitor = []
order_book_data = {}         # Данные книги ордеров
open_positions = {}          # Открытые позиции
density_history = {}         # История плотностей
current_price_cache = {}     # Кэш для текущих цен из WebSocket
volume_cache = {}            # Кэш для хранения объёмов
symbol_leverages = {}        # Словарь плеч для фьючерсных символов
asyncio_loop = None          # Цикл событий asyncio
asyncio_tasks = []           # Список асинхронных задач

# Функция для загрузки API-ключей из файла
def load_api_keys():
    api_key = None
    api_secret = None
    try:
        with open('binance_api.txt', 'r') as f:
            for line in f:
                if 'API_KEY' in line:
                    api_key = line.strip().split('=')[1].strip().strip("'").strip('"')
                elif 'API_SECRET' in line:
                    api_secret = line.strip().split('=')[1].strip().strip("'").strip('"')
    except FileNotFoundError:
        print("Файл binance_api.txt не найден. Пожалуйста, создайте файл с вашими API-ключами.")
        sys.exit(1)
    except Exception as e:
        print(f"Ошибка при чтении binance_api.txt: {e}")
        sys.exit(1)
    if not api_key or not api_secret:
        print("API_KEY или API_SECRET не найдены в binance_api.txt.")
        sys.exit(1)
    return api_key, api_secret

# Загрузка API-ключей
API_KEY, API_SECRET = load_api_keys()

# Инициализация клиента Binance
client = Client(API_KEY, API_SECRET)
async_client = None   # Для асинхронной работы

# Создание приложения Flask
app = Flask(__name__)

# Функция получения общих символов USDT для спота и фьючерсов
def get_common_symbols():
    try:
        spot_exchange_info = client.get_exchange_info()
        futures_exchange_info = client.futures_exchange_info()
    except Exception as e:
        print(f"Ошибка при получении информации о бирже: {e}")
        return []

    spot_symbols = {symbol['symbol'].upper() for symbol in spot_exchange_info['symbols']
                    if symbol['quoteAsset'] == 'USDT' and symbol['status'] == 'TRADING'}
    futures_symbols = {symbol['symbol'].upper() for symbol in futures_exchange_info['symbols']
                       if symbol['quoteAsset'] == 'USDT' and symbol['status'] == 'TRADING'}

    combined_symbols = []
    for spot_symbol in spot_symbols:
        base_asset = spot_symbol.replace('USDT', '')
        # Ищем соответствующий фьючерсный символ
        futures_symbol = None
        if spot_symbol in futures_symbols:
            futures_symbol = spot_symbol
        elif '1000' + base_asset + 'USDT' in futures_symbols:
            futures_symbol = '1000' + base_asset + 'USDT'
        elif base_asset.startswith('1000') and base_asset[4:] + 'USDT' in futures_symbols:
            futures_symbol = base_asset[4:] + 'USDT'
        elif '1000' + spot_symbol in futures_symbols:
            futures_symbol = '1000' + spot_symbol
        elif spot_symbol.startswith('1000') and spot_symbol[4:] in futures_symbols:
            futures_symbol = spot_symbol[4:]
        # Если нашли соответствующий фьючерсный символ, добавляем пару
        if futures_symbol:
            combined_symbols.append({'spot': spot_symbol, 'futures': futures_symbol})

    return combined_symbols

# Функция получения символов для мониторинга
def get_symbols_to_monitor():
    global selected_symbols, symbol_leverages
    symbol_leverages = {}  # Сбросим словарь плеч
    if not selected_symbols:
        return []

    pairs_to_monitor = []

    # Получаем общие символы спота и фьючерсов
    common_symbols = get_common_symbols()

    # Приводим введённые символы к верхнему регистру и удаляем пробелы
    input_symbols = [s.strip().upper() for s in selected_symbols]

    # Создаём списки базовых активов и полных пар из введённых символов
    input_bases = [s.replace('USDT', '') for s in input_symbols]
    input_full_symbols = [s if s.endswith('USDT') else s + 'USDT' for s in input_symbols]

    for pair in common_symbols:
        spot_symbol = pair['spot'].upper()
        futures_symbol = pair['futures'].upper()
        base_asset = spot_symbol.replace('USDT', '')

        # Проверяем, есть ли базовый актив или полный символ в введённых данных
        if base_asset in input_bases or spot_symbol in input_full_symbols or futures_symbol in input_full_symbols:
            pairs_to_monitor.append({'spot': spot_symbol, 'futures': futures_symbol})

    # Получаем плечи для фьючерсных символов
    futures_symbols = [pair['futures'] for pair in pairs_to_monitor]
    try:
        positions = client.futures_account()['positions']
        for pos in positions:
            symbol = pos['symbol'].upper()
            if symbol in futures_symbols:
                leverage = int(pos['leverage'])
                symbol_leverages[symbol] = leverage
    except Exception as e:
        print(f"Ошибка при получении плеча: {e}")

    # Устанавливаем плечо 1 по умолчанию для символов без позиций
    for symbol in futures_symbols:
        if symbol not in symbol_leverages:
            symbol_leverages[symbol] = 1  # Плечо по умолчанию

    print(f"Пары для мониторинга: {pairs_to_monitor}")
    return pairs_to_monitor

# Функция получения 5-минутных объёмов за последние 24 часа
def get_5m_volumes(symbol, market_type):
    key = f"{market_type}_{symbol}"
    now = datetime.now(timezone.utc)
    if key not in volume_cache or (now - volume_cache[key]['last_update']).total_seconds() > 900:
        # Обновляем данные
        end_time = int(now.timestamp() * 1000)
        start_time = end_time - 24 * 60 * 60 * 1000  # За последние 24 часа
        try:
            if market_type == 'spot':
                klines = client.get_historical_klines(
                    symbol,
                    Client.KLINE_INTERVAL_5MINUTE,
                    start_str=start_time,
                    end_str=end_time,
                    limit=288
                )
            else:
                klines = client.futures_historical_klines(
                    symbol,
                    Client.KLINE_INTERVAL_5MINUTE,
                    start_str=start_time,
                    end_str=end_time,
                    limit=288
                )
            volumes = [float(kline[7]) for kline in klines]
            volume_cache[key] = {
                'volumes': volumes,
                'last_update': now
            }
            # Отладочное сообщение
            print(f"Обновлены объёмы для {symbol} ({market_type})")
        except Exception as e:
            print(f"Ошибка при получении исторических данных для {symbol}: {e}")
            return None

    else:
        volumes = volume_cache[key]['volumes']

    return volumes

# Функция для обновления книги ордеров
def update_order_book():
    global order_book_data
    print("Обновление книги ордеров...")
    for pair in symbols_to_monitor:
        for market_type in ['spot', 'futures']:
            if market_type == 'spot':
                symbol = pair['spot']
            else:
                symbol = pair['futures']

            symbol = symbol.upper()
            try:
                if market_type == 'spot':
                    depth = client.get_order_book(symbol=symbol, limit=1000)
                else:
                    depth = client.futures_order_book(symbol=symbol, limit=1000)
                bids = [(float(b[0]), float(b[1])) for b in depth['bids']]
                asks = [(float(a[0]), float(a[1])) for a in depth['asks']]
                order_book_data[f"{market_type}_{symbol}"] = {
                    'bids': bids,
                    'asks': asks,
                    'last_update': datetime.now()
                }
                print(f"Обновлена книга ордеров для {symbol} ({market_type})")
            except Exception as e:
                print(f"Ошибка при обновлении книги ордеров для {symbol} ({market_type}): {e}")
                traceback.print_exc()

# Планировщик для обновления книги ордеров раз в минуту
def start_scheduler():
    schedule.every(1).minutes.do(update_order_book)
    while monitoring_active:
        schedule.run_pending()
        time.sleep(1)

# Функция обработки сообщений мини-текера
def process_miniticker_message(msg, market_type):
    try:
        if isinstance(msg, list):
            for item in msg:
                symbol = item['s'].upper()
                if symbol in [pair[market_type].upper() for pair in symbols_to_monitor]:
                    price = float(item['c'])
                    key = f"{market_type}_{symbol}"
                    current_price_cache[key] = {
                        'price': price,
                        'last_update': datetime.now(timezone.utc)
                    }
                    # Отладочное сообщение
                    # print(f"Обновлена текущая цена для {symbol} ({market_type}): {price}")
        elif isinstance(msg, dict) and 'e' in msg and msg['e'] == '24hrMiniTicker':
            symbol = msg['s'].upper()
            if symbol in [pair[market_type].upper() for pair in symbols_to_monitor]:
                price = float(msg['c'])
                key = f"{market_type}_{symbol}"
                current_price_cache[key] = {
                    'price': price,
                    'last_update': datetime.now(timezone.utc)
                }
                # Отладочное сообщение
                # print(f"Обновлена текущая цена для {symbol} ({market_type}): {price}")
    except Exception as e:
        print(f"Ошибка в process_miniticker_message для {market_type}: {e}")
        traceback.print_exc()

# Функция получения текущей цены из кэша
def get_current_price(symbol, market_type):
    key = f"{market_type}_{symbol}"
    if key in current_price_cache:
        price = current_price_cache[key]['price']
        return price
    else:
        # Текущая цена отсутствует в кэше
        return None

# Функция поиска плотностей
def find_top_densities():
    densities = []
    for pair in symbols_to_monitor:
        spot_symbol = pair['spot'].upper()
        futures_symbol = pair['futures'].upper()
        base_asset = spot_symbol.replace('USDT', '')

        for market_type in ['spot', 'futures']:
            if market_type == 'spot':
                symbol = spot_symbol
            else:
                symbol = futures_symbol

            data = order_book_data.get(f"{market_type}_{symbol}")
            if not data:
                print(f"Нет данных книги ордеров для {symbol} ({market_type})")
                continue

            bids = data['bids']
            asks = data['asks']

            # Получаем текущую цену
            current_price = get_current_price(symbol, market_type)
            if not current_price:
                print(f"Нет текущей цены для {symbol} ({market_type})")
                continue

            # Получаем объёмы
            volumes = get_5m_volumes(symbol, market_type)
            if not volumes:
                print(f"Нет объёмов для {symbol} ({market_type})")
                continue

            avg_5m_volume = sum(volumes) / len(volumes)

            # Фильтруем ордера в пределах 5% от текущей цены
            min_price = current_price * 0.95
            max_price = current_price * 1.05

            densities_found = []

            for side, orders in [('bid', bids), ('ask', asks)]:
                for price, qty in orders:
                    if price < min_price or price > max_price:
                        continue

                    total_value = price * qty
                    if total_value < min_density_volume:
                        continue

                    # Рассчитываем время разъедания
                    consumption_time = (total_value / avg_5m_volume) * 5  # В минутах

                    # Проверяем условие на минимальное время разъедания
                    if consumption_time < min_consumption_time:
                        continue

                    distance = (price - current_price) / current_price * 100  # В процентах

                    key_density = (symbol, market_type, price)
                    if key_density not in density_history:
                        initial_total_value = total_value
                        density_history[key_density] = {
                            'initial_total_value': initial_total_value,
                            'last_total_value': total_value
                        }
                    else:
                        initial_total_value = density_history[key_density]['initial_total_value']
                        density_history[key_density]['last_total_value'] = total_value

                    percentage_change = ((total_value - initial_total_value) / initial_total_value) * 100

                    # Получаем плечо для фьючерсного рынка
                    leverage = 1
                    if market_type == 'futures':
                        leverage = symbol_leverages.get(symbol, 1)

                    densities_found.append({
                        'symbol': base_asset,
                        'market_type': market_type,
                        'leverage': leverage,
                        'price': price,
                        'qty': qty,
                        'total_value': total_value,
                        'initial_total_value': initial_total_value,
                        'percentage_change': percentage_change,
                        'distance': distance,
                        'consumption_time': consumption_time
                    })

            print(f"Найдено {len(densities_found)} плотностей для {symbol} ({market_type})")

            # Оставляем только 2 плотности на пару (1 выше и 1 ниже текущей цены)
            densities_above = [d for d in densities_found if d['price'] > current_price]
            densities_below = [d for d in densities_found if d['price'] < current_price]

            densities_above.sort(key=lambda x: x['price'])
            densities_below.sort(key=lambda x: -x['price'])

            if densities_above:
                densities.append(densities_above[0])
            if densities_below:
                densities.append(densities_below[0])

    densities.sort(key=lambda x: (x['symbol'], x['market_type']))
    return densities

# Функция мониторинга и торговли (обновлена)
def monitor_and_trade():
    while monitoring_active:
        time.sleep(1)

# Асинхронная функция для работы с WebSocket (только для обновления текущих цен)
async def start_socket():
    global async_client
    print("Запуск асинхронных задач для обновления цен")
    async_client = await AsyncClient.create(API_KEY, API_SECRET)
    bm = BinanceSocketManager(async_client)

    tasks = []

    # Функция для запуска мини-текера
    async def start_miniticker_sockets():
        miniticker_tasks = []

        # Фьючерсы MiniTicker
        async def futures_miniticker():
            try:
                print("Запуск фьючерсного MiniTicker сокета")
                ts = bm.futures_multiplex_socket(['!miniTicker@arr'])
                async with ts as tscm:
                    while monitoring_active:
                        try:
                            res = await tscm.recv()
                            if 'data' in res:
                                msgs = res['data']
                                if isinstance(msgs, list):
                                    process_miniticker_message(msgs, 'futures')
                                else:
                                    process_miniticker_message([msgs], 'futures')
                        except Exception as e:
                            print(f"Ошибка в MiniTicker WebSocket (futures): {e}")
                            traceback.print_exc()
                            await asyncio.sleep(1)
            except Exception as e:
                print(f"Ошибка при запуске MiniTicker WebSocket (futures): {e}")
                traceback.print_exc()

        # Спот MiniTicker
        async def spot_miniticker():
            try:
                print("Запуск спотового MiniTicker сокета")
                ts = bm.multiplex_socket(['!miniTicker@arr'])
                async with ts as tscm:
                    while monitoring_active:
                        try:
                            res = await tscm.recv()
                            # Для спотового MiniTicker сообщение приходит непосредственно
                            process_miniticker_message(res, 'spot')
                        except Exception as e:
                            print(f"Ошибка в MiniTicker WebSocket (spot): {e}")
                            traceback.print_exc()
                            await asyncio.sleep(1)
            except Exception as e:
                print(f"Ошибка при запуске MiniTicker WebSocket (spot): {e}")
                traceback.print_exc()

        miniticker_tasks.append(asyncio.create_task(futures_miniticker()))
        miniticker_tasks.append(asyncio.create_task(spot_miniticker()))

        await asyncio.gather(*miniticker_tasks)

    tasks.append(asyncio.create_task(start_miniticker_sockets()))

    try:
        await asyncio.gather(*tasks)
    except Exception as e:
        print(f"Ошибка в задачах WebSocket: {e}")
        traceback.print_exc()
    finally:
        if async_client:
            await async_client.close_connection()
        print("WebSocket connections closed")

# Запуск асинхронных задач
def run_asyncio_tasks():
    global asyncio_loop, asyncio_tasks
    asyncio_loop = asyncio.new_event_loop()
    asyncio.set_event_loop(asyncio_loop)
    asyncio_tasks = [
        asyncio.ensure_future(start_socket()),
    ]
    try:
        asyncio_loop.run_until_complete(asyncio.gather(*asyncio_tasks))
    except Exception as e:
        print(f"Ошибка в основном цикле событий asyncio: {e}")
        traceback.print_exc()

# Функция остановки асинхронных задач
def stop_asyncio_tasks():
    global asyncio_loop, asyncio_tasks, monitoring_active
    print("Остановка асинхронных задач")
    monitoring_active = False  # Сбрасываем флаг активности мониторинга

    # Отменяем текущие задачи
    for task in asyncio_tasks:
        if not task.done():
            task.cancel()

    # Останавливаем цикл событий
    if asyncio_loop and asyncio_loop.is_running():
        asyncio_loop.call_soon_threadsafe(asyncio_loop.stop)
        asyncio_loop = None

    asyncio_tasks = []

# Функции открытия и закрытия позиции
def open_position(density):
    # Здесь вы можете реализовать логику открытия позиции
    print(f"Открытие позиции по {density['symbol']} ({density['market_type']})")

def close_position(symbol):
    # Здесь вы можете реализовать логику закрытия позиции
    print(f"Закрытие позиции по {symbol}")

def check_entry_conditions(density):
    # Реализуйте ваши условия для входа в позицию
    return True  # Пока всегда возвращает True

# Маршруты Flask

@app.route('/')
def index():
    return render_template('index.html',
                           densities=[],
                           open_positions=open_positions,
                           trading_allowed=TRADING_ALLOWED,
                           position_size=POSITION_SIZE_USDT,
                           max_open_positions=MAX_OPEN_POSITIONS,
                           monitored_symbols=", ".join(selected_symbols),
                           monitoring_active=monitoring_active,
                           min_density_volume=min_density_volume,
                           min_consumption_time=min_consumption_time)

@app.route('/start')
def start_trading():
    global TRADING_ALLOWED
    TRADING_ALLOWED = True
    return redirect(url_for('index'))

@app.route('/stop')
def stop_trading():
    global TRADING_ALLOWED
    TRADING_ALLOWED = False
    return redirect(url_for('index'))

@app.route('/set_position_size', methods=['POST'])
def set_position_size():
    global POSITION_SIZE_USDT
    POSITION_SIZE_USDT = float(request.form['position_size'])
    return redirect(url_for('index'))

@app.route('/set_max_positions', methods=['POST'])
def set_max_positions():
    global MAX_OPEN_POSITIONS
    MAX_OPEN_POSITIONS = int(request.form['max_positions'])
    return redirect(url_for('index'))

# Маршрут для установки параметров плотности
@app.route('/set_density_params', methods=['POST'])
def set_density_params():
    global min_density_volume, min_consumption_time
    min_density_volume = float(request.form['min_density_volume'])
    min_consumption_time = float(request.form['min_consumption_time'])
    return redirect(url_for('index'))

@app.route('/set_symbols', methods=['POST'])
def set_symbols():
    global selected_symbols, symbols_to_monitor, monitoring_active, density_history, current_price_cache
    symbols_input = request.form.get('symbols', '')
    selected_symbols = [s.strip().upper() for s in symbols_input.split(',') if s.strip()]

    # Останавливаем текущий мониторинг
    stop_asyncio_tasks()
    monitoring_active = False

    # Обновляем символы для мониторинга
    symbols_to_monitor = get_symbols_to_monitor()
    print(f"Список символов для мониторинга обновлён: {selected_symbols}")

    # Очищаем кеш плотностей и цен при перезапуске сканирования
    density_history = {}
    current_price_cache = {}
    order_book_data.clear()

    # Устанавливаем флаг активности мониторинга
    monitoring_active = True

    # Запускаем планировщик для обновления книги ордеров
    threading.Thread(target=start_scheduler).start()

    # Запускаем асинхронные задачи для обновления цен
    threading.Thread(target=run_asyncio_tasks).start()

    # Запускаем мониторинг и торговлю
    threading.Thread(target=monitor_and_trade).start()

    return redirect(url_for('index'))

@app.route('/monitor_all_usdt')
def monitor_all_usdt():
    global selected_symbols, symbols_to_monitor, monitoring_active, density_history, current_price_cache
    # Останавливаем текущий мониторинг
    stop_asyncio_tasks()
    monitoring_active = False

    # Обновляем символы для мониторинга: все общие символы USDT
    common_symbols = get_common_symbols()
    # Получаем список базовых активов
    selected_symbols = [pair['spot'].replace('USDT', '') for pair in common_symbols]

    # Обновляем символы для мониторинга
    symbols_to_monitor = get_symbols_to_monitor()
    print(f"Список символов для мониторинга обновлён: {selected_symbols}")

    # Очищаем кеш плотностей и цен при перезапуске сканирования
    density_history = {}
    current_price_cache = {}
    order_book_data.clear()

    # Устанавливаем флаг активности мониторинга
    monitoring_active = True

    # Запускаем планировщик для обновления книги ордеров
    threading.Thread(target=start_scheduler).start()

    # Запускаем асинхронные задачи для обновления цен
    threading.Thread(target=run_asyncio_tasks).start()

    # Запускаем мониторинг и торговлю
    threading.Thread(target=monitor_and_trade).start()

    # Перенаправляем на главную страницу
    return redirect(url_for('index'))

@app.route('/stop_monitoring')
def stop_monitoring():
    global monitoring_active
    stop_asyncio_tasks()
    monitoring_active = False
    return redirect(url_for('index'))

@app.route('/get_densities')
def get_densities_route():
    densities = find_top_densities()
    # Преобразуем числовые данные в float для корректной сериализации
    for d in densities:
        d['price'] = float(d['price'])
        d['qty'] = float(d['qty'])
        d['total_value'] = float(d['total_value'])
        d['distance'] = float(d['distance'])
        d['consumption_time'] = float(d['consumption_time'])
        d['initial_total_value'] = float(d['initial_total_value'])
        d['percentage_change'] = float(d['percentage_change'])
        d['leverage'] = int(d['leverage'])
    return jsonify(densities)

@app.route('/get_open_positions')
def get_open_positions_route():
    positions = []
    for symbol, position in open_positions.items():
        positions.append({
            'symbol': symbol,
            'side': position['side'],
            'quantity': float(position['quantity']),
            'entry_price': float(position['entry_price']),
            'current_price': float(position['current_price']),
            'pnl_percent': float(position['pnl_percent'])
        })
    return jsonify(positions)

@app.route('/open_position', methods=['POST'])
def handle_open_position():
    symbol = request.form['symbol']
    density = next((d for d in find_top_densities() if d['symbol'] == symbol), None)
    if density and check_entry_conditions(density):
        open_position(density)
    return redirect(url_for('index'))

@app.route('/close_position', methods=['POST'])
def handle_close_position():
    symbol = request.form['symbol']
    close_position(symbol)
    return redirect(url_for('index'))

# Запуск приложения
if __name__ == '__main__':
    # Запуск Flask-приложения
    app.run(host='0.0.0.0', port=4000)


<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Binance Trading Bot</title>
    <style>
        /* Стили */
        body {
            font-family: Arial, sans-serif;
            background-color: #121212;
            color: #ffffff;
            margin: 0;
            padding: 20px;
        }
        h1, h2 {
            text-align: center;
        }
        .controls {
            text-align: center;
            margin-bottom: 20px;
        }
        .controls form, .controls a, .controls button {
            display: inline-block;
            margin: 0 10px;
        }
        table {
            width: 95%;
            margin: 0 auto 20px;
            border-collapse: collapse;
            color: #ffffff;
        }
        th, td {
            border: 1px solid #555;
            padding: 8px;
            text-align: center;
        }
        th {
            background-color: #333333;
        }
        .status {
            font-weight: bold;
        }
        .button {
            padding: 5px 10px;
            font-size: 14px;
            text-decoration: none;
            border: none;
            border-radius: 3px;
            cursor: pointer;
        }
        .green {
            background-color: #4CAF50;
            color: white;
        }
        .red {
            background-color: #f44336;
            color: white;
        }
        .yellow {
            background-color: #ff9800;
            color: white;
        }
        .form-input {
            padding: 5px;
            font-size: 14px;
            width: 150px;
            border: 1px solid #ccc;
            border-radius: 3px;
        }
        .black-text {
            color: black !important;
        }
        .spinner {
            display: inline-block;
            width: 20px;
            height: 20px;
            border: 3px solid rgba(255, 255, 255, 0.3);
            border-radius: 50%;
            border-top-color: #ffffff;
            animation: spin 1s ease-in-out infinite;
            vertical-align: middle;
        }
        @keyframes spin {
            to { transform: rotate(360deg); }
        }
        .status-text {
            display: inline-block;
            vertical-align: middle;
            margin-left: 5px;
        }
        /* Дополнительные стили для окрашивания ячеек */
        .value-yellow {
            background-color: yellow;
            color: black;
        }
        .value-orange {
            background-color: orange;
            color: black;
        }
        .value-violet {
            background-color: #EE82EE; /* light violet */
            color: black;
        }
        .value-pink {
            background-color: pink;
            color: black;
        }
        .positive {
            background-color: green;
            color: white;
        }
        .negative {
            background-color: red;
            color: white;
        }
    </style>
</head>
<body>
    <h1>Binance Trading Bot</h1>
    <div class="controls">
        <a class="button green" href="/start">СТАРТ</a>
        <a class="button red" href="/stop">СТОП</a>
        <p class="status">Статус торговли: {{ 'Разрешена' if trading_allowed else 'Запрещена' }}</p>
    </div>
    <div class="controls">
        <form action="/set_position_size" method="post">
            Размер позиции (USDT):
            <input class="form-input" type="number" name="position_size" value="{{ position_size }}" min="1" step="any">
            <button class="button yellow" type="submit">SET</button>
        </form>
        <form action="/set_max_positions" method="post">
            Количество позиций:
            <input class="form-input" type="number" name="max_positions" value="{{ max_open_positions }}" min="1">
            <button class="button yellow" type="submit">SET</button>
        </form>
    </div>

    <!-- Форма для установки параметров плотности -->
    <div class="controls">
        <form action="/set_density_params" method="post">
            Объём плотности ($/USDT):
            <input class="form-input" type="number" name="min_density_volume" value="{{ min_density_volume }}" min="0" step="any">
            Время разъедания (мин.):
            <input class="form-input" type="number" name="min_consumption_time" value="{{ min_consumption_time }}" min="0" step="any">
            <button class="button yellow" type="submit">Установить</button>
        </form>
    </div>

    <!-- Форма для ввода символов и кнопка СТОП МОНИТОРИНГ -->
    <div class="controls">
        <form id="monitoring-form" action="/set_symbols" method="post" style="display: inline-block;">
            Монеты для мониторинга (через запятую):
            <input class="form-input" type="text" name="symbols" placeholder="BTC, ETH, DOGE" value="{{ monitored_symbols }}">
            <button class="button yellow" type="submit">MONITORING</button>
        </form>
        <button class="button yellow" onclick="location.href='/monitor_all_usdt'">Monitor ALL USDT</button>
        <a class="button red" href="/stop_monitoring">СТОП МОНИТОРИНГ</a>
        <span id="monitoring-status" style="margin-left: 10px;">
            {% if monitoring_active %}
                <span class="spinner"></span>
                <span class="status-text" style="color: green;">Мониторинг активен</span>
            {% else %}
                <span class="status-text" style="color: red;">Мониторинг не запущен</span>
            {% endif %}
        </span>
    </div>

    <h2>Плотности</h2>
    <table id="densities-table">
        <thead>
            <tr>
                <th>Символ</th>
                <th>Цена</th>
                <th>Количество</th>
                <th>Общая сумма</th>
                <th>Расстояние до цены (%)</th>
                <th>Время разъедания (мин)</th>
            </tr>
        </thead>
        <tbody>
            <!-- Данные будут загружаться динамически через JavaScript -->
        </tbody>
    </table>

    <h2>Открытые позиции</h2>
    <table id="positions-table">
        <thead>
            <tr>
                <th>Символ</th>
                <th>Сторона</th>
                <th>Количество</th>
                <th>Цена входа</th>
                <th>Текущая цена</th>
                <th>PnL (%)</th>
            </tr>
        </thead>
        <tbody>
            <!-- Данные будут загружаться динамически через JavaScript -->
        </tbody>
    </table>

    <script>
        function fetchDensities() {
            fetch('/get_densities')
                .then(response => response.json())
                .then(data => {
                    const tbody = document.querySelector('#densities-table tbody');
                    tbody.innerHTML = '';
                    data.forEach(density => {
                        const row = document.createElement('tr');

                        // Окрашивание ячейки "Общая сумма" в зависимости от значения
                        let totalValueCellClass = '';
                        if (density.total_value > 3000000) {
                            totalValueCellClass = 'value-pink';
                        } else if (density.total_value > 1000000) {
                            totalValueCellClass = 'value-violet';
                        } else if (density.total_value > 500000) {
                            totalValueCellClass = 'value-orange';
                        } else if (density.total_value > 250000) {
                            totalValueCellClass = 'value-yellow';
                        }

                        // Окрашивание ячейки "Расстояние до цены (%)"
                        let distanceCellClass = '';
                        if (density.distance > 0) {
                            distanceCellClass = 'positive';
                        } else if (density.distance < 0) {
                            distanceCellClass = 'negative';
                        }

                        // Форматирование отображения общей суммы с процентом изменения
                        const totalValueFormatted = `${Math.round(density.total_value)}$ (${density.percentage_change >= 0 ? '+' : ''}${density.percentage_change.toFixed(2)}%)`;

                        // Округляем время в минутах до 1 знака после запятой
                        const consumptionTimeFormatted = density.consumption_time.toFixed(1);

                        // Убираем лишние нули в цене
                        const priceFormatted = parseFloat(density.price).toString();

                        // Форматирование символа с учётом плеча для фьючерсов
                        let symbolDisplay = '';
                        if (density.market_type === 'spot') {
                            symbolDisplay = `${density.symbol} (спот)`;
                        } else {
                            symbolDisplay = `${density.symbol} (фьючерс Х${density.leverage})`;
                        }

                        row.innerHTML = `
                            <td>${symbolDisplay}</td>
                            <td>${priceFormatted}</td>
                            <td>${density.qty.toFixed(1)}</td>
                            <td class="${totalValueCellClass}">${totalValueFormatted}</td>
                            <td class="${distanceCellClass}">${density.distance.toFixed(4)}</td>
                            <td>${consumptionTimeFormatted}</td>
                        `;
                        tbody.appendChild(row);
                    });
                })
                .catch(error => {
                    console.error('Ошибка при получении плотностей:', error);
                });
        }

        function fetchPositions() {
            fetch('/get_open_positions')
                .then(response => response.json())
                .then(data => {
                    const tbody = document.querySelector('#positions-table tbody');
                    tbody.innerHTML = '';
                    data.forEach(position => {
                        const row = document.createElement('tr');
                        row.innerHTML = `
                            <td>${position.symbol}</td>
                            <td>${position.side}</td>
                            <td>${position.quantity.toFixed(8)}</td>
                            <td>${position.entry_price.toFixed(8)}</td>
                            <td>${position.current_price.toFixed(8)}</td>
                            <td>${position.pnl_percent.toFixed(2)}</td>
                        `;
                        tbody.appendChild(row);
                    });
                })
                .catch(error => {
                    console.error('Ошибка при получении позиций:', error);
                });
        }

        // Запускаем функции сразу после загрузки страницы
        fetchDensities();
        fetchPositions();

        // Обновляем данные каждые 5 секунд
        setInterval(fetchDensities, 5000);
        setInterval(fetchPositions, 5000);
    </script>
</body>
</html>


После последних твоих измнений, программы не запускается в консоли, сразу закрывается, вот что пишет консоль:
(trade_bot_test) user@ubuntuserver:~$ ./trade_bot_test.sh
Traceback (most recent call last):
  File "/home/user/trade_bot_test/trade_bot_test.py", line 11, in <module>
    import schedule
ModuleNotFoundError: No module named 'schedule'
(trade_bot_test) user@ubuntuserver:~$