---
title: "1.3.5 Datos de cripto y on-chain"
description: "Datos cripto en Python: velas de exchanges con ccxt, paginación y límites, agregadores como CoinGecko y CoinMarketCap, Kaiko y métricas on-chain."
url: "https://pythonparatrading.com/curso-python-trading/nivel-1/datos-de-cripto-y-on-chain/"
lang: "es"
image: "https://pythonparatrading.com/og/curso-python-trading/nivel-1/datos-de-cripto-y-on-chain.png"
---

# 1.3.5 · Datos de cripto y on-chain

> En cripto no hay una bolsa principal ni una cinta consolidada: cada exchange publica sus propios datos y la cadena de bloques publica los suyos. Esta lección lee el formato de velas de ccxt, lo valida, lo pagina contra un exchange simulado y separa lo que dan los exchanges, los agregadores y los datos on-chain.

- Curso: [Nivel 1: Datos financieros, mercados y análisis](https://pythonparatrading.com/curso-python-trading/nivel-1/)
- Módulo: 1.3 · Fuentes de datos de mercado
- Duración: 55 min
- Requisitos: [1.2.6 · Cripto: spot, perpetuos, funding y custodia](https://pythonparatrading.com/curso-python-trading/nivel-1/cripto-spot-perpetuos-y-funding/), [1.3.1 · Tipos de datos de mercado: EOD, barras, ticks, libro y referencia](https://pythonparatrading.com/curso-python-trading/nivel-1/tipos-de-datos-de-mercado/)

## Al terminar esta lección

- Distinguir las tres capas de datos cripto: exchanges (CEX), agregadores y datos on-chain, y qué pregunta responde cada una.
- Convertir la respuesta de `fetch_ohlcv` de ccxt en un DataFrame con marcas de tiempo UTC y validarla: intervalos, coherencia OHLC y vela abierta.
- Paginar una descarga larga con `since` y `limit` respetando el límite de peticiones del exchange.
- Explicar qué mide una métrica on-chain (direcciones activas, flujos a exchanges) y por qué depende de cómo se etiquetan las direcciones.

## Tres capas de datos

En acciones hay una bolsa principal, una cinta consolidada y un regulador que obliga a publicar cada operación (lección
1.1.1). En cripto no: cada exchange centralizado (CEX) es a la vez bolsa, bróker y, muchas veces, custodio, y publica
sus propios precios con su propia API. Encima de ellos hay agregadores que combinan muchos exchanges, y debajo, la cadena
de bloques, que registra las transferencias de verdad. Son tres capas que responden a preguntas distintas:

```mermaid
flowchart TB
  C[Cadena de bloques] --> N[Proveedor on-chain]
  C --> X[Exchanges CEX]
  X --> A[Agregadores]
  X --> T[Tu programa]
  A --> T
  N --> T
```

| Capa | Qué da | Pregunta que responde | Ejemplos |
|---|---|---|---|
| Exchange (CEX) | Velas, operaciones, libro, funding de los perpetuos del propio exchange | ¿A qué precio puedo operar aquí y ahora? | APIs de cada exchange, unificadas con ccxt |
| Agregador | Precio combinado, capitalización, volumen de muchos exchanges | ¿Cuánto vale el activo «en general»? | CoinGecko, CoinMarketCap; Kaiko para uso institucional |
| On-chain | Bloques, transacciones, direcciones, comisiones; métricas derivadas | ¿Quién mueve qué en la red? | Nodo propio, exploradores, Glassnode |

Para un sistema que opera en un exchange, la fuente de precios es **ese exchange**: es donde se ejecuta. El agregador
sirve para comparar y para el universo (qué activos existen, cuánto capitalizan); el on-chain, para variables que no
están en el precio.

## ccxt: una API para más de cien exchanges

Cada exchange tiene su API, con sus nombres, sus formatos y sus límites. ccxt es una librería de código abierto
(Python, JavaScript, PHP…) que las unifica: el mismo método devuelve el mismo formato en todos. Su documentación habla
de más de cien exchanges soportados[^1]. No forma parte del entorno del curso porque necesita red; el uso típico es:

```python skip title="descarga_ccxt.py"
import ccxt

exchange = ccxt.binance()            # enableRateLimit=True por defecto
exchange.load_markets()              # mercados y símbolos unificados
print(exchange.has["fetchOHLCV"], list(exchange.timeframes)[:5])

desde = exchange.parse8601("2025-12-01T00:00:00Z")   # milisegundos
velas = exchange.fetch_ohlcv("BTC/USDT", "1h", since=desde, limit=500)
```

Los símbolos son unificados: `BTC/USDT` es el spot de bitcoin contra USDT en cualquier exchange, y un perpetuo lineal
se escribe `BTC/USDT:USDT` (base, cotización y divisa de liquidación). `has["fetchOHLCV"]` dice si el exchange da velas
y `timeframes`, qué intervalos acepta. `fetch_ohlcv(símbolo, timeframe, since, limit)` devuelve una lista de velas,
de la más antigua a la más reciente, cada una como `[marca de tiempo en ms UTC, apertura, máximo, mínimo, cierre,
volumen]`[^1].

## Leer el formato de `fetch_ohlcv`

`xsn_ccxt_ohlcv.json` es una respuesta con ese formato: las 200 últimas velas horarias de 2025 de XSN, el token ficticio
del curso. Es un ejemplo de formato, no una descarga real, pero se procesa exactamente igual que la de un exchange:

```python
import json
from pathlib import Path

import pandas as pd

velas = json.loads(Path("xsn_ccxt_ohlcv.json").read_text())
print(type(velas).__name__, len(velas), "velas")
velas[:2]
```
```output
list 200 velas
[[1766502000000, 47.1217, 47.298, 46.9181, 46.9485, 115011.657], [1766505600000, 46.9485, 47.3096, 46.913, 47.2735, 156031.363]]
```

Una lista de listas, sin nombres de columna: el orden es la especificación. El primer número es la apertura de la vela
en **milisegundos** desde 1970 en UTC. Convertirlo con `unit="s"` daría fechas en el año 57.000; olvidar `utc=True`
daría fechas sin zona, que se mezclarían mal con cualquier otra serie (lección 1.4.2).

```python
cols = ["ts", "apertura", "maximo", "minimo", "cierre", "volumen"]
df = pd.DataFrame(velas, columns=cols)
df["ts"] = pd.to_datetime(df["ts"], unit="ms", utc=True)
df = df.set_index("ts")
print(df.index[0], "->", df.index[-1])
df.tail(3)
```
```output
2025-12-23 15:00:00+00:00 -> 2025-12-31 22:00:00+00:00
                           apertura   maximo   minimo   cierre     volumen
ts
2025-12-31 20:00:00+00:00   48.7692  48.8314  48.1571  48.1701  142982.716
2025-12-31 21:00:00+00:00   48.1701  48.2427  48.1480  48.1839  183139.453
2025-12-31 22:00:00+00:00   48.1839  48.2721  48.1744  48.2534  106840.362
```

El volumen viene en unidades del activo base (XSN), no en dólares. Para comparar volúmenes entre activos, o con un
agregador que da volumen en USD, hay que multiplicar por el precio, y eso ya es una estimación: cada operación de la vela
tuvo su propio precio.

## Validar antes de guardar

Una respuesta que se ha parseado bien puede estar mal. Tres comprobaciones baratas que conviene pasar a cada descarga:

```python
pasos = df.index.to_series().diff().dropna().value_counts()
print("intervalos:", pasos.to_dict())

cuerpo_max = df[["apertura", "cierre"]].max(axis=1)
cuerpo_min = df[["apertura", "cierre"]].min(axis=1)
ok = (df["maximo"] >= cuerpo_max) & (df["minimo"] <= cuerpo_min)
print("velas OHLC coherentes:", ok.all())

ap, ci = df["apertura"].to_numpy(), df["cierre"].to_numpy()
seguida = ap[1:] == ci[:-1]
print(f"apertura = cierre anterior: {seguida.mean():.0%}")
```
```output
intervalos: {Timedelta('0 days 01:00:00'): 199}
velas OHLC coherentes: True
apertura = cierre anterior: 100%
```

Todos los intervalos son de una hora, ninguna vela tiene el máximo por debajo del cuerpo y, en estos datos, cada vela
abre en el cierre de la anterior. Lo último es propio de un mercado continuo 24/7 como cripto; en acciones no se cumple
de un día a otro (hay subastas y huecos), y en un exchange real puede fallar por una operación suelta entre dos velas.

La cuarta comprobación es la más olvidada: la **vela abierta**. La mayoría de exchanges devuelven también la vela en
curso, con su cierre, su máximo, su mínimo y su volumen todavía cambiando. Si la descarga se hace a las 22:30, la vela
de las 22:00 no ha terminado:

```python
def quitar_vela_abierta(df, ahora, intervalo="1h"):
    fin = df.index + pd.Timedelta(intervalo)
    return df[fin <= ahora]

ahora = pd.Timestamp("2025-12-31 22:30", tz="UTC")
cerradas = quitar_vela_abierta(df, ahora)
print(len(df), "velas;", len(cerradas), "cerradas a", ahora)
print("última cerrada:", cerradas.index[-1])
```
```output
200 velas; 199 cerradas a 2025-12-31 22:30:00+00:00
última cerrada: 2025-12-31 21:00:00+00:00
```

Guardar la vela abierta como definitiva crea un histórico que nadie más puede reproducir: quien la descargue mañana
verá otro cierre y otro volumen. En un backtest, además, es información parcial tratada como completa.

## Re-muestrear: velas de 4 horas

Con el DataFrame en hora UTC, cambiar de intervalo es un `resample`. El detalle es el mismo que con la vela abierta: los
grupos de los extremos pueden estar incompletos. La primera vela del archivo empieza a las 15:00, así que el grupo de
las 12:00 a las 16:00 solo tiene una hora:

```python
reglas = {"apertura": "first", "maximo": "max", "minimo": "min",
          "cierre": "last", "volumen": "sum"}
grupos = df.resample("4h")
v4 = grupos.agg(reglas)
v4["horas"] = grupos["cierre"].count()
print(v4["horas"].value_counts().sort_index().to_dict())
v4 = v4[v4["horas"] == 4].drop(columns="horas")
print(len(v4), "velas de 4 h completas")
v4.round(2).tail(3)
```
```output
{1: 1, 3: 1, 4: 49}
49 velas de 4 h completas
                           apertura  maximo  minimo  cierre    volumen
ts
2025-12-31 08:00:00+00:00     48.17   48.77   48.04   48.48  709687.91
2025-12-31 12:00:00+00:00     48.48   48.68   48.33   48.36  539446.43
2025-12-31 16:00:00+00:00     48.36   48.81   48.32   48.77  701424.06
```

La figura de la cabecera dibuja el resultado. En lugar de 51 grupos quedan 49 velas completas de 4 horas: las de los extremos tenían 1 y 3 horas. `resample` agrupa
por el inicio del intervalo (con `4h`, pandas alinea los grupos con la medianoche UTC), que es también la convención de ccxt y de la
mayoría de exchanges. No todos los proveedores la siguen, como se ve más abajo con CoinGecko.

## Paginar con `since` y `limit`

Un exchange no devuelve años de velas en una sola petición: cada respuesta tiene un máximo de velas (`limit`), que
depende del exchange. Para descargar un periodo largo hay que pedir por páginas, avanzando `since` hasta la última vela
recibida, y esperar entre peticiones para no superar el límite. En ccxt, `exchange.rateLimit` es la pausa en
**milisegundos** que el limitador interno mantiene entre peticiones (multiplicada por el «coste» de cada llamada), y el
limitador viene activado por defecto[^1].

```mermaid
flowchart TB
  S[since = inicio] --> P[fetch_ohlcv]
  P --> V{¿Velas nuevas?}
  V -- no --> F[Fin]
  V -- sí --> G[Guardar]
  G --> A[since = última+1h]
  A --> E[Esperar rateLimit]
  E --> P
```

Para que el bucle se ejecute de verdad sin red, un exchange simulado sirve las 200 velas del archivo con las mismas
reglas: un máximo de 60 velas por llamada, velas desde `since` en adelante y un `rateLimit` de 100 ms, que aquí se
suma en un reloj virtual en lugar de dormir:

```python
class ExchangeSimulado:
    rateLimit = 100          # ms entre peticiones, como en ccxt
    max_velas = 60

    def __init__(self, velas):
        self.velas = velas
        self.llamadas = 0
        self.reloj_ms = 0

    def fetch_ohlcv(self, symbol, timeframe="1h", since=None,
                    limit=None):
        self.llamadas += 1
        self.reloj_ms += self.rateLimit
        n = min(limit or self.max_velas, self.max_velas)
        nuevas = [v for v in self.velas
                  if since is None or v[0] >= since]
        return nuevas[:n]

def descargar(ex, symbol, since, paso_ms=3_600_000):
    todas = []
    while True:
        lote = ex.fetch_ohlcv(symbol, "1h", since=since, limit=1000)
        if not lote:
            break
        todas += lote
        since = lote[-1][0] + paso_ms
    return todas

ex = ExchangeSimulado(velas)
bajadas = descargar(ex, "XSN/USDT", since=velas[0][0])
print(len(bajadas), "velas en", ex.llamadas, "llamadas,",
      ex.reloj_ms, "ms de espera")
print("iguales al original:", bajadas == velas)
```
```output
200 velas en 5 llamadas, 500 ms de espera
iguales al original: True
```

Cuatro llamadas con datos (60 + 60 + 60 + 20 velas) y una quinta que llega vacía y termina el bucle. Aunque se pidieron
1.000 velas por llamada, el exchange solo dio 60: el código no debe suponer que recibe lo que pide, sino avanzar desde lo
que recibe. Dos errores frecuentes en este bucle:

- **Avanzar `since` sumando `limit × intervalo`** en lugar de partir de la última vela recibida. Si el exchange devuelve
  menos velas de las pedidas, se salta un tramo sin darse cuenta.
- **No deduplicar.** Si `since` se fija en la última vela y no en la siguiente, cada página repite una vela. Con
  `+ paso_ms` no ocurre; aun así, conviene `df[~df.index.duplicated()]` al final.

A escala real, 200 velas son minutos de espera; un año de velas de 1 minuto son 525.600 velas, cientos de llamadas por
símbolo. El límite de peticiones marca cuánto tarda una descarga histórica, y superarlo suele acabar en un error de
«demasiadas peticiones» o en un bloqueo temporal de la IP. La lección 1.4.1 trata los reintentos con espera creciente.

## Huecos: el exchange también para

Un mercado 24/7 no garantiza velas 24/7. Los exchanges tienen mantenimientos, caídas y, en pares poco líquidos, horas
sin operaciones. Cada exchange decide qué devolver entonces: algunos no devuelven la vela; otros, una vela con volumen
cero. Para simularlo, se quitan tres horas de la descarga:

```python
con_hueco = df.drop(df.index[100:103])
rejilla = pd.date_range(con_hueco.index[0], con_hueco.index[-1],
                        freq="1h")
faltan = rejilla.difference(con_hueco.index)
print(len(con_hueco), "velas;", len(faltan), "huecos:")
print(list(faltan.strftime("%Y-%m-%d %H:%M")))
```
```output
197 velas; 3 huecos:
['2025-12-27 19:00', '2025-12-27 20:00', '2025-12-27 21:00']
```

Comparar con la rejilla completa es la forma fiable de encontrar huecos; contar filas no dice dónde están. Qué hacer
con ellos es una decisión que hay que documentar: dejarlos como faltantes, o rellenarlos con una vela plana (apertura =
máximo = mínimo = cierre = cierre anterior, volumen 0) si el exchange estaba abierto y simplemente no hubo operaciones.
Si estaba caído, la vela plana es falsa: nadie podía operar a ese precio. La lección 1.4.2 trata la limpieza completa.

## No hay un precio de XSN: hay uno por exchange

Cada exchange tiene su libro y sus participantes, así que el precio de un mismo token difiere un poco de un sitio a
otro. Por eso los perpetuos calculan el funding y las liquidaciones contra un **índice compuesto** de varios exchanges
(lección 1.2.6). `xsn_1h.parquet` trae el spot de un exchange y ese índice:

```python
xsn = pd.read_parquet("xsn_1h.parquet").set_index("ts")
dif_pb = 1e4 * (xsn["spot"] / xsn["indice"] - 1)
dif_pb.describe().round(2)[["mean", "std", "min", "max"]]
```
```output
mean    -0.07
std      4.00
min    -15.62
max     14.80
dtype: float64
```

En los datos del curso el spot se separa del índice unos 4 puntos básicos de desviación típica, con extremos de unos 15.
Es poco para un sistema diario, pero es del orden de la comisión de un exchange y mucho para uno intradía. La
consecuencia práctica: un backtest debe usar los precios del exchange donde va a operar, y una señal calculada con el
precio de un agregador puede no ser ejecutable a ese precio en ningún exchange concreto.

*Fig. — Diferencia entre el spot de XSN en un exchange y su índice compuesto, cada hora de 2024 y 2025.* Histograma de 17.544 horas con la diferencia entre el spot y el índice en puntos básicos: centrado en cero, con forma de campana, casi todo entre −10 y +10 y extremos de −15,6 y +14,8. Dos líneas ámbar marcan una desviación típica, ±4,0 puntos básicos.

## Agregadores: CoinGecko, CoinMarketCap y Kaiko

Los agregadores recogen precios y volúmenes de muchos exchanges y los combinan. Son útiles para construir un universo
(qué tokens existen, su capitalización, en qué exchanges cotizan) y para precios de referencia, no para ejecutar.

- **CoinGecko** tiene un plan Demo gratuito con clave, de 100 llamadas por minuto y 10.000 créditos al mes, con un año
  de historia, según su página de precios y su documentación a fecha de 21/10/2026[^2].
- **CoinMarketCap** tiene un plan Basic gratuito de 15.000 créditos al mes; en ese plan hay cotizaciones históricas
  diarias de un año, pero no velas OHLCV históricas, que empiezan en planes de pago[^3].
- **Kaiko** se dirige a clientes institucionales (operaciones y libros de más de cien exchanges, tipos de referencia e
  índices) y no publica tarifas: hace planes a medida[^4].

Cada agregador tiene su formato, y no siempre coincide con el de los exchanges. El endpoint de velas de CoinGecko
(`/coins/{id}/ohlc`) devuelve cinco valores por vela, **sin volumen**, y su marca de tiempo indica el **cierre** de la
vela, no la apertura. Además elige la granularidad según el número de días pedido (30 minutos para 1 o 2 días, 4 horas
hasta 30 días, 4 días por encima)[^2]. Con una muestra ilustrativa en ese formato:

```python skip title="coingecko_ohlc.py"
import os

import requests

r = requests.get(
    "https://api.coingecko.com/api/v3/coins/bitcoin/ohlc",
    params={"vs_currency": "usd", "days": 7},
    headers={"x-cg-demo-api-key": os.environ["CG_DEMO_KEY"]},
    timeout=10,
)
r.raise_for_status()
ohlc = r.json()    # [[ms de cierre, apertura, máximo, mínimo, cierre]]
```

```python
muestra_cg = [   # muestra ilustrativa con el formato documentado
    [1767225600000, 48.37, 48.83, 48.15, 48.25],
    [1767240000000, 48.25, 48.41, 47.90, 48.02],
]
cg = pd.DataFrame(muestra_cg, columns=cols[:5])
cg["cierre_ts"] = pd.to_datetime(cg["ts"], unit="ms", utc=True)
cg["apertura_ts"] = cg["cierre_ts"] - pd.Timedelta("4h")
cg[["apertura_ts", "cierre_ts", "cierre"]]
```
```output
                apertura_ts                 cierre_ts  cierre
0 2025-12-31 20:00:00+00:00 2026-01-01 00:00:00+00:00   48.25
1 2026-01-01 00:00:00+00:00 2026-01-01 04:00:00+00:00   48.02
```

Si se mezclan velas de ccxt (hora de apertura) con velas de CoinGecko (hora de cierre) sin convertir, una de las dos
series queda desplazada un intervalo entero. Es un sesgo de anticipación de manual: la vela «de las 04:00» de CoinGecko
contiene precios que en la convención de ccxt pertenecen a las 00:00–04:00.

## Datos on-chain

La cadena de bloques es pública: cualquiera con un nodo puede leer cada bloque, cada transacción, cada dirección y su
saldo. A partir de eso, los proveedores on-chain calculan métricas: direcciones activas, volumen transferido, flujos de
entrada y salida de exchanges, beneficios y pérdidas realizados. Glassnode, por ejemplo, documenta más de 800 métricas,
entre ellas el MVRV (capitalización de mercado entre capitalización «realizada») y el SOPR (grado de beneficio o pérdida
realizado de las monedas que se mueven); el acceso completo a su API está en el plan Professional y el plan Advanced solo
tiene una API limitada[^5].

La cadena dice qué direcciones movieron qué cantidad, pero **no de quién son**. Una métrica como «entradas en exchanges»
depende de qué direcciones ha etiquetado el proveedor como de exchanges. Un ejemplo pequeño y ficticio, con cuatro
transferencias:

```python
transfer = pd.DataFrame({
    "origen":  ["w1", "w2", "ex_a", "w3"],
    "destino": ["ex_a", "ex_b", "w4", "w5"],
    "xsn":     [5_000, 3_000, 2_000, 4_000],
})

def flujo_neto(t, etiquetas_exchange):
    entra = t["destino"].isin(etiquetas_exchange)
    sale = t["origen"].isin(etiquetas_exchange)
    return int(t.loc[entra, "xsn"].sum() - t.loc[sale, "xsn"].sum())

print("etiquetado v1:", flujo_neto(transfer, {"ex_a"}))
print("etiquetado v2:", flujo_neto(transfer, {"ex_a", "ex_b", "w5"}))
```
```output
etiquetado v1: 3000
etiquetado v2: 10000
```

Las mismas cuatro transferencias dan un flujo neto de 3.000 XSN hacia exchanges con un etiquetado y de 10.000 con otro,
que ha identificado una dirección más de un exchange (`ex_b`) y otra que el primero no conocía (`w5`). Cuando el
proveedor mejora su etiquetado, suele recalcular la historia: la serie que se descargó hace un año ya no es la de hoy.
Para un backtest es el mismo problema que las revisiones de datos macro (lección 1.5.2): hay que saber si la métrica es
la que se conocía en cada fecha o la revisada después.

<aside class="callout" data-kind="aviso">

Las métricas on-chain describen la red, no el exchange donde operas. Que una métrica se asocie con el precio en un
gráfico no la convierte en señal: antes hay que comprobarlo con datos point-in-time y con costes (nivel 2).

</aside>

## Ejercicios

1. Con `df`, construye velas diarias (UTC) y quédate solo con los días completos. ¿Cuántas quedan y por qué no son 9?

**Solución.**

```python
g = df.resample("1D")
diarias = g.agg(reglas)
diarias["horas"] = g["cierre"].count()
print(diarias["horas"].tolist())
diarias = diarias[diarias["horas"] == 24]
print(len(diarias), "días completos")
```
```output
[9, 24, 24, 24, 24, 24, 24, 24, 23]
7 días completos
```

Las 200 velas abarcan del 23 de diciembre a las 15:00 al 31 de diciembre a las 22:00: nueve fechas, pero el primer día
solo tiene 9 horas y el último 23 (falta la vela de las 23:00). Quedan 7 días completos. Un «día» cripto en UTC no es el
día de Madrid ni el de Nueva York; si el sistema razona en otra zona, hay que re-muestrear en esa zona (lección 1.4.2).

2. Cambia `ExchangeSimulado.max_velas` a 50 y vuelve a descargar. ¿Cuántas llamadas hacen falta? ¿Qué pasaría si
   `descargar` avanzara `since` en `1000 × 1 h` en lugar de desde la última vela?

**Solución.**

```python
ExchangeSimulado.max_velas = 50
ex = ExchangeSimulado(velas)
bajadas = descargar(ex, "XSN/USDT", since=velas[0][0])
print(len(bajadas), "velas en", ex.llamadas, "llamadas")

def descargar_mal(ex, since, limit=1000, paso_ms=3_600_000):
    todas = []
    while lote := ex.fetch_ohlcv("XSN/USDT", "1h", since, limit):
        todas += lote
        since += limit * paso_ms
    return todas

print("avanzando por limit:", len(descargar_mal(ex, velas[0][0])))
ExchangeSimulado.max_velas = 60
```
```output
200 velas en 5 llamadas
avanzando por limit: 50
```

Con páginas de 50 velas hacen falta cinco llamadas (cuatro con datos y una vacía). Avanzando `since` en 1.000 horas,
la segunda llamada ya pide velas posteriores a la última del archivo: solo se descargan las 50 primeras y el resto se
pierde sin ningún error. Es el fallo más peligroso de la paginación, porque el resultado parece correcto.

3. Vas a construir un sistema que opera el perpetuo de XSN en un exchange. Di de qué capa sacarías el precio de las
   señales, el funding y el universo de tokens, y qué comprobarías de cada fuente.

**Solución.**

Precio de las señales y funding: del propio exchange donde se opera (velas y tasas de funding con ccxt), porque es el
precio ejecutable y el funding que se paga o se cobra (lección 1.2.6). Comprobaciones: intervalos y vela abierta,
huecos frente a la rejilla, y la hora de corte del funding en UTC. El índice compuesto del exchange, si lo publica,
para entender liquidaciones. Universo: de un agregador (qué tokens existen y su capitalización), comprobando que cada
token del universo tiene mercado en el exchange y desde cuándo, para no meter en el pasado tokens que todavía no
cotizaban allí (sesgo de supervivencia, lección 1.4.4). On-chain, solo si se va a investigar una métrica concreta y
sabiendo cómo etiqueta el proveedor.

[^1]: Documentación de ccxt, Manual: secciones «OHLCV Candlestick Charts» y «Rate Limit» ([docs.ccxt.com](https://docs.ccxt.com/docs/manual)), consultada el 21/10/2026.
[^2]: CoinGecko API: [precios](https://www.coingecko.com/en/api/pricing), [errores y límites](https://docs.coingecko.com/docs/errors-and-rate-limits) y [Coin OHLC Chart by ID](https://docs.coingecko.com/demo/reference/coins-id-ohlc), consultados el 21/10/2026.
[^3]: [CoinMarketCap API Pricing Plans](https://coinmarketcap.com/api/pricing/), consultada el 21/10/2026.
[^4]: [Kaiko: Pricing and Licenses](https://www.kaiko.com/about-kaiko/pricing-and-contracts) y [Level 1 and Level 2 Data](https://www.kaiko.com/products/l1-l2-data), consultadas el 21/10/2026.
[^5]: Glassnode: [documentación](https://docs.glassnode.com/) y [precios](https://studio.glassnode.com/pricing), consultadas el 21/10/2026.

## Autoevaluación

1. `fetch_ohlcv` devuelve `[1766502000000, 47.1217, 47.298, 46.9181, 46.9485, 115011.657]`. ¿Qué es el primer número?

   - A. Un identificador de vela
   - B. La marca de tiempo de apertura de la vela en milisegundos desde 1970 (UTC)
   - C. La marca de tiempo en segundos
   - D. El volumen en USD

   *Respuesta B: La marca de tiempo de apertura de la vela en milisegundos desde 1970 (UTC).* ccxt usa milisegundos UTC en todas sus marcas de tiempo. `pd.to_datetime(…, unit="ms", utc=True)` lo convierte; con `unit="s"` saldrían fechas absurdas.

2. Descargas las velas de 1 hora a las 22:30 UTC. ¿Qué pasa con la vela de las 22:00?

   - A. Es una vela completa como las demás
   - B. Puede estar todavía abierta: su cierre, máximo, mínimo y volumen cambiarán hasta las 23:00
   - C. El exchange nunca la devuelve
   - D. Es la vela de las 21:00 con otro nombre

   *Respuesta B: Puede estar todavía abierta: su cierre, máximo, mínimo y volumen cambiarán hasta las 23:00.* Muchos exchanges devuelven la vela en curso. Si se guarda como definitiva, el histórico tendrá una vela distinta de la que verá cualquier otro que la descargue más tarde. Hay que descartar las velas cuyo final es posterior al momento de la descarga.

3. ¿Por qué el precio de XSN en un exchange no coincide exactamente con su índice compuesto?

   - A. Porque uno de los dos está mal
   - B. Porque cada exchange tiene su propio libro y sus propios participantes; el índice combina varios exchanges y por eso lo usan el funding y las liquidaciones
   - C. Porque el índice va con un día de retraso
   - D. Porque el exchange cobra comisiones dentro del precio

   *Respuesta B: Porque cada exchange tiene su propio libro y sus propios participantes; el índice combina varios exchanges y por eso lo usan el funding y las liquidaciones.* No hay un precio único de un criptoactivo: hay un precio por exchange. En los datos del curso, el spot de un exchange se separa del índice unos 4 puntos básicos de desviación típica.

4. Un agregador dice que han entrado 10.000 XSN en exchanges hoy. ¿De qué depende esa cifra?

   - A. Solo de la cadena de bloques, que es pública y exacta
   - B. De qué direcciones ha etiquetado el proveedor como de exchanges: la cadena dice qué direcciones movieron qué, no de quién son
   - C. Del precio del día
   - D. De la comisión de la red

   *Respuesta B: De qué direcciones ha etiquetado el proveedor como de exchanges: la cadena dice qué direcciones movieron qué, no de quién son.* Las transacciones son públicas, pero los dueños de las direcciones no. Cada proveedor de datos on-chain mantiene su propio etiquetado; si cambia o se amplía, la serie histórica cambia con él.

5. Un exchange declara `rateLimit = 100` en ccxt. ¿Qué significa?

   - A. 100 peticiones por segundo
   - B. Un mínimo de 100 milisegundos entre peticiones, que ccxt respeta si el limitador está activado
   - C. 100 velas por petición
   - D. 100 peticiones al día

   *Respuesta B: Un mínimo de 100 milisegundos entre peticiones, que ccxt respeta si el limitador está activado.* En ccxt, `rateLimit` es el intervalo mínimo en milisegundos entre peticiones. Con `enableRateLimit` activado (el valor por defecto), la propia librería espera lo necesario.

## Ideas clave

- Los datos cripto vienen de tres capas: exchanges (el precio y el volumen de cada sitio), agregadores (un precio combinado de muchos) y la cadena (transferencias, direcciones, comisiones).
- ccxt unifica las APIs de los exchanges: `fetch_ohlcv` devuelve listas `[ms UTC, apertura, máximo, mínimo, cierre, volumen]`.
- Una descarga seria comprueba los intervalos, la coherencia OHLC y descarta la vela abierta antes de guardar.
- Las descargas largas se paginan con `since` y `limit`, y el ritmo lo marca el límite de peticiones de cada exchange.
- No hay un precio único: cada exchange tiene el suyo. Para comparar o para el funding, se usa un índice compuesto.
- Las métricas on-chain dependen del etiquetado de direcciones del proveedor: hay que saber cuál es y si se revisa hacia atrás.

## Recursos

### Documentación

- [ccxt: manual (OHLCV, límites de peticiones, símbolos unificados)](https://docs.ccxt.com/docs/manual) · docs.ccxt.com
- [CoinGecko API: Coin OHLC Chart by ID](https://docs.coingecko.com/demo/reference/coins-id-ohlc) · docs.coingecko.com
- [Glassnode: documentación de la API y de las métricas](https://docs.glassnode.com/) · docs.glassnode.com

### Datos

- [Datos del nivel 1: velas de XSN en el formato de fetch_ohlcv (JSON)](https://pythonparatrading.com/curso/datos/nivel-1/xsn_ccxt_ohlcv.json) · Descarga · xsn_ccxt_ohlcv.json
- [Datos del nivel 1: XSN spot, perpetuo e índice por horas (Parquet)](https://pythonparatrading.com/curso/datos/nivel-1/xsn_1h.parquet) · Descarga · xsn_1h.parquet

Lección anterior: [1.3.4 · Proveedores de datos profesionales](https://pythonparatrading.com/curso-python-trading/nivel-1/proveedores-de-datos-profesionales/)

Lección siguiente: [1.3.6 · Brókeres como fuente de datos](https://pythonparatrading.com/curso-python-trading/nivel-1/brokers-como-fuente-de-datos/)
