← O artigo
Reprodutibilidade

Maturidade de mercado e alpha

Esta página dá a receita exata por trás de cada número do artigo: dados, universo, biblioteca de regras, engine de execução, custos, a medida pareada, o congelamento e as tabelas descritivas. Os trechos de código são ilustrações curtas da lógica, e não o código de produção.

Dados

Binance: Toda série vem do arquivo público de dados da Binance (data.binance.vision), futuros margens em USD, sem credenciais, cobrindo por contrato:

  • klines mensais de 1 hora, a série de preços para o engine e para a medida;
  • klines mensais de 1 minuto, reduzidos na chegada a uma linha por hora e depois descartados;
  • arquivos mensais de taxa de funding, uma linha por pagamento, trazendo a própria coluna de intervalo de liquidação do arquivo;
  • arquivos diários bookTicker (melhor bid e oferta), publicados de 16 de maio de 2023 a 30 de março de 2024;
  • arquivos diários bookDepth (notional acumulado descansando entre ±1% e ±5% do mid, cerca de um snapshot a cada 30 segundos), usados de 1 de janeiro de 2023 a 9 de agosto de 2026.

Censo: A lista de contratos é reconstruída a partir do arquivo em vez de retirada da exchange ao vivo. Todo símbolo que um dia apareceu sob o prefixo dos klines mensais é enumerado, o que recupera contratos que a exchange removeu depois: 986 símbolos, 12 dos quais nunca negociaram.

Para cada símbolo, os arquivos mensais são percorridos para frente e para trás até a primeira e a última barra com volume negociado positivo. A existência de um arquivo não é evidência de negociação, então os limites vêm do volume e nunca do primeiro ou do último arquivo, depois do que os 832 símbolos negociados cotados em USDT formam o estudo.

Primeiro dia negociado: O relógio de idade de um contrato começa às 00:00 UTC do primeiro dia em que seu perpétuo registrou volume negociado positivo na venue. A idade em dias é o número inteiro de dias desde aquela meia-noite, então toda barra de um dia UTC compartilha uma mesma idade.

first_day_ms = first_positive_volume_bar_ms // DAY_MS * DAY_MS
age_days     = (bar_open_ms - first_day_ms) // DAY_MS          # one clock, used everywhere

Como o mês mais antigo do arquivo é janeiro de 2020, 3 contratos cuja primeira barra arquivada cai nesse mês têm data de listagem censurada à esquerda, todos eles na coorte de calibração. O último dia negociado em todo o censo, 31 de julho de 2026, é o fim de arquivo usado para a sobrevivência.

Caminho intrabarra: Cada mês de barras de 1 minuto colapsa em uma linha por hora: a máxima e a mínima da hora, o minuto em que cada uma imprimiu, um flag high_first (o minuto da máxima precede o da mínima) e um flag ambiguous (as duas no mesmo minuto).

Bybit: Os klines horários de 802 arquivos de perpétuos lineares em USDT foram congelados como um snapshot às 22:23:09 UTC de 21 de setembro de 2026, para que o insumo não pudesse mudar depois do selo. O funding vem do endpoint público v5 de histórico de funding da Bybit, paginado da última barra de cada contrato até a primeira.

Os books vêm do arquivo público de book da Bybit (arquivos de 500 níveis, 200 níveis a partir de 2025), que começa em 2023. O primeiro dia negociado de um contrato na Bybit é o dia UTC de sua primeira barra horária com volume acima de zero.

Universo

Coortes da Binance: A divisão é pelo primeiro dia negociado.

CoorteListadosContratosUso
Calibraçãoem 31 de dezembro de 2021 ou antes140escolhe a biblioteca de regras, nada mais
Evidênciaa partir de 1 de janeiro de 2022692a medida na Binance (amostra de desenvolvimento)

Dos 692 contratos de evidência, 108 não negociam mais. A medida na Binance roda sobre todos os 692; 688 têm as 96 barras horárias de que ela precisa (uma janela de volatilidade de 48 barras mais 48 barras) e 4 não têm. Sua referência madura sai dos mesmos 692, com um contrato de evidência entrando nela em cada dia em que tem mais de 365 dias.

