Saltar al contenido

Leer el formato engine_trades.csv

5 min de lectura

Referencia de engine_trades.csv, la lista de operaciones que PineForge escribe para cada estrategia de su corpus abierto: columna a columna, cómo se emparejan las filas en operaciones y un cargador para pandas.

Versión actual: pineforge-engine v1.10.0, publicada el 2026-11-03.

Actualizado el 2026-09-30 al formato que escribe el motor (pineforge-engine v1.0.0).

engine_trades.csv es la lista de operaciones del motor en un CSV con la misma forma que la exportación «List of Trades» de TradingView, para poder alinear ambas operación por operación. Lo escribe scripts/run_strategy.py, del repositorio del motor, y cada una de las 312 estrategias del pineforge-corpus público incluye uno junto a la exportación de la propia TradingView, tv_trades.csv (una estrategia de autoprueba incluye en su lugar una exportación de TradingView por modo, trades-<mode>.csv). La imagen Docker y los servidores MCP devuelven las mismas operaciones en JSON, en el array trades del informe; este artículo trata del CSV.

Qué es el archivo

Cada fila es una de las dos patas de una operación cerrada: la salida o la entrada. Cada operación tiene exactamente dos filas, primero la de salida, y ambas llevan el mismo Trade #. Las operaciones van de la más reciente a la más antigua: el archivo empieza por el número de operación más alto y termina con la operación 1.

Referencia columna a columna

ColumnaTipoNotas
Trade #enteroEl número de la operación, compartido por sus filas de salida y de entrada.
TypetextoEntry long, Exit long, Entry short o Exit short.
Date and timetextoLa hora del fill en UTC, YYYY-MM-DD HH:MM, sin sufijo de zona horaria.
PricefloatPrecio del fill de este lado.
QtyfloatUnidades operadas, siempre positivas.
Net PnLfloatLa ganancia o pérdida neta de la operación. El mismo valor en las dos filas.
Net PnL %floatNet PnL en porcentaje. El mismo valor en las dos filas.
Favorable excursion USDfloatLa mejor ganancia no realizada durante la operación, cero o positiva.
Adverse excursion USDfloatLa peor pérdida no realizada durante la operación, cero o negativa (la convención de TradingView).
Cumulative PnLfloatSuma acumulada de Net PnL desde la operación 1 hasta esta. El mismo valor en las dos filas.
Engine entry incarnationentero o vacíoEl id que asigna el motor al fill físico de entrada, en las filas de entrada; vacío en las de salida.
Engine range-endopen o vacíoSolo en archivos generados por el motor v0.13.0 o posterior, incluida la v1.0.0: open en la fila de salida de una posición que el motor cerró al final del rango de datos.

Trade #: la clave de emparejamiento

Agrupa por Trade # para reconstruir una operación completa. Cada grupo tiene exactamente una fila de entrada y una de salida. En una estrategia con piramidación, cada entrada recibe su propio número de operación, igual que en la numeración de TradingView.

Net PnL y Cumulative PnL en las dos filas

Las dos filas de una operación llevan el Net PnL, el Net PnL % y el Cumulative PnL de la operación. Cuando sumes Net PnL, cuenta cada operación una sola vez (por ejemplo, solo las filas de salida).

Excursiones

Favorable excursion USD y Adverse excursion USD son el mejor y el peor resultado no realizado que alcanzó la posición antes de cerrarse, en la divisa de la cuenta. El valor adverso es cero o negativo, como en la exportación de TradingView. Juntos ayudan a evaluar dónde colocar el stop: una excursión adversa pequeña en una operación ganadora significa que la posición nunca estuvo en apuros.

Cargarlo en pandas

Este cargador une las filas de entrada y de salida de cada operación en una sola fila por operación. Se comprobó con un archivo del corpus (analyzer-anvil-percent-costs-01: 325 operaciones, P&L neto de −2.817,54).

import pandas as pd
 
def load_trades(path: str) -> pd.DataFrame:
    df = pd.read_csv(path)
 
    # Times are UTC, formatted "YYYY-MM-DD HH:MM", with no timezone suffix
    df["Date and time"] = pd.to_datetime(df["Date and time"], utc=True)
 
    entries = df[df["Type"].str.startswith("Entry")].rename(columns={
        "Date and time": "entry_dt",
        "Price":         "entry_price",
        "Type":          "direction",
    })
    entries["direction"] = entries["direction"].str.replace("Entry ", "")
 
    exits = df[df["Type"].str.startswith("Exit")].rename(columns={
        "Date and time":           "exit_dt",
        "Price":                   "exit_price",
        "Net PnL":                 "net_pnl",
        "Net PnL %":               "net_pnl_pct",
        "Favorable excursion USD": "mfe",
        "Adverse excursion USD":   "mae",
    })
 
    # One entry row and one exit row per Trade #
    trades = entries[["Trade #", "direction", "entry_dt", "entry_price", "Qty"]].merge(
        exits[["Trade #", "exit_dt", "exit_price", "net_pnl", "net_pnl_pct", "mfe", "mae"]],
        on="Trade #",
    )
    return trades.sort_values("Trade #").reset_index(drop=True)
 
 
if __name__ == "__main__":
    trades = load_trades("engine_trades.csv")
    print(trades[["Trade #", "direction", "entry_dt", "exit_dt", "net_pnl", "mfe", "mae"]]
          .head(5)
          .to_string(index=False))
    print(f"\nTotal trades: {len(trades)}")
    print(f"Win rate:     {(trades['net_pnl'] > 0).mean():.1%}")
    print(f"Net P&L:      {trades['net_pnl'].sum():.2f}")

Errores habituales

Cuenta cada operación una sola vez. Net PnL aparece en las dos filas de cada operación, así que sumar la columna entera duplica el resultado. Suma las filas de salida o agrupa antes por Trade #.

El archivo empieza por lo más reciente. La fila 1 es la salida de la última operación. Ordena por Trade # si necesitas orden cronológico.

Las marcas de tiempo están en UTC. La exportación de TradingView usa la zona horaria de tu gráfico, así que alinea ambas antes de comparar horas. Las horas de las operaciones en las listas de operaciones de TradingView del corpus están en Asia/Taipei (UTC+8), y el evaluador lo da por hecho salvo que el inputs.json de una estrategia indique otra zona en tv_trades_csv_tz.

Las posiciones abiertas al final se cierran. Igual que TradingView, el motor cierra una posición que sigue abierta después de la última barra, de modo que aparece como una operación normal (los motores actuales marcan su fila de salida en Engine range-end).

Comparación con la exportación de TradingView

La exportación «List of Trades» de TradingView tiene más columnas y con otros nombres: Trade number (en exportaciones antiguas, Trade #), Signal, un Price USDT con la divisa como sufijo, Size (qty), Net PnL USDT, etcétera, con las fechas en la zona horaria de tu gráfico. Empareja las columnas por su significado, no por su nombre, como hace scripts/verify_corpus.py, el evaluador del motor: acepta todas las variantes de nombre y empareja las operaciones por dirección, hora de entrada (con una hora de margen) y precio de entrada.

Por dónde seguir

  • Explora la galería: las páginas de cada estrategia muestran la lista de operaciones del motor junto a la de TradingView y enlazan los dos archivos CSV.
  • Usa los servidores MCP desde Claude o Cursor: backtest_pine devuelve las mismas operaciones en JSON para que tu asistente las analice; si el informe es demasiado grande para devolverlo en la respuesta, se guarda en un archivo (servidor local) o se ofrece como enlace de descarga (servidor alojado).