HKUDS/Vibe-Trading · error · ValueError

delay requires n >= 1 (lookahead ban)

Error message

delay requires n >= 1 (lookahead ban)

What it means

Shared helper _delay in alpha_030.py guards against lookahead bias by refusing n < 1: delay of 0 would use the current bar's value, leaking future information into the alpha computation. It raises ValueError before calling df.shift(n). Any caller passing 0 or a negative window triggers it immediately.

Source

Thrown at agent/src/factors/zoo/alpha101/alpha_030.py:62

    'extras_required': [],
    'requires_sector': False,
    'universe': ['equity_us', 'equity_in', 'equity_kr'],
    'frequency': ['1D'],
    'decay_horizon': 5,
    'min_warmup_bars': 20,
    'notes': '',
}


def _rolling_sum(df: pd.DataFrame, n: int) -> pd.DataFrame:
    """Rolling window sum; warmup -> NaN."""
    return df.rolling(window=n, min_periods=n).sum()


def _delay(df: pd.DataFrame, n: int) -> pd.DataFrame:
    """Backward shift by n (lookahead-safe; n>=1 required)."""
    if n < 1:
        raise ValueError("delay requires n >= 1 (lookahead ban)")
    return df.shift(n)


def compute(panel: dict) -> pd.DataFrame:
    """Compute the alpha on the OHLCV+ panel and return a wide DataFrame."""
    close = panel["close"]
    volume = panel["volume"]


    # Helper aliases (local closures keep the file standalone & purity-safe).
    rolling_sum = _rolling_sum
    delay = _delay
    s = np.sign(close - delay(close, 1)) + np.sign(delay(close, 1) - delay(close, 2)) + np.sign(delay(close, 2) - delay(close, 3))
    out = safe_div((1.0 - rank(s)) * rolling_sum(volume, 5), rolling_sum(volume, 20))
    return out

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Open agent/src/factors/zoo/alpha101/alpha_030.py, find every _delay(...) call site and confirm each literal is >= 1
  2. If the formula truly wants the current bar, use the operand directly (e.g. `close`) instead of _delay(close, 0)
  3. If n comes from config, validate/coerce it: n = max(1, int(n)) or reject the config early with a clear message
  4. Re-run the alpha's test to confirm no other windows in the chain were corrupted

Example fix

// before
part = _delay(close, 0) * _decay(rank(close), 5)
// after
part = close * _decay(rank(close), 5)
Defensive patterns

Strategy: validation

Validate before calling

def safe_delay(df, n):
    n = int(n)
    if n < 1:
        raise ValueError(f"delay window must be >= 1, got {n}")
    return df.shift(n)

# or, if 'no lag' is a valid config value:
n = n if n >= 1 else None
out = df if n is None else safe_delay(df, n)

Type guard

def is_valid_delay(n: int) -> bool:
    return isinstance(n, int) and not isinstance(n, bool) and n >= 1

Prevention

When it happens

Trigger: compute(panel) in alpha_030 ultimately calls _delay(close, n) with n <= 0 — e.g. a parameterized lookahead/delay constant misconfigured as 0, or an expression like _delay(df, n - 1) evaluated with n = 1.

Common situations: Copying the WorldQuant Alpha101 formula (#30 uses a delay/decay chain) and translating 'delay(x, 0)' literally; editing window constants; generating alphas from config where the delay field is optional and defaults to 0.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/edac9b43118c2d9b. Report an issue: GitHub.