Lección 1.4.5 · 65 min

Construcción de barras: tiempo, ticks, volumen, dólar y desequilibrio

Una barra resume un trozo de la cinta de operaciones, y la regla que decide dónde termina cada trozo cambia lo que se ve en los datos. Esta lección construye cinco tipos de barra desde las 50.946 operaciones de una sesión de NOVA y mide sus diferencias: cuándo cierran, cuántas hay y cómo se distribuyen sus rentabilidades.

Fig. — Barras que cierran en cada media hora de la sesión de NOVA del 2 de junio de 2025: de tiempo (1 minuto), de ticks (130 operaciones) y de volumen (26 600 acciones)
Lecciones del nivel 1
  1. 1.1 · Cómo funcionan los mercados

    1. 1.1.1 Participantes y centros de negociación
    2. 1.1.2 Sesiones, subastas, festivos y calendarios
    3. 1.1.3 Libro de órdenes: bid, ask, spread y profundidad
    4. 1.1.4 Tipos de órdenes
    5. 1.1.5 Costes, liquidación, apalancamiento y margen
  2. 1.2 · Clases de activos

    1. 1.2.1 Acciones: splits, dividendos y eventos corporativos
    2. 1.2.2 ETFs: réplica, tracking error, apalancados y UCITS
    3. 1.2.3 Futuros: especificaciones, margen, roll y contango
    4. 1.2.4 Forex: pares, pips, lotes, rollover y CFD
    5. 1.2.5 Opciones: call, put, valor intrínseco y valor temporal
    6. 1.2.6 Cripto: spot, perpetuos, funding y custodia
    7. 1.2.7 Renta fija y tipos de interés; CFDs y productos apalancados
  3. 1.3 · Fuentes de datos de mercado

    1. 1.3.1 Tipos de datos de mercado: EOD, barras, ticks, libro y referencia
    2. 1.3.2 Fuentes de datos gratuitas
    3. 1.3.3 APIs de datos retail y prosumer
    4. 1.3.4 Proveedores de datos profesionales
    5. 1.3.5 Datos de cripto y on-chain
    6. 1.3.6 Brókeres como fuente de datos
    7. 1.3.7 Licencias y términos de uso de datos
    8. 1.3.8 Cómo elegir proveedor de datos
  4. 1.4 · Ingeniería de datos financieros

    1. 1.4.1 Descarga por REST y websockets: paginación, límites y reintentos
    2. 1.4.2 Limpieza de datos: faltantes, ticks erróneos, huecos y zonas horarias
    3. 1.4.3 Ajustes por splits y dividendos: precios ajustados y sin ajustar
    4. 1.4.4 Universos sin sesgo de supervivencia: deslistados, fusiones y cambios de símbolo
    5. 1.4.5 Construcción de barras: tiempo, ticks, volumen, dólar y desequilibrio
    6. 1.4.6 Series continuas de futuros: reglas de roll, ajuste por diferencia y ajuste por ratio
    7. 1.4.7 Almacenamiento: CSV, Parquet, particionado, DuckDB, TimescaleDB y ArcticDB
    8. 1.4.8 Pipeline reproducible: descarga, validación, actualización incremental y tests de calidad
  5. 1.5 · Datos fundamentales y macro

    1. 1.5.1 Estados financieros, ratios, XBRL y SEC EDGAR
    2. 1.5.2 Datos macro, calendarios económicos, estimaciones y transcripciones
  6. 1.6 · Análisis exploratorio financiero

    1. 1.6.1 Rentabilidades, volatilidad, correlaciones móviles y drawdown
    2. 1.6.2 Hechos estilizados: colas gruesas, agrupamiento, autocorrelación y apalancamiento
    3. 1.6.3 Estacionalidad: día de la semana, mes, cambio de mes y sesiones intradía
  7. 1.7 · Análisis técnico programado

    1. 1.7.1 Indicadores técnicos en Python
    2. 1.7.2 Implementación propia frente a TA-Lib: validar resultados
    3. 1.7.3 Patrones de velas y soportes y resistencias
    4. 1.7.4 Multi-timeframe sin anticipación

Una barra es una forma de muestrear

Los datos de mercado nacen como una cinta de operaciones: precio, tamaño y hora de cada cruce, decenas de miles por sesión en una acción líquida (lección 1.3.1). Casi ningún sistema trabaja con la cinta tal cual. La resume en barras: trozos consecutivos de la cinta, cada uno con su apertura, máximo, mínimo, cierre y volumen (OHLCV).

La pregunta que decide todo lo demás es dónde termina cada trozo. Hay dos familias de respuestas:

  • Barras de tiempo. Cada barra cubre un intervalo fijo de reloj: 1 minuto, 5 minutos, un día. Es el formato que dan casi todos los proveedores y el que muestra cualquier gráfico.
  • Barras de actividad. Cada barra termina cuando el mercado ha hecho «una cantidad fija de algo»: un número de operaciones (ticks), de acciones (volumen), de dinero (dólar) o un desequilibrio entre compras y ventas. Las describe con detalle Marcos López de Prado en el capítulo 2 de Advances in Financial Machine Learning, que llama a las últimas barras «guiadas por la información».

