Almgren-Chriss Sem Rodeios: Execução Ótima que Você Consegue Implementar em uma Tarde
Almgren e Chriss (2001), "Optimal execution of portfolio transactions" (Journal of Risk 3(2), 5–39), é provavelmente o artigo mais citado em pesquisa de execução e um dos menos implementados corretamente. A maior parte do código "Almgren-Chriss" encontrado por aí é um agendador TWAP com um bloco de comentários. A maioria dos posts de blog sobre o tema pula a derivação, acena vagamente para uma função sinh e nunca toca na parte que realmente importa em produção: de onde vêm os parâmetros.
Este artigo faz o trabalho completo. As premissas do modelo, declaradas com honestidade. A trajetória de forma fechada, derivada, não apenas afirmada. A fronteira eficiente e como escolher o parâmetro de aversão a risco. E a calibração das três entradas — impacto temporário , impacto permanente , volatilidade — a partir de dados de livro de ofertas e trades da Binance, com Python funcional e uma discussão honesta sobre por que o ajuste é ruidoso. Esta é a espinha dorsal intelectual de tudo mais em execução: TWAP, VWAP e POV são casos especiais ou primos heurísticos (veja TWAP, VWAP, POV: os algoritmos de execução que todo mundo usa), e as abordagens modernas de ML — agendadores de aprendizado por reforço e modelos neurais de impacto — são tentativas de relaxar exatamente as premissas que estamos prestes a escrever.
A configuração: o que o modelo realmente assume
Você detém unidades de um ativo (digamos, 100 BTC) e precisa liquidar totalmente até o tempo . Divida em intervalos de comprimento . Sua variável de decisão é a trajetória de posição , com trades executados no intervalo .
Três premissas sustentam o modelo inteiro:
1. Passeio aleatório aritmético. O preço não perturbado segue
onde são normais padrão i.i.d. e é a volatilidade absoluta (dólares por unidade de tempo, não percentual). Aritmético, não geométrico — ao longo de algumas horas a diferença é desprezível e o passeio aritmético mantém a álgebra linear-quadrática. Sem drift: Almgren e Chriss discutem o termo de drift, mas para uma liquidação intradiária sua estimativa de alfa ao longo de 4 horas é quase sempre ruído, e zerá-la é o padrão honesto.
2. Impacto permanente linear. : negociar à taxa desloca o preço permanentemente, de forma proporcional, e o deslocamento nunca decai. Cada unidade que você vende empurra o mid para baixo em para sempre. Isso parece grosseiro, mas há uma razão profunda para mantê-lo linear: Huberman e Stanzl (2004) provaram que impacto permanente não linear no tamanho do trade admite estratégias de ida e volta com lucro esperado positivo — manipulação de preço. O impacto permanente linear não é uma simplificação por conveniência; é a única escolha livre de arbitragem nesta classe de modelos.
3. Impacto temporário linear. O preço que você realmente recebe no intervalo é
captura metade do spread bid-ask mais taxas; é a inclinação do custo marginal de demandar liquidez à taxa . O impacto temporário afeta apenas seu próprio fill e desaparece instantaneamente — o book se repõe totalmente antes da sua próxima ordem filha. Cada uma das três palavras "linear", "instantaneamente", "totalmente" está errada nos mercados reais, e a última seção trata de quão errada. Mas primeiro, o benefício de aceitá-las.
Custo e variância de uma trajetória
Some os fills, subtraia da marcação inicial , e você obtém o implementation shortfall. Sua esperança e variância sobre o ruído são:
com (uma correção de discretização que desaparece quando ).
Duas observações que a maioria das implementações não capta:
- O custo permanente não depende da trajetória. Com impacto permanente linear e sem decaimento, você paga o mesmo pedágio permanente independentemente de como agenda a execução. A otimização é inteiramente uma disputa entre o termo de custo temporário (que quer negociação lenta e uniforme — é minimizado por , ou seja, TWAP) e o termo de variância (que quer que seu estoque desapareça ontem — é minimizado pela liquidação imediata).
- O termo de variância é ponderado pelo estoque, não pelo trade. O risco se acumula sobre o que você ainda detém, , não sobre o que você negocia. É por isso que a urgência antecipa o agendamento.
A forma fechada: sinh, cosh e o parâmetro de urgência
Almgren-Chriss minimizam o objetivo média-variância
onde é a aversão a risco em unidades de 1/dólar. Iguale para os pontos internos. O termo temporário contribui com diferenças de segunda ordem de , o termo de variância contribui com em si, e você obtém uma equação de diferenças lineares de segunda ordem:
Esta é a análoga discreta de , e com as condições de contorno , a solução é hiperbólica:
onde resolve , que para pequeno é simplesmente .

