import MetaTrader5 as mt5
import pandas as pd
import numpy as np
from collections import defaultdict
import matplotlib.pyplot as plt  # Добавлен импорт matplotlib.pyplot
from datetime import datetime, timedelta

# Инициализация MetaTrader5
if not mt5.initialize():
    print("initialize() failed")
    mt5.shutdown()

# Установка параметров запроса данных
symbol = 'EURUSD'
timeframe = mt5.TIMEFRAME_H4
start_date = pd.Timestamp('2017-01-01')
end_date = pd.Timestamp('2024-07-01')

# Загрузка данных OHLC
rates = mt5.copy_rates_range(symbol, timeframe, start_date, end_date)
ohlc_data = pd.DataFrame(rates)
ohlc_data['time'] = pd.to_datetime(ohlc_data['time'], unit='s')

# Преобразование данных в направления изменения цен
ohlc_data['direction'] = np.where(ohlc_data['close'].diff() > 0, 'up', 'down')

# Функция для поиска паттернов на покупку
def find_buy_patterns(data, pattern_length):
    patterns = defaultdict(list)
    for i in range(len(data) - pattern_length - 6):
        pattern = tuple(data['direction'][i:i+pattern_length])
        if data['direction'][i+pattern_length+6] == 'up':
            patterns[pattern].append(True)
        else:
            patterns[pattern].append(False)
    return patterns

# Функция для поиска паттернов на продажу
def find_sell_patterns(data, pattern_length):
    patterns = defaultdict(list)
    for i in range(len(data) - pattern_length - 6):
        pattern = tuple(data['direction'][i:i+pattern_length])
        if data['direction'][i+pattern_length+6] == 'down':
            patterns[pattern].append(True)
        else:
            patterns[pattern].append(False)
    return patterns

# Функция для вычисления винрейта и частоты встречаемости паттернов
def calculate_winrate_and_frequency(patterns):
    results = []
    for pattern, outcomes in patterns.items():
        winrate = np.mean(outcomes) * 100
        frequency = len(outcomes)
        results.append((pattern, winrate, frequency))
    results.sort(key=lambda x: x[1], reverse=True)
    return results

# Параметры для поиска паттернов
pattern_lengths = range(3, 70)  # Длины паттернов от 3 до 25
all_buy_patterns = {}
all_sell_patterns = {}

for pattern_length in pattern_lengths:
    buy_patterns = find_buy_patterns(ohlc_data, pattern_length)
    sell_patterns = find_sell_patterns(ohlc_data, pattern_length)
    all_buy_patterns[pattern_length] = buy_patterns
    all_sell_patterns[pattern_length] = sell_patterns

# Вычисление винрейта и частоты встречаемости для всех паттернов на покупку
all_buy_results = []
for pattern_length, patterns in all_buy_patterns.items():
    results = calculate_winrate_and_frequency(patterns)
    all_buy_results.extend(results)

# Вычисление винрейта и частоты встречаемости для всех паттернов на продажу
all_sell_results = []
for pattern_length, patterns in all_sell_patterns.items():
    results = calculate_winrate_and_frequency(patterns)
    all_sell_results.extend(results)

# Фильтрация паттернов, которые встретились чаще 50 раз
filtered_buy_results = [result for result in all_buy_results if result[2] > 10]
filtered_sell_results = [result for result in all_sell_results if result[2] > 10]

# Сортировка и выбор 300 лучших паттернов на покупку
filtered_buy_results.sort(key=lambda x: x[1], reverse=True)
top_300_buy_patterns = filtered_buy_results[:300]

# Сортировка и выбор 300 лучших паттернов на продажу
filtered_sell_results.sort(key=lambda x: x[1], reverse=True)
top_300_sell_patterns = filtered_sell_results[:300]

# Функция для проверки соответствия последних цен паттернам
def check_patterns(data, patterns):
    pattern_length = len(patterns[0][0])
    last_pattern = tuple(data['direction'][-pattern_length:])
    matching_patterns = [pattern for pattern in patterns if pattern[0] == last_pattern]
    return matching_patterns

# Проверка соответствия последних цен паттернам на покупку и продажу
matching_buy_patterns = check_patterns(ohlc_data, top_300_buy_patterns)
matching_sell_patterns = check_patterns(ohlc_data, top_300_sell_patterns)

# Вычисление совокупного шанса на рост и падение
total_buy_winrate = np.mean([pattern[1] for pattern in matching_buy_patterns]) if matching_buy_patterns else 0
total_sell_winrate = np.mean([pattern[1] for pattern in matching_sell_patterns]) if matching_sell_patterns else 0

