Saltar a contenido

Mercado y barras

Qué vas a aprender

  • Qué es una barra OHLCV y por qué es la unidad básica del banco.
  • Qué es un timeframe (1min, 5min, 1h, 1d…) y cómo se derivan unos de otros.
  • Qué significa que un precio esté ajustado por splits y dividendos (adjclose, price_basis) y por qué mezclar series ajustadas y sin ajustar es un error grave.
  • Qué son el tick y el redondeo al tick.

La barra: una foto resumida del mercado

Un mercado produce miles de operaciones por minuto. Guardarlas todas sería caro y, para la mayoría de estrategias, innecesario. Lo que se hace es resumir un intervalo de tiempo en cinco números:

Campo Qué es Analogía
open Primer precio del intervalo La temperatura al abrir la ventana
high Precio máximo La máxima del día
low Precio mínimo La mínima del día
close Último precio del intervalo La temperatura al cerrar la ventana
volume Cantidad negociada Cuánta gente pasó por la calle

A esa foto se le llama barra OHLCV (o vela). Tiene una propiedad que el banco comprueba siempre: low ≤ open, close ≤ high. Si una barra la viola, el dato está roto y la política de limpieza clean.v1 lo repara y lo marca (is_synthetic), en vez de esconderlo.

Anatomía de una barra OHLC: trayectoria del precio en un minuto y la barra que la resume

Figura 1. La trayectoria real del precio durante un minuto (izquierda) se resume en cuatro números: open, high, low y close (derecha).

Vela japonesa alcista y bajista con cuerpo y mechas

Figura 2. La vela japonesa dibuja los mismos cuatro precios; el color y el cuerpo dicen de un vistazo si la barra subió o bajó.

flowchart LR
    T["Operaciones individuales<br/>(ticks)"] -->|resumir intervalo| B["Barra OHLCV<br/>open · high · low · close · volume"]
    B --> V{"¿low ≤ open,close ≤ high?"}
    V -->|sí| OK["Barra canónica"]
    V -->|no| R["Reparar + marcar is_synthetic"]

El intervalo es semiabierto

Una barra de 1 minuto que empieza a las 09:30 cubre [09:30, 09:31): incluye el instante 09:30 y excluye el 09:31. Así ninguna operación puede caer en dos barras a la vez. El esquema canónico lo guarda con tres relojes en nanosegundos UTC:

Columna Significado
market_open_ns Apertura económica del intervalo
market_close_ns Fin del intervalo
data_available_ns Desde cuándo se puede usar el dato

Por qué hay un tercer reloj

En el banco, data_available_ns = market_close_ns por supuesto declarado de latencia de publicación cero. Es un supuesto sintético (el proveedor no documenta su latencia real) y está escrito en el manifiesto. Ese supuesto es lo que hace posible el carril SIM-S next-open idealizado (ver Backtesting causal). El lector filtra siempre por data_available_ns, nunca por la apertura.

Timeframes: la misma película a distintas velocidades

El timeframe (o marco) es la duración de cada barra. El banco trabaja con 1min, 2min, 5min, 15min, 30min, 1h, 1d para NVDA y KO (fuente twelvedata) y con 1d para el universo de Yahoo.

Cinco barras de 1 minuto agregadas en una barra de 5 minutos

Figura 3. Agregar barras: open de la primera, máximo de los high, mínimo de los low, close de la última y suma de volúmenes.

Los marcos superiores se derivan de las barras de 1 minuto con una única implementación (finazbench/data/resample.py):

  • open = primero, high = máximo, low = mínimo, close = último, volume = suma.
  • Los cubos se anclan a la apertura de sesión que dice el calendario (no a medianoche ni a la primera barra presente).
  • Ningún cubo cruza el límite de sesión: la barra de las 15:59 del lunes y la de las 09:30 del martes nunca se mezclan.

El error que se corrigió (DEV-01B-09)

