Quant500 Academy

4. Reconciliación: cuadrar tus libros con los del bróker

Nivel 10: Saber qué está pasando

Ya sabes que el bróker es la fuente de verdad. Eso resuelve quién gana una discusión, pero no resuelve el problema de verdad: enterarte de que hay discusión. La reconciliación es el proceso diario que compara tus libros con los suyos, y es el control que más dinero salva de todo el nivel, porque es el único que detecta los fallos que no producen ni errores ni alertas.

Reconciliar es comparar tres cosas, no una

Cada día, después del cierre y antes de que el sistema pueda operar de nuevo, se comparan tres bloques contra lo que declara el bróker:

Comparar solo una de las tres da una falsa sensación de control. El efectivo puede cuadrar mientras las acciones no cuadran, por ejemplo tras un desdoblamiento que el bróker ajustó y tu base de datos no. Y las posiciones pueden cuadrar mientras el efectivo no lo hace, porque cobraste un dividendo que tu contabilidad no modela.

from decimal import Decimal

TOLERANCIA_ACCIONES = Decimal("0")
TOLERANCIA_EFECTIVO = Decimal("0.05")

def dec(x):
    return Decimal(str(x))

def reconciliar_posiciones(locales, del_broker):
    simbolos = set(locales) | set(del_broker)
    diferencias = []
    for s in sorted(simbolos):
        mia = dec(locales.get(s, 0))
        suya = dec(del_broker.get(s, 0))
        if abs(mia - suya) > TOLERANCIA_ACCIONES:
            diferencias.append({"simbolo": s, "local": mia,
                                "broker": suya, "delta": mia - suya})
    return diferencias

Usa Decimal, no coma flotante: un descuadre de céntimos que aparece y desaparece según el día es indistinguible de un descuadre real y acaba enseñándote a ignorar el proceso.

De dónde salen las diferencias

Casi todas las diferencias que verás caen en seis familias, y conviene reconocerlas de un vistazo:

Fíjate en que solo las dos últimas son fallos tuyos; las cuatro primeras son hechos legítimos del mundo real que tu contabilidad no conocía. Por eso la reconciliación no sirve únicamente para cazar errores de software: sirve para descubrir todo lo que le pasa a tu dinero y tu código todavía no modela. Cada diferencia explicada acaba convertida en una regla nueva, y con los meses el proceso se vuelve silencioso.

Tolerancias: una sola cifra no vale para todo

La tolerancia de acciones es cero y no se negocia: media acción de diferencia no existe en la naturaleza salvo en cuentas fraccionadas, y si tu bróker las permite, la tolerancia sigue siendo el mínimo que él admite. La del efectivo es distinta, porque el redondeo de comisiones sí produce céntimos legítimos, pero hay que ponerla lo bastante baja como para que un error real no quepa dentro. Una tolerancia generosa es la forma más elegante de no enterarte nunca de nada, y tiene la desagradable propiedad de esconder mejor los errores cuanto más pequeña es la cuenta con la que empiezas a operar.

La comprobación completa

def reconciliar_dia(estado_local, cliente, fecha):
    dif_pos = reconciliar_posiciones(estado_local.posiciones, cliente.posiciones())
    dif_efectivo = dec(estado_local.efectivo) - dec(cliente.efectivo())

    mias = {o["id_cliente"] for o in estado_local.operaciones_de(fecha)}
    suyas = {o["id_cliente"] for o in cliente.operaciones(fecha)}
    solo_mias = mias - suyas
    solo_suyas = suyas - mias

    cuadra = (not dif_pos
              and abs(dif_efectivo) <= TOLERANCIA_EFECTIVO
              and not solo_mias and not solo_suyas)

    registrar(logging.INFO if cuadra else logging.ERROR, "reconciliacion",
              fecha=fecha, cuadra=cuadra, posiciones_dif=len(dif_pos),
              delta_efectivo=str(dif_efectivo),
              solo_mias=sorted(solo_mias), solo_suyas=sorted(solo_suyas))
    return cuadra, dif_pos, dif_efectivo, solo_mias, solo_suyas

def cerrar_dia(estado_local, cliente, fecha):
    cuadra, dif_pos, dif_efectivo, solo_mias, solo_suyas = reconciliar_dia(
        estado_local, cliente, fecha)
    if cuadra:
        M.contar("reconciliaciones_ok")
        return True
    estado_local.bloquear("descuadre:" + fecha)
    alertar("critico", "descuadre",
            "reconciliacion de %s no cuadra: %d posiciones, efectivo %s"
            % (fecha, len(dif_pos), dif_efectivo))
    return False

Al encontrar una diferencia: parar y avisar, nunca corregir en silencio

La tentación es enorme: el bróker dice 40 y tú tienes 26, el bróker manda, escribo 40 y mañana arrancamos cuadrados. Nunca hagas eso. Copiar el número tapa el hecho que produjo la diferencia, y ese hecho casi siempre sigue activo: si una orden se duplicó por un reintento mal hecho, mañana se duplicará otra vez, con más dinero y sin que nadie lo note, porque el proceso que debía avisar ha aprendido a ajustar solo.

El comportamiento correcto tiene tres pasos, en este orden: bloquear la operativa para el día siguiente, emitir una alerta crítica, y usar el registro del módulo uno para reconstruir la cadena completa de la orden implicada hasta encontrar la causa. El ajuste se hace después, a mano, con la explicación escrita al lado.

Y ojo con una trampa de diseño frecuente: escribir solo la rama feliz. Un proceso que marca la reconciliación como correcta cuando cuadra y no hace nada cuando no cuadra parece funcionar durante meses. Si además nadie vigila la ausencia de esa marca, con lo que aprendiste en el módulo tres, el descuadre es invisible.

Caso trabajado: dos ejecuciones donde debía haber una

Escenario inventado con fines didácticos. Un jueves la reconciliación devuelve: posiciones con una diferencia de 26 acciones en un valor, efectivo local 4.938 euros por encima del real, y un identificador de ejecución que aparece en el bróker y no en tus libros. Las tres pistas apuntan al mismo sitio: se compró dos veces. El registro confirma que el segundo envío llevaba un identificador de cliente nuevo, así que el bróker lo trató como una orden distinta. Coste del fallo: la posición no deseada más el deslizamiento de deshacerla. Coste de no reconciliar: el mismo error repitiéndose cada semana sin que nadie lo vea.

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.