La idea de fondo es sencilla. El mercado no procesa información al ritmo del reloj: en la primera media hora de NOVA se negocia más del doble que en una media hora de mediodía. Una barra de 1 minuto a las 9:31 resume mucha más actividad que una de las 12:31, y las dos cuentan lo mismo en una media, una volatilidad o un modelo. Las barras de actividad intentan que cada barra lleve una cantidad parecida de información.

Los datos son los de la lección 1.1.1: la sesión completa de NOVA del 2 de junio de 2025, operación a operación.

IN [01]python
import numpy as np
import pandas as pd

op = pd.read_parquet("nova_operaciones.parquet")
dolares = op["precio"] * op["tamano"]
print(f"{len(op)} operaciones, {op['tamano'].sum():,} acciones, "
      f"{dolares.sum() / 1e6:.1f} M USD")
print(op["ts"].min(), "→", op["ts"].max())
OUT [01]La sesión de NOVA, operación a operación
50946 operaciones, 8,888,714 acciones, 473.9 M USD
2025-06-02 13:30:00+00:00 → 2025-06-02 20:00:00+00:00

Barras de tiempo

Con resample de pandas (lección 0.5.3), unas barras de 5 minutos son cinco agregaciones: la primera operación de cada intervalo es la apertura, la última el cierre, el máximo y el mínimo de precios, y la suma de tamaños el volumen.

IN [02]python
def ohlcv(grupos):
    return grupos.agg(
        apertura=("precio", "first"), maximo=("precio", "max"),
        minimo=("precio", "min"), cierre=("precio", "last"),
        volumen=("tamano", "sum"),
    )

ingenuas = ohlcv(op.set_index("ts").resample("5min"))
print(len(ingenuas), "barras")
ingenuas.tail(2)
OUT [02]Barras de 5 minutos con resample: primer intento
79 barras
                           apertura  maximo  minimo  cierre  volumen
ts
2025-06-02 19:55:00+00:00     52.92   53.12   52.91   53.08   177180
2025-06-02 20:00:00+00:00     53.12   53.12   53.12   53.12   885500

Una sesión de 6 horas y media tiene 78 intervalos de 5 minutos, y han salido 79. La última barra, la de las 20:00 UTC (16:00 en Nueva York), tiene una sola operación: el cruce de la subasta de cierre, 885.500 acciones a 53,12. Su marca de tiempo es exactamente las 16:00:00, el instante en que termina la sesión continua, y resample la pone en el intervalo que empieza a esa hora.

No es un error de pandas: es una convención que hay que decidir. El README de nova_barras_5min.parquet dice cuál usa ese archivo: la subasta de cierre cuenta en la última barra, la de las 15:55. Para reproducirlo hay que mover el cruce a esa barra antes de agrupar:

IN [03]python
cierre = pd.Timestamp("2025-06-02 20:00", tz="UTC")
inicio = op["ts"].dt.floor("5min").mask(
    op["ts"] >= cierre, cierre - pd.Timedelta("5min"))
b5 = ohlcv(op.groupby(inicio.rename("ts")))

ref = pd.read_parquet("nova_barras_5min.parquet").set_index("ts")
ref = ref.loc["2025-06-02"]
pd.testing.assert_frame_equal(b5, ref)
print(len(b5), "barras, idénticas a nova_barras_5min")
OUT [03]La subasta de cierre, dentro de la última barra
78 barras, idénticas a nova_barras_5min

assert_frame_equal no imprime nada si las dos tablas coinciden en índice, columnas, tipos y valores, y lanza un error con la primera diferencia si no. Es la forma de comprobar que un proceso propio reproduce los datos de un proveedor antes de sustituirlo (la lección 1.4.8 convierte comprobaciones así en tests).

Las convenciones de una barra de tiempo que conviene saber de cada fuente:

ConvenciónOpciones habitualesEn nova_barras_5min
Marca de la barraInicio del intervalo o finInicio (ts = 13:30 para 13:30–13:35)
IntervaloCerrado por la izquierda o por la derecha[13:30, 13:35)
SubastasBarra propia, dentro de la primera o última, fueraApertura en la primera, cierre en la última
Intervalos sin operacionesSin fila, fila con NaN, fila con el cierre anteriorNo ocurre en NOVA
Operaciones especialesTodas, sin bloques, sin fuera de horaTodas (bloques incluidos)

Una barra marcada con su inicio se conoce a su fin. El error clásico es unir una barra de las 13:30 con una señal de las 13:31: la barra aún no había terminado (lección 1.7.4).

IN [04]python
inicio_1m = op["ts"].dt.floor("min").mask(
    op["ts"] >= cierre, cierre - pd.Timedelta("1min"))
b1 = ohlcv(op.groupby(inicio_1m.rename("ts")))
ops_por_minuto = op.groupby(inicio_1m).size()
print(len(b1), "barras de 1 minuto")
ops_por_minuto.describe().round(0)
OUT [04]Barras de 1 minuto: cuántas operaciones caben en cada una
390 barras de 1 minuto
count    390.0
mean     131.0
std       46.0
min       77.0
25%      102.0
50%      117.0
75%      136.0
max      339.0
dtype: float64

