"""Inputs measured on the EURUSD hourly bars the notebook uses (read only; no replay, no MC).

  a. the cost share of a variable stop is c E[1/l], not c / E[l] (Jensen), for the deployed rule
     and for the two knobs that give a 40-pip mean,
  b. the strict reading of the daily loss rule: a trade that is up at a day boundary and then
     stops out loses more than 5% of that day's equity when the boundary float exceeds
     (5% - r (1 + gamma)) / r of the stop, 0.633 at 3%,
  c. gaps: exits whose bar opens already past the level. The bar broker and the chains book the
     level; a live stop fills at the gap, a live target limit may fill at the better gap price.

Every entry day is scored on both sides, entry at the close of the first bar opening in
07:00-10:00 UTC (the strategy's window), RR 1, held across days until a leg is reached on the
bar's high or low, the stop on a tie. Days are entries, not accounts.
"""
import os
import sys

os.environ["TF_USE_NUMBA"] = "0"    # a read-only reproduction of the sigma kernel, not research code
sys.path.insert(0, "/root/Projects/trading-framework-prime")
os.chdir("/root/Projects/trading-framework-prime")

import numpy as np  # noqa: E402
import pandas as pd  # noqa: E402

from src.notebooks.reports.topics.prime_academy.modules import fxcontrol as FX  # noqa: E402
from src.notebooks.reports.topics.open_timeflow.modules import timeflow as TF  # noqa: E402

PATH = "historical_data/EURUSD/ohlcv_3600/eurusd_1h.parquet"
bars = FX.load(PATH)
raw = pd.read_parquet(PATH)
ts = raw.ts_event.astype("int64").to_numpy()
ts = ts // 10**9 if ts.max() > 10**12 else ts
dclose = pd.Series(bars["c"]).groupby(bars["day"]).last().to_numpy()
sig = np.r_[np.nan, TF.sigma_kf(dclose, rho_v=0.0025)[:-1] / FX.PIP][bars["day"]]
entry = (bars["day"] > TF.SIGMA_SEED_N) & (ts % 86400 >= 7 * 3600) & (ts % 86400 < 10 * 3600)
S = sig[entry]

# a. Jensen
c = 1.0      # pips: $5 commission (0.5 pip) and 0.5-pip spread
for name, beta, floor in (("deployed max(0.75 s, 30)", 0.75, 30.0), ("40-pip knob max(0.924 s, 30)", 0.9244754690483855, 30.0),
                          ("40-pip knob max(0.75 s, 38.3)", 0.75, 38.34362482448531)):
    l = np.maximum(beta * S, floor)
    print(f"{name:32}: mean {l.mean():5.2f} pips, c/E[l] {c / l.mean():.3%}, c E[1/l] {c * np.mean(1 / l):.3%}, "
          f"ratio {np.mean(1 / l) * l.mean():.4f}, share at the floor {np.mean(l == floor):.1%}")
print(f"fixed 40 pips: c/40 = {c / 40:.3%}")

# b and c. every entry day, both sides
h, lo, cl, op, day = bars["h"], bars["l"], bars["c"], bars["o"], bars["day"]
# the first entry bar of each day
ent_idx = pd.Series(np.flatnonzero(entry)).groupby(day[entry]).first().to_numpy()
rows = []
for i in ent_idx:
    l = max(0.75 * sig[i], 30.0) * FX.PIP
    e = cl[i]
    for side in (1.0, -1.0):
        stop, tgt = e - side * l, e + side * l
        worst_boundary_float = -np.inf       # the largest float (in stops) at a 00:00 UTC boundary before the exit
        worst_21 = -np.inf                   # the same at 21:00 UTC, the rollover, for a server day that ends there
        res = None
        for j in range(i + 1, min(i + 2000, len(cl))):
            # a day boundary falls between bar j-1 and bar j when the trading day changes
            if day[j] != day[j - 1]:
                f = side * (cl[j - 1] - e) / l
                worst_boundary_float = max(worst_boundary_float, f)
            if ts[j] % 86400 == 21 * 3600:
                worst_21 = max(worst_21, side * (cl[j - 1] - e) / l)
            adverse = (lo[j] <= stop) if side > 0 else (h[j] >= stop)
            fav = (h[j] >= tgt) if side > 0 else (lo[j] <= tgt)
            if adverse:
                gap = side * (stop - op[j]) / l if side * (op[j] - stop) < 0 else 0.0   # open already past the stop
                res = ("STOP", gap, day[j] - day[i])
                break
            if fav:
                gap = side * (op[j] - tgt) / l if side * (op[j] - tgt) > 0 else 0.0     # open already past the target
                res = ("TARGET", gap, day[j] - day[i])
                break
        if res is None:
            continue
        rows.append(dict(i=i, side=side, l_pips=l / FX.PIP, why=res[0], gap=res[1], days=res[2], bf=worst_boundary_float, bf21=worst_21))
T = pd.DataFrame(rows)
st = T[T.why == "STOP"]
print(f"\nentries {len(ent_idx)} days x 2 sides = {len(T)} resolved trades; stops {len(st)}; held past a day boundary {np.mean(T.days > 0):.1%}")
for r, gam in ((0.03, 0.0335), (0.0315, 0.0335), (0.04, 0.0335)):
    thr = (0.05 - r * (1 + gam)) / r
    q = np.mean((T.why == "STOP") & (T.bf > thr))
    q21 = np.mean((T.why == "STOP") & (T.bf21 > thr))
    print(f"strict daily rule at risk {r:.2%}: breach when the boundary float exceeds {thr:.3f} of the stop; "
          f"breaches per trade {q:.2%} at 00:00 UTC, {q21:.2%} at 21:00 UTC "
          f"(per losing trade {q / np.mean(T.why == 'STOP'):.2%}, {q21 / np.mean(T.why == 'STOP'):.2%})")
for why in ("STOP", "TARGET"):
    d = T[T.why == why]
    print(f"{why}: exits on a gap past the level {np.mean(d.gap > 0):.2%}, mean excess when gapped {d.gap[d.gap > 0].mean():.3f} of the stop, "
          f"mean excess per {why.lower()} {d.gap.mean() * 100:.3f}% of the stop, largest {d.gap.max():.3f}")
tg = T[T.why == "TARGET"]
print(f"targets gapping more than 5% of the stop past the level (a win over the 3.15% cap at 3% risk): {np.mean(tg.gap > 0.05):.2%} of wins")
print(f"stop gap excess per trade, both outcomes: {st.gap.sum() / len(T) * 100:.3f}% of the stop = cost share")
T.to_csv("/tmp/arena-prime-audit/candidate-1/out_s04_trades.csv", index=False)
