# source: https://raw.githubusercontent.com/aicoincom/coinos-skills/1b9b59c2dd5c7e32a2f7e480fc11dad32ccf8425/skills/aicoin-freqtrade/strategies/WhaleFollowStrategy.py
# Github_aicoincom_coinos_skills__WhaleFollowStrategy__20260520_030511 - Follow whale/institutional order flow
# Powered by AiCoin's exclusive big_orders data (200+ exchanges aggregated)
#
# How it works:
#   - Standard indicators (RSI + EMA) provide base signals (works in backtest)
#   - In live/dry-run mode, AiCoin whale data adds an edge:
#     * big_orders: detect large institutional buy/sell pressure
#     * ls_ratio: cross-exchange long/short ratio as contrarian signal
#   - When whales are buying AND retail is short -> strong long signal
#   - When whales are selling AND retail is long -> strong short signal
#
# AiCoin tier required: Normal ($99/mo) for big_orders, Basic ($29/mo) for ls_ratio
# Backtest: works with standard indicators only (conservative estimate)
# Live: AiCoin data adds alpha on top of base signals
#
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter
from pandas import DataFrame
import logging

logger = logging.getLogger(__name__)


class Github_aicoincom_coinos_skills__WhaleFollowStrategy__20260520_030511(IStrategy):
    INTERFACE_VERSION = 3
    timeframe = '15m'
    can_short = True

    # ROI: take profit at these thresholds (optimized via hyperopt)
    minimal_roi = {"0": 0.316, "107": 0.106, "178": 0.047, "217": 0}

    stoploss = -0.236
    use_exit_signal = False  # ROI + trailing stop exits outperform signal-based exits

    trailing_stop = True
    trailing_stop_positive = 0.042
    trailing_stop_positive_offset = 0.061

    # Hyperopt-optimizable parameters (defaults from hyperopt optimization)
    rsi_buy = IntParameter(20, 40, default=34, space='buy')
    rsi_sell = IntParameter(60, 80, default=65, space='sell')
    ema_fast_len = IntParameter(5, 15, default=9, space='buy')
    ema_slow_len = IntParameter(15, 30, default=23, space='buy')
    whale_weight = DecimalParameter(0.0, 1.0, default=0.324, space='buy')

    # AiCoin data (updated periodically in live mode)
    _ac_whale_signal = 0.0   # -1 (selling) to +1 (buying)
    _ac_ls_ratio = 0.5       # 0-1, >0.5 = more longs
    _ac_last_update = 0.0

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # ── Standard indicators (always available) ──
        # RSI
        delta = dataframe['close'].diff()
        gain = delta.clip(lower=0).rolling(window=14).mean()
        loss = (-delta.clip(upper=0)).rolling(window=14).mean()
        rs = gain / loss
        dataframe['rsi'] = 100 - (100 / (1 + rs))

        # EMA
        dataframe['ema_fast'] = dataframe['close'].ewm(
            span=self.ema_fast_len.value, adjust=False).mean()
        dataframe['ema_slow'] = dataframe['close'].ewm(
            span=self.ema_slow_len.value, adjust=False).mean()

        # Volume SMA (for volume confirmation)
        dataframe['vol_sma'] = dataframe['volume'].rolling(window=20).mean()

        # ── AiCoin whale data (live/dry-run only) ──
        dataframe['whale_signal'] = 0.0
        dataframe['ls_ratio'] = 0.5

        if self.dp and self.dp.runmode.value in ('live', 'dry_run'):
            import time
            now = time.time()
            # Update AiCoin data every 5 minutes
            if now - self._ac_last_update > 300:
                self._update_aicoin_data(metadata)
                self._ac_last_update = now

            # Apply to last row (current candle)
            dataframe.iloc[-1, dataframe.columns.get_loc('whale_signal')] = self._ac_whale_signal
            dataframe.iloc[-1, dataframe.columns.get_loc('ls_ratio')] = self._ac_ls_ratio

        return dataframe

    def _update_aicoin_data(self, metadata: dict):
        """Fetch latest AiCoin whale data (live/dry-run only)."""
        try:
            import sys, os
            _sd = os.path.dirname(os.path.abspath(__file__))
            if _sd not in sys.path:
                sys.path.insert(0, _sd)
            from aicoin_data import AiCoinData
            ac = AiCoinData(cache_ttl=300)
            pair = metadata.get('pair', 'BTC/USDT:USDT')
            exchange = self.config.get('exchange', {}).get('name', 'binance')

            # Whale order-book pressure: -1 (selling) .. +1 (buying)
            try:
                self._ac_whale_signal = ac.whale_signal(pair, exchange)
                logger.info(f"AiCoin whale signal for {pair}: {self._ac_whale_signal:.2f}")
            except Exception as e:
                logger.debug(f"AiCoin whale data unavailable: {e}")

            # Long/short ratio normalized to 0..1 ( >0.5 = more longs )
            try:
                self._ac_ls_ratio = ac.ls_ratio_norm()
                logger.info(f"AiCoin L/S ratio: {self._ac_ls_ratio:.2f}")
            except Exception as e:
                logger.debug(f"AiCoin ls_ratio unavailable: {e}")

        except ImportError:
            logger.warning("aicoin_data module not found. Run ft-deploy.mjs to install.")
        except Exception as e:
            logger.warning(f"AiCoin data error: {e}")

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        w = self.whale_weight.value

        # Long entry: uptrend + RSI low + whale buying + retail short
        dataframe.loc[
            (dataframe['rsi'] < self.rsi_buy.value) &
            (dataframe['ema_fast'] > dataframe['ema_slow']) &
            (dataframe['volume'] > dataframe['vol_sma'] * 0.5) &
            # AiCoin boost: whale buying (signal > 0) or no data (signal == 0)
            (dataframe['whale_signal'] >= -0.3 * w) &
            # AiCoin boost: contrarian - retail is short (ls_ratio < 0.5)
            (dataframe['ls_ratio'] <= 0.5 + 0.2 * (1 - w)),
            'enter_long'] = 1

        # Short entry: downtrend + RSI high + whale selling + retail long
        dataframe.loc[
            (dataframe['rsi'] > self.rsi_sell.value) &
            (dataframe['ema_fast'] < dataframe['ema_slow']) &
            (dataframe['volume'] > dataframe['vol_sma'] * 0.5) &
            (dataframe['whale_signal'] <= 0.3 * w) &
            (dataframe['ls_ratio'] >= 0.5 - 0.2 * (1 - w)),
            'enter_short'] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (dataframe['rsi'] > 75),
            'exit_long'] = 1

        dataframe.loc[
            (dataframe['rsi'] < 25),
            'exit_short'] = 1

        return dataframe