Las 390 barras de 1 minuto son iguales para el reloj y muy distintas para el mercado: la más tranquila resume 77 operaciones y la más activa 339, cuatro veces más. En NOVA ningún minuto se queda vacío; en una acción poco líquida o en un futuro de madrugada sí ocurre, y entonces hay que decidir si la barra no existe, existe con NaN o repite el cierre anterior. Las tres opciones cambian la volatilidad que se mide.

Barras de actividad: ticks, volumen y dólar

Todas las barras de actividad se construyen igual. Se recorre la cinta sumando una medida de cada operación; cuando la suma alcanza un umbral, la barra se cierra y la suma vuelve a cero:

nosíOperación nuevaSumar su medida¿Suma ≥ umbral?Cerrar barraSuma a cero
Fuente Mermaid
flowchart TB
  O[Operación nueva] --> A[Sumar su medida]
  A --> U{¿Suma ≥ umbral?}
  U -- no --> O
  U -- sí --> C[Cerrar barra]
  C --> R[Suma a cero]
  R --> O

La medida decide el tipo de barra: 1 por operación da barras de ticks; el tamaño, barras de volumen; precio × tamaño, barras de dólar. El mismo constructor sirve para todas. Usa el valor absoluto de la suma para poder reutilizarlo más abajo con medidas con signo:

IN [05]python
def ids_por_umbral(medida, umbral):
    """Número de barra de cada operación. La que alcanza el
    umbral cierra su barra; la siguiente abre otra."""
    ids = np.empty(len(medida), dtype=np.int64)
    suma, barra = 0.0, 0
    for i, x in enumerate(medida):
        ids[i] = barra
        suma += x
        if abs(suma) >= umbral:
            barra, suma = barra + 1, 0.0
    return ids

def barras(op, ids):
    g = op.groupby(ids)
    out = ohlcv(g)
    out["n_ops"] = g.size()
    out["ts_fin"] = g["ts"].last()   # cuándo se conoce la barra
    return out

ticks = barras(op, ids_por_umbral(np.ones(len(op)), 130))
print(len(ticks), "barras de 130 operaciones")
ticks[["ts_fin", "apertura", "cierre", "volumen", "n_ops"]].head(3)
OUT [05]Un constructor de barras por umbral
392 barras de 130 operaciones
                            ts_fin  apertura  cierre  volumen  n_ops
0 2025-06-02 13:30:17.464135+00:00     53.35   53.34    59313    130
1 2025-06-02 13:30:43.762733+00:00     53.34   53.33    15083    130
2 2025-06-02 13:31:09.294637+00:00     53.33   53.32    17261    130

Es un bucle de Python sobre 50.946 filas: se ejecuta en una fracción de segundo y se lee de un vistazo. Con millones de operaciones conviene vectorizarlo o compilarlo (Polars, Numba), pero la lógica es la misma. La columna ts_fin es la marca de la última operación de cada barra: el momento en que la barra existe. Una barra de actividad no tiene hora de cierre prevista; hay que guardar cuándo se cerró.

El umbral de 130 operaciones sale de dividir las operaciones del día entre 390, para tener tantas barras como minutos. Eso es hacer trampa: el número de operaciones del día solo se conoce al cierre. Para el volumen y el dólar se puede hacer bien, con las 20 sesiones anteriores de nova_barras_5min; para los ticks no, porque ese archivo no guarda cuántas operaciones tuvo cada sesión.

IN [06]python
b = pd.read_parquet("nova_barras_5min.parquet")
b["sesion"] = b["ts"].dt.tz_convert("America/New_York").dt.date
b["dolares"] = b["cierre"] * b["volumen"]
por_sesion = b.groupby("sesion")[["volumen", "dolares"]].sum()

hoy = pd.Timestamp("2025-06-02").date()
previas = por_sesion[por_sesion.index < hoy].tail(20)
umbral_vol = round(previas["volumen"].mean() / 390, -2)
umbral_dol = round(previas["dolares"].mean() / 390, -4)
print(f"umbral de volumen: {umbral_vol:,.0f} acciones")
print(f"umbral de dólar: {umbral_dol:,.0f} USD")
vol_hoy = por_sesion.loc[hoy, "volumen"]
print(f"volumen de hoy / 390: {vol_hoy / 390:,.0f}")
OUT [06]Umbrales con las sesiones anteriores, no con la del día
umbral de volumen: 26,600 acciones
umbral de dólar: 1,360,000 USD
volumen de hoy / 390: 22,792

El dólar de cada barra de 5 minutos se aproxima con su cierre × su volumen; para un umbral basta. Las 20 sesiones anteriores negociaron más que la del 2 de junio, así que el umbral (26.600 acciones) es mayor que el que habría salido con el propio día (22.792). Consecuencia: ese día salen menos de 390 barras. Es justo lo que se busca. Un día tranquilo produce menos barras y uno agitado más; con el umbral del propio día, todos los días tendrían 390 y el número de barras llevaría escondida la actividad que todavía no había ocurrido.

IN [07]python
vol = barras(op, ids_por_umbral(op["tamano"].to_numpy(float),
                                umbral_vol))
