← Volver a los artículos
July 2, 2026
5 min de lectura

El impuesto del framework: cuando tu librería de backtest es más lenta que un bucle ingenuo de pandas

El impuesto del framework: cuando tu librería de backtest es más lenta que un bucle ingenuo de pandas
#algotrading
#backtest
#rendimiento
#búsqueda de parámetros
#vectorización
#benchmark
Part 2 of 10 · Collection
High-Performance Backtest Engines

Parte de la serie "Backtests sin ilusiones".

Hay una suposición cómoda en el fondo de la mayoría de los proyectos de algotrading: que una librería de backtest madura y con muchas estrellas es rápida. Tiene años de contribuciones, un bucle de eventos real, un modelo de bróker, un esquema de comisiones. Seguramente le gana al chapucero bucle de pandas que tú mismo habrías escrito. Así que echas mano de ella, conectas tu estrategia y lanzas un barrido de parámetros: unos pocos miles de configuraciones, un trabajo para toda la noche. Vuelves por la mañana y sigue ejecutándose.

Comparamos ocho motores de backtest en un mismo barrido de parámetros idéntico y encontramos algo que debería cambiar cómo eliges una herramienta para búsqueda. Dos de los frameworks orientados a eventos de código abierto más populares — backtrader y bt — ejecutaron el barrido más lento que un bucle ingenuo de pandas que escribimos como línea base descartable. No un poco más lento. backtrader tardó aproximadamente 2.5× lo que la línea base de pandas; bt, alrededor de 4.7×. Mientras tanto, un motor vectorizado/compilado con el mismo trabajo corrió unas 13,000× más rápido que bt.

Este es el impuesto del framework. Los backtesters populares están construidos para una ejecución honesta: una estrategia, un conjunto de datos, ejecuciones cuidadosas, un bróker que se comporta como un bróker. Eso es exactamente lo que quieres para una validación final o una comprobación de paridad con producción. Es exactamente la herramienta equivocada para aquello en lo que la investigación algo dedica realmente su tiempo: ejecutar la misma estrategia diez mil veces con parámetros ligeramente distintos. Este artículo mide el impuesto, explica el mecanismo y ofrece una regla de decisión para saber cuándo un framework de backtest "de verdad" es la elección equivocada.

Cada número aquí procede de un único banco de pruebas reproducible (benchmarks/bench_oss_engines.py, commit 250dbb5) sobre una carga de trabajo idéntica y con la paridad fijada. Cuando no ejecutamos nosotros mismos un motor, lo decimos y lo ponemos en una sección de honestidad aparte en vez de inventar una cifra.

Una ejecución frente a diez mil

La misma computación de estrategia fluyendo por tres caminos divergentes: una vía orientada a eventos en rojo-naranja obligada a detenerse en cada estación-barra, un árbol de nodos-algoritmo de pesos/rebalanceo en ámbar, y un único haz vectorizado esmeralda-cian que resuelve toda la serie de una vez — el paradigma es el destino para la velocidad del barrido

El hecho que define la búsqueda de parámetros es que el motor se ejecuta miles de veces pero el análisis ocurre una sola vez. Cualquier coste fijo que pagues para montar un backtest — construir el bucle de eventos, instanciar un bróker, asignar un objeto por barra — lo pagas en cada combinación. Un coste que es invisible en una ejecución ("¿a quién le importan 6 segundos?") se convierte en la factura entera a lo largo de un barrido ("6 segundos × 10,000 = 16.6 horas").

Los motores de backtest caen en tres paradigmas, y el paradigma es el destino para el rendimiento del barrido:

  1. Orientado a eventos — el motor recorre barra a barra, emitiendo eventos, llamando a tu callback next()/onBars(), enrutando órdenes a través de un objeto bróker. Esta es la arquitectura de backtrader, backtesting.py, PyAlgoTrade, zipline y nautilus_trader. Refleja cómo funciona realmente el trading en vivo, que es precisamente por lo que se confía en él por su realismo — y precisamente por lo que es lento: la sobrecarga por barra de Python se paga 150,000 veces por combinación.
  2. Pesos/rebalanceobt está aquí. Le entregas una matriz de pesos objetivo y rebalancea en las fechas que especifiques. Sin callback por barra, pero aún con un grafo de objetos por evento (un árbol de algoritmos, un libro de transacciones) evaluado en Python.
  3. Vectorizado / compilado — toda la estrategia se expresa como operaciones sobre arrays (vectorbt), un kernel compilado con JIT (numba), o código nativo (Rust, un kernel de GPU MLX). No hay Python por barra en absoluto. El bucle, si lo hay, corre a velocidad de máquina.

