# source: https://raw.githubusercontent.com/Germoso/ft_userdata/b2ef971ea5cab6f7f669dfc1a16a23307dc3315d/user_data/strategies/Random_Entry.py
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
# flake8: noqa: F401
# isort: skip_file
# --- Do not remove these imports ---
import numpy as np
import pandas as pd
import random
from datetime import datetime, timedelta, timezone
from pandas import DataFrame
from typing import Optional, Union
from functools import reduce

from freqtrade.strategy import (
    IStrategy,
    Trade,
    Order,
    PairLocks,
    informative,  # @informative decorator
    # Hyperopt Parameters
    BooleanParameter,
    CategoricalParameter,
    DecimalParameter,
    IntParameter,
    RealParameter,
    # timeframe helpers
    timeframe_to_minutes,
    timeframe_to_next_date,
    timeframe_to_prev_date,
    # Strategy helper functions
    merge_informative_pair,
    stoploss_from_absolute,
    stoploss_from_open,
)

# --------------------------------
# Add your lib to import here
import talib.abstract as ta
from technical import qtpylib


class Github_Germoso_ft_userdata__Random_Entry__20250608_142417(IStrategy):
    """
    Estrategia de entrada aleatoria con ratio riesgo/beneficio 1:1
    
    Esta estrategia:
    - Realiza operaciones tanto en largo como en corto de forma aleatoria
    - Permite sesgar la probabilidad hacia largo o corto mediante un parámetro
    - Mantiene un ratio riesgo/beneficio de 1:1
    - No utiliza indicadores técnicos para las entradas, solo probabilidad
    """

    # Strategy interface version
    INTERFACE_VERSION = 3

    # Habilitar operaciones en corto
    can_short: bool = True

    # Parámetros optimizables
    # Probabilidad de entrar en largo (0-100)
    # 50 = igual probabilidad de largo y corto
    # >50 = más probabilidad de largo
    # <50 = más probabilidad de corto
    long_bias = IntParameter(low=0, high=100, default=50, space="buy", optimize=True, load=True)
    
    # Objetivo de beneficio (1:1 con el stoploss)
    profit_target = DecimalParameter(0.01, 0.10, default=0.05, space="sell", optimize=True, load=True)

    # Stoploss dinámico (se ajusta según el profit_target para mantener ratio 1:1)
    # Este valor se sobrescribe en el método __init__
    stoploss = -0.05

    # Timeframe
    timeframe = "15m"

    # Procesar solo nuevas velas
    process_only_new_candles = True

    # Configuración de señales de salida
    use_exit_signal = False  # No usamos señales de salida, solo ROI y stoploss
    exit_profit_only = False
    ignore_roi_if_entry_signal = False

    # Número de velas necesarias antes de producir señales válidas
    startup_candle_count: int = 1  # Solo necesitamos 1 vela para operar

    # Configuración de tiempo en vigor de las órdenes
    order_time_in_force = {"entry": "GTC", "exit": "GTC"}
    
    # Configuración de pricing para órdenes de mercado
    entry_pricing = {
        "price_side": "other",  # Requerido para órdenes de mercado
        "use_order_book": False,
        "order_book_top": 1,
        "price_last_balance": 0.0,
    }
    
    exit_pricing = {
        "price_side": "other",  # Requerido para órdenes de mercado
        "use_order_book": False,
        "order_book_top": 1,
    }

    def __init__(self, config: dict) -> None:
        """
        Inicializar la estrategia
        """
        super().__init__(config)
        
        # Inicializar la semilla aleatoria para reproducibilidad
        random.seed(42)
        
        # Configurar el ROI para que coincida con el profit_target
        # Esto se actualizará en el método populate_indicators
        self.minimal_roi = {"0": 0.05}
    
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Genera números aleatorios para tomar decisiones de entrada
        """
        # Actualizar el ROI y stoploss basado en el profit_target
        self.minimal_roi = {"0": self.profit_target.value}
        self.stoploss = -self.profit_target.value  # Stoploss igual al profit target para ratio 1:1
        
        # Generar un número aleatorio para cada vela
        # Este número determinará si entramos en largo, corto o no hacemos nada
        dataframe['random_number'] = np.random.randint(0, 100, size=len(dataframe))
        
        # Añadir una columna para la volatilidad (ATR simple)
        # Esto es solo para información, no se usa en la toma de decisiones
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
        
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Define las condiciones para entrar en posiciones basadas en probabilidad
        """
        # Inicializar columnas de entrada
        dataframe['enter_long'] = 0
        dataframe['enter_short'] = 0
        
        # Probabilidad de entrar en largo basada en el parámetro long_bias
        long_threshold = self.long_bias.value
        
        # Entrar en largo si el número aleatorio es menor que long_threshold
        dataframe.loc[dataframe['random_number'] < long_threshold, 'enter_long'] = 1
        
        # Entrar en corto si el número aleatorio es mayor que (100 - (100 - long_threshold))
        # Esto asegura que no entremos en largo y corto al mismo tiempo
        dataframe.loc[dataframe['random_number'] >= long_threshold, 'enter_short'] = 1
        
        # Verificar que el volumen no sea 0 para evitar errores
        dataframe.loc[dataframe['volume'] <= 0, 'enter_long'] = 0
        dataframe.loc[dataframe['volume'] <= 0, 'enter_short'] = 0
        
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        No utilizamos señales de salida específicas ya que saldremos 
        cuando alcancemos el profit target o el stoploss.
        """
        return dataframe
    
    def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
                           time_in_force: str, current_time: datetime, entry_tag: Optional[str],
                           side: str, **kwargs) -> bool:
        """
        Confirma la entrada en una operación.
        Podemos añadir lógica adicional aquí si queremos.
        """
        # Imprimir información sobre la entrada
        direction = "LONG" if side == "buy" else "SHORT"
        profit_target = self.profit_target.value
        stop_loss = -self.profit_target.value
        
        print(f"Entrando en {direction} para {pair} con profit target: {profit_target:.2%} y stoploss: {stop_loss:.2%}")
        print(f"Probabilidad configurada: {self.long_bias.value}% para LONG, {100-self.long_bias.value}% para SHORT")
        
        # Siempre permitimos la entrada
        return True

    def adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float,
                             current_profit: float, min_stake: float, max_stake: float,
                             **kwargs) -> Optional[float]:
        """
        No ajustamos posiciones en esta estrategia aleatoria.
        """
        return None