Tudo sobre a estratégia se comprime em um único número:
é a urgência. Seu recíproco é o tempo característico do trade: a escala de tempo ao longo da qual vale a pena carregar risco de estoque para economizar custo de impacto. Note o que não depende: o tamanho da ordem e o prazo . Se a escala de tempo intrínseca do seu trade é 20 minutos ou 6 horas é determinado apenas por aversão a risco, volatilidade e liquidez. Se , seu prazo é irrelevante — o modelo liquida em seu próprio cronograma e a cauda do horizonte fica sem uso. Se , o prazo se torna vinculante e você está efetivamente fazendo TWAP.
O limite neutro a risco é o TWAP — é por isso que o TWAP existe
Tome , de modo que . Então e
A trajetória ótima degenera na linha reta: quantidades iguais em intervalos de tempo iguais. O TWAP não é uma heurística que por acaso funciona; é o ótimo exato do modelo Almgren-Chriss para um trader neutro a risco com impacto linear. Toda vez que alguém executa TWAP, está implicitamente afirmando : "não me importo com a variância do meu shortfall, apenas com sua média." Essa é uma posição defensável para ordens pequenas e horizontes curtos. É uma posição estranha para liquidar 5% do volume diário ao longo de 8 horas em um ativo com 4% de volatilidade diária, que é exatamente onde as pessoas o executam mesmo assim. O limite oposto dá para todo : descarregue tudo no primeiro intervalo, pague o que custar. Entre esses extremos, interpola: é quase indistinguível do TWAP, é agressivamente antecipado.
A fronteira eficiente de execução
Para cada você obtém uma trajetória, um custo esperado e uma variância . Varrendo traça-se uma curva no plano — a fronteira eficiente de execução, em analogia direta com Markowitz. Ela é convexa e decrescente: menos variância sempre custa mais shortfall esperado, com retornos rapidamente decrescentes. No ponto da fronteira selecionado por um dado , a aversão a risco é a inclinação (negativa) da tangente: .

A fronteira reformula a pergunta "qual é o ?" — que ninguém consegue responder introspectivamente — em "qual trade-off custo/risco eu quero?", que uma mesa de operações consegue de fato responder. Três formas práticas de escolher o ponto:
- Raciocínio de custo marginal. Percorra a fronteira e pergunte: "movendo deste ponto para o próximo, pago dólares de custo esperado para remover dólares de desvio-padrão do shortfall — vale essa troca?" O "joelho" da fronteira costuma ser óbvio dentro de um fator de 2, e a trajetória é insensível a nessa resolução.
- Orçamento de risco. Fixe um desvio-padrão máximo aceitável de shortfall (por exemplo, "o 1-sigma de um dia da posição residual deve ficar abaixo de 15 bps do notional") e tome a trajetória mais barata que o satisfaça. Este é um problema restrito cujo multiplicador de Lagrange é .
- Definição do tempo característico. Escolha diretamente ("esta ordem deve ter meia-vida de 90 minutos") e derive . É o que a maioria dos praticantes faz implicitamente ao definir um slider de "urgência".
Um exemplo trabalhado com números que justificaremos na seção de calibração. 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}$. Então
O agendamento ótimo vende 46,2 BTC na primeira hora (TWAP: 25). Custo temporário: \eta \int_0^T v(t)^2 dt \approx \3{.}290$2{.}500$53{.}000$69{.}300 do TWAP. Ou seja, por 0,8 bps extras de custo esperado sobre o notional de \10M, você corta o risco de shortfall em 23%. Esse é todo o conteúdo do modelo em uma frase: ele precifica o seguro e deixa você decidir se compra.
A trajetória em si é dez linhas 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}")
Varra lam sobre np.logspace(-8, -4, 50) e plote contra — essa é sua fronteira, e toda a superfície de decisão cabe em um único gráfico que você pode entregar a um comitê de risco.
Calibração: , , a partir de dados da Binance
É aqui que 90% das implementações morrem silenciosamente. A fórmula da trajetória é trivial; os parâmetros não são. Três grandezas, três problemas de estimação diferentes.

