← O artigo
Reprodutibilidade

Cada número do artigo, e de onde ele veio

Método suficiente para conferir a aritmética na mão, ou para reconstruir um estudo equivalente a partir do arquivo público da Binance.

Venue, universo e datas

Apenas futuros perpétuos da Binance margens em USDT, com a seleção de universo datada de 30 de junho de 2022. Um contrato é elegível quando, usando apenas informação disponível naquele dia: for um perpétuo cotado em USDT e margeado em USDT; seu subjacente for uma única moeda, o que exclui os três contratos com índice como subjacente DEFIUSDT, BTCDOMUSDT e ALLUSDT; tiver negociado volume positivo genuíno em ao menos 80% dos dias calendário nos doze meses terminados na data de seleção, com sua primeira observação dentro de 7 dias do início dessa janela; tiver negociado em ao menos 55 dias em abril, maio e junho de 2022; e ainda estiver vivo na data de seleção, com seu último dia de volume positivo caindo dentro de 7 dias dela.

Essa última regra existe porque a contagem do trimestre sozinha não pega um contrato que morreu dentro da janela de ranqueamento. Quatro contratos passaram na contagem de dias mas não estavam negociando na data de seleção e ficam excluídos: ICPUSDT e SCUSDT foram suspensos em 16 e 17 de junho de 2022, enquanto DODOUSDT e AKROUSDT negociaram pela última vez em 27 de maio de 2022 e ainda assim marcaram 57 dias no trimestre.

Os contratos elegíveis são ranqueados pelo volume diário mediano em ativo de cotação entre 1 de abril e 30 de junho de 2022, que para perpétuos lineares já é notional em USDT. A peneira devolve 105 contratos, com o que a escada congelada toma os 10, 25, 50 e 100 primeiros e permanece estritamente aninhada, deixando 5 contratos de margem acima do maior degrau.

Pontos de referência na data de seleção: o rank 1 é BTCUSDT com $13,246M de notional diário mediano, o rank 10 XRPUSDT com $367M, o rank 25 AAVEUSDT com $160M, o rank 50 ENJUSDT com $46M e o rank 100 NKNUSDT com $16M.

eligible = (
    single_coin
    & (volume_days_12m / calendar_days_12m >= 0.80)
    & (first_obs <= window_start + timedelta(days=7))
    & (quarter_trading_days >= 55)
    & (last_positive_volume_day >= SELECTION_DATE - timedelta(days=7))
)
ranked = median_daily_quote_volume[eligible].sort_values(ascending=False)
rungs = {n: ranked.index[:n].tolist() for n in (10, 25, 50, 100)}

Existência de arquivo não é evidência de negociação, então a elegibilidade, o ranqueamento e o fim do histórico de um contrato usam todos volume negociado positivo, com o padding da exchange depois do último trade real descartado. Contratos que morrem depois da data de seleção continuam no universo e vão para caixa na sua última barra negociada.

A lista mestra de símbolos é o índice do arquivo e não o endpoint de contratos vivos, porque o endpoint vivo omite 333 contratos que desde então foram deslistados. Dois renames de ticker são stitchados depois de verificar a continuidade de preço na passagem, MATIC para POL em setembro de 2024 e EOS para A em maio de 2025, ambas migrações 1:1. Uma redenominação, em que o notional muda por um fator constante como nos contratos 1000X, não é um rename e nunca é stitchada.

Os dados dentro da amostra da primeira janela walk-forward cobrem julho de 2022 até junho de 2023, e então o período fora da amostra vai de 1 de julho de 2023 a 31 de julho de 2026, dando 37 meses calendário completos e não sobrepostos. Toda barra usada em qualquer lugar é posterior à data de seleção, e a data de seleção fica tão tarde de propósito: um universo escolhido pela liquidez de junho de 2022 e depois usado para estimar estatísticas de ranking sobre histórico anterior condicionaria essas estimativas a uma sobrevivência que não era conhecível na época.

Dados e calendário

Dois timeframes, 15 minutos e 1 hora, ambos na grade de relógio UTC, tratados como células separadas e nunca agrupados. As barras vêm do arquivo público da Binance para os 105 contratos: klines de 15 minutos e de 1 hora como séries primárias, klines de 1 minuto retidas apenas para reconstruir a ordem em que a máxima e a mínima de cada barra ocorreram, eventos de funding em seus timestamps reais de liquidação e klines de mark price para valoração de funding.

