# source: https://raw.githubusercontent.com/remiotore/freqtrade/44beaeb6a420cd8e9f2e4ea93e11d6cfa192ee03/strategies/Aquma1_1.py
import pandas as pd
import numpy as np
import talib.abstract as ta
from freqtrade.strategy import IStrategy
from typing import Optional

class Github_remiotore_freqtrade__Aquma1_1__20260111_210550(IStrategy):
    # 기본 설정
    timeframe = '15m'
    process_only_new_candles = True
    startup_candle_count = 1000
    can_short = True
    use_exit_signal = True

    stoploss = -0.02
    minimal_roi = {
        "0": 0.04,
        "60": 0.02,
        "120": 0.01,
        "240": 0
    }

    def __init__(self, config: dict, **kwargs):
        super().__init__(config, **kwargs)
        from xgboost import XGBClassifier
        import joblib
        self.model = None
        try:
            self.model = joblib.load('/freqtrade/user_data/models/aquma1_xgb.pkl')
        except Exception as e:
            print(f"모델 로드 실패: {e}")

    def populate_indicators(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        # 기술적 지표 계산 (talib 사용)
        dataframe['RSI'] = ta.RSI(dataframe, timeperiod=14)
        dataframe['MFI'] = ta.MFI(dataframe, timeperiod=14)
        dataframe['ADX'] = ta.ADX(dataframe, timeperiod=14)
        dataframe['EMA_20'] = ta.EMA(dataframe['close'], timeperiod=20)
        
        # MACD 계산
        macd, macdsignal, macdhist = ta.MACD(
            dataframe['close'], fastperiod=12, slowperiod=26, signalperiod=9
        )
        dataframe['MACD'] = macd
        dataframe['MACD_signal'] = macdsignal
        dataframe['MACD_hist'] = macdhist
        
        # Bollinger Bands 계산 (nbdevup/nbdevdn는 float로 전달)
        bb_upper, bb_middle, bb_lower = ta.BBANDS(
            dataframe['close'], timeperiod=20, nbdevup=2.0, nbdevdn=2.0, matype=0
        )
        dataframe['BB_upper'] = bb_upper
        dataframe['BB_middle'] = bb_middle
        dataframe['BB_lower'] = bb_lower
        
        # Stochastic Oscillator 계산
        stoch_k, stoch_d = ta.STOCH(
            dataframe['high'], dataframe['low'], dataframe['close'],
            fastk_period=14, slowk_period=3, slowd_period=3
        )
        dataframe['Stoch_%K'] = stoch_k
        dataframe['Stoch_%D'] = stoch_d
        
        # Directional Indicators 계산
        dataframe['DI_plus'] = ta.PLUS_DI(
            dataframe['high'], dataframe['low'], dataframe['close'], timeperiod=14
        )
        dataframe['DI_minus'] = ta.MINUS_DI(
            dataframe['high'], dataframe['low'], dataframe['close'], timeperiod=14
        )
        
        # 결측치 제거 대신 채우기를 사용하여 데이터프레임 길이 유지
        dataframe.fillna(0, inplace=True)
        
        # 모델 입력 피처 준비 (훈련시 사용한 순서와 동일하게)
        feature_columns = [
            'RSI', 'MFI', 'ADX', 'EMA_20', 'MACD', 'MACD_signal', 'MACD_hist',
            'BB_upper', 'BB_middle', 'BB_lower', 'Stoch_%K', 'Stoch_%D', 'DI_plus', 'DI_minus'
        ]
        features = dataframe[feature_columns].values
        
        # 머신러닝 모델 예측 (상승 확률)
        if self.model:
            try:
                proba = self.model.predict_proba(features)
                dataframe['pred_prob'] = proba[:, 1]
            except Exception as e:
                dataframe['pred_prob'] = 0.0
        else:
            dataframe['pred_prob'] = 0.0

        return dataframe

    def populate_entry_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        dataframe['enter_long'] = 0
        dataframe['enter_short'] = 0

        # 조건 완화 (예측 확률, RSI, EMA 및 ADX 조건)
        long_conditions = [
            dataframe['pred_prob'] > 0.55,
            dataframe['RSI'] < 40,
            dataframe['close'] > dataframe['EMA_20'],
            dataframe['ADX'] > 20
        ]
        if long_conditions:
            dataframe.loc[np.logical_and.reduce(long_conditions), 'enter_long'] = 1

        short_conditions = [
            dataframe['pred_prob'] < 0.45,
            dataframe['RSI'] > 60,
            dataframe['close'] < dataframe['EMA_20'],
            dataframe['ADX'] > 20
        ]
        if short_conditions:
            dataframe.loc[np.logical_and.reduce(short_conditions), 'enter_short'] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        dataframe['exit_long'] = 0
        dataframe['exit_short'] = 0

        # 청산 조건 설정: 롱 포지션은 예측 확률이 낮거나 RSI가 높으면, 숏 포지션은 반대로 설정
        exit_long_cond = (dataframe['pred_prob'] < 0.5) | (dataframe['RSI'] > 60)
        exit_short_cond = (dataframe['pred_prob'] > 0.5) | (dataframe['RSI'] < 40)
        dataframe.loc[exit_long_cond, 'exit_long'] = 1
        dataframe.loc[exit_short_cond, 'exit_short'] = 1

        return dataframe