: o fácil
Volatilidade absoluta por raiz de hora, a partir dos retornos do preço mid. Use barras de 1 minuto e escale; em amostragem mais fina, o ruído de microestrutura (bid-ask bounce) enviesa a variância realizada para cima. Essa estimativa é sólida — vol é o único parâmetro que você vai acertar.
: impacto temporário a partir do book e dos trades
Duas rotas complementares. Rota A — percorrer o book L2. A partir de snapshots de profundidade, calcule o custo de uma varredura marketable hipotética de tamanho : o VWAP dos níveis consumidos menos o mid. Ajustar dá o impacto instantâneo por BTC. Para converter no baseado em taxa do modelo, é preciso assumir um tempo de reposição do book (da ordem de 10–60 s para BTCUSDT): negociar à taxa consome por ciclo de renovação, então . Essa suposição — reposição instantânea e total em um relógio fixo — é exatamente a fresta por onde entra Obizhaeva-Wang; mais adiante. Rota B — regredir o slippage realizado sobre a participação. Agrupe o fluxo agressivo (taker) em bins de 1 minuto; para cada bin, regrida o VWAP do lado taker menos o mid de abertura contra a taxa de volume taker. A Rota B mede o que o mercado de fato cobrou dos agressores; a Rota A mede o que o book em repouso cobraria agora. Quando divergem por um fator de 3, confie em B para o nível e em A para a forma intradiária.
: impacto permanente via regressão estilo Kyle
Regrida as variações de preço mid sobre o fluxo líquido taker assinado ao longo de janelas de 5 minutos:
Depois verifique que o impacto não reverteu uma janela depois — o componente que não reverte é "permanente" na sua escala de tempo de negociação. Este é, de longe, o mais ruidoso dos três.
Código funcional, usando apenas endpoints REST públicos da 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 produção, substitua a página única de aggTrades por alguns dias dos dumps históricos e rode a regressão de sobre milhares de bins, não um punhado.
Por que a calibração em cripto é ruidosa, e o que fazer a respeito
Rode a regressão de em dados reais e você vai obter um de alguns por cento e um coeficiente que se move por um fator de 2–5 entre dias. Isso não é um bug no seu código. As razões são estruturais:
- Endogeneidade. O fluxo responde ao preço tanto quanto o preço responde ao fluxo. Traders de momentum compram porque o preço subiu; uma OLS ingênua de retornos sobre fluxo capta essa reação e a atribui a impacto. A correção limpa é usar seus próprios fills (exógenos por construção) — que você só tem depois de negociar por um tempo.
- Concavidade. O impacto real é côncavo no tamanho — empiricamente próximo de raiz quadrada (Almgren, Thum, Hauptmann e Li, 2005, "Direct estimation of equity market impact", encontraram um expoente próximo de 0,6; a mesma concavidade é robusta em cripto). Ajustar uma reta a uma função côncava faz com que seus e dependam da faixa de tamanhos na amostra. Calibre nas taxas de participação que você realmente vai negociar.
- Fragmentação e liderança de derivativos. O BTCUSDT à vista na Binance é uma venue entre muitas, e a descoberta de preço frequentemente acontece nos perpétuos. Fluxo que você nunca vê move o preço sobre o qual você regride, inflando o termo de ruído.
- Dependência de regime. O medido na sessão asiática não descreve a abertura dos EUA; o impacto aproximadamente dobra quando a volatilidade dobra. Calibre por sessão, e reajuste pelo menos semanalmente.
O ponto a favor: a trajetória é tolerante. , então um erro de fator 2 em move apenas por , e a função de custo é plana perto do ótimo. Acertar dentro de um fator de 2 e dentro de uma ordem de grandeza já captura a maior parte da melhoria disponível sobre o TWAP. Precisão importa para estimativas de custo pré-trade; robustez basta para agendamento.
Onde o modelo quebra, e os artigos que o consertam
Almgren-Chriss é um andaime, e saber exatamente qual viga é estrutural diz a você qual extensão buscar.
Impacto não linear — Almgren (2003). "Optimal execution with nonlinear impact functions and trading-enhanced risk" (Applied Mathematical Finance 10, 1–18) refaz o programa com impacto temporário em lei de potência . Para o empiricamente favorecido — a lei da raiz quadrada — as trajetórias ótimas mudam de caráter: impacto côncavo pune surtos menos do que o impacto linear, então os agendamentos ótimos ficam mais antecipados para o mesmo . A estrutura qualitativa (parâmetro de urgência, fronteira eficiente) sobrevive; a fórmula sinh não.
Resiliência e o LOB — Obizhaeva e Wang (2013). "Optimal trading strategy and supply/demand dynamics" (Journal of Financial Markets 16(1), 1–32; o working paper circulou desde 2005) substitui a dicotomia temporário/permanente por um livro de ofertas com profundidade finita e impacto de decaimento exponencial: seu trade consome o book, e o book se repõe à taxa de resiliência . A premissa AC de que "o impacto temporário desaparece instantaneamente" é o limite . A estratégia ótima muda de forma dramaticamente: um bloco de trade no início, um bloco no final, e uma taxa de negociação constante no meio — os blocos exploram a recuperação do book. Se seu intervalo de ordem filha é comparável ao tempo de reposição da venue (em cripto, segundos a um minuto — frequentemente é o caso), você está em território Obizhaeva-Wang, não Almgren-Chriss, e o ajuste ad hoc de na calibração acima é seu sinal de alerta.
Não arbitragem dinâmica — Gatheral (2010). "No-dynamic-arbitrage and market impact" (Quantitative Finance 10(7), 749–759) pergunta quais combinações de função de impacto instantâneo e kernel de decaimento são internamente consistentes, isto é, não admitem estratégia de ida e volta com custo esperado negativo. Os resultados são precisos: decaimento exponencial é compatível somente com impacto linear — combine decaimento exponencial com impacto em raiz quadrada e o modelo pode ser explorado por uma sequência de trades oscilante; impacto não linear exige decaimento em lei de potência, com uma desigualdade ligando os dois expoentes (para impacto e decaimento , aproximadamente ). Este é o artigo a ler antes de acoplar um kernel de decaimento sofisticado ao seu modelo de impacto: a maioria das combinações ad hoc é secretamente arbitrável, o que na prática significa que seu otimizador vai descobrir a arbitragem e produzir agendamentos oscilantes absurdos. Se sua trajetória "ótima" alterna compras e vendas durante uma liquidação pura, você violou Gatheral, não descobriu alfa.
As sequências de ML. Duas linhas nesta série constroem diretamente sobre este andaime. Primeiro, execução por aprendizado por reforço: uma vez que se admite que o impacto é não linear, dependente do estado e parcialmente observável, a forma fechada desaparece, e RL (do artigo de Q-learning de Nevmyvaka, Feng e Kearns de 2006 em diante) é uma forma natural de buscar no espaço de trajetórias — mas todo agente sério de execução por RL é comparado contra, e frequentemente inicializado a partir de, a solução AC. Segundo, modelos neurais de impacto de preço: substituindo por um funcional aprendido do estado do livro de ofertas, mantendo a lógica de agendamento média-variância por cima. Nenhuma das duas sequências faz sentido sem saber exatamente o que é o baseline linear-gaussiano e por que ele tem a forma que tem.
O que levar
O modelo são três premissas, uma equação de diferenças e um número. Impacto permanente linear (forçado pela ausência de manipulação), impacto temporário linear (uma aproximação em torno da qual você calibra), ruído browniano aritmético. O ótimo troca variância de estoque por custo de impacto, a estratégia inteira se comprime na urgência , e o TWAP surge como o caso degenerado de — que é o fato mais útil do modelo, porque transforma "devemos fazer TWAP nisto?" de uma questão de hábito em uma afirmação verificável sobre aversão a risco. A calibração é o trabalho de verdade: é fácil, é um jogo de fator 2, é um projeto de pesquisa, e a trajetória perdoa os três. Implemente-o em uma tarde; gaste o resto do mês nos diagnósticos de regressão.
Referências
- 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.