# source: https://raw.githubusercontent.com/vigoferrel/qbtc-unified/ef7ffc8f45cc7ab041884cd0c08a2043acc1f0cb/freqtrade_qbtc/strategies/QBTCQuantumAngular.py
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement

"""
🌌 QBTC QUANTUM ANGULAR STRATEGY - Filosofía Cuántica con Ángulos 🌌
Mantiene la filosofía QBTC pero mejora la implementación usando geometría angular

FILOSOFÍA QBTC:
- Resonancia cuántica basada en ángulos geométricos sagrados
- Vectores de precio como ondas electromagnéticas
- Coherencia angular entre múltiples timeframes
- Entanglement entre precio, volumen y momentum
- Fibonacci angles y golden ratio resonance
"""

import numpy as np
import pandas as pd
from pandas import DataFrame
from typing import Optional, Union, Dict, List, Tuple, Any
import math
import logging

from freqtrade.strategy import (
    IStrategy,
    IntParameter,
    DecimalParameter,
    BooleanParameter,
)

import talib.abstract as ta


class Github_vigoferrel_qbtc_unified__QBTCQuantumAngular__20251009_202533(IStrategy):
    """
    🌊 QBTC Quantum Angular Strategy - Filosofía cuántica con ángulos
    
    CONCEPTOS CLAVE:
    - Price Vector Angles: Ángulos de movimiento del precio
    - Quantum Resonance: Resonancia en ángulos fibonacci
    - Angular Momentum: Momentum basado en vectores angulares
    - Coherence Angles: Coherencia entre diferentes timeframes
    - Sacred Geometry: Ángulos dorados y fibonacci
    """
    
    INTERFACE_VERSION = 3
    can_short: bool = False
    
    timeframe = '15m'
    startup_candle_count: int = 89  # Fibonacci number
    
    # ==================== GEOMETRÍA SAGRADA ====================
    
    PHI = 1.618033988749  # Golden Ratio
    PHI_ANGLE = 137.5      # Golden Angle (360/φ²)
    FIBONACCI_ANGLES = [23.6, 38.2, 50.0, 61.8, 78.6]  # Fibonacci retracement angles
    SACRED_ANGLES = [30, 45, 60, 90, 120, 144, 180]     # Sacred geometry angles
    
    # ==================== ROI BASADO EN ÁNGULOS ====================
    
    minimal_roi = {
        "0": 0.0618,    # Golden ratio / 100 * 3.82
        "38": 0.0382,   # Fibonacci 38.2% 
        "61": 0.0236,   # Fibonacci 23.6%
        "89": 0.0144,   # 144/10000 (fibonacci)
        "144": 0.0089,  # 89/10000 (fibonacci)
    }
    
    stoploss = -0.0618  # Golden ratio percentage
    
    # ==================== PARÁMETROS ANGULARES ====================
    
    # Ángulos de entrada - basados en geometría sagrada
    price_vector_angle_min = DecimalParameter(15.0, 45.0, default=23.6, space="buy", optimize=True)
    price_vector_angle_max = DecimalParameter(45.0, 90.0, default=61.8, space="buy", optimize=True)
    
    # Resonancia cuántica - ángulos fibonacci
    quantum_resonance_threshold = DecimalParameter(0.382, 0.786, default=0.618, space="buy", optimize=True)
    
    # Coherencia angular
    angular_coherence_min = DecimalParameter(0.50, 0.80, default=0.618, space="buy", optimize=True)
    
    # Ángulos de salida
    exit_vector_angle = DecimalParameter(100.0, 160.0, default=137.5, space="sell", optimize=True)
    quantum_dissonance_threshold = DecimalParameter(0.20, 0.50, default=0.382, space="sell", optimize=True)
    
    def __init__(self, config: dict) -> None:
        super().__init__(config)
        self.logger = logging.getLogger(__name__)
        
    # ==================== INDICADORES ANGULARES CUÁNTICOS ====================
    
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Indicadores basados en geometría angular cuántica
        """
        
        # Indicadores técnicos base
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
        dataframe['rsi_21'] = ta.RSI(dataframe, timeperiod=21)  # Fibonacci 21
        dataframe['rsi_55'] = ta.RSI(dataframe, timeperiod=55)  # Fibonacci 55
        
        # MACD con períodos fibonacci
        macd = ta.MACD(dataframe, fastperiod=8, slowperiod=21, signalperiod=13)  # All fibonacci
        dataframe['macd'] = macd['macd']
        dataframe['macdsignal'] = macd['macdsignal']
        
        # EMAs fibonacci
        dataframe['ema8'] = ta.EMA(dataframe, timeperiod=8)
        dataframe['ema13'] = ta.EMA(dataframe, timeperiod=13)
        dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21)
        dataframe['ema34'] = ta.EMA(dataframe, timeperiod=34)
        dataframe['ema55'] = ta.EMA(dataframe, timeperiod=55)
        dataframe['ema89'] = ta.EMA(dataframe, timeperiod=89)
        
        # ========== CÁLCULOS ANGULARES CUÁNTICOS ==========
        
        # 1. Price Vector Angles - Ángulos de vectores de precio
        dataframe['price_vector_angle'] = self.calculate_price_vector_angles(dataframe)
        
        # 2. Quantum Resonance - Resonancia en ángulos fibonacci
        dataframe['quantum_resonance'] = self.calculate_quantum_resonance(dataframe)
        
        # 3. Angular Momentum - Momentum angular
        dataframe['angular_momentum'] = self.calculate_angular_momentum(dataframe)
        
        # 4. Coherence Angles - Coherencia angular entre timeframes
        dataframe['angular_coherence'] = self.calculate_angular_coherence(dataframe)
        
        # 5. Golden Ratio Confluence - Confluencia de ratio áureo
        dataframe['golden_confluence'] = self.calculate_golden_confluence(dataframe)
        
        # Volumen con peso fibonacci
        dataframe['volume_ema13'] = ta.EMA(dataframe['volume'], timeperiod=13)
        dataframe['volume_ratio'] = dataframe['volume'] / dataframe['volume_ema13']
        
        return dataframe
    
    def calculate_price_vector_angles(self, dataframe: DataFrame) -> pd.Series:
        """
        Calcula ángulos de vectores de movimiento del precio
        """
        try:
            # Calcular vectores de precio en diferentes períodos fibonacci
            price_changes_8 = dataframe['close'].pct_change(periods=8)
            price_changes_13 = dataframe['close'].pct_change(periods=13)
            price_changes_21 = dataframe['close'].pct_change(periods=21)
            
            # Convertir cambios de precio a ángulos (0-180 grados)
            angles_8 = np.arctan(price_changes_8 * 100) * 180 / np.pi + 90
            angles_13 = np.arctan(price_changes_13 * 100) * 180 / np.pi + 90
            angles_21 = np.arctan(price_changes_21 * 100) * 180 / np.pi + 90
            
            # Promedio ponderado de ángulos con pesos fibonacci
            vector_angles = (
                angles_8 * 0.382 +    # 38.2% peso
                angles_13 * 0.382 +   # 38.2% peso  
                angles_21 * 0.236     # 23.6% peso
            )
            
            # Normalizar a 0-180 grados
            vector_angles = np.clip(vector_angles, 0, 180)
            
            return vector_angles.fillna(90)  # 90 grados = neutral
            
        except Exception:
            return pd.Series([90] * len(dataframe), index=dataframe.index)
    
    def calculate_quantum_resonance(self, dataframe: DataFrame) -> pd.Series:
        """
        Calcula resonancia cuántica basada en ángulos fibonacci
        """
        try:
            # Resonancia basada en proximidad a ángulos fibonacci
            price_angles = dataframe['price_vector_angle']
            
            resonance_scores = []
            for angle in price_angles:
                if np.isnan(angle):
                    resonance_scores.append(0.5)
                    continue
                    
                # Calcular distancia mínima a ángulos fibonacci
                distances = [abs(angle - fib_angle) for fib_angle in self.FIBONACCI_ANGLES]
                min_distance = min(distances)
                
                # Resonancia alta cuando está cerca de ángulos fibonacci
                if min_distance <= 5.0:  # Dentro de 5 grados
                    resonance = 0.90
                elif min_distance <= 10.0:  # Dentro de 10 grados
                    resonance = 0.70
                elif min_distance <= 20.0:  # Dentro de 20 grados
                    resonance = 0.55
                else:
                    resonance = 0.35
                    
                # Bonus especial para golden angle (137.5°)
                if abs(angle - self.PHI_ANGLE) <= 3.0:
                    resonance *= 1.1
                    
                resonance_scores.append(min(resonance, 1.0))
            
            resonance_series = pd.Series(resonance_scores, index=dataframe.index)
            return resonance_series.ewm(span=13).mean()  # Suavizado fibonacci
            
        except Exception:
            return pd.Series([0.5] * len(dataframe), index=dataframe.index)
    
    def calculate_angular_momentum(self, dataframe: DataFrame) -> pd.Series:
        """
        Calcula momentum angular basado en velocidad de cambio de ángulos
        """
        try:
            price_angles = dataframe['price_vector_angle']
            
            # Velocidad angular (cambio de ángulo por período)
            angular_velocity = price_angles.diff(periods=5)  # 5 períodos lookback
            
            # Aceleración angular (cambio de velocidad)
            angular_acceleration = angular_velocity.diff(periods=3)  # 3 períodos
            
            # Momentum angular combinado
            momentum = (
                angular_velocity * 0.618 +      # Golden ratio weight
                angular_acceleration * 0.382    # Fibonacci weight
            )
            
            # Normalizar usando función sigmoidal
            normalized_momentum = 2 / (1 + np.exp(-momentum/10)) - 1  # Range [-1, 1]
            
            return normalized_momentum.fillna(0)
            
        except Exception:
            return pd.Series([0.0] * len(dataframe), index=dataframe.index)
    
    def calculate_angular_coherence(self, dataframe: DataFrame) -> pd.Series:
        """
        Calcula coherencia angular entre diferentes EMAs (timeframes)
        """
        try:
            # Calcular ángulos entre EMAs fibonacci
            angle_8_13 = np.arctan((dataframe['ema8'] - dataframe['ema13']) / dataframe['ema13']) * 180 / np.pi
            angle_13_21 = np.arctan((dataframe['ema13'] - dataframe['ema21']) / dataframe['ema21']) * 180 / np.pi
            angle_21_34 = np.arctan((dataframe['ema21'] - dataframe['ema34']) / dataframe['ema34']) * 180 / np.pi
            angle_34_55 = np.arctan((dataframe['ema34'] - dataframe['ema55']) / dataframe['ema55']) * 180 / np.pi
            
            # Coherencia = similaridad de ángulos (baja desviación estándar)
            angles_matrix = np.column_stack([angle_8_13, angle_13_21, angle_21_34, angle_34_55])
            angles_std = np.nanstd(angles_matrix, axis=1)
            
            # Coherencia alta cuando desviación es baja
            coherence = 1 / (1 + angles_std / 10)  # Normalizado 0-1
            
            return pd.Series(coherence, index=dataframe.index).ewm(span=8).mean()
            
        except Exception:
            return pd.Series([0.5] * len(dataframe), index=dataframe.index)
    
    def calculate_golden_confluence(self, dataframe: DataFrame) -> pd.Series:
        """
        Calcula confluencia basada en el golden ratio
        """
        try:
            # Niveles de precio basados en golden ratio
            high_21 = dataframe['high'].rolling(window=21).max()
            low_21 = dataframe['low'].rolling(window=21).min()
            
            # Niveles fibonacci del rango
            range_21 = high_21 - low_21
            fib_236 = low_21 + range_21 * 0.236
            fib_382 = low_21 + range_21 * 0.382
            fib_618 = low_21 + range_21 * 0.618
            fib_786 = low_21 + range_21 * 0.786
            
            # Distancia del precio actual a niveles fibonacci
            current_price = dataframe['close']
            distances = np.column_stack([
                abs(current_price - fib_236) / current_price,
                abs(current_price - fib_382) / current_price,
                abs(current_price - fib_618) / current_price,
                abs(current_price - fib_786) / current_price
            ])
            
            min_distances = np.min(distances, axis=1)
            
            # Confluencia alta cuando está cerca de niveles fibonacci
            confluence = 1 / (1 + min_distances * 100)
            
            return pd.Series(confluence, index=dataframe.index).ewm(span=13).mean()
            
        except Exception:
            return pd.Series([0.5] * len(dataframe), index=dataframe.index)
    
    # ==================== LÓGICA DE ENTRADA ANGULAR ====================
    
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Lógica de entrada basada en filosofía angular cuántica
        """
        
        # ========== CONDICIONES ANGULARES PRINCIPALES ==========
        
        # 1. Price Vector Angle en rango óptimo
        angle_condition = (
            (dataframe['price_vector_angle'] >= self.price_vector_angle_min.value) &
            (dataframe['price_vector_angle'] <= self.price_vector_angle_max.value)
        )
        
        # 2. Quantum Resonance alta (resonancia con ángulos fibonacci)
        resonance_condition = (
            dataframe['quantum_resonance'] >= self.quantum_resonance_threshold.value
        )
        
        # 3. Angular Coherence (coherencia entre timeframes)
        coherence_condition = (
            dataframe['angular_coherence'] >= self.angular_coherence_min.value
        )
        
        # ========== CONDICIONES DE SOPORTE ==========
        
        # 4. Momentum angular positivo
        momentum_condition = dataframe['angular_momentum'] > 0
        
        # 5. Golden confluence (proximidad a niveles fibonacci)
        confluence_condition = dataframe['golden_confluence'] > 0.5
        
        # 6. Confirmación técnica básica
        technical_condition = (
            (dataframe['rsi'] < 70) &  # No sobrecomprado
            (dataframe['macd'] > dataframe['macdsignal']) &  # MACD alcista
            (dataframe['volume_ratio'] > 0.8)  # Volumen aceptable
        )
        
        # ========== ENTRADA CON MÚLTIPLES CAMINOS ==========
        
        # Camino 1: Todas las condiciones angulares (más restrictivo pero poderoso)
        path_complete = (
            angle_condition & 
            resonance_condition & 
            coherence_condition & 
            momentum_condition &
            technical_condition
        )
        
        # Camino 2: Resonancia muy alta + soporte técnico (relajado)
        path_resonance = (
            (dataframe['quantum_resonance'] > self.quantum_resonance_threshold.value + 0.1) &
            momentum_condition &
            technical_condition
        )
        
        # Camino 3: Confluencia dorada + ángulos buenos
        path_golden = (
            angle_condition &
            confluence_condition &
            (dataframe['angular_coherence'] > 0.5) &
            technical_condition
        )
        
        # ENTRADA FINAL - cualquier camino válido
        entry_condition = path_complete | path_resonance | path_golden
        
        dataframe.loc[entry_condition, 'enter_long'] = 1
        
        return dataframe
    
    # ==================== LÓGICA DE SALIDA ANGULAR ====================
    
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Lógica de salida basada en disonancia angular
        """
        
        # ========== CONDICIONES DE SALIDA ANGULARES ==========
        
        # 1. Ángulo de vector indica reversión
        angle_exit = (
            dataframe['price_vector_angle'] > self.exit_vector_angle.value
        )
        
        # 2. Pérdida de resonancia cuántica
        dissonance_exit = (
            dataframe['quantum_resonance'] < self.quantum_dissonance_threshold.value
        )
        
        # 3. Momentum angular negativo fuerte
        momentum_exit = dataframe['angular_momentum'] < -0.3
        
        # 4. Pérdida de coherencia angular
        coherence_exit = dataframe['angular_coherence'] < 0.4
        
        # 5. Condiciones técnicas de salida
        technical_exit = (
            (dataframe['rsi'] > 75) |  # Sobrecomprado
            (
                (dataframe['macd'] < dataframe['macdsignal']) &
                (dataframe['rsi'] > 60)
            )
        )
        
        # ========== SALIDA FINAL ==========
        
        exit_condition = (
            angle_exit | 
            dissonance_exit | 
            momentum_exit | 
            coherence_exit |
            technical_exit
        )
        
        dataframe.loc[exit_condition, 'exit_long'] = 1
        
        return dataframe
