import numpy as np
import pandas as pd
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from datetime import datetime
import pickle
import MetaTrader5 as mt5
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans

def connect_to_metatrader():
    if not mt5.initialize():
        print(f"Ошибка инициализации MetaTrader5: {mt5.last_error()}")
        return False
    print(f"MetaTrader5 успешно инициализирован. Терминал: {mt5.terminal_info()}")
    return True

def get_eurusd_data(bars_count=5000):
    eurusd_rates = mt5.copy_rates_from_pos("EURUSD", mt5.TIMEFRAME_H1, 0, bars_count)
    if eurusd_rates is None:
        print(f"Ошибка получения данных: {mt5.last_error()}")
        return None
    df = pd.DataFrame(eurusd_rates)
    df['time'] = pd.to_datetime(df['time'], unit='s')
    print(f"Получено {len(df)} баров EURUSD H1")
    return df

def calc_rsi(series, period=14):
    """Расчет RSI"""
    delta = series.diff()
    gain = delta.where(delta > 0, 0).rolling(window=period).mean()
    loss = -delta.where(delta < 0, 0).rolling(window=period).mean()
    rs = gain / loss
    return 100 - (100 / (1 + rs))

def add_indicators(df):
    """
    Добавление индикаторов, разделенных на три группы:
    - ценовые
    - временные
    - объемные
    """
    # --- ЦЕНОВЫЕ ИНДИКАТОРЫ ---
    # Тренд
    df['ema_9'] = df['close'].ewm(span=9, adjust=False).mean()
    df['ema_21'] = df['close'].ewm(span=21, adjust=False).mean()
    df['ema_50'] = df['close'].ewm(span=50, adjust=False).mean()
    
    # Пересечения EMA
    df['ema_cross_9_21'] = (df['ema_9'] > df['ema_21']).astype(int)
    df['ema_cross_21_50'] = (df['ema_21'] > df['ema_50']).astype(int)
    
    # Волатильность
    df['atr_14'] = df['high'].rolling(14).max() - df['low'].rolling(14).min()
    df['range'] = df['high'] - df['low']
    df['range_ratio'] = df['range'] / df['range'].rolling(14).mean()
    
    # Импульс
    df['rsi_14'] = calc_rsi(df['close'], 14)
    df['momentum'] = df['close'] - df['close'].shift(10)
    
    # Свечные характеристики
    df['body_size'] = abs(df['close'] - df['open']) / (df['high'] - df['low'])
    df['upper_shadow'] = (df['high'] - df[['open', 'close']].max(axis=1)) / df['range']
    df['lower_shadow'] = (df[['open', 'close']].min(axis=1) - df['low']) / df['range']
    
    # --- ВРЕМЕННЫЕ ИНДИКАТОРЫ ---
    # Час и день недели
    df['hour'] = df['time'].dt.hour
    df['day_of_week'] = df['time'].dt.dayofweek
    
    # Циклические признаки времени
    df['hour_sin'] = np.sin(2 * np.pi * df['hour'] / 24)
    df['hour_cos'] = np.cos(2 * np.pi * df['hour'] / 24)
    df['day_sin'] = np.sin(2 * np.pi * df['day_of_week'] / 7)
    df['day_cos'] = np.cos(2 * np.pi * df['day_of_week'] / 7)
    
    # Сезонность
    df['month'] = df['time'].dt.month
    df['week_of_year'] = df['time'].dt.isocalendar().week
    df['month_sin'] = np.sin(2 * np.pi * df['month'] / 12)
    df['month_cos'] = np.cos(2 * np.pi * df['month'] / 12)
    
    # Сессии (европейская, американская, азиатская)
    df['european_session'] = ((df['hour'] >= 7) & (df['hour'] < 16)).astype(int)
    df['american_session'] = ((df['hour'] >= 13) & (df['hour'] < 22)).astype(int)
    df['asian_session'] = ((df['hour'] >= 0) & (df['hour'] < 9)).astype(int)
    
    # --- ОБЪЕМНЫЕ ИНДИКАТОРЫ ---
    # Базовые объемные индикаторы
    df['tick_volume'] = df['tick_volume'].astype(float)  # Обеспечиваем тип float
    df['volume_change'] = df['tick_volume'].pct_change(1)
    df['volume_ma_14'] = df['tick_volume'].rolling(14).mean()
    df['rel_volume'] = df['tick_volume'] / df['volume_ma_14']
    
    # Объем относительно диапазона
    df['volume_per_range'] = df['tick_volume'] / df['range']
    
    # Аккумуляция/распределение
    df['ad'] = 0.0
    close = df['close'].values
    high = df['high'].values
    low = df['low'].values
    volume = df['tick_volume'].values
    
    for i in range(1, len(df)):
        money_flow_mult = ((close[i] - low[i]) - (high[i] - close[i])) / (high[i] - low[i]) if high[i] != low[i] else 0
        money_flow_volume = money_flow_mult * volume[i]
        df.loc[i, 'ad'] = df.loc[i-1, 'ad'] + money_flow_volume
    
    # Объемный импульс
    df['volume_momentum'] = df['tick_volume'] - df['tick_volume'].shift(5)
    df['volume_rsi'] = calc_rsi(df['tick_volume'], 14)
    
    return df