Universo da Bybit: A amostra confirmatória vem dos perpétuos lineares em USDT da Bybit negociados pela primeira vez entre 1 de janeiro de 2022 e 31 de julho de 2026, uma janela de listagem com 620 contratos, mantendo aqueles cujo caminho de preço jovem não está nos dados da Binance. Os tokens são pareados com a Binance por identidade em vez de por símbolo, porque as duas venues muitas vezes cotam o mesmo token sob prefixos multiplicadores diferentes:

  1. Remover o sufixo USDT e qualquer prefixo inicial 1M ou potência de dez (10, 100, 1000, ...) para obter o nome do token.
  2. Um casamento de nome que veio da remoção de um prefixo é confirmado em uma hora em comum, onde a razão entre as aberturas das duas venues precisa ficar a menos de 15% de uma potência de dez; um casamento por prefixo que falha é tratado como um token diferente.
  3. Um símbolo idêntico sempre conta como o mesmo token mesmo quando a verificação de preço falha, já que tratá-lo como intocado seria a direção insegura, e um casamento de nome sem hora em comum para verificar também conta.
def token(sym):                        # "1000000BABYDOGEUSDT" -> "BABYDOGE"
    return re.sub(r"^(1M|10+)(?=[A-Z])", "", sym[:-4])

def same_token(bybit_open, binance_open):
    r = bybit_open / binance_open
    k = round(math.log10(r))
    return abs(r / 10**k - 1) < 0.15   # 1000TAG vs TAG passes at 996.40

O casamento por prefixo confirmou 5 pares (1000000BABYDOGE/1MBABYDOGE, 10000SATS/1000SATS, 1000TAG/TAG, 1000TOSHI/TOSHI, 1000TURBO/TURBO). Entre os símbolos idênticos, 4 falharam na verificação de preço e foram mantidos como o mesmo token (HNT, ON, ORBS, SNT), e outros 7 casamentos de nome não tinham hora em comum e também foram mantidos como o mesmo token.

Cada contrato então cai em um grupo.

GrupoRegraContratos
Só Bybito token nunca teve um perpétuo em USDT na Binance79
Bybit primeiroa Binance listou o token pelo menos 30 dias depois da Bybit77
Sobreposiçãotodo o resto464, excluídos

No grupo Bybit primeiro, os dias jovens param no dia anterior à listagem na Binance, com qualquer posição ainda aberta fechada na última barra daquele dia. Isso dá 156 contratos e 4,524 contratos-dia jovens de calendário nas idades de 2 a 30, dos quais 4,284 (da idade 3 em diante) têm estimativa de volatilidade e 3,295 são pareados.

Referência madura da Bybit: Os contratos em USDT da Bybit negociados pela primeira vez a partir de 1 de janeiro de 2022 entram nos dias em que têm mais de 365 dias, espelhando a janela de listagem da perna madura da Binance. Um contrato do grupo Bybit primeiro se junta à perna madura assim que passa de 365 dias, como qualquer outro. Dos 802 arquivos, 715 contratos carregam na medida; 80 são pulados por terem sido listados antes de 2022 e ficarem fora do universo; 7 têm menos de 96 barras.

Regras

Vocabulário: 34 variantes de regra formam o vocabulário, 11 transforms e 23 gates de confluência. Uma regra de transform calcula o z-score de um transform do fechamento sobre uma janela móvel de L barras e opera o sinal quando o z-score cruza um limiar. Uma regra de confluência calcula o z-score do fechamento cru do mesmo jeito e mantém o sinal apenas nas barras em que o gate é verdadeiro.

def desired_state(kind, name, L, thr, o, h, l, c):
    if kind == "transform":
        z = rolling_z(transform(name, c, L), L)
        gate = True
    else:
        z = rolling_z(c, L)
        gate = confluence_gate(name, o, h, l, c, L)
    d = np.where(z > thr, 1, np.where(z < -thr, -1, 0))
    return np.where(gate, d, 0)            # 0 = no instruction this bar

Transforms: base (o fechamento), slope, normalized_price, roc, bias, volZ, accel, disFromMedian, quant_stretch, rank_resid, fold_dev. Gates: RSIge40, RSIge50, Pge0.7, Pge0.8, BW_filter, pi, vr, kurtosis, skew, skew0.75, atr_pct, atr_pct0.8, burstfreq, TinyBody, NoNewLowGreen, RangeSpike, YesterdayPeak, DeadFlat10, InsideBar, SameDirection, TopOfRange, VolContraction, EMAHug.

