Skip to main content

O problema

O spread de uma oportunidade é calculado sobre o topo do livro — o melhor preço de compra contra o melhor preço de venda:
Isso responde “qual o spread?” mas não responde “quanto capital cabe nele?”. Um spread de 3% que só existe em 40 USD de liquidez é ruído: na segunda unidade comprada o preço já subiu e o spread evaporou.

O cálculo

O spread realizável consome o livro de ofertas até preencher um alvo de volume e calcula o preço médio ponderado (VWAP) efetivamente pago e recebido:
  • na exchange de compra, consumimos os asks (é contra eles que se compra);
  • na exchange de venda, consumimos os bids.
O resultado sai no formato 3.1% @ $1k · 1.9% @ $5k · 0.8% @ $10k: o mesmo par, avaliado em três tamanhos de operação.

O campo depth

Oportunidades spot podem trazer um array depth, uma entrada por faixa:
Quando insufficientLiquidity é true, o effectiveSpread é o spread da liquidez que existia — não do notional pedido. No exemplo acima, os 0.78% referem-se aos 2.340quecouberam,na~oaos2.340 que couberam, não aos 10.000. É por isso que filledNotional* existe: sem ele, o número parece uma promessa que o livro não sustenta.

Quando depth não vem

O campo é opcional e some do payload quando não pôde ser calculado. Todo consumidor precisa tratar a ausência. Os motivos legítimos:
  • Fora do top-N. A listagem (GET /v1/arbitrage) só enriquece as N oportunidades de maior spread bruto — buscar o book de todas a cada tick multiplicaria o custo de rede. O detalhe (GET /v1/arbitrage/{id}) sempre calcula.
  • Cotação não suportada. Pares cotados fora de USD/stablecoins, BRL e EUR não têm como ter a faixa convertida.
  • Book indisponível. Falha ou timeout na consulta à exchange.
  • Streaming. O canal SSE GET /v1/realtime/arbitrage publica direto do motor de comparação e não passa pelo enriquecimento, então oportunidades vindas por ele nunca trazem depth.
  • Kill switch. DEPTH_ENABLED=false.

Moedas e conversão

As faixas são definidas em USD para serem comparáveis entre pares. Como o livro está na moeda de cotação, é a faixa que é convertida (alvo = faixa × taxa), não os preços nível a nível — e o VWAP volta para USD no final.
  • USD, USDT, USDC, BUSD, DAI, TUSD, FDUSD são tratadas como paridade 1:1 com o dólar. Um depeg de 0,3% desloca a faixa de “10k"para 10k" para ~9.970 de profundidade real; como o spread é uma razão, o número de manchete não muda.
  • BRL e EUR usam a cotação do serviço de câmbio. Ele não atualiza em fins de semana nem fora do horário comercial, então num boot frio de sábado pares nessas moedas podem ficar sem depth.
A taxa se cancela na razão entre os dois VWAP. Uma cotação desatualizada só desloca qual fatia do livro é amostrada — nunca distorce o effectiveSpread.

Profundidade publicada por exchange

O insufficientLiquidity reflete o book que a exchange devolveu. Algumas truncam: Quando o alvo não é atingido por esse limite e não por falta de liquidez real, bookExhausted vem true — a interface mostra “book truncado pela exchange” em vez de “liquidez insuficiente”.

Configuração

Custo de rede

Cada oportunidade exige dois livros (a perna de compra e a de venda). Três camadas mantêm isso barato:
  1. Corte top-N — de até 25 linhas para 5 na listagem.
  2. Cache de profundidade (20s) — absorve o poll de 10s do front-end, com singleflight colapsando requisições concorrentes idênticas.
  3. Cache de book (4min) — um dado par em uma dada exchange custa no máximo uma ida à rede a cada 4 minutos, independentemente de quantas oportunidades ou clientes o referenciem.
O enriquecimento é sempre não-fatal: qualquer falha resulta na ausência do campo, nunca em erro do request.