Auto-discovered strategy
Symbol: ETH | Exchange: Bitfinex | Role: momentum
Click a year to view chart
| Year | Return | Win Rate | Trades | Max DD | Sharpe |
|---|---|---|---|---|---|
| 2020 | +34.1% | 30.6% | 62 | 28.5% | 0.68 |
| 2021 | +58.8% | 40.4% | 47 | 18.8% | 1.21 |
| 2022 | -19.3% | 43.5% | 23 | 27.9% | -1.18 |
| 2023 | -25.8% | 31.6% | 38 | 26.2% | -1.22 |
| 2024 | +19.3% | 40.0% | 30 | 18.6% | 0.77 |
| 2025 | +23.6% | 29.0% | 31 | 22.0% | 0.53 |
| Window | Train Period | Val Period | Val Return | Val | Test Period | Test Return | Status |
|---|---|---|---|---|---|---|---|
| WF-1 | 2024-01→2025-06 | 2025-07→2025-12 | -12.8% | FAIL | 2026-01→ongoing | +0.0% | FAIL |
Not yet reviewed. Run: ./review_strategy.sh eth_hl_gap_fill
"""
ETH Higher Low Gap Fill Strategy
================================
Trades the structural gap fill pattern on ETH/USD. When price forms a higher low
pattern (bullish structure) and then breaks out above the range, this indicates
the "gap" between the prior resistance and current price is being filled with
strong momentum.
The strategy only trades in confirmed uptrends (EMA50 > EMA200) to avoid
counter-trend entries during bear markets.
Entry Logic:
- EMA50 > EMA200 (uptrend filter)
- Find swing low in last 5 bars
- Confirm it's higher than swing low 5-10 bars prior (higher low pattern)
- Current bar breaks above the range high formed between the two lows
- Current bar is bullish (close > open)
Exit Logic:
- Close below EMA50 (momentum lost)
- 25 bars elapsed (100 hours max hold)
- Trend break (EMA50 < EMA200)
- 5% stop loss
This is a momentum strategy that captures the continuation after a healthy
pullback in an uptrend. The "gap fill" refers to price filling the structural
gap created by the higher low formation.
"""
import sys
sys.path.insert(0, '/root/trade_rules')
from lib import ema
def init_strategy():
return {
'name': 'eth_hl_gap_fill',
'role': 'momentum',
'warmup': 200,
'subscriptions': [
{'symbol': 'tETHUSD', 'exchange': 'bitfinex', 'timeframe': '4h'},
],
'parameters': {}
}
def process_time_step(ctx):
key = ('tETHUSD', 'bitfinex')
bars = ctx['bars'][key]
i = ctx['i']
positions = ctx['positions']
state = ctx['state']
# Initialize state
if 'entry_bar' not in state:
state['entry_bar'] = None
actions = []
# Compute indicators
closes = [bars[j].close for j in range(i+1)]
highs = [bars[j].high for j in range(i+1)]
lows = [bars[j].low for j in range(i+1)]
ema50 = ema(closes, 50)
ema200 = ema(closes, 200)
if ema50[i] is None or ema200[i] is None:
return actions
# Regime filter: must be in uptrend
in_uptrend = ema50[i] > ema200[i]
# Lookback period for swing detection
lb = 5
if key not in positions:
if in_uptrend and i >= lb * 2:
# Find recent swing low (last 5 bars)
recent_low = min(lows[i-lb:i])
recent_low_idx = None
for j in range(i-lb, i):
if lows[j] == recent_low:
recent_low_idx = j
break
if recent_low_idx is None or recent_low_idx < lb:
return actions
# Find prior swing low (5-10 bars before recent)
prior_low = min(lows[recent_low_idx-lb:recent_low_idx])
# Higher low pattern - structural gap formation
if recent_low > prior_low:
# Range high between the two lows
range_high = max(highs[recent_low_idx-lb:recent_low_idx+1])
# Current bar breaks above range (fills the structural gap)
if bars[i].high > range_high:
# Confirm bullish bar
if bars[i].close > bars[i].open:
state['entry_bar'] = i
actions.append({
'action': 'open_long',
'symbol': 'tETHUSD',
'exchange': 'bitfinex',
'size': 1.0,
'stop_loss_pct': 5.0,
})
else:
if state['entry_bar'] is None:
return actions
bars_held = i - state['entry_bar']
# Exit: Close below EMA50 (momentum lost)
if bars[i].close < ema50[i]:
actions.append({
'action': 'close_long',
'symbol': 'tETHUSD',
'exchange': 'bitfinex',
})
state['entry_bar'] = None
return actions
# Exit: Timeout (25 bars = 100 hours)
if bars_held >= 25:
actions.append({
'action': 'close_long',
'symbol': 'tETHUSD',
'exchange': 'bitfinex',
})
state['entry_bar'] = None
return actions
# Exit: Trend break
if not in_uptrend:
actions.append({
'action': 'close_long',
'symbol': 'tETHUSD',
'exchange': 'bitfinex',
})
state['entry_bar'] = None
return actions
# For standalone testing
if __name__ == '__main__':
from strategy import backtest_strategy
results, profitable, _ = backtest_strategy(init_strategy, process_time_step)