# 因子公式使用说明

## 提交格式

- `intraday_expression`：每日特征构造，选填；使用分钟数据时必须完成日内降频。
- `interday_expression`：最终日频表达式；日内层存在时留空等同于 `Feature`。
- `operators.py`：自定义 Python 算子源码，选填，UTF-8，最大 32 KiB。
- `neutralization`：可选中性化配置，`{"enabled": true, "exposures": ["industry", "return", "barra"], "barra_columns": ["BETA", "SIZE"], "return_window": 60}`；对最终日频因子逐日截面 OLS 取残差，`barra_columns`/`return_window` 选填（缺省全部风格、20 日）。

## 可用字段

| 字段 | 频率 | 名称 | 说明 | 单位 |
| --- | --- | --- | --- | --- |
| `VWAP` | 分钟 | 成交均价 | 该股票当前分钟成交额除以成交量得到的成交量加权平均价。 | 价格 |
| `Amount` | 分钟 | 成交额 | 该股票当前分钟内累计成交金额。 | 金额 |
| `Volume` | 分钟 | 成交量 | 该股票当前分钟内累计成交股数。 | 股数 |
| `Close` | 分钟 | 收盘价 | 该股票当前分钟最后一笔有效成交对应的收盘价。 | 价格 |
| `Return` | 分钟 | 收益率 | 当前分钟 Close 相对上一分钟 Close 的变化率；每日开盘重新起算。 | 小数 |
| `DailyReturn` | 日频 | 日收益率 | 正式日收益率字段由百分数转换为小数，例如 1.5% 转换为 0.015。 | 小数 |
| `DailyClose` | 日频 | 复权收盘价 | 使用正式复权因子处理后的日收盘价。 | 价格 |
| `DailyHigh` | 日频 | 复权最高价 | 使用正式复权因子处理后的日最高价。 | 价格 |
| `DailyLow` | 日频 | 复权最低价 | 使用正式复权因子处理后的日最低价。 | 价格 |
| `DailyAmount` | 日频 | 成交额 | 该股票当日累计成交金额。 | 金额 |
| `DailyVolume` | 日频 | 成交量 | 该股票当日累计成交量。 | 股数 |
| `DailyTurnover` | 日频 | 自由流通换手率 | 该股票当日自由流通换手率，保持正式日频源字段口径。 | 百分数 |
| `DailyFreeShare` | 日频 | 自由流通股本 | 该股票当日自由流通股本。 | 股数 |
| `DailyTotalMV` | 日频 | 总市值 | 该股票当日总市值。 | 金额 |
| `DailyTotalShare` | 日频 | 总股本 | 该股票当日总股本。 | 股数 |

## Level2 日频特征

当前版本共 9 个 Level2 日频特征，分为 ORDER、TRADE 和 SNAPSHOT 三类。

### ORDER：逐笔委托

| 字段 | 定义 |
| --- | --- |
| `order__cancel_pressure` | 撤单量 /（新增委托量 + 撤单量）。 |
| `order__order_flow_imbalance` | ［（新增买量−撤单买量）−（新增卖量−撤单卖量）］/（新增委托量 + 撤单量）。 |

### TRADE：逐笔成交

| 字段 | 定义 |
| --- | --- |
| `trade__trade_directional_coverage` | （主动买成交量 + 主动卖成交量）/ 全部成交量。 |
| `trade__trade_imbalance` | （主动买成交量−主动卖成交量）/（主动买成交量 + 主动卖成交量）。 |

### SNAPSHOT：五档盘口快照

| 字段 | 定义 |
| --- | --- |
| `snapshot__mean_relative_spread` | 日内平均（卖一价−买一价）/ 中间价。 |
| `snapshot__mean_depth_imbalance` | 日内平均（五档买量−五档卖量）/（五档买量 + 五档卖量）。 |
| `snapshot__mean_top_depth_imbalance` | 日内平均（买一量−卖一量）/（买一量 + 卖一量）。 |
| `snapshot__midpoint_realized_volatility` | sqrt(Σ[log(midpoint_t)−log(midpoint_t−1)]²)。 |
| `snapshot__mean_snapshot_quote_churn` | 相邻快照20个五档价格/数量字段发生变化的比例，再取日均值。 |

