3 de Novembro de 2026 · execução

Fizemos paper trading de um trade de basis. Uma perna preencheu.

Fizemos paper trading de um trade de basis. Uma perna preencheu.

A função se chamava enter_pack. Isso já deveria ter sido o aviso. Um agente de pesquisa tinha escrito um cash-and-carry de BTC: quando o perpetual negociasse mais de oito basis points acima do spot, comprar o spot, vender o perp, ficar parado. O funding e o basis fariam o resto. O backtest preenchia as duas pernas no mesmo milissegundo, no mid, mais um tick de educação. A curva de equity era uma escada. Ninguém chamaria de empolgante. Esse era o argumento de venda.

Promovemos para paper numa segunda-feira.

Segunda-feira: o pacote que sempre preenchia

O primeiro sinal chegou às 11:17 UTC. O basis na tela era 9.4 bps, perp rico. No backtest, enter_pack retornava dois fills e um residual de zero. No paper, retornou um fill de spot de 0.40 BTC no ask, e um short em espera no perp que não negociou.

O spot tinha pago uma taxa taker de 4 bp e cerca de 1 bp de spread. O preço do perp que queríamos ainda estava no book, depois não estava. Vinte segundos depois, o basis cotado era 5 bps, porque tínhamos acabado de fazer o lift do spot. Quarenta segundos depois o perp preencheu, depois do movimento. Estávamos empacotados. Estávamos empacotados num basis que já não pagava entrar e sair.

O backtest teria registrado "+9.4 bps de carry, residual 0." O blotter tinha 4 bps de custo taker no spot, um long sem hedge de 40 segundos, e um gap de entrada de cerca de 5. Eu tinha uma amostra de um e, ainda assim, confiança demais na escada.

Terça-feira: abrimos a função

enter_pack recebia uma lista de pernas e um mid alvo. Fazia um loop, preenchia cada perna em mid * (1 ± slip) e gravava uma linha de portfólio. Não havia um segundo timestamp. Não havia um branch em que a perna dois não existisse. O teste unitário afirmava residual == 0.

Esse é o teste que te faz sentir adulto. Nunca vai falhar.

Quebramos o pacote em duas ordens com estado próprio: enviada, parcial, preenchida, cancelada. Incompleto virou um objeto de primeira classe. O replay histórico ainda não tinha um book de verdade, então trapaceamos. Atrasar o segundo fill com um sorteio da espera de segunda-feira (média de 26 segundos, uma cauda que eu não gostei) e deslocar o preço pelo que o spot fez naquela janela. Cru, mas a escada ganhou entalhes antes do almoço: pacotes que entravam a 3 bps, pacotes que nunca viravam pacotes.

Quarta-feira: três segundos, depois joga fora

Se a segunda perna não estiver montada dentro de três segundos, zera a primeira. Deveríamos ter começado por aí. Parece conservador até você precificar o aborto. Zerar significa que você cruzou o spot para entrar e cruza de novo para sair. Duas taxas taker, dois spreads, sem carry, sem funding. No replay reescrito, esses abortos não eram raros. Eles se agrupavam nas barras em que o basis parecia mais "real", que costumam ser as barras em que alguma coisa já está fluindo pelo spot.

Na noite de quarta-feira já tínhamos dezessete sinais de paper para discutir.

17sinais de paper em quatro dias
7pacotes com as duas pernas preenchidas
18 bpsmovimento posterior contra um long que sobrou
Backtest, segunda de manhãPaper, até quarta-feira
EntradaAs duas pernas no mid, um timestampSpot no ask; perp 5–40s depois, ou nunca
ResidualSempre 0Sem hedge em 10 de 17 sinais
Taxas num trade que funcionaDois takersDois takers, ou quatro se abortarmos
Motor do PnLBasis + fundingO que o spot fez enquanto esperávamos
Um pacote está completo quando as duas ordens estão preenchidas, ou é uma posição direcional para a qual você não dimensionou. Marque o residual contra o book ao vivo o tempo todo em que ele existir. Se você não consegue marcar, não consegue rodar a estratégia.

Um colega ainda tem um notebook que trata o pacote como um produto listado, BTC_CARRY, com uma "taxa de execução" fixa de 2 bp. Eu gostava daquele notebook. Produzia a mesma escada que o agente. Uma taxa é uma média. Órfãos são uma distribuição. Médias não são atropeladas numa hora fina; 0.40 BTC de spot sobrando, sim.

Quinta-feira: sobra, depois café

Três segundos abortavam quase tudo quando também estávamos esperando na fila. Afrouxamos o paper para quinze. Às 02:11 UTC de quinta-feira, o spot preencheu e o perp não. Eu ainda estava na fase de luto do "vai restar, vai levantar" quando fui fazer café.

02:11:08 preenchido BTCUSDT 0.40 compra
02:11:08 enviado BTCUSDT-PERP 0.40 venda, post-only
02:11:41 ainda ativa
02:26:00 mark do residual −18.1 bps

O BTC caiu 18 bps numa janela quieta. O residual era o trade inteiro. O funding, quando imprimiu, creditou um perp que não tínhamos. O spot só ficou ali sendo long. O caminho overnight do backtest original nunca continha uma posição de uma perna só, então nunca tinha feito a pergunta que fazemos a qualquer outro book: qual é a pior barra que essa sobra pode imprimir, e onde fica o stop de uma posição que não pretendíamos carregar?

Os sete pacotes completos também não foram heróis. Quando a segunda perna imprimiu, nosso próprio lift de spot já tinha pressionado o índice, e o basis de entrada estava 3–5 bps pior do que o snapshot que disparou o sinal. O limiar de oito bps era um número sobre uma tela.

O que pularíamos da próxima vez

Não entregaríamos um multi-leg como uma função única de fill. A mesma regra para um carry perp-spot, um calendar, ou a vertical de call que um agente envia como dois limits e depois comemora quando os dois "preenchem" no mid.

  1. Estado de ordem por perna, inclusive parciais. Trate o pacote como pai de dois tickets.
  2. Um timer máximo de órfão que de fato dispara, e um caminho de zeragem que paga dois spreads na saída.
  3. Mark-to-book no residual, com um stop dimensionado para a sobra. O net empacotado é o nocional errado.
  4. Nenhum carry creditado, nenhum funding acumulado, até as duas pernas estarem montadas. Incompleto é incompleto.
  5. Recuse mid-on-mid como padrão para qualquer coisa que tenha dois tickets. Se precisar chutar, chute pior na perna fina e mais tarde no segundo fill.

E não deixaríamos test_pack_is_atomic entrar no repo. Atômico é propriedade de um matching engine, não de uma função auxiliar. BTC spot e BTC perpetual são dois books que às vezes rimam. O paper é onde eles param de rimar, e é só por isso que a escada alguma vez foi interessante.

backtestingcash-and-carryexecuçãopaper tradingfuturos de cripto
← Todos os artigos