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.
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.
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_pinedevuelve el mismo JSON resumen y puedes pedir análisis del listado sin guardar el CSV en disco.