Os painéis nunca fazem forward fill de um preço ou de um retorno através de uma barra faltante. Um sinal cria uma ordem apenas para a barra imediatamente seguinte, então se essa barra estiver ausente a ordem é cancelada em vez de carregada para uma abertura observada posterior. Uma posição aberta ao longo de um buraco de dados continua aberta e aplica as regras de barreira por gap na sua primeira barra observada posterior, com o que os indicadores completam todo o seu warm-up de novo antes que outro sinal possa disparar.

Os painéis construídos vão de 1 de junho de 2022 a 31 de julho de 2026 e carregam 15,015,087 barras de quinze minutos e 3,757,437 barras horárias ao longo de 105 contratos, com 481,644 eventos de funding. Os gates de dados cobrem timestamps duplicados, monotonicidade, identidades OHLC, preços positivos, histórico de negociação baseado em volume, datas de onboarding, contagens de buracos, concordância da agregação de um minuto para barra e cobertura de funding contra a fonte, todos passando com zero falhas com um buraco mediano de barras de 0.000%.

Spreads

Os spreads são medidos em vez de estimados a partir de barras. O estimador de máxima e mínima de Corwin-Schultz foi testado e rejeitado para esta venue durante uma construção anterior: ele devolveu 18.76 bps para BTCUSDT onde as cotações de bookTicker dão 0.0330 bps, uma superestimação de 568 vezes, ao mesmo tempo em que se movia com a frequência das barras, dando 8.9 bps em barras de 15 minutos contra 18.8 bps em barras de 1 hora. Um estimador que depende da frequência de amostragem das barras a partir das quais é calculado está medindo volatilidade.

O que se mede em vez disso é o repique bid-ask, a mediana da variação absoluta de preço entre trades agregados consecutivos. Os trades alternam entre bid e ask, então essa mediana recupera o spread diretamente. Validado em BTCUSDT para 15 de julho de 2023, devolveu 0.0330 bps contra um ground truth de bookTicker de 0.0330 bps ao longo de 8.3M atualizações de cotação.

# two sampled days per symbol-month, streamed and discarded
changes = agg_trades["price"].diff().abs()
changes = changes[changes > 0]
spread_bps = 1e4 * changes.median() / agg_trades["price"].median()

São amostrados dois dias por símbolo-mês, o dia 7 e o dia 21, com fallback para datas próximas quando um deles está ausente, o que dá cerca de 10,500 símbolo-dias e aproximadamente 26 GB transmitidos. Um dia amostrado não devolve estimativa quando o contrato imprimiu menos de 500 trades ou menos de 100 variações de preço diferentes de zero, o que acontece em 589 de 5,146 símbolo-mês, concentrados na cauda ilíquida.

Um símbolo-mês sem estimativa toma o percentil 90 dos próprios spreads medidos daquele símbolo, enquanto um símbolo sem estimativa em mês nenhum toma o percentil 90 do seu degrau de universo para aquele mês. Os dois fallbacks são deliberadamente largos, então dados ausentes só conseguem fazer negociar parecer mais caro, e 88.6% dos símbolo-mês são precificados diretamente.

O arquivo publica bookTicker desde maio de 2023, sobrepondo a maior parte do período fora da amostra, e ele foi registrado como uma validação estratificada da série medida sob o gate 16. Esse gate nunca rodou para este estudo, então a comparação com ground truth é herdada da construção anterior em vez de repetida aqui.

A biblioteca de estratégias

Oito famílias estruturais com dez configurações numéricas congeladas cada, 80 estratégias, geradas sob a seed 20260808 e herdadas literalmente: bollinger_reversal, breakout, mean_reversion, momentum, moving_average_crossover, rsi_reversal, trend_following e volatility_breakout.

Os parâmetros numéricos aqui são propriedades congeladas de uma estratégia em vez de botões ajustáveis, porque este estudo não faz busca de parâmetros, o que deixa as 80 estratégias como unidades de replicação.

O filtro de implementação da biblioteca herdada tinha sido calibrado num painel de 10 instrumentos, então a cobertura foi remedida neste universo antes do congelamento, lendo atividade, período de holding, cobertura de instrumentos e giro sem nunca ler lucro. Ele não exigiu substituição: 8,398 dos 8,400 pares estratégia-ativo produzem ao menos um candidato em barras de 1 hora, e 8,400 de 8,400 produzem em barras de 15 minutos.

