по прежнему появляются пустые графики графики , может быть отсеевать их по отсутсвию объема сделок в ней или что-то в этом роде? Подумай над этим сейчас пришлю весь скрипт # bot.py

import logging
import requests
import pandas as pd
import numpy as np  # Добавлен импорт numpy
import mplfinance as mpf
from datetime import datetime, timedelta, timezone
import telegram
from telegram import InputMediaPhoto
import asyncio
import config
from io import BytesIO
from ta.trend import MACD, SMAIndicator
from ta.momentum import RSIIndicator, StochasticOscillator
from ta.volatility import BollingerBands
import os
import glob

# Настройка логирования
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler("bot.log"),
        logging.StreamHandler()
    ]
)

MAX_TG_MESSAGE_LENGTH = 4000  # Немного меньше, чем лимит Telegram
MEDIA_GROUP_LIMIT_PER_SECOND = 20  # Настройте по необходимости

# Директория для сохранения графиков
GRAPHS_DIR = 'graphs'

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


# Функция для очистки предыдущих графиков
def clean_previous_graphs():
    try:
        files = glob.glob(os.path.join(GRAPHS_DIR, '*_72h.png'))
        for file in files:
            os.remove(file)
        logging.info(f"Удалено {len(files)} предыдущих графиков.")
    except Exception as e:
        logging.error(f"Ошибка при удалении предыдущих графиков: {e}")


# Функция для получения списка всех фьючерсных пар USDT на Binance
def get_future_symbols():
    try:
        logging.info("Запрос списка фьючерсных пар USDT от Binance.")
        url = 'https://fapi.binance.com/fapi/v1/exchangeInfo'
        response = requests.get(url)
        response.raise_for_status()
        data = response.json()
        symbols = [s['symbol'] for s in data['symbols']
                   if s['quoteAsset'] == 'USDT' and s['contractType'] == 'PERPETUAL']
        logging.info(f"Найдено {len(symbols)} фьючерсных пар USDT.")
        return symbols
    except requests.exceptions.RequestException as e:
        logging.error(f"Ошибка при запросе списка символов: {e}")
        raise
    except Exception as e:
        logging.error(f"Неизвестная ошибка при обработке списка символов: {e}")
        raise


# Функция для получения исторических данных от Binance Futures
def get_historical_klines(symbol, interval, hours):
    try:
        logging.info(f"Запрос данных для {symbol} с интервалом {interval} за последние {hours} часов.")
        end_time = datetime.now(timezone.utc)
        start_time = end_time - timedelta(hours=hours)
        start_timestamp = int(start_time.timestamp() * 1000)
        end_timestamp = int(end_time.timestamp() * 1000)
        url = 'https://fapi.binance.com/fapi/v1/klines'
        params = {
            'symbol': symbol,
            'interval': interval,
            'startTime': start_timestamp,
            'endTime': end_timestamp,
            'limit': 1000  # Максимальное количество данных
        }
        response = requests.get(url, params=params)
        response.raise_for_status()
        data = response.json()
        if not data:
            logging.warning(f"Нет данных для символа {symbol}.")
            return None
        df = pd.DataFrame(data, columns=[
            'Open time', 'Open', 'High', 'Low', 'Close', 'Volume',
            'Close time', 'Quote asset volume', 'Number of trades',
            'Taker buy base asset volume', 'Taker buy quote asset volume', 'Ignore'
        ])
        df['Open time'] = pd.to_datetime(df['Open time'], unit='ms')
        df['Close time'] = pd.to_datetime(df['Close time'], unit='ms')
        df[['Open', 'High', 'Low', 'Close', 'Volume']] = df[['Open', 'High', 'Low', 'Close', 'Volume']].astype(float)
        logging.info(f"Данные успешно получены для {symbol} (строк: {len(df)}).")
        return df[['Open time', 'Open', 'High', 'Low', 'Close', 'Volume']]
    except requests.exceptions.RequestException as e:
        logging.error(f"Ошибка при запросе данных для {symbol}: {e}")
        return None
    except Exception as e:
        logging.error(f"Неизвестная ошибка при обработке данных для {symbol}: {e}")
        return None