Grade: Cada variante é cruzada com lookback L em {4, 8, 12} barras, limiar em {0.5, 1.0, 1.5}, hold máximo em {12, 48, 168} barras, distância de stop em {1, 2, 4} × ATR e reward-to-risk em {1, 1.5, 2, 3, 5}, dando 34 × 405 = 13,770 configurações. Triar cada configuração nos 140 contratos de calibração faz da busca 13,770 × 140 = 1,927,800 execuções do engine no total. Os lookbacks param em 12 barras para que toda regra possa estar aquecida antes do dia medido mais jovem.

Triagem: A triagem roda em contratos com pelo menos 2,000 barras horárias negociadas, sob a convenção intrabarra conservadora (abaixo), cobrando a taxa de 5 bp mais o custo de execução medido de 7.4 bp de um contrato maduro da Binance em cada fill. Os dias jovens nunca chegam até ela, já que os sinais em qualquer barra em que o contrato tem menos de 31 dias são zerados antes da pontuação. Uma configuração é elegível se pelo menos 35 contratos (um quarto da coorte) mostrarem pelo menos 24 trades fechados em pelo menos 6 semanas de calendário distintas, e pelo menos 35 mostrarem um primeiro sinal dentro das 40 primeiras barras do contrato.

signals[age_days < 31] = 0                                   # selection never sees young days
eligible = (n_contracts_active >= 35) & (n_contracts_warm >= 35)
ranked   = sorted(eligible_rows, key=lambda r: -r.pooled_net)
library  = first_per_variant(ranked, k=20)                   # one rule per variant

Sob esses filtros 12,555 configurações são elegíveis, nenhuma com líquido agregado positivo depois dos custos na coorte de calibração e 73 com bruto positivo. A biblioteca são as 20 melhores pelo líquido agregado na calibração, no máximo uma por variante, sem nenhuma triagem de positividade, porque a medida é uma diferença jovem menos maduro que continua definida qualquer que seja o sinal de cada perna.

Biblioteca congelada:

VarianteTipoLLimiarStop (× ATR)Reward-to-riskHold máximo (barras)
BW_filtergate41.541.5168
kurtosisgate120.545168
NoNewLowGreengate121.545168
pigate121.545168
Pge0.8gate121.545168
Pge0.7gate121.545168
TinyBodygate121.543168
skew0.75gate41.515168
InsideBargate41.545168
DeadFlat10gate41.545168
atr_pct0.8gate121.545168
vrgate121.545168
atr_pctgate121.545168
YesterdayPeakgate121.545168
RangeSpikegate81.543168
biastransform121.545168
skewgate41.545168
RSIge50gate121.54548
EMAHuggate41.543168
SameDirectiongate41.545168

Cada gate da biblioteca testa barras já fechadas: BW_filter, o corpo da barra anterior é mais de 70% de seu range; TinyBody, o corpo da barra anterior é menos de 10% de seu range; pi, o corpo da barra anterior dividido pelo ATR de L barras fica a menos de 0.05 de π/4; kurtosis, curtose móvel dos retornos de L barras de pelo menos 5; skew e skew0.75, assimetria móvel dos retornos em valor absoluto de pelo menos 0.5 e 0.75; Pge0.7 e Pge0.8, o fechamento anterior fica nos 30% ou nos 20% superiores de seu range móvel de L barras; atr_pct e atr_pct0.8, o mesmo teste sobre o ATR de L barras dentro de seu próprio range móvel; vr, uma razão de variância de 2 barras para 1 barra de pelo menos 1; NoNewLowGreen, a barra anterior fechou em alta e sua mínima está acima do fechamento de 5 barras antes dela; RangeSpike, o range da barra anterior excede 1.3 vezes sua média de 20 barras; YesterdayPeak, o fechamento de 2 barras atrás é o mais alto dos 3 últimos; DeadFlat10, o fechamento se moveu menos de 0.5% ao longo das 10 barras até o fechamento anterior; InsideBar, a barra de 2 atrás fica dentro do range da barra anterior; SameDirection, as 2 últimas barras fecharam na mesma direção; RSIge50, o RSI anterior de 14 barras é de pelo menos 50; EMAHug, o fechamento anterior fica a menos de 0.92 do range da barra anterior de sua média de 21 barras. O único transform, bias, é uma média exponencial de L barras da inclinação de L barras do fechamento.