def create_price_patterns(df, lookback=5):
    """Создание паттернов последовательностей цены"""
    # Метки роста/падения
    df['price_change'] = df['close'].diff()
    df['binary_label'] = (df['price_change'] > 0).astype(int)
    
    # Лагированные метки
    for i in range(1, lookback + 1):
        df[f'binary_lag_{i}'] = df['binary_label'].shift(i)
    
    # Ключевые паттерны
    df['rise_after_rise'] = ((df['binary_lag_1'] == 1) & (df['binary_lag_2'] == 1)).astype(int)
    df['fall_after_rise'] = ((df['binary_lag_1'] == 0) & (df['binary_lag_2'] == 1)).astype(int)
    df['fall_after_fall'] = ((df['binary_lag_1'] == 0) & (df['binary_lag_2'] == 0)).astype(int)
    df['rise_after_fall'] = ((df['binary_lag_1'] == 1) & (df['binary_lag_2'] == 0)).astype(int)
    
    # Тройные паттерны
    df['triple_rise'] = ((df['binary_lag_1'] == 1) & (df['binary_lag_2'] == 1) & (df['binary_lag_3'] == 1)).astype(int)
    df['triple_fall'] = ((df['binary_lag_1'] == 0) & (df['binary_lag_2'] == 0) & (df['binary_lag_3'] == 0)).astype(int)
    
    # Закодированные паттерны (бинарные числа)
    df['pattern_3bar'] = (df['binary_lag_1'] * 4 + df['binary_lag_2'] * 2 + df['binary_lag_3'] * 1)
    
    return df

def prepare_features_grouped(df, lookback=5):
    """
    Подготовка признаков, разделенных на три группы
    """
    # Добавление индикаторов
    df = add_indicators(df)
    df = create_price_patterns(df, lookback)
    
    # Удаление NaN
    df.dropna(inplace=True)
    
    # Метки для прогноза
    df['price_change'] = df['close'].diff()
    df['binary_label'] = (df['price_change'] > 0).astype(int)
    df['forecast_label'] = df['binary_label'].shift(-1)
    df.dropna(inplace=True)
    
    # --- ГРУППИРОВКА ПРИЗНАКОВ ---
    # Ценовые признаки
    price_features = [
        'ema_9', 'ema_21', 'ema_50', 'ema_cross_9_21', 'ema_cross_21_50',
        'atr_14', 'range', 'range_ratio', 'rsi_14', 'momentum',
        'body_size', 'upper_shadow', 'lower_shadow',
        'rise_after_rise', 'fall_after_rise', 'fall_after_fall', 'rise_after_fall',
        'triple_rise', 'triple_fall', 'pattern_3bar'
    ]
    
    # Временные признаки
    time_features = [
        'hour_sin', 'hour_cos', 'day_sin', 'day_cos',
        'month_sin', 'month_cos', 'european_session', 
        'american_session', 'asian_session'
    ]
    
    # Объемные признаки
    volume_features = [
        'tick_volume', 'volume_change', 'volume_ma_14', 'rel_volume',
        'volume_per_range', 'ad', 'volume_momentum', 'volume_rsi'
    ]
    
    # Проверка наличия всех признаков в датафрейме
    all_features = price_features + time_features + volume_features
    for feature in all_features:
        if feature not in df.columns:
            print(f"Предупреждение: признак {feature} отсутствует в датафрейме")
    
    # Получение групп признаков
    price_data = df[price_features].values
    time_data = df[time_features].values
    volume_data = df[volume_features].values
    
    # Масштабирование данных
    price_scaler = StandardScaler()
    time_scaler = StandardScaler()
    volume_scaler = StandardScaler()
    
    scaled_price = price_scaler.fit_transform(price_data)
    scaled_time = time_scaler.fit_transform(time_data)
    scaled_volume = volume_scaler.fit_transform(volume_data)
    
    # Объединение всех признаков
    all_scaled_data = np.hstack((scaled_price, scaled_time, scaled_volume))
    
    # Метки
    labels = df['binary_label'].values
    forecast_labels = df['forecast_label'].values
    
    # Группированные признаки и скейлеры
    feature_groups = {
        'price': {'data': scaled_price, 'scaler': price_scaler, 'features': price_features},
        'time': {'data': scaled_time, 'scaler': time_scaler, 'features': time_features},
        'volume': {'data': scaled_volume, 'scaler': volume_scaler, 'features': volume_features},
        'all': {'data': all_scaled_data, 'features': all_features}
    }
    
    return feature_groups, labels, forecast_labels, df

