Référence

Lire le format engine_trades.csv

Référence complète du CSV de liste de trades émis par PineForge. Colonne par colonne, encodage des paires, et un snippet Python d’environ 30 lignes pour pandas.

5 min de lecture#docs#csv#engine

Chaque backtest PineForge écrit un fichier engine_trades.csv à côté du rapport JSON récapitulatif. C’est le journal ligne à ligne de chaque fill vu par le moteur : entrées, sorties, quantités, PnL et excursions intra-trade. Ce billet est la référence colonne par colonne — pour comprendre sans ambiguïté ce que signifie chaque cellule et reconstruire les round-trips sans mauvaise surprise.

À quoi sert le fichier

engine_trades.csv est la liste tabulaire des trades d’une exécution terminée. Chaque ligne est un fill — entrée ou sortie de position. Entrées et sorties partagent le même Trade # pour reconstruire les allers-retours.

Le format reflète l’export « List of Trades » de TradingView. Si vous connaissez déjà cet export, les en-têtes vous seront familiers. Différence notable : PineForge émet toujours MFE et MAE, colonnes que TradingView ne montre qu’aux abonnés Premium. Le reste aligne en-tête pour en-tête.

Référence colonne par colonne

Les noms de colonnes restent en anglais comme dans l’export TradingView pour simplifier les diffs. Les explications sont en français.

ColumnTypeRemarques
Trade #entierID du round-trip. Entrée et sortie partagent le même numéro.
TypechaîneUne valeur parmi : Entry long, Exit long, Entry short, Exit short
Date and timeUTCFormat YYYY-MM-DD HH:MM. Pas de suffixe fuseau ; toujours UTC.
PricefloatPrix de fill dans la devise de cotation au moment de l’exécution.
QtyfloatUnités échangées. Toujours positives ; la direction est dans Type.
Net PnLfloatP/L de cette liquidation. Uniquement sur les lignes de sortie. Sur entrée : vide ou zéro.
Net PnL %floatNet PnL en pourcentage du nocional d’entrée.
MFEfloatExcursion favorable max intra-trade — meilleur gain latent avant clôture.
MAEfloatExcursion défavorable max intra-trade — pire perte latente avant clôture.
Cumulative PnLfloatSomme courante de tous les Net PnL jusqu’à la ligne de sortie courante.

Trade # : clé d’appariement

L’entier identifie le round-trip. Chaque entrée reçoit un numéro, la sortie correspondante le répète. En pyramiding vous pouvez avoir plus de deux lignes avec le même Trade #. Parcourez par cette clé, pas seulement par ordre de fichier.

Type : quatre états

Entry long, Exit long, Entry short, Exit short. Pas de ligne « hold » : uniquement des fills.

Net PnL

Renseigné uniquement sur les lignes de sortie. Les entrées sont vides ou zéro. La valeur de vérité est sur la sortie appariée.

MFE et MAE

Meilleure plus-value latente intratrade (MFE) et pire perte latente (MAE), calculées sur l’OHLC du backtest. Utile pour juger le placement des stops.

Appareiller les trades

Groupez par Trade #. Sans pyramiding : une entrée et une sortie typiques. Les lignes ne sont pas forcément triées par numéro de trade ; avec des fills entrelacés, trie ou groupe avant d’inférer la séquence.

Charger dans 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}")

Pièges fréquents

Net PnL côté sortie seulement. Sommer ligne à ligne sans filtre fausse le total.

Cumulative PnL sans positions ouvertes. Si le backtest finit ouvert, le dernier cumul ignore le MTM ; voir open_equity dans le JSON.

Horodatage UTC. Pas de suffixe fuseau, mais toujours UTC.

Ordre des lignes. La ligne suivante ne ferme pas forcément la précédente.

Compatibilité inter-moteurs

Même schéma de base que l’export TradingView ; MFE/MAE absents hors Premium côté TV. PineForge les inclut toujours.

Pour aller plus loin

  • Galerie167 stratégies avec backtests publiés ; sur une fiche, « Download trades CSV » lorsque disponible.
  • MCP Claude / Cursorbacktest_pine renvoie le même JSON récapitulatif ; analyse de la liste de trades sans enregistrer le CSV.