O SHA-256 do arquivo da biblioteca é 3b4c9b75e5ce3ddb66ba21c1d4fcdeda3ad8a6513881857295b0cbdfae528556.

Engine

Roda uma posição por regra por contrato, com notional fixo e sem capitalização composta. O tamanho de posição é uma unidade de notional na entrada, então todo número de P&L é uma fração do notional de entrada.

  • Entrada: Um sinal calculado na barra completa t entra na abertura da barra t+1, e um sinal na mesma direção com posição aberta é ignorado. Um sinal oposto fecha na abertura seguinte e reverte ali, pagando 2 fills, enquanto um zero não é uma instrução de saída.
  • Barreiras: Stop e alvo são fixados na entrada a partir do ATR de Wilder(14) até a barra anterior à entrada, com o stop a stop × ATR do preço de entrada e o alvo a stop × ATR × reward-to-risk; nenhum dos dois se move depois.
  • Hold máximo: A posição sai na abertura da barra assim que H barras se passaram desde a entrada.
  • Gaps: Uma abertura além do stop executa naquela abertura. Uma abertura além do alvo executa no alvo, nunca melhor.
  • Barra de entrada: A entrada é na abertura, então todo o range da barra vem depois dela e as duas barreiras estão vivas na barra de entrada.
  • Mesma barra toca as duas barreiras: Sob a ordem verdadeira (só na Binance) o caminho de 1 minuto decide, com o alvo primeiro se a máxima veio antes da mínima numa posição comprada, o stop primeiro caso contrário, e o espelho disso numa vendida; uma barra cujos extremos imprimiram no mesmo minuto, ou cujo registro de caminho não reproduz a máxima e a mínima da barra, é resolvida como stop. A conservadora resolve toda barra desse tipo como stop, tanto comprada quanto vendida. A Bybit não tem caminho por minuto e por isso roda apenas a conservadora, enquanto a Binance roda as duas.
  • Reentrada depois de um fill em barreira: Se a regra ainda quer posição depois de um stop ou de um alvo executar dentro de uma barra, a nova posição executa no preço da barreira, o preço em que a posição fica de fato zerada, em vez da abertura daquela barra. Nessa barra só o novo stop pode disparar, julgado pelo extremo adverso da barra, enquanto o novo alvo fica vivo a partir da barra seguinte, porque a parte do range anterior ao fill não pode ser atribuída à nova posição.
  • Ordem dentro de uma barra: funding, barreiras por gap, reversão, hold máximo, nova entrada, barreiras intrabarra, fechamento forçado.
  • Funding: Os pagamentos observados são cobrados em seu horário real de liquidação, cada um caindo na primeira barra que abre na hora de liquidação ou depois dela e cobrado sobre a posição mantida imediatamente antes da liquidação como direction × rate × quantity × price, avaliado na abertura daquela barra. Funding positivo debita os comprados e credita os vendidos; uma posição aberta na barra de liquidação não paga nem recebe, enquanto uma carregada para dentro dela paga ou recebe.

Nenhum sinal usa funding. As taxas da Binance vêm dos arquivos de funding do arquivo público com seu intervalo por pagamento, e as da Bybit, do endpoint de histórico de funding.

for i in range(1, n):
    if pos: funding_cost += pos * rate[i] * qty * open_[i]          # 1. funding
    if pos:
        hit = barrier(pos, open_[i], high[i], low[i], hi_first[i], ambiguous[i])
        if hit:     close(i, hit.price); reentry_px = hit.price     # 2. gap / intrabar barrier
        elif want[i-1] == -pos: close(i, open_[i])                   # 3. reversal
        elif i - entry_i >= max_hold: close(i, open_[i])             # 4. maximum hold
    if not pos and want[i-1]:
        enter(i, at=reentry_px if exited_this_bar else open_[i])     # 5. entry
        check_barriers_on_entry_bar(i)                               # 6. intrabar barriers

Todo trade carrega P&L bruto, taxa, slippage e funding em colunas separadas, com gross - fee - slippage - funding = net conferido em cada trade. Ao longo de 7,234,659 trades na Binance (ordem verdadeira), 7,234,637 (conservadora) e 8,059,098 trades na Bybit, nenhum trade viola a identidade e o maior resíduo é 0.0.