El resto de este artículo es la consecuencia empírica de esa taxonomía. Construimos una carga de trabajo, hicimos que cada motor hiciera de forma demostrable el mismo trabajo y lo cronometramos.

La carga de trabajo: una estrategia, ochenta parámetros, idéntica para todos

Un benchmark solo es honesto si cada motor hace el mismo trabajo. El nuestro es deliberadamente sencillo: una carga de trabajo en la que lo único que difiere entre motores es el motor mismo.

  • Datos. Una única serie sintética de cierres de movimiento browniano geométrico: 150,000 barras, seed=42, volatilidad por barra sigma=0.0008, x0=30000. Determinista, para que cualquiera pueda reproducirla bit a bit. Solo cierre por construcción — el OHLC se fija al cierre en cada tramo, porque la estrategia es un cruce de cierres.
  • Estrategia. Un cruce de Hull Moving Average: un HMA de longitud L contra una variante más rápida de HMA de un tercio (HMA vs HMA3). Siempre en mercado, con vuelco de largo/corto en cada cruce. Este es un indicador real y no trivial — dos medias móviles ponderadas anidadas más un suavizado de ventana de raíz cuadrada — no un SMA de juguete, así que el trabajo por barra es representativo.
  • El barrido. 80 longitudes de HMA que abarcan 6..200. Esas son las "diez mil ejecuciones" hechas lo bastante pequeñas para medirlas directamente: 80 combinaciones independientes, cada una un backtest completo sobre 150k barras.
  • Costes. Una comisión de ida y vuelta del 0.09%, dividida por lado para los motores que modelan comisiones por lado. Ejecución en la misma barra a close[i] — la señal en la barra i se ejecuta al cierre de esa barra, la convención que usa nuestro motor de producción.

El cronómetro por combinación envuelve exactamente dos cosas: el precálculo del HMA con numpy y la ejecución del motor. La preparación que es genuinamente de una sola vez (cargar datos, construir los objetos de barra) queda fuera del cronómetro. Hay una ejecución de calentamiento, luego repeticiones best-of-N y — como los motores orientados a eventos son lo bastante lentos como para que 80 combinaciones completas tarden desde muchos minutos hasta más de una hora — cronometramos una muestra uniforme de la rejilla y extrapolamos linealmente. Las combinaciones son independientes, así que la extrapolación lineal es exacta en esperanza; la misma convención se aplica a la línea base de pandas, de modo que ningún motor sale favorecido por ello.

Paridad: probar que todos hacen el mismo trabajo

Aquí está la trampa en la que cae una comparación ingenua de motores: un motor "rápido" podría estar simplemente haciendo menos. Si backtrader registra 2,700 operaciones y tu motor vectorizado registra 40, el motor vectorizado no es más rápido — está equivocado, y la comparación no tiene sentido.

Así que fijamos la comparación con una comprobación de paridad de número de operaciones. En L=104, la referencia de numpy produce exactamente 2,707 operaciones cerradas. Todo motor debe reproducir eso con una tolerancia de ±1, o la ejecución aborta con una aserción work-parity FAILED. La tolerancia existe solo porque los motores discrepan en las convenciones de contabilidad — si la posición abierta final se cierra a la fuerza y se cuenta, si la entrada inicial es una "operación" — no sobre las operaciones en sí:

Motor Operaciones reportadas en L=104 Convención
referencia numpy 2707 idas y vueltas cerradas
backtesting.py 2708 +1: posición final cerrada a la fuerza al terminar
backtrader 2707 posición abierta final no contada
bt 2708 +1: entrada inicial contada como transacción
PyAlgoTrade 2708 +1: entrada inicial contada como ejecución

Todos los motores aterrizan en 2707 ± 1. Sean cuales sean las diferencias de velocidad, no son un artefacto de un motor que se salta trabajo en silencio. Esta es la disciplina que nos permite poner un framework orientado a eventos y un kernel de GPU en la misma tabla y decirlo en serio.

Los resultados

