---
title: "1.3.1 Tipos de datos de mercado: de EOD a L3"
description: "Datos diarios, barras, operaciones, cotizaciones, libro L2 y L3 y datos de referencia: qué responde cada uno, cuánto ocupa y cómo se obtiene uno del otro."
url: "https://pythonparatrading.com/curso-python-trading/nivel-1/tipos-de-datos-de-mercado/"
lang: "es"
image: "https://pythonparatrading.com/og/curso-python-trading/nivel-1/tipos-de-datos-de-mercado.png"
---

# 1.3.1 · Tipos de datos de mercado: EOD, barras, ticks, libro y referencia

> El mismo día de negociación se puede guardar en una fila o en medio millón. Esta lección recorre la escalera completa con una sesión de la acción ficticia NOVA, del mensaje orden a orden a la barra diaria, mide lo que ocupa cada peldaño y repasa las trampas de cinta, marcas de tiempo y zonas horarias que vienen con cada uno.

- 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.1.1 · Participantes y centros de negociación](https://pythonparatrading.com/curso-python-trading/nivel-1/participantes-y-centros-de-negociacion/), [1.1.3 · Libro de órdenes: bid, ask, spread y profundidad](https://pythonparatrading.com/curso-python-trading/nivel-1/libro-de-ordenes-bid-ask-y-profundidad/)

## Al terminar esta lección

- Distinguir datos diarios, barras intradía, operaciones, cotizaciones, libro L2, mensajes L3 y datos de referencia, y qué pregunta responde cada uno.
- Medir con pandas cuántas filas y cuánta memoria ocupa una sesión en cada granularidad.
- Reconstruir barras de 5 minutos y la barra diaria a partir de las operaciones, y comprobar que coinciden con los datos agregados.
- Reconocer los problemas de cinta consolidada frente a un solo centro, de marcas de tiempo (evento frente a recepción, inicio frente a fin de barra) y de zonas horarias.

## Una escalera de agregación

Cuando se habla de «datos de mercado» se habla de cosas muy distintas: el precio de cierre de cada día, una vela de
cinco minutos, cada operación, cada cambio del mejor precio o cada orden que entra y sale del libro. No son fuentes
alternativas, sino peldaños de una misma escalera. En la base están los **mensajes** que emite el motor de
negociación; todo lo demás es una agregación de ellos.

```mermaid
flowchart TB
  M[Mensajes L3] --> B[Libro reconstruido]
  B --> L2[Libro L2]
  L2 --> L1[Cotizaciones L1]
  M --> T[Operaciones]
  T --> BA[Barras intradía]
  BA --> D[Datos diarios]
  R[Referencia] -.-> D
```

- **Mensajes L3** (*market by order*): cada orden que llega, se modifica, se cancela o se ejecuta, con su
  identificador. Es lo que publican los protocolos orden a orden de las bolsas, como ITCH de Nasdaq[^1].
- **Libro L2** (*market by price*): el libro agregado por nivel de precio, con el tamaño total y el número de órdenes
  de cada nivel (lección 1.1.3).
- **Cotizaciones L1** (*top of book*): solo el mejor bid y el mejor ask, con sus tamaños.
- **Operaciones** (*trades* o *ticks*): cada ejecución, con precio, tamaño, centro y condiciones.
- **Barras intradía**: operaciones agregadas en intervalos (1 minuto, 5 minutos, 1 hora) como apertura, máximo,
  mínimo, cierre y volumen (OHLCV).
- **Datos diarios** (EOD, *end of day*): una barra por sesión.
- **Datos de referencia**: lo que describe los instrumentos (especificaciones, eventos corporativos, símbolos,
  universos). No se agrega de los mensajes, pero hace falta para interpretarlos y ajustarlos; por eso la flecha es
  discontinua.

La regla de la escalera es asimétrica: de un peldaño se puede bajar al siguiente (agregar), pero no subir. Con
operaciones se construyen barras; con barras no se recuperan las operaciones.

[^1]: Nasdaq TotalView-ITCH 5.0 (enlace en los recursos): mensajes de alta, ejecución, cancelación, borrado y
sustitución de órdenes, con marcas de tiempo en nanosegundos desde medianoche.

## Seis peldaños de NOVA

Los datos del nivel tienen un ejemplo de cada peldaño para la misma acción ficticia, NOVA, y para la misma sesión, el
2 de junio de 2025, salida de un libro simulado orden a orden (lección 1.1.1). Empezando por arriba, el dato diario:

```python
import numpy as np
import pandas as pd

diario = pd.read_csv("nova_diario_sin_ajustar.csv",
                     parse_dates=["fecha"]).set_index("fecha")
print(len(diario), "sesiones;", diario.index.min().date(), "→",
      diario.index.max().date())
diario.loc["2025-05-30":"2025-06-03"]
```
```output
2011 sesiones; 2018-01-02 → 2025-12-31
            apertura  maximo  minimo  cierre  volumen
fecha
2025-05-30     52.58   53.98   52.01   53.13  6106070
2025-06-02     53.35   53.93   52.55   53.12  8888714
2025-06-03     53.05   53.99   52.74   53.00  8044956
```

Una fila por sesión, ocho años en 2.011 filas. Responde preguntas de días a años: tendencias, volatilidad diaria,
carteras que se rebalancean al cierre. Es el dato más barato de conseguir y de guardar, y el que más trampas de
ajustes tiene (lección 1.4.3: este es **sin ajustar**, con el split de 2021 a la vista).

```python
barras = pd.read_parquet("nova_barras_5min.parquet").set_index("ts")
sesion = barras.loc["2025-06-02"]
print(len(barras), "barras en", barras.index.normalize().nunique(),
      "sesiones;", len(sesion), "el 2 de junio")
sesion.head(3)
```
```output
38940 barras en 502 sesiones; 78 el 2 de junio
                           apertura  maximo  minimo  cierre  volumen
ts
2025-06-02 13:30:00+00:00     53.35   53.48   53.28   53.46   285081
2025-06-02 13:35:00+00:00     53.46   53.48   53.22   53.24   220651
2025-06-02 13:40:00+00:00     53.24   53.47   53.23   53.35   187320
```

78 barras por sesión normal (seis horas y media en tramos de cinco minutos). Responden preguntas de minutos a días:
patrones intradía, estacionalidad dentro de la sesión, ejecución en un horario. La marca de tiempo es el **inicio** de
la barra, en UTC: la primera, 13:30 UTC, son las 9:30 de Nueva York.

```python
op = pd.read_parquet("nova_operaciones.parquet")
cot = pd.read_parquet("nova_cotizaciones.parquet")
print(len(op), "operaciones;", len(cot), "cambios de cotización")
print(op.iloc[1:3].to_string())
print(cot.iloc[1:3].to_string())
```
```output
50946 operaciones; 129247 cambios de cotización
                                ts  precio  tamano agresor     centro condicion
1 2025-06-02 13:30:00.301051+00:00   53.34     100   venta  PRINCIPAL   regular
2 2025-06-02 13:30:00.301052+00:00   53.34     100   venta  PRINCIPAL   regular
                                ts    bid    ask  tam_bid  tam_ask
1 2025-06-02 13:30:00.006643+00:00  53.34  53.36     1600      500
2 2025-06-02 13:30:00.075521+00:00  53.34  53.36     1600      800
```

Operaciones y cotizaciones son los datos «tick a tick». Las operaciones dicen qué se negoció, dónde y a qué precio;
las cotizaciones, a qué precio se podía negociar en cada instante. Juntas permiten medir la horquilla efectiva y el
deslizamiento (lección 1.1.5), clasificar compras y ventas o construir barras alternativas (lección 1.4.5).

```python
l2 = pd.read_parquet("nova_libro_l2.parquet")
l3 = pd.read_parquet("nova_libro_l3.parquet")
print(len(l2), "filas L2 (fotos cada 10 s, 10 niveles por lado)")
print(l2[l2["ts"] == l2["ts"].iloc[0]].head(4).to_string())
print(len(l3), "mensajes L3 en 5 minutos;",
      l3["evento"].value_counts().to_dict())
```
```output
44126 filas L2 (fotos cada 10 s, 10 niveles por lado)
                         ts lado  nivel  precio  tamano  ordenes
0 2025-06-02 13:30:00+00:00  bid      1   53.34    1200        3
1 2025-06-02 13:30:00+00:00  bid      2   53.33     400        2
2 2025-06-02 13:30:00+00:00  bid      3   53.32    1100        3
3 2025-06-02 13:30:00+00:00  bid      4   53.31    1100        2
7241 mensajes L3 en 5 minutos; {'alta': 3443, 'cancelacion': 2842, 'ejecucion': 746, 'modificacion': 210}
```

El L2 dice cuánta liquidez había en cada nivel; el L3, quién (qué orden) la ponía y en qué posición de la cola
estaba. En cinco minutos de NOVA hay 7.241 mensajes, y la mayoría no son operaciones: son altas y cancelaciones de
órdenes que nunca se ejecutan. Ese ruido es la materia prima de los estudios de microestructura (colas, prioridad,
órdenes iceberg) y casi nunca hace falta para un sistema de baja frecuencia.

| Tipo | Una fila es | Responde a | Uso típico |
|---|---|---|---|
| Diario (EOD) | Una sesión | Tendencia, volatilidad, carteras | Backtests diarios, investigación de factores |
| Barras intradía | Un intervalo | Patrones intradía, horarios | Sistemas intradía, estacionalidad de la sesión |
| Operaciones | Una ejecución | Qué se negoció, dónde, cuánto | VWAP, barras de volumen, costes |
| Cotizaciones (L1) | Un cambio del mejor precio | A qué precio se podía operar | Horquilla, deslizamiento, alineación |
| Libro L2 | Un nivel en un instante | Cuánta liquidez había | Impacto de mercado, profundidad |
| Mensajes L3 | Un evento de una orden | Colas y prioridad | Microestructura, simulación de ejecución |
| Referencia | Un instrumento o evento | Qué es y qué le ha pasado | Ajustes, universos, especificaciones |

## Cuánto ocupa cada peldaño

La diferencia entre peldaños es de órdenes de magnitud. Para medirla en una sesión, se cuentan las filas y la memoria
que ocupan en pandas. Los mensajes L3 solo están para cinco minutos; multiplicados por 78 dan una estimación de la
sesión completa:

```python
def mb(df):
    return df.memory_usage(deep=True).sum() / 1e6

dia = diario.loc[["2025-06-02"]]
l3_bytes_fila = l3.memory_usage(deep=True).sum() / len(l3)
tamanos = pd.DataFrame({
    "filas": [len(dia), len(sesion), len(op), len(cot), len(l2),
              len(l3) * 78],
    "MB": [mb(dia), mb(sesion), mb(op), mb(cot), mb(l2),
           l3_bytes_fila * len(l3) * 78 / 1e6],
}, index=["diario", "barras 5 min", "operaciones", "cotizaciones",
          "libro L2 (10 s)", "mensajes L3 (estimado)"])
tamanos["filas"] = tamanos["filas"].astype(int)
tamanos.round(4)
```
```output
                         filas       MB
diario                       1   0.0000
barras 5 min                78   0.0037
operaciones              50946   3.4860
cotizaciones            129247   5.1700
libro L2 (10 s)          44126   2.2506
mensajes L3 (estimado)  564798  33.0479
```

La figura de la cabecera dibuja la tabla en escala logarítmica. De la barra diaria al L3 hay casi seis órdenes de
magnitud: una fila frente a más de medio millón. Y eso para una acción ficticia de liquidez media en un solo libro.
Multiplicado por miles de valores y años de historia, la diferencia
decide la infraestructura: diez años de datos diarios de todo un mercado caben en un portátil; los mensajes L3 de una
sola bolsa durante un año, no (lección 1.4.7). También decide el precio: los proveedores cobran por granularidad,
profundidad histórica y número de bolsas (lecciones 1.3.3 y 1.3.4).

```python
sesiones, valores = 252, 3_000
anual_gb = tamanos["MB"] * sesiones * valores / 1e3
anual_gb.round(3).rename("GB por año (3.000 valores)")
```
```output
diario                        0.036
barras 5 min                  2.830
operaciones                2635.384
cotizaciones               3908.529
libro L2 (10 s)            1701.422
mensajes L3 (estimado)    24984.211
Name: GB por año (3.000 valores), dtype: float64
```

La cuenta es tosca, porque NOVA no es un valor representativo de todo un mercado y los formatos en disco comprimen
mucho más que pandas en memoria (Parquet, lección 1.4.7). Pero el orden de magnitud es el que importa: con datos
diarios, un año de 3.000 valores son unas decenas de megabytes; con operaciones y cotizaciones, varios terabytes; con
L3, decenas de terabytes.

## Treinta segundos de NOVA

La escalera se ve bien en un intervalo corto: de 14:00:00 a 14:00:30 UTC (las 10:00 en Nueva York), cuántos eventos
registra cada granularidad:

```python
ini = pd.Timestamp("2025-06-02 14:00:00", tz="UTC")
fin = ini + pd.Timedelta("30s")
en = lambda df: df[(df["ts"] >= ini) & (df["ts"] < fin)]
print("mensajes L3:  ", len(en(l3[l3["ts"] > ini])))
print("cotizaciones: ", len(en(cot)))
print("operaciones:  ", len(en(op)))
print("fotos L2:     ", en(l2)["ts"].nunique())
print("barras 5 min:  1 (en curso)")
precios = en(cot)[["bid", "ask"]].drop_duplicates()
print("pares bid/ask distintos:", precios.values.tolist())
```
```output
mensajes L3:   656
cotizaciones:  145
operaciones:   79
fotos L2:      3
barras 5 min:  1 (en curso)
pares bid/ask distintos: [[53.08, 53.09]]
```

*Fig. — Treinta segundos de NOVA, de 14:00:00 a 14:00:30 UTC del 2 de junio de 2025: eventos acumulados en cada granularidad, de los mensajes orden a orden a la barra de 5 minutos.* Tres líneas escalonadas que acumulan eventos segundo a segundo en escala de raíz cuadrada: los mensajes L3, destacados, llegan a 656 en 30 segundos; los cambios de cotización L1, a 145; las operaciones, a 79. Rótulos a la derecha añaden 3 fotos L2 y ninguna barra de 5 minutos cerrada.

En medio minuto hay 656 mensajes de órdenes, 145 cambios en el mejor bid o ask (casi todos de tamaño: en esos 30
segundos el precio no se mueve del 53,08/53,09) y 79 operaciones; el L2 hace tres fotos y la barra de cinco minutos que
los contiene todavía ni ha cerrado. La figura acumula los eventos segundo a segundo. (Las primeras 358 filas del L3, la foto
inicial del libro a las 14:00:00 en punto, se excluyen de la cuenta.)

La asimetría de la escalera se puede comprobar en el otro sentido: con las operaciones se reconstruyen las barras y,
con las barras, el dato diario. Deben coincidir con los ficheros agregados:

```python
ohlcv = {"precio": ["first", "max", "min", "last"], "tamano": "sum"}
# la subasta de cierre (20:00:00 UTC) va en la última barra
cierre = op["condicion"] == "subasta_cierre"
ts_barra = op["ts"].mask(cierre, op["ts"] - pd.Timedelta("1us"))
mias = op.set_index(ts_barra).resample("5min").agg(ohlcv)
mias.columns = ["apertura", "maximo", "minimo", "cierre", "volumen"]
print(len(mias), "barras; iguales:", np.allclose(mias, sesion))

dia_mio = pd.Series({"apertura": sesion["apertura"].iloc[0],
                     "maximo": sesion["maximo"].max(),
                     "minimo": sesion["minimo"].min(),
                     "cierre": sesion["cierre"].iloc[-1],
                     "volumen": sesion["volumen"].sum()})
print("Día igual:", np.allclose(dia_mio, dia.iloc[0]))
```
```output
78 barras; iguales: True
Día igual: True
```

Las 78 barras salen exactamente de las operaciones, y el dato diario de las barras. Hay un detalle: la subasta de
cierre se cruza a las 16:00:00 de Nueva York (20:00:00 UTC), justo en el borde, y un `resample` sin más abriría para
ella sola una barra 79 de las 20:00. El proveedor de estos datos la cuenta en la barra de las 15:55, y para casar con
él hay que hacer lo mismo (aquí, adelantando su marca un microsegundo al agrupar). Es el tipo de convención que hay que
conocer para que dos fuentes coincidan.

## Cinta consolidada o un solo centro

En EE. UU., todas las bolsas y centros envían sus operaciones y cotizaciones a un consolidador, que publica la **cinta
consolidada**: en los valores cotizados en la NYSE y en otras bolsas regionales, los sistemas CTS (operaciones) y CQS
(cotizaciones) de la Consolidated Tape Association[^2]. Un proveedor puede dar la cinta consolidada o el feed propio
de una sola bolsa, y los números cambian mucho, como se vio en la lección 1.1.1:

[^2]: Consolidated Tape Association, página de presentación (enlace en los recursos).

```python
def barra_dia(t):
    return pd.Series({
        "apertura": t["precio"].iloc[0], "maximo": t["precio"].max(),
        "minimo": t["precio"].min(), "cierre": t["precio"].iloc[-1],
        "volumen": t["tamano"].sum(), "operaciones": len(t),
        "vwap": (t["precio"] * t["tamano"]).sum() / t["tamano"].sum()})

pd.DataFrame({"consolidada": barra_dia(op),
              "solo principal": barra_dia(
                  op[op["centro"] == "PRINCIPAL"])}).round(3)
```
```output
             consolidada  solo principal
apertura          53.350          53.350
maximo            53.930          53.930
minimo            52.550          52.550
cierre            53.120          53.120
volumen      8888714.000     3564476.000
operaciones    50946.000       25776.000
vwap              53.319          53.291
```

Con la cinta de un solo centro, el volumen del día es el 40 % del real, hay la mitad de operaciones y el VWAP baja de
53,319 a 53,291. Apertura y cierre coinciden porque los fijan las subastas del centro principal; el máximo y el mínimo
también coinciden ese día, pero no tienen por qué: el precio extremo de la sesión puede cruzarse en otro centro. Antes de comparar volúmenes entre fuentes, o de calcular un VWAP
para medir una ejecución, hay que saber qué cinta se usa.

## Marcas de tiempo y zonas horarias

Cada fila de datos de mercado lleva una marca de tiempo, y cada marca tiene una convención. Tres preguntas hay que
hacerse siempre:

1. **¿De qué evento es la marca?** La del motor de negociación (cuando ocurre el evento) o la de recepción (cuando lo
   recibe quien graba los datos). Los proveedores profesionales suelen dar las dos: Databento, por ejemplo, publica
   `ts_event` y `ts_recv`, y advierte de que los relojes del mercado y del receptor no están necesariamente
   sincronizados[^3].
2. **¿Inicio o fin del intervalo?** En barras, una marca 14:00 puede ser el inicio (14:00–14:05) o el fin
   (13:55–14:00). Confundirlas es mirar el futuro.
3. **¿Qué zona horaria?** UTC, la hora de la bolsa o la del ordenador de quien descargó los datos. Las fechas sin hora
   (datos diarios) son la fecha de la sesión, no un instante.

[^3]: Databento, «Common fields, enums and types» (enlace en los recursos).

La primera importa cuando se combinan centros: cada feed llega con su propia latencia, y ordenar por la marca de
recepción cambia el orden de los eventos. Una simulación lo muestra con latencias supuestas de unos cientos de
microsegundos, distintas por centro:

```python
rng = np.random.default_rng(7)
media_us = {"PRINCIPAL": 150, "MTF_A": 400, "MTF_B": 600,
            "OSCURO": 900, "INTERNALIZADOR": 1_200}
lat = rng.exponential(op["centro"].map(media_us).to_numpy())
op["ts_recv"] = op["ts"] + pd.to_timedelta(lat.round(), unit="us")
orden_evento = op.sort_values("ts", kind="stable").index
orden_recv = op.sort_values("ts_recv", kind="stable").index
cambia = (orden_evento != orden_recv).mean()
print(f"Operaciones en otra posición al ordenar por recepción: "
      f"{cambia:.1%}")
```
```output
Operaciones en otra posición al ordenar por recepción: 28.7%
```

Con latencias de este tamaño, el 28,7 % de las operaciones cambia de posición en la secuencia. Para un sistema
de baja frecuencia es irrelevante; para alinear operaciones con cotizaciones al microsegundo (lección 1.1.1) o para
medir quién se adelanta a quién, es la diferencia entre un estudio correcto y uno falso.

La segunda y la tercera pregunta afectan a cualquier sistema intradía:

```python
b = sesion.head(2).copy()
b["fin"] = b.index + pd.Timedelta("5min")
b.index = b.index.tz_convert("America/New_York")
b[["fin", "apertura", "cierre"]].assign(
    fin=lambda x: x["fin"].dt.tz_convert("America/New_York"))
```
```output
                                                fin  apertura  cierre
ts
2025-06-02 09:30:00-04:00 2025-06-02 09:35:00-04:00     53.35   53.46
2025-06-02 09:35:00-04:00 2025-06-02 09:40:00-04:00     53.46   53.24
```

*Fig. — La misma barra de 5 minutos con dos convenciones de marca de tiempo: la primera barra de NOVA del 2 de junio de 2025 va de 9:30 a 9:35 de Nueva York; su cierre solo se conoce a las 9:35, aunque el fichero la etiquete 13:30 UTC (esquema).* Línea de tiempo de 9:25 a 9:50 de Nueva York con tres barras de 5 minutos (9:30, 9:35 y 9:40). Dos líneas verticales marcan el inicio de la primera barra, 13:30 UTC, que es su marca si se etiqueta por el inicio, y su fin, 13:35 UTC, que es su marca si se etiqueta por el fin. Un triángulo destacado en las 9:35 señala el momento en que se conoce el cierre de la barra de las 9:30.

La barra que empieza a las 9:30 de Nueva York termina a las 9:35, y su cierre no se conoce hasta entonces. Una señal
que use ese cierre solo puede ejecutarse a partir de las 9:35. Si el proveedor etiquetara por el fin, la misma barra
llevaría la marca 9:35; mezclar dos fuentes con convenciones distintas desplaza una de ellas cinco minutos. Y al pasar
de UTC a hora local, el cambio de horario de verano mueve la hora UTC de la apertura (13:30 en verano, 14:30 en
invierno; lección 0.4.4): por eso conviene guardar en UTC y convertir solo para mostrar o para filtrar horarios.

## Datos de referencia

El último tipo no tiene precio, pero sin él los precios no se entienden. Tres ejemplos del nivel:

```python
spec = pd.read_csv("fix_especificaciones.csv")
eventos = pd.read_csv("nova_eventos.csv", parse_dates=["fecha_ex"])
miembros = pd.read_csv("universo_miembros.csv",
                       parse_dates=["alta", "baja"])
print(spec[["multiplicador", "tick", "valor_tick", "meses"]]
      .to_string(index=False))
print(eventos[eventos["tipo"] != "dividendo"].to_string(index=False))
print(f"{len(miembros)} tickers han pasado por el índice; "
      f"{miembros['baja'].isna().sum()} siguen hoy")
```
```output
 multiplicador  tick  valor_tick   meses
            50  0.25        12.5 H,M,U,Z
  fecha_ex                     tipo  ratio  importe fecha_pago
2021-08-23                    split    4.0      NaN 2021-08-20
2023-12-14 dividendo_extraordinario    NaN      1.5 2024-01-04
57 tickers han pasado por el índice; 40 siguen hoy
```

- **Especificaciones de contratos**: multiplicador, tick, meses de vencimiento y reglas de liquidación de un futuro
  (lección 1.2.3). Sin el multiplicador, un movimiento de 10 puntos no se puede pasar a dinero.
- **Eventos corporativos**: splits, dividendos, cambios de nombre, fusiones. Con ellos se ajustan las series (lección
  1.4.3). El split 4 por 1 de NOVA en 2021 está en esta tabla; sin ella, el precio diario parece caer un 75 % en un día.
- **Universos**: qué valores formaban un índice en cada fecha. 57 tickers han pasado por un índice que siempre tiene
  40 miembros; usar solo los 40 de hoy es el sesgo de supervivencia (lección 1.4.4).

Los datos de referencia ocupan poco, cambian poco y nadie los mira hasta que fallan. Y cuando fallan, el error no da
un aviso: produce un backtest plausible y falso.

## Qué dato para qué pregunta

La regla práctica es usar el peldaño más alto (el más agregado) que responda a la pregunta. Cada peldaño más fino
multiplica el coste, el volumen de datos y las formas de equivocarse.

```mermaid
flowchart TB
  P{¿Horizonte?} -- días o más --> D[Datos diarios]
  P -- minutos u horas --> B[Barras intradía]
  P -- segundos --> T[Operaciones y L1]
  T --> C{¿Colas, prioridad?}
  C -- sí --> L3[Mensajes L3]
  C -- no --> L2[Libro L2]
```

Un sistema diario como los de los niveles 2 y 3 vive con datos diarios, bien ajustados y sin sesgo de supervivencia.
Uno intradía necesita barras y, para estimar costes, operaciones y cotizaciones. Solo la investigación de
microestructura o la ejecución de alta frecuencia justifican el libro completo.

## Ejercicios

1. Construye barras de **1 minuto** con las operaciones de NOVA del 2 de junio y cuenta cuántas tiene la sesión.
   ¿Cuántas habría en barras de 30 minutos? ¿Dónde cae la subasta de cierre en cada caso?

**Solución.**

```python
for regla in ("1min", "30min"):
    b = op.set_index("ts")["precio"].resample(regla).ohlc()
    print(regla, "→", len(b), "intervalos;", "último:",
          b.index[-1].strftime("%H:%M"), "UTC")
```
```output
1min → 391 intervalos; último: 20:00 UTC
30min → 14 intervalos; último: 20:00 UTC
```

Con `resample` sobre las operaciones, el último intervalo es el que contiene la subasta de cierre de las 20:00:00 UTC:
391 intervalos de 1 minuto (390 de la sesión continua más uno de las 20:00 con solo la subasta) y 14 de 30 minutos
(13 más el de la subasta). Para tener las 390 barras de la sesión hay que decidir dónde va la subasta, como en la
reconstrucción de las barras de 5 minutos.

2. Calcula, con las operaciones, qué porcentaje del volumen de NOVA se negocia en la primera y en la última media hora
   de la sesión, y compáralo con el que le correspondería si el volumen fuera uniforme.

**Solución.**

```python
v = op.set_index("ts")["tamano"].resample("30min").sum()
cuota = 100 * v / v.sum()
print(f"Primera media hora: {cuota.iloc[0]:.1f} %")
print(f"Última media hora (con la subasta): "
      f"{cuota.iloc[-2:].sum():.1f} %")
print(f"Uniforme: {100 / 13:.1f} % por media hora")
```
```output
Primera media hora: 13.5 %
Última media hora (con la subasta): 20.1 %
Uniforme: 7.7 % por media hora
```

La primera media hora concentra el 13,5 % del volumen y la última, con la subasta de cierre, el 20,1 %, frente al
7,7 % que les tocaría con un volumen uniforme. Es el perfil en U que se ve en casi todas las acciones (lecciones 1.1.2 y 1.6.3):
por eso los algoritmos de ejecución reparten las órdenes siguiendo un perfil de volumen y no a partes iguales.

3. Un proveedor ofrece «datos tick a tick» de una acción por un precio. ¿Qué preguntas le harías antes de comprarlos?

**Solución.**

Qué contiene exactamente: solo operaciones, o también cotizaciones; si son L1, L2 o L3. Qué cinta: consolidada o de
una sola bolsa, y qué centros incluye (oscuros, internalizadores, operaciones fuera de hora). Qué marcas de tiempo:
del evento, de recepción o las dos, con qué precisión y en qué zona horaria. Qué condiciones de operación incluye
(subastas, bloques, operaciones corregidas o canceladas). Si los datos están ajustados por eventos corporativos y cómo
se entregan los eventos. Desde cuándo hay histórico y si incluye valores deslistados. Y la licencia: qué se puede hacer
con ellos (lección 1.3.7).

## Autoevaluación

1. Quieres estudiar si una estrategia de cruce de medias diarias funciona en acciones de EE. UU. desde 2005. ¿Qué tipo de dato necesitas?

   - A. Mensajes L3 de todas las bolsas
   - B. Datos diarios (EOD) con ajustes por eventos corporativos y un universo sin sesgo de supervivencia
   - C. Cotizaciones con el mejor bid y ask
   - D. Fotos L2 cada segundo

   *Respuesta B: Datos diarios (EOD) con ajustes por eventos corporativos y un universo sin sesgo de supervivencia.* Una regla que decide una vez al día con el cierre solo necesita OHLCV diario. Lo crítico no es la granularidad, sino los ajustes por splits y dividendos y que el universo incluya los valores que dejaron de cotizar (lecciones 1.4.3 y 1.4.4). Datos más finos solo añadirían coste.

2. ¿Qué diferencia hay entre datos L2 y L3?

   - A. Ninguna
   - B. L2 son operaciones y L3 cotizaciones
   - C. L3 es L2 con retraso
   - D. L2 agrega el libro por nivel de precio (tamaño total y número de órdenes); L3 da cada orden individual con su identificador y sus altas, cancelaciones y ejecuciones

   *Respuesta D: L2 agrega el libro por nivel de precio (tamaño total y número de órdenes); L3 da cada orden individual con su identificador y sus altas, cancelaciones y ejecuciones.* Con L3 (market by order) se puede reconstruir el libro y ver la cola de cada precio; con L2 (market by price) solo el total de cada nivel. Del L3 se puede obtener el L2, pero no al revés.

3. Una barra de 5 minutos tiene la marca 14:00. Tu proveedor la etiqueta por el inicio. ¿Cuándo se conoce su cierre?

   - A. A las 14:05, cuando termina la barra
   - B. A las 14:00
   - C. A las 13:55
   - D. Al cierre de la sesión

   *Respuesta A: A las 14:05, cuando termina la barra.* Una barra etiquetada por el inicio contiene lo que ocurre de 14:00 a 14:05; su cierre solo existe a las 14:05. Usarlo como si estuviera disponible a las 14:00 es mirar cinco minutos en el futuro. Cada proveedor elige su convención, y hay que comprobarla.

4. Comparas el volumen diario de NOVA de dos proveedores y uno da 3,56 millones de acciones y otro 8,89 millones. ¿Cuál es la explicación más probable?

   - A. Uno de los dos está mal
   - B. Uno incluye dividendos
   - C. Uno usa solo las operaciones del centro principal y el otro la cinta consolidada de todos los centros
   - D. Uno usa horario de verano

   *Respuesta C: Uno usa solo las operaciones del centro principal y el otro la cinta consolidada de todos los centros.* En la sesión de NOVA, el centro principal reúne 3.564.476 acciones y la cinta consolidada 8.888.714. Antes de comparar volúmenes, VWAP o máximos y mínimos, hay que saber qué cinta usa cada fuente.

5. ¿Qué son los datos de referencia?

   - A. Los precios de cierre oficiales
   - B. La información que describe los instrumentos y su vida: especificaciones de contratos, eventos corporativos, calendarios, símbolos y composición de universos
   - C. Los datos de un índice de referencia
   - D. Los datos con retraso de 15 minutos

   *Respuesta B: La información que describe los instrumentos y su vida: especificaciones de contratos, eventos corporativos, calendarios, símbolos y composición de universos.* Sin multiplicador, tick, vencimientos, splits, cambios de símbolo o fechas de alta y baja en un índice, los precios no se pueden interpretar ni ajustar. Ocupan poco, pero un error en ellos estropea todo lo demás.

## Ideas clave

- Los datos de mercado forman una escalera: mensajes orden a orden (L3) → libro por niveles (L2) → mejor precio (L1, cotizaciones) y operaciones → barras → datos diarios. Cada peldaño se obtiene del anterior, nunca al revés.
- En NOVA, una sesión es 1 fila en datos diarios, 78 en barras de 5 minutos, 50.946 operaciones, 129.247 cotizaciones y del orden de medio millón de mensajes L3.
- La granularidad se elige por la pregunta: una regla diaria no necesita ticks; un estudio de costes de ejecución necesita operaciones y cotizaciones; uno de colas y prioridad, L3.
- Cinta consolidada y un solo centro dan volúmenes, máximos, mínimos y VWAP distintos. En NOVA, el centro principal reúne solo el 40 % del volumen.
- Cada marca de tiempo tiene su convención: evento o recepción, inicio o fin de barra, UTC o hora local. Equivocarse es una fuente de sesgo de anticipación.
- Los datos de referencia (especificaciones, eventos corporativos, universos) ocupan poco y lo condicionan todo.

## Recursos

### Research

- [Liquidez Order Book - Trading Liquidez Agregada](https://pythonparatrading.com/limit-order-book-y-liquidez-de-mercado/) · Research-0008 · 11 min
- [Big Data Management. 1 - Introduccion al Big Data para trading con ArticDB](https://pythonparatrading.com/big-data-para-trading/) · Research-0019 · 16 min

### Documentación

- [Databento: esquemas de datos (MBO, MBP-10, MBP-1, TBBO, trades, OHLCV, definiciones)](https://databento.com/docs/schemas-and-data-formats) · databento.com
- [Databento: campos comunes y marcas de tiempo (ts_event, ts_recv)](https://databento.com/docs/standards-and-conventions/common-fields-enums-types) · databento.com
- [Nasdaq TotalView-ITCH 5.0: especificación del protocolo orden a orden](https://www.nasdaqtrader.com/content/technicalsupport/specifications/dataproducts/NQTVITCHspecification.pdf) · nasdaqtrader.com
- [Consolidated Tape Association (CTA): cinta consolidada de EE. UU.](https://www.ctaplan.com/index) · ctaplan.com

### Datos

- [Datos del nivel 1: barras de 5 minutos de NOVA, 2024-2025 (Parquet)](https://pythonparatrading.com/curso/datos/nivel-1/nova_barras_5min.parquet) · Descarga · nova_barras_5min.parquet

Lección anterior: [1.2.7 · Renta fija y tipos de interés; CFDs y productos apalancados](https://pythonparatrading.com/curso-python-trading/nivel-1/renta-fija-tipos-y-productos-apalancados/)

Lección siguiente: [1.3.2 · Fuentes de datos gratuitas](https://pythonparatrading.com/curso-python-trading/nivel-1/fuentes-de-datos-gratuitas/)