Sinais, saídas e execução intrabar

Toda regra calcula uma condição em {-1, 0, +1} apenas a partir de barras completas, e um candidato só dispara quando a visão da estratégia de fato se inverte. Uma barra neutra não é nem um sinal nem um reset, então a visão é mantida até o lado oposto disparar.

# a candidate exists only on a genuine directional inversion
view = condition.replace(0, np.nan).ffill()
candidate = view.ne(view.shift(1)) & view.notna() & view.shift(1).notna()

Uma posição fecha no seu take-profit, no seu stop-loss ou quando a estratégia produz um sinal oposto naquele ativo, sem hold máximo. O Wilder ATR(14), conhecido na entrada, fixa as distâncias de barreira para toda a vida do trade, num stop de 2.0 ATR e num alvo de 3.0 ATR para toda estratégia.

Esse par veio de uma calibração apenas de atividade sobre a primeira janela dentro da amostra no degrau focal, lendo período de holding, mix de razões de saída e ocupação sem nunca ler lucro. A ocupação decidiu: ao longo da grade de barreiras, a média de posições concorrentes por estratégia no top-50 foi de 1.96 num stop de 1.0 ATR até 11.33 em 3.0 ATR.

O selecionado em vez disso é o par que dá 6.39 posições concorrentes em média contra uma capacidade focal de 5, amarrando em 34.7% das barras, mantendo as saídas por barreira como maioria dos desfechos com 58.5% contra 41.5% de flips, um hold mediano de 6 barras e zero truncamento.

O OHLC de barra sozinho não consegue dizer qual barreira executou primeiro quando as duas caem dentro de uma barra, então o engine reconstrói o caminho intrabar a partir de barras de 1 minuto como open → first recorded extreme → second recorded extreme → close. Onde a máxima e a mínima caem no mesmo minuto e a ordem não é resolvida, o stop executa primeiro, o que acontece em 0.13% das barras na célula focal. Um gap através de um stop preenche na primeira abertura disponível, ao passo que um gap através de um alvo preenche não melhor que o alvo.

# events sharing one bar timestamp resolve in a fixed order
for ts in timestamps:
    apply_funding(ts)
    apply_gap_barriers(ts)
    apply_opposite_signal_exits(ts)
    release_slots(ts)          # exits always precede entries
    allocate_entries(ts)       # a slot freed at this open is available now
    apply_intrabar_barriers(ts)

O fluxo invariante de candidatos

Esta é a espinha do estudo e o motivo de os modos de execução serem comparáveis. Com capital fixo por slot, um book sem alavancagem e sem impacto modelado, o preço de entrada, o preço de saída, a razão de saída, os custos por unidade, o funding por unidade e o resultado por unidade de um trade não dependem do que mais o portfólio carrega. O desfecho de cada candidato é portanto calculado uma única vez, depois do que todo modo de execução, tamanho de universo, capacidade e regra de ranking é uma política de agendamento sobre o mesmo objeto congelado.

Um candidato é gerado para cada transição de sinal em cada par estratégia-ativo, independentemente da ocupação e de qualquer portfólio, carregando ativo, estratégia, família, timeframe, timestamps de sinal e de entrada, direção, preço de entrada, ATR de entrada, o valor declarado de força, o timestamp de saída pré-calculado, o preço de saída e a razão de saída, mais bruto, taxa, slippage, funding e resultado líquido por unidade nas duas unidades contábeis.

Que esses desfechos sejam bit a bit idênticos em todo braço, capacidade e degrau é um gate bloqueante em vez de uma premissa. O fluxo guarda 3,199,677 candidatos em barras de 1 hora e 13,253,337 em 15 minutos, com 16,452,374 linhas de desfecho no total.

Uma limitação decorre disso diretamente: como os custos são planos no notional, o modelo não cobra mais de um book concentrado do que de um difuso pelo mesmo trade, e é por isso que os diagnósticos neutros em exposição e a sensibilidade de slippage dependente de tamanho são registrados.

Custos, funding e contabilidade

5 bp de taker, 2 bp de maker, 2 bp de slippage, mais metade do spread medido em qualquer fill que atravessa. Um fill que atravessa paga taker mais slippage mais metade do spread, ao passo que um take-profit descansa como ordem limitada e paga a taxa maker sem slippage e sem spread. Um stop atravessa e os fills de entrada sempre atravessam, com os custos aplicados ao notional absoluto preenchido.

