import numpy as np
import pandas as pd
from backtesting import Strategy, Backtest
import talib
import MetaTrader5 as mt5

mt5.initialize()

def get_historical_data(symbol, timeframe, start_pos, count):
    rates = mt5.copy_rates_from_pos(symbol, timeframe, start_pos, count)
    df = pd.DataFrame(rates)
    df['time'] = pd.to_datetime(df['time'], unit='s')  # Convert time to datetime
    df.set_index('time', inplace=True)  # Set time as index
    df.drop(['spread', 'real_volume'], axis=1, inplace=True)  # Drop unnecessary columns
    
    # Rename columns to match backtesting library requirements
    df.rename(columns={'open': 'Open', 'high': 'High', 'low': 'Low', 'close': 'Close'}, inplace=True)
    
    return df[['Open', 'High', 'Low', 'Close']]


def macd(self):
        macd, signal = talib.MACD(self.data['close'], fastperiod=12, slowperiod=26, signalperiod=9)
        if macd.iloc[-1] > signal.iloc[-1]:
            return "Buy"
        elif macd.iloc[-1] < signal.iloc[-1]:
            return "Sell"
        
def twin_range_filter(self):
    close = self.data['close']
    def smoothrng(x, t, m):
        wper = t * 2 - 1
        avrng = talib.EMA(np.abs(x.diff()), timeperiod=t)
        smoothrng = talib.EMA(avrng, timeperiod=wper) * m
        return smoothrng
    per1, mult1, per2, mult2 = 27, 1.6, 55, 2.0
    smrng1 = smoothrng(close, per1, mult1)
    smrng2 = smoothrng(close, per2, mult2)
    smrng = (smrng1 + smrng2) / 2
    def rngfilt(x, r):
        rngfilt = x.copy()
        for i in range(1, len(x)):
            prev_val = rngfilt.iloc[i-1]
            if x.iloc[i] > prev_val:
                rngfilt.iloc[i] = max(prev_val, x.iloc[i] - r.iloc[i])
            else:
                rngfilt.iloc[i] = min(prev_val, x.iloc[i] + r.iloc[i])
        return rngfilt
    filt = rngfilt(close, smrng)
    STR = filt + smrng
    STS = filt - smrng
    FUB = [STR.iloc[0]]
    FLB = [STS.iloc[0]]
    for i in range(1, len(df)):
        FUB.append(STR.iloc[i] if (STR.iloc[i] < STR.iloc[i-1]) or (close.iloc[i-1] > FUB[i-1]) else FUB[i-1])
        FLB.append(STS.iloc[i] if (STS.iloc[i] > STS.iloc[i-1]) or (close.iloc[i-1] < FLB[i-1]) else FLB[i-1])
    FUB = np.array(FUB)
    FLB = np.array(FLB)
    TRF = [FUB[0]]
    for i in range(1, len(df)):
        last_trf = TRF[-1]
        if (last_trf == FUB[i-1] and close.iloc[i] <= FUB[i]) or (last_trf == FLB[i-1] and close.iloc[i] <= FLB[i]):
            TRF.append(FUB[i])
        elif (last_trf == FUB[i-1] and close.iloc[i] >= FUB[i]) or (last_trf == FLB[i-1] and close.iloc[i] >= FLB[i]):
            TRF.append(FLB[i])
        else:
            TRF.append(FUB[i])
    TRF = np.array(TRF)
    long_signal = (close > np.roll(TRF, 1))[1:]
    short_signal = (close < np.roll(TRF, 1))[1:]
    self.data['TRF'] = TRF
    self.data['long_signal'] = np.append([False], long_signal)
    self.data['short_signal'] = np.append([False], short_signal)
    if self.data.iloc[-1]['long_signal']:
        return "Buy"
    elif self.data.iloc[-1]['short_signal']:
        return "Sell"
    
def detect_engulfing(self):
    for i in range(1, len(self.data)):
        current = self.data.iloc[i].copy()
        previous = self.data.iloc[i-1].copy()
        if np.abs(current['open'] - previous['close']) > 0.005:
            current['open'] = previous['close']
        if previous['open'] > previous['close'] and \
        current['close'] > current['open'] and \
        current['close'] >= previous['open'] and \
        previous['close'] >= current['open'] and \
        current['close'] - current['open'] > previous['open'] - previous['close']:
            return "Bullish Engulfing"
        elif previous['close'] > previous['open'] and \
        current['open'] > current['close'] and \
        current['open'] >= previous['close'] and \
        previous['open'] >= current['close'] and \
        current['open'] - current['close'] > previous['close'] - previous['open']:
            return "Bearish Engulfing"
        else:
            return "No Engulfing"

class EngulfingStrategy(Strategy):
    def init(self):
        self.macd = self.I(self.macd)
        self.trf = self.I(self.twin_range_filter)
        self.engulfing = self.I(self.detect_engulfing)

    def next(self):
        # Check for bullish engulfing condition
        if self.macd() == "Sell" and self.trf() == "Buy" and self.engulfing() == "Bullish Engulfing":
            self.buy()

        # Check for bearish engulfing condition
        elif self.macd() == "Buy" and self.trf() == "Sell" and self.engulfing() == "Bearish Engulfing":
            self.sell()

#Define your backtest parameters
symbol = 'XAUUSDm'  # Example symbol
timeframe = mt5.TIMEFRAME_M5  # Example timeframe (H1)
data = get_historical_data(symbol, timeframe, 0, 10000)  # Example: Get 1000 bars of historical data
bt = Backtest(data, EngulfingStrategy, cash=10000, commission=0.0)

# Run the backtest
stats = bt.run()

# Print the performance statistics
print(stats)