### 计算口径

- ORDER：深交所全时期 `order_type ∈ {1,2,3}` 为新增委托、`order_type=4` 为撤单；上交所 2021-06-04 及以前仅 `order_type=2` 作为新增委托且无可靠撤单分类，2021-06-07 起 `order_type ∈ {1,2}` 为新增委托、`order_type=4` 为撤单。`trade_direction=1` 为买、`trade_direction=2` 为卖。实现直接按股票汇总 `order_volume`，不读取 `order_index`，不按委托号聚合，不做委托与撤单 join，也不计算单笔订单数量、价格或生命周期。
- TRADE：`bs_flag=1` 为主动买，`bs_flag=2` 为主动卖；其他值只计入全部成交量。仅价格、成交量有效且非负的记录参与统计；没有可识别方向时，成交不平衡为空。
- SNAPSHOT：连续交易时段为 09:30:00—11:30:00、13:00:00—14:56:59。原始盘口价格乘以 0.01；价格为 0 时对应价格和数量均置空。`midpoint=(买一价+卖一价)/2`。
- 单边盘口：涨停无卖盘时卖方五档总深度按 0，跌停无买盘时买方五档总深度按 0；缺少买一或卖一时，中间价、相对价差和顶部深度不平衡为空。
- 报价变化率：比较五档买卖价格与数量共 20 个字段；仅比较同一股票、同一上午或下午阶段、间隔大于 0 且不超过 15 秒的相邻快照。两帧同一字段都为空视为未变化。
- 中间价波动率：使用相邻有效中间价；缺少买一或卖一的快照不产生收益，当前实现会连接缺失区间前后的两个有效中间价。
- SNAPSHOT 覆盖率：五类基础观测的最小有效数量 / 4802 必须不低于 80%，即至少 3842 条，否则该股票当日不生成 SNAPSHOT 日频特征。

### 数据存在标志

| 字段 | 含义 |
| --- | --- |
| `order__present` | 该股票当天是否有有效的逐笔委托日频结果。 |
| `trade__present` | 该股票当天是否有有效的逐笔成交日频结果。 |
| `snapshot__present` | 该股票当天是否有有效的五档盘口日频结果。 |

以上标志表示对应数据源当天是否有有效日频结果，不是因子值本身。

## 公共算子

### 算术运算

| 算子 | 说明 | 示例 |
| --- | --- | --- |
| `Add(x, y)` | 逐元素相加。 | `Add(Return, 0.001)` |
| `Sub(x, y)` | 逐元素计算 x - y。 | `Sub(Return, CsMean(Return))` |
| `Mul(x, y)` | 逐元素相乘。 | `Mul(CsZScore(Return), CsRank(Volume))` |
| `Div(x, y)` | 安全除法；分母绝对值不大于 1e-12 时返回 NaN。 | `Div(Amount, CsMean(Amount))` |
| `Neg(x)` | 逐元素取相反数。 | `Neg(TsStd(Return, 20))` |
| `Abs(x)` | 逐元素取绝对值。 | `Abs(Return)` |
| `Sign(x)` | 返回元素符号：负数为 -1、零为 0、正数为 1。 | `Sign(Return)` |
| `Log(x)` | 计算自然对数；非正数返回 NaN。 | `Log(Amount)` |
| `Inv(x)` | 逐元素取倒数；接近零的值返回 NaN。 | `Inv(Amount)` |
| `Exp(x)` | 逐元素计算以 e 为底的指数；溢出返回 NaN。 | `Exp(Return)` |
| `Sqrt(x)` | 逐元素开平方；负数返回 NaN。 | `Sqrt(Volume)` |
| `Pow(x, exponent)` | 逐元素计算指定次幂；非实数或溢出结果返回 NaN。 | `Pow(Return, 2)` |
| `Min(x, y)` | 逐元素取较小值。 | `Min(VWAP, Close)` |
| `Max(x, y)` | 逐元素取较大值。 | `Max(VWAP, Close)` |

