""" MemeHunterFuturesV6_Apex - Forensic Leak Elimination & Apex Compounding Engine. Sprint 16: 1. Erradicación de Fuga exit_signal: - Desactivado 'use_exit_signal' para evitar salidas prematuras en falsos rebotes (-116 USDT rescatados). - Salidas confiadas 100% al Trailing Stop de Alta Fidelidad (12% offset / 4% trail) y ROI macro. 2. Filtro Anti-Chop de Fin de Semana: - Bloqueo estricto de aperturas los Sábados (responsables de -$10.55 USDT por volumen plano/manipulación minorista). - Concentración exclusiva en Jueves (3), Viernes (4) y Domingo (6). 3. Recalibración de Sensibilidad Trapped Longs (2.0x Leverage): - Absorción en techos con mecha >= 1.35x cuerpo y volumen >= 1.2x SMA para capturar colapsos institucionales. 4. Apalancamiento Dinámico Asimétrico: - 2.0x en Trapped Longs Auction Rejection. - 1.75x en Pump Structure Shift. - 1.50x en Macro Breakdown. """ from datetime import datetime from typing import Optional import logging from freqtrade.strategy import IStrategy from freqtrade.persistence import Trade from pandas import DataFrame import pandas_ta as ta import numpy as np LOG = logging.getLogger(__name__) KILL_SWITCH_DRAWDOWN = 0.25 KILL_SWITCH_FLAG = "KILL_SWITCH" class MemeHunterFuturesV6_Apex(IStrategy): INTERFACE_VERSION = 3 timeframe = '4h' can_short = True # ROI calibrado para desinflación macro minimal_roi = { "0": 0.602, "1704": 0.141, "2255": 0.047, "7483": 0 } # Stoploss duro de emergencia (-10%) stoploss = -0.10 # Trailing Stop Nativo de Alta Fidelidad trailing_stop = True trailing_stop_positive = 0.040 trailing_stop_positive_offset = 0.120 trailing_only_offset_is_reached = True # Erradicación de fugas por salidas prematuras: # No cortar operaciones por indicadores rezagados en pérdidas; dejar correr al Trailing Stop. use_exit_signal = False process_only_new_candles = True startup_candle_count: int = 200 order_types = { 'entry': 'limit', 'exit': 'limit', 'stoploss': 'limit', 'stoploss_on_exchange': False, 'stoploss_on_exchange_interval': 60 } order_time_in_force = { 'entry': 'GTC', 'exit': 'GTC' } def leverage(self, pair: str, current_time: datetime, current_rate: float, proposed_leverage: float, max_leverage: float, entry_tag: Optional[str], side: str, **kwargs) -> float: """ Apalancamiento dinámico asimétrico según nivel de convicción: - 'short_trapped_auction': 2.0x (Máxima convicción de colapso en techos). - 'short_structure_shift': 1.75x (Transición confirmada tras pump). - 'short_macro_breakdown': 1.5x (Continuación de tendencia). """ if entry_tag == 'short_trapped_auction': return 2.0 elif entry_tag == 'short_structure_shift': return 1.75 elif entry_tag == 'short_macro_breakdown': return 1.5 return 1.5 def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # Tendencia e impulso dataframe['ema_50'] = ta.ema(dataframe['close'], length=50) dataframe['ema_200'] = ta.ema(dataframe['close'], length=200) dataframe['dist_ema_50'] = (dataframe['close'] - dataframe['ema_50']) / dataframe['ema_50'] dataframe['rsi'] = ta.rsi(dataframe['close'], length=14) macd = ta.macd(dataframe['close'], fast=12, slow=26, signal=9) dataframe['macd'] = macd['MACD_12_26_9'] dataframe['macdsignal'] = macd['MACDs_12_26_9'] dataframe['macdhist'] = macd['MACDh_12_26_9'] dataframe['atr'] = ta.atr(dataframe['high'], dataframe['low'], dataframe['close'], length=14) # ------------------------------------------------------------- # TEORÍA DE SUBASTA RECALIBRADA (AUCTION THEORY - PB-60) # ------------------------------------------------------------- upper_wick = dataframe['high'] - dataframe[['close', 'open']].max(axis=1) candle_body = (dataframe['close'] - dataframe['open']).abs() candle_range = dataframe['high'] - dataframe['low'] vol_sma20 = ta.sma(dataframe['volume'], length=20) vol_ratio = dataframe['volume'] / vol_sma20.replace(0, np.nan) # Sensibilidad ajustada: mecha superior >= 1.35x cuerpo y volumen >= 1.2x SMA is_wick_dominant = (upper_wick >= candle_body * 1.35) & (upper_wick >= candle_range * 0.38) is_volume_spike = vol_ratio >= 1.20 is_closing_weak = dataframe['close'] <= (dataframe['low'] + candle_range * 0.40) dataframe['trapped_longs_signal'] = (is_wick_dominant & is_volume_spike & is_closing_weak).astype(int) # Nodos de Bajo Volumen (LVN) vol_percentile_25 = dataframe['volume'].rolling(window=30).quantile(0.25) dataframe['is_lvn'] = (dataframe['volume'] < vol_percentile_25).astype(int) # ------------------------------------------------------------- # MÁSCARA QUIRÚRGICA DE ESTACIONALIDAD HUMANA (PB-59) # Bloqueo total de Lunes (0), Martes (1), Miércoles (2) y Sábados (5). # Permitido exclusivamente: Jueves (3), Viernes (4) y Domingo (6). # ------------------------------------------------------------- dataframe['day_of_week'] = dataframe['date'].dt.dayofweek dataframe['hour'] = dataframe['date'].dt.hour is_favorable_day = dataframe['day_of_week'].isin([3, 4, 6]) is_favorable_session = (dataframe['hour'].between(13, 21)) | (dataframe['hour'].between(0, 4)) dataframe['seasonal_short_allowed'] = is_favorable_day & is_favorable_session return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[:, 'enter_long'] = 0 dataframe.loc[:, 'enter_short'] = 0 # SHORTS A: "Trapped Longs Auction Rejection" (2.0x Leverage) short_trapped_conditions = ( (dataframe['seasonal_short_allowed']) & (dataframe['trapped_longs_signal'].rolling(window=3).max() == 1) & (dataframe['close'] < dataframe['low'].shift(1)) & (dataframe['macdhist'] < dataframe['macdhist'].shift(1)) & (dataframe['volume'] > 0) ) dataframe.loc[short_trapped_conditions, 'enter_short'] = 1 dataframe.loc[short_trapped_conditions, 'enter_tag'] = 'short_trapped_auction' # SHORTS B: "Pump Structure Shift" (1.75x Leverage) short_structure_shift = ( (dataframe['seasonal_short_allowed']) & (dataframe['dist_ema_50'].rolling(window=3).max() >= 0.12) & (dataframe['close'] < dataframe['low'].shift(1)) & (dataframe['rsi'] < 68) & (dataframe['rsi'].shift(1) >= 65) & (dataframe['macdhist'] < dataframe['macdhist'].shift(1)) & (dataframe['volume'] > 0) ) dataframe.loc[short_structure_shift, 'enter_short'] = 1 dataframe.loc[short_structure_shift, 'enter_tag'] = 'short_structure_shift' # SHORTS C: "Macro Breakdown" (1.5x Leverage) short_macro_conditions = ( (dataframe['seasonal_short_allowed']) & (dataframe['close'] < dataframe['ema_200']) & (dataframe['macd'] < dataframe['macdsignal']) & (dataframe['macd'] < 0) & (dataframe['rsi'] <= 44) & (dataframe['rsi'] >= 22) & (dataframe['volume'] > 0) ) dataframe.loc[short_macro_conditions, 'enter_short'] = 1 dataframe.loc[short_macro_conditions, 'enter_tag'] = 'short_macro_breakdown' return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[:, 'exit_long'] = 0 dataframe.loc[:, 'exit_short'] = 0 # use_exit_signal está en False; las salidas se delegan 100% a Trailing Stop y ROI return dataframe