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

if not mt5.initialize():
    print("initialize() failed, error code =", mt5.last_error())
    mt5.shutdown()

symbol = 'XAUUSDm'
timeframe = mt5.TIMEFRAME_M5
rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, 5000)
df = pd.DataFrame(rates)
df['time'] = pd.to_datetime(df['time'], unit='s')
df.set_index('time', inplace=True)
df.drop(['spread', 'real_volume'], axis=1, inplace=True)   
df['macd'] = 0
df['trf'] = 0
df['engulfing'] = 0
print(df)

def macd(df):
    df = df.copy()
    macd, signal, hist = talib.MACD(df['close'], fastperiod=12, slowperiod=26, signalperiod=9)  # Corrected
    if macd.iloc[-1] > signal.iloc[-1]:
        return 1
    elif macd.iloc[-1] < signal.iloc[-1]:
        return 2
    else:
        return 0
        
def twin_range_filter(df):
    df = df.copy()
    close = df['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:]
    df['TRF'] = TRF
    df['long_signal'] = np.append([False], long_signal)
    df['short_signal'] = np.append([False], short_signal)
    if df.iloc[-1]['long_signal']:
        return 1
    elif df.iloc[-1]['short_signal']:
        return 2
    
def detect_engulfing(df):
    df = df.copy()
    for i in range(1, len(df)):
        current = df.iloc[i].copy()
        previous = df.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 1
        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 2
        else:
            return 0
        
df['macd'] = df.apply(lambda x: macd(df.loc[:x.name]), axis=1)
df['trf'] = df.apply(lambda x: twin_range_filter(df.loc[:x.name]), axis=1)
df['engulfing'] = df.apply(lambda x: detect_engulfing(df.loc[:x.name]), axis=1)

df['signal'] = np.where((df['macd'] == 2) & (df['trf'] == 1) & (df['engulfing'] == 1), 1,
                        np.where((df['macd'] == 1) & (df['trf'] == 2) & (df['engulfing'] == 2), 2, 0))

count_sell_signals = df[df['signal'] == 2].shape[0]
print("Jumlah sinyal sell:", count_sell_signals)

df.columns = ['Local time', 'Open', 'High', 'Low', 'Close', 'Volume', 'signal']
df = df.iloc[100:200]
# print(df)

# def SIGNAL():
#     return df.signal

class ChubbStrategy(Strategy):
    def init(self):
        super().init()
        self.signal1 = self.I(lambda: df['signal'])
        
    def next(self):
        super().next() 
        if self.signal1==2:
            sl1 = self.data.Close[-1] - 750e-4
            tp1 = self.data.Close[-1] + 600e-4
            self.buy(sl=sl1, tp=tp1)
        elif self.signal1==1:
            sl1 = self.data.Close[-1] + 750e-4
            tp1 = self.data.Close[-1] - 600e-4
            self.sell(sl=sl1, tp=tp1)


bt = Backtest(df, ChubbStrategy, cash=10000, commission=0.0)

# Run the backtest
stats = bt.run()

# Print the performance statistics
print(stats)