Anclar los cubos a la primera barra presente parece equivalente, pero cuando una sesión empieza con hueco desplaza toda la rejilla. Se midieron 7 sesiones afectadas, con desfases de 6 min en NVDA y 241 min en KO, que producían barras «horarias» empezando a las 09:36 o a las 13:31. Tras la corrección cambiaron de límites 37 barras de 1 h, 75 de 30 min, 156 de 15 min, 479 de 5 min y 1.016 de 2 min.

1d no es 1d_long

freq=1d se deriva del minuto (desde 2020); freq=1d_long viene del CSV diario del proveedor (desde 1999 en NVDA, 1970 en KO). Se guardan separados a propósito: mezclarlos daría dos valores distintos para la misma sesión.

Ajustes: splits y dividendos

Cuando una empresa hace un split 4×1, su precio se divide entre cuatro de un día para otro sin que nadie haya perdido dinero. Si no se ajusta la serie histórica, un indicador vería una «caída» del 75 % que nunca ocurrió. Por eso las series se ajustan:

  • Ajuste por splits: el OHLC de Yahoo y el de twelvedata ya vienen ajustados por splits.
  • Ajuste por dividendos (total return): adjclose añade el efecto de los dividendos. Es otra serie, no «la misma más precisa».

El banco guarda siempre el OHLC del proveedor más adj_close y adj_factor = adj_close / close, y decide al leer con price_basis:

price_basis Qué devuelve
raw_split_adjusted (por defecto) El OHLC tal cual (ajustado solo por splits)
total_return_adjusted open, high, low y close escalados por adj_factor

¿Por qué escalar los cuatro precios y no solo el cierre?

Porque el factor es un escalar positivo por barra: multiplicar los cuatro conserva low ≤ close ≤ high. Escalar solo close fabricaría barras imposibles, y un ATR leería volatilidad inventada.

Regla de oro: adj_close para features, close para ejecutar

Nunca se sobrescribe close. Pedir total_return_adjusted a un instrumento sin adj_close (NVDA y KO de twelvedata, o los pares FX) lanza un error: devolver una serie sin dividendos cuando se pidió con dividendos sería una sustitución silenciosa.

Factores medidos (valor inicial de adj_factor y número de saltos de dividendo):

Serie adj_factor inicial Saltos
AMZN, TSLA 1,000000 0
INTC 0,767229 39
MSFT 0,847031 47
BBVA.MC 0,541159 26
SAN.MC 0,645629 30
TEF.MC 0,443214 23

El tick: la rejilla de precios

Un precio no puede ser cualquier número: se mueve en múltiplos de un tick (0,01 USD en las acciones de EE. UU., por ejemplo). El ledger trabaja con precios en ticks enteros para que la contabilidad sea exacta (lo verás en Ledger SIM-S).

El tick se aplica al precio crudo (DEV-01B-08)

Una serie ajustada por dividendos deja de caer en la rejilla (5,02 EUR × 0,6845 = 3,4362 EUR, que no es múltiplo de 0,001). Es una aproximación declarada: re-redondear la serie ajustada destruiría lo único que la hace útil, que los retornos sean exactos.

Resumen

  • La barra OHLCV resume un intervalo semiabierto [inicio, fin); el banco guarda tres relojes y filtra por data_available_ns.
  • Los timeframes superiores se derivan del minuto, anclados a la sesión y sin cruzarla.
  • price_basis elige entre precios ajustados solo por splits o también por dividendos; nunca se mezclan ni se sustituyen en silencio.
  • Los precios viven en una rejilla de ticks; el ledger opera en enteros.

Para practicar

  1. Una barra tiene open=10, high=12, low=9, close=12,5. ¿Qué dice el banco de ella?
  2. ¿Por qué un cubo de 1 h anclado a medianoche daría barras incorrectas en XNYS?
  3. Pide mentalmente total_return_adjusted para KO de twelvedata. ¿Qué ocurre y por qué es lo correcto?