Metadata-Version: 2.4
Name: alpha101-pipeline
Version: 0.5.0
Summary: DuckDB-powered factor research pipeline: formula engine, factor store, layered backtest
Project-URL: Repository, https://github.com/goldenquant/alpha101
Author: goldenquant
License-Expression: MIT
License-File: LICENSE
Keywords: alpha101,backtest,duckdb,factor,parquet,quant
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial :: Investment
Requires-Python: >=3.9
Requires-Dist: duckdb>=1.0
Requires-Dist: pyarrow>=14.0
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: pytest-benchmark>=4; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: twine; extra == 'dev'
Provides-Extra: plot
Requires-Dist: matplotlib>=3.7; extra == 'plot'
Description-Content-Type: text/markdown

# alpha101-pipeline

基于 DuckDB 的 A 股日内因子研究全流程工具链：公式引擎 → 因子存储 → 分层回测 → 可视化。
内置 **WorldQuant 101 alpha 公式集**，支持 IC 衰减 / 换手率诊断、组合级交易成本 + 净值回测。

## 安装

```bash
pip install alpha101-pipeline
# 带绘图功能：
pip install "alpha101-pipeline[plot]"
# 带测试 / 开发：
pip install "alpha101-pipeline[dev]"
```

## 命令一览

| 命令 | 作用 |
|------|------|
| `alpha101-reorder` | 面板排序 + TIMESTAMP 转换 + 多 row group（谓词下推加速） |
| `alpha101-compute` | 计算 WorldQuant 101 alpha（一条命令算全部/指定 alpha） |
| `alpha101-screen` | 批量筛选：计算 → IC 衰减 + 换手率 → 按 |ICIR| 排名 |
| `alpha101-store` | 因子库（计算并持久化因子） |
| `alpha101-backtest` | 分层回测（IC / 单调性 / 多空，含交易成本与净值） |
| `alpha101-plot` | 分层图 + 分组柱状图 + IC 衰减图 |

## 快速上手

```bash
# 1. 重排面板数据（按 datetime, code 排序，TIMESTAMP 类型，多行组）
alpha101-reorder --input raw.parquet --output panel_sorted.parquet

# 2. 计算因子存入 store
alpha101-store add \
  --source panel_sorted.parquet \
  --store data/factors \
  --formula mom_12='ts_mean(delta(close,1),12)' \
  --formula rev_12='ts_mean(delta(close,1),12) * -1'

# 3. 回测全部因子
alpha101-backtest \
  --store data/factors \
  --out-dir output/backtest \
  --forward 12 --groups 10

# 4. 绘制分层图
alpha101-plot batch \
  --returns-root output/backtest/series \
  --reports-dir output/backtest/reports \
  --out-dir output/backtest/plots
```

### 前向收益模式

日内回测支持两种前向收益计算方式：

```bash
# 默认：前向收益不跨日（避免隔夜跳空污染）
alpha101-backtest --store data/factors --out-dir output --forward 12 --groups 10

# 跨日 close-to-close 收益（适合长周期因子）
alpha101-backtest --store data/factors --out-dir output --forward 60 --groups 10 \
  --no-intraday-only
```

| 参数 | 说明 |
|------|------|
| `--intraday-only` / `--no-intraday-only` | 默认开启。限制前向收益在同一交易日内。`--no-intraday-only` 允许跨日 close-to-close 收益 |
| `--bars-per-day` | 每日 K 线数（默认 48，即 5 分钟线）。`--intraday-only` 开启时，`--forward` 必须 < `--bars-per-day`，否则报错 |

## WorldQuant 101 alpha：计算 / 筛选 / 成本回测