def create_state_clusters(feature_groups, n_clusters_per_group=3):
    """
    Создает кластеры состояний для каждой группы признаков
    """
    group_clusters = {}
    kmeans_models = {}
    
    # Для каждой группы признаков создаем кластеры
    for group_name, group_data in feature_groups.items():
        if group_name != 'all':  # Пропускаем общую группу
            print(f"Создание {n_clusters_per_group} кластеров для группы {group_name}...")
            
            # Применяем KMeans для кластеризации
            kmeans = KMeans(n_clusters=n_clusters_per_group, random_state=42, n_init=10)
            clusters = kmeans.fit_predict(group_data['data'])
            
            group_clusters[group_name] = clusters
            kmeans_models[group_name] = kmeans
    
    return group_clusters, kmeans_models

def combine_state_clusters(group_clusters, labels):
    """
    Комбинирует кластеры из разных групп в единую матрицу состояний
    """
    # Получаем кластеры для каждой группы
    price_clusters = group_clusters['price']
    time_clusters = group_clusters['time']
    volume_clusters = group_clusters['volume']
    
    # Создаем матрицу состояний (3x3x3 = 27 возможных состояний, но мы используем только 9 для упрощения)
    transition_matrix = np.zeros((9, 9))
    rise_matrix = np.zeros((9, 9))
    state_counts = np.zeros(9)
    rise_counts = np.zeros(9)
    
    # Маппинг комбинаций кластеров в состояния
    state_mapping = {}
    for p in range(3):
        for t in range(3):
            for v in range(3):
                state_id = p*3*3 + t*3 + v
                if state_id < 9:  # Ограничиваемся 9 состояниями
                    state_mapping[(p, t, v)] = state_id
    
    print(f"Создано отображение кластеров в состояния: {state_mapping}")
    
    # Преобразуем комбинации кластеров в состояния
    states = np.zeros(len(price_clusters), dtype=int)
    for i in range(len(price_clusters)):
        p, t, v = price_clusters[i], time_clusters[i], volume_clusters[i]
        state_key = (p % 3, t % 3, v % 3)  # Берем по модулю 3, чтобы точно попасть в диапазон
        if state_key in state_mapping:
            states[i] = state_mapping[state_key]
    
    print(f"Распределение состояний: {np.bincount(states)}")
    
    # Анализ вероятностей роста для каждого состояния
    for state in range(9):
        state_mask = (states == state)
        state_count = np.sum(state_mask)
        if state_count > 0:
            state_counts[state] = state_count
            rise_count = np.sum(labels[state_mask])
            rise_counts[state] = rise_count
    
    # Нормализация для получения вероятностей роста
    state_price_direction = {}
    for state in range(9):
        if state_counts[state] > 0:
            state_price_direction[state] = rise_counts[state] / state_counts[state]
        else:
            state_price_direction[state] = 0.5
    
    # Заполнение матрицы переходов и матрицы роста
    for i in range(len(states) - 1):
        curr_state = states[i]
        next_state = states[i + 1]
        
        # Увеличиваем счетчик перехода
        transition_matrix[curr_state, next_state] += 1
        
        # Если следующая свеча растет, увеличиваем счетчик роста
        if i + 1 < len(labels) and labels[i + 1] == 1:
            rise_matrix[curr_state, next_state] += 1
    
    # Нормализация матрицы переходов
    state_transitions = np.zeros((9, 9))
    for i in range(9):
        row_sum = np.sum(transition_matrix[i, :])
        if row_sum > 0:
            state_transitions[i, :] = transition_matrix[i, :] / row_sum
        else:
            # Если нет данных для этого состояния, равномерное распределение
            state_transitions[i, :] = 1/9
    
    # Расчет матрицы вероятности роста
    rise_probability_matrix = np.zeros((9, 9))
    for i in range(9):
        for j in range(9):
            if transition_matrix[i, j] > 0:
                rise_probability_matrix[i, j] = rise_matrix[i, j] / transition_matrix[i, j]
            else:
                rise_probability_matrix[i, j] = 0.5
    
    return states, state_transitions, rise_probability_matrix, state_price_direction, state_mapping

