"""SEA01 — Hour-of-day expectancy (seasonal/intraday pattern).

IDEA: On 1h bars, compute per-UTC-hour mean return using an EXPANDING in-sample
window (strictly causal). Go long during hours whose expanding-window mean is
positive, flat otherwise. Position is vol-targeted.

Causal guarantee:
  - At bar i (UTC hour h), we compute the mean return for hour h using all
    *prior* bars with that same hour: mean_r[h] = mean(r[j] for j < i where hour[j] == h).
  - We assign target[i] based on mean_r[h at bar i], which uses data up to i-1.
  - The lib then holds target[i] during bar i+1 (shift done by lib).

Grid: we test different minimum-samples thresholds (how many past observations of
that hour are required before we take a position): [30, 90].
This keeps total backtests at 2 TFs x 2 params x 2 assets = 8, but study_weights
handles BTC+ETH internally — so 2 TFs x 2 params = 4 calls total.
"""

import sys
sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/alt")
import altlib as al
import numpy as np
import pandas as pd


def sea01_target(df: pd.DataFrame, min_samples: int = 30) -> np.ndarray:
    """Compute vol-targeted position based on expanding per-hour mean return.

    For each bar i:
      - UTC hour = df['datetime'][i].hour
      - expanding mean of past returns for that same UTC hour (uses only j < i)
      - if expanding mean > 0 and count >= min_samples: direction = +1
      - else: flat = 0
    Then vol-target the direction signal.
    """
    dt = pd.to_datetime(df["datetime"])
    c = df["close"].values.astype(float)
    r = al.simple_returns(c)  # r[i] = c[i]/c[i-1] - 1
    n = len(df)

    # For each bar, compute expanding mean return per UTC hour
    hours = dt.dt.hour.values  # 0..23

    # We'll compute causally using cumulative sums per hour
    # hour_cumsum[h], hour_count[h] track sum/count up to bar i-1 for hour h
    hour_cumsum = np.zeros(24, dtype=float)
    hour_count = np.zeros(24, dtype=int)

    direction = np.zeros(n, dtype=float)

    for i in range(n):
        h = hours[i]
        cnt = hour_count[h]
        if cnt >= min_samples:
            mean_r = hour_cumsum[h] / cnt
            direction[i] = 1.0 if mean_r > 0.0 else 0.0
        # else flat (direction[i] = 0)

        # Update with bar i's return (causal: used for bar i+1 onwards)
        hour_cumsum[h] += r[i]
        hour_count[h] += 1

    # Vol-target the binary direction signal
    tgt = al.vol_target(direction, df, target_vol=0.20, vol_win_days=30, leverage_cap=2.0)
    return tgt


if __name__ == "__main__":
    best_rep = None
    best_sharpe = -999.0

    for min_samples in [30, 90]:
        name = f"SEA01-ms{min_samples}"
        rep = al.study_weights(
            name,
            lambda df, ms=min_samples: sea01_target(df, min_samples=ms),
            tfs=("1h",),
        )
        print(al.fmt(rep))
        print("JSON:", al.as_json(rep))

        # Track best by min_asset_full_sharpe
        s = rep["verdict"].get("best_full_sharpe", rep.get("min_asset_full_sharpe", -999))
        if s > best_sharpe:
            best_sharpe = s
            best_rep = rep

    print("\n=== BEST CONFIG ===")
    print(al.fmt(best_rep))
    print("JSON:", al.as_json(best_rep))
