Quant500 Academy

2. Precios: consolidados, con retraso y de cierre

Nivel 1: De dónde vienen y qué traen dentro

Cuando escribes datos["Close"] crees que estás pidiendo "el precio". No existe tal cosa. Existen decenas de números defendibles para el mismo valor y el mismo instante, y la columna que te llega es una decisión que alguien tomó por ti. Este módulo va de saber cuál te tocó.

Un valor cotiza en muchos sitios a la vez

En Estados Unidos hay más de una docena de bolsas registradas y decenas de centros alternativos, además del enorme volumen que se ejecuta fuera de bolsa a través de mayoristas e internalizadores. Ninguna bolsa concentra la mayoría del volumen de un valor grande, y la porción que se ejecuta fuera de los mercados públicos ronda, en los últimos años, del orden de la mitad del total.

De ahí salen dos productos distintos:

Último trade frente a punto medio

El último trade es un hecho: alguien compró y alguien vendió a ese precio. Pero es un hecho contaminado, porque una operación se ejecuta contra la compra o contra la venta, no en el centro. Con una horquilla ancha, el último trade rebota entre los dos extremos sin que el valor se haya movido: es el efecto rebote de la horquilla, y en valores poco líquidos genera una volatilidad medida que no existe.

El punto medio de la mejor compra y la mejor venta no es un precio al que nadie haya operado, pero suele ser mejor estimación del valor consenso en ese instante.

import pandas as pd

libro = pd.DataFrame({
    "hora": pd.to_datetime(["2026-03-16 21:59:58", "2026-03-16 21:59:59"]),
    "bid": [101.10, 101.12],
    "ask": [101.30, 101.34],
    "ultimo": [101.11, 101.30],   # una venta agresiva y luego una compra agresiva
})

libro["medio"] = (libro["bid"] + libro["ask"]) / 2
libro["horquilla_pb"] = (libro["ask"] - libro["bid"]) / libro["medio"] * 10000
libro["sesgo_ultimo_pb"] = (libro["ultimo"] - libro["medio"]) / libro["medio"] * 10000
print(libro[["hora", "medio", "horquilla_pb", "sesgo_ultimo_pb"]])

En un segundo, el punto medio se ha movido apenas tres céntimos y el último trade ha saltado diecinueve. Si tu serie de precios es una secuencia de últimos trades en un valor con horquilla ancha, estás midiendo el signo de las órdenes, no el precio.

El cierre oficial no es el último trade del día

Aquí está el malentendido más caro del módulo. En las bolsas modernas, el cierre se forma en una subasta: durante unos minutos se acumulan órdenes sin cruzarlas y, al final, un algoritmo calcula el precio único que maximiza el volumen negociado. Ese precio de subasta es el cierre oficial, y es el que usan los índices, los fondos indexados y las valoraciones de las carteras.

Consecuencias que hay que interiorizar:

Lo mismo pasa en la apertura: hay una subasta de apertura, y el "Open" que ves puede ser el precio de esa subasta o la primera operación que vio tu proveedor. No son lo mismo.

Por qué dos proveedores serios discrepan

Antes de acusar a nadie de tener datos malos, la lista de razones legítimas por las que dos cierres difieren para el mismo valor y el mismo día:

La forma adulta de gestionar esto no es elegir un proveedor y creerle: es cruzarlos y vigilar la diferencia.

a = pd.read_csv("cierres_proveedor_a.csv", parse_dates=["fecha"])
b = pd.read_csv("cierres_proveedor_b.csv", parse_dates=["fecha"])

comp = a.merge(b, on=["fecha", "ticker"], suffixes=("_a", "_b"))
comp["dif_pb"] = (comp["cierre_a"] / comp["cierre_b"] - 1) * 10000

print(comp["dif_pb"].abs().describe())

# Umbral pragmatico: por debajo de 25 pb suele ser definicion; por encima, investigalo.
sospechosos = comp.loc[comp["dif_pb"].abs() > 25, ["fecha", "ticker", "cierre_a", "cierre_b", "dif_pb"]]
print(sospechosos.sort_values("dif_pb", key=abs, ascending=False).head(20))

Si las discrepancias se agrupan en unas pocas fechas, sospecha de un evento de mercado o de un ajuste aplicado por uno solo de los dos. Si se reparten por igual entre todos los días y valores, lo que tienes es una diferencia de definición, y hay que elegir cuál quieres.

Caso real: 24 de agosto de 2015

Aquella mañana los futuros estadounidenses abrieron desplomados y la apertura del contado se desordenó. Ese día se registraron más de mil interrupciones de negociación por volatilidad, repartidas entre varios cientos de valores y fondos cotizados. Muchos fondos cotizados llegaron a negociarse muy por debajo del valor de los activos que contenían, con caídas intradía que en algunos casos superaron el 30% mientras sus carteras subyacentes caían una fracción de eso.

La lección para quien construye datos: durante esos minutos, "el precio" de un mismo fondo era radicalmente distinto según mirases la última operación, el punto medio de una horquilla enorme, la cinta consolidada o un solo mercado. Un backtest que suponga que podía ejecutar a esos precios está fantaseando. Formación, no asesoramiento financiero.

Para ampliar esta lección

El resto de Quant500

Lo que estás aprendiendo, aplicado al índice todos los días:

La cartera del modelo, cada semana en tu correo

Cada sábado, su semana en un minuto: cómo fue, lo mejor y lo peor, y qué directivos compraron con su dinero. Y el día que el modelo se rehace, qué entra y qué sale. Es una simulación, no un consejo. Como mucho un correo a la semana, sin publicidad y sin cuenta; te das de baja con un clic. Lo hace una persona: Pol Gracia.

Cartera modelo con resultados simulados, no una recomendación personalizada. Tu correo se usa solo para esto: privacidad.

Detrás de la Academia y del resto de Quant500 hay una sola persona: Pol Gracia Serrahima.