De PyTorch a 27 Megabytes: Um Filtro Neural de Trades num VPS Barato

De PyTorch a 27 Megabytes: Um Filtro Neural de Trades num VPS Barato

Treinamos uma rede neural para filtrar trades ao vivo, deployamos num VPS de $9, descobrimos que nosso provedor de dados estava alucinando barras de fim de semana — e depois removemos a NN inteira porque 302 trades nunca foram suficientes para generalizar. A engenharia de deploy foi real. O edge não.

neural-networkbacktestinglive-tradinginfrastructure

Atualização (Março 2026): O filtro NN foi removido do trading ao vivo em ~25 de fevereiro de 2026. Estava fazendo overfitting em 302 trades. A estratégia IPDA CE crua sem o filtro se mostrou mais pura. Epílogo completo abaixo.

O Problema Com Win Rates

Nossa estratégia EURUSD de 30 minutos sobreviveu a cinco rodadas de deflação honesta. O piso: 302 trades, 59.6% win rate, +463 pips, profit factor 1.48 em 2.5 anos.

40% dos trades perdem. Alguns setups cheiram errado antes da entrada — chop de segunda de manhã, ruído no meio da sessão, sinais obsoletos. A pergunta: uma rede neural consegue aprender quais trades pular?

A resposta acabou sendo não. Mas a jornada nos ensinou coisas que valem a pena guardar.

Os Padrões Condicionais

Antes de treinar qualquer coisa, rodamos análises post-hoc no baseline de 302 trades:

Segunda domina o PnL — terça e quinta são negativos líquidos

Segunda sozinha carrega 80% do lucro total (PF 3.56, 73.9% WR). Terça e quinta são negativos líquidos. Isso se manteve ao longo de 2.5 anos.

Horários de abertura de sessão ganham, horários do meio da sessão perdem

O padrão se repete intraday: 09:00 UTC (abertura de Londres) e 13:00 UTC (abertura de NY) são vencedores. Horários no meio da sessão perdem. O edge se concentra nas transições de sessão.

Esses padrões condicionais são reais. O problema não eram os padrões — era tentar ensinar uma rede neural a capturá-los com 302 amostras. Um humano poderia simplesmente operar segundas-feiras e aberturas de sessão com regras fixas. Mas nós queríamos fronteiras suaves, filtros probabilísticos. Essa ambição precisava de ordens de magnitude mais dados do que tínhamos.

O Que a Rede Vê

Onze features. Sem dados de preço — a estratégia já tomou sua decisão estrutural. A NN vê apenas metadados:

features = [
    is_london_open,          # sessão (binário)
    is_long,                 # direção (binário)
    hour_sin, hour_cos,      # codificação cíclica da hora
    dow_sin, dow_cos,        # codificação cíclica do dia da semana
    signal_age_normalized,   # idade do setup (0-20 dias)
    signal_size_normalized,  # tamanho do setup em pips
    risk_pips_normalized,    # distância do SL
    is_monday,               # flag de segunda-feira
    is_session_open,         # flag de hora de abertura de sessão
]

Codificação cíclica importa. Hora 23 e hora 0 são vizinhas, não estão a 23 de distância. Sem isso, a rede acha que sexta-feira está maximamente distante de segunda.

Arquitetura: Deliberadamente Simples

Input (11) → Linear(32) → ReLU → Linear(16) → ReLU → Linear(1) → Sigmoid

Três camadas. 593 parâmetros. Deliberadamente pequena porque 302 trades é um dataset minúsculo. Uma rede mais profunda memorizaria.

Ela memorizou mesmo assim.

Treinamento: ensemble de 10 modelos com seeds aleatórias diferentes, state dicts com média. BCEWithLogitsLoss, Adam, 300 epochs, batch 32. O ensemble deveria suavizar ruído dependente de seed. Com tão poucas amostras, ele suavizou ruído num sabor diferente de ruído.

O Efeito do Filtro (In-Sample)

Antes e depois: impacto do filtro NN em ambos os pares

EURUSD no limiar P(win) ≥ 0.45:

MétricaBaselineNN FiltradoMudança
Trades302188-38%
Win Rate59.6%66.5%+6.9pp
Profit Factor1.482.22+50%
Pips Totais+463+460-0.6%

Mesmo lucro, 38% menos trades, PF dramaticamente melhor. No papel, a rede removeu ruído sem sacrificar edge.

A curva de equity ficou mais limpa:

Curva de equity: caminho filtrado é mais suave com menos drawdown

A curva filtrada (azul) acompanha o mesmo ponto final que o baseline (cinza) mas com menos drawdown e menos períodos flat. Bonito. Suspeito.

Como os Scores se Separam

Distribuição P(win): vitórias agrupam à direita, perdas agrupam à esquerda

