Files
FX-ML-Trading-Engine/Q Research/scripts/update_metrics_from_tca.py
T
2025-11-14 23:16:51 +00:00

91 lines
3.5 KiB
Python

#!/usr/bin/env python3
"""
Append a metrics row using TCA summary + KPI overrides.
Example:
python scripts/update_metrics_from_tca.py \
--tca QuantTrader/results/execution/tca_summary.json \
--run-id 20251112_parallel_demo_live \
--status pass --latency-avg 28 --latency-p95 45 \
--total-pnl 27.3 --max-exposure 500000 --max-drawdown 0.02 \
--rolling-sharpe 1.5 --live-drawdown 0.03 \
--live-latency-p95 45 --slippage-bps 1.2
"""
from __future__ import annotations
import argparse
import csv
import json
from datetime import datetime, timezone
from pathlib import Path
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Append metrics row from TCA summary.")
parser.add_argument("--tca", required=True, help="Path to tca_summary.json")
parser.add_argument("--metrics", default="results/risk/metrics.csv", help="Metrics CSV path")
parser.add_argument("--run-id", required=True)
parser.add_argument("--status", default="pass")
parser.add_argument("--latency-avg", type=float, required=True)
parser.add_argument("--latency-p95", type=float, required=True)
parser.add_argument("--total-pnl", type=float, required=True)
parser.add_argument("--max-exposure", type=float, required=True)
parser.add_argument("--max-drawdown", type=float, required=True)
parser.add_argument("--rolling-sharpe", type=float, required=True)
parser.add_argument("--live-drawdown", type=float, required=True)
parser.add_argument("--live-latency-p95", type=float, required=True)
parser.add_argument("--slippage-bps", type=float, required=True)
parser.add_argument("--rejects", type=int, default=0)
parser.add_argument("--kills", type=int, default=0)
parser.add_argument("--max-gross-notional", type=float, default=0.0)
return parser.parse_args()
def main() -> None:
args = parse_args()
tca_path = Path(args.tca)
if not tca_path.exists():
raise SystemExit(f"TCA summary not found: {tca_path}")
with tca_path.open("r", encoding="utf-8") as f:
tca = json.load(f)
timestamp = datetime.now(timezone.utc).isoformat()
row = {
"timestamp": timestamp,
"run_id": args.run_id,
"rejects": args.rejects,
"kills": args.kills,
"status": args.status,
"latency_ms_avg": args.latency_avg,
"latency_ms_p95": args.latency_p95,
"total_pnl": args.total_pnl,
"max_gross_notional": args.max_gross_notional or args.max_exposure,
"max_symbol_exposure": args.max_exposure,
"max_drawdown_pct": args.max_drawdown,
"rolling_sharpe_30d": args.rolling_sharpe,
"live_drawdown_pct": args.live_drawdown,
"live_latency_ms_p95": args.live_latency_p95,
"slippage_bps": args.slippage_bps,
"paper_trade_count": tca.get("paper_trade_count"),
"paper_total_pnl": tca.get("paper_total_pnl"),
"live_trade_count": tca.get("live_trade_count"),
"live_total_pnl": tca.get("live_total_pnl"),
"pnl_diff_mean": tca.get("pnl_diff_mean"),
"pnl_diff_std": tca.get("pnl_diff_std"),
}
metrics_path = Path(args.metrics)
metrics_path.parent.mkdir(parents=True, exist_ok=True)
write_header = not metrics_path.exists()
with metrics_path.open("a", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=row.keys())
if write_header:
writer.writeheader()
writer.writerow(row)
print(f"Appended metrics row for run={args.run_id}")
if __name__ == "__main__":
main()