# Добавляем функцию визуализации паттернов
def visualize_patterns(patterns, title, filename):
    patterns = patterns[:20]  # Берем топ-20 для наглядности
    patterns.reverse()  # Разворачиваем список для правильного отображения на графике

    fig, ax = plt.subplots(figsize=(12, 8))
    
    winrates = [p[1] for p in patterns]
    frequencies = [p[2] for p in patterns]
    labels = [' '.join(p[0]) for p in patterns]

    ax.barh(range(len(patterns)), winrates, align='center', color='skyblue', zorder=10)
    ax.set_yticks(range(len(patterns)))
    ax.set_yticklabels(labels)
    ax.invert_yaxis()  # Инвертируем ось Y для отображения лучших паттернов сверху

    ax.set_xlabel('Winrate (%)')
    ax.set_title(title)

    # Добавляем частоту встречаемости
    for i, v in enumerate(winrates):
        ax.text(v + 1, i, f'Freq: {frequencies[i]}', va='center')

    plt.tight_layout()
    plt.savefig(filename)
    plt.close()

# Визуализируем топ паттерны для покупки и продажи
visualize_patterns(top_300_buy_patterns, f'Top 20 Buy Patterns for {symbol}', 'top_buy_patterns.png')
visualize_patterns(top_300_sell_patterns, f'Top 20 Sell Patterns for {symbol}', 'top_sell_patterns.png')

# Функция для симуляции торговли
def simulate_trade(data, direction, entry_price, take_profit, stop_loss):
    for i, row in data.iterrows():
        current_price = row['close']
        
        if direction == "BUY":
            if current_price >= entry_price + take_profit:
                return {'profit': take_profit, 'duration': i}
            elif current_price <= entry_price - stop_loss:
                return {'profit': -stop_loss, 'duration': i}
        else:  # SELL
            if current_price <= entry_price - take_profit:
                return {'profit': take_profit, 'duration': i}
            elif current_price >= entry_price + stop_loss:
                return {'profit': -stop_loss, 'duration': i}
    
    # Если цикл завершился без достижения TP или SL, закрываем по текущей цене
    last_price = data['close'].iloc[-1]
    profit = (last_price - entry_price) if direction == "BUY" else (entry_price - last_price)
    return {'profit': profit, 'duration': len(data)}

# Функция для проведения бэктеста
def backtest_pattern_system(data, buy_patterns, sell_patterns):
    equity_curve = [10000]  # Начальный капитал $10,000
    trades = []
    
    for i in range(len(data) - max(len(p[0]) for p in buy_patterns + sell_patterns)):
        current_data = data.iloc[:i+1]
        last_pattern = tuple(current_data['direction'].iloc[-len(buy_patterns[0][0]):])
        
        matching_buy = [p for p in buy_patterns if p[0] == last_pattern]
        matching_sell = [p for p in sell_patterns if p[0] == last_pattern]
        
        if matching_buy and not matching_sell:
            entry_price = current_data['close'].iloc[-1]
            take_profit = 0.001  # 10 pips
            stop_loss = 0.0005  # 5 pips
            trade_result = simulate_trade(data.iloc[i+1:], "BUY", entry_price, take_profit, stop_loss)
            trades.append(trade_result)
            equity_curve.append(equity_curve[-1] + trade_result['profit'] * 10000)  # Умножаем на 10000 для конвертации в доллары
        elif matching_sell and not matching_buy:
            entry_price = current_data['close'].iloc[-1]
            take_profit = 0.001  # 10 pips
            stop_loss = 0.0005  # 5 pips
            trade_result = simulate_trade(data.iloc[i+1:], "SELL", entry_price, take_profit, stop_loss)
            trades.append(trade_result)
            equity_curve.append(equity_curve[-1] + trade_result['profit'] * 10000)  # Умножаем на 10000 для конвертации в доллары
        else:
            equity_curve.append(equity_curve[-1])
    
    return equity_curve, trades

# Проводим бэктест
equity_curve, trades = backtest_pattern_system(ohlc_data, top_300_buy_patterns, top_300_sell_patterns)

# Визуализация результатов бэктеста
plt.figure(figsize=(12, 6))
plt.plot(equity_curve)
plt.title('Equity Curve')
plt.xlabel('Trades')
plt.ylabel('Equity ($)')
plt.savefig('equity_curve.png')
plt.close()

# Расчет статистики бэктеста
total_profit = equity_curve[-1] - equity_curve[0]
win_rate = sum(1 for trade in trades if trade['profit'] > 0) / len(trades) if trades else 0
average_profit = sum(trade['profit'] for trade in trades) / len(trades) if trades else 0

print(f"\nBacktest Results:")
print(f"Total Profit: ${total_profit:.2f}")
print(f"Win Rate: {win_rate:.2%}")
print(f"Average Profit per Trade: ${average_profit*10000:.2f}")
print(f"Total Trades: {len(trades)}")

# Вывод статистики
print(f"Matching buy patterns: {len(matching_buy_patterns)}")
print(f"Matching sell patterns: {len(matching_sell_patterns)}")
print(f"Total buy winrate: {total_buy_winrate:.2f}%")
print(f"Total sell winrate: {total_sell_winrate:.2f}%")

# Завершение работы MetaTrader5
mt5.shutdown()
