# source: https://raw.githubusercontent.com/remiotore/freqtrade/44beaeb6a420cd8e9f2e4ea93e11d6cfa192ee03/strategies/MultiTimeframeStrategy_v1.py
import math
import numpy as np
import pandas as pd
import talib as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
from pandas import DataFrame
from datetime import datetime
from typing import Optional, Union, Tuple
from freqtrade.persistence import Trade
from freqtrade.strategy import DecimalParameter, IStrategy, IntParameter, informative

class Github_remiotore_freqtrade__MultiTimeframeStrategy_v1__20260111_210550(IStrategy):
  INTERFACE_VERSION = 3
  can_short: bool = True
  levarage_input = 10.0
  stoploss = -0.058
  trailing_stop = False
  trailing_only_offset_is_reached = True
  trailing_stop_positive = 0.019
  trailing_stop_positive_offset = 0.053
  position_adjustment_enable = True
  timeframe = "5m"
  minimal_roi = {
      "0": 0.113,
      "29": 0.074,
      "54": 0.037,
      "76": 0,
  }
  process_only_new_candles = True
  use_exit_signal = True
  exit_profit_only = True
  ignore_roi_if_entry_signal = False

  buy_params = {
      "buy_rsi_5m": 20, "buy_rsi_1h": 12, "buy_rsi_4h": 19,
      "buy_rsi_compare_long_5m": 33, "buy_rsi_compare_long_1h": 52, "buy_rsi_compare_long_4h": 29,
      "buy_rsi_compare_short_5m": 54, "buy_rsi_compare_short_1h": 51, "buy_rsi_compare_short_4h": 63,
      "long_multiplier_5m": 0.985, "long_multiplier_1h": 0.961,
      "short_multiplier_5m": 1.01, "short_multiplier_1h": 1.026,
      "buy_factor_5m": 3.011, "buy_factor_1h": 3.55,
      "buy_period_5m": 13, "buy_period_1h": 7,
  }
  sell_params = {
      "sell_rsi_compare_long_5m": 65, "sell_rsi_compare_long_1h": 71,
      "sell_rsi_compare_short_5m": 25, "sell_rsi_compare_short_1h": 33,
      "sell_decr_pos_5m": 0.115, "sell_decr_pos_1h": 0.265
  }

  buy_rsi_5m = IntParameter(8, 20, default=buy_params['buy_rsi_5m'], space="buy", optimize=True)
  buy_rsi_1h = IntParameter(8, 20, default=buy_params['buy_rsi_1h'], space="buy", optimize=True)
  buy_rsi_4h = IntParameter(8, 20, default=buy_params['buy_rsi_4h'], space="buy", optimize=True)

  buy_rsi_compare_long_5m = IntParameter(20, 65, default=buy_params['buy_rsi_compare_long_5m'], space="buy", optimize=True)
  buy_rsi_compare_long_1h = IntParameter(20, 65, default=buy_params['buy_rsi_compare_long_1h'], space="buy", optimize=True)
  buy_rsi_compare_long_4h = IntParameter(20, 65, default=buy_params['buy_rsi_compare_long_4h'], space="buy", optimize=True)

  buy_rsi_compare_short_5m = IntParameter(20, 65, default=buy_params['buy_rsi_compare_short_5m'], space="buy", optimize=True)
  buy_rsi_compare_short_1h = IntParameter(20, 65, default=buy_params['buy_rsi_compare_short_1h'], space="buy", optimize=True)
  buy_rsi_compare_short_4h = IntParameter(20, 65, default=buy_params['buy_rsi_compare_short_4h'], space="buy", optimize=True)

  sell_rsi_compare_long_5m = IntParameter(60, 80, default=sell_params['sell_rsi_compare_long_5m'], space="sell", optimize=True)
  sell_rsi_compare_long_1h = IntParameter(60, 80, default=sell_params['sell_rsi_compare_long_1h'], space="sell", optimize=True)

  sell_rsi_compare_short_5m = IntParameter(20, 40, default=sell_params['sell_rsi_compare_short_5m'], space="sell", optimize=True)
  sell_rsi_compare_short_1h = IntParameter(20, 40, default=sell_params['sell_rsi_compare_short_1h'], space="sell", optimize=True)

  long_multiplier_5m = DecimalParameter(0.95, 1.0, default=buy_params['long_multiplier_5m'], decimals=3, space='buy', optimize=True)
  long_multiplier_1h = DecimalParameter(0.95, 1.0, default=buy_params['long_multiplier_1h'], decimals=3, space='buy', optimize=True)

  short_multiplier_5m = DecimalParameter(1.0, 1.05, default=buy_params['short_multiplier_5m'], decimals=3, space='buy', optimize=True)
  short_multiplier_1h = DecimalParameter(1.0, 1.05, default=buy_params['short_multiplier_1h'], decimals=3, space='buy', optimize=True)

  buy_factor_5m = DecimalParameter(1, 5, default=buy_params['buy_factor_5m'], decimals=3, space="buy", optimize=True)
  buy_factor_1h = DecimalParameter(1, 5, default=buy_params['buy_factor_1h'], decimals=3, space="buy", optimize=True)

  buy_period_5m = IntParameter(4, 15, default=buy_params['buy_period_5m'], space="buy", optimize=True)
  buy_period_1h = IntParameter(4, 15, default=buy_params['buy_period_1h'], space="buy", optimize=True)

  sell_decr_pos_5m = DecimalParameter(0.01, 0.3, decimals=3, default=sell_params['sell_decr_pos_5m'], space="sell", optimize=True)
  sell_decr_pos_1h = DecimalParameter(0.01, 0.3, decimals=3, default=sell_params['sell_decr_pos_1h'], space="sell", optimize=True)

  @informative('1h')
  def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
      dataframe['rsi'] = ta.RSI(dataframe['close'], timeperiod=self.buy_rsi_1h.value)  
      dataframe['ssl_up'], dataframe['ssl_down'] = self.SSLChannels(dataframe, 10)

      dataframe["adx"] = ta.ADX(dataframe['high'], dataframe['low'], dataframe['close'])  
      dataframe['WMA1'] = ta.WMA(dataframe["close"], timeperiod=30)
      dataframe['WMA2'] = ta.WMA(dataframe["close"], timeperiod=60)
      dataframe['SSL'] = ta.WMA(dataframe['WMA1'] - dataframe['WMA2'], timeperiod=int(math.sqrt(60)))

      return dataframe

  @informative('4h')
  def populate_indicators_4h(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
      dataframe['rsi'] = ta.RSI(dataframe['close'], timeperiod=self.buy_rsi_4h.value)  
      dataframe['ssl_up'], dataframe['ssl_down'] = self.SSLChannels(dataframe, 10)
      return dataframe

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

      dataframe['rsi'] = ta.RSI(dataframe['close'], timeperiod=self.buy_rsi_5m.value)  
      dataframe['ssl_up'], dataframe['ssl_down'] = self.SSLChannels(dataframe, 10)

      
      dataframe["adx"] = ta.ADX(dataframe['high'], dataframe['low'], dataframe['close'])  
      dataframe['WMA1'] = ta.WMA(dataframe["close"], timeperiod=30)
      dataframe['WMA2'] = ta.WMA(dataframe["close"], timeperiod=60)
      dataframe['SSL'] = ta.WMA(dataframe['WMA1'] - dataframe['WMA2'], timeperiod=int(math.sqrt(60)))

      return dataframe

  def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
      conditions = {}

      conditions['long_1h'] = (
          (dataframe['rsi_1h'] < self.buy_rsi_compare_long_1h.value) &
          (dataframe['ssl_up_1h'] > dataframe['ssl_down_1h']) &
          (dataframe['SSL_1h'] > 0) &
          (dataframe['ssl_up_4h'] > dataframe['ssl_down_4h'])  
      )
      conditions['short_1h'] = (
          (dataframe['rsi_1h'] > self.buy_rsi_compare_short_1h.value) &
          (dataframe['ssl_up_1h'] < dataframe['ssl_down_1h']) &
          (dataframe['SSL_1h'] < 0) &
          (dataframe['ssl_up_4h'] < dataframe['ssl_down_4h'])  
      )

      conditions['long_5m'] = (
          (dataframe['rsi'] < self.buy_rsi_compare_long_5m.value) &
          (dataframe['ssl_up'] > dataframe['ssl_down']) &
          ((dataframe['ssl_up_1h'] > dataframe['ssl_down_1h']) | (dataframe['ssl_up_4h'] > dataframe['ssl_down_4h']))  
      )
      conditions['short_5m'] = (
          (dataframe['rsi'] > self.buy_rsi_compare_short_5m.value) &
          (dataframe['ssl_up'] < dataframe['ssl_down']) &
          ((dataframe['ssl_up_1h'] < dataframe['ssl_down_1h']) | (dataframe['ssl_up_4h'] < dataframe['ssl_down_4h']))  
      )

      dataframe.loc[conditions['long_1h'], 'enter_long'] = 1
      dataframe.loc[conditions['long_1h'], 'enter_tag'] = '1h_long'

      dataframe.loc[conditions['long_5m'], 'enter_long'] = 1
      dataframe.loc[conditions['long_5m'], 'enter_tag'] = '5m_long'

      dataframe.loc[conditions['short_1h'], 'enter_short'] = 1
      dataframe.loc[conditions['short_1h'], 'enter_tag'] = '1h_short'

      dataframe.loc[conditions['short_5m'], 'enter_short'] = 1
      dataframe.loc[conditions['short_5m'], 'enter_tag'] = '5m_short'

      return dataframe

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

      dataframe.loc[
          (dataframe['rsi'] > self.sell_rsi_compare_long_5m.value) &
          (dataframe['rsi_1h'] > self.sell_rsi_compare_long_1h.value) &
          (dataframe['ssl_up'] < dataframe['ssl_down']),
          'exit_long'] = 1

      dataframe.loc[
          (dataframe['rsi'] < self.sell_rsi_compare_short_5m.value) &
          (dataframe['rsi_1h'] < self.sell_rsi_compare_short_1h.value) &
          (dataframe['ssl_up'] > dataframe['ssl_down']),
          'exit_short'] = 1

      return dataframe

  def custom_entry_price(self, pair: str, trade: Optional["Trade"], current_time: datetime, proposed_rate: float,
                         entry_tag: Optional[str], side: str, **kwargs) -> float:
      dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
      last_candle = dataframe.iloc[-1].squeeze()

      if side == "long":
          if entry_tag == '1h_long':
              return last_candle['close'] * self.long_multiplier_1h.value
          elif entry_tag == '5m_long':
              return last_candle['close'] * self.long_multiplier_5m.value
      else:
          if entry_tag == '1h_short':
              return last_candle['close'] * self.short_multiplier_1h.value
          elif entry_tag == '5m_short':
              return last_candle['close'] * self.short_multiplier_5m.value

      return proposed_rate

  def adjust_trade_position(self, trade: Trade, current_time: datetime,
                            current_rate: float, current_profit: float,
                            min_stake: Optional[float], max_stake: float,
                            current_entry_rate: float, current_exit_rate: float,
                            current_entry_profit: float, current_exit_profit: float,
                            **kwargs) -> Union[Optional[float], Tuple[Optional[float], Optional[str]]]:
      if trade.enter_tag == '1h_long' or trade.enter_tag == '1h_short':
          if current_profit > self.sell_decr_pos_1h.value and trade.nr_of_successful_exits == 0:
              return -(trade.stake_amount / 2), 'half_profit_1h'
      elif trade.enter_tag == '5m_long' or trade.enter_tag == '5m_short':
          if current_profit > self.sell_decr_pos_5m.value and trade.nr_of_successful_exits == 0:
              return -(trade.stake_amount / 2), 'half_profit_5m'
      return None

  def leverage(self, pair: str, current_time: datetime, current_rate: float,
               proposed_leverage: float, max_leverage: float, entry_tag: Optional[str], side: str,
               **kwargs) -> float:
      if self.levarage_input > max_leverage:
          return max_leverage
      return self.levarage_input

  def SSLChannels(self, dataframe, length=7):
      df = dataframe.copy()
      df["ATR"] = ta.ATR(df['high'], df['low'], df['close'], timeperiod=14)
      df["smaHigh"] = df["high"].rolling(length).mean() + df["ATR"]
      df["smaLow"] = df["low"].rolling(length).mean() - df["ATR"]
      df["hlv"] = np.where(
          df["close"] > df["smaHigh"], 1, np.where(df["close"] < df["smaLow"], -1, np.NAN)
      )
      df["hlv"] = df["hlv"].ffill()
      df["sslDown"] = np.where(df["hlv"] < 0, df["smaHigh"], df["smaLow"])
      df["sslUp"] = np.where(df["hlv"] < 0, df["smaLow"], df["smaHigh"])
      return df["sslDown"], df["sslUp"]

  def supertrend(self, dataframe: DataFrame, multiplier, period):
      df = dataframe.copy()

      df['ATR'] = ta.ATR(df['high'], df['low'], df['close'], period)

      st = 'ST_' + str(period) + '_' + str(multiplier)
      stx = 'STX_' + str(period) + '_' + str(multiplier)
      df['basic_ub'] = (df['high'] + df['low']) / 2 + multiplier * df['ATR']
      df['basic_lb'] = (df['high'] + df['low']) / 2 - multiplier * df['ATR']
      df['final_ub'] = 0.00
      df['final_lb'] = 0.00
      for i in range(period, len(df)):
          df['final_ub'].iat[i] = df['basic_ub'].iat[i] if df['basic_ub'].iat[i] < df['final_ub'].iat[i - 1] or \
              df['close'].iat[i - 1] > df['final_ub'].iat[i - 1] else \
              df['final_ub'].iat[i - 1]
          df['final_lb'].iat[i] = df['basic_lb'].iat[i] if df['basic_lb'].iat[i] > df['final_lb'].iat[i - 1] or \
              df['close'].iat[i - 1] < df['final_lb'].iat[i - 1] else \
              df['final_lb'].iat[i - 1]
      df[st] = 0.00
      for i in range(period, len(df)):
          df[st].iat[i] = df['final_ub'].iat[i] if df[st].iat[i - 1] == df['final_ub'].iat[i - 1] and df['close'].iat[
              i] <= df['final_ub'].iat[i] else \
              df['final_lb'].iat[i] if df[st].iat[i - 1] == df['final_ub'].iat[i - 1] and df['close'].iat[i] > \
              df['final_ub'].iat[i] else \
              df['final_lb'].iat[i] if df[st].iat[i - 1] == df['final_lb'].iat[i - 1] and df['close'].iat[i] >= \
              df['final_lb'].iat[i] else \
              df['final_ub'].iat[i] if df[st].iat[i - 1] == df['final_lb'].iat[i - 1] and df['close'].iat[i] < \
              df['final_lb'].iat[i] else 0.00
      df[stx] = np.where((df[st] > 0.00), np.where((df['close'] < df[st]), 'down', 'up'), np.NaN)
      df['final_band'] = np.where((df[st] > 0.00), np.where((df['close'] < df[st]), df['final_lb'], df['final_ub']),
                                  np.NaN)
      df.drop(['basic_ub', 'basic_lb', 'final_ub', 'final_lb'], inplace=True, axis=1)
      return DataFrame(index=df.index, data={
          'ST': df[st],
          'STX': df[stx],
          'Fin_band': df['final_band']
      })

  def SSLChannels_ATR(self, dataframe, length=7):
      df = dataframe.copy()
      df["ATR"] = ta.ATR(df, timeperiod=14)
      df["smaHigh"] = df["high"].rolling(length).mean() + df["ATR"]
      df["smaLow"] = df["low"].rolling(length).mean() - df["ATR"]
      df["hlv"] = np.where(
          df["close"] > df["smaHigh"], 1, np.where(df["close"] < df["smaLow"], -1, np.NAN)
      )
      df["hlv"] = df["hlv"].ffill()
      df["sslDown"] = np.where(df["hlv"] < 0, df["smaHigh"], df["smaLow"])
      df["sslUp"] = np.where(df["hlv"] < 0, df["smaLow"], df["smaHigh"])
      return df["sslDown"], df["sslUp"]
