Quant500 Academy

3. Fundamentales: cuándo se publicaron de verdad

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

Con los precios, el problema es qué número te dan. Con los fundamentales, el problema es cuándo. Toda cifra contable arrastra dos fechas distintas, y confundirlas es el fallo que más veces convierte una estrategia mediocre en una maravilla imposible.

Las dos fechas de cualquier fundamental

Entre una y otra pasan semanas o meses. Y aquí está el detalle que casi todas las fuentes baratas te ocultan: el CSV suele traer solo la primera. Te dan una tabla indexada por fin de trimestre y te dejan creer que ese día ya existía el dato. No existía. El 31 de diciembre nadie sabe todavía el beneficio del cuarto trimestre, ni siquiera la propia empresa.

Cuánto tarda de verdad: los plazos del regulador estadounidense

La SEC fija plazos máximos según el tamaño del emisor, y sirven como referencia del desfase típico:

Además hay un matiz que confunde a mucha gente: la nota de prensa de resultados suele salir antes que el informe completo, comunicada al mercado en un 8-K. Es decir, hay dos momentos distintos en que el mercado se entera de cosas distintas del mismo trimestre. La cifra auditada y detallada llega después.

Traducido a números redondos: para un anual, entre el fin del ejercicio y su presentación completa pueden pasar dos o tres meses. Si tu backtest asume que el 1 de enero ya podías filtrar por el beneficio del año que acababa de terminar, has adelantado el reloj un trimestre entero.

Lo primero, entonces, es mirar si tu fuente trae siquiera la segunda fecha, y qué pinta tiene el desfase:

import pandas as pd

fund = pd.read_csv("fundamentales.csv", parse_dates=["fin_periodo", "fecha_publicacion"])

print("Filas sin fecha de publicacion:", fund["fecha_publicacion"].isna().sum())

fund["desfase_dias"] = (fund["fecha_publicacion"] - fund["fin_periodo"]).dt.days
print(fund["desfase_dias"].describe())

# Anomalias: publicaciones antes del cierre del periodo o desfases absurdos.
raras = fund[(fund["desfase_dias"] < 0) | (fund["desfase_dias"] > 200)]
print(raras[["cik", "fin_periodo", "fecha_publicacion", "desfase_dias"]].head())

Si la columna de publicación no existe, o si el describe te devuelve una mediana de cero días, tu fuente no es utilizable para nada que dependa del calendario: te está dando la contabilidad como si hubiera sido instantánea. El desarrollo completo de qué le hace eso a un backtest es materia del siguiente nivel; aquí basta con saber que es una propiedad de la fuente, no un detalle menor.

La cifra de 2019 que ves hoy no es la de 2019

La segunda mitad del problema es que las cifras cambian después de publicadas, y las bases de datos suelen sobrescribirlas sin dejar rastro:

Una base de datos en el momento correcto (point-in-time) guarda todas las versiones y te deja preguntar "qué se sabía el 3 de marzo de 2020". Una base normal guarda solo la última. Si solo tienes instantáneas, al menos guárdalas y compáralas:

viejo = pd.read_parquet("fundamentales_2021_06.parquet")
nuevo = pd.read_parquet("fundamentales_2026_06.parquet")

clave = ["cik", "fin_periodo", "concepto"]
comp = viejo.merge(nuevo, on=clave, suffixes=("_viejo", "_nuevo"))
comp["cambio_pct"] = (comp["valor_nuevo"] / comp["valor_viejo"] - 1) * 100

revisados = comp[comp["cambio_pct"].abs() > 0.5]
print("Puntos revisados:", len(revisados), "de", len(comp))
print(revisados.groupby("concepto")["cambio_pct"].agg(["count", "median"]).sort_values("count", ascending=False).head(10))

El resultado suele sorprender: los conceptos de flujo y las partidas con estimaciones (provisiones, deterioros, impuestos diferidos) se revisan mucho más que las ventas. Si tu señal se apoya en una partida muy revisable, buena parte de lo que mides en el pasado es información que entonces no estaba disponible en esa forma.

Tres reglas prácticas que salen de todo lo anterior:

Caso real: Wirecard, junio de 2020

Wirecard había llegado a formar parte del índice alemán de grandes valores. En junio de 2020, sus auditores se negaron a firmar las cuentas de 2019 porque no encontraban unos 1.900 millones de euros que figuraban en cuentas fiduciarias. La compañía acabó reconociendo que ese saldo probablemente no existía y retirando la validez de estados financieros de ejercicios anteriores. Días después presentó insolvencia.

Piensa en lo que eso significa para una base de datos. Durante años, cualquier filtro cuantitativo que mirase la caja de Wirecard leyó una cifra publicada, auditada y perfectamente formateada. Hoy, esa fila puede aparecer corregida, vacía o marcada; y una descarga hecha en 2019 y otra hecha ahora no coinciden. Ni una ni otra está mal: son dos momentos distintos del mismo hecho. Guardar cuándo supiste cada cosa no es burocracia, es lo que hace que el pasado sea reconstruible. Formación, no asesoramiento financiero.

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.