Sob a convenção de ordem verdadeira, de 7,555,948 barras horárias da Binance, 7,525,107 carregam uma ordem de minutos resolvida, 24,391 têm os dois extremos no mesmo minuto e 6,450 têm um registro de caminho que não bate com a máxima e a mínima da barra, com os 2 últimos grupos resolvendo como stop.

Custos

Todo fill paga uma taxa taker mais um custo de execução medido para a faixa de idade do contrato, a um tamanho de posição registrado de $10,000. O fill de entrada é cobrado pela faixa da idade de entrada e o fill de saída pela faixa da idade de saída, escalado pelo preço de saída sobre o de entrada, para que a cobrança não dependa do resultado do trade. Um fill na idade 0 ou 1, que não tem faixa, paga o custo de 2-7 dias.

slip = (cost_bp[bucket(age_entry)] + cost_bp[bucket(age_exit)] * exit_px / entry_px) / 1e4
fee  = fee_bp / 1e4 * (1 + exit_px / entry_px)
net  = gross - fee - slip - funding

Taxas: A Binance cobra 5 bp por lado e a Bybit 5.5 bp por lado, o nível taker base.

Meio-spread na Binance: Vem do arquivo bookTicker, amostrado numa escada fixa de idades: 0, 1, 2, 3, 5, 7, 10, 14, 21, 30, 45, 60, 120, 200, 300, 400, 500, 700, 900 e 1,200 dias, para todo contrato em USDT que negociou em algum momento dentro da janela de cotações, mantendo os degraus que caem dentro dela. Por contrato-dia, o meio-spread é a mediana, sobre as cotações daquele dia, de (ask - bid) / (ask + bid) em pontos-base, descartando dias com menos de 100 cotações válidas. O valor da faixa é a mediana sobre os contratos-dia, 1,425 contratos-dia de 257 contratos.

Impacto na Binance: Uma única amostra de profundidade, num único esquema de amostragem, cobre todas as idades, então as pernas jovem e madura vêm do mesmo painel. Para cada contrato ela toma dias de bookDepth nas idades 3, 7, 14, 28, 45, 75, 120, 160, 240 e 300, mais 8 dias igualmente espaçados da idade 366 (ou de 1 de janeiro de 2023, se for mais tarde) até o último dia do contrato, todos dentro da janela do arquivo.

Dentro de cada dia, o notional acumulado descansando em cada banda é a mediana sobre os snapshots daquele dia. Uma compra de $10,000 é então varrida pela escada de asks de 1% a 5% com densidade uniforme dentro de cada banda, e o custo é a distância média do mid ponderada por notional. Por faixa, o valor é a mediana sobre os dias de cada contrato na faixa, e depois a mediana sobre os contratos.

def sweep_cost_bp(cum_notional, size=10_000, bands=(1, 2, 3, 4, 5)):   # bands in % of mid
    filled = paid = prev_n = prev_d = 0.0
    for d, n in zip(bands, cum_notional):
        take = min(size - filled, n - prev_n)
        paid += take * (prev_d + (d - prev_d) * take / (n - prev_n) / 2)   # uniform in band
        filled += take; prev_n, prev_d = n, d
        if filled >= size: break
    return paid / size * 100                                          # % of mid -> bp
Faixa na BinanceMeio-spread, bpContratos-dia (contratos)Impacto de $10,000, bpContratos-dia (contratos)Custo por fill, bp
2-7d1.56346 (88)4.561,273 (642)6.12
8-30d1.22355 (94)3.981,266 (650)5.20
31-90d1.20168 (89)4.801,160 (613)6.00
91-180d1.2091 (91)5.691,022 (526)6.89
181-365d1.22104 (64)7.39848 (448)8.61
acima de 365d1.18189 (150)6.223,780 (477)7.40

Bybit: Os custos são amostrados do arquivo público de book da Bybit. Os dias jovens cobrem todo contrato do universo nas idades 3, 7, 14 e 28, quando essa idade ainda é jovem (nos contratos do grupo Bybit primeiro, antes da listagem na Binance). Os dias maduros vêm de 60 contratos em USDT da Bybit fora do universo, sorteados com a semente 3, 4 datas aleatórias cada, em idades acima de 365 e entre 1 de junho de 2023 e 31 de julho de 2026, com apenas os negociados pela primeira vez a partir de 2022 contando para a faixa madura.

