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:
@@ -0,0 +1,47 @@
|
||||
"""CLI for alpha combination."""
|
||||
|
||||
import os
|
||||
import click
|
||||
|
||||
from pipeline.combo.combine import combine_alphas, COMBO_METHODS
|
||||
|
||||
|
||||
@click.group(name="combo")
|
||||
def combo():
|
||||
"""Combine multiple alphas into a single combined weight."""
|
||||
|
||||
|
||||
@combo.command("combine")
|
||||
@click.option(
|
||||
"--alpha-paths", required=True,
|
||||
help="Comma-separated paths to alpha parquet files",
|
||||
)
|
||||
@click.option("--combo-name", required=True, help="Name for this combo")
|
||||
@click.option(
|
||||
"--method", default="equal_weight",
|
||||
type=click.Choice(list(COMBO_METHODS.keys())),
|
||||
help="Combination method",
|
||||
)
|
||||
@click.option("--output-dir", default="combos", help="Directory to save combo parquet")
|
||||
def combine(alpha_paths, combo_name, method, output_dir):
|
||||
"""Combine multiple alphas and save as parquet."""
|
||||
paths = [p.strip() for p in alpha_paths.split(",") if p.strip()]
|
||||
if len(paths) < 2:
|
||||
click.echo("Error: --alpha-paths requires at least 2 comma-separated paths", err=True)
|
||||
return
|
||||
|
||||
result = combine_alphas(
|
||||
alpha_paths=paths,
|
||||
combo_name=combo_name,
|
||||
method=method,
|
||||
)
|
||||
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
out_path = f"{output_dir}/{combo_name}.pq"
|
||||
result.to_parquet(out_path, index=False)
|
||||
click.echo(f"Saved combo: {out_path} ({len(result):,} rows)")
|
||||
click.echo(
|
||||
f"Weight stats — min: {result['weight'].min():.4f}, "
|
||||
f"max: {result['weight'].max():.4f}, "
|
||||
f"mean: {result['weight'].mean():.4f}"
|
||||
)
|
||||
Reference in New Issue
Block a user