← Voltar aos artigos
July 2, 2026
5 min read

O imposto do framework: quando sua biblioteca de backtest é mais lenta que um loop ingênuo de pandas

O imposto do framework: quando sua biblioteca de backtest é mais lenta que um loop ingênuo de pandas
#algotrading
#backtest
#desempenho
#busca de parâmetros
#vetorização
#benchmark
Part 2 of 10 · Collection
High-Performance Backtest Engines

Parte da série "Backtests sem ilusões".

Há uma suposição confortável no fundo da maioria dos projetos de algotrading: a de que uma biblioteca de backtest madura e bem avaliada é rápida. Tem anos de contribuições, um loop de eventos de verdade, um modelo de corretora, um esquema de comissões. Certamente ganha do loop de pandas remendado que você mesmo teria escrito. Então você recorre a ela, conecta sua estratégia e inicia uma varredura de parâmetros — alguns milhares de configurações, um trabalho para a noite toda. Você volta pela manhã e ela ainda está rodando.

Comparamos oito motores de backtest em uma mesma varredura de parâmetros idêntica e encontramos algo que deveria mudar como você escolhe uma ferramenta para busca. Dois dos frameworks orientados a eventos de código aberto mais populares — backtrader e bt — rodaram a varredura mais lentos que um loop ingênuo de pandas que escrevemos como baseline descartável. Não um pouco mais lentos. O backtrader levou cerca de 2.5× o tempo do baseline de pandas; o bt, cerca de 4.7×. Enquanto isso, um motor vetorizado/compilado no mesmo trabalho rodou cerca de 13,000× mais rápido que o bt.

Este é o imposto do framework. Os backtesters populares são feitos para uma execução honesta — uma estratégia, um conjunto de dados, execuções cuidadosas, uma corretora que se comporta como uma corretora. Isso é exatamente o que você quer para uma validação final ou uma checagem de paridade com produção. É exatamente a ferramenta errada para aquilo em que a pesquisa em algo de fato gasta seu tempo: rodar a mesma estratégia dez mil vezes com botões levemente diferentes. Este artigo mede o imposto, explica o mecanismo e dá uma regra de decisão para quando um framework de backtest "de verdade" é a escolha errada.

Cada número aqui vem de um único aparato reproduzível (benchmarks/bench_oss_engines.py, commit 250dbb5) sobre uma carga de trabalho idêntica e com a paridade travada. Onde não rodamos um motor nós mesmos, dizemos isso e o colocamos numa seção de honestidade à parte, em vez de inventar um valor.

Uma execução versus dez mil

A mesma computação de estratégia fluindo por três caminhos divergentes: uma trilha orientada a eventos em vermelho-laranja obrigada a parar em cada estação-barra, uma árvore de nós-algoritmo de pesos/rebalanceamento em âmbar, e um único feixe vetorizado esmeralda-ciano que resolve toda a série de uma vez — o paradigma é destino para a velocidade da varredura

O fato que define a busca de parâmetros é que o motor roda milhares de vezes, mas a análise acontece uma única vez. Qualquer custo fixo que você pague para montar um backtest — construir o loop de eventos, instanciar uma corretora, alocar um objeto por barra — você paga em cada combinação. Um custo que é invisível em uma execução ("quem liga para 6 segundos?") vira a conta inteira ao longo de uma varredura ("6 segundos × 10,000 = 16.6 horas").

Os motores de backtest se dividem em três paradigmas, e o paradigma é destino para o desempenho da varredura:

  1. Orientado a eventos — o motor caminha barra a barra, emitindo eventos, chamando seu callback next()/onBars(), roteando ordens através de um objeto corretora. Essa é a arquitetura do backtrader, backtesting.py, PyAlgoTrade, zipline e nautilus_trader. Espelha como o trading ao vivo de fato funciona, que é precisamente por que confiam nele pelo realismo — e precisamente por que ele é lento: a sobrecarga por barra do Python é paga 150,000 vezes por combinação.
  2. Pesos/rebalanceamento — o bt fica aqui. Você lhe entrega uma matriz de pesos-alvo e ele rebalanceia nas datas que você especificar. Sem callback por barra, mas ainda com um grafo de objetos por evento (uma árvore de algoritmos, um livro de transações) avaliado em Python.
  3. Vetorizado / compilado — a estratégia inteira é expressa como operações sobre arrays (vectorbt), um kernel compilado com JIT (numba), ou código nativo (Rust, um kernel de GPU MLX). Não há Python por barra algum. O loop, se houver, roda em velocidade de máquina.