def fill_cost(notional, crosses, spread_bps):
    fee = TAKER_BPS if crosses else MAKER_BPS
    slip = SLIPPAGE_BPS if crosses else 0.0
    half_spread = 0.5 * spread_bps if crosses else 0.0
    return abs(notional) * (fee + slip + half_spread) / 1e4

O funding usa eventos de funding observados da Binance em seus timestamps reais de liquidação em vez de um cronograma assumido, e uma observação de funding esperada e ausente é uma falha de dados em vez de um zero, com o que funding positivo debita comprados e credita vendidos. O funding é avaliado sobre a posição mantida imediatamente antes do timestamp de liquidação, então uma nova entrada naquele timestamp não paga nem recebe, enquanto uma posição carregada para dentro dele e encerrada nele paga.

Toda linha do ledger preserva bruto, taxa, slippage, funding e líquido separadamente, com o que a identidade gross - fee - slippage - funding = net se mantém dentro de 1e-9 no nível de linha e no agregado, reconciliando a 0.0 ao longo de todos os 16,452,374 desfechos candidatos.

Duas unidades contábeis são mantidas o tempo todo e nenhuma substitui a outra: R normalizado, a unidade por trade normalizada por ATR usada para qualidade de seleção, e depois retorno sobre notional, usado para lucro e perda do portfólio no peso da posição. Capital ocioso não ganha nada e os retornos são aritméticos sobre capital fixo, sem nada capitalizando.

Os braços e as regras de ranking

Todos os braços usam capital fixo, capital igual por posição aberta, um book sem alavancagem e sem capitalização. O capital não usado fica ocioso e as posições ativas nunca são reescalonadas para substituir uma que ficou de fora ou foi recusada.

BraçoSlotsPesoSeleção quando os slots são escassos
Base independenteU, o tamanho do universo1/Ununca escassos, todo candidato é aceito
AleatórioK1/Ksorteio uniforme entre os concorrentes
Força do sinalK1/Kmaior força declarada
Expectativa históricaK1/Kmaior expectativa dentro da amostra para o ativo
Information ratioK1/Kmaior information ratio dentro da amostra para o ativo

As capacidades são K em {1, 3, 5, 10} e os tamanhos de universo U em {10, 25, 50, 100}. Os portfólios são sempre de estratégia única: uma estratégia compete ao longo dos ativos pelos seus próprios slots, e sinais de famílias diferentes nunca dividem uma fila. O retorno reportado de um braço é a média de peso igual ao longo dos 80 portfólios de estratégia única, um book de 1/80 que permanece sem alavancagem.

O braço aleatório é a média sobre 100 réplicas independentes sob um gerador com seed por célula, janela e réplica, já que um sorteio único é ruído e nunca é reportado como o braço aleatório.

A força do sinal é declarada por família e fixada antes da execução, sempre numa unidade livre de escala para que um ativo de alta volatilidade não possa superar um de baixa volatilidade por motivos alheios a convicção, já que distâncias percentuais cruas codificariam um viés de volatilidade e são proibidas.

FamíliaForça
bollinger_reversalexcesso do z-score absoluto do log-preço sobre a largura da banda, em desvios padrão
breakoutdistância do fechamento além do extremo anterior do canal e seu buffer, em ATR de entrada
mean_reversionexcesso do z-score absoluto sobre o limiar de entrada, em desvios padrão
momentumexcesso do retorno de lookback escalado por volatilidade sobre o seu limiar
moving_average_crossoverseparação absoluta entre EMA rápida e lenta, em ATR de entrada
rsi_reversaldistância do RSI além do seu limite, em pontos de RSI
trend_followingexcesso da distância à EMA escalada por volatilidade sobre o seu limiar
volatility_breakoutmagnitude do retorno direcional de lookback escalada por volatilidade

A expectativa histórica é o resultado líquido médio dentro da amostra por candidato executado sob a base independente, em R normalizado, exigindo ao menos 20 candidatos executados dentro da amostra para aquele par estratégia-ativo. O information ratio divide essa média pelo seu próprio desvio padrão dentro da amostra e exige o mesmo mínimo mais um desvio padrão diferente de zero. Ativos abaixo de qualquer um dos limiares ranqueiam por último, ordenados por índice congelado do universo, sem shrinkage, sem pooling e sem modelo.