# Функция для вычисления технических индикаторов
def compute_indicators(df):
    try:
        logging.info("Вычисление технических индикаторов.")

        # RSI
        rsi = RSIIndicator(close=df['Close'], window=14)
        df['RSI'] = rsi.rsi()

        # MACD
        macd = MACD(close=df['Close'])
        df['MACD'] = macd.macd()
        df['MACD_Signal'] = macd.macd_signal()
        df['MACD_Diff'] = macd.macd_diff()

        # Скользящие средние (SMA50)
        sma50 = SMAIndicator(close=df['Close'], window=50)
        df['SMA50'] = sma50.sma_indicator()

        # Полосы Боллинджера
        bb = BollingerBands(close=df['Close'], window=20, window_dev=2)
        df['BB_High'] = bb.bollinger_hband()
        df['BB_Low'] = bb.bollinger_lband()

        # Стохастический осциллятор
        stochastic = StochasticOscillator(high=df['High'], low=df['Low'], close=df['Close'], window=14, smooth_window=3)
        df['Stoch_%K'] = stochastic.stoch()
        df['Stoch_%D'] = stochastic.stoch_signal()

        logging.info("Технические индикаторы успешно вычислены.")
        return df
    except Exception as e:
        logging.error(f"Ошибка при вычислении индикаторов: {e}")
        raise


# Функция для определения направления на основе индикаторов
def analyze_indicators(df):
    try:
        logging.info("Анализ индикаторов для определения направления движения.")
        latest = df.iloc[-1]

        signals = {
            'RSI': None,
            'MACD': None,
            'Скользящая средняя (SMA50)': None,
            'Полосы Боллинджера': None,
            'Стохастический осциллятор': None
        }

        # RSI
        if latest['RSI'] > 70:
            signals['RSI'] = 'Медвежий сигнал'
        elif latest['RSI'] < 30:
            signals['RSI'] = 'Бычий сигнал'

        # MACD
        if latest['MACD'] > latest['MACD_Signal']:
            signals['MACD'] = 'Бычий сигнал'
        elif latest['MACD'] < latest['MACD_Signal']:
            signals['MACD'] = 'Медвежий сигнал'

        # Скользящая средняя (SMA50)
        if latest['SMA50'] > latest['Close']:
            signals['Скользящая средняя (SMA50)'] = 'Медвежий сигнал'
        elif latest['SMA50'] < latest['Close']:
            signals['Скользящая средняя (SMA50)'] = 'Бычий сигнал'

        # Полосы Боллинджера
        if latest['Close'] > latest['BB_High']:
            signals['Полосы Боллинджера'] = 'Медвежий сигнал'
        elif latest['Close'] < latest['BB_Low']:
            signals['Полосы Боллинджера'] = 'Бычий сигнал'

        # Стохастический осциллятор
        if latest['Stoch_%K'] > latest['Stoch_%D']:
            signals['Стохастический осциллятор'] = 'Бычий сигнал'
        elif latest['Stoch_%K'] < latest['Stoch_%D']:
            signals['Стохастический осциллятор'] = 'Медвежий сигнал'

        # Подсчет сигналов
        bullish = sum(1 for signal in signals.values() if signal == 'Бычий сигнал')
        bearish = sum(1 for signal in signals.values() if signal == 'Медвежий сигнал')
        total = bullish + bearish

        if total == 0:
            direction = "Нейтрально"
            probability = "N/A"
        else:
            if bullish > bearish:
                direction = "Бычий тренд"
                probability = f"{(bullish / total) * 100:.2f}% вероятность"
            elif bearish > bullish:
                direction = "Медвежий тренд"
                probability = f"{(bearish / total) * 100:.2f}% вероятность"
            else:
                direction = "Нейтрально"
                probability = "50% вероятность"

        logging.info(f"Определенное направление: {direction} с вероятностью {probability}.")
        return direction, probability

    except Exception as e:
        logging.error(f"Ошибка при анализе индикаторов: {e}")
        raise


# Функция для построения графика
def plot_candlestick(df, symbol):
    try:
        logging.info(f"Построение графика свечей для {symbol}.")

        if df.empty:
            logging.warning(f"Пустой DataFrame для {symbol}. Пропуск построения графика.")
            return None

        # Проверка наличия достаточного количества данных
        if df.shape[0] < 10:
            logging.warning(f"Недостаточно данных для построения графика {symbol}. (строк: {df.shape[0]})")
            return None

        logging.info(f"Данные для построения графика {symbol}:\n{df.tail()}")

        df = df.copy()
        df.set_index('Open time', inplace=True)
        mc = mpf.make_marketcolors(up='g', down='r', inherit=True)
        s = mpf.make_mpf_style(marketcolors=mc)
        filename = os.path.join(GRAPHS_DIR, f"{symbol}_72h.png")

        # Построение свечей
        mpf.plot(
            df,
            type='candle',
            style=s,
            title=f'{symbol} - Последние 72 часа',
            ylabel='Цена (USDT)',
            volume=True,
            savefig=filename,
            tight_layout=True,
            returnfig=False,
            show_nontrading=False,
            figsize=(12, 8)
        )

        logging.info(f"График сохранен как {filename}.")
        return filename
    except Exception as e:
        logging.error(f"Ошибка при построении графика для {symbol}: {e}")
        return None


