Escáner Automatizado de Zonas de Oferta y Demanda con Alertas en Telegram para Bybit
Un sistema basado en Python escanea todos los pares USDT en Bybit, identifica zonas de oferta y demanda utilizando conceptos de Smart Money, rastrea retestes de precio con formación de pinbar y envía señales de trading instantáneamente a Telegram con marcado en gráficos. Procesar cientos de activos toma minutos en lugar de horas de análisis manual.
La lógica sigue una cadena: detección de zona → reteste de precio → reacción de acción de precio → generación de señal. Las zonas se filtran por ATR, rupturas y superposiciones para excluir niveles irrelevantes.
Teoría de las Zonas de Oferta y Demanda
Las zonas de liquidez son áreas clave donde se acumulan posiciones y ocurre el impulso posterior. Las zonas de demanda se forman donde el precio subió agresivamente, las zonas de oferta donde cayó bruscamente. Una señal se activa cuando el precio regresa a una zona con confirmación de pinbar (una vela con una mecha larga).
Criterios de validez de zona:
- Impulso hacia la zona > 2 ATR
- Impulso fuera de la zona > 2 ATR
- Tamaño de zona 1–6 ATR
- Sin ruptura después de la formación
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
Los parámetros ajustan la sensibilidad: marco temporal de 4H para zonas, período ATR 100 para evaluación de volatilidad, escaneo cada 300 segundos.
Integración con la API de Bybit
El sistema carga listas de símbolos y velas históricas:
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() # de más antiguo a más nuevo
return klines
Solo se filtran pares USDT. Los datos se procesan en un DataFrame de pandas con ATR calculado.
Detección de Bloques de Orden
Los bloques de orden son estructuras fundamentales para las zonas. OB de demanda: una vela bajista seguida de un impulso alcista por encima del máximo anterior.
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]
# OB DE DEMANDA
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
})
Similar para oferta. El impulso se filtra >1.5 ATR para excluir movimientos laterales.
Bloques de Rechazo y Zonas Avanzadas
Los bloques de rechazo se detectan por proporciones de vela: mecha inferior >55% del rango, cuerpo <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
# Rechazo de demanda
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 filtro avanzado verifica impulsos antes/después de la zona en relación con el ATR.
Función de invalidación:
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']
Las zonas superpuestas se eliminan ordenando por frescura y excluyendo superposiciones.
Selección de Zonas Más Cercanas y Activador Pinbar
Se seleccionan dos zonas más cercanas al precio actual:
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 en marco temporal de 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
upper_wick = h - max(o, c)
lower_wick = min(o, c) - 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
Generación y Envío de Señales
Verificación de señal larga:
def check_long_signal(symbol, klines_5m, zones):
last = klines_5m[-2] # vela cerrada
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
La señal se visualiza en un gráfico con marcado de zona y se envía a Telegram mediante la API del bot.
Puntos Clave
- El sistema utiliza 4H para zonas y 5m para activadores, filtrando ruido de marcos temporales inferiores.
- La normalización ATR adapta las zonas a la volatilidad del activo sin atarse a BTC.
- Protección contra duplicados mediante sent_signals e invalidación de zonas rotas.
- Procesamiento de todos los pares USDT de Bybit en minutos con enfoque en zonas más cercanas.
- Acción de precio (pinbar) como activador final después de confirmación estructural.
— Editorial Team
Aún no hay comentarios.