# source: https://raw.githubusercontent.com/remiotore/freqtrade/44beaeb6a420cd8e9f2e4ea93e11d6cfa192ee03/strategies/MLStrategy2.py
import numpy as np  # noqa
import pandas as pd  # noqa
from pandas import DataFrame
from freqtrade.strategy.interface import IStrategy
from sklearn.ensemble import RandomForestClassifier
import talib
import ccxt
import pickle
import os
import warnings
warnings.simplefilter(action='ignore', category=pd.errors.PerformanceWarning)

class Github_remiotore_freqtrade__MLStrategy2__20260111_210550(IStrategy):
    sma_periods = [10, 20, 50]
    rsi_period = 14
    bbands_period = 20
    window_length = 50
    model_kind = 'RandomForest'
    models = {}
    model_timeframes = {'5m': 1, '15m': 3, '30m': 6}  #timeframes of models with their corresponding dividers    
    pairs = ['DOGE/USDT', 'ETH/USDT', 'BTC/USDT'] #, 'BTC/USDT', 'ETH/USDT'
    stoploss = -0.271
    timeframe = '5m'

    def bot_start(self, **kwargs) -> None:
        for pair in self.pairs:
            self.models[pair] = {}
            for timeframe in self.model_timeframes.keys():
                with open(f'model_{self.model_kind}_{pair.replace("/", "-")}_{timeframe}.pkl', 'rb') as file:
                    self.models[pair][timeframe] = pickle.load(file)

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:

        for sma_period in self.sma_periods:
            dataframe[f'sma_{sma_period}'] = talib.SMA(dataframe['close'], timeperiod=sma_period)
        dataframe['rsi'] = talib.RSI(dataframe['close'], timeperiod=self.rsi_period)
        upper, _, lower = talib.BBANDS(dataframe['close'], timeperiod=self.bbands_period, nbdevup=2, nbdevdn=2, matype=0)

        dataframe['bband_lower'] = lower
        dataframe['bband_upper'] = upper

        return dataframe
    
    def preprocess_dataframe_for_prediction(self, temp_df):
        cols = ['open', 'high', 'low', 'close', 'volume']   #columns to be stacked in one row
        i = 1
        while i < self.window_length-1:
            for col in cols:
                temp_df[f'{col}_{i}'] = temp_df[col].shift(self.window_length-i)
            i = i + 1
        for col in cols:
            temp_df[f'{col}_{self.window_length-1}'] = temp_df[col]
            temp_df[col] = temp_df[col].shift(self.window_length)

        cols = temp_df.columns.tolist()

        indicator_cols = [f'sma_{period}' for period in self.sma_periods]    #columns to be moved to end
        indicator_cols.extend(['rsi', 'bband_lower', 'bband_upper'])
        for indicator in indicator_cols:
            cols.append(cols.pop(cols.index(indicator)))
        temp_df = temp_df[cols]
        
        temp_df = temp_df.drop(columns=['date'])
        if 'enter_long' in temp_df.columns:
            temp_df = temp_df.drop(columns=['enter_long'])
        temp_df = temp_df.dropna(axis=0)
        return temp_df

    def predict_with_model(self, models, dataframe, sell=False):
        all_predictions = []
        for timeframe in self.model_timeframes.keys():
            divider = self.model_timeframes[timeframe]
            predictions = []
            temp_df = dataframe.copy()
            target_mod = (temp_df.shape[0]-1) % divider
            temp_df = temp_df[temp_df.index % divider == target_mod].reset_index(drop=True)  #takes df.length/divider rows from df, ending with the most recent row

            for i in range(self.window_length):
                predictions.append(False)
            if sell:
                predictions.extend([not a for a in models[timeframe].predict(self.preprocess_dataframe_for_prediction(temp_df))])
            else:
                predictions.extend(models[timeframe].predict(self.preprocess_dataframe_for_prediction(temp_df)))
            predictions = np.repeat(predictions, divider)

            all_predictions.append(predictions)
        
        majority_predictions = [sum(pred)/len(pred) > 0.5 for pred in zip(*all_predictions)]    #True if over half of the models predicted True for this timestep
        return majority_predictions
        
    def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:

        print('buy meta:', metadata)
        if metadata['pair'] in self.pairs:
            predictions = self.predict_with_model(self.models[metadata['pair']], dataframe)
            print(len(predictions))
            print(predictions)
            dataframe['buy'] = predictions
        else:
            dataframe['buy'] = False
        print('buy df-populated:', dataframe)
        return dataframe

    def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:

        print('sell meta:', metadata)
        if metadata['pair'] in self.pairs:
            predictions = self.predict_with_model(self.models[metadata['pair']], dataframe, True)
            print(predictions)
            dataframe['sell'] = 0
        else:
            dataframe['sell'] = False
        print('sell df-populated:', dataframe)
        return dataframe

'''
    def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
                            proposed_stake: float, min_stake: Optional[float], max_stake: float,
                            leverage: float, entry_tag: Optional[str], side: str,
                            **kwargs) -> float:
        
        dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
        current_candle = dataframe.iloc[-1].squeeze()

        if current_candle['fastk_rsi_1h'] > current_candle['fastd_rsi_1h']:
            if self.config['stake_amount'] == 'unlimited':

                return max_stake
            else:

                return self.wallets.get_total_stake_amount() / self.config['max_open_trades']

        return proposed_stake
'''
