Automatisierter Supply/Demand-Zonen-Scanner mit Telegram-Benachrichtigungen für Bybit
Ein Python-basiertes System scannt alle USDT-Paare auf Bybit, identifiziert Supply- und Demand-Zonen mithilfe von Smart-Money-Konzepten, verfolgt Preisrücktests mit Pinbar-Formation und sendet sofort Handelsignale mit Chart-Markierungen an Telegram. Die Verarbeitung von Hunderten von Assets dauert Minuten statt Stunden manueller Analyse.
Die Logik folgt einer Kette: Zonenerkennung → Preisrücktest → Price-Action-Reaktion → Signalgenerierung. Zonen werden nach ATR, Ausbrüchen und Überlappungen gefiltert, um irrelevante Levels auszuschließen.
Theorie der Supply- und Demand-Zonen
Liquiditätszonen sind Schlüsselbereiche, in denen sich Positionen ansammeln und anschließender Schwung entsteht. Demand-Zonen bilden sich dort, wo der Preis aggressiv gestiegen ist, Supply-Zonen dort, wo er scharf gefallen ist. Ein Signal wird ausgelöst, wenn der Preis mit Pinbar-Bestätigung (eine Kerze mit langem Docht) in eine Zone zurückkehrt.
Kriterien für die Zonengültigkeit:
- Impuls in die Zone > 2 ATR
- Impuls aus der Zone > 2 ATR
- Zonengröße 1–6 ATR
- Kein Ausbruch nach der Formation
MAX_ZONE_ATR = 7
MIN_ZONE_ATR = 1
LOOKBACK_PRE = 8
LOOKAHEAD_POST = 8
MIN_PRE_MOVE_ATR = 2
MIN_POST_MOVE_ATR = 2
MIN_ZONE_SIZE_ATR = 1.0
MAX_ZONE_SIZE_ATR = 6.0
Parameter passen die Empfindlichkeit an: 4H-Zeitrahmen für Zonen, ATR-Periode 100 für Volatilitätsbewertung, Scannen alle 300 Sekunden.
Integration mit der Bybit-API
Das System lädt Symbollisten und historische Kerzen:
def get_symbols():
data = client.get_instruments_info(category="linear")
symbols = []
for item in data["result"]["list"]:
if item["quoteCoin"] == "USDT":
symbols.append(item["symbol"])
return symbols
def get_klines(symbol, interval, limit):
data = client.get_kline(
category="linear",
symbol=symbol,
interval=interval,
limit=limit
)
klines = data["result"]["list"]
klines.reverse() # von ältester zu neuester
return klines
Nur USDT-Paare werden gefiltert. Daten werden in einem pandas DataFrame mit berechnetem ATR verarbeitet.
Order-Block-Erkennung
Order-Blöcke sind grundlegende Strukturen für Zonen. Demand-OB: eine bärische Kerze, gefolgt von einem bullischen Impuls über dem vorherigen Hoch.
def detect_order_blocks(df):
for i in range(1, len(df)):
o1 = df.open.iloc[i-1]
c1 = df.close.iloc[i-1]
h1 = df.high.iloc[i-1]
l1 = df.low.iloc[i-1]
o2 = df.open.iloc[i]
c2 = df.close.iloc[i]
atr = df.atr.iloc[i]
# DEMAND OB
if c1 < o1 and c2 > o2 and c2 > h1:
impulse = c2 - l1
if impulse > atr * 1.5:
zones.append({
"type": "demand",
"kind": "orderblock",
"low": l1,
"high": o1,
"start_bar": i-1
})
Ähnlich für Supply. Impuls wird auf >1,5 ATR gefiltert, um range-bound Bewegungen auszuschließen.
Rejection-Blöcke und erweiterte Zonen
Rejection-Blöcke werden durch Kerzenproportionen erkannt: unterer Docht >55% der Range, Körper <35%.
def detect_rejection_blocks(df):
for i in range(2, len(df)-1):
o = df.open.iloc[i]
c = df.close.iloc[i]
h = df.high.iloc[i]
l = df.low.iloc[i]
atr = df.atr.iloc[i]
body = abs(c - o)
full = h - l
upper_wick = h - max(o,c)
lower_wick = min(o,c) - l
body_ratio = body / full
lower_ratio = lower_wick / full
# DEMAND rejection
if lower_ratio > 0.55 and body_ratio < 0.35:
zones.append({
"type": "demand",
"kind": "rejection",
"low": l,
"high": min(o,c),
"start_bar": i
})
Ein erweitertes Filter prüft Impulse vor/nach der Zone relativ zum ATR.
Ungültigkeitsfunktion:
def is_zone_broken(df, zone):
start_idx = zone['start_bar'] + 1
slice_df = df.iloc[start_idx:]
if zone['type'] == 'supply':
break_level = slice_df['high'].max() if INVALIDATION_METHOD == "wick" else slice_df['close'].max()
return break_level > zone['high']
else:
break_level = slice_df['low'].min() if INVALIDATION_METHOD == "wick" else slice_df['close'].min()
return break_level < zone['low']
Überlappende Zonen werden durch Sortierung nach Frische und Ausschluss von Überlappungen entfernt.
Auswahl der nächstgelegenen Zonen und Pinbar-Trigger
Zwei Zonen, die dem aktuellen Preis am nächsten liegen, werden ausgewählt:
def get_nearest_zones(price, zones, n=2):
def zone_distance(z):
if price < z["low"]:
return z["low"] - price
elif price > z["high"]:
return price - z["high"]
return 0
return sorted(zones, key=zone_distance)[:n]
Pinbar auf 5m-Zeitrahmen:
def is_bullish_pinbar(candle):
o, h, l, c = float(candle[1]), float(candle[2]), float(candle[3]), float(candle[4])
body = abs(c - o)
total_range = h - l
if body > 0.3 * total_range or c <= o or lower_wick < body * 2 or upper_wick > body * 0.5:
return False
return True
Signalgenerierung und -versand
Long-Signalprüfung:
def check_long_signal(symbol, klines_5m, zones):
last = klines_5m[-2] # geschlossene Kerze
price = float(last[4])
if not is_bullish_pinbar(last):
return None
for zone in zones:
if price_in_zone(price, zone):
key = f"{symbol}_{last[0]}_long"
if key in sent_signals:
return None
sent_signals[key] = True
return zone
return None
Das Signal wird auf einem Chart mit Zonenmarkierung visualisiert und über die Bot-API an Telegram gesendet.
Wichtige Punkte
- Das System verwendet 4H für Zonen und 5m für Trigger, um Rauschen von niedrigeren Zeitrahmen zu filtern.
- ATR-Normalisierung passt Zonen an die Volatilität des Assets an, ohne an BTC gebunden zu sein.
- Duplikatschutz über sent_signals und Ungültigkeit gebrochener Zonen.
- Verarbeitung aller Bybit-USDT-Paare in Minuten mit Fokus auf nächstgelegene Zonen.
- Price Action (Pinbar) als finaler Trigger nach struktureller Bestätigung.
— Editorial Team
Noch keine Kommentare.