Aquí está la tabla completa, ordenada de más rápido a más lento. combos/s es el rendimiento; la última columna es cuánto tarda el barrido completo de 80 combinaciones. La fila de línea base es M0 — el motor ingenuo de pandas, un bucle for sobre barras con contabilidad escalar, lo que escribirías en una tarde y tirarías. Todo lo más lento que esa línea base está en negrita.

Motor combos/s Paradigma Barrido completo de 80 combos
kernel de GPU MLX 779 vectorizado (GPU de Apple) 0.10 s
Rust nativo ~350 compilado 0.23 s
mp + numba 246 JIT compilado + multiproceso 0.33 s
vectorbt 56.9 vectorizado (numpy/numba) 1.4 s
numba (un solo núcleo) 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 ingenuo + bucle 0.28 línea base escalar 4.8 min
backtrader 0.11 orientado a eventos 12.7 min
bt 0.06 pesos / rebalanceo 22.5 min

Lee la tabla de arriba abajo y el paradigma se ordena solo: los cinco primeros son todos vectorizados o compilados, los cinco últimos son todos orientados a eventos o basados en grafo de objetos — con el bucle ingenuo de pandas situado por encima de dos frameworks maduros y populares. La distancia de arriba abajo es de cuatro órdenes de magnitud. Sobre exactamente la misma carga de 2,707 operaciones, el kernel de MLX termina el barrido en una décima de segundo; bt necesita veintidós minutos y medio. Eso es un factor de aproximadamente 13,000×.

El escándalo en el medio de la tabla

La paradoja en el medio de la tabla: un llano corredor de bucle for ingenuo en gris-azulado cruzando la meta por delante de dos ornamentadas máquinas-framework orientadas a eventos, cargadas de funciones y resplandeciendo en rojo-naranja, que aún se afanan detrás, lastradas por modelos de bróker y pilas de analizadores — la humilde línea base venciendo a los frameworks maduros en la idéntica carga de 2,707 operaciones

Los números llamativos están en los extremos, pero el resultado instructivo está en el medio: backtrader (0.11 combos/s) y bt (0.06 combos/s) son ambos más lentos que la línea base ingenua de pandas (0.28 combos/s).

Esto merece asimilarse. M0 no es un motor ingenioso. Es un bucle for de Python que indexa en un DataFrame, rastrea una posición y efectivo en escalares simples y añade operaciones a una lista — el "control" deliberadamente no optimizado que incluimos para tener algo obviamente malo al que ganar. El acceso por fila de pandas es notoriamente lento, y nos apoyamos en ello. Y aun así dos de las librerías de backtest más recomendadas del ecosistema le pierden: backtrader por 2.5×, bt por 4.7×.

El matiz que mantiene esto honesto: no todo motor orientado a eventos es más lento que pandas. backtesting.py (1.42 combos/s) supera a la línea base por 5×, porque es un bucle de eventos ligero, respaldado por numpy, que mantiene la creación de objetos por barra al mínimo. PyAlgoTrade (0.51) también aventaja a la línea base. Así que "orientado a eventos" no es automáticamente una sentencia de muerte — pero cuanto más pesada es la maquinaria por barra, peor se pone, y backtrader y bt cargan la maquinaria más pesada aquí. El paradigma fija el techo; la implementación decide en qué punto por debajo de ese techo aterrizas.

El punto no es que estas sean malas librerías. El modelo de bróker de backtrader y el diseño de árbol de algoritmos de bt existen para comprarte corrección y expresividad — manejo realista de órdenes, rebalanceo de cartera, analizadores. Esas funciones tienen un coste en tiempo de ejecución, y ese coste es invisible cuando ejecutas una vez. A lo largo de un barrido, es la historia entera.

Por qué los motores orientados a eventos pagan el impuesto

Dónde se cobra el impuesto del framework, por barra: una única iteración de barra explotada en sus costosos componentes — un objeto de barra materializado, un marco de callback, un bróker enrutando una orden, un libro contable al que se añade una entrada — cada uno una caseta de peaje resplandeciente en rojo-naranja que rebana una moneda, repetida a lo largo de un vasto corredor que se aleja de doce millones de iteraciones de barra idénticas

