Lección 320 min60 XPGratis
Descargar datos reales: tu primer CSV de mercado
Al terminar sabrás
- Descargar cierres diarios de un activo desde una fuente pública con un script propio
- Guardar los datos en CSV con un formato fijo y pasarlos al script de métricas
- Hacer las cuatro comprobaciones de integridad antes de usar cualquier dato de mercado
Antes de esta conviene haber hecho Tu primer script quant: de precios a métricas en 40 líneas.
Hasta ahora los datos eran simulados o de diez precios. Ahora vas a bajar un año de cierres diarios reales de bitcoin, gratis, desde el bucket público de Binance, con un script que puedes releer en un minuto. Y antes de calcular nada, vas a hacer las cuatro comprobaciones que separan un dato de mercado de un archivo con números.
El problema
Descargar datos es fácil. Usar datos malos sin saberlo es lo normal: fechas duplicadas, huecos de días, un cambio de unidad en la marca temporal a mitad de serie, precios en la zona horaria equivocada. Ninguno da error. Todos rompen el backtest. La disciplina de comprobar los datos antes de tocarlos se enseña aquí porque es el primer dato real que tocas.
Tres capas
Intuición
Binance publica cada mes un archivo comprimido por cada activo e intervalo con todas las velas de ese mes. Son archivos públicos en una dirección web predecible: cambias el nombre del activo, el año y el mes en la URL y tienes el archivo. El script hace eso doce veces (uno por mes), descomprime, se queda con la fecha y el cierre, junta todo y lo guarda en un CSV con dos columnas.
Después, antes de alegrarte, miras el CSV como miraría un inspector: ¿hay un dato por día sin huecos? ¿Hay fechas repetidas? ¿Los precios tienen sentido (ningún cero, ningún salto del 90 %)? ¿Las fechas están en UTC? Si las cuatro respuestas son sí, el dato sirve. Si no, lo arreglas antes de calcular nada.
Ejemplo numérico
Lo que produce el script para BTCUSDT en 2025 (requiere red; ejecútalo en tu ordenador):
- 12 archivos ZIP descargados, uno por mes, unos 20-40 KB cada uno.
- 365 filas en el CSV final (cripto cotiza todos los días; en futuros serían ~252).
- Columnas:
fecha(UTC, medianoche de cada día) ycierre.
Las cuatro comprobaciones, como código de una línea cada una sobre el DataFrame df:
| Comprobación | Código | Debe dar |
|---|---|---|
| Sin duplicados | df.fecha.duplicated().sum() | 0 |
| Sin huecos | df.fecha.diff().dt.days.max() | 1 (cripto) o ≤ 4 (futuros, por fines de semana y festivos) |
| Precios razonables | df.cierre.pct_change().abs().max() | < 0,3 en diario (un salto mayor es sospechoso salvo evento conocido) |
| Zona horaria | df.fecha.dt.tz | UTC |
Una trampa real de esta fuente: Binance cambió en 2025 la unidad de sus marcas temporales de milisegundos a microsegundos. Un script que asuma milisegundos convierte las fechas nuevas al año 56.000. El nuestro detecta la unidad por el tamaño del número. Ese tipo de sorpresa es la norma con datos de mercado, no la excepción.
Formal
Fuente. data.binance.vision es un bucket de objetos público con la estructura data/spot/{monthly|daily}/klines/{SIMBOLO}/{INTERVALO}/{SIMBOLO}-{INTERVALO}-{AAAA}-{MM}.zip. Cada ZIP contiene un CSV sin cabecera con las columnas estándar de klines de Binance (open_time, open, high, low, close, volume, close_time, quote_volume, trades, taker_buy_base, taker_buy_quote, ignore). Comprobado en E0 (22 sep 2026). Los términos de uso de Binance restringen la redistribución; el uso propio para research está permitido en la práctica [VERIFICAR términos].
Formato de salida (contrato de la academia). Todo CSV de precios que produzcan nuestros scripts tiene: columna fecha en ISO 8601 con zona UTC explícita, columnas de precio en minúsculas (open, high, low, close o solo cierre para series de cierres), sin índice numérico, ordenado ascendente por fecha. Este contrato es lo que primer_script.py espera y lo que esperará el motor de backtest del Nivel 2. Un formato fijo elimina una familia entera de bugs.
Integridad. Las cuatro comprobaciones de la capa azul son el mínimo. En el Nivel 3 (M3.2 y M3.5) se amplían a: sesiones parciales y festivos, barras con volumen cero, precios fuera del rango [low, high], gaps de roll en futuros (M0.2.4), y consistencia entre proveedores. La regla general: un dato de mercado no se usa hasta que ha pasado un script de validación que devuelve "OK" o una lista de problemas. Nunca se corrigen datos a mano; se corrigen en código, documentando qué se hizo.
Zona horaria. Las marcas de Binance son UTC. Las de CME (si compras datos) suelen venir en hora de Chicago o en UTC según proveedor. Todo se convierte a UTC al cargar (tz_convert("UTC") si ya tiene zona; tz_localize("America/Chicago") seguido de tz_convert("UTC") si viene sin zona en hora local). M0.2.5 explica por qué.
Reproducibilidad. El script descarga siempre lo mismo para el mismo símbolo y rango; el CSV resultante debería tener el mismo hash cada vez (Binance no reescribe archivos mensuales cerrados). Guardar el hash junto al CSV es una práctica del Nivel 2.
Ejemplo trabajado con código
El script de descarga. Requiere conexión; aquí se muestra el código y no una salida, porque la salida depende de lo que Binance sirva el día que lo ejecutes.
# requests 2.32 · pandas 3.0 — Descarga cierres diarios de BTCUSDT desde el bucket público de Binance (data.binance.vision)
# Requiere red. Genera data/btc_diario.csv con columnas fecha,cierre para usar con primer_script.py.
import io, zipfile, sys
from pathlib import Path
import requests, pandas as pd
def descargar_mes(simbolo: str, anio: int, mes: int) -> pd.DataFrame:
url = f"https://data.binance.vision/data/spot/monthly/klines/{simbolo}/1d/{simbolo}-1d-{anio}-{mes:02d}.zip"
r = requests.get(url, timeout=60); r.raise_for_status()
with zipfile.ZipFile(io.BytesIO(r.content)) as z:
nombre = z.namelist()[0]
df = pd.read_csv(z.open(nombre), header=None)
df = df.iloc[:, :6]; df.columns = ["open_time", "open", "high", "low", "close", "volume"]
unidad = "us" if df.open_time.iloc[0] > 1e14 else "ms" # Binance cambió a microsegundos en 2025
df["fecha"] = pd.to_datetime(df.open_time, unit=unidad, utc=True)
return df[["fecha", "close"]].rename(columns={"close": "cierre"})
if __name__ == "__main__":
simbolo = sys.argv[1] if len(sys.argv) > 1 else "BTCUSDT"
partes = [descargar_mes(simbolo, 2025, m) for m in range(1, 13)]
df = pd.concat(partes).sort_values("fecha")
Path("data").mkdir(exist_ok=True)
df.to_csv("data/btc_diario.csv", index=False)
print(f"{len(df)} días guardados en data/btc_diario.csv ({df.fecha.iloc[0].date()} → {df.fecha.iloc[-1].date()})")Al ejecutarlo en tu ordenador con red, la última línea imprime cuántos días guardó y el rango de fechas, y el CSV queda en data/btc_diario.csv. Después:
uv run python primer_script.py data/btc_diario.csvproduce la tabla de métricas de la lección anterior, esta vez sobre bitcoin 2025.
Figura
(Figura estática en SVG.)
El error típico
Descargar, ver que el CSV tiene filas, y calcular. Tres casos reales que no dan error: (1) el proveedor devuelve el mismo mes dos veces por un reintento, y tienes 30 fechas duplicadas que inflan el volumen y distorsionan los retornos (uno de cada dos retornos es cero); (2) faltan tres semanas de datos por un fallo del proveedor y el retorno "de esos días" se convierte en un salto enorme en una sola barra, que tu estrategia interpreta como señal; (3) las fechas están en hora local sin zona y, al mezclar con otra fuente en UTC, las barras se desalinean un día. Ninguno se ve mirando el gráfico por encima. Todos se ven con las cuatro comprobaciones. Hazlas siempre; automatízalas en el Nivel 2.
Quiz
5 preguntas · se aprueba con 60 %
1.`df.fecha.duplicated().sum()` devuelve 30. Significa:
2.En una serie diaria de futuros, `df.fecha.diff().dt.days.max()` da 4. ¿Problema?
3.Binance cambió las marcas temporales de milisegundos a microsegundos. Un script que asume ms:
4.El contrato de formato de la academia para un CSV de cierres es:
5.Encuentras un salto del 40 % en un día en cierres diarios de BTC. ¿Qué haces primero?
0 de 5 respondidas
Ejercicio
- Ejecuta
descargar_binance.pyy pásale aprimer_script.pyel CSV resultante. Anota retorno total, volatilidad y drawdown máximo de BTC en 2025. - Escribe
comprobar_datos.pyque lea un CSV con el contrato de la academia y ejecute las cuatro comprobaciones, imprimiendo "OK" o la lista de problemas. Pásalo sobredata/btc_diario.csvy sobredata/serie10.csvde la lección anterior. - Cambia el símbolo a ETHUSDT y repite.
Resultado verificable: BTC 2025 debe dar 365 filas, 0 duplicados, hueco máximo de 1 día y zona UTC. Los valores de retorno y volatilidad no se dan aquí porque dependen de los datos reales que descargues: anótalos con la fecha de descarga; es tu primer dato de mercado propio. La volatilidad anualizada de BTC en 2025 debería quedar entre el 35 % y el 70 % (con √365, no √252: cripto cotiza todos los días); si te sale fuera de ese rango, revisa la anualización.
Qué te llevas
- Los datos públicos de Binance se descargan con 30 líneas; lo difícil no es bajarlos, es comprobarlos.
- Cuatro comprobaciones mínimas: duplicados, huecos, saltos, zona horaria. Siempre, antes de calcular.
- Formato fijo (fecha UTC, cierre, ascendente) para todo CSV de la academia. Los datos no se arreglan a mano: se arreglan en código y se documenta.
Siguiente lección: Agregar y remuestrear barras: de 15 minutos a 1 hora sin partir la sesión. La última pieza del Nivel 0: manipular series temporales en pandas con las reglas de M0.2.5 y M0.4.1.