O arquivo de cada dia é reproduzido a partir de snapshot mais deltas, e a cada 60 segundos de tempo de exchange são registrados o meio-spread e o custo de uma ordem a mercado de $10,000 contra o mid, sendo esse custo a média de uma compra e de uma venda caminhadas pelo book. Por contrato-dia guarda-se a mediana de cada um, e por faixa, a mediana sobre os contratos-dia. O impacto é o custo cheio menos o meio-spread.

Dos 788 contratos-dia amostrados, 706 existem no arquivo.

Faixa de origem na BybitContratos-dia (contratos)Meio-spread, bpImpacto, bpCusto por fill, bp
2-7d270 (135)3.8125.2029.01
8-30d272 (136)3.9725.1729.14
acima de 365d164 (42)2.6127.2029.81

Como a amostra mede 3 faixas, as outras são mapeadas para a mais próxima medida: 31-90d toma o custo de 8-30d, enquanto 91-180d e 181-365d tomam o custo acima de 365d. Toda constante de custo das duas venues vive em um único arquivo, que tanto a medida quanto o congelamento leem (SHA-256 eb0e6aa42c367840b8d59fd8ac59ec43200bd968b4514ef53f594a0cd434e4eb).

Medida

Unidade: A observação é o contrato-dia, um dia UTC em que o contrato tem pelo menos uma barra horária; as idades 0 e 1 nunca são dias de observação, embora trades abertos nelas ainda contribuam para os dias em que são carregados da idade 2 em diante. O P&L bruto, a taxa, o slippage, o funding e o líquido de cada trade são espalhados igualmente pelos dias vivos, do dia de entrada ao dia de saída inclusive. O valor de um contrato-dia é a soma, sobre as 20 regras, de suas parcelas naquele dia, dividida por 20, em pontos-base, com os dias em que nenhuma regra carrega posição contando como zero.

share = 1.0 / (exit_day_idx - entry_day_idx + 1)
day_pnl[entry_day_idx : exit_day_idx + 1] += trade_pnl * share
daily_bp = day_pnl.sum(over_rules) / 20 * 1e4                # fixed library size

Volatilidade: A volatilidade realizada é o desvio-padrão dos últimos 48 retornos logarítmicos horários, contados em barras e não em horas de relógio. O valor do dia d é lido da barra que abre às 23:00 UTC do dia anterior, a barra que fecha exatamente quando o dia d começa, de modo que nada de dentro do dia d alcança sua própria covariável.

Se essa barra estiver faltando, o dia não tem volatilidade e não pode ser pareado. Com uma janela de 48 barras fechada antes de o dia começar, a idade 2 não tem estimativa, então o primeiro dia jovem com estimativa é a idade 3.

Faixas: As faixas de volatilidade são cortadas sobre o log natural da volatilidade realizada horária em −7.0, −6.5, −6.0, −5.5, −5.0, −4.5, −4.25, −4.0, −3.75, −3.5, −3.25 e −3.0, dando 13 faixas com extremos abertos. As faixas de idade vão de 2-7, 8-30, 31-90, 91-180 e 181-365 dias, com maduro em 366 dias ou mais e a janela jovem primária nas idades de 2 a 30.

Pareamento: Os contratos-dia maduros são primeiro agregados pela média dentro de cada célula (dia de calendário, faixa de volatilidade), para que uma célula cheia não pese mais que uma rala. Cada contrato-dia jovem é diferenciado contra a média madura de sua própria célula. Um dia jovem cuja célula não tem contrato maduro é contado como não pareado e nunca é comparado contra uma célula diferente.

A medida é a média das diferenças pareadas. Uma conferência de contagem de linhas garante que toda observação jovem sai como exatamente uma linha pareada ou uma linha não pareada.

ref = mature.groupby(["day", "vol_bucket"]).net.mean()
diffs = young.join(ref, on=["day", "vol_bucket"], how="left", rsuffix="_ref")
matched = diffs.dropna(subset=["net_ref"])
estimand = (matched.net - matched.net_ref).mean()
AmostraDias jovens pareadosNão pareadosParcela não pareada
Binance, idades 2-3015,2443,57319.0%
Bybit, idades 2-303,29598923.1%

Para a decomposição, o mesmo pareamento roda separadamente sobre bruto, taxa, slippage e funding, sobre cada faixa de idade (apenas quando ela tem pelo menos 30 linhas jovens) e sobre os 3 braços. Todos os braços vêm de um único fluxo de sinais: o long-short opera os dois sinais, o apenas long zera os sinais vendidos para caixa e o apenas short zera os sinais comprados.