Os scores P(win) não estavam perfeitamente separados — 58.6% de acurácia OOS. As distribuições mudaram ligeiramente: vitórias agruparam mais alto, perdas mais baixo. O limiar de 0.45 cortou a cauda esquerda onde perdas dominam.

58.6% de acurácia OOS numa tarefa de classificação binária com 302 amostras. Isso deveria ter sido o sinal de alerta. Isso mal está acima do acaso, e “mal acima do acaso” numa amostra pequena é indistinguível de ruído.

Escolhendo o Limiar

Varredura de limiar: PF sobe enquanto pips totais se mantêm, depois caem

Escolhemos 0.45 porque ficava no joelho: PF estabilizou em ~2.2 enquanto pips totais ainda acompanhavam o baseline. Acima de 0.50, o filtro matava vencedores reais também. Abaixo de 0.40, trades marginais demais sobreviviam.

Ajuste de limiar num dataset de 302 amostras é ajustar uma curva a estática.

Cross-Pair: Não Transfere

Testamos o modelo EURUSD no GBPUSD. Acurácia de transferência: 44%. Pior que jogar uma moeda. Padrões temporais são específicos por par.

Um modelo GBPUSD independente mostrou o mesmo padrão in-sample — PF saltando de 1.41 para 1.86 enquanto preservava pips. Mesma miragem de 302 amostras, par diferente.

EURGBP baseline PF era 1.08. Morto na chegada.

Validação Estatística (Que Não Validou o Que Achávamos)

Bootstrap Monte Carlo — 200 caminhos de equity simulados:

Monte Carlo: a estratégia é positiva na grande maioria dos caminhos

Mesmo o resultado do percentil 5 produz +188 pips. O detalhamento mensal:

PnL Mensal: 21 de 32 meses lucrativos

21 de 32 meses lucrativos. Pior: -38 pips. Melhor: +129 pips.

Drawdown: máximo ~100 pips, recupera em prazos razoáveis

Drawdown máximo: -101 pips (~12% de drawdown na conta com 1% de risco).

Essas estatísticas validaram a estratégia baseline, não o filtro NN. O edge bruto do IPDA CE é real. O Monte Carlo prova que a distribuição subjacente de trades se sustenta. A contribuição da NN? Não validada em nenhum período out-of-sample que importa.

O Problema do VPS

Trading ao vivo roda num VPS Windows Contabo de $9/mês — 2 vCPUs, 4GB RAM, MT5, Task Scheduler a cada 30 minutos.

PyTorch CPU-only: ~2GB instalado. Num VPS de 4GB rodando MT5 e Windows, isso não é viável. O modelo tem 593 parâmetros. Três multiplicações de matrizes e dois ReLUs. Precisamos de multiplicação, não diferenciação automática.

A Reescrita em Numpy

Esse trabalho de engenharia continua genuinamente útil independente do valor preditivo da NN.

PyTorch para produção: dependência de 2GB para 3.7KB em RAM

# Exportar uma vez (requer torch, rodar localmente):
def export_weights(pt_path, json_path):
    checkpoint = torch.load(pt_path, map_location="cpu")
    sd = checkpoint["model_state_dict"]
    weights = {
        "W1": sd["net.0.weight"].tolist(),
        "b1": sd["net.0.bias"].tolist(),
        # ... mesmo para camadas 2, 3
    }
    Path(json_path).write_text(json.dumps({"weights": weights, ...}))

# Inferência (apenas numpy, roda no VPS):
class TradeScorer:
    def _forward(self, x: np.ndarray) -> np.ndarray:
        h1 = np.maximum(0, x @ self._W1.T + self._b1)   # ReLU
        h2 = np.maximum(0, h1 @ self._W2.T + self._b2)  # ReLU
        return (h2 @ self._W3.T + self._b3).squeeze(-1)  # logit

Arquivos de pesos: 21KB JSON. Memória em runtime: 3.7KB. Inferência: ~0.1ms por trade. O VPS saiu de “não dá pra instalar torch” para “não precisa de torch.” Se você precisar deployar um modelo PyTorch pequeno num ambiente com recursos limitados, esse padrão funciona. Exporte os pesos como JSON, reconstrua o forward pass em numpy. Sem ONNX, sem TorchScript, sem dependências de runtime além do numpy.

Defesa em Duas Camadas

  1. Motor de sinais: .filter() descarta ordens com P(win) < limiar
  2. Orquestrador de trades: gate defensivo rejeita qualquer sinal que passe

Cinto e suspensórios. Esse padrão de arquitetura — defesa em profundidade para execução de trades — permanece sólido mesmo após removermos o componente NN.

A Descoberta das Barras Fantasma

Essa foi a descoberta real.

Durante o deploy, trocamos a fonte de dados de uma API de terceiros para o copy_rates_from_pos() nativo do MT5. Mesmo símbolo, mesmo timeframe, mesma contagem de barras. Resultados diferentes.

