Quatro números, todos medidos na nossa própria stack de paper trading, uma estratégia, sinais de 1-minuto no perpétuo BTCUSDT, 31 dias e 1,418 ordens. Tempo mediano desde o fecho da barra até ao ack da ordem pela exchange: 195 ms. Percentil 99: 2,970 ms. Mediana no 1% dos minutos com a volatilidade realizada mais alta: 840 ms. E a constante de latência que esteve na configuração do backtest durante a maior parte do ano anterior: 0.
Três destes números são aborrecidos. Toda a gente sabe que o zero está errado, toda a gente acaba por substituí-lo por qualquer coisa, e essa qualquer coisa é quase sempre uma constante que alguém escolheu a olho. 100 ms, 250 ms, «vamos ser conservadores, 500». O número que realmente importa é 840, porque diz que a latência é uma função do estado do mercado, e os estados de mercado em que rebenta são os mesmos em que o sinal julga ter encontrado alguma coisa.
Para onde vão os 195 milissegundos
Aqui está o orçamento completo, medido etapa a etapa com timestamps de relógio monotónico escritos em cada salto. A máquina é uma instância AWS em ap-northeast-1, a mesma região dos endpoints da venue, que é praticamente o melhor que um setup de retalho sem colocation consegue.
| Etapa | p50 | p95 | p99 |
|---|---|---|---|
| Fecho da barra → evento WS kline-closed recebido | 128 ms | 402 ms | 1,940 ms |
| Parse + atualização do estado das features | 3 ms | 9 ms | 28 ms |
| Decisão: eval do modelo + verificações de risco | 11 ms | 22 ms | 60 ms |
| Submissão da ordem → ack da exchange | 47 ms | 180 ms | 1,100 ms |
| Ponta a ponta (medido, não somado) | 195 ms | 690 ms | 2,970 ms |
Duas coisas a notar antes da parte interessante. Primeiro, as medianas não somam: 128 + 3 + 11 + 47 = 189, e a mediana ponta a ponta medida é 195. Os percentis não são aditivos, e se construir a estimativa de latência somando os p99 de cada etapa obtém 3,128 ms quando o p99 real é 2,970. Aqui chega a ser próximo, absurdamente errado quando as etapas estão correlacionadas na outra direção. Meça o percurso completo.
Segundo, olhe para a linha de cima. Dois terços do orçamento mediano gastam-se à espera que a exchange lhe diga que a barra fechou. Não é a rede, não é o código, não é o encaminhamento de ordens. A venue agrega os trades numa kline, decide que o minuto acabou, e faz o push. Esse push tem uma fila à frente, e a fila é partilhada com todos os outros subscritores daquele stream.
Os 840
Agrupe cada uma dessas 1,418 ordens pela volatilidade realizada do minuto que produziu o sinal, e a distribuição desagrega-se.
| Classe de volatilidade da barra do sinal | Ordens | p50 ponta a ponta | p99 ponta a ponta |
|---|---|---|---|
| Quartil inferior | 201 | 141 ms | 488 ms |
| Dois quartis do meio | 796 | 183 ms | 1,210 ms |
| Quartil superior | 407 | 312 ms | 2,880 ms |
| Top 1% de todos os minutos | 14 | 840 ms | 4,100 ms |
Todas as etapas degradam em conjunto, e é por isso que o efeito se acumula. A fila de agregação e broadcast da venue é alimentada pela carga do matching-engine, por isso um minuto com 11,000 trades faz o push mais tarde do que um minuto com 600. O próprio processo está a lidar com mais atualizações de depth e mais trade prints na mesma janela, por isso a atualização de features que normalmente leva 3 ms passa a levar 28. O caminho REST da ordem compete com o pânico de toda a gente, e começa a aparecer o ocasional 503-and-retry que por si só custa um segundo inteiro. O coeficiente de correlação entre o número de trades da barra do sinal e a latência ponta a ponta na nossa amostra foi 0.41. Não é enorme. Chega para arruinar uma constante.
E as estratégias que sobrevivem a um primeiro crivo são, esmagadoramente, as que negociam ruturas, reversões, dislocações, cascatas de liquidação. Disparam exatamente na classe em que a latência é pior. Um sinal de momentum que só dispara quando o minuto se moveu 40 bps é um sinal que se organizou, estruturalmente, para ser sempre servido pela versão mais lenta da stack.
Uma vez passámos um dia a perseguir um outlier de 3.6 segundos que aparecia mais ou menos à mesma hora todas as noites. Era o logrotate, a fazer copy-truncate de um ficheiro de 2 GB no mesmo volume EBS para onde o processo estava a escrever, a bloquear o event loop. O mercado era inocente. Meça primeiro, só depois culpe a microestrutura.
O que isso faz a um fill
O preço deriva enquanto se espera. Num minuto aleatório, o movimento adverso médio ao longo da nossa mediana de 195 ms foi cerca de 1.1 bps contra a direção do sinal. Nos minutos do quartil superior a 312 ms, 5.8 bps. Nos minutos do top-1% a 840 ms, cerca de 14 bps de deriva adversa antes de a ordem sequer chegar ao book.
Nos perps de BTC o spread quase não participa nisto: um tick numa cotação de 84,000 é 0.012 bps, por isso o dano é deriva adversa e não custo de crossing. Passe a mesma estratégia para um perp mid-cap e o segundo termo acorda — o spread no topo do book na nossa amostra passou de 1.4 bps em minutos calmos para 6.2 bps no quartil superior, e a depth nesse touch adelgaçou-se para menos de metade. Paga a deriva, o spread mais largo e a pior depth, tudo ao mesmo tempo, tudo porque partilham uma causa.
Quando fizemos replay dos mesmos 31 dias com latência condicional por barra em vez de 200 ms fixos, o preço médio de entrada do backtest piorou 3.1 bps, concentrado quase por completo em 9% das trades. A divergência de preço de entrada entre backtest e paper caiu de 4.2 bps para 0.9 bps. O Sharpe in-sample dessa estratégia passou de 1.7 para 1.1, que é o número honesto e sempre o foi.
Então o que é que se mete realmente na config
Não um escalar. O que corremos agora:
- Amostre, não fixe. Sorteie a latência de cada ordem simulada a partir de uma distribuição empírica condicionada à atividade da própria barra — o número de trades e o retorno absoluto chegam, e ambos já estão numa kline standard. Ajuste uma vez por venue e por tipo de ordem, reajuste mensalmente.
- Mantenha a cauda. Não ajuste uma lognormal e dê o assunto por encerrado; o p99 vive em tempestades de retry e pausas de GC que nenhuma distribuição suave reproduz. Usamos os quantis empíricos com uma cauda reamostrada acima do p95.
- A latência também se aplica aos cancels. Um stop-replacement ou um cancel de hedge está sujeito ao mesmo atraso, e em mercados rápidos o cancel está numa corrida contra um fill. Se o simulador cancelar de imediato, o modelo de risco é ficção.
- Acrescente um staleness guard e simule-o. O nosso descarta qualquer sinal cuja barra tenha mais de 1.5 segundos à chegada. É um comportamento real com um custo real — remove trades em silêncio, e tem de remover as mesmas no backtest.
- Recalibre contra o paper. O paper trading é onde se obtém a ground truth. Registe os quatro timestamps de salto em cada ordem e faça o diff das distribuições contra o que o simulador assumiu, semanalmente.
Outras classes de ativos, a mesma doença. Ações dos EUA: o consolidated tape acrescenta o seu próprio atraso de agregação ao do broker, e alarga-se sob pressão de message-rate pela mesma razão estrutural. Mercados de previsão: entrada de ordens só por REST na maior parte das venues, e medimos medianas sub-100 ms que passam a vários segundos quando um evento de resolução chega e toda a gente aparece ao mesmo tempo. Opções de cripto: a cotação a partir da qual está a fazer o pricing pode estar dois segundos desatualizada no minuto exato em que o subjacente se moveu.
A forma geral do erro é tratar um parâmetro de execução como uma constante independente quando é uma função da mesma variável de estado que conduz o sinal. As fees comportam-se; o funding comporta-se; a latência não. Se for para ficar com um número na cabeça de tudo isto, fique com o rácio: o nosso p99 é quinze vezes a nossa mediana, e o sinal chega desproporcionalmente perto do extremo do p99.
Instrumente os quatro saltos. É uma tarde de trabalho e vai dizer-lhe mais sobre a estratégia do que outra semana de pesquisa de parâmetros.
← Todos os artigos