### 截面运算

| 算子 | 说明 | 示例 |
| --- | --- | --- |
| `CsRank(x)` | 沿股票轴计算稳定的截面百分位排名，有效值范围为 (0, 1]；输出与原面板对齐，可继续参与其他运算。 | `CsRank(Div(Amount, CsMean(Amount)))` |
| `CsMean(x)` | 沿股票轴计算有效值均值，并将均值广播回该截面的每只有效股票，可用于去均值、相对均值等计算。 | `CsMean(Abs(Return))` |
| `CsZScore(x)` | 沿股票轴去均值并除以样本标准差；输出与原面板对齐，可继续参与其他运算。 | `CsZScore(Sub(Return, CsMean(Return)))` |

### 时间序列

| 算子 | 说明 | 示例 |
| --- | --- | --- |
| `TsMean(x, window)` | 日内滚动均值；窗口形成前返回 NaN。 | `TsMean(Amount, 20)` |
| `TsStd(x, window)` | 日内滚动样本标准差；窗口形成前返回 NaN。 | `TsStd(Return, 20)` |
| `TsSum(x, window)` | 日内滚动求和。 | `TsSum(Volume, 20)` |
| `TsMin(x, window)` | 日内滚动最小值。 | `TsMin(Return, 20)` |
| `TsMax(x, window)` | 日内滚动最大值。 | `TsMax(Return, 20)` |
| `TsDelay(x, periods)` | 在当日分钟轴滞后，不跨交易日。 | `TsDelay(Close, 5)` |
| `TsDelta(x, periods)` | 当前值减去日内滞后值。 | `TsDelta(Close, 5)` |
| `TsRank(x, window)` | 当前值在日内滚动窗口中的百分位排名。 | `TsRank(Amount, 20)` |
| `TsZScore(x, window)` | 当前值相对日内滚动均值和样本标准差的标准分。 | `TsZScore(Return, 20)` |
| `TsCorr(x, y, window)` | 两组分钟序列的日内滚动相关系数。 | `TsCorr(Return, Volume, 20)` |
| `TsCov(x, y, window)` | 两组分钟序列的日内滚动样本协方差。 | `TsCov(Return, Volume, 20)` |
| `TsAutoCorr(x, window, lag)` | 日内滚动自相关；lag 必须是小于 window 的正整数。 | `TsAutoCorr(Return, 20, 1)` |
| `TsArgMax(x, window)` | 滚动最大值位置占窗口长度的比例，越接近 1 表示越靠近当前。 | `TsArgMax(Amount, 20)` |
| `TsArgMin(x, window)` | 滚动最小值位置占窗口长度的比例，越接近 1 表示越靠近当前。 | `TsArgMin(Amount, 20)` |
| `TsDecayLinear(x, window)` | 线性衰减均值，越靠近当前时点权重越高。 | `TsDecayLinear(Return, 20)` |
| `TsDemean(x, window)` | 当前值减去日内滚动均值。 | `TsDemean(Return, 20)` |
| `TsReturn(x, periods)` | 当前值相对日内滞后值的变化率。 | `TsReturn(Close, 5)` |
| `TsMedian(x, window)` | 日内滚动中位数。 | `TsMedian(Amount, 20)` |
| `TsVar(x, window)` | 日内滚动样本方差。 | `TsVar(Return, 20)` |
| `TsSkew(x, window)` | 日内滚动偏度。 | `TsSkew(Return, 20)` |
| `TsKurt(x, window)` | 日内滚动超额峰度。 | `TsKurt(Return, 20)` |
| `TsQuantile(x, q, window)` | 日内滚动 q 分位数，q 必须位于 [0, 1]。 | `TsQuantile(Amount, 0.9, 20)` |

### 日内聚合（必选其一）