dol = barras(op, ids_por_umbral(dolares.to_numpy(), umbral_dol))
print(len(vol), "barras de volumen;", len(dol), "barras de dólar")
vol.nlargest(3, "volumen")[["ts_fin", "volumen", "n_ops"]]
OUT [07]Barras de volumen y de dólar
290 barras de volumen; 303 barras de dólar
                              ts_fin  volumen  n_ops
289        2025-06-02 20:00:00+00:00   897226     85
191 2025-06-02 17:54:43.496329+00:00    85637    157
238 2025-06-02 19:05:40.906104+00:00    75375     37

Salen 290 barras de volumen y 303 de dólar. Las tres barras más grandes cuentan por qué una barra de actividad no siempre mide exactamente el umbral: la operación que lo alcanza entra entera. La última barra del día tiene 897.226 acciones porque contiene el cruce de cierre de 885.500, que por sí solo vale 33 umbrales. Las otras dos llevan dentro dos de los bloques del centro oscuro de la lección 1.1.1 (63.000 acciones a las 17:54:43 UTC y 68.200 a las 19:05:40). Hay dos formas de tratarlo: aceptar barras más grandes, o construir las barras solo con la negociación continua y tratar subastas y bloques aparte (ejercicio 1).

Entre volumen y dólar, en una sola sesión, casi no hay diferencia: el precio de NOVA se mueve entre 52,6 y 53,9 USD, así que un umbral en acciones y uno en dólares son casi proporcionales. La diferencia aparece en años, cuando el precio cambia mucho o la acción hace un split:

IN [08]python
d = pd.read_csv("nova_diario_sin_ajustar.csv", parse_dates=["fecha"])
d["dolares"] = d["cierre"] * d["volumen"]
anual = d.groupby(d["fecha"].dt.year)[
    ["cierre", "volumen", "dolares"]].median()
# umbrales fijos, calibrados para 50 barras al día en 2018
u_vol = anual.loc[2018, "volumen"] / 50
u_dol = anual.loc[2018, "dolares"] / 50
anual["barras_vol"] = anual["volumen"] / u_vol
anual["barras_dol"] = anual["dolares"] / u_dol
anual[["cierre", "barras_vol", "barras_dol"]].round(1)
OUT [08]Por qué dólares: el split de 2021
       cierre  barras_vol  barras_dol
fecha
2018    214.6        50.0        50.0
2019    185.0        51.9        45.7
2020    191.7        48.6        43.8
2021    189.6        73.0        56.1
2022     57.0       205.3        53.3
2023     46.6       201.5        43.2
2024     59.5       199.4        52.5
2025     58.2       197.9        54.8
Fig. — Barras de NOVA por sesión con un umbral fijo, calibrado para 50 al día en 2018: de volumen en acciones frente a volumen en dólares, 2018–2025

El 23 de agosto de 2021 NOVA hizo un split 4 por 1 (lección 1.2.1): cada acción pasó a valer la cuarta parte y se negocian unas cuatro veces más acciones por el mismo dinero. Con un umbral fijo en acciones, las barras de volumen pasan de unas 50 a unas 200 al día (2021 sale en 73 porque mezcla meses antes y después del split). Las de dólar se mantienen entre 43 y 56: la actividad económica no ha cambiado. Con datos sin ajustar (lección 1.4.3), el umbral en dólares es el que tiene sentido a lo largo de los años.

Barras de desequilibrio

Las barras de ticks, volumen y dólar miden cuánto se negocia. Las de desequilibrio miden otra cosa: en qué dirección. Cada operación lleva un signo, bt=+1b_t = +1 si la inició un comprador y bt=−1b_t = -1 si la inició un vendedor. Si el flujo está equilibrado, la suma de signos ronda el cero; si alguien compra con insistencia, crece. Una barra de desequilibrio de ticks se cierra cuando esa suma se aleja lo bastante del cero.

NOVA trae el lado agresor en cada operación. La mayoría de las fuentes no lo dan, y entonces se estima con la regla del tick: una operación a un precio mayor que la anterior es una compra; a uno menor, una venta; al mismo precio, hereda el signo anterior.

IN [09]python
signo = op["agresor"].map({"compra": 1, "venta": -1}).fillna(0)

# regla del tick: +1 si el precio sube, −1 si baja; si no cambia,
# el signo anterior. Es lo que queda cuando no hay lado agresor.
cambio = np.sign(op["precio"].diff()).replace(0, np.nan)
regla_tick = cambio.ffill().fillna(0)

con_lado = signo != 0
acierto = (regla_tick == signo)[con_lado]
print(f"La regla del tick acierta el lado en el {acierto.mean():.1%}")
print(f"Autocorrelación del signo: {signo.autocorr(1):.2f}")
acierto.groupby(op["centro"]).mean().round(3)
OUT [09]El signo de cada operación
La regla del tick acierta el lado en el 94.0%
Autocorrelación del signo: 0.55
centro
INTERNALIZADOR    0.783
MTF_A             1.000
MTF_B             1.000
OSCURO            0.498
PRINCIPAL         0.999
dtype: float64

