import asyncio
import json
import aiohttp
import websockets
import time

# Configuración thresholds
IMBALANCE_BUY_THRESHOLD = 0.7
IMBALANCE_SELL_THRESHOLD = 0.3
LIQUIDITY_THRESHOLD = 50

DEPTH_LEVELS = 5
TOP_N_PAIRS = 10

# Configuración scalping
TAKE_PROFIT_PCT = 0.1  # 0.1%
STOP_LOSS_PCT = 0.05   # 0.05%
FEE_PCT = 0.02         # 0.02% por trade

# Lista de operaciones abiertas
open_trades = []

# Último precio conocido
last_prices = {}

async def fetch_usdc_pairs_top_volume():
    url_info = "https://api.binance.com/api/v3/exchangeInfo"
    url_tickers = "https://api.binance.com/api/v3/ticker/24hr"

    async with aiohttp.ClientSession() as session:
        async with session.get(url_info) as resp_info:
            data_info = await resp_info.json()
            usdc_symbols = {
                s["symbol"]: s
                for s in data_info["symbols"]
                if s["quoteAsset"] == "USDC" and s["status"] == "TRADING"
            }

        async with session.get(url_tickers) as resp_tickers:
            data_tickers = await resp_tickers.json()
            volumes = []
            for t in data_tickers:
                sym = t["symbol"]
                if sym in usdc_symbols:
                    vol = float(t["quoteVolume"])
                    volumes.append((sym.lower(), vol))

        volumes.sort(key=lambda x: x[1], reverse=True)
        top_pairs = [s for s, v in volumes[:TOP_N_PAIRS]]
        return top_pairs

async def listen_orderbook(symbol):
    uri = f"wss://stream.binance.com:9443/ws/{symbol}@depth"
    while True:
        try:
            async with websockets.connect(uri) as ws:
                print(f"✅ Conectado al book de {symbol.upper()}")
                async for msg in ws:
                    data = json.loads(msg)
                    bids_raw = data.get("bids") or data.get("b")
                    asks_raw = data.get("asks") or data.get("a")
                    if not bids_raw or not asks_raw:
                        continue

                    bids = [(float(p), float(q)) for p, q in bids_raw[:DEPTH_LEVELS]]
                    asks = [(float(p), float(q)) for p, q in asks_raw[:DEPTH_LEVELS]]

                    sum_bids = sum(q for p, q in bids)
                    sum_asks = sum(q for p, q in asks)

                    imbalance = sum_bids / (sum_bids + sum_asks) if (sum_bids + sum_asks) > 0 else 0

                    mid_price = (bids[0][0] + asks[0][0]) / 2
                    last_prices[symbol] = mid_price

                    # Detecta señal
                    signal = None
                    if imbalance > IMBALANCE_BUY_THRESHOLD and sum_asks < LIQUIDITY_THRESHOLD:
                        signal = "BUY"
                    elif imbalance < IMBALANCE_SELL_THRESHOLD and sum_bids < LIQUIDITY_THRESHOLD:
                        signal = "SELL"

                    # Si hay señal, guardar operación simulada
                    if signal:
                        take_profit = mid_price * (1 + TAKE_PROFIT_PCT/100) if signal == "BUY" else mid_price * (1 - TAKE_PROFIT_PCT/100)
                        stop_loss = mid_price * (1 - STOP_LOSS_PCT/100) if signal == "BUY" else mid_price * (1 + STOP_LOSS_PCT/100)
                        trade = {
                            "symbol": symbol.upper(),
                            "side": signal,
                            "entry_price": mid_price,
                            "take_profit": take_profit,
                            "stop_loss": stop_loss,
                            "timestamp": time.time()
                        }
                        open_trades.append(trade)

        except Exception as e:
            print(f"⚠️ Error en {symbol}: {e}. Reintentando en 5s...")
            await asyncio.sleep(5)

async def check_trades():
    while True:
        await asyncio.sleep(1)  # Verifica cada segundo
        closed_trades = []

        for trade in open_trades:
            current_price = last_prices.get(trade["symbol"].lower())
            if not current_price:
                continue

            side = trade["side"]
            entry = trade["entry_price"]
            tp = trade["take_profit"]
            sl = trade["stop_loss"]

            exited = False
            pnl = 0

            if side == "BUY":
                if current_price >= tp:
                    pnl = (tp - entry) - (entry * FEE_PCT/100) - (tp * FEE_PCT/100)
                    reason = "Take Profit"
                    exited = True
                elif current_price <= sl:
                    pnl = (sl - entry) - (entry * FEE_PCT/100) - (sl * FEE_PCT/100)
                    reason = "Stop Loss"
                    exited = True
            elif side == "SELL":
                if current_price <= tp:
                    pnl = (entry - tp) - (entry * FEE_PCT/100) - (tp * FEE_PCT/100)
                    reason = "Take Profit"
                    exited = True
                elif current_price >= sl:
                    pnl = (entry - sl) - (entry * FEE_PCT/100) - (sl * FEE_PCT/100)
                    reason = "Stop Loss"
                    exited = True

            if exited:
                print("=" * 100)
                print(
                    f"✅ {trade['symbol']} {side} cerrado por {reason} | "
                    f"Entrada: {entry:.6f} | Salida: {current_price:.6f} | PnL (fees incl.): {pnl:+.6f}"
                )
                print("=" * 100)
                closed_trades.append(trade)

        # Elimina los trades que ya cerraron
        for t in closed_trades:
            open_trades.remove(t)

async def main():
    pairs = await fetch_usdc_pairs_top_volume()
    print(f"Conectando a {len(pairs)} pares USDC más líquidos: {', '.join(p.upper() for p in pairs)}")
    tasks = [listen_orderbook(symbol) for symbol in pairs]
    tasks.append(check_trades())
    await asyncio.gather(*tasks)

if __name__ == "__main__":
    asyncio.run(main())
