La cadena que debes poder reconstruir es datos → información disponible → señal → orden → ejecución → registro contable. Si falta un eslabón, una curva atractiva puede esconder un uso de información futura o una operación que nunca habría podido financiarse. Python facilita inspeccionar cada paso; no garantiza que el modelo sea correcto ni que resulte más rápido. MT5 también permite pruebas con varios instrumentos: la elección depende del trabajo que necesitas controlar.

Define los datos antes de calcular indicadores

Guarda instrumento y producto, proveedor, intervalo, periodo, zona horaria y base de precios: Bid, Ask, punto medio u otra. Conserva el archivo original y las transformaciones por separado. Comprueba orden temporal, duplicados y huecos; un cierre de mercado no equivale a un fallo de datos. Rellenar un hueco con precios inventados altera el recorrido y puede crear ejecuciones.

La función copy_rates_range de la integración de MetaTrader 5 devuelve barras con tiempo, OHLC, volumen de ticks, spread y volumen real. Trabaja con fechas UTC según su documentación. La historia disponible depende también de la del terminal y su límite de barras: solicitar un periodo no demuestra haberlo recibido completo. Esos campos tampoco reconstruyen automáticamente todas las comisiones y cargos históricos de financiación. Consulta el inventario de control de datos para backtesting.

En pandas, una operación entre Series alinea las etiquetas. Supón precios A=100, B=102, C=104 y señales almacenadas en orden C=1, A=0, B=0. El producto conserva A=0, B=0 y C=104. Convertir ambas Series por separado en arrays y multiplicarlas por posición seleccionaría 100 en A. Tener tres filas en cada objeto no basta: valida unicidad, conjunto de identificadores y correspondencia temporal. Las etiquetas ausentes requieren una política explícita, no un relleno silencioso.

Escoge un motor por su contrato de simulación

pandas y NumPy sirven para transformar datos y construir cálculos. Backtrader organiza una simulación por eventos. vectorbt ofrece modos basados en órdenes, señales y funciones de órdenes; su simulación también actualiza efectivo y posiciones, y puede ignorar o rechazar órdenes. «Vectorizado» no significa que todas las operaciones sean independientes ni que desaparezca el estado de la cartera. En vectorbt, el comportamiento de acumulación y reentrada depende del modo y de sus parámetros.

Compara versiones, licencias, derechos de uso de datos y supuestos antes de elegir. No deduzcas un plazo de ejecución universal ni que todas las prestaciones o fuentes sean gratuitas. Para una cartera compartida, define prioridad entre órdenes simultáneas, margen disponible y rechazos. Sumar curvas independientes puede financiar dos posiciones con el mismo dinero. Si modelas deslizamiento aleatorio, guarda distribución, semilla y versión, además del resultado.

Separa la etiqueta temporal de la información disponible

Una barra H1 etiquetada 10:00 por su apertura no revela su máximo, mínimo y cierre hasta finalizar. En resample, closed determina qué extremo pertenece al grupo y label qué extremo lo identifica; ninguno convierte un cierre futuro en información conocida al inicio. Usa marcas UTC internamente y convierte a Europe/Madrid al presentar el registro. No fijes un desplazamiento horario constante para todo el año.

Para datos macroeconómicos necesitas la hora de publicación y, si procede, la versión disponible entonces, no solo el mes al que se refiere el dato. En merge_asof, ordena las claves y elige conscientemente dirección, tolerancia y coincidencias exactas. Ejemplo hipotético: decisión a las 10:04 UTC; publicación a las 10:00 con valor 50 y otra a las 10:05 con valor 60. Una unión hacia atrás encuentra 50, con cuatro minutos de antigüedad. La opción de vecino más próximo encuentra 60, a un minuto, pero todavía futuro. Con tolerancia de dos minutos, la unión hacia atrás queda sin dato: no debe sustituirlo por 60.

Una coincidencia de hora exacta tampoco prueba que el dato estuviera disponible antes de decidir. Documenta el orden de eventos y la precisión del reloj. Esta distinción conecta con backtesting y con la separación temporal de entrenamiento y evaluación.

Una señal desplazada no es una ejecución

Prueba una ruptura con datos sintéticos. En las primeras veinte barras, los máximos son 100, 101, …, 119; para cada una, apertura=máximo −0,5, mínimo=máximo −2 y cierre=máximo −1. La barra 21 tiene apertura 120, máximo 121, mínimo 118 y cierre 120,5. La 22 tiene apertura 120,6, máximo 122, mínimo 119 y cierre 121. El máximo previo de veinte barras en la 21 es 119: la condición 120,5> 119 es verdadera. Incluir el máximo de la propia barra produciría 120,5> 121, falsa.

previous_high = bars["high"].shift(1).rolling(20).max()
signal = bars["close"] > previous_high
next_bar_signal = signal.shift(1, fill_value=False)

El desplazamiento sitúa la señal en la barra 22, pero no fija tamaño, precio de ejecución, aceptación ni salida. En el funcionamiento ordinario documentado de Backtrader, una orden Market emitida tras procesar la barra se ejecuta en la apertura de la siguiente disponible; otros modos y configuraciones pueden cambiar el supuesto. Si stop y objetivo caben en la misma barra, OHLC no determina por sí solo cuál se alcanzó antes.

