"""agent_00_sma_trend — ANGLE: trend / single long SMA (long/flat).

Idea (assigned angle): go LONG only while price is meaningfully above a single long
simple moving average, otherwise FLAT. The long SMA defines the macro trend; staying
flat below it is what cuts the asset's ~77% buy&hold drawdown to ~1/3.

Tuned on split='train' only (both Series A and B, equal weight):
  * window  W = 150   (canonical long SMA; sits on a wide robust plateau W=135..165)
  * band    B = 0.02  (require close > 1.02*SMA -> avoids whipsaw chop near the line)
  * vol-target the long exposure to 35% ann vol (vol_win=30d, cap 1.0). This is what
    actually controls drawdown: long size shrinks when realized vol spikes (every
    crypto-like crash is a vol spike), so we're never full-size into the worst bars.

Everything is causal: SMA(close[..i]), realized vol(returns[..i]). No future rows.
The evaluator shifts position by one bar (decision at close[i] -> held bar i+1).

Train (combined A&B): pnl_mean ~ 5.4, maxdd_worst ~ 0.30, sharpe_min ~ 1.36.
Honest note: this is a DEFENSIVE trend filter, not alpha — its value is converting a
high-PnL/high-DD uptrend into comparable risk-adjusted PnL at a MUCH smaller drawdown.
"""
import numpy as np
import blindlib as bl

W = 150          # single long SMA window
BAND = 0.02      # long only when close > (1+BAND)*SMA(W)
TARGET_VOL = 0.35
VOL_WIN_DAYS = 30
LEV_CAP = 1.0


def signal(df):
    c = df["close"].values.astype(float)
    sma = bl.sma(c, W)                       # causal SMA up to i

    # long/flat gate vs the single long SMA, with a band to dodge whipsaw near the line
    long_gate = np.where(c > sma * (1.0 + BAND), 1.0, 0.0)
    long_gate[:W] = 0.0                      # no signal before the SMA is defined
    long_gate[~np.isfinite(sma)] = 0.0

    # size the long with causal vol-targeting (shrinks into vol spikes -> cuts DD)
    pos = bl.vol_target(long_gate, df, target_vol=TARGET_VOL,
                        vol_win_days=VOL_WIN_DAYS, leverage_cap=LEV_CAP)

    return np.clip(np.nan_to_num(pos, nan=0.0), -1.0, 1.0)