def predict_with_matrix(state_transitions, rise_probability_matrix, current_state):
    """
    Прогнозирование следующего движения цены с использованием матрицы переходов,
    учитывая все 27 возможных состояний
    """
    # Вероятности перехода в следующее состояние
    next_state_probs = state_transitions[current_state, :]
    
    # Расчет взвешенной вероятности роста с учетом всех возможных переходов
    weighted_prob = 0
    total_prob = 0
    
    for next_state, prob in enumerate(next_state_probs):
        weighted_prob += prob * rise_probability_matrix[current_state, next_state]
        total_prob += prob
    
    # Нормализация (если необходимо)
    if total_prob > 0:
        weighted_prob = weighted_prob / total_prob
    
    # Прогноз
    prediction = 1 if weighted_prob > 0.5 else 0
    confidence = max(weighted_prob, 1 - weighted_prob)
    
    # Наиболее вероятное следующее состояние
    next_state = np.argmax(next_state_probs)
    
    return prediction, confidence, next_state, weighted_prob

def get_state_for_features(kmeans_models, state_mapping, price_features, time_features, volume_features):
    """
    Определение состояния для новых признаков
    """
    # Получение кластеров для новых данных
    price_cluster = kmeans_models['price'].predict([price_features])[0] % 3
    time_cluster = kmeans_models['time'].predict([time_features])[0] % 3
    volume_cluster = kmeans_models['volume'].predict([volume_features])[0] % 3
    
    # Преобразование в состояние
    state_key = (price_cluster, time_cluster, volume_cluster)
    if state_key in state_mapping:
        return state_mapping[state_key]
    else:
        return 0  # Состояние по умолчанию

def save_model(feature_groups, kmeans_models, state_transitions, rise_probability_matrix, 
              state_price_direction, state_mapping, filename="hmm_eurusd_h1_matrix.bin"):
    with open(filename, "wb") as f:
        pickle.dump({
            "feature_groups": {k: {'scaler': v['scaler'], 'features': v.get('features', None)} 
                              for k, v in feature_groups.items() if k != 'all'},
            "kmeans_models": kmeans_models,
            "state_transitions": state_transitions,
            "rise_probability_matrix": rise_probability_matrix,
            "state_price_direction": state_price_direction,
            "state_mapping": state_mapping,
            "created_at": datetime.now()
        }, f)
    print(f"Модель сохранена в файл {filename}")

