init: estudio_mercados analysis from fn_registry
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "8b2334dd",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 01 — Descarga Masiva de Datos Historicos (Binance)\n",
|
||||
"\n",
|
||||
"Dos metodos para obtener datos historicos:\n",
|
||||
"\n",
|
||||
"1. **REST API** (`/api/v3/klines`) — Paginado, max 1000 velas por request. Ideal para 7 dias.\n",
|
||||
"2. **Data Vision** (`data.binance.vision`) — CSVs comprimidos diarios/mensuales. Ideal para meses/anos.\n",
|
||||
"\n",
|
||||
"**Rate limits REST:** 6000 weight/min, klines cuesta 2 weight.\n",
|
||||
"\n",
|
||||
"| Endpoint | Max/req | Weight | Uso |\n",
|
||||
"|---|---|---|---|\n",
|
||||
"| `/api/v3/klines` | 1000 velas | 2 | Candlesticks OHLCV |\n",
|
||||
"| `/api/v3/aggTrades` | 1000 trades | 2 | Trades agregados (max 1h window) |\n",
|
||||
"| `/api/v3/historicalTrades` | 1000 trades | 25 | Trades individuales (requiere API key) |\n",
|
||||
"| `data.binance.vision` | Sin limite | 0 | CSVs bulk diarios/mensuales |"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "3fe45a0a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import requests\n",
|
||||
"import time\n",
|
||||
"import datetime\n",
|
||||
"import io\n",
|
||||
"import zipfile\n",
|
||||
"import pandas as pd\n",
|
||||
"\n",
|
||||
"BASE = \"https://api.binance.com\"\n",
|
||||
"DATA_VISION = \"https://data.binance.vision\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "721f68a0",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Metodo 1: REST API — Klines con paginacion automatica\n",
|
||||
"\n",
|
||||
"`GET /api/v3/klines` devuelve max 1000 velas. Paginamos con `startTime`/`endTime`.\n",
|
||||
"\n",
|
||||
"Para 7 dias de velas 1m: ceil(7*24*60/1000) = **11 requests** (22 weight total, trivial)."
|
||||
]
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"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/<stream>`\n",
|
||||
"- Testnet: `wss://testnet.binance.vision/ws/<stream>`\n",
|
||||
"- Multi-stream: `wss://stream.binance.com:9443/stream?streams=<s1>/<s2>`\n",
|
||||
"\n",
|
||||
"**Streams principales:**\n",
|
||||
"| Stream | Nombre | Frecuencia |\n",
|
||||
"|---|---|---|\n",
|
||||
"| Trades individuales | `<symbol>@trade` | Cada trade |\n",
|
||||
"| Klines en vivo | `<symbol>@kline_<interval>` | Cada cambio en vela |\n",
|
||||
"| Mini ticker 24h | `<symbol>@miniTicker` | ~1s |\n",
|
||||
"| Book ticker (best bid/ask) | `<symbol>@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",
|
||||
"`<symbol>@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"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c723d607",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 03 — Libro de Ordenes en Tiempo Real (Binance)\n",
|
||||
"\n",
|
||||
"Dos enfoques para mantener un order book local:\n",
|
||||
"\n",
|
||||
"### Enfoque A: Partial Book Depth (simple)\n",
|
||||
"Stream `<symbol>@depth<levels>@100ms` con levels = 5, 10, 20.\n",
|
||||
"Envia snapshot completo del top N en cada update. Sin logica de sync.\n",
|
||||
"\n",
|
||||
"### Enfoque B: Diff Depth + REST Snapshot (completo)\n",
|
||||
"1. Abrir stream `<symbol>@depth@100ms` (diffs incrementales)\n",
|
||||
"2. Buffear eventos iniciales\n",
|
||||
"3. Pedir snapshot REST: `GET /api/v3/depth?symbol=X&limit=1000`\n",
|
||||
"4. Descartar eventos con `u <= lastUpdateId` del snapshot\n",
|
||||
"5. Primer evento procesado debe tener `U <= lastUpdateId+1` AND `u >= lastUpdateId+1`\n",
|
||||
"6. Aplicar: qty > 0 = update nivel, qty = 0 = eliminar nivel\n",
|
||||
"7. Validar continuidad: cada evento `U` == anterior `u + 1`, si no, re-sync\n",
|
||||
"\n",
|
||||
"### Campos del depth update\n",
|
||||
"```json\n",
|
||||
"{\n",
|
||||
" \"U\": 157, \"u\": 160,\n",
|
||||
" \"b\": [[\"price\", \"qty\"], ...], \n",
|
||||
" \"a\": [[\"price\", \"qty\"], ...] \n",
|
||||
"}\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "23b19294",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import asyncio\n",
|
||||
"import json\n",
|
||||
"import requests\n",
|
||||
"import websockets\n",
|
||||
"from decimal import Decimal\n",
|
||||
"from collections import deque\n",
|
||||
"import pandas as pd\n",
|
||||
"\n",
|
||||
"BASE = \"https://api.binance.com\"\n",
|
||||
"WS_BASE = \"wss://stream.binance.com:9443/ws\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "cff459c9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Enfoque A: Partial Book Depth (simple, sin sync)\n",
|
||||
"\n",
|
||||
"Stream `<symbol>@depth<levels>@100ms` — recibe snapshot completo del top N cada 100ms.\n",
|
||||
"\n",
|
||||
"Ideal para monitoreo rapido sin necesidad de mantener estado."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "bf70712b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"async def stream_top_book(symbol: str, levels: int = 10, snapshots: int = 50) -> list[dict]:\n",
|
||||
" \"\"\"Captura N snapshots del top del order book.\"\"\"\n",
|
||||
" url = f\"{WS_BASE}/{symbol.lower()}@depth{levels}@100ms\"\n",
|
||||
" results = []\n",
|
||||
"\n",
|
||||
" async with websockets.connect(url) as ws:\n",
|
||||
" while len(results) < snapshots:\n",
|
||||
" data = json.loads(await ws.recv())\n",
|
||||
" best_bid = (float(data[\"bids\"][0][0]), float(data[\"bids\"][0][1]))\n",
|
||||
" best_ask = (float(data[\"asks\"][0][0]), float(data[\"asks\"][0][1]))\n",
|
||||
" spread = best_ask[0] - best_bid[0]\n",
|
||||
" mid = (best_bid[0] + best_ask[0]) / 2\n",
|
||||
" results.append({\n",
|
||||
" \"time\": pd.Timestamp.now(tz=\"UTC\"),\n",
|
||||
" \"best_bid\": best_bid[0], \"bid_qty\": best_bid[1],\n",
|
||||
" \"best_ask\": best_ask[0], \"ask_qty\": best_ask[1],\n",
|
||||
" \"spread\": spread, \"spread_bps\": (spread / mid) * 10000,\n",
|
||||
" \"bids\": [(float(p), float(q)) for p, q in data[\"bids\"]],\n",
|
||||
" \"asks\": [(float(p), float(q)) for p, q in data[\"asks\"]],\n",
|
||||
" })\n",
|
||||
"\n",
|
||||
" return results"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c4c1bfe6",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 04 — Trading Programatico (Binance API)\n",
|
||||
"\n",
|
||||
"Operaciones de trading via REST API con autenticacion HMAC-SHA256.\n",
|
||||
"\n",
|
||||
"**Autenticacion:** Cada request firmado necesita:\n",
|
||||
"1. Header `X-MBX-APIKEY` con tu API key\n",
|
||||
"2. Parametro `timestamp` (unix ms, dentro de 5000ms del server)\n",
|
||||
"3. Parametro `signature` = HMAC-SHA256(query_string, secret_key)\n",
|
||||
"\n",
|
||||
"**Endpoints de trading (Spot):**\n",
|
||||
"| Accion | Metodo | Endpoint | Weight |\n",
|
||||
"|---|---|---|---|\n",
|
||||
"| Crear orden | POST | `/api/v3/order` | 1 |\n",
|
||||
"| Test orden | POST | `/api/v3/order/test` | 1 |\n",
|
||||
"| Cancelar orden | DELETE | `/api/v3/order` | 1 |\n",
|
||||
"| Cancelar todas | DELETE | `/api/v3/openOrders` | 1 |\n",
|
||||
"| Ver orden | GET | `/api/v3/order` | 4 |\n",
|
||||
"| Ordenes abiertas | GET | `/api/v3/openOrders` | 6 |\n",
|
||||
"| Cuenta/balances | GET | `/api/v3/account` | 20 |\n",
|
||||
"| Mis trades | GET | `/api/v3/myTrades` | 20 |\n",
|
||||
"\n",
|
||||
"**Tipos de orden:** MARKET, LIMIT (GTC/IOC/FOK), STOP_LOSS_LIMIT, TAKE_PROFIT_LIMIT, LIMIT_MAKER\n",
|
||||
"\n",
|
||||
"**TESTNET:** `https://testnet.binance.vision` — mismo API, balances gratis, keys via GitHub login"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "599a54e7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import hashlib\n",
|
||||
"import hmac\n",
|
||||
"import time\n",
|
||||
"import math\n",
|
||||
"import requests\n",
|
||||
"import pandas as pd\n",
|
||||
"\n",
|
||||
"# --- CONFIGURACION ---\n",
|
||||
"# Para testnet (seguro para pruebas):\n",
|
||||
"BASE = \"https://testnet.binance.vision\"\n",
|
||||
"# Para produccion (dinero real):\n",
|
||||
"# BASE = \"https://api.binance.com\"\n",
|
||||
"\n",
|
||||
"# Crea tus keys en https://testnet.binance.vision (login con GitHub)\n",
|
||||
"API_KEY = \"\" # <-- tu API key aqui\n",
|
||||
"API_SECRET = \"\" # <-- tu secret aqui"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "acc0b354",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Firma HMAC-SHA256\n",
|
||||
"\n",
|
||||
"Toda request autenticada requiere `timestamp` + `signature`. La firma es HMAC del query string completo."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "3fa09567",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def signed_request(method: str, endpoint: str, params: dict | None = None) -> dict:\n",
|
||||
" \"\"\"Request firmado a Binance API (funciona con testnet y produccion).\"\"\"\n",
|
||||
" if params is None:\n",
|
||||
" params = {}\n",
|
||||
"\n",
|
||||
" params[\"timestamp\"] = int(time.time() * 1000)\n",
|
||||
" params[\"recvWindow\"] = 5000\n",
|
||||
"\n",
|
||||
" query_string = \"&\".join(f\"{k}={v}\" for k, v in params.items())\n",
|
||||
" signature = hmac.new(\n",
|
||||
" API_SECRET.encode(), query_string.encode(), hashlib.sha256\n",
|
||||
" ).hexdigest()\n",
|
||||
" params[\"signature\"] = signature\n",
|
||||
"\n",
|
||||
" headers = {\"X-MBX-APIKEY\": API_KEY}\n",
|
||||
"\n",
|
||||
" if method == \"GET\":\n",
|
||||
" resp = requests.get(f\"{BASE}{endpoint}\", params=params, headers=headers)\n",
|
||||
" elif method == \"POST\":\n",
|
||||
" resp = requests.post(f\"{BASE}{endpoint}\", params=params, headers=headers)\n",
|
||||
" elif method == \"DELETE\":\n",
|
||||
" resp = requests.delete(f\"{BASE}{endpoint}\", params=params, headers=headers)\n",
|
||||
" else:\n",
|
||||
" raise ValueError(f\"Metodo no soportado: {method}\")\n",
|
||||
"\n",
|
||||
" resp.raise_for_status()\n",
|
||||
" return resp.json()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "725c3d14",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Consultar informacion del simbolo (filtros de ordenes)\n",
|
||||
"\n",
|
||||
"Antes de operar, hay que conocer los filtros: `LOT_SIZE` (min/max qty, step), `PRICE_FILTER` (tick size), `NOTIONAL` (min valor en quote)."
|
||||
]
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
Reference in New Issue
Block a user