O restante deste artigo é a consequência empírica dessa taxonomia. Construímos uma carga de trabalho, fizemos cada motor executar de forma comprovável o mesmo trabalho e cronometramos.

A carga de trabalho: uma estratégia, oitenta botões, idêntica para todos

Um benchmark só é honesto se cada motor faz o mesmo trabalho. O nosso é deliberadamente simples — uma carga de trabalho em que a única coisa que difere entre os motores é o próprio motor.

  • Dados. Uma única série sintética de fechamentos de movimento browniano geométrico: 150,000 barras, seed=42, volatilidade por barra sigma=0.0008, x0=30000. Determinística, para que qualquer um possa reproduzi-la bit a bit. Somente fechamento por construção — o OHLC é fixado no fechamento em cada trecho, porque a estratégia é um cruzamento de fechamentos.
  • Estratégia. Um cruzamento de Hull Moving Average: um HMA de comprimento L contra uma variante mais rápida de HMA de um terço (HMA vs HMA3). Sempre no mercado, virando comprado/vendido a cada cruzamento. Este é um indicador real e não trivial — duas médias móveis ponderadas aninhadas mais um suavizador de janela de raiz quadrada — não um SMA de brinquedo, então o trabalho por barra é representativo.
  • A varredura. 80 comprimentos de HMA abrangendo 6..200. Essas são as "dez mil execuções" reduzidas o bastante para serem medidas diretamente: 80 combinações independentes, cada uma um backtest completo sobre 150k barras.
  • Custos. Uma taxa de ida e volta de 0.09%, dividida por lado para os motores que modelam comissões por lado. Execução na mesma barra em close[i] — o sinal na barra i é executado no fechamento daquela barra, a convenção que nosso motor de produção usa.

O cronômetro por combinação envolve exatamente duas coisas: o pré-cálculo do HMA com numpy e a execução do motor. A preparação que é genuinamente de uma única vez (carregar dados, construir os objetos de barra) fica fora do cronômetro. Há uma execução de aquecimento, depois repetições best-of-N e — como os motores orientados a eventos são lentos o bastante para que 80 combinações completas levem de muitos minutos a mais de uma hora — cronometramos uma amostra uniforme da grade e extrapolamos linearmente. As combinações são independentes, então a extrapolação linear é exata em esperança; a mesma convenção é aplicada ao baseline de pandas, de modo que nenhum motor é favorecido por isso.

Paridade: provar que todos fazem o mesmo trabalho

Aqui está a armadilha em que uma comparação ingênua de motores cai: um motor "rápido" pode estar simplesmente fazendo menos. Se o backtrader registra 2,700 operações e seu motor vetorizado registra 40, o motor vetorizado não é mais rápido — está errado, e a comparação não tem sentido.

Então travamos a comparação com uma checagem de paridade de número de operações. Em L=104, a referência de numpy produz exatamente 2,707 operações fechadas. Todo motor deve reproduzir isso dentro de uma tolerância de ±1, ou a execução aborta com uma asserção work-parity FAILED. A tolerância existe apenas porque os motores discordam nas convenções de contabilidade — se a posição aberta final é fechada à força e contada, se a entrada inicial é uma "operação" — não sobre as operações em si:

Motor Operações reportadas em L=104 Convenção
referência numpy 2707 idas e voltas fechadas
backtesting.py 2708 +1: posição final fechada à força no fim
backtrader 2707 posição aberta final não contada
bt 2708 +1: entrada inicial contada como transação
PyAlgoTrade 2708 +1: entrada inicial contada como execução

Todo motor aterrissa em 2707 ± 1. Sejam quais forem as diferenças de velocidade, elas não são um artefato de um motor pulando trabalho em silêncio. Essa é a disciplina que nos permite pôr um framework orientado a eventos e um kernel de GPU na mesma tabela e dizê-lo a sério.

Os resultados

Aqui está a tabela inteira, ordenada do mais rápido ao mais lento. combos/s é a vazão; a última coluna é quanto tempo leva a varredura completa de 80 combinações. A linha de baseline é M0 — o motor ingênuo de pandas, um loop for sobre barras com contabilidade escalar, aquilo que você escreveria numa tarde e jogaria fora. Tudo mais lento que esse baseline está em negrito.