stats = (
    is_trades.groupby(["strategy", "symbol"])["net_r"]
    .agg(["mean", "std", "count"])
    .query("count >= 20")
)
expectancy_rank = stats["mean"].sort_values(ascending=False)
info_ratio_rank = (stats["mean"] / stats["std"]).sort_values(ascending=False)

Com que frequência o limiar amarra foi medido antes do congelamento apenas a partir de contagens de candidatos, sem leitura de lucro. No degrau focal uma mediana de 48 dos 50 ativos é ranqueável por estratégia-janela, e 79.8% das estratégia-janela carregam ao menos 10 ativos ranqueáveis, com a falta concentrada nas famílias lentas, onde rsi_reversal passa dos 20 trades dentro da amostra em 33% das células contra 91% de moving_average_crossover.

Mecânica dos slots

A recusa é immediate-or-cancel, então um candidato só é elegível a um slot na sua própria barra de entrada, e se nenhum slot estiver livre naquele instante ele é recusado permanentemente e nunca entra depois. Uma fila persistente foi considerada e rejeitada porque um candidato que espera entra com um sinal velho e a um preço diferente, o que faz dele um trade diferente e quebra a afirmação de que só a seleção de trades difere entre os braços.

Dentro de um mesmo timestamp, as saídas e as liberações de slot são processadas antes da alocação de entradas, depois do que os candidatos concorrentes são ordenados pela regra de ranking do braço e os slots são preenchidos em ordem de ranking até a capacidade ser atingida, com empates resolvidos pelo menor índice congelado do universo. A ocupação nunca pode exceder K e o capital empregado nunca pode exceder 100%, ambos impostos como gates bloqueantes.

Quando K é maior ou igual a U a restrição nunca pode amarrar e o braço de portfólio deve produzir um ledger idêntico ao da base independente, um controle positivo e um gate bloqueante.

Chegadas simultâneas são o canal minoritário, com mais de um candidato chegando no mesmo timestamp em 9.8% dos eventos de chegada e mais de cinco em 2.4% das barras. A recusa portanto acontece na maior parte porque trades anteriores ainda ocupam os slots, o mecanismo realista e o que o desenho pretende testar. Também significa que o ranking age sobre uma fatia estreita, dado que a recusa por um book já cheio é cega ao ranking.

Cronograma walk-forward

Toda janela é de 12 meses calendário dentro da amostra seguidos de 1 mês calendário fora da amostra, avançando um mês por vez, então julho de 2023 é o primeiro mês fora da amostra e julho de 2026 o último, dando 37 janelas.

Os dados dentro da amostra servem exatamente a um propósito, estimar as estatísticas de ranking por ativo, sem busca de parâmetros dentro da amostra e sem seleção de modelo dentro da amostra em lugar nenhum. Os rankings são congelados no limite da janela e aplicados por um mês fora da amostra, e uma posição aberta sob o ranking de um mês nunca é redecidida quando o ranking rola.

O warm-up de indicador mais longo pode usar barras anteriores ao início de um bloco, porque essas barras já eram conhecidas, embora linhas de warm-up nunca contribuam com lucro ou perda para o bloco que está sendo pontuado.

Uma janela de 12 meses dentro da amostra deixa as estatísticas de ranking mais ruidosas do que uma janela mais longa deixaria, e os dois braços ranqueados são construídos exatamente a partir dessas estatísticas, então o desenho pende para achar que ranquear não bate o aleatório. Isso foi registrado como propriedade conhecida em vez de descoberto depois, daí a sensibilidade de janela mais longa.

Inferência

O engine constrói retornos líquidos diários pareados sobre capital fixo para todo braço em toda célula, em dias calendário UTC: 1,127 dias ao longo de 162 clusters de semana. A reamostragem é sobre clusters de semana calendário de segunda a domingo UTC, onde um limite de mês corta uma semana em fragmentos separados de janela-semana e todo ativo, estratégia e braço dentro de um fragmento amostrado viaja junto.

# paired, week-clustered, common random numbers across arms
rng = np.random.default_rng(20260810)
for b in range(B):
    weeks = rng.choice(clusters, size=len(clusters), replace=True)
    draw = np.concatenate([day_index[w] for w in weeks])
    stat[b] = 1e4 * (ret_a[draw].sum() - ret_b[draw].sum())
