Como o estudo foi construído
O suficiente para checar um número da nota à mão, ou para reconstruir um estudo equivalente com os dados públicos da Binance. Todas as entradas são gratuitas e públicas.
Dados
Três fontes públicas, todas vindas do arquivo público da Binance e do seu endpoint de métricas, cobrindo os contratos perpétuos USD-M. Nada pago, nada proprietário.
| Entrada | Granularidade | Serve para |
|---|---|---|
| Candles | 1 minuto | preço, retornos, volatilidade, volume |
| Índice de prêmio | 1 minuto | variáveis de base |
| Financiamento realizado | por evento | fluxo de caixa na barra da liquidação |
| Contratos em aberto | 1 hora | variáveis de posicionamento |
As quatro entradas são públicas e gratuitas. O financiamento é um fluxo de caixa e não uma variável explicativa, porque um modelo que vê o próprio financiamento pode aprender a operar em volta da liquidação.
Duas peculiaridades do arquivo precisam de tratamento, sem o qual o painel desalinha silenciosamente. Os arquivos de candles mudam de convenção de cabeçalho no meio do histórico, e um período antigo marca o tempo em microssegundos em vez de milissegundos. Os dois são normalizados na leitura.
A cobertura dos contratos em aberto é um teto duro, não um limite de coleta. A plataforma só publica as métricas do segundo maior contrato a partir de dezembro de 2021, e datas anteriores devolvem ausência para a maioria dos contratos. Uma extensão retroativa sobre os 354 contratos rodou 148 minutos e acrescentou zero linhas. É por isso que a comparação de variáveis fica restrita às janelas mais recentes.
Universo
354 contratos, dos quais 56 foram retirados antes do fim da amostra e ficam mantidos nas datas em que de fato foram negociados. A seleção é feita na data: em cada virada de mês, os contratos são ordenados pelo volume médio diário em dólares dos 30 dias estritamente anteriores, e os 40 primeiros ficam elegíveis.
Os filtros de elegibilidade entram antes da ordenação, de modo que não podem depender de um resultado. A moeda de cotação tem de ser USDT, contratos com vencimento são excluídos, e subjacentes que não são cripto também.
# history eligibility is tested on genuinely traded volume, not file existence,
# because the archive pads a contract's file after its last real trade
traded = bars.filter(pl.col("quote_volume") > 0)
last_real = traded["open_time"].max()
bars = bars.filter(pl.col("open_time") <= last_real)Observação e ação
Uma decisão por hora. A ação é um conjunto de 20 números entre menos um e mais um, um por vaga, lidos como a fração da conta que se quer manter naquele contrato. Positivo é comprado, negativo é vendido, e vender a descoberto é permitido porque são contratos perpétuos.
Quatro regras se aplicam depois, nesta ordem. Qualquer coisa entre menos e mais dois centésimos vai para zero exato, para que não ter posição seja uma escolha alcançável e não um equilíbrio instável. Cada contrato é limitado a um quarto da conta. Se a soma dos pesos em valor absoluto passa de um, o conjunto todo é reduzido proporcionalmente: não há alavancagem. Vagas com um contrato que não está sendo negociado naquela hora são forçadas a zero.
A observação traz, por contrato, as suas colunas de variáveis mais três grandezas do próprio agente: o peso que ele carrega, se está no lucro ou no prejuízo naquela posição, e há quanto tempo a carrega. Depois vêm doze grandezas no nível da conta: exposição bruta e líquida, queda desde o topo, logaritmo do patrimônio, número de contratos ativos e carregados, avanço dentro do episódio, resultado realizado, tamanho médio das operações, custo e financiamento acumulados, e o teto bruto.
As posições são reduzidas à força a mercado se a exposição bruta passar de uma vez e meia o teto, e um episódio termina se o patrimônio cair abaixo de um quarto do valor inicial.
Custos e financiamento
Os custos entram dentro da recompensa em vez de serem descontados depois, o que importa porque as duas recompensas por operação ficariam cegas a eles, e uma recompensa por operação cega a custos ensina comprovadamente a operar em excesso.
fee = taker_fee * traded_notional # 5 bp per fill
slippage = (base_slip + impact_k * sigma * sqrt(participation)) * traded_notional
participation = traded_notional / max(interval_dollar_volume, 1.0) # capped at 5%
funding = position_notional * funding_rate # on the true settlement bar onlyO termo de impacto é escalado pela volatilidade realizada do contrato, e não só pela participação. Sem o termo de volatilidade a expressão fica dimensionalmente errada: uma versão anterior cobrava 2,3% de deslizamento numa operação de dez mil dólares no maior contrato do universo.
A participação é medida contra um tamanho de conta fixo em vez de frações do patrimônio, porque senão o teto nunca morde. Operações abaixo de um milionésimo da conta são canceladas, o que impede que o ruído de arredondamento em precisão simples seja registrado como cerca de mil operações fantasma por execução.
| Parâmetro | Valor |
|---|---|
| Taxa de tomador | 5 pb por execução |
| Deslizamento base | 2 pb por execução |
| Impacto | raiz quadrada da participação, escalada pela volatilidade realizada |
| Teto de participação | 5% do volume em dólares do intervalo |
| Financiamento | taxa realizada, aplicada uma vez na barra do evento |
| Conta | 100.000 USD, fixo |
Parâmetros de custo. Todos são cobrados em cada execução, sobre o tamanho real da posição.
Validação deslizante
Vinte janelas deslizantes. Cada uma toma 18 meses de treino, 3 de validação e 3 de teste, com uma purga de 7 dias e um embargo de um dia em cada fronteira. O tamanho da purga é o histórico mais longo exigido por uma variável, de modo que nenhuma linha de treino possa ver uma barra que se sobreponha a um alvo de teste.
A normalização é ajustada só nas linhas de treino. O ponto de checagem é escolhido pelo patrimônio de validação em quatro segmentos do treino, nunca pelo teste. O teste é tocado uma vez por configuração.
A triagem usou as janelas 1, 3, 5, 8, 11, 14, 17 e 19. O período fora da amostra vai de 17 de novembro de 2021 a 17 de agosto de 2026.
# features from completed bar t -> decision after bar t -> fill at bar t+1
# a rotation into a slot whose contract changed must NOT settle against the
# previous contract's price, which is the single worst defect found in build
rotated = (sym_id[t+1, k] != sym_id[t, k]) or (not live[t+1, k])
step_ret = 0.0 if rotated else (px[t+1, k] / px[t, k] - 1.0)Desenhos de recompensa
Seis desenhos, iguais em todo o resto. Dois só falam quando uma posição fecha, quatro falam a cada hora.
| Desenho | Paga por | Quando |
|---|---|---|
| Resultado líquido por operação | o resultado realizado depois dos custos daquela operação | no fechamento |
| Sinal de cada operação | mais um por fechamento no lucro, menos um por fechamento no prejuízo | no fechamento |
| Retorno logarítmico | a variação horária do valor da conta | a cada hora |
| Retorno menos a queda máxima | o mesmo, descontada uma penalidade pela profundidade abaixo do topo | a cada hora |
| Índice de Sharpe direto | Sharpe diferencial, atualizado continuamente | a cada hora |
| Retorno escalado pela volatilidade | o retorno horário dividido pela volatilidade recente | a cada hora |
Os seis desenhos. Os dois primeiros correspondem à especificação original e são liquidados líquidos das taxas, do deslizamento e do financiamento daquela operação.
Agentes de referência
Sete, rodados no mesmo ambiente e no mesmo modelo de custos. Não ter posição, comprar e manter o universo, comprar e manter um único contrato, peso igual, uma regra de seguir tendência, uma regra de reversão à média, e um agente aleatório.
O que importa é o agente aleatório. Ele é casado com a rotação medida da política contra a qual é comparado, mantendo a sua carteira aleatória pelo número de passos que reproduz aquela intensidade de negociação. Comparar uma política com uma posição comprada e mantida a favoreceria, porque ela paga muito mais custo. Também é gerada uma escala de agentes aleatórios com vários prazos de permanência, para que cada política seja julgada contra o degrau mais próximo da sua própria rotação.
Não ter posição fica mantido mesmo que esse agente nunca opere. Não fazer nada aqui é uma estratégia legítima, e desqualificá-lo por falta de operações eliminaria justamente a referência que detecta um agente que aprendeu a ficar de fora.
Inferência
A estatística registrada é o percentil 25 do retorno ajustado ao risco fora da amostra agregado entre os pontos de partida aleatórios, e não a mediana. Ordenar pela mediana deixaria apresentar como vantagem um resultado carregado por três partidas de sorte em cinco, que é exatamente o que aconteceu uma vez neste estudo e foi interceptado por essa regra.
A contagem de tentativas para qualquer correção de multiplicidade é o número de configurações distintas, nunca o número de execuções. Pontos de partida de uma mesma configuração não são tentativas independentes. As séries fora da amostra agregadas são pontuadas diretamente; números por janela nunca são promediados num número principal, e a quantidade de janelas nunca é multiplicada numa contagem de estratégias.
A probabilidade de sobreajuste do backtest é calculada por validação cruzada combinatória simétrica sobre a família de configurações. O erro por família é controlado por Holm e por Benjamini-Hochberg. O Sharpe deflacionado usa a contagem de configurações distintas como número de tentativas.
# per-window fractions each restart at 1.0, so they must not be summed across
# windows. The MEAN across windows is additive and the parts reconcile; a
# median of each column separately does not sum to the median net.
gross = mean(w.total + w.cost - w.funding for w in windows)
cost = mean(w.cost for w in windows)
net = mean(w.total for w in windows) # gross - cost + funding == netControles
Eles rodam antes de qualquer leitura de resultado, e uma falha interrompe o estudo em vez de virar uma nota de pé de página.
| Controle | O que ele intercepta |
|---|---|
| Invariância ao truncamento | uma variável que olha adiante, exigindo que o histórico truncado reproduza o histórico completo |
| Alvo plantado | um aparato incapaz de detectar vazamento mesmo quando se injeta um |
| Fidelidade do núcleo | desvio do simulador rápido contra uma implementação lenta e independente |
| Invariantes de contabilidade | dezessete verificações, entre elas um índice de peso igual calculado à mão |
| Identidade de amostra | ramos de uma mesma comparação apoiados em linhas diferentes |
| Degeneração | uma execução que é só um agente de referência com nome de política |
| Integridade da reserva | uma janela reservada tocada por alguma execução |
| Reprodutibilidade | uma reexecução que não reproduz exatamente os números registrados |
Cada controle roda antes de qualquer leitura de resultado, e uma falha interrompe o estudo. Um teste que não pode falhar não é controle: por isso o teste com alvo plantado tem de melhorar de forma clara, e se não melhorar é o próprio aparato que está quebrado.
O controle de contabilidade foi o que justificou o seu lugar. Ele interceptou um preço fictício usado para liquidar uma rotação para uma vaga cujo contrato havia mudado, o que anunciava um retorno multiplicado por 31 onde a verdade calculada à mão era 1,46.
Execuções e desvios
219 execuções, nenhuma falha. A comparação de recompensas está equilibrada em cinco pontos de partida aleatórios por desenho; um desenho foi depois levado a quarenta para o experimento de custos, e essa extensão é apresentada em separado em vez de misturada numa tabela de cinco partidas.
Três desvios do plano ficam registrados. Duas das vinte janelas reservadas foram usadas na triagem, o que as consumiu e deixou a reserva com duas janelas intactas. A comparação de variáveis foi refeita num conjunto de janelas mais recente logo que ficou claro que a cobertura dos contratos em aberto, de outro modo, apoiaria os seus ramos em amostras diferentes. E uma grade secundária de cadência foi abandonada porque representava cerca de 26 horas para um orçamento de quatro, então a questão da cadência é tratada só pelos agentes de referência, o que é evidência mais fraca do que uma grade de políticas.
Um número publicado foi corrigido depois. Uma primeira versão da decomposição de custos somava as colunas do registro de operações tanto entre janelas como entre pontos de partida, o que conta em dobro e soma frações de capital que reiniciam cada uma em um. O registro é primeiro a entrar, primeiro a sair e só guarda o realizado: a sua soma difere legitimamente do retorno total pela marcação do estoque ainda aberto no fim da janela. As decomposições agora vêm dos acumuladores do simulador.

