0a6f367fbf
Weights formed from close[t] now earn the t→t+1 return: forward returns are computed on the full market calendar before selecting signal dates, so a sparse signal grid earns the next available return rather than the next signal date, and the final signal date (no forward return) is dropped. Signal pct_change uses fill_method=None so suspended names do not inherit stale prices. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
27 lines
915 B
Python
27 lines
915 B
Python
"""Volatility-scaled short-horizon reversal alpha."""
|
|
import pandas as pd
|
|
|
|
from pipeline.alpha.base import BaseAlpha
|
|
from pipeline.alpha.registry import register_alpha
|
|
|
|
|
|
@register_alpha
|
|
class ReversalVolAlpha(BaseAlpha):
|
|
"""Reversal scaled by trailing volatility.
|
|
|
|
The raw reversal ``-close.pct_change(lookback)`` is divided by the rolling
|
|
standard deviation of daily returns over ``vol_window``, so the score favors
|
|
oversold names whose move is large *relative* to their own volatility.
|
|
"""
|
|
|
|
name = "reversal_vol"
|
|
|
|
def __init__(self, lookback: int = 5, vol_window: int = 20):
|
|
self.lookback = lookback
|
|
self.vol_window = vol_window
|
|
|
|
def signal(self, close: pd.DataFrame) -> pd.DataFrame:
|
|
reversal = -close.pct_change(self.lookback, fill_method=None)
|
|
vol = close.pct_change(fill_method=None).rolling(self.vol_window).std()
|
|
return reversal / vol
|