point, lo, hi = stat.mean(), *np.percentile(stat, [2.5, 97.5])

Réplicas de bootstrap pareado agrupado por semana sob a seed 20260810 constroem o intervalo primário, onde em cada réplica a estatística é a diferença de retorno aritmético acumulado ao longo do histórico fora da amostra reamostrado. Cada p-valor secundário bilateral é o dobro da menor probabilidade de cauda do bootstrap com a correção de réplicas finitas (count + 1) / (B + 1), limitada a um, antes de entrar no Benjamini-Hochberg.

Nenhum ajuste de multiplicidade se aplica ao único contraste primário registrado. D_cap e D_rank formam uma família de Benjamini-Hochberg de dois, ao passo que a superfície registrada de 288 contrastes forma uma família separada corrigida dentro de si mesma, da qual 193 são significantes.

Um trade realizado lança todo o seu resultado líquido no dia em que sai. Os holds medianos são de 6 horas em barras de 1 hora e 1.2 hora em 15 minutos, então o que importa para o agrupamento por semana é com que frequência um trade atravessa um limite de semana: 6.1% em 1 hora e 1.5% em 15 minutos. Esses poucos introduzem uma dependência leve entre clusters adjacentes, o que subestima ligeiramente a incerteza, um efeito que a variante de atribuição por dia de entrada limita em 1.9 bp sobre D_cap.

Diagnósticos descritivos em vez de unidades de bootstrap, as 37 diferenças mensais não são 37 estratégias independentes. O Sharpe pode ser reportado descritivamente e nunca seleciona uma configuração, ranqueia um braço ou decide uma conclusão.

O corpus nulo sem drift

O gate 20 constrói trinta corpora independentes sem drift, cada um de 50 ativos sintéticos carregando a volatilidade realizada e o spread de um contrato real, e passa cada um pelo pipeline inteiro da célula focal: sinais, candidatos, saídas por primeira passagem sob o modelo de custo real, estatísticas de ranking dentro da amostra, e então os braços. Essas séries não contêm previsibilidade de retorno de nenhum tipo, então qualquer coisa que um braço ranqueado ganhe nelas foi fabricada pela maquinaria.

# volatility and spread preserved on purpose; drift removed
for seed in range(1, 31):
    rng = np.random.default_rng(seed)
    for symbol in rung_50:
        sigma = realised_vol[symbol]        # per-bar, from the real panel
        log_ret = rng.normal(loc=0.0, scale=sigma, size=n_bars)
        synthetic[symbol] = np.exp(np.cumsum(log_ret)) * first_price[symbol]
    run_full_pipeline(synthetic, spreads=measured_spreads)

Preservar o perfil de volatilidade é o ponto. As barreiras são escaladas por ATR enquanto os custos são uma fração fixa do notional, então um ativo de baixa volatilidade paga proporcionalmente mais custo por unidade de risco. A expectativa passada e a força do sinal se correlacionam ambas com volatilidade, e volatilidade persiste, então uma regra de ranking consegue selecionar trades mais baratos por unidade de risco sem prever absolutamente nada.

Um corpus nulo que igualasse a volatilidade teria escondido precisamente o mecanismo que vale testar.

Registrada como condição de aprovação estava que nenhuma vantagem de braço ranqueado sobre o aleatório tivesse um intervalo de 95% inteiramente acima de zero ao longo das seeds. A força do sinal passa dessa barra em 8 de 30 corpora que não contêm sinal, então o gate falha. A expectativa não dispara a condição registrada, e ainda assim fica no percentil 87 do seu próprio nulo, um p empírico de 0.133 calculado como a fração de sorteios do nulo iguais ou acima da estimativa real.

Diagnósticos que separam emprego de capital de seleção

Quatro diagnósticos existem especificamente para impedir que um artefato de caixa ociosa seja pontuado como habilidade, e são obrigatórios onde quer que um número de manchete apareça: R líquido por candidato oferecido, retorno líquido por unidade de capital médio empregado, utilização de oportunidade com a taxa de recusa, e então ocupação média e máxima com capital ocioso médio.

# denominator is the frozen candidate stream, identical for every arm
net_r_per_offered = taken["net_r"].sum() / offered_count
binding_rate = rejected_book_full / arrivals   # asset-block rejections counted separately

