"""MRV07 — Consecutive-down buy in uptrend.
After N+ consecutive lower closes AND close > SMA100 (uptrend filter),
buy at close[i]; exit after max_bars or on the first green close (close > prev close).

Grid: try (consec_n, max_bars) combinations on 1d.
Total backtests: 3 configs x 2 assets = 6.
"""
import sys
sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/alt")
import altlib as al
import numpy as np


def make_entries_fn(consec_n=3, sma_win=100, max_bars=10):
    """Factory for consecutive-down buy entries.

    Signal: close[i] < close[i-1] < ... < close[i-consec_n+1] (N consecutive lower closes)
            AND close[i] > SMA100 (uptrend filter).
    Entry:  buy at close[i] (filled immediately).
    Exit:   after max_bars bars (hard stop); no TP/SL (green-close exit not encodable
            causally in the entries-list format — green close requires next-bar data).
    """
    def entries_fn(df):
        c = df["close"].values.astype(float)
        n = len(c)
        sma100 = al.sma(c, sma_win)
        entries = []

        for i in range(n):
            # Need at least consec_n prior bars
            if i < consec_n:
                entries.append(None)
                continue

            # Check SMA100 (uptrend)
            if np.isnan(sma100[i]) or c[i] <= sma100[i]:
                entries.append(None)
                continue

            # Check N consecutive lower closes
            consecutive_down = True
            for k in range(consec_n):
                if k == 0:
                    # close[i] < close[i-1]
                    if c[i] >= c[i-1]:
                        consecutive_down = False
                        break
                else:
                    # close[i-k] < close[i-k-1]
                    if c[i-k] >= c[i-k-1]:
                        consecutive_down = False
                        break

            if consecutive_down:
                entries.append({
                    "dir": 1,
                    "tp": None,
                    "sl": None,
                    "max_bars": max_bars,
                })
            else:
                entries.append(None)

        return entries
    return entries_fn


# Grid: 3 configs (consec_n, max_bars)
# Hypothesis: after 3 consecutive dips in uptrend, expect mean-reversion bounce
CONFIGS = [
    dict(consec_n=3, max_bars=5,  label="N3_mb5"),
    dict(consec_n=3, max_bars=10, label="N3_mb10"),
    dict(consec_n=4, max_bars=5,  label="N4_mb5"),
]

best_rep = None
best_hold = -999.0
best_label = None

for cfg in CONFIGS:
    label = cfg["label"]
    fn = make_entries_fn(consec_n=cfg["consec_n"], max_bars=cfg["max_bars"])
    rep = al.study_signals(f"MRV07-{label}", fn, tfs=("1d",))
    hold = rep["verdict"].get("best_holdout_sharpe", -999)
    full = rep["verdict"].get("best_full_sharpe", -999)
    print(f"\n[{label}] full={full:.3f} hold={hold:.3f} grade={rep['verdict']['grade']}")
    if hold > best_hold:
        best_hold = hold
        best_rep = rep
        best_label = label

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