# factorlab-lib **Repository Path**: mkee/factorlab-lib ## Basic Information - **Project Name**: factorlab-lib - **Description**: No description available - **Primary Language**: Python - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-11-01 - **Last Updated**: 2026-11-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # factorlab — 独立本地因子库 / Standalone Local Factor Library A self-contained Python package that evaluates quantitative factor formulas locally over pandas panels. **No PandaAI platform, no skill, no login, no network.** Drop it into any project with a `sys.path` entry and go. 一个完全独立的因子库:在本地用 pandas 面板求值因子公式。不依赖 PandaAI 平台、 skill、登录或网络,任何项目加一行 `sys.path` 即可调用。 --- ## 1. 安装 / Install 无需安装,加入 `sys.path` 即可(或 `pip install -e .`): ```python import sys sys.path.insert(0, r"E:\quant\factorlab-lib") from factorlab import FactorLab, load_panel ``` 依赖:Python 3.10+、`pandas`、`numpy`。仅此而已。 --- ## 2. 一分钟上手 / One-minute tour ```python from factorlab import FactorLab, load_panel lab = FactorLab() # 自动加载内置的 364 个因子定义 panel = load_panel(r"E:\quant\xbx_data\data\stock-trading-data", start="2024-01-01", end="2025-12-31", symbols=my_400_tickers) # 不传 symbols 即全市场 # (a) 直接用注册表里的因子名 v = lab.compute("F-SIZE", panel) # LOG(MARKET_CAP) -> DataFrame(日期 x 股票) # (b) 直接写公式,不需要注册表条目 v = lab.compute("RANK(-LOG(MARKET_CAP)) * RANK(ROC(CLOSE,20))", panel) # (c) 选股:取截面排名前 10 top = lab.screen("F-MOM20", panel, top_n=10, ascending=True) ``` 完整可运行示例见 `E:\quant\ext_demo\demo.py`(放在因子库目录之外,证明独立性)。 --- ## 3. 数据面板 / Panel `load_panel()` 读取逐股票 CSV(GBK 编码、首行为说明行),堆叠成 `{字段: DataFrame(日期 x 股票)}`。 | 字段 | 含义 | | --- | --- | | `OPEN` `HIGH` `LOW` `CLOSE` | 开高低收 | | `PREV_CLOSE` | 前收盘 | | `VOLUME` `AMOUNT` | 成交量(股)、成交额 | | `FLOAT_CAP` `MARKET_CAP` | 流通市值、总市值 | | `TURNOVER` | 由 `AMOUNT / FLOAT_CAP` 近似得出 | ```python panel = load_panel(data_dir, start=None, end=None, symbols=None, min_dates=60, limit=None, progress=False) ``` - `symbols` 不传则加载目录下全部文件(约 5.4k 只,约 6 分钟)。 **传入股票列表可把加载时间降到秒级。** - `panel.restrict([...])` 事后裁剪股票;`panel.symbols` / `panel.dates` / `panel.fields()` 查看。 你也可以不用 `load_panel`,直接传任何 `{字段: DataFrame}` 字典——只要 index 是日期、columns 是股票代码,与 `panel` 对齐即可。 --- ## 4. 求值 / Computing ```python lab.compute(name_or_formula, panel, **extra_fields) -> DataFrame ``` - 第一个参数是**注册表里的因子名**,或者**一段公式字符串**。 - 需要基本面字段(PB、净利润等)时,用关键字传入,列名须与 panel 对齐: ```python lab.compute("F-PB", panel, PB_RATIO_TTM=my_pb_dataframe) ``` `compute_many()` 批量计算,失败的返回异常对象而不是中断: ```python res = lab.compute_many(["F-SIZE", "F-MOM20", "R6"], panel) for name, val in res.items(): if isinstance(val, Exception): print(name, "failed:", val) else: ... ``` ### 4.1 缺字段时报错清晰 / Clear missing-field errors 公式引用了你没提供的字段时,抛 `MissingFieldError` 并**列出全部缺项与可用项**: ``` formula needs fields not in the panel: PB_RATIO_TTM, NET_PROFIT available: CLOSE, OPEN, MARKET_CAP, ... ``` `lab.missing_for(formula)` 只查询不求值,先看需要什么数据再决定是否计算。 --- ## 5. 公式语法 / Formula syntax 支持 PandaAI 公式模式的完整文法,优先级由低到高: ``` expr := ternary ternary := or_expr ('?' expr ':' expr)? or_expr := and_expr (('||' | '|') and_expr)* and_expr:= cmp (('&&' | '&') cmp)* cmp := add (('<' | '>' | '<=' | '>=' | '=' | '==' | '!=') add)* add := mul (('+' | '-') mul)* mul := unary (('*' | '/') unary)* unary := '-' unary | '!' unary | power power := atom ('^' unary)? # 右结合 atom := NUMBER | IDENT | IDENT '(' args ')' | '(' expr ')' ``` - 字段名**大小写不敏感**:`MARKET_CAP` 与 `market_cap` 等价。 - `=` 与 `==` 都是相等比较。 - `^` 支持**序列指数**(Alpha101 变参幂因子所需),逐元素求值。 - `MEAN(A,B)` 是**两个序列的逐元素均值**,不是滚动均值——滚动用 `MA(X,N)`。 ### 5.1 `lab.check(formula)` 只解析、不求值,返回字段、算子、未知算子、所需额外字段: ```python info = lab.check("MYFACTOR(CLOSE, 20)") # {'fields': ['CLOSE'], 'operators': ['MYFACTOR'], # 'unknown_operators': ['MYFACTOR'], 'needs_beyond_ohlcv': [], 'computable': False} ``` --- ## 6. 算子 / Operators 105 个算子,`FactorLab.operators()` 或 `factorlab.OPS` 查看全部。按类别: | 类别 | 算子 | | --- | --- | | 截面 | `RANK` `SCALE` `ZSCORE` | | 逐元素 | `ABS` `LOG` `LOGABS` `SIGN` `AS_FLOAT` `RD` `POWER` `SIGNEDPOWER` `SIN` `COS` `TAN` `ARCSIN` `ARCCOS` `ARCTAN` `MAX` `MIN` `MEAN` `EQUAL` `IF` `VALUEWHEN` | | 滞后/差分 | `DELAY` `REF` `DELTA` `DIFF` `ROC` `RETURNS` `PCT_CHANGE` `FUTURE_RETURNS` ⚠️ | | 滚动统计 | `MA` `TS_MEAN` `SUM` `SUMAC` `STD` `STDDEV` `VAR` `TS_MAX` `HHV` `TS_MIN` `LLV` `TS_MIDDLE` `TS_MEDIAN` `TS_MAD` `AVEDEV` `TS_ZSCORE` `TS_SKEW` `TS_KURT` `PRODUCT` | | 滚动特例 | `TS_RANK` `TS_ARGMAX` `TS_ARGMIN` `HHVBARS` `LLVBARS` `DECAYLINEAR` `WMA` `SLOPE` `INTERCEPT` `ANGLE` `FORCAST` `FORECAST` `EMA` `DMA` `SMA` `COUNT` `EVERY` `EXIST` `BARSSINCEN` `BARSLAST` `BARSLASTCOUNT` `CONST` `SHARPE` `SUM_ABS_PRICE_CHANGE` `MEAN_ABS_PRICE_CHANGE` `ADV` | | 双序列 | `CORR` `CORRELATION` `COV` `COVARIANCE` `TS_REGRESSION` `SUMIF` `CROSS` `LONGCROSS` `LAST` | | 技术指标 | `RSI` `WR` `BOLL_UPPER` `BOLL_MID` `BOLL_LOWER` `BOLL_WIDTH` `BIAS` `CCI` `PSY` `PSYMA` `ATR` `MACD_DIF` `MACD_DEA` `MACD` `KDJ_K` `KDJ_D` `KDJ_J` `DEMA` `TEMA` `OBV` | ⚠️ `FUTURE_RETURNS` 是**未来函数**,回测无意义,仅供研究对照。 **窗口参数取整**:Alpha101 第 58–101 号因子的窗口是拟合出的小数 (如 `DECAYLINEAR(x, 7.89291)`),本库四舍五入到最近整数——这是通行做法。 **时间序列算子**沿日期轴(`axis=0`)滚动,**截面算子**沿股票轴(`axis=1`) 求值。滚动窗口默认 `min_periods=N`,所以序列头部是 NaN 而非短窗估计值。 --- ## 7. 注册表 / Registry ``` Registry: 364 factors (library=79, alpha101=101, alpha191=184); 313 computable from OHLCV+cap locally ``` 三个来源,全部打包在 `factorlab/data/` 内,**不读取外部目录**: | 来源 | 数量 | 说明 | | --- | --- | --- | | `library` | 79 | 自建因子库(`library.csv`,含窗口/方向/状态/净值超额) | | `alpha101` | 101 | WorldQuant 101 Formulaic Alphas,已译为公式模式 | | `alpha191` | 184 | 国泰君安 191(JoinQuant 镜像;7 条无公式已剔除) | ```python lab.get("R6") # -> FactorDef(name, formula, direction, window, ...) lab.list(source="alpha101", computable=True) lab.list(contains="MOM") # 按名字或公式模糊匹配 lab.registry.names(source="library") ``` `FactorDef` 字段:`name` `formula` `source` `direction` `ftype` `window` `window_start` `window_end` `status` `factor_id` `net_excess` `a_score` `note` `fields`,以及派生属性 `uses_fundamentals` / `computable_locally`。 ### 7.1 本地可算性 / What "computable" means `computable_locally` 为真,表示公式只用到 8 个基础价量字段 (`OPEN HIGH LOW CLOSE VOLUME AMOUNT TURNOVER MARKET_CAP`)。 用到 PB、净利润、研发费用等基本面字段的因子需要你自己提供数据。 --- ## 8. 已知边界 / Known limits - **`A191_010` 求值失败**:JoinQuant 页面括号错位写成 `RANK(x, 5)`,而 `RANK` 只收 1 个参数。该条在 `note` 字段已标注,属上游页面勘误,不是本库缺陷。 - **Alpha101 行业中性化的 18 条**:平台与本库都没有行业分类字段,公式是剥掉 中性化后的内层表达式,会残留行业暴露。 - **Alpha191 的 JQ 页面有约 34 处错字/断词**,已逐条修复并记录在 `note`; 引用前建议回核国泰君安原报告。 - **`TS_SKEW` / `TS_KURT`** 使用 pandas 的样本偏度/峰度估计,窗口小于 4 时返回 NaN。 - **`BOLL_WIDTH`** 按平台文档实现为 `(上轨-下轨)/中轨` 的相对值;若你需要绝对带宽, 用 `BOLL_UPPER(x,n,p) - BOLL_LOWER(x,n,p)`。 --- ## 9. 精度验证 / Accuracy `factorlab` 附带的测试: - **算子一致性**(`t3_ops.py`):63 项断言,每个算子对手算参考值逐元素比对, 全部通过。 - **全量求值**(`t2_eval.py`):313 个本地可算因子在真实数据上求值,312 成功, 1 失败(即第 8 节的 `A191_010`,上游括号错位)。 - **独立性**(`t5_standalone.py`):断言包内不 import skill / 平台模块, 四份目录文件全部打包在 `factorlab/data/` 内。 重跑: ```bash python E:\quant\factorlab-lib\tests\t3_ops.py # 算子一致性 python E:\quant\factorlab-lib\tests\t2_eval.py # 全量求值 python E:\quant\factorlab-lib\tests\t5_standalone.py python E:\quant\ext_demo\demo.py # 外部项目调用演示 ``` --- ## 10. 文件结构 / Layout ``` E:\quant\factorlab-lib\ ├── factorlab\ # 包本体 │ ├── __init__.py # 公共 API 出口 │ ├── parser.py # 分词 + 递归下降解析器 -> AST │ ├── evaluate.py # AST 求值器(字段解析、比较、逻辑、算术) │ ├── ops.py # 105 个算子实现(pandas/numpy 向量化) │ ├── panel.py # xbx CSV 加载 -> 面板 │ ├── registry.py # 因子定义注册表 │ ├── lab.py # FactorLab 门面类 │ └── data\ # 打包的目录文件(library.csv + 两份 alpha + operators.md) ├── tests\ # 测试脚本(直接 python 运行,非 pytest) │ ├── t2_eval.py # 全量求值测试 │ ├── t3_ops.py # 算子一致性测试 │ └── t5_standalone.py # 独立性测试 └── scripts\ └── bundle_data.py # 重新打包目录文件的脚本 E:\quant\ext_demo\demo.py # 外部项目调用演示(放在库目录之外) ``` --- ## 11. 中文速查 / Quick reference ```python import sys; sys.path.insert(0, r"E:\quant\factorlab-lib") from factorlab import FactorLab, load_panel lab = FactorLab() panel = load_panel(r"E:\quant\xbx_data\data\stock-trading-data", start="2024-01-01", end="2025-12-31", symbols=codes) lab.summary() # 有哪些因子 lab.get("R6") # 看定义 lab.missing_for("R6") # 缺什么字段 lab.check("RANK(-ROC(CLOSE,120))") # 公式体检 lab.compute("RANK(-ROC(CLOSE,120))", panel) # 求值 lab.screen("F-MOM20", panel, top_n=10) # 截面选股 lab.compute_many(names, panel) # 批量 ```