Almgren-Chriss sin rodeos: ejecución óptima que puedes implementar en una tarde
Almgren y Chriss (2001), "Optimal execution of portfolio transactions" (Journal of Risk 3(2), 5–39), es probablemente el paper más citado en investigación de ejecución y uno de los peor implementados correctamente. La mayoría del código "Almgren-Chriss" que circula por ahí es en realidad un programador de TWAP con un bloque de comentarios. La mayoría de los artículos de blog sobre el tema se saltan la derivación, mencionan de pasada una función sinh y nunca tocan la parte que realmente importa en producción: de dónde salen los parámetros.
Este artículo lo hace completo. Los supuestos del modelo, expuestos con honestidad. La trayectoria de forma cerrada, derivada, no simplemente afirmada. La frontera eficiente y cómo elegir el parámetro de aversión al riesgo. Y la calibración de los tres insumos —impacto temporal , impacto permanente , volatilidad — a partir de datos del libro de órdenes y de operaciones de Binance, con código Python funcional y una discusión honesta de por qué el ajuste es ruidoso. Esta es la columna vertebral intelectual de todo lo demás en ejecución: TWAP, VWAP y POV son casos especiales o primos heurísticos (ver TWAP, VWAP, POV: los algoritmos de ejecución que todos usan), y los enfoques modernos de ML —programadores de aprendizaje por refuerzo y modelos neuronales de impacto— son intentos de relajar exactamente los supuestos que estamos a punto de escribir.
El planteamiento: qué asume realmente el modelo
Tienes unidades de un activo (digamos, 100 BTC) y debes liquidarlas por completo antes del tiempo . Divide en intervalos de longitud . Tu variable de decisión es la trayectoria de tenencias , con operaciones ejecutadas en el intervalo .
Tres supuestos sostienen todo el modelo:
1. Caminata aleatoria aritmética. El precio no perturbado sigue
donde son normales estándar i.i.d. y es volatilidad absoluta (dólares por unidad de tiempo, no porcentaje). Aritmética, no geométrica: a lo largo de unas pocas horas la diferencia es despreciable y la caminata aritmética mantiene el álgebra lineal-cuadrática. Sin deriva: Almgren y Chriss discuten el término de deriva, pero para una liquidación intradía tu estimación de alfa a lo largo de 4 horas es casi siempre ruido, y fijarla en cero es la postura honesta por defecto.
2. Impacto permanente lineal. : operar a una tasa desplaza el precio de forma permanente, proporcional, y el desplazamiento nunca decae. Cada unidad que vendes empuja el mid hacia abajo en para siempre. Esto parece tosco, pero hay una razón profunda para mantenerlo lineal: Huberman y Stanzl (2004) demostraron que un impacto permanente no lineal en el tamaño de la operación admite estrategias de ida y vuelta con beneficio esperado positivo —manipulación de precios—. El impacto permanente lineal no es una simplificación por conveniencia; es la única elección libre de arbitraje dentro de esta clase de modelos.
3. Impacto temporal lineal. El precio que realmente recibes en el intervalo es
captura la mitad del spread bid-ask más las comisiones; es la pendiente del costo marginal de demandar liquidez a la tasa . El impacto temporal afecta solo a tu propia ejecución y se desvanece instantáneamente: el libro se repone por completo antes de tu siguiente orden hija. Cada una de las tres palabras "lineal", "instantáneamente", "por completo" es incorrecta en mercados reales, y la última sección trata sobre cuán incorrecta. Pero primero, la recompensa por aceptarlas.
Costo y varianza de una trayectoria
Suma las ejecuciones, resta del mark inicial , y obtienes el implementation shortfall. Su esperanza y varianza sobre el ruido son:
con (una corrección de discretización que se desvanece cuando ).
Dos observaciones que la mayoría de las implementaciones pasan por alto:
- El costo permanente no depende de la trayectoria. Con impacto permanente lineal y sin decaimiento, pagas el mismo peaje permanente sin importar cómo programes la ejecución. La optimización es enteramente una pugna entre el término de costo temporal (que quiere una negociación lenta y uniforme; se minimiza con , es decir, TWAP) y el término de varianza (que quiere que tu inventario desaparezca ayer; se minimiza con liquidación inmediata).
- El término de varianza está ponderado por inventario, no por operación. El riesgo se acumula sobre lo que aún tienes, , no sobre lo que negocias. Por eso la urgencia carga el programa hacia el inicio.
La forma cerrada: sinh, cosh y el parámetro de urgencia
Almgren-Chriss minimizan el objetivo de media-varianza
donde es la aversión al riesgo en unidades de 1/dólares. Fija para los puntos interiores. El término temporal aporta diferencias segundas de , el término de varianza aporta el propio , y obtienes una ecuación en diferencias lineal de segundo orden:
Este es el análogo discreto de , y con las condiciones de frontera , la solución es hiperbólica:
donde resuelve , que para pequeño es simplemente .

