# source: https://raw.githubusercontent.com/remiotore/ccxt-freqtrade/44beaeb6a420cd8e9f2e4ea93e11d6cfa192ee03/strategies/s10lchimoku_zema_hyper.py

from freqtrade.strategy import IStrategy, merge_informative_pair, DecimalParameter, IntParameter
from pandas import DataFrame
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.persistence import Trade
from datetime import datetime

import pandas as pd
import numpy as np
import technical.indicators as ftt
from freqtrade.exchange import timeframe_to_minutes
import logging
logger = logging.getLogger(__name__)









def ssl_atr(dataframe, length=7):
    df = dataframe.copy()
    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'])

class Github_remiotore_ccxt_freqtrade__s10lchimoku_zema_hyper__20260111_210550(IStrategy):
    INTERFACE_VERSION = 3

    timeframe = '5m'

    informative_timeframe = '1h'
    can_short = True


    startup_candle_count = 450


    process_only_new_candles = True

    minimal_roi = {
        '0': 0.078, 
        '40': 0.062, 
        '99': 0.039, 
        '218': 0
    }
    stoploss = - 0.1
    trailing_stop = True
    trailing_stop_positive = 0.001  # Positive offset for trailing stop.
    trailing_stop_positive_offset = 0.01  # Offset for triggering the trailing stop.
    trailing_only_offset_is_reached = True  # Only trigger trailing stop if the offset is reached.

    low_offset = DecimalParameter(0.5, 2.5, default=0.4, space='buy', optimize=True)
    high_offset = DecimalParameter(0.5, 2.5, default=1.004, space='sell', optimize=True)
    zema_len_buy = IntParameter(30, 90, default=72, space='buy', optimize=False)
    zema_len_sell = IntParameter(30, 90, default=51, space='sell', optimize=False)

    def informative_pairs(self):
        pairs = self.dp.current_whitelist()
        informative_pairs = [(pair, self.informative_timeframe) for pair in pairs]
        return informative_pairs

    def slow_tf_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        displacement = 30
        ichimoku = ftt.ichimoku(dataframe, conversion_line_period=20, base_line_periods=60, laggin_span=120, displacement=displacement)
        dataframe['chikou_span'] = ichimoku['chikou_span']

        dataframe['tenkan_sen'] = ichimoku['tenkan_sen'] 

        dataframe['kijun_sen'] = ichimoku['kijun_sen']


        dataframe['senkou_a'] = ichimoku['senkou_span_a']

        dataframe['senkou_b'] = ichimoku['senkou_span_b']


        dataframe['leading_senkou_span_a'] = ichimoku['leading_senkou_span_a']
        dataframe['leading_senkou_span_b'] = ichimoku['leading_senkou_span_b']

        dataframe['cloud_green'] = ichimoku['cloud_green'] * 1
        dataframe['cloud_red'] = ichimoku['cloud_red'] * -1

        dataframe.loc[:, 'cloud_top'] = dataframe.loc[:, ['senkou_a', 'senkou_b']].max(axis=1)
        dataframe.loc[:, 'cloud_bottom'] = dataframe.loc[:, ['senkou_a', 'senkou_b']].min(axis=1)


        dataframe['future_green'] = (dataframe['leading_senkou_span_a'] > dataframe['leading_senkou_span_b']).astype('int') * 2
        dataframe['future_red'] = (dataframe['leading_senkou_span_a'] < dataframe['leading_senkou_span_b']).astype('int') * 2



        dataframe['chikou_high'] = (dataframe['chikou_span'] > dataframe['cloud_top']).shift(displacement).fillna(0).astype('int')
        dataframe['chikou_low'] = (dataframe['chikou_span'] < dataframe['cloud_bottom']).shift(displacement).fillna(0).astype('int')

        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
        ssl_down, ssl_up = ssl_atr(dataframe, 10)

        dataframe['ssl_down'] = ssl_down
        dataframe['ssl_up'] = ssl_up
        dataframe['ssl_ok'] = (ssl_up > ssl_down).astype('int') * 3
        dataframe['ssl_bear'] = (ssl_up < ssl_down).astype('int') * 3

        dataframe['ichimoku_ok'] = (
            (dataframe['tenkan_sen'] > dataframe['kijun_sen']) 
            & 
            (dataframe['close'] > dataframe['cloud_top']) 
            & 
            (dataframe['future_green'] > 0) 
            & 
            (dataframe['chikou_high'] > 0)
        ).astype('int') * 4
        dataframe['ichimoku_bear'] = (
            (dataframe['tenkan_sen'] < dataframe['kijun_sen']) 
            & 
            (dataframe['close'] < dataframe['cloud_bottom']) 
            & 
            (dataframe['future_red'] > 0) 
            & 
            (dataframe['chikou_low'] > 0)
        ).astype('int') * 4  # not NaN

        dataframe['ichimoku_valid'] = (dataframe['leading_senkou_span_b'] == dataframe['leading_senkou_span_b']).astype('int') * 1

        dataframe['trend_pulse'] = (
            (dataframe['ichimoku_ok'] > 0) 
            & 
            (dataframe['ssl_ok'] > 0)
        ).astype('int') * 2

        dataframe['bear_trend_pulse'] = (
            (dataframe['ichimoku_bear'] > 0) 
            & 
            (dataframe['ssl_bear'] > 0)
        ).astype('int') * 2

        dataframe['trend_over'] = (
            (dataframe['ssl_ok'] == 0) 
            | 
            (dataframe['close'] < dataframe['cloud_top'])
        ).astype('int') * 1

        dataframe['bear_trend_over'] = (
            (dataframe['ssl_bear'] == 0) 
            | 
            (dataframe['close'] > dataframe['cloud_bottom'])
        ).astype('int') * 1
        
        dataframe.loc[dataframe['trend_pulse'] > 0, 'trending'] = 3
        dataframe.loc[dataframe['trend_over'] > 0, 'trending'] = 0
        dataframe['trending'].ffill()
        dataframe.loc[dataframe['bear_trend_pulse'] > 0, 'bear_trending'] = 3
        dataframe.loc[dataframe['bear_trend_over'] > 0, 'bear_trending'] = 0
        dataframe['bear_trending'].ffill()
        return dataframe

    def fast_tf_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe[f'zema_{self.zema_len_buy.value}'] = ftt.dema(dataframe, period=self.zema_len_buy.value)
        dataframe[f'zema_{self.zema_len_sell.value}'] = ftt.dema(dataframe, period=self.zema_len_sell.value)
        dataframe[f'zema_enter_long'] = ftt.dema(dataframe, period=self.zema_len_buy.value) - self.low_offset.value * dataframe['atr']
        dataframe[f'zema_exit_long'] = ftt.dema(dataframe, period=self.zema_len_sell.value) + self.high_offset.value * dataframe['atr']
        dataframe[f'zema_enter_short'] = ftt.dema(dataframe, period=self.zema_len_buy.value) + self.low_offset.value * dataframe['atr']
        dataframe[f'zema_exit_short'] = ftt.dema(dataframe, period=self.zema_len_sell.value) - self.high_offset.value * dataframe['atr']
  
        return dataframe

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        assert timeframe_to_minutes(self.timeframe) == 5, 'Run this strategy at 5m.'
        if self.timeframe == self.informative_timeframe:
            dataframe = self.slow_tf_indicators(dataframe, metadata)
        else:
            assert self.dp, 'DataProvider is required for multiple timeframes.'
            informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.informative_timeframe)
            informative = self.slow_tf_indicators(informative.copy(), metadata)
            dataframe = merge_informative_pair(dataframe, informative, self.timeframe, self.informative_timeframe, ffill=True)

            skip_columns = [s + '_' + self.informative_timeframe for s in ['date', 'open', 'high', 'low', 'close', 'volume']]
            dataframe.rename(columns=lambda s: s.replace('_{}'.format(self.informative_timeframe), '') if not s in skip_columns else s, inplace=True)
        dataframe = self.fast_tf_indicators(dataframe, metadata)
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                (dataframe['ichimoku_valid'] > 0) & 
                (dataframe['ichimoku_ok'] > 0) &
                (dataframe['ssl_ok'] > 0) & 
                (dataframe['close'] < dataframe['zema_enter_long'])
            ), 
            ['enter_long' , 'enter_tag' ]
        ] = (1 , 'long_')
 
        dataframe.loc[
            (
                (dataframe['ichimoku_valid'] > 0) &
                (dataframe['ichimoku_bear'] > 0) &
                (dataframe['ssl_bear'] > 0) &
                (dataframe['close'] > dataframe['zema_enter_short']) 
            ),
            ['enter_short' , 'enter_tag']
        ] = (1 , 'short_')
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                (dataframe['close'] > dataframe['zema_exit_long'])
                |
                (dataframe['ssl_bear'] > 0)
            ),
            ['exit_long' , 'exit_tag']
        ] = (1 , '_long')

        dataframe.loc[
            (
                (dataframe['close'] < dataframe['zema_exit_short'])
                |
                (dataframe['ssl_ok'] > 0)
            ),
            ['exit_short' , 'exit_tag']
        ] = (1 , '_short')
        return dataframe

    def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float,
                           rate: float, time_in_force: str, exit_reason: str,
                           current_time: datetime, **kwargs) -> bool:        
        if exit_reason in ('roi'):
            dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
            current_candle = dataframe.iloc[-1]
            if current_candle is not None :
                current_candle = current_candle.squeeze()

                if current_candle['trending'] > 0 and trade.trade_direction == "long":
                    return False
                if current_candle['bear_trending'] > 0 and trade.trade_direction == "short":
                    return False
        return True






    plot_config = {

        'main_plot': {
            'senkou_a': {
                'color': 'green',
                'fill_to': 'senkou_b',
                'fill_label': 'Ichimoku Cloud',
                'fill_color': 'rgba(0,0,0,0.2)',
            },

            'senkou_b': {
                'color': 'red',
            },
            'tenkan_sen': { 'color': 'blue' },
            'kijun_sen': { 'color': 'orange' },


            'ssl_up': { 'color': 'green' },





        },
        'subplots': {
            "Trend": {
                'trending': {'color': 'green'},
                'bear_trending': {'color': 'red'},
            },
            "Bull": {
                'trend_pulse': {'color': 'blue'},
                'trending': {'color': 'orange'},
                'trend_over': {'color': 'red'},
            },
            "Bull Signals": {
                'ichimoku_ok': {'color': 'green'},
                'ssl_ok': {'color': 'red'},
            },
            "Bear": {
                'bear_trend_pulse': {'color': 'blue'},
                'bear_trending': {'color': 'orange'},
                'bear_trend_over': {'color': 'red'},
            },
            "Bear Signals": {
                'ichimoku_bear': {'color': 'green'},
                'ssl_bear': {'color': 'red'},
            },
            "Misc": {
                'ichimoku_valid': {'color': 'green'},
            },
        }
    }