def visualize_matrices(states, state_transitions, rise_probability_matrix):
    """
    Визуализация матриц переходов и вероятностей роста
    """
    # Визуализация распределения состояний
    plt.figure(figsize=(12, 6))
    state_counts = np.bincount(states, minlength=9)
    plt.bar(range(9), state_counts)
    plt.title('Распределение состояний в тренировочных данных')
    plt.xlabel('Состояние')
    plt.ylabel('Количество наблюдений')
    plt.xticks(range(9))
    plt.grid(axis='y', linestyle='--', alpha=0.7)
    plt.savefig('state_distribution.png')
    plt.close()

    # Визуализация матрицы переходов
    plt.figure(figsize=(10, 8))
    plt.imshow(state_transitions, cmap='viridis', interpolation='none')
    plt.colorbar(label='Вероятность перехода')
    plt.title('Матрица переходов между состояниями')
    plt.xlabel('Следующее состояние')
    plt.ylabel('Текущее состояние')
    plt.xticks(range(9))
    plt.yticks(range(9))

    # Добавление текстовых значений
    for i in range(9):
        for j in range(9):
            text_color = 'white' if state_transitions[i, j] < 0.5 else 'black'
            plt.text(j, i, f'{state_transitions[i, j]:.2f}', 
                     ha='center', va='center', color=text_color)

    plt.savefig('transition_matrix.png')
    plt.close()

    # Визуализация матрицы вероятностей роста
    plt.figure(figsize=(10, 8))
    plt.imshow(rise_probability_matrix, cmap='RdYlGn', interpolation='none', vmin=0, vmax=1)
    plt.colorbar(label='Вероятность роста')
    plt.title('Вероятность роста при переходе между состояниями')
    plt.xlabel('Следующее состояние')
    plt.ylabel('Текущее состояние')
    plt.xticks(range(9))
    plt.yticks(range(9))

    # Добавление текстовых значений
    for i in range(9):
        for j in range(9):
            text_color = 'white' if rise_probability_matrix[i, j] < 0.5 else 'black'
            plt.text(j, i, f'{rise_probability_matrix[i, j]:.2f}', 
                     ha='center', va='center', color=text_color)

    plt.savefig('rise_probability_matrix.png')
    plt.close()

def evaluate_model_with_all_transitions(test_states, state_transitions, rise_probability_matrix, test_labels):
    """
    Оценка модели с учетом всех 27 возможных переходов
    """
    # Матрица ошибок
    confusion = np.zeros((2, 2))
    
    # Оценка прогнозов
    predictions = []
    confidences = []
    weighted_probabilities = []

    for i in range(len(test_states) - 1):
        curr_state = test_states[i]
        
        # Получение всех вероятностей переходов из текущего состояния
        next_state_probs = state_transitions[curr_state, :]
        
        # Расчет взвешенной вероятности роста с учетом всех 27 возможных переходов
        weighted_prob = 0
        total_transition_probability = 0
        
        # Перебираем все возможные комбинации (3x3x3=27 состояний, но используем 9 для упрощения)
        for next_state in range(9):
            transition_prob = next_state_probs[next_state]
            rise_prob = rise_probability_matrix[curr_state, next_state]
            weighted_prob += transition_prob * rise_prob
            total_transition_probability += transition_prob
        
        # Нормализация (если требуется)
        if total_transition_probability > 0:
            weighted_prob = weighted_prob / total_transition_probability
        
        # Прогноз
        prediction = 1 if weighted_prob > 0.5 else 0
        confidence = max(weighted_prob, 1 - weighted_prob)
        
        predictions.append(prediction)
        confidences.append(confidence)
        weighted_probabilities.append(weighted_prob)
        
        # Обновление матрицы ошибок
        if i + 1 < len(test_labels):
            actual = test_labels[i + 1]
            confusion[prediction, actual] += 1

    # Расчет различных метрик эффективности
    accuracy = np.sum(confusion[0, 0] + confusion[1, 1]) / np.sum(confusion)
    precision = confusion[1, 1] / (confusion[1, 0] + confusion[1, 1]) if (confusion[1, 0] + confusion[1, 1]) > 0 else 0
    recall = confusion[1, 1] / (confusion[0, 1] + confusion[1, 1]) if (confusion[0, 1] + confusion[1, 1]) > 0 else 0
    f1_score = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0

    print(f"Точность модели на тестовой выборке: {accuracy:.4f}")
    print(f"Precision: {precision:.4f}")
    print(f"Recall: {recall:.4f}")
    print(f"F1-score: {f1_score:.4f}")
    print(f"Матрица ошибок:\n{confusion}")

    # Анализ уверенности предсказаний
    bins = [0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0]
    confidence_groups = np.digitize(confidences, bins)
    accuracy_by_confidence = {}

    for bin_idx in range(1, len(bins)):
        bin_mask = (confidence_groups == bin_idx)
        if np.sum(bin_mask) > 0:
            bin_accuracy = np.mean([1 if predictions[i] == test_labels[i+1] else 0 
                                    for i in range(len(predictions)) 
                                    if bin_mask[i] and i+1 < len(test_labels)])
            accuracy_by_confidence[f"{bins[bin_idx-1]:.2f}-{bins[bin_idx]:.2f}"] = (bin_accuracy, np.sum(bin_mask))

    print("\nТочность по группам уверенности:")
    for conf_range, (acc, count) in sorted(accuracy_by_confidence.items()):
        print(f"Уверенность {conf_range}: точность {acc:.4f} (количество: {count})")
    
    return accuracy, confusion, predictions, confidences, weighted_probabilities

