mirror of
https://github.com/xavierchuan/FX-ML-Trading-Engine.git
synced 2026-08-16 18:38:06 +00:00
Add files via upload
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Compare paper vs. live fills to produce a lightweight TCA summary.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pandas as pd
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Compare paper vs live fills.")
|
||||
parser.add_argument("--paper", required=True, help="Path to paper fills CSV.")
|
||||
parser.add_argument("--live", required=True, help="Path to live fills CSV.")
|
||||
parser.add_argument("--out", default="results/execution/tca_summary.json", help="Output JSON path.")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def load(path: str) -> pd.DataFrame:
|
||||
csv = Path(path)
|
||||
if not csv.exists():
|
||||
raise SystemExit(f"fills CSV not found: {csv}")
|
||||
df = pd.read_csv(csv)
|
||||
if df.empty:
|
||||
raise SystemExit(f"{csv} is empty")
|
||||
if "ts" in df.columns:
|
||||
df["ts"] = pd.to_datetime(df["ts"])
|
||||
return df
|
||||
|
||||
|
||||
def summary(df: pd.DataFrame, label: str) -> dict:
|
||||
pnl = df["pnl"] if "pnl" in df.columns else pd.Series(dtype=float)
|
||||
latency = df["adapter_latency_ms"] if "adapter_latency_ms" in df.columns else pd.Series(dtype=float)
|
||||
return {
|
||||
f"{label}_trade_count": int(len(df)),
|
||||
f"{label}_total_pnl": float(pnl.sum()) if not pnl.empty else None,
|
||||
f"{label}_avg_pnl": float(pnl.mean()) if not pnl.empty else None,
|
||||
f"{label}_avg_latency_ms": float(latency.mean()) if not latency.empty else None,
|
||||
}
|
||||
|
||||
|
||||
def pnl_gap(paper: pd.DataFrame, live: pd.DataFrame) -> dict:
|
||||
cols = []
|
||||
if "ts" in paper.columns and "ts" in live.columns:
|
||||
merged = pd.merge(
|
||||
paper[["ts", "pnl"]].rename(columns={"pnl": "paper_pnl"}),
|
||||
live[["ts", "pnl"]].rename(columns={"pnl": "live_pnl"}),
|
||||
on="ts",
|
||||
how="outer",
|
||||
)
|
||||
else:
|
||||
merged = pd.DataFrame({"paper_pnl": paper.get("pnl"), "live_pnl": live.get("pnl")})
|
||||
merged = merged.fillna(0.0)
|
||||
merged["pnl_diff"] = merged["live_pnl"] - merged["paper_pnl"]
|
||||
return {
|
||||
"pnl_diff_mean": float(merged["pnl_diff"].mean()),
|
||||
"pnl_diff_std": float(merged["pnl_diff"].std(ddof=0)),
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
paper = load(args.paper)
|
||||
live = load(args.live)
|
||||
|
||||
report = {"paper_path": args.paper, "live_path": args.live}
|
||||
report.update(summary(paper, "paper"))
|
||||
report.update(summary(live, "live"))
|
||||
report.update(pnl_gap(paper, live))
|
||||
|
||||
out_path = Path(args.out)
|
||||
out_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
out_path.write_text(json.dumps(report, indent=2, default=float), encoding="utf-8")
|
||||
print(json.dumps(report, indent=2, default=float))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user