Referencia

Leer el formato engine_trades.csv

Referencia completa del CSV de lista de operaciones que emite PineForge. Significado columna a columna, cómo se emparejan trades, y un snippet Python de ~30 líneas para cargarlo en pandas.

5 min de lectura#docs#csv#engine

Cada backtest de PineForge deja un archivo engine_trades.csv junto al informe JSON resumen. Es el libro mayor fila a fila de cada fill que vio el motor: entradas, salidas, cantidades, PnL y métricas de excursión dentro del trade. Esta entrada es la referencia columna a columna para ese archivo — pensada para quien quiere entender sin ambigüedad qué significa cada celda y cómo reconstruir round-trips sin sustos.

Qué es el archivo

engine_trades.csv es la lista tabular de operaciones de una corrida terminada. Cada fila es un fill — entrada a posición o salida. Las entradas y salidas comparten el mismo Trade # para recomponer idas y vueltas.

El diseño calca la exportación «List of Trades» de TradingView. Si ya trabajaste con ese CSV, los encabezados te sonarán familiares. La diferencia práctica: PineForge siempre incluye MFE y MAE; TradingView solo los muestra en planes Premium. El resto coincide encabezado a encabezado.

Referencia columna a columna

Los nombres de columna coinciden con el CSV de TradingView; los mantenemos en inglés para que puedas diff línea a línea. Las notas las damos en español.

ColumnTypeNotas
Trade #enteroID del round-trip. Entrada y salida comparten el mismo número.
TypecadenaUno de: Entry long, Exit long, Entry short, Exit short
Date and timeUTCFormato YYYY-MM-DD HH:MM. Sin sufijo de zona; siempre UTC.
PricefloatPrecio de fill en divisa cotizada en el instante de ejecución.
QtyfloatUnidades operadas. Siempre positivas; la dirección va en Type.
Net PnLfloatP/L de esta liquidación. Solo en filas de salida. En entrada suele ir vacío o cero.
Net PnL %floatNet PnL como porcentaje del nocional de entrada.
MFEfloatMáxima excursión favorable intradia — mejor ganancia no realizada antes del cierre.
MAEfloatMáxima excursión adversa intradia — peor pérdida no realizada antes del cierre.
Cumulative PnLfloatSuma acumulada de todos los Net PnL hasta la fila de salida actual.

Trade #: la clave de emparejamiento

El entero identifica el round-trip. Cada entrada recibe un número y la salida correspondiente repite el mismo. Si hay pirámides puedes ver más de dos filas con el mismo Trade #. Recorre por esta clave, no solo por orden de filas.

Type: cuatro valores

Entry long, Exit long, Entry short, Exit short. Long/short da la dirección; entry/exit dice la pata del ciclo. No hay filas de «hold» ni «flat»: solo fills.

Net PnL y dónde mirarlo

Net PnL va solo en filas de salida. Las entradas suelen venir vacías o en cero. Si ves un valor raro en entrada, ignóralo; la salida emparejada manda.

MFE y MAE

MFE es la mejor ganancia intradia no realizada; MAE la peor pérdida intradia no realizada. Se calculan con OHLC del backtest. Sirven para evaluar stops: MAE pequeño con Net PnL grande suele indicar que no te exprimieron antes del objetivo.

Emparejar trades: recorrer el archivo

Lo habitual es agrupar por Trade #. Sin pirámides cada grupo es pare entrada/salida; con pirámides puede haber varias entradas y salidas bajo el mismo número.

Las filas no tienen por qué ir ordenadas por Trade #. Con órdenes entrelazadas puedes ver la salida del trade 17 antes de la entrada del 12. Agrupa o ordena antes de asumir secuencia lineal.

Cargarlo en pandas

import pandas as pd
 
def load_trades(path: str) -> pd.DataFrame:
    df = pd.read_csv(path)
 
    # Parse timestamps; UTC, no tz suffix in the file
    df["Date and time"] = pd.to_datetime(df["Date and time"], utc=True)
 
    entries = df[df["Type"].str.startswith("Entry")].copy()
    exits   = df[df["Type"].str.startswith("Exit")].copy()
 
    entries = entries.rename(columns={
        "Date and time": "entry_dt",
        "Price":         "entry_price",
        "Qty":           "entry_qty",
        "Type":          "direction",
    })
    entries["direction"] = entries["direction"].str.replace("Entry ", "")
 
    exits = exits.rename(columns={
        "Date and time": "exit_dt",
        "Price":         "exit_price",
        "Net PnL":       "net_pnl",
        "Net PnL %":     "net_pnl_pct",
        "MFE":           "mfe",
        "MAE":           "mae",
    })
 
    # Join on Trade # (take last exit for pyramiding strategies)
    exits_agg = exits.groupby("Trade #").last().reset_index()
    entries_agg = entries.groupby("Trade #").first().reset_index()
 
    trades = entries_agg.merge(exits_agg[
        ["Trade #", "exit_dt", "exit_price", "net_pnl", "net_pnl_pct", "mfe", "mae"]
    ], on="Trade #")
 
    return trades
 
 
if __name__ == "__main__":
    trades = load_trades("engine_trades.csv")
    print(trades[["Trade #", "direction", "entry_dt", "exit_dt", "net_pnl", "mfe", "mae"]]
          .head(10)
          .to_string(index=False))
    print(f"\nTotal trades: {len(trades)}")
    print(f"Win rate:     {(trades['net_pnl'] > 0).mean():.1%}")
    print(f"Avg MFE:      {trades['mfe'].mean():.4f}")
    print(f"Avg MAE:      {trades['mae'].mean():.4f}")

Con pirámides toma la primera entrada y la última salida por Trade #; si necesitas cada salida parcial, itera dentro del grupo.

Qué suele pegarte

Net PnL solo en salida. Si sumas fila a fila sin filtrar, duplicas o te pierdes valores.

Cumulative PnL sin posiciones abiertas. Solo suma trades cerrados. Si queda posición abierta al cortar el backtest, el último acumulado no marca ese MTM; el JSON trae open_equity aparte.

Timestamps en UTC. Sin sufijo de zona, pero siempre UTC. Para comparar con otro huso, convierte tú; pd.to_datetime(..., utc=True) ayuda.

El orden temporal no es el orden lógico del trade. No asumas que la fila N+1 cierra la fila N.

Cruce con otros motores

TradingView exporta los mismos encabezados base; MFE/MAE solo en Premium. PineForge siempre los emite. Un mismo loader puede servir para ambos CSV si compruebas antes si existen esas columnas.

Siguientes pasos

  • Galería: 167 estrategias con backtests publicados — entra en una ficha y usa «Download trades CSV» cuando esté disponible.
  • MCP desde Claude o Cursor: backtest_pine devuelve el mismo JSON resumen y puedes pedir análisis del listado sin guardar el CSV en disco.