# source: https://raw.githubusercontent.com/secondlinger/WinnieBari/73bedc96c87439cd3ca5ae33aca9cb01ed125468/user_data/strategies/CustomRLStrategy.py
# user_data/strategies/Github_secondlinger_WinnieBari__CustomRLStrategy__20250919_011108.py
from datetime import datetime

import pandas as pd
import talib.abstract as ta
from pandas import DataFrame

import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.persistence import Trade
from freqtrade.strategy.interface import IStrategy

buy_params = {
    "base_nb_candles_buy": 8,
    "ewo_high": 2.403,
    "ewo_high_2": -5.585,
    "ewo_low": -14.378,
    "lookback_candles": 3,
    "low_offset": 0.984,
    "low_offset_2": 0.942,
    "profit_threshold": 1.008,
    "rsi_buy": 72
}

sell_params = {
    "base_nb_candles_sell": 16,
    "high_offset": 1.084,
    "high_offset_2": 1.401,
    "pHSL": -0.15,
    "pPF_1": 0.016,
    "pPF_2": 0.024,
    "pSL_1": 0.014,
    "pSL_2": 0.022
}


def EWO(dataframe, ema_length=5, ema2_length=35):
    df = dataframe.copy()
    ema1 = ta.EMA(df, timeperiod=ema_length)
    ema2 = ta.EMA(df, timeperiod=ema2_length)
    emadif = (ema1 - ema2) / df['low'] * 100
    return emadif


class Github_secondlinger_WinnieBari__CustomRLStrategy__20250919_011108(IStrategy):
    timeframe = "5m"
    can_short = True
    stoploss = -0.99
    # より現実的なROI設定
    minimal_roi = {
        "0": 0.20,
        "10": 0.10,
        "30": 0.05,
        "60": 0.03,
        "120": 0.01,
        "240": 0
    }

    # トレーリングストップの改善
    trailing_stop = True
    trailing_stop_positive = 0.015  # より厳しく
    trailing_stop_positive_offset = 0.03
    trailing_only_offset_is_reached = True

    fast_ewo = 50
    slow_ewo = 200

    use_exit_signal = True
    exit_profit_only = False
    exit_profit_offset = 0.01
    ignore_roi_if_entry_signal = False

    inf_1h = '1h'

    process_only_new_candles = True
    startup_candle_count = 200
    use_custom_stoploss = True

    plot_config = {
        'main_plot': {
            'ma_buy': {'color': 'orange'},
            'ma_sell': {'color': 'orange'},
        },
    }

    slippage_protection = {
        'retries': 3,
        'max_slippage': -0.02
    }

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

    def informative_1h_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        assert self.dp, "DataProvider is required for multiple timeframes."

        informative_1h = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.inf_1h)

        return informative_1h

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

        dataframe['hma_50'] = qtpylib.hull_moving_average(dataframe['close'], window=50)
        dataframe['ema_100'] = ta.EMA(dataframe, timeperiod=100)

        dataframe['sma_9'] = ta.SMA(dataframe, timeperiod=9)

        dataframe['EWO'] = EWO(dataframe, self.fast_ewo, self.slow_ewo)

        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
        dataframe['rsi_fast'] = ta.RSI(dataframe, timeperiod=4)
        dataframe['rsi_slow'] = ta.RSI(dataframe, timeperiod=20)

        return dataframe

    def populate_indicators(self, dataframe, metadata):
        dataframe = self.normal_tf_indicators(dataframe, metadata)
        dataframe = self.freqai.start(dataframe, metadata, self)  # RL予測を注入
        return dataframe

    def set_freqai_targets(self, dataframe: DataFrame, **kwargs) -> DataFrame:
        dataframe["&-action"] = 0
        return dataframe

    def feature_engineering_standard(self, dataframe: DataFrame, **kwargs) -> DataFrame:
        # 既存のフィーチャー
        dataframe["%-raw_close"] = dataframe["close"]
        dataframe["%-raw_open"] = dataframe["open"]
        dataframe["%-raw_high"] = dataframe["high"]
        dataframe["%-raw_low"] = dataframe["low"]
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)

        # 追加のフィーチャー
        # ボラティリティ指標
        dataframe["%-volatility"] = dataframe['close'].rolling(20).std()

        # トレンド強度
        dataframe["%-trend_strength"] = (dataframe['close'] - dataframe['close'].rolling(20).mean()) / dataframe[
            "%-volatility"]

        # 出来高関連
        if 'volume' in dataframe.columns:
            dataframe["%-volume_sma"] = dataframe['volume'].rolling(20).mean()
            dataframe["%-volume_ratio"] = dataframe['volume'] / dataframe["%-volume_sma"]

        # リターン関連
        dataframe["%-returns"] = dataframe['close'].pct_change()
        dataframe["%-returns_1h"] = dataframe['close'].pct_change(12)  # 5分足なので12期間=1時間

        return dataframe

    def populate_entry_trend(self, df, metadata):
        if "do_predict" not in df.columns:
            df["do_predict"] = 1
        if "&-action" not in df.columns:
            df["&-action"] = 0

        # エントリーシグナルの統計をログ出力
        long_signals = (df["do_predict"] == 1) & (df["&-action"] == 1)
        short_signals = (df["do_predict"] == 1) & (df["&-action"] == 3)

        if long_signals.sum() > 0:
            print(f"{metadata['pair']}: Long signals: {long_signals.sum()}")
        if short_signals.sum() > 0:
            print(f"{metadata['pair']}: Short signals: {short_signals.sum()}")

        df.loc[long_signals, "enter_long"] = 1
        df.loc[short_signals, "enter_short"] = 1

        return df

    def populate_exit_trend(self, df, metadata):
        if "do_predict" not in df.columns: df["do_predict"] = 1
        if "&-action" not in df.columns: df["&-action"] = 0
        df.loc[(df["do_predict"] == 1) & (df["&-action"] == 2), "exit_long"] = 1
        df.loc[(df["do_predict"] == 1) & (df["&-action"] == 4), "exit_short"] = 1
        return df

    def custom_entry_price(self, pair: str, current_time, proposed_rate: float,
                           entry_tag: str, side: str, **kwargs) -> float:
        try:
            dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)

            if dataframe.empty or 'atr' not in dataframe.columns:
                return proposed_rate

            latest_atr = dataframe.iloc[-1]['atr']

            # ATRが異常値でないかチェック
            if pd.isna(latest_atr) or latest_atr <= 0:
                return proposed_rate

            latebuy = 0.1
            atr_offset = latest_atr * latebuy

            if side == 'long':
                return max(dataframe.iloc[-1]['close'] - atr_offset,
                           proposed_rate * 0.999)  # 最大0.1%まで下げる
            else:
                return min(dataframe.iloc[-1]['close'] + atr_offset,
                           proposed_rate * 1.001)  # 最大0.1%まで上げる

        except Exception as e:
            print(f"Entry price calculation failed: {e}")
            return proposed_rate

    def custom_exit_price(self, pair: str, trade: Trade,
                          current_time: datetime, proposed_rate: float,
                          current_profit: float, exit_tag: str | None, **kwargs) -> float:
        try:
            dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)

            # 최신 ATR 값 가져오기
            latest_atr = dataframe.iloc[-1]['atr']
            current_close = dataframe.iloc[-1]['close']

            # ATR * 0.1 오프셋
            atr_offset = latest_atr * 0.01

            if trade.is_short:
                # 숏: 현재가에서 ATR * 0.1만큼 아래
                return current_close - atr_offset
            else:
                # 롱: 현재가에서 ATR * 0.1만큼 위
                return current_close + atr_offset
        except:
            return proposed_rate