También debes definir el rearme. Considera cinco barras con condición verdadera, verdadera, verdadera, falsa, verdadera. Se entra en la primera y un stop cierra en la segunda antes de evaluar una nueva entrada; después no hay otras salidas. La regla «si estoy sin posición y la condición es verdadera» reentra en la segunda. La regla «tras salir, necesito observar una condición falsa» se rearma en la cuarta y entra en la quinta. Ambas producen dos entradas, en momentos distintos. No atribuyas esa diferencia a una biblioteca sin revisar su configuración.

Reconcilia precios, costes y moneda de cuenta

Ejemplo hipotético, sin financiación ni deslizamiento adicional: compras 10.000 EUR al Ask 1,1002 USD/EUR cuando el Bid es 1,1000. Más tarde vendes todo al Bid 1,1012, con Ask 1,1014. La diferencia ejecutada es 0,0010 USD por EUR: diez pips de 0,0001, equivalentes a 10 USD. Dos comisiones de 1 USD dejan 8 USD netos. El spread ya está incorporado al comprar al Ask y vender al Bid; restar otros dos pips dejaría 6 USD y duplicaría ese coste.

Si la cuenta está en euros y declaras para este ejercicio un cambio de conversión fijo de 1,10 USD/EUR, esos 8 USD son 7,2727… EUR, redondeados a 7,27 EUR. Es un supuesto independiente de los precios de entrada y salida, no una cotización actual ni una comisión. En un registro real conserva hora, tipo aplicado y cargos de conversión. Separa spread, comisión y deslizamiento.

Del precio ejecutado al resultado en eurosOperación hipotética de compra y venta de 10.000 EUR con precios expresados en USD por EUR. La conversión a moneda de cuenta usa un supuesto fijo independiente.EJEMPLO DE CÁLCULOCompra al Ask1,1002 USD/EURVenta al Bid1,1012 USD/EURResultado antes de comisión10 USDResultado neto8 USDConversión hipotética7,27 EUR
Del precio ejecutado al resultado en euros. Operación hipotética de compra y venta de 10.000 EUR con precios expresados en USD por EUR. La conversión a moneda de cuenta usa un supuesto fijo independiente.

El spread está dentro de los precios ejecutados; las comisiones se restan una vez y la conversión exige su propio tipo.

Compra al Ask
1,1002 USD/EUR – 10.000 EUR; el Bid simultáneo es 1,1000.
Venta al Bid
1,1012 USD/EUR – El Ask simultáneo es 1,1014.
Resultado antes de comisión
10 USD – 10.000 × (1,1012 − 1,1002); diez pips.
Resultado neto
8 USD – Dos comisiones de 1 USD; sin financiación ni deslizamiento adicional.
Conversión hipotética
7,27 EUR – 8 / 1,10 USD por EUR; redondeo al final.

Interpreta métricas desde el registro

Con riesgo monetario inicial igual por operación, cuarenta resultados de +2 R y sesenta de −1 R suman +20 R: ganancias brutas 80 R, pérdidas absolutas 60 R, profit factor 1,3333… y media +0,2 R. Un coste de 0,1 R en cada operación deja ganadoras de +1,9 R y perdedoras de −1,1 R: 76 R frente a 66 R, neto +10 R, factor 1,1515… y media +0,1 R. Es una cuenta didáctica, no un backtest ejecutado.

Reconstruye patrimonio con posiciones abiertas y cerradas, capital inicial y flujos externos. Calcula drawdown sobre la serie adecuada; no publiques Sharpe o rentabilidad si sus datos de base no se pueden reproducir. Registra todas las variantes de instrumentos, filtros, parámetros y particiones que probaste. La investigación de Bailey y colaboradores explica el problema de seleccionar el mejor resultado entre muchas pruebas; este ejemplo no estima una probabilidad de sobreajuste.

Como diagnóstico separado, cien operaciones con 75 ganancias de 0,2 R y 25 pérdidas de 1 R dan 15 R −25 R=−10 R y factor 0,6, pese al 75 % de aciertos. Esa combinación es compatible con pérdidas sin demostrar por sí sola un sesgo de programación. Examina primero la esperanza matemática, los costes y las operaciones concretas.

Construye una prueba que puedas revisar

  1. Crea un proyecto aislado sin credenciales de operativa real. Guarda versiones, procedencia y permisos de datos, identificadores únicos y tiempos UTC. Antes de evaluar una estrategia, comprueba que una nueva ejecución conserva las mismas filas y genera un inventario explícito de duplicados, huecos y transformaciones.
  2. Reproduce los casos pequeños de alineación, publicación y ruptura. Inspecciona a mano información disponible, señal, orden y ejecución en cada fila; añade la política de rearme y el orden entre salida y nueva entrada. El resultado debe ser una lista esperada de eventos que puedas comparar con el motor.
  3. Reconcilia cada ejecución con efectivo, posición, costes y moneda de cuenta; después reserva un periodo sin usar para elegir variantes. Archiva también pruebas descartadas, configuración y registro final. Si cambia una cifra al actualizar datos o biblioteca, localiza primero qué evento o supuesto explica la diferencia.