Motor combos/s Paradigma Varredura completa de 80 combos
kernel de GPU MLX 779 vetorizado (GPU da Apple) 0.10 s
Rust nativo ~350 compilado 0.23 s
mp + numba 246 JIT compilado + multiprocesso 0.33 s
vectorbt 56.9 vetorizado (numpy/numba) 1.4 s
numba (núcleo único) 39.7 JIT compilado 2.0 s
backtesting.py 1.42 orientado a eventos 56 s
PyAlgoTrade 0.51 orientado a eventos 2.6 min
M0 — pandas ingênuo + loop 0.28 baseline escalar 4.8 min
backtrader 0.11 orientado a eventos 12.7 min
bt 0.06 pesos / rebalanceamento 22.5 min

Leia a tabela de cima para baixo e o paradigma se ordena sozinho: os cinco primeiros são todos vetorizados ou compilados, os cinco últimos são todos orientados a eventos ou baseados em grafo de objetos — com o loop ingênuo de pandas situado acima de dois frameworks maduros e populares. A distância de cima a baixo é de quatro ordens de grandeza. Sobre exatamente a mesma carga de 2,707 operações, o kernel de MLX termina a varredura em um décimo de segundo; o bt precisa de vinte e dois minutos e meio. Isso é um fator de aproximadamente 13,000×.

O escândalo no meio da tabela

O paradoxo no meio da tabela: um corredor de loop for ingênuo, simples e cinza-azulado, cruzando a linha de chegada à frente de duas ornamentadas máquinas-framework orientadas a eventos, cheias de recursos e brilhando em vermelho-laranja, que ainda labutam atrás, pesadas por modelos de corretora e pilhas de analisadores — o humilde baseline vencendo os frameworks maduros na idêntica carga de 2,707 operações

Os números que chamam a atenção estão nos extremos, mas o resultado instrutivo está no meio: backtrader (0.11 combos/s) e bt (0.06 combos/s) são ambos mais lentos que o baseline ingênuo de pandas (0.28 combos/s).

Isso merece ser digerido. M0 não é um motor esperto. É um loop for de Python que indexa em um DataFrame, rastreia uma posição e caixa em escalares simples e anexa operações a uma lista — o "controle" deliberadamente não otimizado que incluímos para ter algo obviamente ruim de bater. O acesso por linha do pandas é notoriamente lento, e nós nos apoiamos nisso. E ainda assim duas das bibliotecas de backtest mais recomendadas do ecossistema perdem para ele: o backtrader por 2.5×, o bt por 4.7×.

A nuance que mantém isto honesto: nem todo motor orientado a eventos é mais lento que pandas. O backtesting.py (1.42 combos/s) supera o baseline por 5×, porque é um loop de eventos enxuto, apoiado em numpy, que mantém a criação de objetos por barra no mínimo. O PyAlgoTrade (0.51) também passa à frente do baseline. Então "orientado a eventos" não é automaticamente uma sentença de morte — mas quanto mais pesada a maquinaria por barra, pior fica, e o backtrader e o bt carregam a maquinaria mais pesada aqui. O paradigma fixa o teto; a implementação decide onde, abaixo desse teto, você aterrissa.

O ponto não é que estas sejam bibliotecas ruins. O modelo de corretora do backtrader e o design de árvore de algoritmos do bt existem para lhe comprar correção e expressividade — manejo realista de ordens, rebalanceamento de carteira, analisadores. Esses recursos têm um custo em tempo de execução, e esse custo é invisível quando você roda uma vez. Ao longo de uma varredura, ele é a história inteira.

Por que os motores orientados a eventos pagam o imposto

Onde o imposto do framework é cobrado, por barra: uma única iteração de barra explodida em seus componentes custosos — um objeto de barra materializado, um quadro de callback, uma corretora roteando uma ordem, um livro contábil sendo anexado — cada um uma cabine de pedágio brilhando em vermelho-laranja que raspa uma moeda, repetida ao longo de um vasto corredor que se afasta de doze milhões de iterações de barra idênticas