```bash
# 1. 一键计算 101 alpha（自动只算面板列支持的；含 vwap/cap/industry 可算更多）
alpha101-compute --input panel_sorted.parquet --output alphas.parquet

# 只算指定 alpha；--rank-pct 用 [0,1] 分位 rank（WQ 原始量级，默认是 0..n-1 序号）
alpha101-compute --input panel_sorted.parquet --output alphas.parquet \
  --alphas 1,7,54,101 --rank-pct

# 2. 批量筛选：IC 衰减（多档 horizon）+ 换手率，按 |ICIR| 排名 -> screen.csv / screen.md
alpha101-screen --input panel_sorted.parquet --out-dir output/screen --horizons 1,5,10,20

# 3. 带交易成本的回测（cost-bps = 每单位换手率的单边成本，基点）
alpha101-backtest --store data/factors --out-dir output --forward 12 --groups 10 \
  --cost-bps 5
```

`alpha101-backtest` 在启用 series 时会写出 `<factor>_nav.parquet`（含 `ls_gross / ls_net / nav_gross / nav_net`），可由 `alpha101-plot` 绘制；报告 JSON 含 `turnover`、`long_short`（毛）与 `long_short_net`（扣成本）。

## 低内存 / 受限设备优化

引擎全程 out-of-core（`read_parquet` + `COPY` 流式，DuckDB 超出 RAM 自动 spill 到临时盘），并对低内存机器提供一组可组合的开关。内存紧张时一行搞定：

```bash
alpha101-compute --input panel_sorted.parquet --output alphas.parquet --low-memory
```

`--low-memory` 是聚合预设，等价于 `--dtype float32 --chunked always`（engine 入口再加 `--exclude-input-columns`）；显式传的参数仍优先。也可按需单独开启：

| 开关 | 作用 | 取舍 |
|---|---|---|
| `--dtype float32` | 因子列用 32 位浮点 | 纯因子文件磁盘 -33%、读内存 ~-50%；精度 ~1e-7（截面 rank 等序数不受影响；超范围值如 signed_power 爆炸会钳位到 ±3.4e38） |
| `--chunked always` | 强制按 stock_id 分块 | 峰值内存最低；略慢。`auto` 在低内存(<1GB budget)时本就更早分块 |
| `--exclude-input-columns` | 输出剔除 OHLCV 等原始列，只留 date/id + 因子 | standalone 输出体积 -60%+（`alpha101-compute` 的合并产物本就只含因子列，无需此开关） |
| `--temp-dir <大盘>` | 指定 DuckDB spill 目录 | 不传则自动选可用空间最大的挂载点，避免小系统盘写满 |
| `--memory-limit 2GB --threads 1` | 压低 DuckDB 上限与并发 | 上限越低分块越细；单线程峰值最低 |

> 注：`--memory-limit` 设得过低（低于 DuckDB 运行下限）反而会 OOM——给到几百 MB 以上让其有 spill 余地。临时 spill 目录在进程退出时自动清理。

## Python API

```python
from alpha101_pipeline import FactorStore, run_intraday_multi
from pathlib import Path

# 计算因子
store = FactorStore(Path("data/factors"))
store.add_factors(
    [("mom_12", "ts_mean(delta(close,1),12)")],
    source=Path("panel_sorted.parquet"),
)

# 回测
reports = run_intraday_multi(
    store.store_dir,
    ["mom_12"],
    source_panel=Path("panel_sorted.parquet"),
    factor_files={"mom_12": store.factor_path("mom_12")},
)

# 跨日前向收益（默认 intraday_only=True 只计算日内收益）
reports = run_intraday_multi(
    store.store_dir,
    ["mom_12"],
    forward_period=60,          # 跨日（超过 bars_per_day=48）
    bars_per_day=48,
    intraday_only=False,        # 允许 close-to-close 跨日收益
    source_panel=Path("panel_sorted.parquet"),
    factor_files={"mom_12": store.factor_path("mom_12")},
)
```

