You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
62 lines
2.4 KiB
Python
62 lines
2.4 KiB
Python
"""Honest G2 check: recompute relief on V49.91's ACTUAL negative-PnL days only.
|
|
|
|
Advisor flagged that "worst-20%" of V49's days inflates by including zero-PnL
|
|
no-trade days, making the metric partly tautological.
|
|
|
|
Compares:
|
|
- V50 avg daily PnL (all days)
|
|
- V50 avg daily PnL on V49's actual loss days (v49_pnl < 0)
|
|
- V50 avg daily PnL on V49's actual no-trade days (v49_pnl == 0)
|
|
|
|
Real anti-correlation = V50 does BETTER on V49 loss days than V50's overall avg.
|
|
Just "fills the gap" = V50 does similar on V49 zero days.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
|
|
|
|
def main() -> int:
|
|
if len(sys.argv) < 3:
|
|
print("Usage: honest_g2_check.py V49_PATH V50_PATH")
|
|
return 1
|
|
with open(sys.argv[1]) as f:
|
|
v49 = {r["date"]: float(r["daily_pnl"]) for r in json.load(f)["daily_summary"]}
|
|
with open(sys.argv[2]) as f:
|
|
v50 = {r["date"]: float(r["day_pnl"]) for r in json.load(f)["daily_results"]}
|
|
|
|
common = sorted(set(v49) & set(v50))
|
|
|
|
def stats(label: str, dates: list[str]) -> None:
|
|
if not dates:
|
|
print(f" {label}: 0 days")
|
|
return
|
|
v49_vals = [v49[d] for d in dates]
|
|
v50_vals = [v50[d] for d in dates]
|
|
avg49 = sum(v49_vals) / len(dates)
|
|
avg50 = sum(v50_vals) / len(dates)
|
|
sum50 = sum(v50_vals)
|
|
print(f" {label:36s}: n={len(dates):3d} V49 avg=${avg49:>+7.2f} V50 avg=${avg50:>+7.2f} V50 sum=${sum50:>+7.0f}")
|
|
|
|
for slice_label, slice_dates in [
|
|
("FULL", common),
|
|
("TRAIN (2025-H2)", [d for d in common if d <= "2025-12-31"]),
|
|
("TEST (2026 YTD)", [d for d in common if d >= "2026-01-01"]),
|
|
]:
|
|
print(f"\n=== {slice_label} ({len(slice_dates)} days) ===")
|
|
stats("All days", slice_dates)
|
|
stats("V49 actual loss days (v49<0)", [d for d in slice_dates if v49[d] < 0])
|
|
stats("V49 zero-PnL no-trade days", [d for d in slice_dates if v49[d] == 0])
|
|
stats("V49 win days (v49>0)", [d for d in slice_dates if v49[d] > 0])
|
|
|
|
print("\n=== Interpretation ===")
|
|
print("If V50 avg on V49-loss-days > V50 avg on all days → real anti-correlation")
|
|
print("If V50 avg on V49-loss-days ≈ V50 avg on all days → independent (fills the gap)")
|
|
print("If V50 avg on V49-loss-days < V50 avg on all days → mildly correlated (regime alignment)")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|