Inferência: Um bootstrap percentil agrupado por semana sobre as diferenças pareadas usa 2,000 réplicas e a semente 20260810. As semanas são blocos de 7 dias contados a partir da época Unix, então vão de quinta a quarta em UTC, e toda observação jovem dentro de um bloco sorteado viaja junto.

As médias das células maduras ficam presas em seus valores de amostra completa, com a reamostragem apenas sobre as diferenças jovens. O intervalo de 95% são os percentis 2.5 e 97.5 das médias das réplicas.

rng    = np.random.default_rng(20260810)
block  = matched_day // 7                                  # epoch-day blocks
groups = [diff[block == b] for b in np.unique(block)]
draws  = [np.concatenate(rng.choice(groups, len(groups))).mean() for _ in range(2000)]
lo, hi = np.percentile(draws, [2.5, 97.5])

Leituras: A Binance roda sob as duas convenções intrabarra e é a amostra de desenvolvimento. A Bybit roda uma única vez sob a convenção conservadora e é o teste confirmatório. A regra de publicação foi fixada antes da execução na Bybit: um intervalo que exclui o zero com o mesmo sinal da estimativa da Binance é um efeito de idade replicado, e qualquer outra coisa é um nulo reportado com a largura de intervalo medida.

Congelamento

Antes de a medida na Bybit rodar, um pacote foi selado contendo os hashes SHA-256 de:

  • a implementação, incluindo a medida, o engine, o engine de triagem, o vocabulário de regras, a triagem da biblioteca, o módulo de constantes, o cliente do arquivo, o construtor de custos, o arquivo de custos, o construtor do universo da Bybit, o driver de análise e o próprio congelamento;
  • os artefatos: a biblioteca congelada, a triagem completa de 13,770 linhas, o universo da Bybit, a amostra de custos da Bybit, o censo e a lista completa de símbolos do arquivo;
  • toda série de dados de entrada, arquivo por arquivo: cada série de painel da Binance para todos os 832 contratos (klines horários, mark price, premium index, funding, caminho por minuto, profundidade e a amostra de profundidade), as amostras de spread cotado, os 986 registros do censo, mais 802 arquivos de klines da Bybit, 802 arquivos de funding da Bybit e 872 arquivos de amostra de book da Bybit;
  • as constantes registradas, lidas dos módulos que as executam: faixas de idade e de volatilidade, janela de volatilidade, tamanho da biblioteca, tamanho de posição, semente e número de réplicas do bootstrap, a grade, os limiares de elegibilidade e as tabelas de custo.

A selagem terminou às 22:26:51 UTC de 21 de setembro de 2026 sob o hash de pacote 23f5bf876015564577d880fe86f7a5ac22b370d05f42713a7ef4fb846c8d31e1. Uma cópia foi enviada às 22:28:34 UTC para um armazenamento de objetos fora da máquina, que registra um horário de upload que a máquina local não pode alterar, e os resultados da Bybit foram gerados às 22:32:53 UTC, com os 2 resultados da Binance vindo em seguida às 22:32:58 UTC.

Verificação: A medida refaz o hash de tudo que está no pacote antes de rodar e se recusa a começar se algum item tiver se mexido, ou se o horário do selo não for anterior ao da execução. Uma execução na Bybit também recusa uma biblioteca diferente da selada, um subconjunto de símbolos, um destino de saída diferente do selado e qualquer arquivo de saída da Bybit mais antigo que o selo. Cada arquivo de resultados registra então tanto o hash do pacote quanto o horário do selo.

ok, moved, bundle = verify(bundle_path)          # re-hash code, data, artifacts, constants
if not ok or bundle["created_utc"] >= now_utc():
    raise SystemExit("freeze does not verify; run refused")
results["seal"] = {"bundle_sha256": bundle["bundle_sha256"], "sealed_utc": bundle["created_utc"]}

Tabelas descritivas

Cada tabela da primeira metade do artigo é calculada como segue, com a idade sempre contada em dias inteiros desde o primeiro dia negociado do censo.