def main():
    # Подключение к MT5
    if not connect_to_metatrader():
        return
    
    # Получение данных
    df = get_eurusd_data(bars_count=5000)
    if df is None:
        return
    
    # Подготовка признаков по группам
    feature_groups, labels, forecast_labels, df = prepare_features_grouped(df, lookback=5)
    
    # Разделение данных
    train_size = int(0.8 * len(labels))
    train_labels = labels[:train_size]
    test_labels = labels[train_size:]
    
    # Создание групп признаков для обучения и тестирования
    train_groups = {}
    test_groups = {}
    
    for group_name, group_data in feature_groups.items():
        train_groups[group_name] = {
            'data': group_data['data'][:train_size],
            'scaler': group_data.get('scaler'),
            'features': group_data.get('features')
        }
        
        test_groups[group_name] = {
            'data': group_data['data'][train_size:],
            'scaler': group_data.get('scaler'),
            'features': group_data.get('features')
        }
    
    # Создание кластеров состояний
    group_clusters, kmeans_models = create_state_clusters(train_groups)
    
    # Комбинирование кластеров в матрицу
    print("Создание матрицы состояний...")
    states, state_transitions, rise_probability_matrix, state_price_direction, state_mapping = combine_state_clusters(
        group_clusters, train_labels
    )
    
    # Визуализация матриц
    visualize_matrices(states, state_transitions, rise_probability_matrix)
    
    # Оценка модели
    print("Оценка модели...")
    
    # Получение состояний для тестовых данных
    test_states = []
    
    for i in range(len(test_groups['price']['data'])):
        price_features = test_groups['price']['data'][i]
        time_features = test_groups['time']['data'][i]
        volume_features = test_groups['volume']['data'][i]
        
        # Получение состояния
        state = get_state_for_features(
            kmeans_models, state_mapping, price_features, time_features, volume_features
        )
        test_states.append(state)
    
    # Оценка модели с учетом всех возможных переходов
    accuracy, confusion, predictions, confidences, weighted_probabilities = evaluate_model_with_all_transitions(
        test_states, state_transitions, rise_probability_matrix, test_labels
    )
    
    # Анализ состояний
    print("\nАнализ состояний:")
    for state in range(9):
        count = np.sum(states == state)
        percentage = count / len(states) * 100
        direction = "рост" if state_price_direction[state] > 0.5 else "падение"
        print(f"Состояние {state}: {direction} (вероятность роста: {state_price_direction[state]:.4f}, {percentage:.2f}%)")
    
    # Анализ матрицы переходов
    print("\nНаиболее вероятные переходы состояний:")
    for i in range(9):
        next_state = np.argmax(state_transitions[i, :])
        prob = state_transitions[i, next_state]
        rise_prob = rise_probability_matrix[i, next_state]
        direction = "рост" if rise_prob > 0.5 else "падение"
        print(f"{i} -> {next_state}: вероятность перехода {prob:.4f}, {direction} (вероятность роста: {rise_prob:.4f})")
    
    # Прогноз для последнего состояния с учетом всех переходов
    last_state = test_states[-1]
    
    # Вероятности перехода в следующее состояние
    next_state_probs = state_transitions[last_state, :]
    
    # Расчет взвешенной вероятности роста с учетом всех возможных переходов
    weighted_rise_prob = 0
    for next_state, prob in enumerate(next_state_probs):
        weighted_rise_prob += prob * rise_probability_matrix[last_state, next_state]
    
    # Прогноз
    prediction = "рост" if weighted_rise_prob > 0.5 else "падение"
    confidence = max(weighted_rise_prob, 1 - weighted_rise_prob)
    
    print(f"\nПрогноз для следующего бара (с учетом всех 27 переходов): {prediction}")
    print(f"Уверенность: {confidence:.4f}")
    print(f"Текущее состояние: {last_state}")
    print(f"Взвешенная вероятность роста: {weighted_rise_prob:.4f}")
    
    # Анализ влияния групп признаков
    print("\nАнализ влияния групп признаков на предсказание:")
    
    # Получение признаков для последнего бара
    last_price = test_groups['price']['data'][-1]
    last_time = test_groups['time']['data'][-1]
    last_volume = test_groups['volume']['data'][-1]
    
    # Получение кластеров
    price_cluster = kmeans_models['price'].predict([last_price])[0] % 3
    time_cluster = kmeans_models['time'].predict([last_time])[0] % 3
    volume_cluster = kmeans_models['volume'].predict([last_volume])[0] % 3
    
    print(f"Последний бар: кластер цены = {price_cluster}, кластер времени = {time_cluster}, кластер объема = {volume_cluster}")
    
    # Важность каждой группы
    print("\nВажнейшие признаки в каждой группе:")
    
    for group_name in ['price', 'time', 'volume']:
        features = feature_groups[group_name]['features']
        kmeans = kmeans_models[group_name]
        cluster_centers = kmeans.cluster_centers_
        
        for cluster_idx in range(3):
            center = cluster_centers[cluster_idx]
            # Получаем важность каждого признака как его отклонение от нуля в центре кластера
            importances = np.abs(center)
            # Сортируем признаки по важности
            sorted_idx = np.argsort(-importances)
            top_features = [(features[i], importances[i]) for i in sorted_idx[:3]]
            
            print(f"Кластер {cluster_idx} группы {group_name}. Топ-3 признака:")
            for feature, importance in top_features:
                print(f"  - {feature}: {importance:.4f}")
    
    # Сохранение модели
    save_model(feature_groups, kmeans_models, state_transitions, rise_probability_matrix, 
              state_price_direction, state_mapping)
    
    # Отключение
    mt5.shutdown()
    print("MetaTrader5 отключен")

