Pular para o conteúdo principal
engenhariaponto flutuanteintegração de api
Ponto Flutuante Vai Te Custar Dinheiro: Precisão em Sistemas de Trading

Ponto Flutuante Vai Te Custar Dinheiro: Precisão em Sistemas de Trading

Arredondamento parece um problema resolvido. Você tem um número, tem um step size e arredonda para o valor válido mais próximo. Uma função utilitária, vinte linhas, resolvido para sempre.

Não é uma função só. Em um sistema que envia ordens para várias exchanges, precisão é um domínio inteiro, e os bugs que ele produz compartilham uma propriedade desagradável: eles não lançam exceção. Eles produzem uma ordem levemente errada, ou uma ordem que é rejeitada por um motivo que o usuário não consegue ver.

Veja como esse trabalho realmente é, usando três problemas reais do nosso fluxo de ordens.

Pontos-chave

  • O IEEE-754 torna o alinhamento de step não confiável: escalar 0.29 por 100 resulta em 28.999999999999996, que o piso reduz para um valor um step inteiro menor.
  • Três exchanges impõem três regras incompatíveis de ordem mínima. Uma verifica o valor em USDT, outra verifica a contagem de contratos, outra verifica ambos.
  • Nosso pior bug nessa área veio de inventar uma regra que uma exchange não tem e então rejeitar ordens válidas com base nela.
  • Nunca sintetize um limite que a API não forneceu. Se uma exchange não tem mínimo nocional, calcular um a partir de outros campos é invenção, não cautela.
  • Metadados ausentes da exchange devem gerar um erro, não recorrer a um valor padrão. Um padrão errado produz ordens erradas silenciosamente.

1. O problema aritmético: arredondar move o seu número

Comece pelo caso mais simples, porque é o que as pessoas presumem ser seguro.

Uma exchange aceita quantidades em múltiplos de um step size. Você tem 0.29 e um step de 0.01. Esse valor já é válido, então aplicar o piso deveria não mudar nada. A implementação padrão escala para cima, aplica o piso e escala de volta para baixo:

0.29 / 0.01 = 28.999999999999996   ← IEEE-754, not 29
floor(...)  = 28
28 × 0.01   = 0.28

Um valor que já estava alinhado desceu um step inteiro, silenciosamente, na direção de uma ordem menor. Sem exceção, sem aviso, e isso só acontece para certas combinações de valor e step, que é justamente o que torna difícil de perceber.

A correção é um epsilon aplicado durante a escala, grande o suficiente para absorver o ruído de representação, mas pequeno o suficiente para que um valor genuinamente abaixo como 0.289999 ainda tenha o piso aplicado corretamente. Escrevi sobre a descoberta disso em o que eu subestimei ao construir esta plataforma; o que importa aqui é a categoria a que isso pertence.

Porque, assim que você aceita que a aritmética sobre quantidades não é confiável, começa a olhar para tudo o mais que toca uma quantidade. É aí que estão as surpresas de verdade.

2. Três exchanges, três mínimos incompatíveis

Toda exchange impõe uma ordem mínima. Você esperaria que a regra diferisse no seu valor, não no seu formato. Ela difere no formato:

ExchangeRegra
Binanceqty × price >= minNotional (uma verificação de valor em USDT)
Bybitqty >= minOrderQty e notional >= minNotional (duas verificações)
OKX perpétuosqty >= minSz (contagem de contratos, sem nenhuma verificação de nocional)

Leia essa tabela como três perguntas diferentes sendo feitas sobre a mesma ordem. A Binance pergunta quanto ela vale. A OKX pergunta quantos contratos ela é. A Bybit pergunta as duas coisas, e uma ordem pode satisfazer só uma delas e ainda assim ser rejeitada.

Há uma complicação adicional na OKX: as quantidades estão em contratos, não em moedas, então um tamanho de contrato precisa converter entre os dois antes que qualquer disso faça sentido. Uma quantidade que significa "0.5 BTC" em uma exchange significa "50 contratos" em outra.

A consequência arquitetural pela qual optamos: cada exchange valida as próprias ordens. O motor de estratégia não contém um único condicional sobre com qual exchange está falando. Ele entrega a ordem para um adaptador, e o adaptador aplica as próprias regras. Toda vez que tentamos unificar essas regras atrás de uma única abstração, a abstração virava uma mentira no momento em que um quarto caso aparecia.

