Onde o seu resultado fica depois de embaralhar os retornos alguns milhares de vezes — e o que esse número não responde.
O backtest termina e o Sharpe parece razoável. Mas aquela pergunta no fundo da sua cabeça continua sem resposta: a estratégia captou mesmo alguma coisa, ou você só deu umas cutiladas aleatórias dentro de um trecho de alta? Um jeito de responder é embaralhar os dados alguns milhares de vezes e ver em que posição o seu resultado fica naquela pilha de ruído — que é exatamente o que a linha «Valor-p do MCPT» na aba Backtest da área de trabalho faz. Este artigo explica o que ele testa, como se lê e qual pergunta continua sem resposta mesmo depois de ele passar.
O MCPT (Monte Carlo Permutation Test, teste de permutação de Monte Carlo) é um teste estatístico: ele reordena aleatoriamente a série de retornos do período do backtest muitas e muitas vezes, recalculando o Sharpe a cada vez sobre o mesmo conjunto de posições de entrada e saída, para montar uma distribuição inteira de «com que cara ficaria a pura sorte», e depois olha em que ponto dessa distribuição o seu resultado real cai.
| Item | Conteúdo |
|---|---|
| Hipótese nula | Os períodos de retorno que esta estratégia escolheu não são melhores do que os escolhidos ao acaso |
| Definição do valor-p | A proporção de Sharpes embaralhados que ficam maiores ou iguais ao Sharpe real |
| Leitura | p < 0.05 → Há uma vantagem estatisticamente significativa no nível de confiança de 95% |
| Escopo do teste | Todos os dados do backtest, sem divisão entre treino e teste |
Quanto menor o valor-p, mais raro é o caso de «embaralhar de qualquer jeito e ainda assim te vencer». Tomando as 2000 permutações padrão, p = 0.05 quer dizer que nessas 2000 vezes houve 100 resultados aleatórios que não perderam para você; p = 0.005 quer dizer que houve só 10.
Este é o ponto mais crítico de todo o desenho, e também o mais fácil de entender errado: o que é embaralhado é a série de retornos futuros, enquanto a sua série de posições fica fixa do começo ao fim.
O que cada permutação faz: position(as suas posições) ← fixo, calculado uma única vez fora do laço × vol_scalar(dimensionamento de posição) ← fixo, calculado uma única vez fora do laço − fee_cost(taxas) ← fixo, calculado uma única vez fora do laço × série de retornos embaralhada ← o único termo sorteado de novo a cada vez = Sharpe desta permutação
Como as taxas e o dimensionamento de posição são calculados fora do laço, toda permutação carrega o mesmo custo de transação e a mesma alavancagem. A única variável que sobra é a ordem em que os retornos aparecem — ou seja, os momentos que você escolheu são melhores do que momentos escolhidos ao acaso?
Porque isso seria entregar o resultado de bandeja. Embaralhar um vetor binário de posições cria muito mais trocas de entrada e saída do que a própria estratégia faz; com fee = 0.0005, cada permutação carregaria cerca de 30 a 40 vezes o peso das taxas, e todos os Sharpes embaralhados seriam empurrados para um terreno profundamente negativo. Tendo a sua estratégia uma vantagem real ou não, o valor-p sairia bonito e sem nenhum sentido.
Ele é automático, não opcional. Roda uma vez em todo backtest de estratégia Tipo A, e o resultado é escrito direto nas estatísticas do backtest e exibido na aba Backtest da área de trabalho.
| Configuração | Padrão | Observação |
|---|---|---|
| Permutações | 2000 | Dá para sobrescrever com MCPT_N na estratégia, mas continua sujeito à fórmula de orçamento abaixo |
| Semente aleatória | 42 | Um gerador próprio, que não mexe na semente global — o mesmo backtest sempre devolve o mesmo valor-p |
| Taxa | O FEE da própria estratégia | O que o backtest usar é o que é passado |
| Desligar | MCPT = False | Pula inteiro e não escreve nenhum campo de MCPT |
Dois comportamentos que vale a pena lembrar: durante uma varredura de parâmetros o MCPT não roda (uma varredura são dezenas a centenas de backtests, e incluí-lo deixaria tudo lento a ponto de ser inutilizável), então o valor-p que você vê vem sempre do backtest rodado depois que os parâmetros foram adotados; e, uma vez que a estratégia está no ar, cada disparo ao vivo ou agendado leva junto os mesmos campos de MCPT sem alteração — mesmo código, mesmos parâmetros, então o valor-p continua valendo, e é por isso que uma estratégia rodando continua exibindo ele o tempo todo.
A aba Backtest da área de trabalho não julga se passou ou não; ela só mostra o número e acrescenta uma linha de explicação. Quando p < 0.05 ela diz:
Quando não passa, ela diz:
O «passou ou não passou» de verdade aparece nos três critérios de qualidade da Biblioteca de estratégias. O critério 2 se chama «Estatisticamente significativa» e a descrição dele diz «Teste de permutação MCPT p < 0.05», enquanto o veredicto real exige duas condições ao mesmo tempo:
| Condição | Limiar |
|---|---|
| Valor-p | < 0.05 |
| Permutações | ≥ 1000 |
| Estratégia de carteira (Tipo C) | Mostra «MCPT não se aplica (estratégia de carteira)»; não conta como falha |
Só quando os três critérios passam ou não se aplicam é que a estratégia recebe o selo «Verificada». Para deixar clara a divisão de trabalho: a sua máquina só calcula o valor-p e o número de permutações; se isso conta como aprovado é decisão do lado da publicação.
Pegue o exemplo do tutorial btc_sma_cross que vem com a área de trabalho e rode uma vez (BTCUSDT 1h, a partir de 2022-01-01, SMA_FAST = 45 / SMA_SLOW = 100, sem mudar uma letra dos parâmetros, 41,287 barras). Todos os números abaixo vêm daquela única execução de 2026-09-17.
| Backtest | Valor |
|---|---|
| Retorno total | +123.95% |
| Benchmark (comprar e segurar) | +64.36% |
| Drawdown máximo | −44.93% |
| Sharpe Ratio | 0.6751 |
| Negociações | 478 |
| Taxas pagas (sobre o capital) | 23.90% |
| MCPT | Valor |
|---|---|
| Valor-p | 0.0160 |
| Permutações | 2000 |
| Linha vermelha do histograma, «Sharpe real» | 0.8985 |
| Faixa da distribuição | −0.8565 ~ 1.4218 |
A linha que o próprio backtest imprime no stdout é esta:
MCPT p-value: 0.0160 (n=2000, significant edge at 95%)
Como ler: p = 0.0160 quer dizer que, de 2000 embaralhamentos aleatórios, 32 produziram um Sharpe que não perdeu para o dele — em outras palavras, nesse trecho de histórico esses momentos de entrada e saída não parecem escolhidos a esmo. As 2000 permutações também estão acima das 1000 exigidas pelo critério, então este item está aprovado.
Esta é a seção mais importante do artigo. Aquela mesma estratégia, no mesmo dia e com o mesmo código, passou também por uma validação fora da amostra walk-forward:
| Teste | A pergunta que ele faz | A execução de 2026-09-17 | Resultado |
|---|---|---|---|
| MCPT | Nesta série fixa de posições, dá para distinguir este resultado de sorte? | p = 0.0160(n = 2000) | Aprovado |
| Walk-forward | O próprio ato de escolher parâmetros a partir do histórico continua funcionando no trecho seguinte? | Eficiência fora da amostra −0.334(Sharpe fora da amostra −0.45) | Muito abaixo do limiar |
Um passou, o outro não, e nenhum dos dois está calculado errado — porque simplesmente não estão respondendo à mesma pergunta. O MCPT testa que «este conjunto de entradas e saídas já decidido, colocado sobre este trecho de histórico, não parece chute»; o walk-forward testa se «escolher parâmetros a partir do histórico ainda se sustenta ao passar para o trecho seguinte, que ele nunca viu». Passar no primeiro não implica o segundo.
Isto não é dizer que a estratégia está quebrada — ela é o exemplo do tutorial que vem com a área de trabalho, e o ponto aqui é a diferença de significado entre os dois testes. Para os números completos do walk-forward e como lê-los, veja Como funciona a validação fora da amostra.
Se você pedir ao agente para desenhar o histograma do MCPT e mandar no chat, a legenda daquele gráfico vai imprimir uma linha:
Actual OOS Sharpe = ⟨o seu número⟩
OOS é a abreviação de out-of-sample (fora da amostra). Aquela linha é um rótulo que sobrou de um comentário antigo; não entenda ao pé da letra. O MCPT roda do começo ao fim sobre todos os dados do backtest, sem nenhuma janela de treino, janela de teste ou divisão por proporção — não tem como ser algo fora da amostra, e a tabela comparativa acima é a prova.
O gráfico da aba Backtest da área de trabalho não tem esse problema: a linha vermelha dele está rotulada como «Sharpe real». Além disso, o gráfico que aparece no chat é gerado por uma reexecução manual, e uma reexecução manual não usa necessariamente os mesmos parâmetros da automática, então os números do gráfico também não precisam bater com aquela linha da aba.
Quantas vezes o MCPT automático roda de fato não depende inteiramente de você. Existe uma fórmula de orçamento de execução:
permutações reais = min( MCPT_N, 20000, max( 200, 4e8 ÷ número de barras ) )
Quanto mais barras, menor fica 4e8 ÷ número de barras. A execução acima tinha 41,287 barras, bem longe do teto, então nenhuma das 2000 foi cortada. Mas a comparação registrada na documentação oficial diz: 40,000 barras → 2000; 200,000 barras (dois anos de 5 minutos) → 2000; 1,000,000 de barras (dois anos de 1 minuto) → 400.
O problema é que o critério de publicação corta em permutações ≥ 1000. Fazendo as contas pela fórmula, assim que o número de barras passa de cerca de 400,000, as permutações reais caem abaixo de 1000 — e o critério te reprova mesmo com p = 0.001. E na tela não há nenhuma pista: o aviso do corte só é gravado no log da máquina, o texto explicativo da área de trabalho insere tranquilamente o número já cortado, e não há nenhum estilo de alerta.
Volte às duas tabelas acima: no mesmo backtest, o cartão de Sharpe Ratio traz 0.6751 e o «Sharpe real» do histograma traz 0.8985, uma diferença de 0.22 que dá para ver a olho nu. Não é bug, são duas formas de cálculo diferentes:
| Item | O Sharpe interno do MCPT(0.8985) | O Sharpe Ratio da aba Backtest(0.6751) |
|---|---|---|
| Como os retornos são calculados | Retornos simples, sem separar o trecho de abertura do de fechamento | O trecho overnight e o intradiário são precificados separadamente |
| Dimensionamento de posição | Sempre aplicado:30% de volatilidade alvo、2 de teto de alavancagem | Só se a própria estratégia tiver escrito |
A chave está na segunda linha: o MCPT automático não passa adiante a configuração de volatilidade alvo da própria estratégia, ele usa sempre os valores padrão da função. O btc_sma_cross não faz alvo de volatilidade, mas o Sharpe de MCPT dele ainda é o número «depois de aplicar 30% de volatilidade alvo e um teto de 2×» (para o que a volatilidade alvo faz, veja Como funciona o alvo de volatilidade). Os dois números serem diferentes é normal; hoje a interface não explica isso.
Quando falha, falha em silêncio: nenhuma mensagem de erro, o backtest termina bem como sempre, e o front simplesmente fica sem aquela linha. São pelo menos quatro causas, e a tela não distingue entre elas:
| Causa | O que aconteceu |
|---|---|
| A estratégia escreveu MCPT = False | Pulado direto |
| Este backtest fez 0 negociações | Não há posições para testar, então é pulado |
| A lib da máquina é de uma versão antiga | O carregamento falha, então é pulado |
| Dados curtos demais, Sharpe que dá NaN, ou qualquer exceção | Pulado; as estatísticas do backtest são escritas como sempre |
O motivo de não escrever nada em vez de escrever errado é bem prático: qualquer NaN ou inf faz o upload de estratégias da máquina inteira ser rejeitado, então basta os números não estarem limpos para esses campos ficarem todos sem ser escritos.
Backtests do Tipo C (estratégias de carteira: N símbolos com um vetor de pesos) não rodam MCPT. O motivo é que a estrutura deste teste é «uma série de preços contra uma série de posições», e uma estratégia de carteira não tem essa estrutura. O que vale dizer com honestidade: não existe teste substituto.
| O que você quer fazer | O que acontece de verdade |
|---|---|
| Achatar a carteira inteira em uma só série de retornos e reamostrar | Isso é um bootstrap sobre retornos já realizados, responde a outra pergunta e ainda por cima não consegue produzir um valor-p — e o valor-p é justamente todo o sentido de o MCPT existir. Esse caminho é proibido por escrito, e o agente também não tem permissão para fabricar algo na mão como substituto |
| O que você espera que o cartão do critério mostre | «MCPT não se aplica (estratégia de carteira)», e não «Não aprovado» — não conta como falha e não afeta o selo «Verificada» |
Quantas permutações são? Se aquele número no texto explicativo estiver abaixo de 1000, este valor-p não passa no critério de publicação por menor que ele seja. Olhe o número primeiro, o valor-p depois.
Este valor-p é de qual backtest? A varredura de parâmetros não roda MCPT. O valor-p que você tem pertence só «ao backtest rodado depois que os parâmetros foram adotados»; se o código mudou no meio do caminho, é preciso rodar de novo para valer.
A linha vermelha não bate com o cartão? Normal. A execução acima foi 0.8985 contra 0.6751, só muda a forma de calcular; não há o que depurar.
Você não estaria lendo «significativa» como «vai dar lucro»? A mesma estratégia pode ser aprovada com p = 0.016 e naufragar com uma eficiência fora da amostra de −0.334. O valor-p não afirma absolutamente nada sobre o futuro.
Aquela linha simplesmente sumiu? Primeiro descubra qual das quatro causas é (a estratégia desligou / 0 negociações / lib antiga / dados insuficientes); não tome isso como «o teste não passou».
Entendido o valor-p, a próxima pergunta é a metade direita daquela tabela comparativa: será que se sustenta ao trocar para um trecho de dados que ele nunca viu? Esse caminho é a validação fora da amostra, e a área de trabalho tem uma aba própria para ela. As estratégias oficiais da Biblioteca de estratégias que levam o selo «Verificada» passaram no critério 2 exatamente neste teste de que o artigo fala — cada uma vem com backtest real e os números podem ser lidos direto: Biblioteca de estratégias.