Scanner Automatisé des Zones d'Offre et de Demande avec Alertes Telegram pour Bybit
Un système basé sur Python analyse toutes les paires USDT sur Bybit, identifie les zones d'offre et de demande en utilisant les concepts de Smart Money, suit les retests de prix avec formation de pinbar, et envoie instantanément des signaux de trading sur Telegram avec annotations graphiques. Le traitement de centaines d'actifs prend quelques minutes au lieu d'heures d'analyse manuelle.
La logique suit une chaîne : détection de zone → retest de prix → réaction du price action → génération de signal. Les zones sont filtrées par ATR, les cassures et les chevauchements pour exclure les niveaux non pertinents.
Théorie des Zones d'Offre et de Demande
Les zones de liquidité sont des zones clés où les positions s'accumulent et où un momentum ultérieur se produit. Les zones de demande se forment là où le prix a fortement augmenté, les zones d'offre là où il a fortement chuté. Un signal se déclenche lorsque le prix revient dans une zone avec confirmation par pinbar (une bougie avec une longue mèche).
Critères de validité d'une zone :
- Impulsion vers la zone > 2 ATR
- Impulsion hors de la zone > 2 ATR
- Taille de la zone 1–6 ATR
- Pas de cassure après la 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
Les paramètres ajustent la sensibilité : timeframe de 4H pour les zones, période ATR de 100 pour l'évaluation de la volatilité, scan toutes les 300 secondes.
Intégration avec l'API Bybit
Le système charge les listes de symboles et les bougies historiques :
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() # from oldest to newest
return klines
Seules les paires USDT sont filtrées. Les données sont traitées dans un DataFrame pandas avec ATR calculé.
Détection des Blocs d'Ordres
Les blocs d'ordres sont des structures fondamentales pour les zones. Bloc d'ordre de demande : une bougie baissière suivie d'une impulsion haussière au-dessus du précédent plus haut.
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
})
De même pour l'offre. L'impulsion est filtrée >1.5 ATR pour exclure les mouvements en range.
Blocs de Rejet et Zones Avancées
Les blocs de rejet sont détectés par les proportions de bougie : mèche inférieure >55% de la plage, corps <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
})
Un filtre avancé vérifie les impulsions avant/après la zone par rapport à l'ATR.
Fonction d'invalidation :
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']
Les zones qui se chevauchent sont supprimées en triant par fraîcheur et en excluant les chevauchements.
Sélection des Zones les Plus Proches et Déclenchement Pinbar
Deux zones les plus proches du prix actuel sont sélectionnées :
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 sur le timeframe 5m :
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
Génération et Envoi des Signaux
Vérification du signal d'achat :
def check_long_signal(symbol, klines_5m, zones):
last = klines_5m[-2] # closed candle
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
Le signal est visualisé sur un graphique avec annotations de zone et envoyé à Telegram via l'API bot.
Points Clés
- Le système utilise le 4H pour les zones et le 5m pour les déclencheurs, filtrant le bruit des timeframes inférieurs.
- La normalisation ATR adapte les zones à la volatilité de l'actif sans être liée au BTC.
- Protection contre les doublons via sent_signals et invalidation des zones cassées.
- Traitement de toutes les paires USDT de Bybit en quelques minutes avec focus sur les zones les plus proches.
- Price action (pinbar) comme déclencheur final après confirmation structurelle.
— Editorial Team
Aucun commentaire pour le moment.