Como um contrato muda ao envelhecer: Todos os 692 contratos de evidência da Binance entram sem histórico mínimo, dos quais 691 contribuem. Por contrato e faixa de idade (0-1, 2-7, 8-30, 31-90, 91-180, 181-365, acima de 365 dias), as colunas usam as barras horárias daquele contrato com volume acima de zero e precisam de pelo menos 24 barras na faixa:

  • volatilidade: desvio-padrão dos retornos logarítmicos horários vezes √8,760;
  • correlação com o Bitcoin: correlação de Pearson dos retornos logarítmicos horários com BTCUSDT nos timestamps em comum;
  • volume contra o próprio nível maduro: o volume horário mediano cotado da faixa dividido pela mediana do mesmo contrato depois de 365 dias, apenas para os 352 contratos que alcançaram essa idade.

O funding é calculado por pagamento, e não por barra. Cada pagamento é reescalado para um equivalente de 8 horas como rate × 8 / interval (zeros incluídos) e atribuído à faixa da idade do pagamento. Por contrato, a média e o desvio-padrão são tomados sobre pelo menos 3 pagamentos, depois do que cada coluna é a mediana entre contratos.

r8 = rate * 8.0 / interval_hours                     # 1h, 2h, 4h and 8h payments comparable
per_contract = payments.groupby("bucket").r8.agg(["mean", "std"])   # needs >= 3 payments
table = per_contract_all.groupby("bucket").median()                 # median across contracts

Cronograma de liquidação: Todos os 832 contratos em USDT são cobertos pela coluna de intervalo por pagamento do arquivo, com o intervalo inicial tomado como o intervalo do primeiro pagamento de um contrato e tabulado por ano de listagem. "Nunca liquida em oito horas" e "muda de intervalo pelo menos uma vez" são calculados sobre o histórico completo de pagamentos de cada contrato. As parcelas de pagamento por idade agregam todos os pagamentos da faixa de idade (0-7 dias, 8-30 dias e assim por diante) entre contratos.

Spreads cotados por idade: Usam a amostra de meio-spread descrita em Custos, com medianas por faixa sobre os contratos-dia. A tabela do mesmo contrato usa os 86 contratos listados dentro da janela de cotações: por contrato, o meio-spread mediano em cada faixa; a razão entre seu valor de 0-1 dia e seu valor na faixa posterior; depois a mediana dessas razões e a parcela acima de um.

Profundidade e impacto por idade: Usam a amostra de profundidade e a varredura descritas em Custos, lado da compra, $10,000, com a primeira linha agregando as idades 3 e 7. A coluna do mesmo contrato divide o impacto mediano de cada contrato na faixa jovem por sua própria mediana depois de 365 dias, nos contratos que têm as duas, e depois reporta a razão mediana e a parcela abaixo de um.

Custos de execução na Bybit: A amostra de book descrita em Custos, 706 contratos-dia.

Teste de Abdi-Ranaldo: Os 86 contratos listados dentro da janela de cotações contribuem com as idades 0 a 60 da escada fixa, 987 contratos-dia. A estimativa por contrato-dia é a mediana, sobre as barras horárias daquele dia, do valor de Abdi-Ranaldo (valores negativos truncados em zero), apenas barras com volume acima de zero. O valor real contra o qual ela é comparada é o meio-spread cotado medido do mesmo dia.

As células são medianas sobre os pares de cada faixa de idade e a coluna de razão é a mediana das razões par a par, enquanto a correlação de postos é de Spearman sobre todos os 987 pares (+0.0349). O estimador devolve o spread efetivo cheio, o que responde por cerca de metade da diferença de nível, e dividi-lo por dois não muda a correlação de postos.

Sobrevivência: O fim do arquivo é o último dia negociado do censo (31 de julho de 2026), e um contrato é elegível para a marca de idade k se foi listado pelo menos k dias antes do fim do arquivo. Ele alcançou a marca se negociou por pelo menos k dias, ou se ainda estava negociando dentro de 7 dias do fim do arquivo. A parcela é alcançados sobre elegíveis, calculada separadamente para os contratos listados a partir de 2022 (692) e até 2021 (140).

eligible = archive_end - first_traded >= k_days
reached  = (last_traded - first_traded >= k_days) | (last_traded >= archive_end - 7_days)
share    = reached[eligible].mean()

Pedindo o pipeline completo

O pipeline completo (censo, construtor de painéis, amostradores de book, triagem, engine, medida, congelamento e scripts descritivos) não é distribuído a partir desta página. Para pedi-lo, escreva para daniel@daru.finance.