Gap fill continuation pattern - when price creates inefficiency zones through weak bars, then recovers through them showing buyer strength.
Symbol: BTC | Exchange: Bitfinex
| Year | Return | Win Rate | Trades | Max DD | Sharpe |
|---|---|---|---|---|---|
| 2020 | +4.7% | 42.9% | 14 | 9.6% | 0.25 |
| 2021 | +60.8% | 45.0% | 20 | 8.5% | 1.72 |
| 2022 | +5.8% | 50.0% | 10 | 4.7% | 0.85 |
| 2023 | +3.9% | 12.5% | 16 | 16.1% | 0.14 |
| 2024 | +60.7% | 66.7% | 9 | 3.7% | 2.71 |
| 2025 | -15.9% | 21.1% | 19 | 15.3% | -1.43 |
See strategy file
See strategy file
"""
Strategy: price_inefficiency_zone_fill
======================================
Gap fill continuation pattern - when price creates inefficiency zones
through weak bars, then recovers through them showing buyer strength.
Performance: 5/6 years profitable | Total: +123.3%
2020: +4.7% | 42% WR | 14 trades
2021: +60.9% | 45% WR | 20 trades
2022: +5.8% | 50% WR | 10 trades
2023: +5.9% | 12% WR | 16 trades
2024: +60.7% | 66% WR | 9 trades
2025: -14.7% | 21% WR | 19 trades
"""
import sys
sys.path.insert(0, "/root/trade_rules")
from lib import ema
def init_strategy():
return {
'name': 'price_inefficiency_zone_fill',
'subscriptions': [
{'symbol': 'tBTCUSD', 'exchange': 'bitfinex', 'timeframe': '4h'},
],
'parameters': {}
}
def process_time_step(ctx):
key = ('tBTCUSD', 'bitfinex')
bars = ctx['bars'].get(key, [])
i = ctx['i']
positions = ctx['positions']
if not bars or i >= len(bars) or i < 50:
return []
actions = []
has_position = key in positions
if not has_position:
# Entry: Gap fill continuation pattern
curr = bars[i]
# Calculate EMA30 for trend
closes_slice = [bars[j].close for j in range(max(0, i-100), i+1)]
ema30_vals = ema(closes_slice, 30)
if ema30_vals[-1] is None:
return []
# Must be above EMA30
if curr.close < ema30_vals[-1]:
return []
# Look for 2-3 weak bars in recent past (2-4 bars ago)
weak_count = 0
highest_of_weak = 0
for j in range(i-4, i-1):
if j < 0:
continue
bar = bars[j]
rng = bar.high - bar.low
if rng == 0:
continue
close_pos = (bar.close - bar.low) / rng
if close_pos < 0.3: # Weak bar (close in lower 30%)
weak_count += 1
highest_of_weak = max(highest_of_weak, bar.high)
# Need at least 2 weak bars creating an inefficiency zone
if weak_count < 2:
return []
# Current bar must close above that weak zone (filled the gap)
if curr.close <= highest_of_weak:
return []
# Must break 8-bar high (momentum)
recent_high = max(bars[j].high for j in range(i-8, i))
if curr.high <= recent_high:
return []
# Current bar must be strong (close in upper 60%)
rng = curr.high - curr.low
if rng == 0:
return []
close_pos = (curr.close - curr.low) / rng
if close_pos < 0.6:
return []
actions.append({
'action': 'open_long',
'symbol': 'tBTCUSD',
'exchange': 'bitfinex',
'size': 1.0,
})
else:
# Exit: close below EMA18
closes_slice = [bars[j].close for j in range(max(0, i-100), i+1)]
ema_vals = ema(closes_slice, 18)
if ema_vals[-1] is not None and bars[i].close < ema_vals[-1]:
actions.append({
'action': 'close_long',
'symbol': 'tBTCUSD',
'exchange': 'bitfinex',
})
return actions