Las dos subastas, sin agresor, quedan con signo 0. La regla del tick acierta el lado en el 94,0 % de las operaciones de NOVA, pero el promedio esconde dos mundos. En los centros con libro acierta casi siempre (99,9 % o más): una compra agresiva paga el ask, por encima de la operación anterior si esta fue una venta al bid. En el centro oscuro acierta la mitad de las veces (49,8 %), lo mismo que una moneda: cruza al punto medio, y el punto medio no dice nada del lado. El internalizador queda en medio (78,3 %), porque ejecuta dentro de la horquilla. La autocorrelación de 0,55 dice que los signos vienen en rachas: una compra suele ir seguida de otra compra, porque las órdenes grandes se ejecutan partidas en muchas operaciones pequeñas del mismo lado (lección 1.1.4). Esas rachas son lo que las barras de desequilibrio intentan detectar.

López de Prado define la barra de desequilibrio de ticks así: se acumula θT=∑t=1Tbt\theta_T = \sum_{t=1}^{T} b_t y la barra se cierra en la primera operación TT en la que

∣θT∣≥E0[T]⋅∣2 P[bt=1]−1∣|\theta_T| \ge \mathrm{E}_0[T] \cdot \left| 2\,\mathrm{P}[b_t = 1] - 1 \right|

donde E0[T]\mathrm{E}_0[T] es la longitud esperada de una barra y 2 P[bt=1]−12\,\mathrm{P}[b_t = 1] - 1 el desequilibrio esperado por operación, ambos estimados con medias móviles exponenciales de las barras anteriores. La versión de esta lección es una simplificación: un umbral fijo para ∣θT∣|\theta_T|, que es exactamente lo que ya hace ids_por_umbral con los signos como medida.

IN [10]python
ids_deseq = ids_por_umbral(signo.to_numpy(), 64)
deseq = barras(op, ids_deseq)
deseq["desequilibrio"] = signo.groupby(ids_deseq).sum()
print(len(deseq), "barras de desequilibrio")
deseq[["ts_fin", "cierre", "n_ops", "desequilibrio"]].head(4)
OUT [10]Barras de desequilibrio de ticks, con umbral fijo
400 barras de desequilibrio
                            ts_fin  cierre  n_ops  desequilibrio
0 2025-06-02 13:30:43.375112+00:00   53.33    259          -64.0
1 2025-06-02 13:30:59.502791+00:00   53.32     76          -64.0
2 2025-06-02 13:31:20.125031+00:00   53.30     98          -64.0
3 2025-06-02 13:31:34.342418+00:00   53.29    100          -64.0

Con un umbral de 64 salen 400 barras. Cada una termina cuando las compras superan a las ventas (o al revés) en 64 operaciones. Las cuatro primeras son de ventas (−64): la sesión abrió con presión vendedora y, entre el cierre de la primera barra y el de la cuarta, el precio bajó de 53,33 a 53,29 en 51 segundos. La primera barra necesitó 259 operaciones para desequilibrarse; la segunda, solo 76: cuando el flujo es de un solo lado, las barras se acortan.

¿Por qué no usar la versión adaptativa del libro? Porque con estos datos se va a la deriva:

IN [11]python
def umbrales_adaptativos(b, t0=100, alfa=0.05):
    """Umbral E[T]·|E[θ/T]| con medias exponenciales,
    actualizado al cerrar cada barra."""
    e_t, e_b = float(t0), abs(b[:t0].mean())
    theta, n, umbrales = 0.0, 0, []
    for x in b:
        theta, n = theta + x, n + 1
        if abs(theta) >= e_t * e_b:
            e_t = (1 - alfa) * e_t + alfa * n
            e_b = (1 - alfa) * e_b + alfa * abs(theta / n)
            umbrales.append(e_t * e_b)
            theta, n = 0.0, 0
    return np.array(umbrales)

u = umbrales_adaptativos(signo.to_numpy())
print(len(u), "barras")
print("umbral tras las barras 1, 50, 100 y última:",
      u[[0, 49, 99, -1]].round(0))
OUT [11]La versión adaptativa: el umbral se va a la deriva
183 barras
umbral tras las barras 1, 50, 100 y última: [ 17.  47.  79. 359.]

El umbral empieza en 17 y termina en 359: veinte veces mayor. Es un problema conocido de esta definición. Una barra larga sube E[T]\mathrm{E}[T], un umbral más alto produce barras aún más largas, y la media exponencial se realimenta. El resultado depende mucho de t0 y alfa, y a media sesión ya no se parece a lo que se quería medir. Con un umbral fijo (calibrado con sesiones anteriores, como el de volumen) el comportamiento es predecible y fácil de comprobar. Si se usa la versión adaptativa, conviene acotar el umbral entre un mínimo y un máximo y vigilarlo.

¿Qué medir?Cuánto se negociaHacia dóndeTicksVolumenDólarDesequilibrioAños con splits
Fuente Mermaid
flowchart TB
  P{¿Qué medir?} --> C[Cuánto se negocia]
  P --> D[Hacia dónde]
  C --> T[Ticks]
  C --> V[Volumen]
  C --> S[Dólar]
  D --> I[Desequilibrio]
  S --> L[Años con splits]

Cuántas barras hay en cada media hora

El efecto de cada regla se ve contando cuántas barras cierran en cada media hora de la sesión. Las barras de tiempo se cuentan por su inicio; las de actividad, por su ts_fin. El cruce de las 16:00 se cuenta en la última media hora.