3. O caro: inventar uma regra que não existe

Este é o bug que eu mais gostaria que outro engenheiro evitasse, porque o erro se parece com diligência.

Os perpétuos da OKX não têm mínimo nocional. Nós não sabíamos disso. Raciocinando por analogia com as outras exchanges, nós calculamos um: contagem mínima de contratos vezes tamanho do contrato vezes preço. Parecia razoável. Produzia um número plausível em USDT. E era ficção.

Onde isso quebrou é específico e vale a pena detalhar. Uma estratégia DCA coloca as ordens de segurança abaixo do preço de entrada. Nosso mínimo sintético era calculado a um preço e depois comparado com ordens de preço mais baixo, então a mesma quantidade que passava no topo da escada falhava mais abaixo.

O resultado: ordens de exatamente 0.1 contratos, que é o mínimo da exchange e teriam sido aceitas, rejeitadas pelo nosso próprio validador antes mesmo de saírem do nosso sistema. A exchange nunca as viu. Do lado do usuário, a estratégia simplesmente não fez o que estava configurada para fazer.

A lição vale muito além do trading:

Não sintetize uma restrição que a API não forneceu. Se uma exchange expõe um mínimo em contratos, o mínimo é em contratos. Derivar um equivalente baseado em valor não é ser cuidadoso, é inventar uma regra e depois aplicá-la contra o usuário.

O sinal, em retrospecto, era que o nosso valor calculado dependia do preço, enquanto a regra real da exchange não dependia. Sempre que a sua versão de uma restrição tem uma entrada que a real não tem, você construiu algo diferente do que pretendia modelar.

4. Metadados ausentes devem ser um erro, não um valor padrão

A última peça é uma política, não um bug, e é o que impede os três primeiros de voltarem a acontecer silenciosamente.

Toda exchange nos dá metadados por símbolo: step size, tick size, quantidade mínima, tamanho do contrato. Qualquer um deles pode estar ausente, e o movimento tentador é um fallback. Step size ausente? Assuma 0.001. Quantidade mínima ausente? Assuma zero e deixe a exchange decidir.

Nós fazemos o oposto: um campo obrigatório ausente lança exceção, imediatamente, antes de qualquer ordem ser construída. Nada mais adiante tem a chance de construir sobre um palpite.

O raciocínio é a assimetria de consequências. Um erro lançado é uma falha barulhenta que aparece na hora, diante de alguém que pode corrigi-la. Um valor padrão é uma resposta errada e silenciosa que envia uma ordem com o tamanho errado e se revela mais tarde, em dinheiro. Entre um erro que você não consegue ignorar e um número que você não consegue verificar, o erro é mais barato sempre.

Isso também significa que a camada de metadados é honesta sobre o que sabe. O código que lê um campo obrigatório recebe um valor real ou uma exceção. Ele nunca recebe um placeholder vestido com o formato de dados reais.

O que levar disso

  • Trate o alinhamento de step e tick como aritmética que pode mover o seu valor, não como formatação.
  • Não unifique regras que são genuinamente diferentes. A validação por exchange continua correta conforme exchanges são adicionadas; uma abstração compartilhada sobre regras incompatíveis, não.
  • Nunca derive um limite que a API não declarou. Se a sua versão de uma restrição recebe uma entrada que a real não tem, é uma restrição diferente.
  • Falhe de forma barulhenta com metadados ausentes. Valores padrão convertem um erro visível em uma ordem invisível com preço errado.

O trabalho de precisão não tem glamour e nunca aparece em uma lista de recursos. Mas cada um desses bugs produziu uma ordem que estava errada ou uma ordem que nunca existiu, e em um sistema que opera em nome de alguém, isso é a mesma categoria de falha do que perder o dinheiro da pessoa diretamente.


Leitura Relacionada

Crie uma conta

Coloque seu conhecimento em prática e abra uma conta Freya hoje.

Registre-se agora
Este conteúdo é apenas para fins educacionais e não constitui aconselhamento financeiro. O trading de criptomoedas envolve riscos significativos. Nunca invista mais do que você pode se dar ao luxo de perder.