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
| Colonne | Type | Remarques |
|---|---|---|
Trade # | entier | Numéro du trade, commun à sa ligne de sortie et à sa ligne d’entrée. |
Type | texte | Entry long, Exit long, Entry short ou Exit short. |
Date and time | texte | Heure d’exécution en UTC, YYYY-MM-DD HH:MM, sans suffixe de fuseau horaire. |
Price | float | Prix d’exécution de ce côté du trade. |
Qty | float | Unités échangées, toujours positives. |
Net PnL | float | Gain ou perte net du trade. Même valeur sur les deux lignes. |
Net PnL % | float | Net PnL en pourcentage. Même valeur sur les deux lignes. |
Favorable excursion USD | float | Meilleur gain latent pendant le trade, nul ou positif. |
Adverse excursion USD | float | Pire perte latente pendant le trade, nulle ou négative (convention de TradingView). |
Cumulative PnL | float | Somme cumulée des Net PnL, du trade 1 jusqu’à ce trade. Même valeur sur les deux lignes. |
Engine entry incarnation | entier ou vide | Identifiant, 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-end | open ou vide | Uniquement 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_pinerenvoie 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é).