El mecanismo no es misterioso. Un backtest orientado a eventos, por barra, hace algo así:

  1. Avanza el reloj, corta la siguiente barra del feed de datos y la materializa como un objeto (un Bar, una Line, un diccionario).
  2. Dispara un callback hacia el código de usuario (next(), onBars()), que es una llamada a función de Python con su propio marco.
  3. Dentro del callback, consulta el estado del bróker/posición, de nuevo a través de llamadas a métodos y búsquedas de atributos.
  4. Si se crea una orden, la enruta a través del bróker: la valida, comprueba el margen/efectivo, programa una ejecución, muta un objeto de cartera, añade una entrada a un libro de transacciones.
  5. Actualiza analizadores, observadores y cualquier contabilidad que mantenga el framework.

Ahora multiplica por 150,000 barras, luego por 80 combinaciones: doce millones de iteraciones de barra por barrido, cada una un puñado de asignaciones de objetos y despachos dinámicos a nivel de Python. La sobrecarga por operación de Python — de decenas a cientos de nanosegundos por una búsqueda de atributo o una asignación pequeña — es trivial una vez y ruinosa doce millones de veces. El caso de bt es una variante de la misma enfermedad: aunque rebalancea solo en las fechas de operación en lugar de en cada barra, cada rebalanceo evalúa un árbol de objetos-algoritmo y toca un libro de cartera respaldado por pandas, y hay 2,707 de esos por combinación, por 80.

El bucle ingenuo de pandas le gana a backtrader y bt por una razón contundente: hace menos por barra. Se salta el bróker, los objetos de evento, la pila de analizadores, la máquina de estados de enrutamiento de órdenes. Paga el feo impuesto por fila de pandas, pero ese único impuesto feo sigue siendo más barato que el prolijo, completo en funciones, impuesto de un objeto por evento del framework. Cuando reduces un backtest a "posición × siguiente retorno − comisiones", la mayor parte de lo que un framework hace por barra es sobrecarga que no estás usando durante una búsqueda.

Y esta es la trampa: la sobrecarga es la razón por la que elegiste el framework. Querías el bróker realista. Querías los analizadores. Durante la validación final, lo quieres todo. Durante una búsqueda de 10,000 combinaciones en la que solo lees un escalar objetivo por el otro extremo, estás pagando una limusina para dar vueltas a la pista.

El otro extremo de la tabla: vectorizado y compilado

La parte alta de la tabla es lo que ocurre cuando borras por completo el Python por barra.

  • vectorbt (56.9 combos/s) expresa toda la estrategia como operaciones sobre arrays de numpy/numba. No hay bucle de barras en Python — la señal, la posición, el PnL son todos a nivel de array. Ejecuta el barrido en 1.4 segundos frente a los 22.5 minutos de bt: unas 950× más rápido en trabajo idéntico. (Cubrimos el diseño de vectorbt con más profundidad en el resumen de vectorbt y en la comparación más amplia pandas-vs-polars.)
  • numba (39.7) es el bucle de pandas, sin cambiar de forma, compilado con JIT a código nativo. El mismo algoritmo que M0, pero @njit convierte 0.28 combos/s en 39.7 — una aceleración de ~140× a partir de un decorador, porque la sobrecarga del intérprete que dominaba el bucle escalar simplemente se evapora.
  • mp + numba (246) ejecuta el kernel compilado a lo largo de los núcleos de CPU. Las combinaciones son vergonzosamente paralelas — cada una es independiente — así que el multiprocesamiento escala casi linealmente sobre el JIT.
  • Rust nativo (~350) elimina lo último del pegamento de Python: todo el barrido es nativo.
  • MLX GPU (779) mapea el barrido sobre un kernel de GPU de silicio de Apple. 80 combinaciones se convierten en 80 carriles paralelos de aritmética; el barrido termina antes de que sueltes la tecla enter.

Dos cosas merecen nombrarse con precisión. Primero, numba prueba que el paradigma importa más que el lenguaje. M0 y numba ejecutan el mismo algoritmo — la diferencia es puramente que uno es Python interpretado por barra y el otro es compilado. Eso es todo el impuesto del framework en un A/B controlado: ~140× por borrar el intérprete del bucle interno. Segundo, el salto de numba (39.7) a mp+numba (246) a MLX (779) ya no trata del motor en absoluto — trata de la orquestación y el hardware. Una vez que el impuesto por barra desaparece, la velocidad se vuelve una cuestión de cuántas combinaciones ejecutas en paralelo y sobre qué silicio. Recorremos toda esa progresión en la escalera de velocidad de motores de backtest, y la razón por la que la última milla está dominada por el coste de proceso/serialización en el artículo sobre el impuesto IPC.

