Cada número da nota, e de onde ele veio
Método suficiente para conferir a aritmética à mão, ou para reconstruir um estudo equivalente a partir das mesmas fontes públicas e licenciadas.
Dados e universo
Ações. Dez ações americanas de grande capitalização, selecionadas em 31 de dezembro de 2022 pelo volume mediano diário em dólares entre outubro e dezembro de 2022. A fonte são dados de negócio e cotação de 1 minuto elegíveis para NBBO, excluindo lotes fracionados e venues fora do NBBO. Os instrumentos são chaveados por um identificador de segurança estável, com fatores de ajuste encadeados entre mudanças de identificador, para que splits e reuso de ticker não corrompam os retornos.
Cripto. Dez contratos perpétuos da Binance com margem em USDT, selecionados da mesma forma a partir de arquivos públicos de klines e funding, cada um exigindo 24 meses de volume genuinamente negociado antes do início da amostra.
Amostra. O teste fora da amostra roda de 1º de janeiro de 2023 a 31 de julho de 2026, nas frequências de barra de 15 minutos e 1 hora, para os dois mercados.
Conjunto de features
Toda família de modelo vê o mesmo conjunto de 62 features, construídas apenas a partir de OHLCV: retornos e momentum em várias defasagens, volatilidade realizada rolante, posição na faixa, distância até a máxima e mínima rolantes, razões e variações de volume, e features de formato de candle como fração do corpo e tamanho dos pavios. Nenhuma coluna de custo, spread, taxa ou funding está presente na matriz de features, e isso foi verificado programaticamente antes de qualquer resultado fora da amostra ser produzido.
# volatilidade realizada de 6 barras e posição dentro da faixa recente de máxima/mínima
df["vol_6"] = df["ret_1"].rolling(6).std()
df["pos_in_range_6"] = (
(df["close"] - df["low"].rolling(6).min())
/ (df["high"].rolling(6).max() - df["low"].rolling(6).min())
)Busca de modelo, travada antes do OOS
Três famílias de modelo: um modelo linear regularizado, um ensemble de árvores com gradient boosting e uma pequena rede neural feed-forward. Cada família recebe exatamente 30 configurações candidatas, geradas e com hash calculado uma única vez, antes de qualquer janela fora da amostra ser pontuada. Cada execução confere o hash antes de tocar nos dados, e uma divergência aborta a execução em vez de deixar a grade derivar silenciosamente entre sessões.
import hashlib, json
configs = {"linear": linear_grid, "gbt": gbt_grid, "mlp": mlp_grid}
digest = hashlib.sha256(json.dumps(configs, sort_keys=True).encode()).hexdigest()
assert digest == "6c6d89b6cfd435ce875876820443405f5d383956a5673ea38f5d2d410b490e47"Mecânica walk-forward
Seis comprimentos de treino (1, 2, 3, 6, 12 e 24 meses calendário) cruzados com três cronogramas fora da amostra (fixo de um mês, fixo de três meses, e dois terços do comprimento de treino) dá 216 células de resultado. Caminhar isso para frente ao longo de toda a amostra produziu 5.868 janelas de modelo rolantes e 176.040 tentativas de configuração no total.
A seleção dentro de cada janela é cronológica, nunca embaralhada, e a configuração vencedora é re-treinada na janela in-sample completa antes de sequer tocar no bloco fora da amostra. Os scores de configuração, metadados, previsões e o ledger de trades completo de cada janela são persistidos antes de um sentinel de conclusão ser escrito, então a execução é retomável e verificável janela por janela.
for window in rolling_windows(train_months, schedule):
fit_rows, val_rows = chronological_split(window.is_slice, frac=0.8)
scored = [fit_and_validate(cfg, fit_rows, val_rows) for cfg in configs[model_family]]
winner = max(scored, key=lambda s: s.validation_ic)
final_model = fit(winner.config, window.is_slice) # re-treina na janela IS completa
preds = final_model.predict(window.oos_slice) # pontuado exatamente uma vez, fora da amostraRegra de portfólio e modelo de custos
O alvo de previsão é o retorno de abertura-para-fechamento da próxima barra. A regra de trading compra as duas previsões cross-sectional mais altas e vende as duas mais baixas, a 25% de peso absoluto cada, entrando na abertura da barra seguinte e saindo no fechamento dessa mesma barra.
book = pd.concat([
ranked.head(2).assign(side=+1),
ranked.tail(2).assign(side=-1),
]).assign(weight=lambda d: 0.25 * d["side"])
gross = book["weight"] * book["fwd_return"]
net = (
gross
- book["weight"].abs() * book["spread"]
- book["weight"].abs() * fee_bps # 5 bps taker (cripto), 0.5 bps (ações)
- book["weight"].abs() * slippage_bps # 2 bps (cripto apenas)
+ book["weight"] * book["funding_signed"] # cripto apenas, na barra real de settlement
)Cripto paga o spread mensal medido, uma taxa taker de 5 bps e 2 bps de slippage por execução, mais funding sinalizado real no seu evento de settlement de oito horas verdadeiro: funding positivo debita posições compradas e credita posições vendidas, funding negativo inverte isso. Ações pagam o spread medido por barra e 0.5 bps por execução, sem perna de funding. Bruto, spread, taxas, slippage, funding e líquido são mantidos como colunas de ledger separadas para cada posição, e o agregado reconcilia com esses componentes exatamente. O maior erro de identidade observado ao longo de toda a execução foi 0.0 bps.
Inferência estatística
As comparações usam um bootstrap agrupado por semana sobre a série pooled fora da amostra, não a média de Sharpe por janela nem reamostragem por linha, porque os retornos são correlacionados serial e cross-sectionalmente. O procedimento de Benjamini-Hochberg controla a taxa de descoberta falsa em toda célula testada, incluindo as perdedoras.
boot_diffs = week_clustered_bootstrap(shorter_length_pnl, baseline_24m_pnl, n_boot=10_000) p_values = [two_sided_p(observed_diff, boot_diffs) for observed_diff in cell_diffs] significant = benjamini_hochberg(p_values, alpha=0.05)
Credibilidade e checagens de vazamento
Antes de qualquer número fora da amostra ser inspecionado: o teste de truncamento futuro reproduziu toda feature com erro máximo de 0.0; o alvo de previsão bateu com o retorno realizado de abertura-para-fechamento da próxima barra com margem de 0.0000018 bps; todos os eventos de funding na amostra de gate bateram com seus timestamps brutos de exchange; um controle positivo com o alvo plantado diretamente no conjunto de features elevou o IC pooled de 0.0172 para 0.9999; e três controles negativos de rotação circular, que destroem qualquer alinhamento temporal genuíno, retornaram ICs de 0.0165, 0.0418 e −0.0461, consistentes com ruído.
Um guard genérico de lookahead inicialmente sinalizou a maioria dos bundles representativos, porque ele assume que um score correlacionado com o retorno no timestamp t foi negociado durante esse mesmo retorno. A convenção deste engine é diferente: o retorno em t é um input observado no fechamento daquela barra, e a posição entra na abertura da barra seguinte. Uma verificação de timeline linha a linha, unindo decisões, entradas, saídas e alvos de volta ao painel de origem, em 3.563.992 linhas de previsão, encontrou zero erros de timing, zero divergências de preço de entrada ou saída, e um erro máximo de reconstrução do alvo de 0.0000008 bps — confirmando que era a suposição do guard, e não o pipeline, que estava errada.
Ambiente
Python 3.10.12 polars 1.40.1 numpy 2.2.6 scipy 1.15.3 scikit-learn 1.7.2 lightgbm 4.6.0 torch 2.11.0+cu130 matplotlib 3.10.9 pyarrow 24.0.0 markdown 3.10.2
Todas as seeds de modelo, bootstrap e geração de configuração são fixas. Uma janela só é reaproveitada de uma execução anterior quando seu sentinel de conclusão e o hash de configuração concordam com os arquivos da execução atual; caso contrário, ela é recomputada.
Os gráficos na nota são desenhados a partir das tabelas de resultado publicadas, as mesmas tabelas de onde vêm os números no texto.
Pedindo o pipeline completo
Esta página é suficiente para conferir cada número da nota à mão, ou para reconstruir um estudo equivalente a partir das mesmas fontes de dados públicas e licenciadas. Os scripts de produção — coleta de dados, construção do painel, o runner do experimento, a análise e a geração das figuras — não estão anexados. Envie um e-mail para daniel@daru.finance e eu envio para você.