A API retornava barras até sábado — dados sintéticos de fim de semana. Essas barras fantasma consumiam slots na nossa janela de 500 barras, empurrando dias de negociação reais para fora:

Fonte500 barras cobremÚltima barra
API de terceiros11 Fev → 21 Fev (Sáb)Sábado 17:00 ⚠️
MT5 direto6 Fev → 20 Fev (Sex)Sexta 23:30 ✅

MT5 dá 5 dias reais de negociação a mais de histórico. Dois sinais pendentes que existiam sob a API desapareceram sob o MT5 — corretamente identificados como já invalidados por barras que a janela truncada da API não conseguia ver.

Seu provedor de dados faz parte da sua estratégia. Mesma lógica, dados diferentes, trades diferentes. Cortamos o intermediário. Essa lição se aplica a toda estratégia sistemática, com NN ou não. Se você está usando uma API de dados de terceiros para alimentar sinais no MT5, compare a saída dela contra os dados nativos do MT5. Diferenças não são casos de borda — são divergência de execução.

Semana 1: Resultados Ao Vivo

Deploy em 17 de fevereiro de 2026 com €10.000 demo. O filtro NN não estava ativo para esses trades — são baseline puro:

#DataDireçãoResultadoPipsP&L Líquido
118 FevLONG❌ SL-10.0-€181
219 FevLONG✅ TP+10.7+€229
320 FevLONG❌ SL-8.5-€193

Semana 1: -€145.67 (-1.46%). 1 vitória, 2 derrotas. Três trades é ruído, não sinal.

O Epílogo: Nós Overfittamos

A última linha do post perguntava: “o edge do filtro persiste ao vivo, ou overfittamos 302 trades?”

Nós overfittamos 302 trades.

Por volta de 25 de fevereiro de 2026, o filtro NN foi removido do VPS ao vivo. A razão era simples: 302 amostras de treinamento não são suficientes para uma rede neural aprender padrões generalizáveis, mesmo uma de 593 parâmetros. Os padrões condicionais — dominância de segunda-feira, agrupamento em abertura de sessão — são features reais do price action EURUSD 30 minutos. Mas uma rede treinada em 302 exemplos desses padrões aprende os 302 trades específicos, não a estrutura subjacente.

A estratégia IPDA CE crua, sem nenhum filtro de rede neural, continuou operando. No início de março de 2026, a conta estava em €9.540,23, recuperando de um drawdown máximo de -6.14% para -4.6%. O edge vive na física de gap-fill e nas camadas de deflação que aplicamos antes de qualquer ML tocar nela. A NN era ornamentação.

GBPUSD — o “segundo par” que planejávamos deployar — nunca foi ao vivo. A estratégia é específica por ativo. Transfer learning entre pares forex produziu acurácia abaixo de moeda, e o modelo GBPUSD independente sofreu o mesmo problema de amostra pequena.

O que sobreviveu:

  • A estratégia baseline. Cinco rodadas de deflação, validada em tick, ajustada por spread. Ainda operando, ainda lucrativa.
  • A engenharia de deploy. Exportação PyTorch-para-numpy, arquitetura de defesa em duas camadas, gestão de recursos do VPS. Esses padrões funcionam independente de qual modelo você está rodando.
  • A descoberta das barras fantasma. Divergência de provedor de dados é um risco real e pouco discutido em trading sistemático.
  • A pergunta certa. “Nós overfittamos?” era a coisa certa a se perguntar. O erro foi não esperar dados out-of-sample suficientes antes de publicar os resultados in-sample como se fossem conclusões.

O que morreu:

  • O filtro NN. 302 amostras, 593 parâmetros, ensemble de 10 seeds — nada disso foi suficiente. O backtest parecia limpo porque o modelo aprendeu o conjunto de treinamento, não o mercado.
  • Ambições cross-pair. GBPUSD nunca foi deployado. O edge, se existe lá, precisa de seu próprio pipeline de validação — não um port do EURUSD.

A lição é velha mas aparentemente precisa ser reaprendida: se seu conjunto de treinamento cabe numa planilha, você não precisa de uma rede neural. Regras fixas — operar segundas, pular meio de sessão — teriam capturado os mesmos padrões condicionais sem o risco de overfitting. Nós recorremos a ML porque parecia sofisticado. O mercado não liga para sofisticação.

Próximos Passos

  • Estratégia crua continua — IPDA CE no EURUSD 30m, sem filtro NN, sem enfeites
  • Atualizações de equity semanais — performance real, sem cherry-picking. A conta está no vermelho mas recuperando.
  • Regras fixas acima de modelos suaves — se padrões condicionais persistirem, filtros simples de dia/hora com dados forward suficientes, não redes neurais treinadas em 302 trades

Este post faz parte de uma série contínua documentando trading sistemático ao vivo com resultados honestos. Anterior: Começando Pelo Fim: Desafios de Prop Firm como Otimização de Variância.