refactor: class-based alpha factory + month-partitioned data pipeline

Replace the old signal/strategy/backtest modules with a decoupled
data → alpha → combo pipeline (parquet between phases, .pq extension).

Alphas:
- BaseAlpha + @register_alpha factory/plugin registry; one file per
  built-in (reversal, reversal_vol, momentum); external alphas via
  --alpha-module. Alphas are z-scored position weights, not predictors.

Data:
- baostock primary / akshare fallback, treated consistently.
- New --universe all (~5000 A-shares via query_all_stock, filtered).
- login-once batch downloader; empty-string OHLCV coerced to NaN.
- Month-partitioned dataset {output_dir}/{universe}/month=YYYY-MM/*.pq
  with chunked durability flushes; --data-path is the dataset dir.

CLI logs at INFO by default (--log-level) so progress is visible.
Docs (README, CLAUDE.md) updated incl. pipeline diagram and roadmap
TODOs for portfolio construction / backtest / paper trading.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Yuxuan Yan
2026-06-09 14:07:07 +08:00
parent 769cf25daa
commit 1caa63faeb
54 changed files with 1640 additions and 1120 deletions
+7
View File
@@ -0,0 +1,7 @@
"""Built-in alpha library.
Importing this package imports each alpha module, which registers the alpha via
the ``@register_alpha`` decorator. Add a new built-in by dropping a module here
and importing it below.
"""
from pipeline.alpha.library import momentum, reversal, reversal_vol # noqa: F401
+18
View File
@@ -0,0 +1,18 @@
"""Short-horizon momentum alpha."""
import pandas as pd
from pipeline.alpha.base import BaseAlpha
from pipeline.alpha.registry import register_alpha
@register_alpha
class MomentumAlpha(BaseAlpha):
"""Positive trailing return: stocks that rose score high."""
name = "momentum"
def __init__(self, lookback: int = 5):
self.lookback = lookback
def signal(self, close: pd.DataFrame) -> pd.DataFrame:
return close.pct_change(self.lookback)
+18
View File
@@ -0,0 +1,18 @@
"""Short-horizon reversal alpha."""
import pandas as pd
from pipeline.alpha.base import BaseAlpha
from pipeline.alpha.registry import register_alpha
@register_alpha
class ReversalAlpha(BaseAlpha):
"""Negative trailing return: oversold stocks score high."""
name = "reversal"
def __init__(self, lookback: int = 5):
self.lookback = lookback
def signal(self, close: pd.DataFrame) -> pd.DataFrame:
return -close.pct_change(self.lookback)
+26
View File
@@ -0,0 +1,26 @@
"""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)
vol = close.pct_change().rolling(self.vol_window).std()
return reversal / vol