| 算子 | 说明 | 示例 |
| --- | --- | --- |
| `DayMean(x)` | 取全天分钟均值。 | `DayMean(Neg(Return))` |
| `DaySum(x)` | 取全天分钟合计。 | `DaySum(Volume)` |
| `DayLast(x)` | 取当日最后一个有效分钟值。 | `DayLast(CsRank(Return))` |
| `PeriodMean(start_time, end_time, x)` | 对闭区间内的分钟值取均值。 | `PeriodMean(145000, 150000, Return)` |
| `PeriodSum(start_time, end_time, x)` | 对闭区间内的分钟值求和。 | `PeriodSum(93000, 100000, Volume)` |
| `PeriodLast(start_time, end_time, x)` | 取闭区间内最后一个有效分钟值。 | `PeriodLast(145000, 150000, CsRank(Return))` |

### 日间运算（选填）

| 算子 | 说明 | 示例 |
| --- | --- | --- |
| `DailyMean(Feature, window)` | 跨交易日滚动均值。 | `DailyMean(Feature, 5)` |
| `DailyEMA(Feature, window)` | 跨交易日指数移动平均，alpha = 2 / (window + 1)。 | `DailyEMA(Feature, 10)` |
| `DailyStd(Feature, window)` | 跨交易日滚动样本标准差。 | `DailyStd(Feature, 20)` |
| `DailySum(Feature, window)` | 跨交易日滚动求和。 | `DailySum(Feature, 5)` |
| `DailyMin(Feature, window)` | 跨交易日滚动最小值。 | `DailyMin(Feature, 20)` |
| `DailyMax(Feature, window)` | 跨交易日滚动最大值。 | `DailyMax(Feature, 20)` |
| `DailyRank(Feature, window)` | 当前值在跨日滚动窗口中的百分位排名。 | `DailyRank(Feature, 20)` |
| `DailyZScore(Feature, window)` | 当前值相对跨日滚动均值和样本标准差的标准分。 | `DailyZScore(Feature, 20)` |
| `Delay(Feature, periods)` | 按交易日滞后，正数只读取历史。 | `Delay(Feature, 1)` |
| `Delta(Feature, periods)` | 当前 Feature 减去跨日滞后值。 | `Delta(Feature, 5)` |
| `Correlation(x, y, window)` | 两组日频表达式的跨日滚动相关系数。 | `Correlation(Feature, Delay(Feature, 1), 20)` |

## 使用规则

1. 公式既可写成单个表达式，也可写成顺序赋值；分段写法的最后一个赋值变量作为输出，支持换行、英文分号和中文分号。
2. 分段公式的局部变量必须以小写字母开头，只能引用前面已经定义的变量，且不能重复赋值。
3. 使用分钟数据时，第一栏最终输出必须是 DayMean、DaySum、DayLast、PeriodMean、PeriodSum 或 PeriodLast 的日内降频结果；降频后的日频运算应放在第二栏。
4. 日内表达式可引用 Daily 字段；系统按日期、股票广播，信号按收盘后形成。
5. 日内滚动窗口必须是正整数，且每天重新开始计算。
6. 日间表达式只能引用 Feature、Daily 前缀字段、公开日间算子，以及本次提交唯一的自定义 Python 算子。
7. 日间表达式留空等同于 Feature。
8. 第一步和第二步都留空但提供 operators.py 时，入口函数参数名必须都是 Daily 字段名，系统会自动生成日间自定义算子调用；包含 window、alpha 等数字参数时必须显式填写日间表达式。
9. 公共算子支持嵌套组合；算术算子可以接收字段、常量以及时间序列、截面和其他算术算子的输出。CsMean 等截面统计结果会广播并保持原股票轴，不会变成无法继续计算的单一标量。
10. 缺失值不会自动填充为零。
11. 中性化为选填：启用后对最终日频因子逐日做横截面 OLS 回归取残差；暴露变量可选行业（申万一级）、收益（20日滚动）与 Barra 风格（10因子，含规模），可多选。参数固定：zscore 标准化、clip 4.0、min_obs 30。