O mecanismo não é misterioso. Um backtest orientado a eventos, por barra, faz algo assim:

  1. Avança o relógio, fatia a próxima barra do feed de dados e a materializa como um objeto (um Bar, uma Line, um dicionário).
  2. Dispara um callback para o código de usuário (next(), onBars()), que é uma chamada de função Python com seu próprio quadro.
  3. Dentro do callback, consulta o estado da corretora/posição, novamente através de chamadas de método e buscas de atributo.
  4. Se uma ordem é criada, roteia-a através da corretora: valida-a, checa margem/caixa, agenda uma execução, muta um objeto de carteira, anexa a um livro de transações.
  5. Atualiza analisadores, observadores e qualquer contabilidade que o framework mantenha.

Agora multiplique por 150,000 barras, depois por 80 combinações: doze milhões de iterações de barra por varredura, cada uma um punhado de alocações de objetos e despachos dinâmicos em nível de Python. A sobrecarga por operação do Python — de dezenas a centenas de nanossegundos para uma busca de atributo ou uma alocação pequena — é trivial uma vez e ruinosa doze milhões de vezes. O caso do bt é uma variante da mesma doença: embora rebalanceie apenas nas datas de operação em vez de a cada barra, cada rebalanceamento avalia uma árvore de objetos-algoritmo e toca um livro de carteira apoiado em pandas, e há 2,707 desses por combinação, vezes 80.

O loop ingênuo de pandas ganha do backtrader e do bt por uma razão contundente: ele faz menos por barra. Pula a corretora, os objetos de evento, a pilha de analisadores, a máquina de estados de roteamento de ordens. Ele paga o feio imposto por linha do pandas, mas esse único imposto feio ainda é mais barato que o arrumado, completo em recursos, imposto de um objeto por evento do framework. Quando você reduz um backtest a "posição × próximo retorno − taxas", a maior parte do que um framework faz por barra é sobrecarga que você não está usando durante uma busca.

E essa é a armadilha: a sobrecarga é a razão pela qual você escolheu o framework. Você queria a corretora realista. Você queria os analisadores. Durante a validação final, você quer tudo isso. Durante uma busca de 10,000 combinações em que você só lê um escalar objetivo pela outra ponta, você está pagando uma limusine para dar voltas na pista.

A outra ponta da tabela: vetorizado e compilado

O topo da tabela é o que acontece quando você apaga o Python por barra por completo.

  • vectorbt (56.9 combos/s) expressa a estratégia inteira como operações sobre arrays de numpy/numba. Não há loop de barras em Python — o sinal, a posição, o PnL são todos em nível de array. Ele roda a varredura em 1.4 segundos contra os 22.5 minutos do bt: cerca de 950× mais rápido em trabalho idêntico. (Cobrimos o design do vectorbt com mais profundidade na visão geral do vectorbt e na comparação mais ampla pandas-vs-polars.)
  • numba (39.7) é o loop de pandas, sem mudar de forma, compilado com JIT para código nativo. O mesmo algoritmo que M0, mas @njit transforma 0.28 combos/s em 39.7 — uma aceleração de ~140× a partir de um decorador, porque a sobrecarga do interpretador que dominava o loop escalar simplesmente evapora.
  • mp + numba (246) roda o kernel compilado através dos núcleos de CPU. As combinações são vergonhosamente paralelas — cada uma é independente — então o multiprocessamento escala quase linearmente sobre o JIT.
  • Rust nativo (~350) remove o último resquício da cola de Python: a varredura inteira é nativa.
  • MLX GPU (779) mapeia a varredura sobre um kernel de GPU de silício da Apple. 80 combinações viram 80 pistas paralelas de aritmética; a varredura termina antes de você soltar a tecla enter.

Duas coisas merecem ser nomeadas com precisão. Primeiro, o numba prova que o paradigma importa mais que a linguagem. M0 e numba rodam o mesmo algoritmo — a diferença é puramente que um é Python interpretado por barra e o outro é compilado. Isso é todo o imposto do framework num A/B controlado: ~140× por apagar o interpretador do loop interno. Segundo, o salto de numba (39.7) para mp+numba (246) para MLX (779) já não é sobre o motor em absoluto — é sobre orquestração e hardware. Uma vez que o imposto por barra some, a velocidade vira uma questão de quantas combinações você roda em paralelo e sobre qual silício. Percorremos toda essa progressão na escada de velocidade de motores de backtest, e a razão pela qual a última milha é dominada pelo custo de processo/serialização no artigo do imposto IPC.