IN [12]python
def por_media_hora(ts_fin):
    ny = ts_fin.dt.tz_convert("America/New_York")
    # el cruce de las 16:00 cuenta en la última media hora
    ny = ny.mask(ny.dt.hour == 16, ny - pd.Timedelta("1min"))
    return ny.dt.strftime("%H:%M").map(
        lambda h: h[:3] + ("00" if h[3] < "3" else "30")
    ).value_counts().sort_index()

tabla = pd.DataFrame({
    "tiempo_1m": por_media_hora(b1.index.to_series()),
    "ticks": por_media_hora(ticks["ts_fin"]),
    "volumen": por_media_hora(vol["ts_fin"]),
    "dolar": por_media_hora(dol["ts_fin"]),
    "deseq": por_media_hora(deseq["ts_fin"]),
})
tabla
OUT [12]Barras por media hora de Nueva York
       tiempo_1m  ticks  volumen  dolar  deseq
09:30         30     55       42     44     55
10:00         30     34       24     25     38
10:30         30     27       21     21     23
11:00         30     26       18     19     24
11:30         30     24       17     18     20
12:00         30     24       19     20     29
12:30         30     25       17     18     20
13:00         30     25       19     20     27
13:30         30     24       18     19     30
14:00         30     25       18     19     24
14:30         30     28       20     21     30
15:00         30     31       23     24     33
15:30         30     44       34     35     47

La figura de la cabecera dibuja tres columnas de esta tabla. Las barras de 1 minuto son una recta: 30 en cada media hora, por definición. Las de actividad dibujan la U del volumen intradía: 55 barras de ticks y 42 de volumen en la primera media hora, 24 y 17 a mediodía, 44 y 34 en la última. Las de dólar siguen a las de volumen a menos de tres barras de distancia. Las de desequilibrio también tienen forma de U, pero más irregular (20 a las 11:30, 29 a las 12:00), porque no miden cuánto se negocia sino cuándo el flujo se inclina a un lado.

Dicho de otra forma: con barras de actividad, el sistema «mira» el mercado más a menudo cuando el mercado hace más cosas. La siguiente figura lo enseña sobre el precio, con barras más grandes para que se distingan una a una:

Fig. — Precio de NOVA cada 3 minutos el 2 de junio de 2025 y dónde cierra cada barra: 26 de tiempo (15 minutos) frente a 26 de volumen (319 200 acciones)

Las 26 barras de 15 minutos cierran a intervalos iguales. Las 26 barras de volumen se amontonan en la apertura y el cierre y se espacian a mediodía: las dos primeras se llenan en menos de 8 minutos y, entre las 12:00 y las 14:00, una barra de 319.200 acciones tarda entre 17 y 21 minutos.

Rentabilidades por tipo de barra

El argumento clásico a favor de las barras de actividad es estadístico: si cada barra lleva una cantidad parecida de información, sus rentabilidades deberían parecerse más entre sí, con menos barras «tranquilas» y menos «explosivas», y por tanto colas menos gruesas que las de las barras de tiempo. Se puede medir con la asimetría, la curtosis en exceso (0 en una normal) y el estadístico de Jarque-Bera, que combina las dos (lección 0.7.1):

JB=n6(S2+K24)JB = \frac{n}{6}\left(S^2 + \frac{K^2}{4}\right)

con nn el número de rentabilidades, SS la asimetría y KK la curtosis en exceso. Si los datos fueran normales, JBJB seguiría una χ2\chi^2 con 2 grados de libertad: por encima de 5,99 se rechaza la normalidad al 5 %.

IN [13]python
from scipy import stats

def forma(cierres):
    r = np.log(cierres).diff().dropna()
    return {
        "barras": len(r),
        "desv_pb": 1e4 * r.std(),
        "asimetria": stats.skew(r),
        "curtosis_exc": stats.kurtosis(r),
        "jarque_bera": stats.jarque_bera(r).statistic,
    }

tipos = {"tiempo_1m": b1, "ticks": ticks, "volumen": vol,
         "dolar": dol, "deseq": deseq}
resumen = pd.DataFrame({k: forma(v["cierre"])
                        for k, v in tipos.items()}).T
resumen.round(2)
OUT [13]Forma de la distribución de rentabilidades
           barras  desv_pb  asimetria  curtosis_exc  jarque_bera
tiempo_1m   389.0     6.90       0.10          3.73       225.72
ticks       391.0     6.69       0.30          2.20        84.98
volumen     289.0     8.59       0.33          1.56        34.58
dolar       302.0     8.45       0.34          2.15        64.07
deseq       399.0     5.48       0.46          1.90        74.01
Fig. — Curtosis en exceso de las rentabilidades logarítmicas de NOVA por tipo de barra, sesión del 2 de junio de 2025 (una normal tendría 0)