Todo lo relativo a la estrategia se comprime en un solo número:
es la urgencia. Su recíproco es el tiempo característico de la operación: la escala temporal durante la cual vale la pena sostener el riesgo de inventario para ahorrar costo de impacto. Nótese lo que no depende: del tamaño de la orden ni del plazo . Que la escala temporal intrínseca de tu operación sea de 20 minutos o de 6 horas lo determinan únicamente la aversión al riesgo, la volatilidad y la liquidez. Si , tu plazo es irrelevante: el modelo liquida según su propio calendario y la cola del horizonte queda sin usar. Si , el plazo es vinculante y en la práctica estás haciendo TWAP.
El límite neutral al riesgo es TWAP — por eso existe TWAP
Toma , de modo que . Entonces y
La trayectoria óptima degenera en la línea recta: cantidades iguales en intervalos de tiempo iguales. TWAP no es una heurística que resulta funcionar; es el óptimo exacto del modelo de Almgren-Chriss para un operador neutral al riesgo con impacto lineal. Cada vez que alguien ejecuta TWAP, está afirmando implícitamente : "no me importa la varianza de mi shortfall, solo su media". Esa es una postura defendible para órdenes pequeñas y horizontes cortos. Es una postura extraña para liquidar el 5% del volumen diario en 8 horas en un activo con 4% de volatilidad diaria, que es exactamente donde la gente lo ejecuta de todos modos. El límite opuesto da para todo : liquidar todo en el primer intervalo, pagar lo que cueste. Entre estos extremos, interpola: es apenas distinguible de TWAP, está agresivamente cargado al inicio.
La frontera eficiente de la ejecución
Para cada obtienes una trayectoria, un costo esperado y una varianza . Recorrer traza una curva en el plano —la frontera eficiente de la ejecución, en analogía directa con Markowitz—. Es convexa y decreciente: menos varianza siempre cuesta más shortfall esperado, con rendimientos marginales rápidamente decrecientes. En el punto de la frontera seleccionado por un dado, la aversión al riesgo es la pendiente (negativa) de la tangente: .

La frontera reformula la pregunta "¿qué es ?" —que nadie puede responder introspectivamente— en "¿qué compromiso entre costo y riesgo quiero?", que una mesa de trading sí puede responder. Tres formas prácticas de elegir el punto:
- Razonamiento de costo marginal. Recorre la frontera y pregunta: "moviéndome de este punto al siguiente, pago dólares de costo esperado para eliminar dólares de desviación estándar del shortfall; ¿acepto ese intercambio?" El codo de la frontera suele ser evidente dentro de un factor de 2, y la trayectoria es poco sensible a a esa resolución.
- Presupuesto de riesgo. Fija una desviación estándar de shortfall máxima aceptable (por ejemplo, "el 1-sigma a un día de la posición residual debe mantenerse por debajo de 15 pb del nocional") y toma la trayectoria más barata que la satisfaga. Es un problema restringido cuyo multiplicador de Lagrange es .
- Fijación del tiempo característico. Elige directamente ("esta orden debería tener una vida media de 90 minutos") y despeja . Esto es lo que la mayoría de los profesionales hacen implícitamente cuando fijan un control deslizante de "urgencia".
Un ejemplo resuelto con números que justificaremos en la sección de calibración. Vender BTC a S_0 = \100{,}000T = 4\sigma = $600\sqrt{\text{h}}\eta = 1.0\ $\cdot\text{h}/\text{BTC}\lambda = 10^{-6}\ $^{-1}$. Entonces
El programa óptimo vende 46.2 BTC en la primera hora (TWAP: 25). Costo temporal: \eta \int_0^T v(t)^2 dt \approx \3{,}290$2{,}500$53{,}000$69{,}300undefined10M, reduces el riesgo de shortfall en un 23%. Ese es todo el contenido del modelo en una frase: pone precio al seguro y te deja decidir si comprarlo.
La trayectoria en sí son diez líneas de Python:
import numpy as np
def almgren_chriss(X, T, N, sigma, eta, gamma, lam):
"""Optimal liquidation trajectory (discrete AC 2001)."""
tau = T / N
eta_t = eta - 0.5 * gamma * tau # tilde-eta
kappa_t2 = lam * sigma**2 / eta_t # tilde-kappa^2
kappa = np.arccosh(0.5 * kappa_t2 * tau**2 + 1) / tau
t = np.arange(N + 1) * tau
x = X * np.sinh(kappa * (T - t)) / np.sinh(kappa * T)
n = x[:-1] - x[1:] # child order sizes
E = 0.5 * gamma * X**2 + (eta_t / tau) * np.sum(n**2)
V = sigma**2 * tau * np.sum(x[1:]**2)
return x, n, E, V
x, n, E, V = almgren_chriss(X=100, T=4, N=48, sigma=600,
eta=1.0, gamma=0.3, lam=1e-6)
print(f"first-hour qty: {n[:12].sum():.1f} BTC, "
f"E=${E:,.0f}, std=${np.sqrt(V):,.0f}")
Recorre lam sobre np.logspace(-8, -4, 50) y grafica frente a : esa es tu frontera, y toda la superficie de decisión cabe en un gráfico que puedes entregar a un comité de riesgo.
Calibración: , , a partir de datos de Binance
Aquí es donde el 90% de las implementaciones mueren en silencio. La fórmula de la trayectoria es trivial; los parámetros no lo son. Tres magnitudes, tres problemas de estimación distintos.