O que não medimos (e por que estamos lhe contando)

A credibilidade de um benchmark vive naquilo que ele se recusa a forjar. Rodamos oito motores de ponta a ponta sob paridade. A vários frameworks conhecidos não demos um número, e preferimos listá-los honestamente a extrapolar um valor que não medimos:

  • zipline / zipline-reloaded — orientado a eventos, linhagem Quantopian. Preparação pesada (um calendário de trading completo e um bundle de dados), o que torna delicada uma cronometragem por combinação de maçãs com maçãs. Arquiteturalmente ele se posiciona com o backtrader no campo orientado a eventos; esperaríamos vê-lo perto daquela ponta da tabela, mas não provamos isso.
  • nautilus_trader — orientado a eventos com um núcleo em Rust/Cython, explicitamente projetado para paridade ao vivo. Seu núcleo é compilado, então é o motor orientado a eventos com maior probabilidade de não pagar o imposto completo do Python — uma medição genuinamente interessante que ainda não realizamos.
  • QuantConnect Lean — baseado em C#, um runtime totalmente diferente; não diretamente comparável em um aparato de Python.
  • Jesse — orientado a eventos, focado em cripto; revisamos seu design em uma nota à parte, mas não o comparamos aqui.
  • QSTrader — orientado a eventos, voltado a carteira; as mesmas ressalvas de paradigma.
  • fastquant — tentamos; a instalação/API estava quebrada em nosso ambiente, então não há número. Não vamos chutar um.

Duas ressalvas honestas sobre os números que de fato reportamos. Os valores de vectorbt, numba, mp+numba, Rust nativo e MLX vêm da nossa própria escada de motores sobre a carga idêntica, não do aparato OSS que produziu as quatro linhas orientadas a eventos — é a mesma carga de trabalho, mas um instrumento de medição diferente, e o valor de Rust nativo é um aproximado ~350, não uma medição precisa. E o combos/s absoluto é específico do hardware; o que viaja é a ordem e as proporções, que são grandes o bastante (13,000× de cima a baixo, 2.5–4.7× para a inversão de pandas versus framework) para que nenhuma diferença razoável de hardware as vire.

Em defesa dos motores orientados a eventos

O motor orientado a eventos em seu papel apropriado: uma única execução de validação meticulosa e de alta fidelidade de uma estratégia sobrevivente, representada como um instrumento preciso esmeralda-e-ciano modelando uma execução realista, respeitando a margem e o ciclo de vida da ordem — a mesma maquinaria por barra que era puro desperdício durante a busca mostrada aqui como valioso artesanato, um cuidadoso ensaio geral antes do deploy

Seria fácil ler isto como "backtesters orientados a eventos são ruins". Essa é a lição errada, e injusta.

Os motores orientados a eventos são feitos para um trabalho diferente, e são bons nele. A corretora por barra, o ciclo de vida da ordem, a lógica de execução, os analisadores — essa maquinaria existe para fazer um backtest se assemelhar ao trading ao vivo o máximo possível. Quando seu objetivo é um único ensaio geral confiável de uma estratégia que você está prestes a implantar, você quer que o motor sue cada execução, modele execuções parciais, respeite a margem e se recuse a deixar você operar a preços que você não conseguiria. Essa fidelidade é o produto. Seu custo em tempo de execução é o preço do realismo, e para uma execução o preço é desprezível.

A falha não é o motor; é usá-lo na fase errada. A pesquisa tem duas fases distintas com requisitos opostos:

  • A busca quer vazão. Você está explorando uma paisagem, a maior parte da qual é lixo, e precisa avaliar milhares de pontos para achar os poucos que valem uma segunda olhada. A fidelidade por ponto mal importa — você está ranqueando, não implantando. Aqui o imposto do framework é puro desperdício.
  • A validação quer fidelidade. Você tem um punhado de candidatos e precisa saber, com a maior precisão possível, se eles sobrevivem à execução realista, taxas, deslizamento e às armadilhas de antecipação que inflam os retornos no papel. Aqui o motor orientado a eventos justifica seu custo.

O erro que o imposto do framework pune é rodar sua busca no seu motor de validação — pagando tarifas de limusine para explorar uma paisagem da qual você vai jogar fora 99%.

A regra de decisão

A lição prática se comprime em uma única decisão:

