| """
|
| Summarise a set of WarriorDipZ tester runs: the report's MARK-TO-MARKET equity
|
| drawdown plus journal-derived cadence, swap drag and an IS/OOS split.
|
|
|
| python grid_summary.py claude_g4_c0 claude_g4_c10 ...
|
|
|
| Screen (the account's actual rule): cadence >= 2/mo, equity maxDD <= 5%,
|
| ret/DD >= 2 -- with ret/DD computed on the MARK-TO-MARKET drawdown, never the
|
| exit-based one, which understates concurrent losses.
|
| """
|
|
|
| from __future__ import annotations
|
|
|
| import re
|
| import sys
|
| import numpy as np
|
|
|
| TERM = r"C:\Users\admin\AppData\Roaming\MetaQuotes\Terminal\10CE948A1DFC9A8C27E56E827008EBD4"
|
| COMMON = r"C:\Users\admin\AppData\Roaming\MetaQuotes\Terminal\Common\Files"
|
| DEPOSIT = 100000.0
|
|
|
|
|
| def report(name):
|
| t = open(rf"{TERM}\{name}.htm", encoding="utf-16").read()
|
| cells = [re.sub(r"\s+", " ", re.sub(r"<[^>]+>", "", c)).strip()
|
| for c in re.findall(r"<td[^>]*>(.*?)</td>", t, flags=re.S)]
|
| out = {}
|
| for i, c in enumerate(cells):
|
| nxt = next((x for x in cells[i + 1:i + 4] if x), "")
|
| if c == "Total Net Profit:":
|
| out["net"] = float(nxt.replace(" ", ""))
|
| elif c == "Equity Drawdown Maximal:":
|
| out["eqdd"] = float(re.search(r"\(([\d.]+)%\)", nxt).group(1)) / 100
|
| elif c == "Profit Factor:":
|
| out["pf"] = float(nxt)
|
| return out
|
|
|
|
|
| def journal(name):
|
| raw = np.genfromtxt(rf"{COMMON}\dipz_trades_{name}.csv", delimiter=",",
|
| skip_header=1, dtype=str, encoding="ansi")
|
| ts = lambda s: np.datetime64(s[:10].replace(".", "-") + "T" + s[11:] + ":00") # noqa: E731
|
| return [dict(sym=r[1], t=ts(r[2]), x=ts(r[5]), net=float(r[7]), gross=float(r[9]),
|
| swap=float(r[10])) for r in raw]
|
|
|
|
|
| def seg_stats(rows):
|
| rows = sorted(rows, key=lambda r: r["x"])
|
| q = DEPOSIT + np.concatenate([[0], np.cumsum([r["net"] for r in rows])])
|
| dd = ((np.maximum.accumulate(q) - q) / np.maximum.accumulate(q)).max()
|
| return q[-1] / DEPOSIT - 1, dd
|
|
|
|
|
| if __name__ == "__main__":
|
| names = sys.argv[1:]
|
| print(f"{'run':<16}{'n':>5}{'/mo':>6}{'net':>8}{'CAGR':>7}{'eqDD':>7}{'ret/DD':>8}{'PF':>6}"
|
| f"{'swap':>8}{'swap%':>7} {'IS ret/DDx':>10}{'OOS ret/DDx':>12} screen")
|
| for nm in names:
|
| try:
|
| rp, js = report(nm), journal(nm)
|
| except Exception as e: # noqa: BLE001
|
| print(f"{nm:<16} unreadable: {e}")
|
| continue
|
| yrs = (max(r["x"] for r in js) - min(r["t"] for r in js)) / np.timedelta64(365, "D")
|
| tot = rp["net"] / DEPOSIT
|
| cagr = (1 + tot) ** (1 / yrs) - 1
|
| rdd = tot / rp["eqdd"]
|
| swap = sum(r["swap"] for r in js)
|
| gross = sum(r["gross"] for r in js)
|
| split = np.datetime64("2024-01-01")
|
| is_t, is_d = seg_stats([r for r in js if r["t"] < split])
|
| oo_t, oo_d = seg_stats([r for r in js if r["t"] >= split])
|
| permo = len(js) / (yrs * 12)
|
| fails = [k for k, bad in (("cadence", permo < 2), ("eqDD", rp["eqdd"] > 0.05),
|
| ("ret/DD", rdd < 2)) if bad]
|
| print(f"{nm:<16}{len(js):>5}{permo:>6.1f}{tot:>+8.1%}{cagr:>+7.1%}{rp['eqdd']:>7.2%}"
|
| f"{rdd:>8.2f}{rp['pf']:>6.2f}{swap:>8.0f}{swap / gross if gross else 0:>7.0%}"
|
| f" {is_t / is_d if is_d else float('nan'):>10.2f}{oo_t / oo_d if oo_d else float('nan'):>12.2f}"
|
| f" {'PASS' if not fails else 'fail:' + ','.join(fails)}")
|
| print("\nIS/OOS ret/DDx use the exit-based drawdown inside each window (split 2024-01-01);"
|
| " the headline ret/DD uses the tester's mark-to-market equity drawdown.")
|