def load_model(filename="hmm_eurusd_h1_matrix.bin"):
    """
    Загрузка модели из файла
    """
    with open(filename, "rb") as f:
        model_data = pickle.load(f)
    
    print(f"Модель загружена из файла {filename}, создана: {model_data['created_at']}")
    return model_data

def predict_next_bar(model_data, new_data):
    """
    Прогноз для новой свечи с использованием обученной модели
    """
    # Подготовка данных
    df = new_data.copy()
    df = add_indicators(df)
    df = create_price_patterns(df)
    
    # Удаление NaN
    df.dropna(inplace=True)
    
    # Получение последней свечи
    last_row = df.iloc[-1]
    
    # Извлечение признаков
    price_features = [last_row[feature] for feature in model_data['feature_groups']['price']['features']]
    time_features = [last_row[feature] for feature in model_data['feature_groups']['time']['features']]
    volume_features = [last_row[feature] for feature in model_data['feature_groups']['volume']['features']]
    
    # Масштабирование признаков
    price_scaler = model_data['feature_groups']['price']['scaler']
    time_scaler = model_data['feature_groups']['time']['scaler']
    volume_scaler = model_data['feature_groups']['volume']['scaler']
    
    scaled_price = price_scaler.transform([price_features])[0]
    scaled_time = time_scaler.transform([time_features])[0]
    scaled_volume = volume_scaler.transform([volume_features])[0]
    
    # Получение состояния
    kmeans_models = model_data['kmeans_models']
    state_mapping = model_data['state_mapping']
    
    state = get_state_for_features(
        kmeans_models, state_mapping, scaled_price, scaled_time, scaled_volume
    )
    
    # Прогноз с учетом всех 27 возможных переходов
    state_transitions = model_data['state_transitions']
    rise_probability_matrix = model_data['rise_probability_matrix']
    
    # Вероятности перехода
    next_state_probs = state_transitions[state, :]
    
    # Расчет взвешенной вероятности роста
    weighted_rise_prob = 0
    for next_state, prob in enumerate(next_state_probs):
        weighted_rise_prob += prob * rise_probability_matrix[state, next_state]
    
    # Прогноз
    prediction = "рост" if weighted_rise_prob > 0.5 else "падение"
    confidence = max(weighted_rise_prob, 1 - weighted_rise_prob)
    
    return {
        "prediction": prediction,
        "confidence": confidence,
        "current_state": state,
        "weighted_rise_probability": weighted_rise_prob,
        "next_state_probabilities": next_state_probs
    }

if __name__ == "__main__":
    main()
