💬 Forum

WhaleFollowStrategy

🏆 League #1883 / 1922

aicoincom/coinos-skills/skills/aicoin-freqtrade/strategies/WhaleFollowStrategy.py · ★49 · ⑂14 · first seen 2026-07-16 · repo updated 2026-06-09 · ⬇ 1 download

Basics mode: futures timeframe: 15m interface version: 3
Settings stoploss: -0.236 has minimal roi trailing hyperopt hyperopt params: 5
Concepts trailing
Other aicoin_data
15 related strategies ( identical code, similar name)

Each tile is a different kind of check — from an instant code lint to full sandboxed backtests and forward tests on recent data. Not sure what a check actually proves? See the FAQ →

Source

Download Raw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# WhaleFollowStrategy - 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 WhaleFollowStrategy(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