← Strategies Hostile Adversarial Audit · Full Report

Indonesia Momentum — Auditoria Hostil

Data: 2026-08-25 · Auditor: agente adversarial independente (2ª auditoria, sem confiança em auditorias anteriores)

Objecto: generate_indonesia_backtest.py / generate_indonesia_sweep.py; config alegada 12M skip=0, top 15 equal-weight, mcap ≥ 1T IDR, regime ^JKSE vs MA200, mensal, 2004-01→2026-07; resultado alegado CAGR +28.0%, Sharpe 0.99, MaxDD -41.8%, Alpha +18.2pp.

Artefactos da auditoria: ~/eodhd_data/audit2/ (scripts audit2_*.py, CSVs mensais, caches). Originais não modificados.

← Back to the plain-language strategy report

A. Veredicto executivo

Não investiria capital real nesta estratégia na sua forma actual, porque os números-manchete (28.0%/0.99) estão inflacionados por quatro camadas cumulativas — (1) universo sem QUALQUER delisting antes de 2017 e com <50% dos delistings reais depois de 2017 (survivorship estrutural do fornecedor EODHD), (2) configuração escolhida in-sample de uma grelha multi-parâmetro numa pesquisa multi-mercado (winner's curse), (3) zero custos de transacção com turnover de 32%/mês num mercado onde 28-41% das posições excedem 100% do volume diário, e (4) convenções favoráveis (benchmark price-only vs estratégia total-return; rf=0) — e porque a janela mais limpa e verificável (2017-2026), com custos e rf realistas, entrega Sharpe ≈ 0.4-0.7 e CAGR ≈ 15-16%, não 0.99/28%. O sinal cross-sectional de momentum é genuíno (o motor distingue-o claramente de placebos), mas o produto investável implícito é uma estratégia média com drawdowns de -35/-45%, não a máquina de Sharpe ~1 anunciada.

Confiança no veredicto: 85/100. (Os 15 restantes: a magnitude exacta do survivorship é inquantificável com estes dados; é concebível — mas improvável — que os delistings em falta fossem neutros.)

O que sobrevive à auditoria: a aritmética do motor (reproduzi 28.0/0.99/-41.8/+18.2 ao mês exacto com uma reimplementação 100% independente), a ausência de lookahead mecânico, a assimetria sinal-vs-inverso, o plateau de parâmetros e a persistência de algum alpha em todas as sub-janelas.


B. Tabela de riscos

# Problema Severidade Evidência Impacto estimado Resolvido?
1 Survivorship estrutural: 0/1014 históricos de preços terminam antes de 2017; apenas 26 mortes reais de acções (2017-25) vs ~40-60 delistings reais no período (só 2025: 15 oficiais; 2009-2016 dezenas documentadas, todas ausentes) CRÍTICA scan completo audit2_data_forensics.py; log do downloader ("53 delisted", 27 dos quais são warrants); fontes IDX/Databoks 2004-2016 inquantificável (RISCO NÃO RESOLVIDO); proxy: Sharpe cai de 1.36 (2004-16) para 0.66 (2017-26) ❌ Não resolúvel com estes dados
2 Selecção in-sample da config (12-0/1T/MA200 = melhor célula de grelha 5×7 + 6 MAs; Indonésia = melhor de pesquisa multi-mercado desde Aug-02) ALTA generate_indonesia_sweep.py STEP1/2; backtest_asia_latam.py (grelha JK anterior com outros parâmetros) Winner's curse; expectativa live < backtest mesmo sem mais nenhum bug. Mitigante: plateau suave (12-0:0.99, 6-0:1.06, 9-0:0.96) ⚠️ Mitigado por plateau, não eliminado
3 Custos = 0 com turnover médio 32%/mês (~7.7× ano só de compras) ALTA battery B5-B7 50bps/lado → 24.8%/0.90; 100bps → 21.7%/0.82; 200bps → 15.6%/0.64 ❌ Não incluídos no original
4 Investibilidade: a $1M de capital, 28% das posições >100% do ADV20; a $5M, 41%; top contributors (MGLV, PGUN, POLU, TFAS) negociavam $10-40k/dia quando "detidos" ALTA análise posição/ADV com FX anual; forensics dos top contributors O backtest tal como especificado é inexecutável à escala; variante com filtro ADV≥10B IDR mantém Sharpe 1.15 (0.87 em 2017+) — o alpha não morre, mas a estratégia auditada não é a executável ⚠️ Alpha sobrevive ao filtro; números-manchete não
5 Benchmark price-only vs estratégia total-return MÉDIA C2: bench TR (+3% yield) → alpha +14.9pp, não +18.2pp -3.3pp de alpha ❌ no original
6 Sharpe com rf=0 em moeda com rf≈6% MÉDIA C1: excess-Sharpe 0.85 (cash a render rf) Sharpe 0.99 → 0.85 ❌ no original
7 Limpeza de dados full-sample: 50 tickers excluídos a história inteira com base em eventos futuros; ~metade das exclusões são marginais (HERO, MAPI, ISAT, AKRA, TOWR, DILD, TMAS: 1 dia suspeito em 22 anos); UNVR excluída por um critério de dominância do índice sintético MÉDIA exclusion_reasons.json; A2-A4 Limpeza vale +0.4 a +1.0pp CAGR e +0.04-0.07 Sharpe vs limpeza mínima com princípios (27.6/0.95) ou nenhuma (27.0/0.92) ⚠️ Direcção favorável, magnitude pequena
8 Concentração: top-5 acções = 79% do P&L$; remover top-5 meses → 19.6%/0.89; 2016 +167%, 2025 +132%, 2026 YTD -28% MÉDIA análise de atribuição Cauda direita domina (típico de momentum, mas agrava o risco de survivorship/pump) ⚠️ Sobrevive parcialmente (top-5 zeradas: 22.3%/0.91)
9 Ano de 2004 morto (warm-up EMPTY) dentro das estatísticas "n=270" BAIXA A5/A6: com sinal pré-2004 real: 29.7%/1.03; a começar em 2005: 29.5%/1.02 Subestima o período investido (~+1.5pp); rotulagem enganosa, direcção anti-conservadora nula ✔️ quantificado
10 mcap_daily com níveis historicamente duvidosos (UNVR 2004 implica 0.7B acções vs 7.63B reais; TLKM idem) MÉDIA teste de acções implícitas por ano O corte de 1T IDR pode classificar mal empresas no passado; stress 0.5T/2T/5T mantém Sharpe 0.93-0.97 → efeito limitado, mas níveis não são fiáveis ⚠️ RISCO NÃO RESOLVIDO (nível), mitigado (robustez ao corte)
11 Warrants classificados como "Common Stock" no tickers.parquet (27 de 53 "delisted") BAIXA forensics Excluídos do universo apenas pelo gate lateral de fundamentais (por sorte, não por design); contaminam a alegação "inclui delisted" ✔️ identificado
12 Convenção delisting=0%: 0 posições em 270 meses atingiram morte terminal BAIXA (mas sintoma do #1) teste directo Impacto 0.0pp — a alegação anterior de "7 tickers afectados" está errada para esta config; a ausência total de mortes em 22 anos × 15 posições é em si evidência de survivorship ✔️ testado

C. Todos os bugs/vícios encontrados (localização exacta)

  1. Warm-up cego dentro das estatísticasgenerate_indonesia_sweep.py linhas 253-255 + 323-324: price_matrix só começa em 2004-01, logo mom_matrix.shift(12) é NaN durante 2004 inteiro → 12 meses "EMPTY" com ret=0 enquanto o benchmark corre, dentro dos n=270. Havia dados de 2003 disponíveis e não usados. Correcção (sinal pré-2004 real): 29.7%/1.03. Efeito: entendimento errado do período (efectivamente 2005-2026), direcção conservadora.
  2. Limpeza informada pelo futurogenerate_indonesia_backtest.py STEP 2 (linhas 92-210): um ticker é excluído da história INTEIRA se em qualquer dia (mesmo 2025) disparar um check. Cheques (d)/(e) capturam também eventos reais. 50 exclusões (incl. UNVR por dominância, linha 250-255). Antes/depois: limpeza original 28.0/0.99; limpeza mínima só-corrupção-dura (24 tickers: sentinela, >2000%/mês, broken-adjust) 27.6/0.95; sem limpeza 27.0/0.92.
  3. Gate de fundamentais escondido no universo — STEP 4 (linha 323): o universo exige ≥4 trimestres de EBIT mesmo com use_ebit=False (842 vs 936 tickers). Não enviesa materialmente (A2: 27.4/0.95) mas é um filtro não declarado na especificação da estratégia. (Testei e rejeitei a hipótese de exclusão dos bancos — EODHD preenche ebit para bancos JK.)
  4. Bug na auditoria anterioraudit_indonesia_independent.py linhas 56-58: o comentário afirma "null mcap on sentinel-close days" mas o código não o faz; execução é zero-lag (compra ao próprio fecho do sinal); warrants só parcialmente excluídos. Os seus números (32.4/1.03, reproduzi exactamente) validam menos do que o alegado.
  5. Alegação "verificação de lag 270/270" — o código só faz assert a 3 meses (backtest linhas 540-562, sweep 559-572). A minha comparação painel-completo confirmou que as datas M_j/E_j e os modos CASH/MOM coincidem 270/270 — a lógica está certa; a alegação de verificação exaustiva era falsa.
  6. tickers.parquet: 27 warrants com Type='Common Stock' — bug de classificação do fornecedor/downloader que infla a contagem de "delisted names" citada como defesa contra survivorship.
  7. Sem custos, benchmark PR, rf=0 — ver riscos #3, #5, #6 (não são "bugs" de código, são convenções que empolam os manchetes).

Sem bugs encontrados em: construção do calendário (M_j = último dia de negociação real; E_j = dia seguinte real — 270/270 idênticos na reimplementação); lag sinal→execução; lógica as-of do regime (JKSE e MA200 avaliados a M_j, executados a E_j — implementável); ffill limitado a 5 dias (apenas passado); ordenação/desempate do ranking; aritmética de CAGR/Sharpe/MaxDD. A reprodução independente convergiu ao mês exacto (corr. mensal 1.000, mesmos 4 decimais nos agregados) quando alimentada com o mesmo universo — toda a divergência inicial (overlap médio 13.4/15 picks) foi rastreada à lista de exclusões, e a nada mais.


D. Resultados auditados

Variante CAGR Sharpe MaxDD Bench Alpha
Original (alegado e reproduzido ao decimal) +28.0% 0.99 -41.8% +9.9% (PR) +18.2pp
Reimplementação independente, mesmo universo +28.0% 0.99 -41.8% +9.9% +18.2pp
Reimpl., limpeza mínima com princípios +27.6% 0.95 -43.4% +9.9% +17.7pp
Reimpl., universo sujo (sem exclusões) +27.0% 0.92 -46.1% +9.9% +17.1pp
Benchmark comparável TR (JKSE +3% yield, pressuposto declarado) +28.0% 0.99 -41.8% +13.2% +14.9pp
rf=6% (cash rende rf; excess-Sharpe) +30.2% 0.85 -39.4% +9.9% +20.3pp
Execução: zero-lag / next-open / +2 dias +29.3 / +28.6 / +26.5% 0.98 / 0.97 / 0.93
Custos 50 / 100 / 200 bps por lado +24.8 / +21.7 / +15.6% 0.90 / 0.82 / 0.64 -46/-50/-56% +9.9% +15.0/+11.8/+5.7pp
2004-2016 (janela suja) +34.7% 1.36 -24.8% +16.3% +18.4pp
2017-2026 standalone (janela mais limpa) +19.1% 0.66 -41.8% +1.7% +17.4pp
2017-2026 + 100bps + rf6% +15.1% 0.56 (0.38 excess) -47.5% +1.7% +13.4pp
Executável: filtro ADV20≥10B IDR, full / 2017+ / 2017+ c/100bps +28.6 / +23.1 / +16.2% 1.15 / 0.87 / 0.67 -32/-32/-34%
Placebo: aleatório ×20 (mesma pipeline) +12.6% [9.6-15.5] 0.76 [0.63-0.88] -28.5% 9.9% +2.7pp
Placebo: aleatório sem regime ×10 +13.4% 0.62 ≈ JCI (0.60) ≈0
Placebo: ranking invertido (bottom-15) +1.5% 0.18 -80.5% +9.9% -8.3pp
Placebo: sinal com +1 mês de atraso +21.4% 0.89 -38.4% +9.9% +11.6pp

Leitura dos placebos (secção 9): a pipeline NÃO fabrica Sharpe do nada — aleatório sem regime ≈ mercado (0.62 vs 0.60). O regime MA200 acrescenta ~+0.14 de Sharpe a qualquer carteira; o momentum acrescenta ~+0.23 acima do aleatório-com-regime e o inverso colapsa (-80% DD). O sinal é real; os manchetes é que não são.

Subperíodos (cortes fixos): 2004-08: 30.1%/1.34 · 2009-12: 56.2%/1.64 · 2013-16: 30.1%/1.22 · 2017-20: 12.5%/0.61 · 2021-26: 25.0%/0.72. Primeira metade 1.29, segunda metade 0.85. Anos: 2016 +167%, 2025 +132%, 2026 YTD -28%. Hit rate global 63%; turnover 32%/mês.

Concentração: top-5 acções = 79% do P&L$ (MGLV +70.6, BBHI +42.3, MLPT +31.5, BUVA +30.8, PGUN +28.9 por unidade inicial); zerar top-5 → 22.3%/0.91; remover top 5 meses → 19.6%/0.89; remover top 10% meses → 3.4%/0.28 (assimetria de cauda esperada em momentum, mas concentrada em micro-caps recentes de perfil "saham gorengan").


E. O que continua incerto (explicitamente)

  1. Magnitude do survivorship 2004-2016 — INQUANTIFICÁVEL com estes dados. Zero delistings pré-2017 no dataset; os mortos em falta (BORN, DAVO, TRUB, SOBI, BAEK, etc.) eram exactamente do tipo que o momentum compra antes do colapso. O diferencial 1.36→0.66 entre janelas mistura survivorship com mudança de regime — não é separável. RISCO NÃO RESOLVIDO.
  2. Survivorship residual 2017-2026: o dataset tem ~26 mortes reais vs ~40-60 delistings oficiais no período (2025 sozinho: 15 oficiais vs 10 no dataset) — até a "janela limpa" está incompleta. Os números 2017+ desta auditoria são tectos, não estimativas centrais.
  3. Fiabilidade dos níveis de mcap histórico (acções implícitas de UNVR/TLKM incompatíveis com valores conhecidos nos anos 2000). Robustez ao corte mitiga; não elimina.
  4. PR da estratégia tecnicamente irrecuperável com EODHD (adjusted_close mistura splits+dividendos sem factores separáveis); o TR do benchmark usa o pressuposto declarado de 3% de yield.
  5. Custos reais IDX (spread, impacto, levy de venda 0.1%): não medidos com dados de mercado; 50-200bps/lado é um intervalo plausível, não uma medição. IBKR não testado para os nomes IDX.
  6. ^JKSE via yfinance: fonte não auditada; corroborada apenas pela correlação com o índice sintético interno.
  7. Preços de fecho de nomes com $10-40k/dia de volume: mesmo "sem custos", o próprio print de fecho pode não ser transaccionável em tamanho algum relevante.

Secção 11 — Julgamento das alegações anteriores

Alegação Veredicto
"Verificação exaustiva de lag 270/270" FALSA-EXAGERADA (código só assevera 3 meses; a lógica em si está certa — verifiquei eu os 270)
"Sentinela 999999.9999 encontrada e neutralizada na price_cache" CONFIRMADA (4 tickers com sentinela exacta, BCIC com 5044 dias; canal derivado via close cru tratado; reproduzi a neutralização de forma independente)
"50 tickers estruturalmente excluídos" CONFIRMADA COM RESSALVAS (50 = 49 checks + UNVR por dominância; ~metade das exclusões assenta em 1-2 dias suspeitos em 22 anos e usa informação futura; vale +0.4-1.0pp)
"tickers.parquet inclui delisted names" CONFIRMADA COM RESSALVAS → materialmente enganosa (53 flags, mas 27 são warrants; só 26 acções mortas, todas ≥2017)
"Reimplementação independente anterior concordou em 32.4%/1.03" CONFIRMADA (reexecutei: 32.4/1.03) mas o script tem execução zero-lag e um fix de mcap prometido em comentário e não implementado — valida menos do que anuncia
"Survivorship: zero delistings pré-2017 nos dados" CONFIRMADA — e agravada: também faltam >50% dos delistings pós-2017
"Alpha sobrestimado 2-3pp por benchmark price-only" CONFIRMADA (3.3pp com yield 3%)
"Sharpe com rf≈6% é ~0.90" CONFIRMADA COM RESSALVAS (o meu cálculo estrito: 0.85; 0.85-0.90 conforme convenção de cash)
"Plateau suave, não pico isolado" CONFIRMADA (grelha própria: 6-0:1.06, 9-0:0.96, 12-0:0.99, 12-1:0.89; cortes 0.5/1/2/5T: 0.93-0.97) — mas não anula o winner's curse multi-mercado
"2016/2025 são empresas reais identificáveis" CONFIRMADA COM RESSALVAS (BBHI/ARTO/PANI reais e líquidas; MGLV/PGUN/POLU/TFAS negociavam $10-40k/dia — reais mas inexecutáveis)
"Delisting exit 0% afecta 7 tickers, efeito pequeno" NÃO CONFIRMADA (nesta config: 0 posições atingem morte terminal; efeito 0.0pp — o número "7" não corresponde a nada verificável aqui)

Secção 10 — Arqueologia de código

Sem repositório git. Todos os scripts Indonésia criados em lote a 2026-08-25 07:13-07:14 (mesmo template dos motores AU/TH/MY, gerados no mesmo minuto); auditoria anterior às 07:53. Não existem versões antigas nem outputs contraditórios para JK — sem incidente de versões. Existe porém backtest_asia_latam.py (Aug-02) com uma grelha JK anterior (mcap 4.8T, N∈{5..30}, vários MAs) — prova de que o espaço de pesquisa total sobre este mercado é muito maior do que o declarado no sweep, reforçando o risco #2.


F. Próximas 3 auditorias mais valiosas

  1. Reconstruir o universo com a lista oficial de delistings do IDX/KSEI (2004-2026) — obter a lista pública de emitentes delistados (IDX publica; academia cobre 2009-2016), cruzar com o dataset, e para 2017-2026 injectar os mortos em falta com retorno terminal -50/-100% num re-run: transforma o maior risco (survivorship) de "inquantificável" em "medido" pelo menos na janela recente.
  2. Medição real de executabilidade via IBKR — para os 15 nomes do sinal actual e uma amostra histórica: spreads, profundidade, disponibilidade dos tickers IDX, custo total estimado por escalão de AUM ($100k/$1M/$5M); substituir a curva 50-200bps por números medidos.
  3. Paper-trading de 6-12 meses com a regra exacta congelada (incluindo o filtro de liquidez executável), comparando fills reais vs fechos do backtest — o único teste que nenhum dataset histórico pode falsificar. (O -28% de 2026 YTD já é o primeiro dado quase-live e é negativo.)