Aller au contenu

Lire le format engine_trades.csv

5 min de lecture

Référence du fichier engine_trades.csv, la liste de trades que PineForge écrit pour chaque stratégie de son corpus ouvert : colonne par colonne, comment les lignes s’apparient en trades, et un chargeur pandas.

Dernière version de pineforge-engine : v1.10.0, sortie le 2026-11-03.

Mis à jour le 2026-09-30 pour refléter le format qu’écrit le moteur (pineforge-engine v1.0.0).

engine_trades.csv est la liste de trades du moteur, dans un CSV calqué sur l’export « List of Trades » de TradingView, pour que les deux puissent être alignés trade par trade. C’est scripts/run_strategy.py, dans le dépôt du moteur, qui l’écrit, et chacune des 312 stratégies du pineforge-corpus public en fournit un, à côté de l’export de TradingView lui-même, tv_trades.csv (une stratégie d’autotest fournit à la place un export TradingView par mode, trades-<mode>.csv). L’image Docker et les serveurs MCP renvoient les mêmes trades en JSON, dans le tableau trades du rapport ; ce billet porte sur le CSV.

Ce que contient le fichier

Chaque ligne représente un côté d’un trade clôturé : la sortie ou l’entrée. Chaque trade occupe exactement deux lignes, la ligne de sortie en premier, et les deux portent le même Trade #. Les trades vont du plus récent au plus ancien : le fichier commence par le numéro de trade le plus élevé et se termine par le trade 1.

Référence colonne par colonne

ColonneTypeRemarques
Trade #entierNuméro du trade, commun à sa ligne de sortie et à sa ligne d’entrée.
TypetexteEntry long, Exit long, Entry short ou Exit short.
Date and timetexteHeure d’exécution en UTC, YYYY-MM-DD HH:MM, sans suffixe de fuseau horaire.
PricefloatPrix d’exécution de ce côté du trade.
QtyfloatUnités échangées, toujours positives.
Net PnLfloatGain ou perte net du trade. Même valeur sur les deux lignes.
Net PnL %floatNet PnL en pourcentage. Même valeur sur les deux lignes.
Favorable excursion USDfloatMeilleur gain latent pendant le trade, nul ou positif.
Adverse excursion USDfloatPire perte latente pendant le trade, nulle ou négative (convention de TradingView).
Cumulative PnLfloatSomme cumulée des Net PnL, du trade 1 jusqu’à ce trade. Même valeur sur les deux lignes.
Engine entry incarnationentier ou videIdentifiant, côté moteur, de l’exécution physique de l’entrée, sur les lignes d’entrée ; vide sur les lignes de sortie.
Engine range-endopen ou videUniquement dans les fichiers produits par le moteur à partir de la v0.13.0, v1.0.0 comprise : open sur la ligne de sortie d’une position que le moteur a clôturée en fin de plage de données.

Trade # : la clé d’appariement

Regroupez par Trade # pour reconstituer un aller-retour. Chaque groupe compte exactement une ligne d’entrée et une ligne de sortie. Une stratégie qui pratique le pyramidage obtient un numéro de trade par entrée, comme TradingView les numérote.

Net PnL et Cumulative PnL sur les deux lignes

Les deux lignes d’un trade portent son Net PnL, son Net PnL % et son Cumulative PnL. Quand vous additionnez les Net PnL, comptez chaque trade une seule fois (par exemple, uniquement les lignes de sortie).

Excursions

Favorable excursion USD et Adverse excursion USD sont le meilleur et le pire résultat latent atteints par la position avant sa clôture, dans la devise du compte. La valeur défavorable est nulle ou négative, comme dans l’export de TradingView. Ensemble, elles aident à juger le placement des stops : une faible excursion défavorable sur un trade gagnant signifie que la position n’a jamais été mise sous pression.

Charger le fichier dans pandas

Ce chargeur fusionne les lignes d’entrée et de sortie de chaque trade en une seule ligne par trade. Il a été vérifié sur un fichier du corpus (analyzer-anvil-percent-costs-01 : 325 trades, P&L net 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}")

Pièges courants

Comptez chaque trade une seule fois. Net PnL figure sur les deux lignes d’un trade : additionner toute la colonne double le résultat. Additionnez les lignes de sortie, ou regroupez d’abord par Trade #.

Le fichier commence par le trade le plus récent. La ligne 1 est la sortie du dernier trade. Triez par Trade # si vous avez besoin de l’ordre chronologique.

Les horodatages sont en UTC. L’export de TradingView utilise le fuseau horaire de votre graphique : alignez les deux avant de comparer des heures. Les heures des trades dans les listes de trades de TradingView du corpus sont en Asia/Taipei (UTC+8), et le script de notation part de ce principe, sauf si le inputs.json d’une stratégie indique un autre fuseau dans tv_trades_csv_tz.

Les positions encore ouvertes à la fin sont clôturées. Comme TradingView, le moteur clôture une position toujours ouverte après la dernière barre ; elle apparaît donc comme un trade normal (les moteurs actuels signalent sa ligne de sortie dans Engine range-end).

Comparer avec l’export de TradingView

L’export « List of Trades » de TradingView compte plus de colonnes, sous d’autres noms : Trade number (Trade # dans les exports plus anciens), Signal, un Price USDT suffixé par la devise, Size (qty), Net PnL USDT, etc., avec des dates dans le fuseau horaire de votre graphique. Appariez les colonnes par leur sens, pas par leur nom, comme le fait scripts/verify_corpus.py, le script de notation du moteur : il accepte toutes les graphies et apparie les trades par direction, heure d’entrée (à une heure près) et prix d’entrée.

Pour aller plus loin

  • Parcourir la galerie : les pages de stratégie affichent la liste de trades du moteur à côté de celle de TradingView, et renvoient vers les deux fichiers CSV.
  • Utiliser les serveurs MCP depuis Claude ou Cursor : backtest_pine renvoie les mêmes trades en JSON pour que votre assistant les analyse ; un rapport trop volumineux pour être renvoyé directement est enregistré dans un fichier (serveur local) ou proposé en téléchargement via un lien (serveur hébergé).