A taxa de amarração é a fração das chegadas de candidatos recusadas porque o book já mantinha K posições, com as recusas causadas pela regra de uma posição por ativo contadas separadamente e não tratadas como amarração, e a base independente é incluída como controle sob a expectativa de ficar perto de zero.

Um book de capacidade 1 mantendo uma posição por vez num universo de 100 nomes vai mostrar um Sharpe lisonjeiro que é quase inteiramente caixa ociosa, então reportar o retorno sobre capital fixo ao lado das medidas de capital empregado e por candidato oferecido mantém isso visível.

Para a célula focal um ledger completo por posição, 2,303,532 linhas ao longo dos cinco braços, é escrito para inspeção. Produzir esses diagnósticos exigiu uma reimplementação do scheduler em Python puro, que concordou com o kernel em numba em todo conjunto aceito ao longo dos cinco braços e das 80 estratégias com zero discordâncias.

Gates e resultados das verificações

Oito dos 21 gates bloqueantes da especificação estão implementados. Os seis gates anteriores ao fora da amostra rodam apenas sobre dados dentro da amostra e todos passam.

GateResultado
identidade K ≥ U contra a base independentediferença absoluta máxima de 3.47e-17
ranking com previsão perfeita domina o aleatório e o anti-ranking+12.53 contra -1.29 contra -15.13
estabilidade de seed do aleatóriodispersão de 0.042, umas 330 vezes menor que a diferença da previsão perfeita
capital empregado nunca excede o capital fixomáximo de capital médio empregado 95.2%
uma posição por ativo por estratégiaimposto, 7,244 aceitos de 40,824 oferecidos
defeito plantado é detectadoa identidade diverge por 0.514 em vez de zero

Além desses, a caminhada de primeira passagem em numba reproduz exatamente a referência congelada em Python ao longo de 59,214 candidatos com todas as seis razões de saída exercitadas, e uma suíte de testes adversariais somou mais 18,672 comparações com zero divergências. A paridade de scheduler do gate 14 passa com zero discordâncias, ao passo que o gate 20 falha, e essa falha é o que retirou a afirmação sobre ranking.

Duas verificações depois do primeiro congelamento, uma do engine e uma da estatística, encontraram quatro defeitos que mudaram os números: um slot liberado para qualquer trade que fechasse dentro da própria barra de entrada, então 7.4% dos trades não ocupavam capacidade; candidatos aceitos com qualquer ATR positivo, deixando denominadores de risco denormais produzirem resultados tão grandes quanto 1e95; um book que esvaziava em cada limite de mês; e um desempate pela ordem de linha do parquet em vez do índice congelado do universo. Corrigir tudo isso moveu a manchete de -10,459 bp para -8,090 bp com todo sinal e conclusão inalterados.

Treze gates seguem não implementados, nenhum dos quais sustenta uma afirmação que o artigo faz. Uma execução piloto anterior ao congelamento existe e está em quarentena em vez de reportada, já que a ordenação de gates original da especificação exigia rodar o scheduler antes do congelamento, o que necessariamente expôs números fora da amostra. A ordenação foi corrigida para que todo gate dependente do scheduler agora rode apenas sobre o período dentro da amostra.

freeze             f575878c86937ab63067aeb98c9b8fb7723e5ec4a0a64c4112cc6f1ce77d8d6b
superseded         5ecb4866f65386ddb18358d991b2eb541a3111362a4afb5646caf9cca62eb3b9
superseded         bd72f21f1180bb32e2fd17fed02b2a616ffe9f2dfe6f2e91a60385e67de04d39
specification      1f7687a381a256e3f4d6ec0502bc8069705c4d70d0f6b01527107a8f6c0744b0
universe           429fcc435cd8ab32ca98480e6d011ee590ccfd79541b2431af8489128c81a3e5
data manifest      add2fc3c1fc460d0eb74ba08d14ae829593f779fcd6ab02dfe890e27e73ab0dd

Os gráficos do artigo são desenhados a partir das tabelas de resultado publicadas, que são as mesmas tabelas de onde vêm os números do texto.

Pedindo o pipeline completo

Esta página é suficiente para conferir cada número do artigo na mão, ou para reconstruir um estudo equivalente a partir do arquivo público da Binance, embora os scripts de produção, a construção dos dados, o scheduler em numba, a análise e a construção das figuras não estejam anexados. Escreva para daniel@daru.finance e eu envio.

← Voltar ao artigo