Lo que dicen los números, y lo que no:

  • Las barras de actividad tienen colas menos gruesas. La curtosis en exceso baja de 3,73 con barras de 1 minuto a entre 1,56 (volumen) y 2,20 (ticks). El estadístico de Jarque-Bera baja de 225,7 a entre 34,6 y 85,0.
  • Ninguna es normal. Todos los JBJB están muy por encima de 5,99. Las barras de actividad se acercan a la normal; no llegan.
  • La asimetría sube algo en las de actividad (de 0,10 a entre 0,30 y 0,46). No todo mejora a la vez.
  • Una sesión no prueba nada. Son unas 300 o 400 rentabilidades de un único día de una acción sintética. Antes de elegir un tipo de barra por su forma estadística, la comparación hay que repetirla en muchas sesiones y en los activos que se van a operar.
  • Las desviaciones no son comparables entre sí: dependen del número de barras. Una barra de volumen de NOVA dura de media más que un minuto, así que su desviación (8,59 puntos básicos) es mayor.

Lo que esto cambia en tus datos

  • Las barras de un proveedor son una decisión del proveedor. Marca de inicio o de fin, subastas dentro o fuera, minutos vacíos con o sin fila: antes de mezclar barras de dos fuentes hay que saber qué hace cada una, y comprobarlo reconstruyendo unas cuantas desde las operaciones cuando se tengan.
  • Una barra existe cuando se cierra. Las de tiempo, al final del intervalo; las de actividad, en la operación que alcanza el umbral. Esa es la marca de tiempo que hay que usar para unirlas con señales u otros activos.
  • Los umbrales se calibran con el pasado. Con sesiones anteriores, y en dólares si el periodo incluye splits o cambios grandes de precio.
  • Subastas y bloques deforman las barras de actividad. Un solo cruce de cierre de NOVA vale 33 barras de volumen. Decide si entran y documenta la decisión.

Ejercicios

  1. Construye barras de volumen solo con las operaciones de condición regular (sin subastas ni bloques), con el mismo umbral. ¿Cuántas salen y cuál es el volumen de la barra más grande?
Solución
IN [14]python
reg = op[op["condicion"] == "regular"].reset_index(drop=True)
vol_reg = barras(reg, ids_por_umbral(
    reg["tamano"].to_numpy(float), umbral_vol))
print(len(vol_reg), "barras")
print("barra más grande:", vol_reg["volumen"].max(), "acciones")
OUT [14]Barras de volumen sin subastas ni bloques
285 barras
barra más grande: 29450 acciones

Salen 285 barras, cinco menos que con todas las operaciones, y la más grande tiene 29.450 acciones, poco más que el umbral de 26.600: sin el cruce de cierre ni los bloques, ninguna operación es lo bastante grande para deformar una barra. A cambio, las subastas y los bloques (un 14,0 % del volumen del día) hay que tratarlos aparte, por ejemplo como eventos con su propia columna.

  1. Agrega las barras de 5 minutos del 2 de junio de 2025 de nova_barras_5min.parquet en barras de 15 minutos y comprueba que coinciden con las que salen de construirlas directamente desde las operaciones.
Solución
IN [15]python
b15_desde_5 = ref.resample("15min").agg({
    "apertura": "first", "maximo": "max", "minimo": "min",
    "cierre": "last", "volumen": "sum"})

inicio_15 = op["ts"].dt.floor("15min").mask(
    op["ts"] >= cierre, cierre - pd.Timedelta("15min"))
b15_desde_op = ohlcv(op.groupby(inicio_15.rename("ts")))
pd.testing.assert_frame_equal(b15_desde_5, b15_desde_op,
                              check_freq=False)
print(len(b15_desde_op), "barras de 15 minutos, iguales")
OUT [15]De 5 a 15 minutos
26 barras de 15 minutos, iguales

Las barras de tiempo se pueden agregar en barras de tiempo más largas sin volver a la cinta: primera apertura, máximo de máximos, mínimo de mínimos, último cierre y suma de volúmenes. check_freq=False solo evita comparar el atributo freq del índice, que resample rellena y groupby no. Con barras de actividad esto no funciona: una barra de volumen del doble de umbral no es la unión de dos barras consecutivas, porque la operación que cruza el umbral se reparte de otra forma.

  1. Un sistema de reversión a la media en NOVA usa barras de 1 minuto y una regla «vender si la rentabilidad de la barra supera dos desviaciones típicas». Con lo visto en esta lección, ¿qué problema tiene en la primera media hora y qué alternativas le propondrías?
Solución

En la primera media hora cada barra de 1 minuto resume mucha más actividad que a mediodía (hasta 339 operaciones frente a un mínimo de 77), así que sus rentabilidades son más grandes: con una desviación típica calculada sobre todo el día, la regla disparará sobre todo en la apertura y el cierre, no porque el precio esté «fuera de lugar» sino porque el mercado se mueve más. Alternativas: una desviación típica que dependa de la hora del día (calculada con sesiones anteriores, nunca con la del día), o barras de volumen o de dólar, que cierran más a menudo cuando hay más actividad y reparten mejor los movimientos entre barras. En cualquier caso, el umbral de la barra y el de la regla se calibran con datos anteriores, y la comparación se repite en muchas sesiones antes de confiar en ella.

Respuesta plegada en cada pregunta