Lo que no medimos (y por qué te lo contamos)

La credibilidad de un benchmark vive en lo que se niega a fingir. Ejecutamos ocho motores de extremo a extremo bajo paridad. A varios frameworks conocidos no les pusimos un número, y preferimos listarlos honestamente antes que extrapolar una cifra que no medimos:

  • zipline / zipline-reloaded — orientado a eventos, linaje Quantopian. Preparación pesada (un calendario de trading completo y un bundle de datos), lo que hace delicada una cronometración por combinación de manzanas con manzanas. Arquitectónicamente se sitúa con backtrader en el campo orientado a eventos; esperaríamos verlo cerca de ese extremo de la tabla, pero no lo hemos probado.
  • nautilus_trader — orientado a eventos con un núcleo en Rust/Cython, diseñado explícitamente para paridad en vivo. Su núcleo está compilado, así que es el motor orientado a eventos con más probabilidad de no pagar el impuesto completo de Python — una medición genuinamente interesante que aún no hemos realizado.
  • QuantConnect Lean — basado en C#, un runtime totalmente distinto; no directamente comparable en un banco de pruebas de Python.
  • Jesse — orientado a eventos, centrado en cripto; revisamos su diseño en una nota aparte pero no lo comparamos aquí.
  • QSTrader — orientado a eventos, orientado a cartera; los mismos matices de paradigma.
  • fastquant — lo intentamos; la instalación/API estaba rota en nuestro entorno, así que no hay número. No vamos a inventar uno.

Dos salvedades honestas sobre los números que reportamos. Las cifras de vectorbt, numba, mp+numba, Rust nativo y MLX provienen de nuestra propia escalera de motores sobre la carga idéntica, no del banco de pruebas OSS que produjo las cuatro filas orientadas a eventos — es la misma carga de trabajo pero un aparato de medición distinto, y la cifra de Rust nativo es un aproximado ~350, no una medición ajustada. Y los combos/s absolutos son específicos del hardware; lo que viaja es el orden y las proporciones, que son lo bastante grandes (13,000× de arriba abajo, 2.5–4.7× para la inversión de pandas frente a framework) como para que ninguna diferencia razonable de hardware las voltee.

En defensa de los motores orientados a eventos

El motor orientado a eventos en su papel adecuado: una única ejecución de validación meticulosa y de alta fidelidad de una estrategia superviviente, representada como un instrumento preciso esmeralda-y-cian que modela una ejecución realista, respetando el margen y el ciclo de vida de la orden — la misma maquinaria por barra que era puro desperdicio durante la búsqueda mostrada aquí como valiosa artesanía, un cuidadoso ensayo general antes del despliegue

Sería fácil leer esto como "los backtesters orientados a eventos son malos". Esa es la lección equivocada, y una injusta.

Los motores orientados a eventos están construidos para un trabajo distinto, y son buenos en él. El bróker por barra, el ciclo de vida de la orden, la lógica de ejecución, los analizadores — esa maquinaria existe para que un backtest se parezca al trading en vivo lo más posible. Cuando tu objetivo es un único ensayo general fiable de una estrategia que estás a punto de desplegar, quieres que el motor sude cada ejecución, modele ejecuciones parciales, respete el margen y se niegue a dejarte operar a precios que no habrías podido conseguir. Esa fidelidad es el producto. Su coste en tiempo de ejecución es el precio del realismo, y para una ejecución el precio es insignificante.

El fallo no es el motor; es usarlo en la fase equivocada. La investigación tiene dos fases distintas con requisitos opuestos:

  • La búsqueda quiere rendimiento. Estás explorando un paisaje, la mayoría del cual es basura, y necesitas evaluar miles de puntos para encontrar los pocos que merecen una segunda mirada. La fidelidad por punto apenas importa — estás clasificando, no desplegando. Aquí el impuesto del framework es puro desperdicio.
  • La validación quiere fidelidad. Tienes un puñado de candidatos y necesitas saber, con la mayor precisión posible, si sobreviven a la ejecución realista, comisiones, deslizamiento y las trampas de anticipación que inflan los retornos sobre el papel. Aquí el motor orientado a eventos se gana su coste.

El error que castiga el impuesto del framework es ejecutar tu búsqueda en tu motor de validación — pagando tarifas de limusina para explorar un paisaje del que vas a tirar el 99%.

La regla de decisión

La conclusión práctica se comprime en una sola decisión:

