Volver al inicio

Automatización de la búsqueda de zonas de oferta y demanda en Bybit

El sistema automatiza la búsqueda de zonas de oferta/demanda en Bybit a través de bloques de órdenes y de rechazo con filtros ATR. Confirmación de entrada por pinbar en TF 5m con envío de gráficos a Telegram. Desglose completo del código para desarrolladores middle/senior.

Python-bot: zonas de demanda/oferta Bybit + Telegram
Advertisement 728x90

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).

Google AdInline article slot

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:

Google AdInline article slot
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.

Google AdInline article slot

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

Advertisement 728x90

Leer después