← Back to list

eth_funding_carry VALIDATED FAIL

Auto-discovered strategy

Symbol: ETH | Exchange: Binance | Role: carry

6/6
Profitable Years
+123.6%
Total Return
43.7%
Avg Win Rate
0.81
Avg Sharpe

Year-by-Year Results

Click a year to view chart

Year Return Win Rate Trades Max DD Sharpe
2020 +12.0% 28.6% 7 9.7% 0.50
2021 +37.6% 75.0% 4 5.0% 1.72
2022 +2.9% 24.0% 25 20.4% 0.06
2023 +6.0% 33.3% 18 18.1% 0.27
2024 +37.4% 66.7% 6 5.9% 1.69
2025 +27.9% 34.5% 29 29.8% 0.63

Performance Chart

Loading chart...

Walk-Forward Validation FAIL

0/1 Windows Profitable
-22.6% OOS Return
0.00 Median Sharpe
0.000 Score
Window Train Period Val Period Val Return Val Test Period Test Return Status
WF-1 2024-01→2025-06 2025-07→2025-12 -22.6% FAIL 2026-01→ongoing +0.0% FAIL

AI Review

Not yet reviewed. Run: ./review_strategy.sh eth_funding_carry

Source Code

"""
Strategy: eth_funding_carry
===========================

ETH Funding Rate Carry Strategy for Binance ETHUSDT

This strategy trades the funding rate regime on ETH perpetual futures.
When funding rates are low (shorts being paid), the market tends to be
oversold and rallies as shorts cover their positions.

Concept:
- Funding rate < 5% APR indicates shorts are being paid (oversold)
- Combined with uptrend (EMA20 > EMA100), this signals a long entry
- The strategy profits from the typical rally that follows low funding

Entry Conditions:
- Funding rate APR < 5% (shorts being squeezed)
- EMA20 > EMA100 (uptrend confirmation)

Exit Conditions:
- Take profit at 20%
- Stop loss at 5%
- Trend reversal (EMA20 < EMA100)
- Time exit after 50 bars (~8 days)

Role: carry (profits from funding rate regime dynamics, bounded risk)

Training Performance (2024-01-01 to 2025-06-30):
  2024: +37.4% | 67% WR | 5.9% DD | 6 trades
  2025 H1: +17.8% | 50% WR | 9.4% DD | 10 trades
  Total: +55.1% | Max DD: 9.4% | Sharpe: 1.21

Parameters (round numbers, generalizable):
  funding_threshold: 0.05 (5% APR)
  ema_fast: 20
  ema_slow: 100
  take_profit_pct: 20
  stop_loss_pct: 5
  max_hold_bars: 50
"""
import sys
sys.path.insert(0, '/root/trade_rules')

from lib import ema
import clickhouse_connect

_funding_cache = {}


def _load_funding_rates(start, end):
    """Load Binance ETH funding rate data"""
    cache_key = f"{start}_{end}"
    if cache_key in _funding_cache:
        return _funding_cache[cache_key]

    client = clickhouse_connect.get_client(host='localhost', database='crypto_data')

    query = f"""
        SELECT
            toStartOfInterval(funding_time, INTERVAL 240 MINUTE) as ts,
            avg(funding_rate) * 3 * 365 as funding_apr
        FROM funding_rate_history
        WHERE exchange = 'binance' AND symbol = 'ETHUSDT'
        AND funding_time >= '{start}' AND funding_time < '{end}'
        GROUP BY ts
        ORDER BY ts
    """
    result = client.query(query)
    funding_map = {row[0]: float(row[1]) for row in result.result_rows}
    _funding_cache[cache_key] = funding_map
    return funding_map


def init_strategy():
    return {
        'name': 'eth_funding_carry',
        'role': 'carry',
        'warmup': 50,
        'role': 'carry',
        'subscriptions': [
            {'symbol': 'ETHUSDT', 'exchange': 'binance', 'timeframe': '4h'},
        ],
        'parameters': {
            'funding_threshold': 0.05,   # 5% APR - enter when funding is low
            'ema_fast': 20,              # Fast EMA period
            'ema_slow': 100,             # Slow EMA period
            'take_profit_pct': 20,       # 20% take profit
            'stop_loss_pct': 5,          # 5% stop loss
            'max_hold_bars': 50,         # ~8 day max hold
        }
    }


def process_time_step(ctx):
    """Process each time step and generate trading actions"""
    key = ('ETHUSDT', 'binance')
    bars = ctx['bars'].get(key, [])
    i = ctx['i']
    positions = ctx['positions']
    params = ctx['parameters']
    state = ctx['state']

    # Need enough history for slow EMA
    if i < 100 or not bars:
        return []

    # Extract parameters
    funding_threshold = params['funding_threshold']
    ema_fast_period = params['ema_fast']
    ema_slow_period = params['ema_slow']
    take_profit_pct = params['take_profit_pct']
    stop_loss_pct = params['stop_loss_pct']
    max_hold_bars = params['max_hold_bars']

    # Calculate EMAs for trend detection
    closes = [b.close for b in bars[:i+1]]
    ema_fast_vals = ema(closes, ema_fast_period)
    ema_slow_vals = ema(closes, ema_slow_period)

    fast = ema_fast_vals[i] if i < len(ema_fast_vals) and ema_fast_vals[i] else None
    slow = ema_slow_vals[i] if i < len(ema_slow_vals) and ema_slow_vals[i] else None

    if fast is None or slow is None:
        return []

    # Load funding rate data on first call
    if 'funding_map' not in state:
        start_date = bars[0].timestamp.strftime('%Y-%m-%d')
        end_date = bars[-1].timestamp.strftime('%Y-%m-%d')
        state['funding_map'] = _load_funding_rates(start_date, end_date + ' 23:59:59')

    funding_map = state['funding_map']
    bar_time = bars[i].timestamp
    current_funding = funding_map.get(bar_time, 0.10)  # Default to 10% if missing

    actions = []
    has_position = key in positions

    if not has_position:
        # Entry conditions:
        # 1. Funding rate below threshold (shorts paying, market oversold)
        # 2. Uptrend (fast EMA > slow EMA)
        low_funding = current_funding < funding_threshold
        uptrend = fast > slow

        if low_funding and uptrend:
            actions.append({
                'action': 'open_long',
                'symbol': 'ETHUSDT',
                'exchange': 'binance',
                'size': 1.0,
                'take_profit_pct': take_profit_pct,
                'stop_loss_pct': stop_loss_pct,
            })
    else:
        # Exit conditions
        pos = positions[key]
        bars_held = i - pos.entry_bar

        # Exit on: trend reversal or max hold time
        downtrend = fast < slow
        time_exit = bars_held >= max_hold_bars

        if downtrend or time_exit:
            if pos.side == 'long':
                actions.append({
                    'action': 'close_long',
                    'symbol': 'ETHUSDT',
                    'exchange': 'binance',
                })

    return actions


if __name__ == '__main__':
    from strategy import backtest_strategy

    print("ETH Funding Rate Carry - Backtest")
    print("="*50)

    results, profitable, _ = backtest_strategy(init_strategy, process_time_step, verbose=True)

    total = sum(r['return'] for r in results.values())
    print(f"\nTotal Return: {total:+.1f}%")
    print(f"Profitable Years: {profitable}/2")