```python
# 计算 101 alpha + 批量筛选（IC 衰减 / 换手率 / 排名）
from alpha101_pipeline.formulas import compute_alphas
from alpha101_pipeline.screen import screen_alphas
from alpha101_pipeline.backtest import compute_ic_decay, compute_turnover

compute_alphas(Path("panel_sorted.parquet"), Path("alphas.parquet"))          # 自动选择可算的 alpha
df = screen_alphas(Path("panel_sorted.parquet"), Path("output/screen"),       # -> screen.csv
                   horizons=[1, 5, 10, 20])

# 单因子诊断：IC 衰减曲线 + 换手率
decay = compute_ic_decay(panel, None, "mom_12", "datetime", "code", None, [1,5,10], "close")
turnover = compute_turnover(panel, None, "mom_12", "datetime", "code", None)
```

## 支持的函数

### 时序窗口函数（PARTITION BY 股票 ORDER BY 日期）

| 函数 | 说明 |
|------|------|
| `delay(x, d)` | 取 d 期前的值 |
| `delta(x, d)` | 与 d 期前的差值 |
| `ts_sum(x, d)` / `sum(x, d)` | 滚动求和 |
| `ts_mean(x, d)` / `mean(x, d)` / `sma(x, d)` | 滚动均值 |
| `ts_min(x, d)` / `min(x, d)` | 滚动最小值 |
| `ts_max(x, d)` / `max(x, d)` | 滚动最大值 |
| `ts_stddev(x, d)` / `stddev(x, d)` | 滚动标准差 |
| `ts_variance(x, d)` / `variance(x, d)` | 滚动方差 |
| `ts_count(x, d)` | 滚动非空计数 |
| `ts_count_not_nan(x, d)` | 滚动非 NaN 计数 |
| `ts_zscore(x, d)` | 滚动 Z-Score |
| `ts_pct_change(x, d)` | 滚动百分比变化 |
| `product(x, d)` | 滚动乘积 |
| `decay_linear(x, d)` | 线性衰减加权和 |
| `ts_corr(x, y, d)` / `correlation(x, y, d)` | 滚动皮尔逊相关 |
| `ts_covariance(x, y, d)` / `covariance(x, y, d)` | 滚动协方差 |
| `bollinger_upper(x, d)` | 布林带上轨 |
| `bollinger_lower(x, d)` | 布林带下轨 |
| `ts_median(x, d)` / `median(x, d)` | 滚动中位数 |
| `ts_quantile(x, d, q)` / `quantile(x, d, q)` | 滚动分位数 |
| `wma(x, d)` | 加权移动平均 |
| `ts_skew(x, d)` / `skew(x, d)` | 滚动偏度 |
| `ts_kurt(x, d)` / `kurt(x, d)` | 滚动峰度 |
| `ts_mad(x, d)` / `mad(x, d)` | 滚动平均绝对偏差 |
| `ts_rank(x, d)` | 滚动时序排名 |
| `slope(x, y, d)` / `regr_slope(x, y, d)` | 滚动回归斜率 |
| `rsquare(x, y, d)` / `regr_r2(x, y, d)` | 滚动回归 R² |
| `resi(x, y, d)` / `regr_resid(x, y, d)` | 滚动回归残差 |
| `idxmax(x, d)` / `ts_argmax(x, d)` | 滚动窗口最大值位置 |
| `idxmin(x, d)` / `ts_argmin(x, d)` | 滚动窗口最小值位置 |

### 截面函数（PARTITION BY 日期）

| 函数 | 说明 |
|------|------|
| `rank(x)` | 截面序号排名（0..n-1，0 = 最小；并列按 id 破开） |
| `rank_pct(x)` | 截面分位排名 ∈ [0,1]（1.0 = 最大；对应 WorldQuant 原始 rank 语义） |
| `scale(x)` | 截面绝对值归一化（Σ|x| = 1） |
| `zscore(x)` | 截面 Z-Score |
| `demean(x)` | 截面去均值 |
| `winsorize(x)` / `winsorize(x, lo, hi)` | 截面缩尾：截断到 `[p_lo, p_hi]`（默认 `[p1, p99]`），抑制长尾（如 `signed_power` 爆炸） |
| `neutralize(x, y)` | 截面 OLS 回归残差：x 对 y 的回归残差（e.g. `neutralize(factor, log(cap))`） |
| `mktneutralize(x, cap)` | 市值中性化快捷函数：等价于 `neutralize(x, log(cap))` |