Autoevaluación

  1. Con resample("5min") sobre las operaciones de NOVA salen 79 barras en lugar de 78. ¿Por qué?

    • APorque falta una operación en los datos
    • BPorque el cruce de la subasta de cierre lleva la marca 20:00:00 UTC y abre una barra propia, la de las 16:00, que el proveedor incluye en la de las 15:55
    • CPorque resample cuenta mal en días con cambio de hora
    • DPorque las barras de 5 minutos siempre son 79 en la NYSE
    Ver respuesta

    Respuesta B. Porque el cruce de la subasta de cierre lleva la marca 20:00:00 UTC y abre una barra propia, la de las 16:00, que el proveedor incluye en la de las 15:55

    La sesión continua termina a las 16:00 de Nueva York y el cruce de cierre ocurre justo en ese instante. Si no se asigna a la última barra, queda una barra de una sola operación con 885.500 acciones. Es una convención, y cada proveedor dice cuál usa.

  2. Un sistema usa barras de volumen con un umbral fijo de 50.000 acciones desde 2018. En 2021 la acción hace un split 4 por 1. ¿Qué pasa con sus barras?

    • ANada: el volumen no cambia con un split
    • BCada sesión produce unas cuatro veces más barras, porque se negocian cuatro veces más acciones por el mismo dinero
    • CSalen cuatro veces menos barras
    • DLas barras pasan a ser de dólar automáticamente
    Ver respuesta

    Respuesta B. Cada sesión produce unas cuatro veces más barras, porque se negocian cuatro veces más acciones por el mismo dinero

    Tras un split 4 por 1 cada acción vale la cuarta parte y se negocian unas cuatro veces más. En NOVA, con un umbral fijo, las barras de volumen pasan de unas 50 a unas 200 al día; las de dólar se quedan entre 43 y 56. Por eso se prefieren las de dólar, o umbrales recalculados con datos ajustados.

  3. ¿Por qué los umbrales de la lección se calculan con las 20 sesiones anteriores y no con el volumen del propio día?

    • APorque así salen más barras
    • BPorque el volumen total del día solo se conoce al cierre: usarlo para decidir dónde terminan las barras de la mañana es información del futuro
    • CPorque pandas no puede sumar el volumen del día
    • DPor convención, sin más motivo
    Ver respuesta

    Respuesta B. Porque el volumen total del día solo se conoce al cierre: usarlo para decidir dónde terminan las barras de la mañana es información del futuro

    Es el sesgo de anticipación aplicado a la construcción de datos. Con el volumen del día cada sesión daría exactamente unas 390 barras, y ese número ya lleva dentro cuánto se iba a negociar. Con las sesiones anteriores, un día tranquilo da menos barras (290 el 2 de junio) y uno agitado, más.

  4. En la sesión de NOVA, la curtosis en exceso de las rentabilidades es 3,73 con barras de 1 minuto y 1,56 con barras de volumen. ¿Qué se puede concluir?

    • AQue las rentabilidades de las barras de volumen son normales
    • BQue las barras de volumen tienen colas menos gruesas en esta sesión, aunque el test de Jarque-Bera sigue rechazando la normalidad en todos los tipos
    • CQue las barras de tiempo están mal construidas
    • DQue con barras de volumen una estrategia gana más
    Ver respuesta

    Respuesta B. Que las barras de volumen tienen colas menos gruesas en esta sesión, aunque el test de Jarque-Bera sigue rechazando la normalidad en todos los tipos

    Muestrear por actividad reparte mejor la información entre barras y reduce las colas, pero no las elimina: el estadístico de Jarque-Bera de las barras de volumen (34,6) sigue muy por encima del valor crítico del 5 % (5,99). Y es una sola sesión: un resultado para comprobar en muchas, no una ley.

  5. Una barra de volumen termina con la operación de las 10:02:17.4. ¿Desde cuándo puede usarla un sistema para decidir?

    • ADesde el inicio de la barra
    • BDesde las 10:02:17.4, cuando llega la operación que alcanza el umbral, y no antes
    • CDesde el inicio del minuto siguiente
    • DDesde el cierre de la sesión
    Ver respuesta

    Respuesta B. Desde las 10:02:17.4, cuando llega la operación que alcanza el umbral, y no antes

    Una barra de actividad no tiene hora de cierre conocida de antemano: se cierra cuando se alcanza el umbral. Por eso la lección guarda ts_fin, la marca de la última operación, y es esa marca la que hay que usar para alinear la barra con cualquier otra serie.

Ideas clave

  • Una barra es una regla de muestreo: tiempo fijo, número de operaciones, acciones, dólares o desequilibrio entre compras y ventas.
  • Reproducir las barras de un proveedor exige conocer sus convenciones: en NOVA, la subasta de cierre de las 16:00 cuenta en la barra de las 15:55.
  • Las barras de tiempo cierran al mismo ritmo todo el día; las de actividad dibujan la U del volumen: 42 barras de volumen en la primera media hora y 17 a mediodía.
  • Los umbrales se calibran con sesiones anteriores, nunca con el total del día, y las barras de dólar resisten los splits que rompen las de volumen.
  • En esta sesión las barras de actividad tienen colas menos gruesas que las de 1 minuto (curtosis de 1,56 a 2,20 frente a 3,73), pero ninguna es normal.
  • Una barra de actividad se conoce cuando llega la operación que la cierra: su marca de tiempo es la de esa operación.

Recursos

Documentación