{ "cells": [ { "cell_type": "markdown", "id": "45f026b5", "metadata": {}, "source": [ "# 02 — Streaming de Datos en Tiempo Real (Binance WebSocket)\n", "\n", "Binance ofrece WebSocket streams push-based para datos de mercado en tiempo real.\n", "\n", "**Base URLs:**\n", "- Produccion: `wss://stream.binance.com:9443/ws/`\n", "- Testnet: `wss://testnet.binance.vision/ws/`\n", "- Multi-stream: `wss://stream.binance.com:9443/stream?streams=/`\n", "\n", "**Streams principales:**\n", "| Stream | Nombre | Frecuencia |\n", "|---|---|---|\n", "| Trades individuales | `@trade` | Cada trade |\n", "| Klines en vivo | `@kline_` | Cada cambio en vela |\n", "| Mini ticker 24h | `@miniTicker` | ~1s |\n", "| Book ticker (best bid/ask) | `@bookTicker` | Cada cambio |\n", "| Todos los tickers | `!miniTicker@arr` | ~1s |\n", "\n", "**Reglas de conexion:**\n", "- Ping cada 3 min desde Binance, pong requerido\n", "- Desconexion automatica a las 24h — reconectar periodicamente\n", "- Se puede suscribir/desuscribir dinamicamente via JSON" ] }, { "cell_type": "code", "execution_count": null, "id": "6bb48c2e", "metadata": {}, "outputs": [], "source": [ "import asyncio\n", "import json\n", "import websockets\n", "import pandas as pd\n", "from datetime import datetime, timezone\n", "from collections import deque\n", "\n", "WS_BASE = \"wss://stream.binance.com:9443/ws\"" ] }, { "cell_type": "markdown", "id": "2b2d9b2d", "metadata": {}, "source": [ "## Stream de Trades individuales\n", "\n", "`@trade` — recibe cada trade ejecutado en tiempo real.\n", "\n", "Campos clave:\n", "- `p` = precio, `q` = cantidad\n", "- `m` = true si el buyer es maker (es decir, fue un sell market order que impacto un bid)\n", "- `t` = trade ID, `T` = timestamp" ] }, { "cell_type": "code", "execution_count": null, "id": "aaed9f77", "metadata": {}, "outputs": [], "source": [ "async def stream_trades(symbol: str, max_trades: int = 100) -> list[dict]:\n", " \"\"\"Captura N trades en tiempo real y retorna como lista.\"\"\"\n", " url = f\"{WS_BASE}/{symbol.lower()}@trade\"\n", " trades = []\n", "\n", " async with websockets.connect(url) as ws:\n", " while len(trades) < max_trades:\n", " msg = json.loads(await ws.recv())\n", " trades.append({\n", " \"trade_id\": msg[\"t\"],\n", " \"time\": datetime.fromtimestamp(msg[\"T\"] / 1000, tz=timezone.utc),\n", " \"price\": float(msg[\"p\"]),\n", " \"qty\": float(msg[\"q\"]),\n", " \"is_buyer_maker\": msg[\"m\"],\n", " \"side\": \"SELL\" if msg[\"m\"] else \"BUY\",\n", " })\n", "\n", " return trades" ] }, { "cell_type": "markdown", "id": "5dc19bb4", "source": "### Ejemplo: Capturar 100 trades de BTCUSDT", "metadata": {} }, { "cell_type": "code", "id": "2ad6bc1c", "source": "trades = await stream_trades(\"BTCUSDT\", max_trades=100)\ndf_trades = pd.DataFrame(trades)\nprint(f\"Capturados {len(df_trades)} trades\")\nprint(f\"Rango de tiempo: {df_trades['time'].min()} -> {df_trades['time'].max()}\")\nprint(f\"Precio: {df_trades['price'].min():.2f} - {df_trades['price'].max():.2f}\")\nprint(f\"BUY: {(df_trades['side'] == 'BUY').sum()}, SELL: {(df_trades['side'] == 'SELL').sum()}\")\ndf_trades.head(10)", "metadata": {}, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "id": "65a65f5b", "source": "## Stream de Klines en vivo\n\n`@kline_` — recibe actualizaciones de la vela actual y velas cerradas.\n\nCampo clave: `k.x` = true cuando la vela esta cerrada (final). Mientras `x=false`, los valores OHLCV son parciales.", "metadata": {} }, { "cell_type": "code", "id": "99c11390", "source": "async def stream_klines(symbol: str, interval: str = \"1m\", max_closed: int = 5) -> list[dict]:\n \"\"\"Captura N velas CERRADAS en tiempo real (espera a que x=true).\"\"\"\n url = f\"{WS_BASE}/{symbol.lower()}@kline_{interval}\"\n closed_candles = []\n current = None\n\n async with websockets.connect(url) as ws:\n while len(closed_candles) < max_closed:\n msg = json.loads(await ws.recv())\n k = msg[\"k\"]\n current = {\n \"open_time\": pd.Timestamp(k[\"t\"], unit=\"ms\", tz=\"UTC\"),\n \"open\": float(k[\"o\"]),\n \"high\": float(k[\"h\"]),\n \"low\": float(k[\"l\"]),\n \"close\": float(k[\"c\"]),\n \"volume\": float(k[\"v\"]),\n \"trades\": k[\"n\"],\n \"is_closed\": k[\"x\"],\n }\n if k[\"x\"]: # vela cerrada\n closed_candles.append(current)\n print(f\"Vela cerrada #{len(closed_candles)}: {current['close']:.2f} | vol={current['volume']:.4f} | trades={current['trades']}\")\n\n return closed_candles", "metadata": {}, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "id": "ddab999b", "source": "### Ejemplo: Capturar 3 velas cerradas de 1m de BTCUSDT\n\n**Nota:** Esto tarda hasta 3 minutos esperando que se cierren las velas.", "metadata": {} }, { "cell_type": "code", "id": "5e4b017f", "source": "candles = await stream_klines(\"BTCUSDT\", interval=\"1m\", max_closed=3)\ndf_candles = pd.DataFrame(candles)\ndf_candles", "metadata": {}, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "id": "b067572a", "source": "## Multi-stream: multiples feeds en una conexion\n\nCombinar trades + klines + book ticker de varios pares en un solo WebSocket.", "metadata": {} }, { "cell_type": "code", "id": "b4763056", "source": "async def stream_multiple(streams: list[str], max_messages: int = 100) -> list[dict]:\n \"\"\"Captura N mensajes de multiples streams combinados.\"\"\"\n combined = \"/\".join(streams)\n url = f\"wss://stream.binance.com:9443/stream?streams={combined}\"\n messages = []\n\n async with websockets.connect(url) as ws:\n while len(messages) < max_messages:\n raw = json.loads(await ws.recv())\n messages.append({\n \"stream\": raw[\"stream\"],\n \"data\": raw[\"data\"],\n })\n\n return messages\n\n\n# Ejemplo: trades de BTC + ETH + book ticker de BTC\nmulti = await stream_multiple([\n \"btcusdt@trade\",\n \"ethusdt@trade\",\n \"btcusdt@bookTicker\",\n], max_messages=50)\n\n# Contar mensajes por stream\nfrom collections import Counter\ncounts = Counter(m[\"stream\"] for m in multi)\nprint(\"Mensajes por stream:\")\nfor stream, count in counts.most_common():\n print(f\" {stream}: {count}\")", "metadata": {}, "execution_count": null, "outputs": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.7" } }, "nbformat": 4, "nbformat_minor": 5 }