Busca en un motor vectorizado/compilado. Valida a los supervivientes en uno orientado a eventos.

En concreto:

  1. Construye o toma prestado un kernel rápido para el barrido. vectorbt si lo quieres listo para usar; un bucle compilado con numba si tu estrategia no se vectoriza limpiamente (el decorador @njit por sí solo compró ~140× aquí). Pasa todo el espacio de parámetros por él.
  2. Nunca ejecutes un barrido grande en backtrader, bt, zipline o cualquier framework pesado orientado a eventos. Si un barrido en uno de esos es tu cuello de botella, la solución no es una máquina más grande — es el motor equivocado. Incluso el bucle ingenuo de pandas le ganaría a dos de ellos.
  3. Promueve una lista corta al motor orientado a eventos para una comprobación de fidelidad. Toma el puñado de supervivientes y vuélvelos a ejecutar en el motor realista, donde el modelo de bróker y la lógica de ejecución pueden exponer problemas que el kernel rápido abstrajo.
  4. Impón paridad entre los dos. El motor rápido y el motor de fidelidad deben coincidir en número de operaciones y PnL sobre una configuración fija (nuestra comprobación de ±1 operación en L=104), o la búsqueda y la validación están midiendo estrategias distintas y todo el pipeline es una mentira.

Esta es la misma arquitectura de dos velocidades que aparece siempre que la búsqueda y la validación tienen perfiles de coste opuestos, y es por lo que nuestro propio stack mantiene una ruta rápida vectorizada/compilada para la búsqueda de parámetros y reserva la maquinaria pesada para la evaluación de la función objetivo final y las comprobaciones de mesetas.

Conclusiones

  1. Popular ≠ rápido para barridos. Sobre una carga de trabajo idéntica y con la paridad fijada (150k barras, 80 combinaciones de cruce de HMA, 2,707 operaciones), backtrader (0.11 combos/s) y bt (0.06) ambos corrieron más lento que un bucle ingenuo de pandas (0.28). Un framework maduro y con muchas estrellas no es automáticamente la opción rápida.
  2. El impuesto del framework es por barra, y un barrido lo multiplica. Doce millones de iteraciones de barra por barrido, cada una cargando un objeto de evento, un callback y una ida y vuelta al bróker. Un coste que es invisible en una ejecución es la factura entera a lo largo de diez mil.
  3. El paradigma fija el techo. Los motores vectorizados/compilados (vectorbt 56.9, numba 39.7, mp+numba 246, Rust nativo ~350, MLX 779) le ganan a los motores orientados a eventos por dos a cuatro órdenes de magnitud — hasta ~13,000× de arriba abajo. El mismo algoritmo, meramente compilado con JIT, fue 140× más rápido.
  4. No todos los motores orientados a eventos son iguales. backtesting.py (1.42) y PyAlgoTrade (0.51) aún le ganan a la línea base ingenua; el impuesto escala con lo pesada que sea la maquinaria por barra. La implementación decide en qué punto por debajo del techo aterrizas.
  5. Dos motores, dos fases. Busca en un kernel vectorizado/compilado; valida a los supervivientes en el motor realista orientado a eventos. Impón paridad de número de operaciones/PnL entre ellos para que ambos estén midiendo la misma estrategia.
  6. Sé honesto sobre lo que mediste. Comparamos ocho motores bajo paridad y listamos los que no medimos (zipline, nautilus_trader, Lean, Jesse, QSTrader y la instalación de fastquant que no arrancaba) en vez de inventarles números.

El resumen incómodo: si un barrido de parámetros es tu cuello de botella, el problema probablemente no es tu máquina ni tu estrategia. Es que estás ejecutando una búsqueda en un motor construido para una única ejecución honesta — y serías más rápido con el bucle de pandas que te daba demasiada vergüenza conservar.

El banco de pruebas completo, las aserciones de paridad y los resultados JSON crudos por motor viven en benchmarks/bench_oss_engines.py y benchmarks/results_oss/ en el commit 250dbb5. Para el extremo compilado/GPU de la escalera, ve la escalera de velocidad de motores de backtest y el análisis del impuesto 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

Mantente a la vanguardia

Suscríbete a nuestro boletín para recibir información exclusiva sobre trading con IA, análisis de mercado y actualizaciones de la plataforma.

Respetamos tu privacidad. Puedes darte de baja en cualquier momento.