: la fácil
Volatilidad absoluta por raíz de hora, a partir de retornos del precio mid. Usa velas de 1 minuto y escala; a muestreos más finos, el ruido de microestructura (bid-ask bounce) sesga al alza la varianza realizada. Esta estimación es sólida: la volatilidad es el único parámetro que acertarás.
: impacto temporal a partir del libro y de las operaciones
Dos rutas complementarias. Ruta A — recorrer el libro L2. A partir de capturas de profundidad, calcula el costo de un barrido hipotético marketable de tamaño : el VWAP de los niveles consumidos menos el mid. Ajustar da el impacto instantáneo por BTC. Para convertirlo en el basado en tasa del modelo debes asumir un tiempo de reposición del libro (del orden de 10–60 s para BTCUSDT): operar a la tasa consume por ciclo de refresco, así que . Ese supuesto —reposición instantánea y total en un reloj fijo— es exactamente la grieta por donde entra Obizhaeva-Wang; más sobre esto abajo. Ruta B — regresar el slippage realizado sobre la participación. Agrupa el flujo agresivo (taker) en bins de 1 minuto; para cada bin, regresa el VWAP del lado taker menos el mid de apertura contra la tasa de volumen taker. La Ruta B mide lo que el mercado realmente cobró a los agresores; la Ruta A mide lo que el libro en reposo cobraría en este momento. Cuando difieren por un factor de 3, confía en B para el nivel y en A para la forma intradía.
: impacto permanente vía regresión al estilo Kyle
Regresa los cambios del precio mid sobre el flujo neto taker con signo en ventanas de 5 minutos:
Luego verifica que el impacto no haya revertido una ventana después: el componente que no revierte es "permanente" en tu escala temporal de negociación. Esta es, con diferencia, la más ruidosa de las tres.
Código funcional, usando solo los endpoints REST públicos de Binance:
import requests, numpy as np, pandas as pd
B = "https://api.binance.com/api/v3"
sym = "BTCUSDT"
kl = requests.get(f"{B}/klines", params=dict(
symbol=sym, interval="1m", limit=1000)).json()
close = np.array([float(k[4]) for k in kl])
sigma = np.diff(close).std() * np.sqrt(60) # $/sqrt(h)
d = requests.get(f"{B}/depth",
params=dict(symbol=sym, limit=5000)).json()
bids = np.array(d["bids"], dtype=float) # [price, qty]
mid = (bids[0, 0] + float(d["asks"][0][0])) / 2
cq = np.cumsum(bids[:, 1]) # cum qty
cn = np.cumsum(bids[:, 0] * bids[:, 1]) # cum notional
sizes = np.linspace(0.5, 50, 40) # BTC probes
cost = [mid - np.interp(q, cq, cn) / q for q in sizes]
eps, eta_inst = np.polyfit(sizes, cost, 1)[::-1] # cost ~ eps + k*q
eta = eta_inst * (30 / 3600) # 30s refresh -> $*h/BTC
tr = requests.get(f"{B}/aggTrades",
params=dict(symbol=sym, limit=1000)).json()
df = pd.DataFrame(dict(
t=[t["T"] for t in tr],
p=[float(t["p"]) for t in tr],
q=[float(t["q"]) * (-1 if t["m"] else 1) for t in tr]))
df["bin"] = df.t // 300_000 # 5-min bins
g = df.groupby("bin").agg(dp=("p", lambda s: s.iloc[-1] - s.iloc[0]),
qn=("q", "sum"))
gamma = np.polyfit(g.qn, g.dp, 1)[0] # $/BTC
print(f"sigma={sigma:.0f} $/sqrt(h) eta={eta:.2f} $*h/BTC "
f"gamma={gamma:.3f} $/BTC")
Para producción, reemplaza la única página de aggTrades por varios días de los volcados históricos y ejecuta la regresión de sobre miles de bins, no un puñado.
Por qué la calibración en cripto es ruidosa, y qué hacer al respecto
Ejecuta la regresión de sobre datos reales y obtendrás un de unos pocos puntos porcentuales y un coeficiente que se mueve por un factor de 2–5 entre días. Esto no es un error en tu código. Las razones son estructurales:
- Endogeneidad. El flujo responde al precio tanto como el precio responde al flujo. Los operadores de momentum compran porque el precio subió; una regresión OLS ingenua de retornos sobre flujo capta su reacción y se la atribuye al impacto. La solución limpia es usar tus propias ejecuciones (exógenas por construcción), que solo tienes después de haber operado un tiempo.
- Concavidad. El impacto real es cóncavo en el tamaño —empíricamente cercano a raíz cuadrada (Almgren, Thum, Hauptmann y Li, 2005, "Direct estimation of equity market impact", encontraron un exponente cercano a 0.6; la misma concavidad es robusta en cripto). Ajustar una recta a una función cóncava hace que tu y dependan del rango de tamaños en la muestra. Calibra en las tasas de participación a las que realmente vas a operar.
- Fragmentación y liderazgo de derivados. BTCUSDT spot en Binance es una plaza entre muchas, y el descubrimiento de precio ocurre con frecuencia en los perpetuos. Flujo que nunca ves mueve el precio sobre el que regresas, inflando el término de ruido.
- Dependencia del régimen. El medido en la sesión asiática no describe la apertura de EE. UU.; el impacto aproximadamente se duplica cuando la volatilidad se duplica. Calibra por sesión y reajusta al menos semanalmente.
La gracia salvadora: la trayectoria es indulgente. , así que un error de factor 2 en mueve solo por , y la función de costo es plana cerca del óptimo. Acertar dentro de un factor de 2 y dentro de un orden de magnitud ya captura la mayor parte de la mejora disponible sobre TWAP. La precisión importa para las estimaciones de costo pre-trade; la robustez basta para la programación.
Dónde se rompe el modelo, y los papers que lo arreglan
Almgren-Chriss es un andamiaje, y saber exactamente qué viga es estructural te dice a qué extensión recurrir.
Impacto no lineal — Almgren (2003). "Optimal execution with nonlinear impact functions and trading-enhanced risk" (Applied Mathematical Finance 10, 1–18) rehace el programa con impacto temporal en ley de potencias . Para el empíricamente favorecido —la ley de raíz cuadrada—, las trayectorias óptimas cambian de carácter: el impacto cóncavo penaliza menos las ráfagas que el impacto lineal, así que los programas óptimos se vuelven más cargados al inicio para el mismo . La estructura cualitativa (parámetro de urgencia, frontera eficiente) sobrevive; la fórmula sinh no.
Resiliencia y el LOB — Obizhaeva y Wang (2013). "Optimal trading strategy and supply/demand dynamics" (Journal of Financial Markets 16(1), 1–32; el working paper circuló desde 2005) reemplaza la dicotomía temporal/permanente por un libro de órdenes límite con profundidad finita e impacto que decae exponencialmente: tu operación consume el libro, y el libro se repone a una tasa de resiliencia . El supuesto de AC de que "el impacto temporal se desvanece instantáneamente" es el límite . La estrategia óptima cambia de forma drásticamente: un bloque al inicio, un bloque al final, y una tasa de negociación constante entre medias; los bloques explotan la recuperación del libro. Si el intervalo de tu orden hija es comparable al tiempo de reposición de la plaza (en cripto, de segundos a un minuto —a menudo lo es—), estás en territorio de Obizhaeva-Wang, no de Almgren-Chriss, y el ajuste de en la calibración de arriba es tu señal de advertencia.
No-arbitraje dinámico — Gatheral (2010). "No-dynamic-arbitrage and market impact" (Quantitative Finance 10(7), 749–759) pregunta qué combinaciones de función de impacto instantáneo y kernel de decaimiento son internamente consistentes, es decir, no admiten una estrategia de ida y vuelta con costo esperado negativo. Los resultados son tajantes: el decaimiento exponencial es compatible solo con impacto lineal —combinar decaimiento exponencial con impacto de raíz cuadrada permite que el modelo sea explotado mediante una secuencia de operaciones oscilante—; el impacto no lineal requiere decaimiento en ley de potencias, con una desigualdad que vincula ambos exponentes (para impacto y decaimiento , aproximadamente ). Este es el paper que hay que leer antes de acoplar un kernel de decaimiento sofisticado a tu modelo de impacto: la mayoría de las combinaciones ad hoc son secretamente arbitrables, lo que en la práctica significa que tu optimizador descubrirá el arbitraje y producirá programas oscilantes absurdos. Si tu trayectoria "óptima" alterna compras y ventas durante una liquidación pura, has violado a Gatheral, no descubierto alfa.
Las secuelas de ML. Dos hilos de esta serie se construyen directamente sobre este andamiaje. Primero, la ejecución con aprendizaje por refuerzo: una vez que admites que el impacto es no lineal, dependiente del estado y parcialmente observable, la forma cerrada desaparece, y el RL (desde el paper de Q-learning de Nevmyvaka, Feng y Kearns de 2006 en adelante) es una forma natural de explorar el espacio de trayectorias, pero todo agente de ejecución con RL serio se compara contra la solución de AC, y a menudo se inicializa a partir de ella. Segundo, los modelos neuronales de impacto de precio: reemplazar por un funcional aprendido del estado del libro de órdenes, manteniendo por encima la lógica de programación de media-varianza. Ninguna de las dos secuelas tiene sentido sin conocer exactamente cuál es la línea base lineal-gaussiana y por qué tiene la forma que tiene.
Qué llevarse
El modelo son tres supuestos, una ecuación en diferencias y un número. Impacto permanente lineal (forzado por la ausencia de manipulación), impacto temporal lineal (una aproximación en torno a la cual calibras), ruido browniano aritmético. El óptimo pone en la balanza la varianza de inventario contra el costo de impacto, toda la estrategia colapsa en la urgencia , y TWAP surge como el caso degenerado , que es el hecho individual más útil del modelo, porque convierte "¿deberíamos hacer TWAP con esto?" de una cuestión de hábito en una afirmación verificable sobre aversión al riesgo. La calibración es el trabajo real: es fácil, es un juego de factor 2, es un proyecto de investigación, y la trayectoria perdona los tres. Impleméntalo en una tarde; dedica el resto del mes a los diagnósticos de la regresión.
Referencias
- Almgren, R., Chriss, N. (2001). "Optimal execution of portfolio transactions." Journal of Risk, 3(2), 5–39.
- Almgren, R. (2003). "Optimal execution with nonlinear impact functions and trading-enhanced risk." Applied Mathematical Finance, 10(1), 1–18.
- Almgren, R., Thum, C., Hauptmann, E., Li, H. (2005). "Direct estimation of equity market impact." Risk, 18(7), 58–62.
- Huberman, G., Stanzl, W. (2004). "Price manipulation and quasi-arbitrage." Econometrica, 72(4), 1247–1275.
- Obizhaeva, A., Wang, J. (2013). "Optimal trading strategy and supply/demand dynamics." Journal of Financial Markets, 16(1), 1–32.
- Gatheral, J. (2010). "No-dynamic-arbitrage and market impact." Quantitative Finance, 10(7), 749–759.
- Nevmyvaka, Y., Feng, Y., Kearns, M. (2006). "Reinforcement learning for optimized trade execution." ICML 2006.
Authors
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.