Busque em um motor vetorizado/compilado. Valide os sobreviventes em um orientado a eventos.

Concretamente:

  1. Construa ou tome emprestado um kernel rápido para a varredura. vectorbt se você o quer pronto para uso; um loop compilado com numba se sua estratégia não vetoriza de forma limpa (o decorador @njit sozinho comprou ~140× aqui). Passe todo o espaço de parâmetros por ele.
  2. Nunca rode uma varredura grande no backtrader, bt, zipline ou qualquer framework pesado orientado a eventos. Se uma varredura em um deles é seu gargalo, a solução não é uma máquina maior — é o motor errado. Até o loop ingênuo de pandas ganharia de dois deles.
  3. Promova uma lista curta ao motor orientado a eventos para uma checagem de fidelidade. Pegue o punhado de sobreviventes e rode-os de novo no motor realista, onde o modelo de corretora e a lógica de execução podem expor problemas que o kernel rápido abstraiu.
  4. Imponha paridade entre os dois. O motor rápido e o motor de fidelidade devem concordar em número de operações e PnL sobre uma configuração fixa (nossa checagem de ±1 operação em L=104), ou a busca e a validação estão medindo estratégias diferentes e todo o pipeline é uma mentira.

Essa é a mesma arquitetura de duas velocidades que aparece sempre que a busca e a validação têm perfis de custo opostos, e é por isso que nosso próprio stack mantém uma rota rápida vetorizada/compilada para a busca de parâmetros e reserva a maquinaria pesada para a avaliação da função objetivo final e as checagens de platô.

Pontos principais

  1. Popular ≠ rápido para varreduras. Sobre uma carga de trabalho idêntica e com a paridade travada (150k barras, 80 combinações de cruzamento de HMA, 2,707 operações), o backtrader (0.11 combos/s) e o bt (0.06) ambos rodaram mais lentos que um loop ingênuo de pandas (0.28). Um framework maduro e bem avaliado não é automaticamente a escolha rápida.
  2. O imposto do framework é por barra, e uma varredura o multiplica. Doze milhões de iterações de barra por varredura, cada uma carregando um objeto de evento, um callback e uma ida e volta à corretora. Um custo que é invisível em uma execução é a conta inteira ao longo de dez mil.
  3. O paradigma fixa o teto. Os motores vetorizados/compilados (vectorbt 56.9, numba 39.7, mp+numba 246, Rust nativo ~350, MLX 779) ganham dos motores orientados a eventos por duas a quatro ordens de grandeza — até ~13,000× de cima a baixo. O mesmo algoritmo, meramente compilado com JIT, ficou 140× mais rápido.
  4. Nem todos os motores orientados a eventos são iguais. O backtesting.py (1.42) e o PyAlgoTrade (0.51) ainda ganham do baseline ingênuo; o imposto escala com quão pesada é a maquinaria por barra. A implementação decide onde, abaixo do teto, você aterrissa.
  5. Dois motores, duas fases. Busque em um kernel vetorizado/compilado; valide os sobreviventes no motor realista orientado a eventos. Imponha paridade de número de operações/PnL entre eles para que ambos estejam medindo a mesma estratégia.
  6. Seja honesto sobre o que você mediu. Comparamos oito motores sob paridade e listamos os que não medimos (zipline, nautilus_trader, Lean, Jesse, QSTrader e a instalação do fastquant que não rodava) em vez de inventar números para eles.

O resumo incômodo: se uma varredura de parâmetros é seu gargalo, o problema provavelmente não é sua máquina nem sua estratégia. É que você está rodando uma busca em um motor feito para uma única execução honesta — e você seria mais rápido com o loop de pandas que você tinha vergonha demais de manter.

O aparato completo, as asserções de paridade e os resultados JSON brutos por motor vivem em benchmarks/bench_oss_engines.py e benchmarks/results_oss/ no commit 250dbb5. Para a ponta compilada/GPU da escada, veja a escada de velocidade de motores de backtest e a análise do imposto IPC.

blog.disclaimer

Authors

Eugen Soloviov
Eugen Soloviov

Trading-systems engineer

Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.

Newsletter

Fique à frente do mercado

Assine nossa newsletter para insights exclusivos sobre trading com IA, análises de mercado e atualizações da plataforma.

Respeitamos sua privacidade. Cancele a inscrição a qualquer momento.