# source: https://raw.githubusercontent.com/GoodyNick/Freqai-Strategy/7fd33daeeee90ffa1f11d1418314d9f4681e74cc/strategies/LSTMStrategy.py
import numpy as np
import pandas as pd
import torch
import talib.abstract as ta
from technical import qtpylib
from functools import reduce
from freqtrade.strategy import IStrategy
from freqtrade.persistence import Trade
from freqaimodels.lstm_model import CryptoLSTM


class Github_GoodyNick_Freqai_Strategy__LSTMStrategy__20250226_175652(IStrategy):
    """
    FreqAI strategy using LSTM model for trade execution.
    """
    minimal_roi = {"0": 1}  # Let the model decide exits
    stoploss = -1  # Fully model-based exits
    timeframe = "1h"
    can_short = True
    use_exit_signal = True
    use_custom_stoploss = True
    process_only_new_candles = True
    startup_candle_count = 50

    def __init__(self, config):
        super().__init__(config)
        self.device = 'cuda' if torch.cuda.is_available() else 'cpu'
        self.model = CryptoLSTM(input_size=10, hidden_size=32, num_layers=2).to(self.device)
        self.model.load_state_dict(torch.load("lstm_model.pth", map_location=self.device))
        self.model.eval()

    def feature_engineering_expand_all(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        """
        Expands all features for FreqAI and applies filtering before training.
        """
        dataframe["%-cci-period"] = ta.CCI(dataframe, timeperiod=20)
        dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=10)
        dataframe["%-momentum-period"] = ta.MOM(dataframe, timeperiod=4)
        dataframe['%-ma-period'] = ta.SMA(dataframe, timeperiod=10)
        dataframe['%-macd-period'], dataframe['%-macdsignal-period'], dataframe['%-macdhist-period'] = ta.MACD(
            dataframe['close'], slowperiod=12, fastperiod=26
        )
        dataframe['%-roc-period'] = ta.ROC(dataframe, timeperiod=2)

        return dataframe

    def set_freqai_targets(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        """
        Assigns the prediction target column for FreqAI.
        """
        dataframe['&-s_target'] = self.create_target_T(dataframe)
        return dataframe

    def create_target_T(self, dataframe: pd.DataFrame):
        """
        Creates a target label (T) using scaled log future returns.
        """
        lookahead = 10
        dataframe['future_return'] = np.log(dataframe['close'].shift(-lookahead) / dataframe['close'])
        dataframe['T'] = dataframe['future_return'].rolling(window=5).mean() * 100
        dataframe['T'] = dataframe['T'].fillna(0)
        return dataframe['T']

    def populate_indicators(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
        dataframe['dynamic_stoploss'] = np.clip(-dataframe['atr'] * 1.75, -0.03, -0.10)
        dataframe = self.freqai.start(dataframe, metadata, self)
        return dataframe

    def populate_entry_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        confidence_threshold = 0.8
        prediction_mean = dataframe["&-s_target"].rolling(100).mean()
        prediction_std = dataframe["&-s_target"].rolling(100).std()
        long_threshold = prediction_mean + 0.5 * prediction_std
        short_threshold = prediction_mean - 0.5 * prediction_std

        enter_long_conditions = [
            dataframe["do_predict"] == 1,
            qtpylib.crossed_above(dataframe["&-s_target"], long_threshold),
            dataframe["prediction_confidence"] > confidence_threshold,
            dataframe["volume"] > 0
        ]

        enter_short_conditions = [
            dataframe["do_predict"] == 1,
            qtpylib.crossed_below(dataframe["&-s_target"], short_threshold),
            dataframe["prediction_confidence"] > confidence_threshold,
            dataframe["volume"] > 0
        ]

        dataframe.loc[reduce(lambda x, y: x & y, enter_long_conditions), ["enter_long", "enter_tag"]] = (1, "long")
        dataframe.loc[reduce(lambda x, y: x & y, enter_short_conditions), ["enter_short", "enter_tag"]] = (1, "short")
        return dataframe

    def populate_exit_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        confidence_threshold = 0.85
        exit_threshold = dataframe["&-s_target"].rolling(100).median()

        strong_exit_long_conditions = [
            dataframe["do_predict"] == 1,
            qtpylib.crossed_below(dataframe["&-s_target"], exit_threshold),
            dataframe["prediction_confidence"] > confidence_threshold
        ]

        strong_exit_short_conditions = [
            dataframe["do_predict"] == 1,
            qtpylib.crossed_above(dataframe["&-s_target"], exit_threshold),
            dataframe["prediction_confidence"] > confidence_threshold
        ]

        dataframe.loc[reduce(lambda x, y: x & y, strong_exit_long_conditions), ["exit_long", "exit_tag"]] = (1, "strong_exit_long")
        dataframe.loc[reduce(lambda x, y: x & y, strong_exit_short_conditions), ["exit_short", "exit_tag"]] = (1, "strong_exit_short")
        return dataframe
