# source: https://raw.githubusercontent.com/remiotore/freqtrade/44beaeb6a420cd8e9f2e4ea93e11d6cfa192ee03/strategies/freqalgo.py
from freqtrade.strategy import IStrategy
from freqtrade.strategy.parameters import DecimalParameter, IntParameter
from pandas_ta.core import ta
import pandas as pd
import numpy as np
from datetime import datetime
from freqtrade.persistence import Trade
from functools import reduce
import talib.abstract as ta_lib

class Github_remiotore_freqtrade__freqalgo__20260111_210550(IStrategy):
    """
    Freqtrade strategia oparta na ALGOX Pine Script dla timeframe 15m.
    Dla trybu spot: tylko long positions (buy/sell), bez shortów.
    Wejścia na podstawie crossoverów Heikin Ashi lub Renko EMA.
    Wyjścia z TP/SL na podstawie ATR.
    """

    INTERFACE_VERSION = 3

    # Ustawienie timeframe na 15 minut
    timeframe = "15m"

    # Minimal ROI dla spot
    minimal_roi = {"0": 0.01}  # Minimalny ROI, np. 1%

    # Stoploss (domyślny)
    stoploss = -0.05  # -5% SL domyślny, ale używamy custom

    # Trailing stop (opcjonalnie)
    trailing_stop = False

    # Parametry optymalizacyjne
    startup_candle_count = 500  # Max bars back
    tp_type = "ATR"  # 'ATR', 'Trailing', 'Options' - dla spot używamy ATR dla TP/SL
    setup_type = "Open/Close"  # 'Open/Close' lub 'Renko'
    atr_length = IntParameter(10, 30, default=20, space="buy")
    profit_factor = DecimalParameter(1.0, 4.0, default=2.5, space="sell")
    stop_factor = DecimalParameter(0.5, 2.0, default=1.0, space="sell")
    ema1_length = IntParameter(1, 5, default=2, space="buy")
    ema2_length = IntParameter(5, 15, default=10, space="buy")
    atr_len_renko = IntParameter(1, 10, default=3, space="buy")

    def informative_pairs(self):
        return []

    def populate_indicators(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        # Heikin Ashi candles
        heikinashi = ta.ha(dataframe['open'], dataframe['high'], dataframe['low'], dataframe['close'])
        dataframe['ha_open'] = heikinashi['HA_open']
        dataframe['ha_close'] = heikinashi['HA_close']

        # ATR
        dataframe['atr'] = ta.atr(dataframe['high'], dataframe['low'], dataframe['close'], length=self.atr_length.value)

        if self.setup_type == "Renko":
            # Symulacja Renko (prosta, bo Freqtrade nie ma wbudowanego Renko; używamy ATR-based bricks)
            brick_size = dataframe['atr'].rolling(window=self.atr_len_renko.value).mean()
            # Prosta symulacja Renko close/open
            dataframe['renko_close'] = dataframe['close']  # Placeholder; dostosuj jeśli masz lepszą implementację
            dataframe['renko_open'] = dataframe['open']
            dataframe['ema1'] = ta.ema(dataframe['renko_close'], length=self.ema1_length.value)
            dataframe['ema2'] = ta.ema(dataframe['renko_close'], length=self.ema2_length.value)
        else:
            # Dla Open/Close używamy Heikin Ashi
            dataframe['ema1'] = ta.ema(dataframe['ha_close'], length=self.ema1_length.value)
            dataframe['ema2'] = ta.ema(dataframe['ha_close'], length=self.ema2_length.value)

        return dataframe

    def populate_entry_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        conditions_long = []

        if self.setup_type == "Open/Close":
            # Crossover ha_close > ha_open
            conditions_long.append(dataframe['ha_close'] > dataframe['ha_open'])
            conditions_long.append(dataframe['ha_close'].shift(1) <= dataframe['ha_open'].shift(1))
        elif self.setup_type == "Renko":
            # Crossover ema1 > ema2
            conditions_long.append(dataframe['ema1'] > dataframe['ema2'])
            conditions_long.append(dataframe['ema1'].shift(1) <= dataframe['ema2'].shift(1))

        dataframe.loc[reduce(lambda x, y: x & y, conditions_long), 'enter_long'] = 1
        dataframe['enter_tag'] = 'long_entry'

        # Brak shortów dla spot
        dataframe['enter_short'] = 0

        return dataframe

    def populate_exit_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        conditions_short = []

        if self.setup_type == "Open/Close":
            conditions_short.append(dataframe['ha_close'] < dataframe['ha_open'])
            conditions_short.append(dataframe['ha_close'].shift(1) >= dataframe['ha_open'].shift(1))
        elif self.setup_type == "Renko":
            conditions_short.append(dataframe['ema1'] < dataframe['ema2'])
            conditions_short.append(dataframe['ema1'].shift(1) >= dataframe['ema2'].shift(1))

        dataframe.loc[reduce(lambda x, y: x & y, conditions_short), 'exit_long'] = 1
        dataframe['exit_tag'] = 'long_exit'

        dataframe['exit_short'] = 0
        return dataframe

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float,
                        current_profit: float, **kwargs) -> float:
        """
        Custom stoploss oparty na ATR SL.
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()

        sl_price = trade.open_rate - (self.stop_factor.value * last_candle['atr'])
        sl_offset = (sl_price / trade.open_rate) - 1

        return sl_offset

    def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float,
                    current_profit: float, **kwargs) -> str:
        """
        Custom exit dla TP1/TP2/TP3 oparty na ATR.
        """
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()

        tp1_price = trade.open_rate + (self.profit_factor.value * last_candle['atr'])
        tp2_price = trade.open_rate + (2 * self.profit_factor.value * last_candle['atr'])
        tp3_price = trade.open_rate + (3 * self.profit_factor.value * last_candle['atr'])

        if current_rate >= tp3_price:
            return 'tp3_exit'
        elif current_rate >= tp2_price:
            return 'tp2_exit'
        elif current_rate >= tp1_price:
            return 'tp1_exit'

        return None

    def adjust_trade_position(self, trade: Trade, current_time: datetime,
                              current_rate: float, current_profit: float,
                              min_stake: float, max_stake: float,
                              current_entry_rate: float, current_exit_rate: float,
                              current_entry_profit: float, current_exit_profit: float,
                              **kwargs):
        """
        Partial exits dla TP1/TP2/TP3.
        """
        exit_reason = self.custom_exit(
            pair=trade.pair, trade=trade, current_time=current_time,
            current_rate=current_rate, current_profit=current_profit
        )
        if exit_reason == 'tp1_exit':
            return - (trade.amount * 0.5)  # 50% qty
        elif exit_reason == 'tp2_exit':
            return - (trade.amount * 0.3)  # 30% qty
        elif exit_reason == 'tp3_exit':
            return - (trade.amount * 0.2)  # 20% qty

        return None
