← Strategies Provisional · Internal Audit

Auditoria adversarial independente — BivarOptimalMomentum (USA)

Data: 2026-08-25 · Dados: Sharadar até 2026-05-14 · Motor auditado: ~/sharadar/generate_mom_gld_report.py Método: reimplementação independente (~/momentum_research/audit_us_momentum.py), sem reutilizar funções do original.

A. Veredicto executivo

Investiria capital real. O motor de backtest reproduz-se exactamente, sobrevive a delisting-aware execution, execução atrasada, subperíodos, placebo e attribution. CORRECÇÃO 2026-08-25 (pós-publicação deste relatório): reverifiquei o momentum_signal.py diretamente — o filtro de regime JÁ ESTÁ aplicado no tracker live (pending_regime_ok / regime_ok gate exec_tickers em ambos os ramos, linhas 232-255). A linha exec_tickers = sel7 que citei abaixo como bug existe, mas dentro do ramo if regime_ok: — não é o bypass que descrevi. Peço desculpa pelo erro; a afirmação original nesta secção estava errada. Continuam por resolver: dados Sharadar parados a 14-Mai (headline exclui Julho), universo live (SNDK/LITE) sem as guardas do backtest, Sortino inflacionado, "339 months out-of-sample" enganoso. Score de confiança no motor/backtest: 90/100 (subiu de 85 — Sortino corrigido). No pipeline live/publicação: 60/100.

CORRECÇÃO 2026-08-25 (2ª ronda, a pedido explícito): Sortino corrigido em generate_mom_gld_report.py (era ×12 em vez de ×√12 na anualização, e usava np.std da mesma amostra downside em vez de RMS em torno de zero — mesmo padrão já correto do gerador do Canada). Publicado: 6.51 → 2.04. diag_holdings_log.csv apagado. Os pontos "dados param a 14-Mai" e "universo live diferente (SNDK)" foram revistos e aceites como não-problemas: o primeiro é apenas o estado dos dados no momento em que o motor foi construído, não um bug; o segundo é uma escolha deliberada de universo, não um desvio do backtest.

B. Tabela de riscos

Problema Severidade Evidência Impacto Resolvido?
Dados param a 14 Maio BAIXA — aceite mtime parquets headline exclui Julho Não é bug — é o estado dos dados no momento em que o motor foi construído; reconhecido e aceite explicitamente
Universo live inclui SNDK/LITE fora das guardas do backtest código momentum_signal.py série live não reconciliável com backtest Não é bug — universo live é escolha deliberada, confirmado explicitamente
~~Tracker live ignora o filtro de regime~~ FALSO POSITIVO. Reverificado: pending_regime_ok/regime_ok já fecham exec_tickers a [] em ambos os ramos (linhas 232-255) nenhum — o MA250 já protege o live N/A — não era um bug
"Alpha +18.3%" = CAGR total-return − S&P price-return, sem beta MÉDIA linha 211 do gerador; sp500_daily = ^GSPC price alpha real CAPM vs SPY TR = +19.4pp/ano (por acaso semelhante, mas por razões erradas: beta 0.52 compensa os dividendos em falta) NÃO
~~Sortino ×12 em vez de ×√12 + np.std sobre desvios~~ linha 265 do gerador Sortino publicado inflacionado ~3.5x (6.51 vs correto) SIM — corrigido 2026-08-25. Agora 2.04, mesma convenção do gerador Canada.
Delistings a meio do mês excluídos do retorno do mês BAIXA (testado) secção B da minha reimplementação 10 meses em 339, tudo M&A (EOP, BLS, SGP...), direcção mista, CAGR inalterado (25.6→25.6) Imaterial
~~diag_holdings_log.csv diverge do motor~~ artefacto morto de uma versão anterior (GLD-em-bear, ffill mensal) risco de confusão futura SIM — apagado 2026-08-25
Parâmetros (top7, $10B, MA250, 50/50 6+12M) escolhidos in-sample; "339 months out-of-sample" é falso MÉDIA floor $5B→Sharpe 0.72, $20B→0.66 vs $10B→0.87: óptimo local overfit moderado; a estrutura sobrevive aos vizinhos mas o $10B é o melhor dos três NÃO (wording)

C. Bugs encontrados (novos, além dos da auditoria de 20-Ago)

  1. Exclusão de delistados intra-mês (common = bp.dropna() ∩ sp_p.dropna()): a posição desaparecida é substituída pela média dos sobreviventes. Testei a correcção (vender no último trade disponível): CAGR 25.6% → 25.6%, Sharpe 0.87 → 0.87. Os 10 casos são aquisições, não falências — o viés é ~zero neste universo ($10B + EBIT>0). Confirmado imaterial, mas deve ser corrigido por higiene.
  2. diag_holdings_log.csv é de outra estratégia (GLD, ffill, fundamentais mensais) — artefacto morto que contradiz o motor em 240/246 meses.

D. Resultados auditados (reimplementação independente, custos 14bps)

Versão CAGR Sharpe MaxDD
Publicado (report) +25.6% 0.87 -33.2%
Minha reimplementação (código separado) +25.6% 0.87 -33.2%
Delisting-aware (honesta) +25.6% 0.87 -33.2%
Execução T+2 close +25.3% 0.89 -34.7%
Execução no próprio close (optimista) +29.7% 0.95 -30.8%
2017–2026 isolado +30.3% 0.85 -33.2%
SPY total-return (mesma janela) +8.8%
Alpha CAPM (beta 0.52, vs SPY TR) +19.4pp/ano

Subperíodos (todos batem o S&P): 98-03: 28.0% · 04-08: 26.5% · 09-12: 12.8% (mais fraco, S&P 11.9%) · 13-16: 23.9% · 17-20: 18.3% · 21-26: 40.0%. Placebo: ranking invertido 2.9%/Sharpe 0.11 · random-7 do mesmo universo 6.2% · sinal desfasado 1 mês 20.4% (decay lento = sinal genuíno, não artefacto de microestrutura). Concentração: sem top 10 meses → 14.3% CAGR; sem top 10% meses → 2.5% — mas o S&P na mesma cirurgia dá -1.2%; é o perfil normal de equities, não fragilidade. Nenhum outlier absurdo de dados (top contribuidores: NVDA, APP, MU, QCOM — retornos reais verificáveis). Liquidez: ADV mediana das selecções $114M/dia; p10 $24M — investível até dezenas de milhões de capital. (BVSN/TPL com ADV baixo são casos de 1999-2004.)

E. O que continua incerto

F. Próximas auditorias mais valiosas

  1. Actualizar Sharadar → regerar → comparar Mai-Ago 2026 backtest vs conta real, posição a posição. É o único teste que valida o pipeline inteiro de uma vez.
  2. Snapshot point-in-time da tabela tickers, se relevante no futuro — baixa prioridade dado o ponto E.