# source: https://raw.githubusercontent.com/Bederf/lea-freqai-system/29e791a811926194bfcd253585516f157342cd5b/user_data/strategies/LeaFreqAIStrategy.py
"""
LEA FreqAI Strategy
Based on "Deep Learning in Quantitative Trading" by Zhang & Zohren (2025)

LSTM-based cryptocurrency trading with:
- Volatility targeting for position sizing
- Market regime detection via BTC/ETH correlation
- Walk-forward validation
- Sharpe ratio optimization
"""

from freqtrade.strategy.interface import IStrategy
from pandas import DataFrame
import numpy as np
import talib.abstract as ta


class Github_Bederf_lea_freqai_system__LeaFreqAIStrategy__20251004_171650(IStrategy):
    """
    LEA (LSTM Enhanced Alpha) Strategy with FreqAI integration.
    """

    # Basic settings
    timeframe = "5m"
    can_short = False

    # ROI and stoploss
    minimal_roi = {
        "0": 10  # 1000% - effectively disabled, let FreqAI handle exits
    }
    stoploss = -1.0  # -100% - effectively disabled, let FreqAI handle stops

    # Trailing stop
    trailing_stop = False

    # Run once per candle
    process_only_new_candles = True

    # Startup candle count
    startup_candle_count = 300

    # Order types
    order_types = {
        "entry": "limit",
        "exit": "limit",
        "stoploss": "market",
        "stoploss_on_exchange": False
    }

    # Informative pairs for market regime detection
    def informative_pairs(self):
        """
        Use BTC and ETH as informative pairs for market regime detection.
        """
        pairs = [
            ("BTC/USDT", "1h"),
            ("ETH/USDT", "1h")
        ]
        return pairs

    def populate_indicators(self, df: DataFrame, metadata: dict) -> DataFrame:
        """
        Generate features for FreqAI model.
        Based on paper's feature engineering approach.
        """

        # ============================================
        # Price-based features
        # ============================================

        # Returns at multiple horizons
        df["%ret_1"] = df["close"].pct_change(1)
        df["%ret_3"] = df["close"].pct_change(3)
        df["%ret_6"] = df["close"].pct_change(6)
        df["%mom_12"] = df["close"].pct_change(12)
        df["%mom_24"] = df["close"].pct_change(24)

        # Volatility features
        rng = (df["high"] - df["low"]).replace(0, np.nan)
        df["%atr14_rel"] = rng.rolling(14).mean() / df["close"]
        df["%rng_24"] = (df["high"].rolling(24).max() - df["low"].rolling(24).min()) / df["close"]

        # Z-scores (mean reversion signals)
        roll = df["close"].rolling(48)
        df["%z_48"] = (df["close"] - roll.mean()) / (roll.std().replace(0, np.nan))

        # Volume features
        df["%vol_z_48"] = (df["volume"] - df["volume"].rolling(48).mean()) / (
            df["volume"].rolling(48).std().replace(0, np.nan)
        )

        # ============================================
        # Technical indicators
        # ============================================

        # RSI
        df["%rsi"] = ta.RSI(df, timeperiod=14) / 100.0  # Normalize to 0-1

        # MACD
        macd = ta.MACD(df)
        df["%macd"] = macd["macd"] / df["close"]
        df["%macd_signal"] = macd["macdsignal"] / df["close"]

        # Bollinger Bands
        bollinger = ta.BBANDS(df)
        df["%bb_upper"] = (bollinger["upperband"] - df["close"]) / df["close"]
        df["%bb_lower"] = (df["close"] - bollinger["lowerband"]) / df["close"]

        # ============================================
        # Market regime features (BTC/ETH)
        # ============================================

        # Get informative pairs
        inf_btc = self.dp.get_pair_dataframe(pair="BTC/USDT", timeframe="1h")
        inf_eth = self.dp.get_pair_dataframe(pair="ETH/USDT", timeframe="1h")

        if not inf_btc.empty and not inf_eth.empty:
            # BTC trend strength
            inf_btc["%btc_trend"] = inf_btc["close"] / inf_btc["close"].rolling(168).mean() - 1.0

            # Market volatility (from BTC)
            btc_ret_1h = inf_btc["close"].pct_change()
            inf_btc["%market_vol"] = btc_ret_1h.rolling(48).std()

            # BTC dominance signal
            btc_mom = inf_btc["close"].pct_change(24)
            eth_mom = inf_eth["close"].pct_change(24)
            inf_btc["%btc_dom"] = btc_mom - eth_mom

            # Merge with main dataframe
            df = self.merge_informative_pair(
                base_df=df,
                informative_df=inf_btc,
                timeframe="1h",
                ffill=True,
                append_timeframe=True,
                prefix="mkt",
                columns=["%btc_trend", "%market_vol", "%btc_dom"],
            )

        # ============================================
        # Cleanup
        # ============================================

        # Replace infinities with NaN
        df.replace([np.inf, -np.inf], np.nan, inplace=True)

        # Forward fill NaN values
        df.fillna(method="ffill", inplace=True)
        df.fillna(method="bfill", inplace=True)

        return df

    def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
        """
        Entry signal generation.

        In FreqAI mode, we typically let the model decide entries.
        Setting enter_long=1 allows the model to enter when conditions are favorable.
        """
        df.loc[:, "enter_long"] = 1
        return df

    def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
        """
        Exit signal generation.

        In FreqAI mode, we typically let the model decide exits.
        Setting exit_long=0 prevents premature exits.
        """
        df.loc[:, "exit_long"] = 0
        return df

    def confirm_trade_entry(self, pair: str, order_type: str, amount: float,
                           rate: float, time_in_force: str, current_time, entry_tag, **kwargs) -> bool:
        """
        Optional: Add additional entry confirmation logic here.
        """
        return True

    def custom_exit(self, pair: str, trade, current_time, current_rate,
                    current_profit, **kwargs):
        """
        Optional: Custom exit logic.
        For now, let FreqAI handle exits.
        """
        return None
