PineForgeが公開コーパスの全戦略について書き出すトレード一覧、engine_trades.csvのリファレンス。列ごとの意味、行がどうトレードとしてペアになるか、そしてpandas用のローダー。
現在の最新版は、pineforge-engine v1.10.0(2026-11-03 リリース)です。
2026-09-30 更新:エンジンが実際に書き出すフォーマット(pineforge-engine v1.0.0)に合わせました。
engine_trades.csv は、エンジンのトレード一覧を TradingView の「List of Trades」エクスポートと同じ形の CSV にしたもので、両者をトレード単位で突き合わせられるようになっています。書き出すのはエンジンリポジトリの scripts/run_strategy.py で、公開コーパス pineforge-corpus に収録された 312 本の戦略はそれぞれ、TradingView 自身のエクスポートである tv_trades.csv と並べてこのファイルを同梱しています(ただしセルフテスト用の戦略 1 本だけは、その代わりにモードごとの TradingView エクスポート trades-<mode>.csv を同梱しています)。Docker イメージと MCP サーバーは同じトレードを JSON で、レポートの trades 配列として返します。この記事で扱うのは CSV のほうです。
どんなファイルか
各行は、決済済みトレードの片側、つまりエグジットかエントリーのどちらかです。どのトレードもちょうど 2 行からなり、エグジット行が先に来て、両方の行に同じ Trade # が入ります。トレードは新しい順に並んでいて、ファイルは最大のトレード番号から始まり、トレード 1 で終わります。
列ごとのリファレンス
| 列 | 型 | 説明 |
|---|---|---|
Trade # | 整数 | トレードの番号。そのトレードのエグジット行とエントリー行で共通です。 |
Type | 文字列 | Entry long、Exit long、Entry short、Exit short のいずれか。 |
Date and time | 文字列 | 約定時刻(UTC)。形式は YYYY-MM-DD HH:MM で、タイムゾーンの接尾辞は付きません。 |
Price | float | この側の約定価格。 |
Qty | float | 取引数量。常に正の値です。 |
Net PnL | float | トレードの純損益。両方の行に同じ値が入ります。 |
Net PnL % | float | Net PnL をパーセントで表したもの。両方の行に同じ値が入ります。 |
Favorable excursion USD | float | トレード中の最大の含み益。ゼロまたは正の値です。 |
Adverse excursion USD | float | トレード中の最大の含み損。ゼロまたは負の値です(TradingView の慣例)。 |
Cumulative PnL | float | トレード 1 からこのトレードまでの Net PnL の累計。両方の行に同じ値が入ります。 |
Engine entry incarnation | 整数または空 | 物理的なエントリー約定に対してエンジンが付ける ID。エントリー行に入り、エグジット行では空です。 |
Engine range-end | open または空 | エンジン v0.13.0 以降(v1.0.0 を含む)のファイルにのみある列です。データ範囲の終端でエンジンが決済したポジションのエグジット行に open が入ります。 |
Trade #:ペアをつなぐキー
ラウンドトリップを復元するには、Trade # でグループ化します。各グループにはエントリー行とエグジット行がちょうど 1 行ずつあります。ピラミッディングする戦略では、TradingView の採番と同じく、エントリーごとに 1 つのトレード番号が振られます。
Net PnL と Cumulative PnL は両方の行に入る
トレードの 2 行にはどちらも、そのトレードの Net PnL、Net PnL %、Cumulative PnL が入っています。Net PnL を合計するときは、各トレードを一度だけ数えてください(たとえばエグジット行だけを使うなど)。
エクスカーション
Favorable excursion USD と Adverse excursion USD は、ポジションが決済されるまでに到達した最良と最悪の含み損益で、口座通貨建てです。TradingView のエクスポートと同じく、adverse 側の値はゼロまたは負になります。この 2 つを合わせて見ると、ストップの置き方を評価しやすくなります。勝ちトレードで adverse excursion が小さければ、そのポジションは一度も追い込まれなかったということです。
pandas に読み込む
次のローダーは、各トレードのエントリー行とエグジット行を結合し、1 トレードにつき 1 行にまとめます。コーパスのファイル(analyzer-anvil-percent-costs-01:325 トレード、純損益 −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}")
よくある落とし穴
各トレードは一度だけ数える。 Net PnL はトレードの 2 行の両方に入っているので、列全体を合計すると結果が 2 倍になります。エグジット行だけを合計するか、先に Trade # でグループ化してください。
ファイルは新しい順に並んでいる。 1 行目は最後のトレードのエグジットです。時系列順が必要なら、Trade # でソートしてください。
タイムスタンプは UTC。 TradingView 自身のエクスポートはチャートのタイムゾーンを使うので、時刻を比べる前に両者をそろえてください。コーパスの TradingView トレード一覧の時刻は Asia/Taipei(UTC+8)です。グレーダーも、戦略の inputs.json が tv_trades_csv_tz で別のタイムゾーンを指定していない限り、このタイムゾーンを前提にします。
最後まで残ったポジションは決済される。 TradingView と同じく、エンジンは最終バーを過ぎても保有中のポジションを決済するので、通常のトレードとして現れます(現行エンジンでは、そのエグジット行が Engine range-end でフラグ付けされます)。
TradingView のエクスポートと比較する
TradingView の「List of Trades」エクスポートは列が多く、名前も異なります。Trade number(古いエクスポートでは Trade #)、Signal、通貨名の付いた Price USDT、Size (qty)、Net PnL USDT などで、日時はチャートのタイムゾーンで記録されます。列は名前ではなく意味で対応づけてください。エンジンのグレーダーである scripts/verify_corpus.py もそうしています。このスクリプトはどの表記も受け付け、方向、エントリー時刻(1 時間以内)、エントリー価格でトレードをペアにします。
次に読むなら
- ギャラリーを見る:戦略ページでは、エンジンのトレード一覧が TradingView のものと並べて表示され、両方の CSV ファイルへのリンクもあります。
- Claude や Cursor から MCP サーバーを使う:
backtest_pineは同じトレードを JSON で返すので、アシスタントに分析を任せられます。インラインで返すには大きすぎるレポートは、ファイルに保存されるか(ローカルサーバー)、ダウンロード用のリンクとして返されます(ホスト型サーバー)。