"""SEA07 — Monday Effect (expanding Monday expectancy).

IDEA: On 1d bars, use the expanding-window mean Monday return as a directional signal.
  - Compute an expanding (causal) mean of Monday returns seen so far.
  - If the expanding Monday mean > 0 (continuation): go long (+1) on Mondays, flat otherwise.
  - If the expanding Monday mean < 0 (reversal): go short (-1) on Mondays, flat otherwise.
  - Also try "Friday signal": what happened last Friday may predict the Monday direction.
    We track expanding Friday return mean and use its sign to predict the following Monday.

Signal styles tested (4 configs, 1 TF = 1d, 2 assets = <=8 cells total):
  1. Monday continuation:  long on Mondays when expanding E[Monday ret] > 0, else flat
  2. Monday always long:   always long on Mondays regardless of prior expectancy (baseline)
  3. Friday-to-Monday:     on Monday, go in the direction of last Friday's expanding mean
  4. Monday vol-adjusted:  same as #1 but NOT vol-targeted (raw position, to isolate the signal)

All signals are on 1d only (as required).

Causal guarantee:
  - Expanding Monday mean at bar i uses only Monday returns j < i (causal).
  - Friday-to-Monday: expanding Friday mean uses only Friday returns j < i (causal).
  - lib shifts position by 1 bar automatically (decided at close[i], held during bar i+1).
    WAIT: Monday bar i means we hold on Monday. close[i] of a Monday is ALREADY the end of Monday.
    So to hold DURING Monday, we must decide at close[i-1] (Sunday or prior day).
    Implementation: set target[i] = 0 always; set target[i-1] = signal for Monday i.
    But altlib shifts target[i] -> held at bar i+1. So to be in position DURING bar i:
      we need target[i-1] != 0, which becomes pos[i] = target[i-1].
    Correct approach: for each Monday bar at index i, we set target[i-1] = signal.
    This means at close of Sunday (i-1), we enter; held during bar i (Monday).
    Since 1d bars, Sunday doesn't exist: previous bar is Friday at i-1.
    So: at close of Friday (i-1), we set the position to be held on Monday (i).
    This is the natural way: target[i-1] = signal, lib shifts to pos[i] = target[i-1].

Expanding stats use only data BEFORE the current Monday being evaluated:
  - When setting target[i-1] for Monday i: we have seen all Monday returns up to i-1 (none of
    which are Mondays in typical weeks; so effectively all Mondays before this one).
"""

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 sea07_monday_continuation(df: pd.DataFrame, min_samples: int = 10,
                               use_friday: bool = False,
                               vol_tgt: bool = True) -> np.ndarray:
    """
    Monday-effect signal on daily bars.

    Parameters
    ----------
    min_samples : int
        Minimum Monday (or Friday) samples needed before trusting the expectancy.
    use_friday : bool
        If True, use the expanding mean of Friday returns to predict Monday direction.
        If False, use the expanding mean of Monday returns (continuation/reversal).
    vol_tgt : bool
        Whether to apply vol-targeting to the direction signal.
    """
    dt = pd.to_datetime(df["datetime"])
    c = df["close"].values.astype(float)
    r = al.simple_returns(c)
    n = len(df)

    # Day of week: 0=Monday, 1=Tuesday, ..., 4=Friday, 5=Saturday, 6=Sunday
    dow = dt.dt.dayofweek.values  # 0=Mon, 4=Fri

    # Expanding stats for Monday and Friday returns
    mon_sum = 0.0
    mon_cnt = 0
    fri_sum = 0.0
    fri_cnt = 0

    # target[i]: position decided at close[i], held during bar i+1
    # To be in position DURING Monday bar i, we set target[i-1].
    # target is indexed by bar where decision is made.
    target = np.zeros(n, dtype=float)

    for i in range(1, n):
        # Update stats with bar i-1 (the bar we just closed)
        prev_dow = dow[i - 1]
        prev_r = r[i - 1]

        if prev_dow == 0:  # previous bar was a Monday
            # We accumulate Monday return AFTER using it for the next decision
            # (this bar i is Tuesday or later; the Monday return r[i-1] is now known)
            pass  # will update after computing signal for i

        # Current bar i: what day is it?
        curr_dow = dow[i]

        if curr_dow == 0:
            # Bar i is a Monday. We want to be in position during this bar.
            # Decision must be made at close[i-1] (Friday or whatever preceded it).
            # So we set target[i-1] based on stats available BEFORE bar i.
            if use_friday:
                # Use expanding Friday expectancy to decide Monday direction
                if fri_cnt >= min_samples and fri_sum != 0:
                    fri_mean = fri_sum / fri_cnt
                    direction = 1.0 if fri_mean > 0 else -1.0
                else:
                    direction = 0.0
            else:
                # Use expanding Monday expectancy: continuation or reversal
                if mon_cnt >= min_samples and mon_sum != 0:
                    mon_mean = mon_sum / mon_cnt
                    direction = 1.0 if mon_mean > 0 else -1.0
                else:
                    direction = 0.0

            target[i - 1] = direction

        # Now update the expanding stats with bar i-1's return (after using stats for bar i)
        # This ensures we never use r[i-1] to decide signal for bar i
        if prev_dow == 0:
            mon_sum += prev_r
            mon_cnt += 1
        elif prev_dow == 4:
            fri_sum += prev_r
            fri_cnt += 1

    if vol_tgt:
        return al.vol_target(target, df, target_vol=0.20, vol_win_days=30, leverage_cap=2.0)
    else:
        return target


if __name__ == "__main__":
    results = []

    # Grid: 4 configs on 1d only
    grid = [
        # (name_suffix, min_samples, use_friday, vol_tgt)
        ("mon-cont-ms10-vt",   10, False, True),   # Monday continuation, vol-targeted
        ("mon-cont-ms20-vt",   20, False, True),   # Monday continuation, more samples
        ("fri2mon-ms10-vt",    10, True,  True),   # Friday->Monday, vol-targeted
        ("fri2mon-ms20-vt",    20, True,  True),   # Friday->Monday, more samples
    ]

    # Use study_weights (continuous position style is appropriate for "hold on Mondays")
    for suffix, min_s, use_fri, vt in grid:
        name = f"SEA07-{suffix}"
        rep = al.study_weights(
            name,
            lambda df, ms=min_s, uf=use_fri, v=vt: sea07_monday_continuation(
                df, min_samples=ms, use_friday=uf, vol_tgt=v
            ),
            tfs=("1d",),
        )
        print(al.fmt(rep))
        print("JSON:", al.as_json(rep))
        print()

        best_cell = max(rep["cells"], key=lambda c: c.get("min_asset_holdout_sharpe", -9))
        results.append((
            rep["verdict"].get("best_holdout_sharpe",
                               best_cell.get("min_asset_holdout_sharpe", -9)),
            rep["verdict"].get("best_full_sharpe",
                               best_cell.get("min_asset_full_sharpe", -9)),
            name,
            rep,
        ))

    # Pick best config by hold-out Sharpe (tie-break: full Sharpe)
    results.sort(key=lambda x: (x[0], x[1]), reverse=True)
    best_hold, best_full, best_name, best_rep = results[0]

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