# Функция для отправки отчета в Telegram
async def send_telegram_report(token, chat_id, qualifying_symbols):
    try:
        if not qualifying_symbols:
            message = "Отчет по фьючерсным парам USDT:\nНет монет с вероятностью продолжения тренда 80% и более."
            logging.info("Отправка сообщения о том, что подходящих монет не найдено.")
            bot = telegram.Bot(token=token)
            await bot.send_message(chat_id=chat_id, text=message)
            return

        # Подготовка медиа-групп
        media_group = []
        for symbol_info in qualifying_symbols:
            symbol = symbol_info['symbol']
            direction = symbol_info['direction']
            probability = symbol_info['probability']
            image_path = symbol_info['image']

            if image_path is None:
                logging.warning(f"Нет данных изображения для {symbol}. Пропуск.")
                continue

            # Создание подписи для графика
            caption = (
                f"📈 Монета: {symbol}\n"
                f"🔺 Направление: {direction}\n"
                f"🔢 Вероятность: {probability}"
            )

            # Добавление изображения и подписи в медиа-группу
            try:
                with open(image_path, 'rb') as photo_file:
                    media_group.append(InputMediaPhoto(photo_file, caption=caption))
            except Exception as e:
                logging.error(f"Ошибка при открытии изображения {image_path}: {e}")
                continue

            # Проверка, достигло ли количество медиа в группе лимита (10)
            if len(media_group) == 10:
                logging.info(f"Отправка группы из {len(media_group)} графиков в Telegram.")
                bot = telegram.Bot(token=token)
                await bot.send_media_group(chat_id=chat_id, media=media_group)
                media_group = []
                await asyncio.sleep(1)  # Добавляем задержку в 1 секунду между отправками

        # Отправка оставшихся графиков
        if media_group:
            logging.info(f"Отправка последней группы из {len(media_group)} графиков в Telegram.")
            bot = telegram.Bot(token=token)
            await bot.send_media_group(chat_id=chat_id, media=media_group)
            media_group = []
            await asyncio.sleep(1)  # Добавляем задержку

        # Очистка директории графиков после отправки
        try:
            logging.info("Удаление отправленных графиков.")
            files = glob.glob(os.path.join(GRAPHS_DIR, '*_72h.png'))
            for file in files:
                os.remove(file)
            logging.info(f"Удалено {len(files)} графиков.")
        except Exception as e:
            logging.error(f"Ошибка при удалении графиков: {e}")

        logging.info("Отчет успешно отправлен в Telegram.")
    except telegram.error.RetryAfter as e:
        logging.error(
            f"Ошибка при отправке отчета в Telegram: Flood control exceeded. Retry after {e.retry_after} seconds.")
        await asyncio.sleep(e.retry_after)  # Ожидание указанного времени перед повторной попыткой
        await send_telegram_report(token, chat_id, qualifying_symbols)  # Повторная отправка отчета
    except Exception as e:
        logging.error(f"Ошибка при отправке отчета в Telegram: {e}")


# Основная функция
async def main():
    try:
        # Очистка предыдущих графиков перед запуском
        clean_previous_graphs()

        symbols = get_future_symbols()
        qualifying_symbols = []

        for symbol in symbols:
            df = get_historical_klines(symbol, '1h', 72)
            if df is None or len(df) < 50:  # Убедиться, что данных достаточно для SMA50
                logging.warning(f"Недостаточно данных для анализа {symbol}. Пропуск.")
                continue

            df = compute_indicators(df)
            direction, probability = analyze_indicators(df)

            # Проверка вероятности продолжения тренда
            if probability != "N/A":
                try:
                    prob_value = float(probability.split('%')[0])
                except ValueError:
                    logging.warning(f"Некорректное значение вероятности для {symbol}: {probability}. Пропуск.")
                    continue

                if prob_value >= 80.0:
                    logging.info(f"Монета {symbol} соответствует критерию (prob={probability}).")

                    # Построение графика
                    image_path = plot_candlestick(df, symbol)
                    if image_path is None:
                        logging.warning(f"График для {symbol} не создан. Пропуск.")
                        continue

                    # Добавление в список квалифицированных монет
                    qualifying_symbols.append({
                        'symbol': symbol,
                        'direction': direction,
                        'probability': probability,
                        'image': image_path
                    })

        if qualifying_symbols:
            # Отправка отчетных графиков в Telegram
            logging.info(f"Отправка отчета с {len(qualifying_symbols)} монетами.")
            await send_telegram_report(config.TELEGRAM_TOKEN, config.CHAT_ID, qualifying_symbols)
        else:
            logging.info("Нет монет, соответствующих критериям для отчета.")

        logging.info("Процесс завершен успешно.")
    except Exception as e:
        logging.error(f"Процесс завершился с ошибкой: {e}")


if __name__ == '__main__':
    asyncio.run(main())