diff --git a/README.md b/README.md index 44b264b..aace217 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,31 @@ ln -sf "$(pwd)/tools/pqcat.py" ~/.local/bin/pqcat # ~/.local/bin must be on PA pqcat alphas/reversal_5d.pq --info ``` +### `alphaview` — alpha weight(s) alongside bar data for one symbol + +Join the bar dataset and one or more alpha parquet files on `(symbol, date)` and +print them side by side, so you can eyeball how a weight moves against price / +volume over a time range. + +| Option | Default | Description | +| --- | --- | --- | +| `--data-path` | (required) | Bar dataset dir or parquet file | +| `--alpha-path` | (required) | Comma-separated alpha parquet path(s) — each `alpha_name` becomes a column | +| `--symbol` | (required) | Symbol id, e.g. `sh600000` | +| `--start-date` | — | `YYYY-MM-DD` (inclusive) | +| `--end-date` | — | `YYYY-MM-DD` (inclusive) | +| `-c, --columns` | `close,volume` | Comma-separated bar columns to show | + +```bash +python3 cli.py alphaview \ + --data-path data/daily_bars/csi500 \ + --alpha-path alphas/reversal_5d.pq,alphas/momentum_5d.pq \ + --symbol sh600000 --start-date 2024-01-01 --end-date 2024-03-31 \ + -c close,volume,vwap +``` + +Also standalone like `pqcat` — `ln -sf "$(pwd)/tools/alphaview.py" ~/.local/bin/alphaview`. + ## Alphas: the factory / plugin interface An **alpha** is a class that maps a wide close matrix (date index × `symbol_id` @@ -272,6 +297,7 @@ directory yields an extra `month` (`YYYY-MM`) partition column on top of - `pipeline/common/schema.py` — parquet column contracts - `data/downloader.py`, `data/universe.py` — baostock/akshare download + constituents - `tools/pqcat.py` — standalone parquet inspector (`pqcat`), also wired as `cli.py pqcat` +- `tools/alphaview.py` — standalone alpha-vs-bar viewer (`alphaview`), also wired as `cli.py alphaview` - `examples/alphas/` — example external alpha(s) ## Roadmap (not yet implemented) diff --git a/cli.py b/cli.py index c23eb8c..0eae281 100644 --- a/cli.py +++ b/cli.py @@ -15,6 +15,7 @@ from pipeline.data.cli import data from pipeline.alpha.cli import alpha from pipeline.combo.cli import combo from tools.pqcat import pqcat +from tools.alphaview import alphaview @click.group() @@ -39,6 +40,7 @@ cli.add_command(data) cli.add_command(alpha) cli.add_command(combo) cli.add_command(pqcat) +cli.add_command(alphaview) if __name__ == "__main__": diff --git a/tools/alphaview.py b/tools/alphaview.py new file mode 100755 index 0000000..2d90ecd --- /dev/null +++ b/tools/alphaview.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +"""Show alpha weight(s) alongside daily bar data for a single symbol. + +Joins the bar dataset and one or more alpha parquet files on (symbol, date) so +you can eyeball how a weight moves against price/volume over a time range. +""" + +import click +import pandas as pd + + +def _filter_dates(df, start_date, end_date): + if start_date: + df = df[df["date"] >= pd.Timestamp(start_date)] + if end_date: + df = df[df["date"] <= pd.Timestamp(end_date)] + return df + + +@click.command(name="alphaview") +@click.option("--data-path", required=True, type=click.Path(exists=True), help="Bar dataset dir or parquet file") +@click.option("--alpha-path", "alpha_paths", required=True, help="Comma-separated alpha parquet path(s)") +@click.option("--symbol", required=True, help="Symbol id, e.g. sh600000") +@click.option("--start-date", default=None, help="YYYY-MM-DD (inclusive)") +@click.option("--end-date", default=None, help="YYYY-MM-DD (inclusive)") +@click.option("-c", "--columns", default="close,volume", help="Comma-separated bar columns to show") +def alphaview(data_path, alpha_paths, symbol, start_date, end_date, columns): + """Print bar columns + alpha weight(s) for SYMBOL over a date range.""" + bar_cols = [c.strip() for c in columns.split(",") if c.strip()] + + bars = pd.read_parquet(data_path) + bars = bars[bars["symbol_id"] == symbol] + if bars.empty: + raise click.ClickException(f"Symbol {symbol!r} not found in {data_path}") + missing = [c for c in bar_cols if c not in bars.columns] + if missing: + raise click.ClickException(f"Bar columns not found: {', '.join(missing)}") + bars = _filter_dates(bars, start_date, end_date) + out = bars[["date", *bar_cols]].set_index("date").sort_index() + + for path in (p.strip() for p in alpha_paths.split(",") if p.strip()): + adf = pd.read_parquet(path) + adf = adf[adf["symbol_id"] == symbol] + adf = _filter_dates(adf, start_date, end_date) + # One alpha file may carry multiple alpha_name values; spread to columns. + wide = adf.pivot_table(index="date", columns="alpha_name", values="weight") + out = out.join(wide, how="left") + + if out.empty: + raise click.ClickException("No rows in the requested date range") + + with pd.option_context("display.max_rows", None, "display.max_columns", None, "display.width", None): + click.echo(f"symbol: {symbol} rows: {len(out):,}") + click.echo(out.to_string()) + + +if __name__ == "__main__": + alphaview()