Quant500 Academy

3. Fusiones, escisiones, cambios de ticker y bajas

Nivel 3: Ajustes y operaciones societarias

Un split y un dividendo cambian la escala de una serie. Las operaciones societarias de este módulo hacen algo peor: cambian la identidad de lo que la serie está midiendo, o la terminan. Y una serie que termina no siempre significa lo mismo.

Fusiones: qué pasa con la absorbida

Cuando A absorbe a B, la serie de B se acaba, pero el accionista de B no se queda con las manos vacías. Recibe efectivo, acciones de A, o una mezcla. Lo que tu base de datos debe registrar es la rentabilidad final real de esa última sesión, no un hueco.

Supón que cada acción de B se canjea por 0,75 acciones de A, que B cotizaba a 30,00 y que A cerraba a 44,00 el último día de B. El accionista de B recibe 0,75 por 44,00, es decir 33,00 de valor por unos títulos que valían 30,00: una rentabilidad final del +10,0%. Si tu serie corta en 30,00 y no registra nada más, has borrado ese 10%, que además suele ser positivo porque las adquisiciones se pagan con prima.

Del lado de A no hay que ajustar nada por el canje en sí: A emite acciones nuevas y a cambio se queda un negocio, así que su precio no da ningún salto artificial. El error típico en la absorbente es otro y es contable: sus fundamentales pasan de golpe a incluir un negocio que antes no estaba, de modo que series de ventas o de beneficio por acción dan saltos legítimos pero brutales.

Escisiones: el ajuste más olvidado

Una escisión es lo contrario. La matriz entrega a sus accionistas títulos de una filial que pasa a cotizar por su cuenta. El accionista sigue teniendo lo mismo, repartido en dos papeles. Y el precio de la matriz cae de golpe, exactamente igual que con un dividendo, solo que aquí el importe puede ser enorme.

import pandas as pd

# Escision 1 a 1: la matriz cierra a 66,00 la vispera; en la fecha ex
# la matriz cierra a 28,00 y la escindida a 38,00.
cierre_matriz_ex, cierre_escindida_ex, razon = 28.00, 38.00, 1.0
valor_entregado = razon * cierre_escindida_ex

# Queremos que la rentabilidad del dia ex salga del 0,00%.
factor = cierre_matriz_ex / (cierre_matriz_ex + valor_entregado)
print(round(factor, 6))     # 0.424242

matriz = pd.Series(
    [64.00, 65.50, 66.00, 28.00],
    index=pd.to_datetime([
        "2015-07-15", "2015-07-16", "2015-07-17", "2015-07-20",
    ]),
)
fecha_ex = pd.Timestamp("2015-07-20")
ajustada = matriz.copy()
ajustada[matriz.index < fecha_ex] *= factor

print(round(matriz.pct_change().iloc[-1], 4))      # -0.5758  falso
print(round(ajustada.pct_change().iloc[-1], 4))    #  0.0000  correcto

Un menos 57,6% que no existió. eBay escindió PayPal el 20 de julio de 2015 entregando una acción de PayPal por cada acción de eBay, y la cotización de eBay se dividió aproximadamente por la mitad ese día sin que nadie perdiera nada. Una serie sin este ajuste convierte esa operación en el peor día de la historia de la compañía.

Cambios de ticker: el evento sin factor

Un cambio de símbolo no tiene contenido económico: el factor es 1. Su peligro es de otra naturaleza. Facebook pasó a cotizar como META el 9 de junio de 2022. Si tu cargador une precios, fundamentales y señales por la columna del símbolo, esa fecha parte la empresa en dos: un histórico que muere en junio de 2022 y una compañía que nace ese día sin pasado. Cualquier ventana de 200 sesiones, cualquier momento a doce meses y cualquier estimación de volatilidad quedan vacíos durante casi un año.

Hay un agravante: los símbolos liberados se reasignan. Meses después, otra empresa puede estar cotizando bajo el símbolo antiguo, y tu unión por texto encadenará dos compañías distintas en una sola serie continua, con un salto de precio en medio que parecerá un evento de mercado.

Bajas: el mismo silencio, dos significados opuestos

Cuando una empresa deja de cotizar, la serie simplemente se detiene. Ese silencio puede significar dos cosas opuestas.

# La tabla de bajas guarda el motivo y lo cobrado, no solo la fecha de corte.
# Los ultimos cierres van redondeados, a modo de ilustracion.
bajas = pd.DataFrame({
    "id_permanente": [11111, 22222],
    "ultima_sesion": pd.to_datetime(["2022-10-27", "2008-09-16"]),
    "motivo": ["adquisicion en efectivo", "quiebra"],
    "cobrado_por_accion": [54.20, 0.00],
    "ultimo_cierre": [53.70, 0.20],
})

bajas["retorno_final"] = bajas["cobrado_por_accion"] / bajas["ultimo_cierre"] - 1
print(bajas[["motivo", "retorno_final"]].round(4))
# adquisicion en efectivo     0.0093
# quiebra                    -1.0000

Sin la columna del motivo y sin la del importe cobrado, esas dos filas serían indistinguibles. Tratar las dos igual es un error caro, y en cualquiera de los dos sentidos. Si asignas menos 100% a todas, castigas injustamente a las compradas con prima. Si arrastras el último precio conocido hasta el final del periodo, regalas al accionista de Lehman un capital que se evaporó. Y si directamente excluyes del universo a las que no llegaron vivas al final, ya no estás midiendo una estrategia: estás midiendo una lista de supervivientes.

Caso real: octubre de 2022 frente a septiembre de 2008

Pon las dos series una al lado de la otra. Ambas se cortan de golpe. Ambas dejan de aparecer en el fichero de precios del día siguiente. Un cargador ingenuo las ve idénticas: dos huecos.

Y son lo contrario. Una cerró con un cobro en efectivo de 54,20 dólares por acción; la otra con un valor residual próximo a cero. Por eso la tabla de bajas de un proveedor serio no guarda solo una fecha: guarda el motivo, la contraprestación y la rentabilidad final del último tramo. Sin ese campo, tu base de datos no distingue una salida por la puerta grande de una quiebra, y la diferencia entre ambas puede ser el 100% del capital invertido en esa posición.

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.