### 分组函数（PARTITION BY 日期 + 分组列）

| 函数 | 说明 |
|------|------|
| `group_mean(x, group)` | 分组均值 |
| `group_rank(x, group)` | 组内排名 |
| `group_neutralize(x, group)` / `indneutralize(x, group)` | 分组中性化 |
| `group_zscore(x, group)` | 组内 Z-Score |

### 数学函数（标量）

| 函数 | 说明 |
|------|------|
| `abs(x)` | 绝对值 |
| `log(x)` | 自然对数 |
| `sqrt(x)` | 平方根 |
| `sign(x)` | 符号函数 |
| `exp(x)` | 指数 |
| `round(x)` | 四舍五入 |
| `floor(x)` | 向下取整 |
| `ceil(x)` | 向上取整 |
| `sin(x)` | 正弦 |
| `cos(x)` | 余弦 |
| `tan(x)` | 正切 |
| `signed_power(x, n)` / `power(x, n)` / `pow(x, n)` | 幂运算（保留符号） |
| `min(x, y)` | 两值取小 |
| `max(x, y)` | 两值取大 |

### 工具函数

| 函数 | 说明 |
|------|------|
| `if(cond, then, else)` | 条件选择 |
| `fillna(x, val)` | 空值填充 |
| `clip(x, lo, hi)` | 截断到 [lo, hi] |
| `is_finite(x)` | 是否有限值 |

### 不支持的函数（递归/状态型，无法用纯 SQL 表达）

`ema`, `rsi`, `macd`, `atr`, `roc`, `obv`, `cci`, `mfi`

## 运算符

| 优先级 | 运算符 | 说明 |
|--------|--------|------|
| 1（最高） | `()` | 括号 |
| 2 | `^` | 幂运算（右结合：`2^3^2 = 2^9 = 512`） |
| 3 | `-x` | 一元负号 |
| 4 | `*` `/` | 乘除（`/` 除零返回 NULL） |
| 5 | `+` `-` | 加减 |
| 6 | `>` `<` `>=` `<=` `==` `!=` | 比较（返回 **1.0 / 0.0**，可参与算术，如 `15 * (a < b)`） |
| 7 | `&&` | 逻辑与（返回 1.0 / 0.0） |
| 8 | `\|\|` | 逻辑或（返回 1.0 / 0.0） |
| 9（最低） | `? :` | 三元条件（`close > 100 ? 1 : 0`） |

> **小数窗口**：时序函数的窗口 `d` 接受小数（Kakushadze 调优常数，如 `3.43976`），按四舍五入取整。
> `log(≤0)`、`sqrt(<0)` 返回 NULL（不产生 inf/NaN 污染）。

## 回测输出指标

| 指标 | 说明 |
|------|------|
| IC 均值 / ICIR | 每日 Spearman 秩相关 IC 的均值和信息比率 |
| MS（单调性得分） | 相邻组收益方向一致比例（0~1，1.0 = 完美单调） |
| Spearman | 组号 vs 年化收益的秩相关（-1~+1） |
| 多空夏普 / 年化 | 最高组减最低组的多空组合绩效（毛） |
| 多空（扣成本） | `long_short_net`：按 `--cost-bps` 扣除交易成本后的多空绩效 |
| 换手率 | `turnover` ∈ [0,1]：截面分位排名的日均变动（可交易性/成本代理） |
| 净值曲线 | `<factor>_nav.parquet`：`nav_gross` / `nav_net` 累计净值 |
| 分组年化收益 | 每个分层组的年化收益率 |
| IC 衰减 | `compute_ic_decay` / `alpha101-screen`：多档 horizon 的 IC 曲线 |

## 许可证

MIT
