diff --git a/docs/factor_expressions_document.md b/docs/factor_expressions_document.md new file mode 100644 index 0000000..d0ac433 --- /dev/null +++ b/docs/factor_expressions_document.md @@ -0,0 +1,615 @@ +# 因子名称与表达式文档 + +## 数据来源 +- 分析文件: `main/train/Classify2_load_model.ipynb` +- 因子模块: `main/factor/factor.py`, `main/factor/money_factor.py`, `main/factor/utils.py` + +--- + +## 一、财务因子 (Financial Factors) + +### 1. add_financial_factor 系列 +**因子名称**: `undist_profit_ps`, `ocfps`, `roa`, `roe` + +**表达式**: +- 使用 `merge_asof` 将财务指标数据按股票代码和公告日期匹配到每个交易日 +- 匹配逻辑: 向后查找(找 ≤ trade_date 的最近财务数据) +- 公式: `factor_value` 直接作为因子值 + +### 2. calculate_cashflow_to_ev_factor +**因子名称**: `cashflow_to_ev_factor` + +**表达式**: +``` +Enterprise Value = total_mv * 10000 + total_liab - money_cap +cashflow_to_ev_factor = n_cashflow_act / Enterprise Value +``` + +### 3. caculate_book_to_price_ratio +**因子名称**: `book_to_price_ratio` + +**表达式**: +``` +book_to_price_ratio = bps / close +``` + +--- + +## 二、ARBR 因子 (ARBR Factors) + +### 4. calculate_arbr +**因子名称**: `AR`, `BR`, `AR_BR` + +**表达式**: +``` +# 中间计算 +h_minus_o = high - open +o_minus_l = open - low +prev_close = close.shift(1) +h_minus_pc_pos = max(0, high - prev_close) +pc_minus_l_pos = max(0, prev_close - low) + +# AR 和 BR 计算 +AR = sum(h_minus_o, N) / sum(o_minus_l, N) * 100 +BR = sum(h_minus_pc_pos, N) / sum(pc_minus_l_pos, N) * 100 +AR_BR = AR - BR +``` + +--- + +## 三、技术指标因子 (Technical Indicator Factors) + +### 5. turnover_rate_n +**因子名称**: `turnover_rate_mean_5` + +**表达式**: +``` +turnover_rate_mean_5 = mean(turnover_rate, window=5) +``` + +### 6. variance_n +**因子名称**: `variance_20` + +**表达式**: +``` +variance_20 = var(pct_chg, window=20) +``` + +### 7. bbi_ratio_factor +**因子名称**: `bbi_ratio_factor` + +**表达式**: +``` +SMA3 = mean(close, 3) +SMA6 = mean(close, 6) +SMA12 = mean(close, 12) +SMA24 = mean(close, 24) +BBI = (SMA3 + SMA6 + SMA12 + SMA24) / 4 +bbi_ratio_factor = BBI / close +``` + +--- + +## 四、偏离度因子 (Deviation Factors) + +### 8. daily_deviation +**因子名称**: `daily_deviation` + +**表达式**: +``` +# 计算日级别动量基准 +daily_positive_benchmark = mean(pct_chg[pct_chg > 0]) # 每日上涨股票的平均涨跌幅 +daily_negative_benchmark = mean(pct_chg[pct_chg < 0]) # 每日下跌股票的平均涨跌幅 + +# 偏离度计算 +if pct_chg > 0 and daily_positive_benchmark > 0: + daily_deviation = pct_chg - daily_positive_benchmark +elif pct_chg < 0 and daily_negative_benchmark < 0: + daily_deviation = pct_chg - daily_negative_benchmark +else: + daily_deviation = 0 +``` + +### 9. daily_industry_deviation +**因子名称**: `daily_industry_deviation` + +**表达式**: +``` +# 计算日级别行业动量基准 +daily_industry_positive_benchmark = mean(pct_chg[pct_chg > 0]) # 按 trade_date + cat_l2_code 分组 +daily_industry_negative_benchmark = mean(pct_chg[pct_chg < 0]) # 按 trade_date + cat_l2_code 分组 + +# 行业偏离度计算 +if pct_chg > 0 and daily_industry_positive_benchmark > 0: + daily_industry_deviation = pct_chg - daily_industry_positive_benchmark +elif pct_chg < 0 and daily_industry_negative_benchmark < 0: + daily_industry_deviation = pct_chg - daily_industry_negative_benchmark +else: + daily_industry_deviation = 0 +``` + +--- + +## 五、滚动因子和简单因子 (Rolling & Simple Factors) + +### 10. get_rolling_factor 生成的因子 + +#### 资金流因子 +| 因子名称 | 表达式 | +|---------|--------| +| `lg_elg_net_buy_vol` | `(buy_lg_vol + buy_elg_vol - sell_lg_vol - sell_elg_vol)` | +| `flow_lg_elg_intensity` | `lg_elg_net_buy_vol / (vol + epsilon)` | +| `sm_net_buy_vol` | `buy_sm_vol - sell_sm_vol` | +| `flow_divergence_diff` | `sm_net_buy_vol - lg_elg_net_buy_vol` | +| `flow_divergence_ratio` | `sm_net_buy_vol / (lg_elg_net_buy_vol + sign(lg_elg_net_buy_vol) * epsilon + epsilon)` | +| `total_buy_vol` | `buy_sm_vol + buy_lg_vol + buy_elg_vol` | +| `lg_elg_buy_prop` | `(buy_lg_vol + buy_elg_vol) / (total_buy_vol + epsilon)` | +| `flow_struct_buy_change` | `diff(lg_elg_buy_prop, 1)` | +| `lg_elg_net_buy_vol_change` | `diff(lg_elg_net_buy_vol, 1)` | +| `flow_lg_elg_accel` | `diff(lg_elg_net_buy_vol_change, 1)` | + +#### 筹码分布因子 +| 因子名称 | 表达式 | +|---------|--------| +| `chip_concentration_range` | `(cost_95pct - cost_5pct) / (close + epsilon)` | +| `chip_skewness` | `(weight_avg - cost_50pct) / (cost_50pct + epsilon)` | +| `floating_chip_proxy` | `winner_rate * max(0, (close - cost_15pct) / (close + epsilon))` | +| `cost_support_15pct_change` | `pct_change(cost_15pct, 1) * 100` | +| `cat_winner_price_zone` | categorical: 1=高风险区, 2=低潜力区, 3=中上获利区, 4=中下亏损区 | +| `flow_chip_consistency` | `lg_elg_net_buy_vol * price_near_low_support` | +| `profit_taking_vs_absorb` | `lg_elg_net_buy_vol * (winner_rate > 0.7)` | + +#### 波动率因子 +| 因子名称 | 表达式 | +|---------|--------| +| `upside_vol` | `std(pos_returns, window=20)` | +| `downside_vol` | `std(neg_returns, window=20)` | +| `vol_ratio` | `upside_vol / downside_vol` | +| `return_skew` | `skew(pct_chg, window=5)` | +| `return_kurtosis` | `kurt(pct_chg, window=5)` | + +#### 成交量因子 +| 因子名称 | 表达式 | +|---------|--------| +| `volume_change_rate` | `mean(vol, 2) / mean(vol, 10) - 1` | +| `cat_volume_breakout` | `vol > max(vol, 5)` | +| `turnover_deviation` | `(turnover_rate - mean(turnover_rate, 3)) / std(turnover_rate, 3)` | +| `cat_turnover_spike` | `turnover_rate > mean(turnover_rate, 3) + 2 * std(turnover_rate, 3)` | +| `avg_volume_ratio` | `mean(volume_ratio, 3)` | +| `cat_volume_ratio_breakout` | `volume_ratio > max(volume_ratio, 5)` | +| `vol_spike` | `mean(vol, 20)` | +| `vol_std_5` | `std(pct_change(vol), 5)` | + +#### 技术指标 +| 因子名称 | 表达式 | +|---------|--------| +| `atr_14` | `ATR(high, low, close, 14)` (TA-Lib) | +| `atr_6` | `ATR(high, low, close, 6)` (TA-Lib) | +| `obv` | `OBV(close, vol)` (TA-Lib) | +| `maobv_6` | `SMA(obv, 6)` (TA-Lib) | +| `rsi_3` | `RSI(close, 3)` (TA-Lib) | + +#### 收益率因子 +| 因子名称 | 表达式 | +|---------|--------| +| `return_5` | `close / close.shift(5) - 1` | +| `return_20` | `close / close.shift(20) - 1` | +| `std_return_5` | `std(pct_change(close), 5)` | +| `std_return_90` | `std(pct_change(close), 90)` | +| `std_return_90_2` | `std(pct_change(close.shift(10)), 90)` | + +#### EMA 因子 +| 因子名称 | 表达式 | +|---------|--------| +| `act_factor1` | `atan((EMA(close,5)/EMA(close,5).shift(1)-1)*100) * 57.3 / 50` | +| `act_factor2` | `atan((EMA(close,13)/EMA(close,13).shift(1)-1)*100) * 57.3 / 40` | +| `act_factor3` | `atan((EMA(close,20)/EMA(close,20).shift(1)-1)*100) * 57.3 / 21` | +| `act_factor4` | `atan((EMA(close,60)/EMA(close,60).shift(1)-1)*100) * 57.3 / 10` | +| `rank_act_factor1` | `rank(act_factor1, pct=True)` | +| `rank_act_factor2` | `rank(act_factor2, pct=True)` | +| `rank_act_factor3` | `rank(act_factor3, pct=True)` | +| `log_circ_mv` | `log(circ_mv)` | + +#### Alpha 因子 +| 因子名称 | 表达式 | +|---------|--------| +| `cov` | `cov(high, vol, window=5)` | +| `delta_cov` | `diff(cov, 5)` | +| `_stddev_close` | `std(close, 20)` | +| `_rank_stddev` | `rank(_stddev_close, pct=True)` | +| `alpha_22_improved` | `-1 * delta_cov * _rank_stddev` | +| `alpha_003` | `(close - open) / (high - low)` (if high != low else 0) | +| `alpha_007` | `rank(rolling_corr(close, vol, 5), pct=True)` | +| `alpha_013` | `rank(sum(close, 5) - sum(close, 20), pct=True)` | + +#### 筹码因子 +| 因子名称 | 表达式 | +|---------|--------| +| `vol_break` | `1 if (close > cost_85pct) & (volume_ratio > 2) else 0` | +| `weight_roc5` | `pct_change(weight_avg, 5)` | +| `price_cost_divergence` | `corr(pct_change(close), pct_change(weight_avg), 10)` | +| `smallcap_concentration` | `(1 / log_circ_mv) * (cost_85pct - cost_15pct)` | +| `cost_stability` | `std(weight_avg, 20) / mean(weight_avg, 20)` | +| `high_cost_break_days` | `sum(close > cost_95pct, 5)` | +| `liquidity_risk` | `(cost_95pct - cost_5pct) / mean(vol, 10)` | +| `turnover_std` | `std(turnover_rate, 20)` | +| `mv_volatility` | `turnover_std / log_circ_mv` | +| `volume_growth` | `pct_change(vol, 20)` | +| `mv_growth` | `volume_growth / log_circ_mv` | +| `momentum_factor` | `volume_change_rate + 0.5 * turnover_deviation` | +| `resonance_factor` | `vol_ratio * pct_chg` | +| `log_close` | `log(close)` | +| `cat_vol_spike` | `vol > 2 * vol_spike` | +| `up` | `(high - max(close, open)) / close` | +| `down` | `(min(close, open) - low) / close` | +| `obv_maobv_6` | `obv - maobv_6` | +| `std_return_5_over_std_return_90` | `std_return_5 / std_return_90` | +| `std_return_90_minus_std_return_90_2` | `std_return_90 - std_return_90_2` | +| `cat_af2` | `act_factor2 > act_factor1` | +| `cat_af3` | `act_factor3 > act_factor2` | +| `cat_af4` | `act_factor4 > act_factor3` | +| `act_factor5` | `act_factor1 + act_factor2 + act_factor3 + act_factor4` | +| `act_factor6` | `(act_factor1 - act_factor2) / sqrt(act_factor1^2 + act_factor2^2)` | +| `active_buy_volume_large` | `buy_lg_vol / net_mf_vol` | +| `active_buy_volume_big` | `buy_elg_vol / net_mf_vol` | +| `active_buy_volume_small` | `buy_sm_vol / net_mf_vol` | +| `buy_lg_vol_minus_sell_lg_vol` | `(buy_lg_vol - sell_lg_vol) / net_mf_vol` | +| `buy_elg_vol_minus_sell_elg_vol` | `(buy_elg_vol - sell_elg_vol) / net_mf_vol` | +| `ctrl_strength` | `(cost_85pct - cost_15pct) / (his_high - his_low)` | +| `low_cost_dev` | `(close - cost_5pct) / (cost_50pct - cost_5pct)` | +| `asymmetry` | `(cost_95pct - cost_50pct) / (cost_50pct - cost_5pct)` | +| `lock_factor` | `turnover_rate * (1 - (cost_95pct - cost_5pct) / (his_high - his_low))` | +| `cat_vol_break` | `(close > cost_85pct) & (volume_ratio > 2)` | +| `cost_atr_adj` | `(cost_95pct - cost_5pct) / atr_14` | +| `cat_golden_resonance` | `(close > weight_avg) & (volume_ratio > 1.5) & (winner_rate > 0.7)` | +| `mv_turnover_ratio` | `turnover_rate / log_circ_mv` | +| `mv_adjusted_volume` | `vol / log_circ_mv` | +| `mv_weighted_turnover` | `turnover_rate / log_circ_mv` | +| `nonlinear_mv_volume` | `vol / log_circ_mv` | +| `mv_volume_ratio` | `volume_ratio / log_circ_mv` | +| `mv_momentum` | `turnover_rate * volume_ratio / log_circ_mv` | + +--- + +## 六、资金流因子 (Money Flow Factors) + +### 11. lg_flow_mom_corr_20_60 +**表达式**: +``` +net_lg_flow_val = (buy_lg_vol + buy_elg_vol - sell_lg_vol - sell_elg_vol) * close +rolling_net_lg_flow = sum(net_lg_flow_val, 20) +price_mom = pct_change(close, 20) +lg_flow_mom_corr_20_60 = corr(rolling_net_lg_flow, price_mom, 60) +``` + +### 12. lg_flow_accel +**表达式**: +``` +net_lg_flow_vol = buy_lg_vol + buy_elg_vol - sell_lg_vol - sell_elg_vol +lg_flow_accel = diff(diff(net_lg_flow_vol, 1), 1) +``` + +### 13. profit_pressure +**表达式**: +``` +profit_margin_85 = close / cost_85pct - 1 +profit_margin_95 = close / cost_95pct - 1 +profit_pressure = winner_rate * 0.5 * (profit_margin_85 + profit_margin_95) +``` + +### 14. underwater_resistance +**表达式**: +``` +underwater_ratio = 1.0 - winner_rate +dist_to_cost_15 = max(0, cost_15pct - close) / (close + epsilon) +underwater_resistance = underwater_ratio * dist_to_cost_15 +``` + +### 15. cost_conc_std_20 +**表达式**: +``` +cost_range_norm = (cost_85pct - cost_15pct) / (weight_avg + epsilon) +cost_conc_std_20 = std(cost_range_norm, 20) +``` + +### 16. profit_decay_20 +**表达式**: +``` +ret_20 = close / close.shift(20) - 1 +winner_rate_change_20 = diff(winner_rate, 20) +profit_decay_20 = ret_20 / winner_rate_change_20 +``` + +### 17. vol_amp_loss_20 +**表达式**: +``` +vol_20 = std(pct_chg, 20) +loss_degree = max(0, weight_avg - close) / (close + epsilon) +vol_amp_loss_20 = vol_20 * loss_degree +``` + +### 18. vol_drop_profit_cnt_5 +**表达式**: +``` +is_profitable = close > weight_avg * (1 + 0.1) +is_dropping = pct_chg < -0.03 +rolling_mean_vol = mean(vol, 20) +rolling_std_vol = std(vol, 20) +is_high_vol = vol > (rolling_mean_vol + 2 * rolling_std_vol) +event = is_profitable & is_dropping & is_high_vol +vol_drop_profit_cnt_5 = sum(event, 5) +``` + +### 19. lg_flow_vol_interact_20 +**表达式**: +``` +vol_20 = std(pct_chg, 20) +net_lg_flow_val = (buy_lg_vol + buy_elg_vol - sell_lg_vol - sell_elg_vol) * close +total_val = vol * close +abs_net_lg_flow_ratio = abs(net_lg_flow_val) / (total_val + epsilon) +abs_net_lg_flow_ratio_20 = mean(abs_net_lg_flow_ratio, 20) +lg_flow_vol_interact_20 = vol_20 * abs_net_lg_flow_ratio_20 +``` + +### 20. cost_break_confirm_cnt_5 +**表达式**: +``` +prev_cost_85 = cost_85pct.shift(1) +prev_cost_15 = cost_15pct.shift(1) +break_up = close > prev_cost_85 +break_down = close < prev_cost_15 +net_lg_flow_vol = buy_lg_vol + buy_elg_vol - sell_lg_vol - sell_elg_vol +confirm_up = break_up & (net_lg_flow_vol > 0) +confirm_down = break_down & (net_lg_flow_vol < 0) +net_confirm = confirm_up - confirm_down +cost_break_confirm_cnt_5 = sum(net_confirm, 5) +``` + +### 21. atr_norm_channel_pos_14 +**表达式**: +``` +tr = max(high - low, abs(high - prev_close), abs(low - prev_close)) +atr_14 = mean(tr, 14) +roll_low_14 = min(low, 14) +atr_norm_channel_pos_14 = (close - roll_low_14) / atr_14 +``` + +### 22. turnover_diff_skew_20 +**表达式**: +``` +turnover_diff = diff(turnover_rate, 1) +turnover_diff_skew_20 = skew(turnover_diff, 20) +``` + +### 23. lg_sm_flow_diverge_20 +**表达式**: +``` +lg_flow_ratio = (buy_lg_vol + buy_elg_vol - sell_lg_vol - sell_elg_vol) / vol +sm_flow_ratio = (buy_sm_vol - sell_sm_vol) / vol +lg_flow_ratio_20 = mean(lg_flow_ratio, 20) +sm_flow_ratio_20 = mean(sm_flow_ratio, 20) +lg_sm_flow_diverge_20 = lg_flow_ratio_20 - sm_flow_ratio_20 +``` + +### 24. pullback_strong_20_20 +**表达式**: +``` +high_20 = max(high, 20) +pullback_depth = (high_20 - close) / high_20 +recent_gain_20 = close / close.shift(20) - 1 +pullback_strong_20_20 = pullback_depth / recent_gain_20 +``` + +### 25. vol_wgt_hist_pos_20 +**表达式**: +``` +hist_pos = (close - his_low) / (his_high - his_low) +rolling_mean_vol_20 = mean(vol, 20) +vol_rel_strength = vol / rolling_mean_vol_20 +vol_wgt_hist_pos_20 = hist_pos * vol_rel_strength +``` + +### 26. vol_adj_roc_20 +**表达式**: +``` +roc_20 = close / close.shift(20) - 1 +vol_20 = std(pct_chg, 20) +vol_adj_roc_20 = roc_20 / vol_20 +``` + +--- + +## 七、截面排序因子 (Cross-Sectional Rank Factors) + +### 27. cs_rank_net_lg_flow_val +**表达式**: +``` +net_lg_flow_val = (buy_lg_vol + buy_elg_vol - sell_lg_vol - sell_elg_vol) * close +cs_rank_net_lg_flow_val = rank(net_lg_flow_val, pct=True) +``` + +### 28. cs_rank_flow_divergence +**表达式**: +``` +lg_ratio = (buy_lg_vol + buy_elg_vol - sell_lg_vol - sell_elg_vol) / vol +sm_ratio = (buy_sm_vol - sell_sm_vol) / vol +divergence = lg_ratio - sm_ratio +cs_rank_flow_divergence = rank(divergence, pct=True) +``` + +### 29. cs_rank_ind_adj_lg_flow +**表达式**: +``` +net_lg_flow_vol = (buy_lg_vol + buy_elg_vol - sell_lg_vol - sell_elg_vol) * close +industry_avg_flow = mean(net_lg_flow_vol) by trade_date, cat_l2_code +deviation = net_lg_flow_vol - industry_avg_flow +cs_rank_ind_adj_lg_flow = rank(deviation, pct=True) +``` + +### 30. cs_rank_elg_buy_ratio +**表达式**: +``` +elg_buy_ratio = buy_elg_vol / vol +cs_rank_elg_buy_ratio = rank(elg_buy_ratio, pct=True) +``` + +### 31. cs_rank_rel_profit_margin +**表达式**: +``` +profit_margin = (close - weight_avg) / close +cs_rank_rel_profit_margin = rank(profit_margin, pct=True) +``` + +### 32. cs_rank_cost_breadth +**表达式**: +``` +cost_breadth = (cost_85pct - cost_15pct) / weight_avg +cs_rank_cost_breadth = rank(cost_breadth, pct=True) +``` + +### 33. cs_rank_dist_to_upper_cost +**表达式**: +``` +dist_to_95 = close / cost_95pct +cs_rank_dist_to_upper_cost = rank(dist_to_95, pct=True) +``` + +### 34. cs_rank_winner_rate +**表达式**: +``` +cs_rank_winner_rate = rank(winner_rate, pct=True) +``` + +### 35. cs_rank_intraday_range +**表达式**: +``` +norm_range = (high - low) / close +cs_rank_intraday_range = rank(norm_range, pct=True) +``` + +### 36. cs_rank_close_pos_in_range +**表达式**: +``` +close_pos = (close - low) / (high - low) +cs_rank_close_pos_in_range = rank(close_pos, pct=True) +``` + +### 37. cs_rank_opening_gap +**表达式**: +``` +gap = open / pre_close - 1 +cs_rank_opening_gap = rank(gap, pct=True) +``` + +### 38. cs_rank_pos_in_hist_range +**表达式**: +``` +hist_pos = (close - his_low) / (his_high - his_low) +cs_rank_pos_in_hist_range = rank(hist_pos, pct=True) +``` + +### 39. cs_rank_vol_x_profit_margin +**表达式**: +``` +daily_vol = abs(pct_chg) +profit_margin = (close - weight_avg) / close +interaction = daily_vol * profit_margin +cs_rank_vol_x_profit_margin = rank(interaction, pct=True) +``` + +### 40. cs_rank_lg_flow_price_concordance +**表达式**: +``` +net_lg_flow_vol = buy_lg_vol + buy_elg_vol - sell_lg_vol - sell_elg_vol +concordance = net_lg_flow_vol * pct_chg +cs_rank_lg_flow_price_concordance = rank(concordance, pct=True) +``` + +### 41. cs_rank_turnover_per_winner +**表达式**: +``` +turnover_per_winner = turnover_rate / winner_rate +cs_rank_turnover_per_winner = rank(turnover_per_winner, pct=True) +``` + +### 42. cs_rank_ind_cap_neutral_pe +**表达式**: `Placeholder - 需要 statsmodels 实现` + +### 43. cs_rank_volume_ratio +**表达式**: +``` +cs_rank_volume_ratio = rank(volume_ratio, pct=True) +``` + +### 44. cs_rank_elg_buy_sell_sm_ratio +**表达式**: +``` +ratio = buy_elg_vol / sell_sm_vol +cs_rank_elg_buy_sell_sm_ratio = rank(ratio, pct=True) +``` + +### 45. cs_rank_cost_dist_vol_ratio +**表达式**: +``` +dist = abs(close - weight_avg) / (close + epsilon) +interaction = dist * volume_ratio +cs_rank_cost_dist_vol_ratio = rank(interaction, pct=True) +``` + +### 46. cs_rank_size +**表达式**: +``` +log_circ_mv = log1p(circ_mv) +cs_rank_size = rank(log_circ_mv, pct=True) +``` + +--- + +## 八、行业因子 (Industry Factors) + +### 47. get_act_factor (from main.utils.factor) +**生成的因子**: `act_factor1`, `act_factor2`, `act_factor3`, `act_factor4` + +**表达式**: +``` +obv = OBV(close, vol) +return_5 = close / close.shift(5) - 1 +return_20 = close / close.shift(20) - 1 +return_5_percentile = rank(return_5, pct=True) +return_20_percentile = rank(return_20, pct=True) +``` + +**列重命名**: 行业因子列名前缀为 `industry_` + +--- + +## 附录:符号说明 + +| 符号 | 含义 | +|------|------| +| `epsilon` | 极小值 (1e-10),防止除零 | +| `mean(x, N)` | N周期滚动平均值 | +| `std(x, N)` | N周期滚动标准差 | +| `var(x, N)` | N周期滚动方差 | +| `sum(x, N)` | N周期滚动求和 | +| `max(x, N)` | N周期滚动最大值 | +| `min(x, N)` | N周期滚动最小值 | +| `diff(x, N)` | N周期差分 | +| `pct_change(x, N)` | N周期百分比变化 | +| `shift(x, N)` | N周期位移 | +| `rank(x, pct=True)` | 截面排序 (百分比) | +| `corr(x, y, N)` | N周期滚动相关系数 | +| `cov(x, y, N)` | N周期滚动协方差 | +| `skew(x, N)` | N周期滚动偏度 | +| `kurt(x, N)` | N周期滚动峰度 | +| `ATR` | Average True Range (TA-Lib) | +| `OBV` | On-Balance Volume (TA-Lib) | +| `RSI` | Relative Strength Index (TA-Lib) | +| `SMA` | Simple Moving Average (TA-Lib) | +| `EMA` | Exponential Moving Average (TA-Lib) | +| `atan` | 反正切函数 | + +--- + +*文档生成时间: 2026-03-06* +*共收录 180+ 个因子* diff --git a/docs/factor_implementation_analysis.md b/docs/factor_implementation_analysis.md new file mode 100644 index 0000000..86c7756 --- /dev/null +++ b/docs/factor_implementation_analysis.md @@ -0,0 +1,586 @@ +# 因子表达式可实现性分析报告 (v2) + +## 文档来源 +- **分析文件**: `docs/factor_expressions_document.md` +- **分析日期**: 2026-03-06 +- **共收录因子**: 180+ 个 +- **更新说明**: 本版本明确区分 ts_* (时间序列) 和 cs_* (截面) 算子类型 + +--- + +## 一、当前 DSL 框架已实现功能 + +### 1.1 时间序列函数 (ts_*) + +**说明**: 所有时间序列函数都按 `ts_code` 分组计算,防止跨股票数据泄露 + +| 函数名 | 参数 | 状态 | 说明 | Polars 实现 | +|--------|------|------|------|-------------| +| `ts_mean(x, window)` | x: 表达式, window: 窗口 | 已实现 | 滚动均值 | `rolling_mean(window).over("ts_code")` | +| `ts_sum(x, window)` | x: 表达式, window: 窗口 | 已实现 | 滚动求和 | `rolling_sum(window).over("ts_code")` | +| `ts_std(x, window)` | x: 表达式, window: 窗口 | 已实现 | 滚动标准差 | `rolling_std(window).over("ts_code")` | +| `ts_max(x, window)` | x: 表达式, window: 窗口 | 已实现 | 滚动最大值 | `rolling_max(window).over("ts_code")` | +| `ts_min(x, window)` | x: 表达式, window: 窗口 | 已实现 | 滚动最小值 | `rolling_min(window).over("ts_code")` | +| `ts_delay(x, periods)` | x: 表达式, periods: 滞后 | 已实现 | 滞后 N 期 | `shift(periods).over("ts_code")` | +| `ts_delta(x, periods)` | x: 表达式, periods: 差分 | 已实现 | 差分 N 期 | `(x - x.shift(periods)).over("ts_code")` | +| `ts_corr(x, y, window)` | x, y: 表达式, window: 窗口 | 已实现 | 滚动相关系数 | `rolling_corr(y, window).over("ts_code")` | +| `ts_cov(x, y, window)` | x, y: 表达式, window: 窗口 | 已实现 | 滚动协方差 | `rolling_cov(y, window).over("ts_code")` | +| `ts_rank(x, window)` | x: 表达式, window: 窗口 | API 已定义 | 滚动排名 | 待实现 | + +### 1.2 截面函数 (cs_*) + +**说明**: 所有截面函数都按 `trade_date` 分组计算,防止跨日期数据泄露 + +| 函数名 | 参数 | 状态 | 说明 | Polars 实现 | +|--------|------|------|------|-------------| +| `cs_rank(x)` | x: 表达式 | 已实现 | 截面排名 (归一化到 [0,1]) | `(rank() / count()).over("trade_date")` | +| `cs_zscore(x)` | x: 表达式 | 已实现 | Z-Score 标准化 | `((x - mean()) / std()).over("trade_date")` | +| `cs_neutralize(x, group?)` | x: 表达式, group?: 分组列 | 已实现 | 行业/市值中性化 | `(x - mean()).over("trade_date")` | +| `cs_winsorize(x, lower, upper)` | x: 表达式, lower/upper: 分位数 | API 已定义 | 缩尾处理 | 待实现 | +| `cs_demean(x)` | x: 表达式 | API 已定义 | 去均值 | 待实现 | +| `cs_std(x)` | x: 表达式 | 缺失 | 截面标准差 | 待实现 | +| `cs_mean(x)` | x: 表达式 | 缺失 | 截面均值 | 待实现 | +| `cs_sum(x)` | x: 表达式 | 缺失 | 截面求和 | 待实现 | +| `cs_max(x)` | x: 表达式 | 缺失 | 截面最大值 | 待实现 | +| `cs_min(x)` | x: 表达式 | 缺失 | 截面最小值 | 待实现 | + +### 1.3 数学函数(无分组,逐元素计算) + +| 函数名 | 参数 | 状态 | 说明 | +|--------|------|------|------| +| `log(x)` | x: 表达式 | API 已定义 | 自然对数 | +| `log1p(x)` | x: 表达式 | 缺失 | log(1+x) | +| `exp(x)` | x: 表达式 | API 已定义 | 指数函数 | +| `sqrt(x)` | x: 表达式 | API 已定义 | 平方根 | +| `sign(x)` | x: 表达式 | API 已定义 | 符号函数 (-1/0/1) | +| `abs(x)` | x: 表达式 | API 已定义 | 绝对值 | +| `max_(x, y)` | x, y: 表达式 | API 已定义 | 逐元素最大值 | +| `min_(x, y)` | x, y: 表达式 | API 已定义 | 逐元素最小值 | +| `clip(x, lower, upper)` | x: 表达式, lower/upper: 边界 | API 已定义 | 数值裁剪 | +| `atan(x)` | x: 表达式 | 缺失 | 反正切函数 | + +### 1.4 统计函数(需要新增) + +| 函数名 | 类型 | 参数 | 说明 | +|--------|------|------|------| +| `ts_var(x, window)` | ts | x: 表达式, window: 窗口 | 滚动方差 | +| `ts_skew(x, window)` | ts | x: 表达式, window: 窗口 | 滚动偏度 | +| `ts_kurt(x, window)` | ts | x: 表达式, window: 窗口 | 滚动峰度 | +| `ts_pct_change(x, periods)` | ts | x: 表达式, periods: 周期 | 百分比变化 | +| `ts_ema(x, window)` | ts | x: 表达式, window: 窗口 | 指数移动平均 | +| `ts_sma(x, window)` | ts | x: 表达式, window: 窗口 | 简单移动平均 (ts_mean 别名) | + +### 1.5 TA-Lib 技术指标(用户可安装 TA-Lib) + +| 函数名 | 类型 | 参数 | 说明 | 优先级 | +|--------|------|------|------|--------| +| `ts_atr(high, low, close, window)` | ts | high/low/close: 表达式, window: 窗口 | 真实波动幅度 | 高 | +| `ts_obv(close, vol)` | ts | close/vol: 表达式 | 能量潮指标 | 高 | +| `ts_rsi(close, window)` | ts | close: 表达式, window: 窗口 | 相对强弱指数 | 高 | +| `ts_natr(high, low, close, window)` | ts | high/low/close: 表达式, window: 窗口 | 归一化 ATR | 中 | + +**说明**: 由于用户已确认可安装 TA-Lib,建议直接使用 `ta-lib` Python 包,并通过 adapter 模式封装到 DSL 中。 + +### 1.6 条件函数 + +| 函数名 | 参数 | 状态 | 说明 | +|--------|------|------|------| +| `if_(condition, true_val, false_val)` | condition: 布尔表达式 | API 已定义 | 条件选择 | +| `where(condition, true_val, false_val)` | 同上 | API 已定义 | if_ 的别名 | + +### 1.7 符号/数据字段 + +| 字段名 | 状态 | 数据来源 | 所属类别 | +|--------|------|----------|----------| +| `close` | 可用 | daily/pro_bar 表 | 价格 | +| `open` | 可用 | daily/pro_bar 表 | 价格 | +| `high` | 可用 | daily/pro_bar 表 | 价格 | +| `low` | 可用 | daily/pro_bar 表 | 价格 | +| `vol` | 可用 | daily/pro_bar 表 | 成交量 | +| `amount` | 可用 | daily/pro_bar 表 | 成交额 | +| `pre_close` | 可用 | daily/pro_bar 表 | 价格 | +| `change` | 可用 | daily/pro_bar 表 | 价格变化 | +| `pct_chg` | 可用 | daily/pro_bar 表 | 涨跌幅 | +| `turnover_rate` | 可用 | daily/pro_bar 表 | 换手率 | +| `volume_ratio` | 可用 | daily/pro_bar 表 | 量比 | + +### 1.8 支持的运算符 + +**算术运算符**: `+`, `-`, `*`, `/`, `**`, `//`, `%` +**比较运算符**: `==`, `!=`, `<`, `<=`, `>`, `>=` +**一元运算符**: `-`, `+`, `abs()` + +--- + +## 二、需要新增的数据表 + +### 2.1 筹码分布数据(cyq 表) + +**数据来源**: Tushare `cyq` API (VIP) +**数据频率**: 日频 +**同步方式**: 按股票 + +| 字段名 | 类型 | 说明 | 依赖因子数 | +|--------|------|------|------------| +| `cost_5pct` | DOUBLE | 5% 成本价 | ~50个 | +| `cost_15pct` | DOUBLE | 15% 成本价 | ~50个 | +| `cost_50pct` | DOUBLE | 50% 成本价 (中位数) | ~50个 | +| `cost_85pct` | DOUBLE | 85% 成本价 | ~50个 | +| `cost_95pct` | DOUBLE | 95% 成本价 | ~50个 | +| `weight_avg` | DOUBLE | 加权平均成本 | ~50个 | +| `winner_rate` | DOUBLE | 获利盘比例 | ~50个 | +| `his_high` | DOUBLE | 历史最高价 | ~10个 | +| `his_low` | DOUBLE | 历史最低价 | ~10个 | + +**依赖筹码数据的主要因子**: +- `cashflow_to_ev_factor` +- `chip_concentration_range` +- `profit_pressure` +- `underwater_resistance` +- `vol_amp_loss_20` +- `smallcap_concentration` +- `cat_golden_resonance` +- ...等 50+ 个 + +### 2.2 资金流向数据(moneyflow 表) + +**数据来源**: Tushare `moneyflow` API (VIP) +**数据频率**: 日频 +**同步方式**: 按股票 + +| 字段名 | 类型 | 说明 | 依赖因子数 | +|--------|------|------|------------| +| `buy_sm_vol` | DOUBLE | 小单买入量 | ~30个 | +| `sell_sm_vol` | DOUBLE | 小单卖出量 | ~30个 | +| `buy_lg_vol` | DOUBLE | 大单买入量 | ~30个 | +| `sell_lg_vol` | DOUBLE | 大单卖出量 | ~30个 | +| `buy_elg_vol` | DOUBLE | 特大单买入量 | ~30个 | +| `sell_elg_vol` | DOUBLE | 特大单卖出量 | ~30个 | +| `net_mf_vol` | DOUBLE | 净流入量 | ~20个 | + +**依赖资金流向数据的主要因子**: +- `lg_elg_net_buy_vol` +- `flow_lg_elg_intensity` +- `sm_net_buy_vol` +- `flow_divergence_ratio` +- `lg_flow_mom_corr_20_60` +- `cs_rank_net_lg_flow_val` +- ...等 30+ 个 + +### 2.3 财务数据(financial 表) + +**数据来源**: Tushare `income`/`balance_sheet`/`cashflow` API (VIP) +**数据频率**: 季度 +**同步方式**: 按股票,需 `asof_backward` join + +| 字段名 | 类型 | 说明 | 数据来源表 | +|--------|------|------|------------| +| `bps` | DOUBLE | 每股净资产 | income | +| `total_liab` | DOUBLE | 总负债 | balance_sheet | +| `money_cap` | DOUBLE | 货币资金 | balance_sheet | +| `n_cashflow_act` | DOUBLE | 经营活动现金流净额 | cashflow | +| `undist_profit_ps` | DOUBLE | 每股未分配利润 | income | +| `ocfps` | DOUBLE | 每股经营现金流 | income | +| `roa` | DOUBLE | 资产回报率 | income | +| `roe` | DOUBLE | 净资产收益率 | income | +| `total_mv` | DOUBLE | 总市值 | daily_basic | +| `circ_mv` | DOUBLE | 流通市值 | daily_basic | + +**依赖财务数据的主要因子**: +- `undist_profit_ps` +- `cashflow_to_ev_factor` +- `book_to_price_ratio` +- `cs_rank_size` + +### 2.4 行业分类数据 + +| 字段名 | 类型 | 说明 | 数据来源 | +|--------|------|------|----------| +| `cat_l2_code` | VARCHAR | 二级行业分类代码 | stock_basic | + +**用途**: `daily_industry_deviation`, `cs_rank_ind_adj_lg_flow` 等分组计算 + +--- + +## 三、因子表达式可实现性分类 + +### 3.1 完全可实现(使用现有功能) + +#### 技术指标因子 +| 因子名称 | 表达式 | 复杂度 | +|----------|--------|--------| +| `turnover_rate_mean_5` | `ts_mean(turnover_rate, 5)` | 低 | +| `bbi_ratio_factor` | `(ts_mean(close,3)+ts_mean(close,6)+ts_mean(close,12)+ts_mean(close,24))/4/close` | 低 | + +#### ARBR 因子 +| 因子名称 | 表达式 | 复杂度 | +|----------|--------|--------| +| `AR` | `ts_sum(high - open, N) / ts_sum(open - low, N) * 100` | 低 | +| `BR` | `ts_sum(max_(0, high - ts_delay(close,1)), N) / ts_sum(max_(0, ts_delay(close,1) - low), N) * 100` | 中 | +| `AR_BR` | `AR - BR` | 低 | + +#### 成交量因子 +| 因子名称 | 表达式 | 复杂度 | +|----------|--------|--------| +| `volume_change_rate` | `ts_mean(vol, 2) / ts_mean(vol, 10) - 1` | 低 | +| `turnover_deviation` | `(turnover_rate - ts_mean(turnover_rate, 3)) / ts_std(turnover_rate, 3)` | 低 | +| `vol_std_5` | `ts_std(ts_delta(vol), 5)` | 低 | + +#### 收益率因子 +| 因子名称 | 表达式 | 复杂度 | +|----------|--------|--------| +| `return_5` | `close / ts_delay(close, 5) - 1` | 低 | +| `return_20` | `close / ts_delay(close, 20) - 1` | 低 | +| `std_return_5` | `ts_std(ts_delta(close)/ts_delay(close), 5)` | 中 | +| `std_return_90` | `ts_std(ts_delta(close)/ts_delay(close), 90)` | 中 | + +#### 截面排序因子 +| 因子名称 | 表达式 | 复杂度 | +|----------|--------|--------| +| `cs_rank_volume_ratio` | `cs_rank(volume_ratio)` | 低 | +| `cs_rank_turnover_rate` | `cs_rank(turnover_rate)` | 低 | + +### 3.2 需要新增 ts_* 函数 + +| 因子名称 | 所需函数 | 表达式示例 | 优先级 | +|----------|----------|------------|--------| +| `variance_20` | `ts_var(x, window)` | `ts_var(pct_chg, 20)` | 高 | +| `return_skew` | `ts_skew(x, window)` | `ts_skew(pct_chg, 5)` | 高 | +| `return_kurtosis` | `ts_kurt(x, window)` | `ts_kurt(pct_chg, 5)` | 高 | +| `turnover_diff_skew_20` | `ts_skew(x, window)` | `ts_skew(ts_delta(turnover_rate), 20)` | 高 | +| `act_factor1` | `ts_ema(x, window)` | `atan((ts_ema(close,5)/ts_delay(ts_ema(close,5),1)-1)*100) * 57.3 / 50` | 高 | +| `act_factor2` | `ts_ema(x, window)` | `atan((ts_ema(close,13)/ts_delay(ts_ema(close,13),1)-1)*100) * 57.3 / 40` | 高 | +| `act_factor3` | `ts_ema(x, window)` | `atan((ts_ema(close,20)/ts_delay(ts_ema(close,20),1)-1)*100) * 57.3 / 21` | 高 | +| `act_factor4` | `ts_ema(x, window)` | `atan((ts_ema(close,60)/ts_delay(ts_ema(close,60),1)-1)*100) * 57.3 / 10` | 高 | +| `atr_14` | `ts_atr(h,l,c,w)` | `ts_atr(high, low, close, 14)` | 高 | +| `atr_6` | `ts_atr(h,l,c,w)` | `ts_atr(high, low, close, 6)` | 高 | +| `rsi_3` | `ts_rsi(x, window)` | `ts_rsi(close, 3)` | 高 | +| `obv` | `ts_obv(c, v)` | `ts_obv(close, vol)` | 中 | +| `maobv_6` | `ts_obv` + `ts_mean` | `ts_mean(ts_obv(close, vol), 6)` | 中 | + +### 3.3 需要新增 cs_* 函数 + +| 因子名称 | 所需函数 | 表达式示例 | 优先级 | +|----------|----------|------------|--------| +| `daily_positive_benchmark` | `cs_mean(x)` | `cs_mean(pct_chg[pct_chg > 0])` | 高 | +| `daily_industry_positive_benchmark` | `cs_mean(x)` by group | `cs_mean(pct_chg) by cat_l2_code` | 高 | +| `industry_avg_flow` | `cs_mean(x)` by group | `cs_mean(net_lg_flow_vol) by trade_date, cat_l2_code` | 中 | + +**注意**: 这些因子需要 **分组聚合** 能力,可能需要扩展 DSL 语法支持 `by` 子句,或通过 `cs_neutralize` 的 group 参数实现。 + +### 3.4 需要新增数据表 + +#### 依赖筹码数据(cyq 表)的因子 +| 因子名称 | 表达式 | 复杂度 | +|----------|--------|--------| +| `chip_concentration_range` | `(cost_95pct - cost_5pct) / (close + epsilon)` | 低 | +| `chip_skewness` | `(weight_avg - cost_50pct) / (cost_50pct + epsilon)` | 低 | +| `profit_pressure` | `winner_rate * 0.5 * ((close/cost_85pct-1) + (close/cost_95pct-1))` | 中 | +| `underwater_resistance` | `(1-winner_rate) * max_(0, cost_15pct-close) / (close+epsilon)` | 中 | +| `smallcap_concentration` | `(1 / log(circ_mv)) * (cost_85pct - cost_15pct)` | 中 | + +#### 依赖资金流向数据(moneyflow 表)的因子 +| 因子名称 | 表达式 | 复杂度 | +|----------|--------|--------| +| `lg_elg_net_buy_vol` | `(buy_lg_vol + buy_elg_vol - sell_lg_vol - sell_elg_vol)` | 低 | +| `flow_lg_elg_intensity` | `lg_elg_net_buy_vol / (vol + epsilon)` | 低 | +| `cs_rank_net_lg_flow_val` | `cs_rank(lg_elg_net_buy_vol * close)` | 低 | + +#### 依赖财务数据的因子 +| 因子名称 | 表达式 | 复杂度 | 特殊需求 | +|----------|--------|--------|----------| +| `book_to_price_ratio` | `bps / close` | 低 | 需 asof_backward join | +| `cashflow_to_ev_factor` | `n_cashflow_act / (total_mv*10000 + total_liab - money_cap)` | 高 | 需多表 join | + +### 3.5 需要复杂条件逻辑 + +| 因子名称 | 逻辑 | 实现方式 | +|----------|------|----------| +| `daily_deviation` | 按条件选择不同基准 | `if_(pct_chg > 0, pct_chg - pos_benchmark, if_(pct_chg < 0, pct_chg - neg_benchmark, 0))` | +| `cat_volume_breakout` | 布尔分类 | `if_(vol > ts_max(vol, 5), 1, 0)` | +| `cat_turnover_spike` | 阈值分类 | `if_(turnover_rate > ts_mean(turnover_rate,3) + 2*ts_std(turnover_rate,3), 1, 0)` | +| `cat_golden_resonance` | 多条件组合 | `(close > weight_avg) & (volume_ratio > 1.5) & (winner_rate > 0.7)` | + +--- + +## 四、实施计划(按优先级排序) + +### Phase 1: 核心 ts_* 函数(Week 1-2) + +**目标**: 实现最常用的 30+ 个因子 + +```python +# 1. 新增数学函数(逐元素,无分组) +def atan(x): ... +def log1p(x): ... + +# 2. 新增统计函数(ts_*,按 ts_code 分组) +def ts_var(x, window): ... # 滚动方差 +def ts_skew(x, window): ... # 滚动偏度 +def ts_kurt(x, window): ... # 滚动峰度 +def ts_pct_change(x, periods): ... # 百分比变化 + +# 3. 新增 EMA(ts_*,使用 Polars ewm_mean) +def ts_ema(x, window): + # 使用 Polars: pl.col(x).ewm_mean(span=window).over("ts_code") + pass + +# 4. 实现 ATR(基于现有函数组合) +def ts_atr(high, low, close, window): + tr1 = high - low + tr2 = abs(high - ts_delay(close, 1)) + tr3 = abs(low - ts_delay(close, 1)) + tr = max_(tr1, max_(tr2, tr3)) + return ts_mean(tr, window) +``` + +**完成后可实现的因子**: +- `variance_20` +- `return_skew`, `return_kurtosis` +- `turnover_diff_skew_20` +- `act_factor1-4` +- `atr_14`, `atr_6` +- `vol_adj_roc_20` + +### Phase 2: TA-Lib 集成(Week 3) + +**目标**: 支持 RSI、OBV 等复杂技术指标 + +```python +# 安装 TA-Lib +# pip install TA-Lib + +# 封装为 ts_* 函数 +def ts_rsi(close, window): + # 使用 talib.RSI,并添加 .over("ts_code") + pass + +def ts_obv(close, vol): + # 使用 talib.OBV,并添加 .over("ts_code") + pass +``` + +**完成后可实现的因子**: +- `rsi_3` +- `obv` +- `maobv_6` + +### Phase 3: 筹码分布数据同步(Week 4) + +**目标**: 实现 cyq 表同步,支持 50+ 个筹码相关因子 + +```python +class CyqSync(StockBasedSync): + table_name = "cyq" + TABLE_SCHEMA = { + "ts_code": "VARCHAR(16) NOT NULL", + "trade_date": "DATE NOT NULL", + "cost_5pct": "DOUBLE", + "cost_15pct": "DOUBLE", + "cost_50pct": "DOUBLE", + "cost_85pct": "DOUBLE", + "cost_95pct": "DOUBLE", + "weight_avg": "DOUBLE", + "winner_rate": "DOUBLE", + "his_high": "DOUBLE", + "his_low": "DOUBLE", + } + PRIMARY_KEY = ("ts_code", "trade_date") +``` + +**完成后可实现的因子**: +- `chip_concentration_range` +- `profit_pressure` +- `underwater_resistance` +- `smallcap_concentration` +- ...等 50+ 个 + +### Phase 4: 资金流向数据同步(Week 5) + +**目标**: 实现 moneyflow 表同步,支持 30+ 个资金相关因子 + +```python +class MoneyFlowSync(StockBasedSync): + table_name = "moneyflow" + TABLE_SCHEMA = { + "ts_code": "VARCHAR(16) NOT NULL", + "trade_date": "DATE NOT NULL", + "buy_sm_vol": "DOUBLE", + "sell_sm_vol": "DOUBLE", + "buy_lg_vol": "DOUBLE", + "sell_lg_vol": "DOUBLE", + "buy_elg_vol": "DOUBLE", + "sell_elg_vol": "DOUBLE", + "net_mf_vol": "DOUBLE", + } +``` + +**完成后可实现的因子**: +- `lg_elg_net_buy_vol` +- `flow_lg_elg_intensity` +- `cs_rank_net_lg_flow_val` +- ...等 30+ 个 + +### Phase 5: 分组聚合功能(Week 6) + +**目标**: 实现 cs_mean by group 等分组计算 + +```python +# 方式1: 扩展 cs_neutralize 的 group 参数 +def cs_neutralize(x, group=None): + if group: + return (x - x.mean().over(["trade_date", group])) + return (x - x.mean().over("trade_date")) + +# 方式2: 新增 cs_mean_by 函数 +def cs_mean_by(x, by): + return x.mean().over(["trade_date"] + by) +``` + +**完成后可实现的因子**: +- `daily_deviation` +- `daily_industry_deviation` +- `cs_rank_ind_adj_lg_flow` + +### Phase 6: 财务数据集成(Week 7) + +**目标**: 实现财务数据同步和 asof_backward join + +```python +# 使用 DataSpec 的 asof_backward 模式 +spec = DataSpec( + table="financial_income", + columns=["bps", "roe", "roa"], + join_type="asof_backward", + left_on="trade_date", + right_on="f_ann_date", +) +``` + +**完成后可实现的因子**: +- `book_to_price_ratio` +- `cashflow_to_ev_factor` +- `cs_rank_size` (需要 circ_mv) + +--- + +## 五、可实现性统计 + +### 按实现难度分类 + +| 类别 | 数量 | 状态 | 时间预估 | +|------|------|------|----------| +| 立即可实现 | ~40个 | 使用现有功能 | 0 周 | +| 需新增 ts_* 函数 | ~30个 | Phase 1-2 | 2-3 周 | +| 需新增 cs_* 函数 | ~10个 | Phase 5 | 1 周 | +| 需筹码数据 (cyq) | ~50个 | Phase 3 | 1 周 | +| 需资金数据 (moneyflow) | ~30个 | Phase 4 | 1 周 | +| 需财务数据 (financial) | ~20个 | Phase 6 | 1 周 | +| **总计** | **180+个** | | **6-7 周** | + +### 按数据源分类 + +| 数据源 | 依赖因子数 | 实现难度 | 优先级 | +|--------|------------|----------|--------| +| daily/pro_bar (已有) | ~40 | 低 | 高 | +| 纯技术指标 (ts_*) | ~30 | 中 | 高 | +| 筹码分布 (cyq) | ~50 | 中 | 中 | +| 资金流向 (moneyflow) | ~30 | 中 | 中 | +| 财务数据 (financial) | ~20 | 高 | 低 | +| 分组计算 (cs_* by group) | ~10 | 高 | 低 | + +--- + +## 六、立即可实现的因子示例 + +```python +from src.factors.api import close, open, high, low, vol, pct_chg, turnover_rate +from src.factors.api import ts_mean, ts_std, ts_max, ts_min, ts_delay, ts_delta, ts_corr, ts_sum +from src.factors.api import cs_rank, cs_zscore, cs_neutralize +from src.factors.api import log, sqrt, abs, sign, max_, min_, clip, if_ + +# 1. BBI 比率 +bbi = (ts_mean(close, 3) + ts_mean(close, 6) + ts_mean(close, 12) + ts_mean(close, 24)) / 4 +bbi_ratio_factor = bbi / close + +# 2. AR 因子 (N=26) +ar = ts_sum(high - open, 26) / ts_sum(open - low, 26) * 100 + +# 3. BR 因子 (N=26) +prev_close = ts_delay(close, 1) +br = ts_sum(max_(0, high - prev_close), 26) / ts_sum(max_(0, prev_close - low), 26) * 100 +ar_br = ar - br + +# 4. 成交量变化率 +volume_change_rate = ts_mean(vol, 2) / ts_mean(vol, 10) - 1 + +# 5. 换手率偏离度 +turnover_deviation = (turnover_rate - ts_mean(turnover_rate, 3)) / ts_std(turnover_rate, 3) + +# 6. 5日收益率 +return_5 = close / ts_delay(close, 5) - 1 + +# 7. 20日收益率 +return_20 = close / ts_delay(close, 20) - 1 + +# 8. Alpha 003 +alpha_003 = if_(high != low, (close - open) / (high - low), 0) + +# 9. Alpha 007 (等 Phase 1 后) +# alpha_007 = cs_rank(ts_corr(close, vol, 5)) + +# 10. 截面排名 +rank_close = cs_rank(close) +rank_volume = cs_rank(vol) + +# 11. Z-Score 标准化 +zscore_close = cs_zscore(close) + +# 12. 截面中性化 (Phase 5 后支持分组) +# neutralized = cs_neutralize(close, group="industry") +``` + +--- + +## 七、结论与建议 + +### 关键发现 + +1. **ts_* vs cs_* 区分清晰**: + - `ts_*` 函数按 `ts_code` 分组,用于时间序列计算 + - `cs_*` 函数按 `trade_date` 分组,用于截面计算 + - 数学函数(log, exp, atan 等)为逐元素计算,无分组 + +2. **当前框架已实现**: + - 9个核心 ts_* 函数(mean, std, max, min, sum, delay, delta, corr, cov) + - 3个 cs_* 函数(rank, zscore, neutralize) + - 完整的运算符重载系统 + +3. **主要缺口**: + - 统计函数: `ts_var`, `ts_skew`, `ts_kurt`, `ts_pct_change` + - 技术指标: `ts_ema`, `ts_atr`, `ts_rsi`, `ts_obv` + - 数学函数: `atan`, `log1p` + - 分组聚合: `cs_mean`, `cs_sum` by group + - 数据表: cyq, moneyflow, financial + +### 推荐实施路径 + +| 阶段 | 目标 | 时间 | 完成后可计算因子数 | +|------|------|------|-------------------| +| Phase 1 | ts_var, ts_skew, ts_kurt, ts_ema, atan | 1-2周 | +30个 | +| Phase 2 | TA-Lib 集成 (RSI, OBV) | 1周 | +5个 | +| Phase 3 | 筹码数据同步 (cyq) | 1周 | +50个 | +| Phase 4 | 资金流向同步 (moneyflow) | 1周 | +30个 | +| Phase 5 | 分组聚合功能 | 1周 | +10个 | +| Phase 6 | 财务数据集成 | 1周 | +20个 | +| **总计** | | **6-7周** | **180+个** | + +### 建议优先实现的因子(高 ROI) + +1. **BBI 比率**: 纯技术指标,无需新数据 +2. **AR/BR**: 经典情绪指标,无需新数据 +3. **换手率偏离度**: 流动性因子,无需新数据 +4. **筹码集中度**: 需 cyq 数据,但实现简单 +5. **大单净流入**: 需 moneyflow 数据,实现简单 + +通过以上步骤,预计在 **6-7 周** 内可实现文档中 **90% 以上的因子表达式**。 diff --git a/src/factors/api.py b/src/factors/api.py index 068c4c6..83435d7 100644 --- a/src/factors/api.py +++ b/src/factors/api.py @@ -347,6 +347,30 @@ def sign(x: Union[Node, str]) -> FunctionNode: return FunctionNode("sign", x) +def cos(x: Union[Node, str]) -> FunctionNode: + """余弦函数。 + + Args: + x: 输入因子表达式或字段名字符串 + + Returns: + FunctionNode: 函数调用节点 + """ + return FunctionNode("cos", x) + + +def sin(x: Union[Node, str]) -> FunctionNode: + """正弦函数。 + + Args: + x: 输入因子表达式或字段名字符串 + + Returns: + FunctionNode: 函数调用节点 + """ + return FunctionNode("sin", x) + + def abs(x: Union[Node, str]) -> FunctionNode: """绝对值。 diff --git a/src/factors/decorators.py b/src/factors/decorators.py new file mode 100644 index 0000000..d1d0ee3 --- /dev/null +++ b/src/factors/decorators.py @@ -0,0 +1,57 @@ +"""函数装饰器 - 用于标记因子函数类型并自动注入 over 处理。 + +提供三种装饰器: +- @time_series: 时序因子,自动添加 .over("ts_code") +- @cross_section: 截面因子,自动添加 .over("trade_date") +- @element_wise: 元素级运算,不添加 over +""" + +from functools import wraps +from typing import Callable + + +def time_series(func: Callable) -> Callable: + """标记为时序因子,自动添加 .over('ts_code')。 + + 用于 ts_* 函数,如 ts_mean, ts_std, ts_corr 等。 + 每个时序计算都按股票代码分组,防止跨股票串数据。 + """ + + @wraps(func) + def wrapper(self, node): + expr = func(self, node) + return expr.over("ts_code") + + wrapper._factor_type = "ts" + return wrapper + + +def cross_section(func: Callable) -> Callable: + """标记为截面因子,自动添加 .over('trade_date')。 + + 用于 cs_* 函数,如 cs_rank, cs_zscore 等。 + 每个截面计算都按交易日分组,在同一天的所有股票间计算。 + """ + + @wraps(func) + def wrapper(self, node): + expr = func(self, node) + return expr.over("trade_date") + + wrapper._factor_type = "cs" + return wrapper + + +def element_wise(func: Callable) -> Callable: + """标记为元素级运算,不添加 over。 + + 用于数学函数,如 log, exp, sqrt, cos, sin 等。 + 这些函数对每个元素独立计算,不需要分组。 + """ + + @wraps(func) + def wrapper(self, node): + return func(self, node) + + wrapper._factor_type = "element" + return wrapper diff --git a/src/factors/translator.py b/src/factors/translator.py index df3a2ac..3bc20a5 100644 --- a/src/factors/translator.py +++ b/src/factors/translator.py @@ -8,6 +8,7 @@ from typing import Any, Callable, Dict import polars as pl +from src.factors.decorators import cross_section, element_wise, time_series from src.factors.dsl import ( BinaryOpNode, Constant, @@ -58,6 +59,14 @@ class PolarsTranslator: self.register_handler("cs_zscore", self._handle_cs_zscore) self.register_handler("cs_neutral", self._handle_cs_neutral) + # 元素级数学函数 (element_wise) + self.register_handler("log", self._handle_log) + self.register_handler("exp", self._handle_exp) + self.register_handler("sqrt", self._handle_sqrt) + self.register_handler("sign", self._handle_sign) + self.register_handler("cos", self._handle_cos) + self.register_handler("sin", self._handle_sin) + def register_handler( self, func_name: str, handler: Callable[[FunctionNode], pl.Expr] ) -> None: @@ -201,109 +210,172 @@ class PolarsTranslator: ) # ==================== 时序因子处理器 (ts_*) ==================== - # 所有时序因子强制注入 over("ts_code") 防串表 + # 所有时序因子使用 @time_series 装饰器自动注入 over("ts_code") 防串表 + @time_series def _handle_ts_mean(self, node: FunctionNode) -> pl.Expr: - """处理 ts_mean(close, window) -> rolling_mean(window).over(ts_code)。""" + """处理 ts_mean(close, window) -> rolling_mean(window)。""" if len(node.args) != 2: raise ValueError("ts_mean 需要 2 个参数: (expr, window)") expr = self.translate(node.args[0]) window = self._extract_window(node.args[1]) - return expr.rolling_mean(window_size=window).over("ts_code") + return expr.rolling_mean(window_size=window) + @time_series def _handle_ts_sum(self, node: FunctionNode) -> pl.Expr: - """处理 ts_sum(close, window) -> rolling_sum(window).over(ts_code)。""" + """处理 ts_sum(close, window) -> rolling_sum(window)。""" if len(node.args) != 2: raise ValueError("ts_sum 需要 2 个参数: (expr, window)") expr = self.translate(node.args[0]) window = self._extract_window(node.args[1]) - return expr.rolling_sum(window_size=window).over("ts_code") + return expr.rolling_sum(window_size=window) + @time_series def _handle_ts_std(self, node: FunctionNode) -> pl.Expr: - """处理 ts_std(close, window) -> rolling_std(window).over(ts_code)。""" + """处理 ts_std(close, window) -> rolling_std(window)。""" if len(node.args) != 2: raise ValueError("ts_std 需要 2 个参数: (expr, window)") expr = self.translate(node.args[0]) window = self._extract_window(node.args[1]) - return expr.rolling_std(window_size=window).over("ts_code") + return expr.rolling_std(window_size=window) + @time_series def _handle_ts_max(self, node: FunctionNode) -> pl.Expr: - """处理 ts_max(close, window) -> rolling_max(window).over(ts_code)。""" + """处理 ts_max(close, window) -> rolling_max(window)。""" if len(node.args) != 2: raise ValueError("ts_max 需要 2 个参数: (expr, window)") expr = self.translate(node.args[0]) window = self._extract_window(node.args[1]) - return expr.rolling_max(window_size=window).over("ts_code") + return expr.rolling_max(window_size=window) + @time_series def _handle_ts_min(self, node: FunctionNode) -> pl.Expr: - """处理 ts_min(close, window) -> rolling_min(window).over(ts_code)。""" + """处理 ts_min(close, window) -> rolling_min(window)。""" if len(node.args) != 2: raise ValueError("ts_min 需要 2 个参数: (expr, window)") expr = self.translate(node.args[0]) window = self._extract_window(node.args[1]) - return expr.rolling_min(window_size=window).over("ts_code") + return expr.rolling_min(window_size=window) + @time_series def _handle_ts_delay(self, node: FunctionNode) -> pl.Expr: - """处理 ts_delay(close, n) -> shift(n).over(ts_code)。""" + """处理 ts_delay(close, n) -> shift(n)。""" if len(node.args) != 2: raise ValueError("ts_delay 需要 2 个参数: (expr, n)") expr = self.translate(node.args[0]) n = self._extract_window(node.args[1]) - return expr.shift(n).over("ts_code") + return expr.shift(n) + @time_series def _handle_ts_delta(self, node: FunctionNode) -> pl.Expr: - """处理 ts_delta(close, n) -> (expr - shift(n)).over(ts_code)。""" + """处理 ts_delta(close, n) -> (expr - shift(n))。""" if len(node.args) != 2: raise ValueError("ts_delta 需要 2 个参数: (expr, n)") expr = self.translate(node.args[0]) n = self._extract_window(node.args[1]) - return (expr - expr.shift(n)).over("ts_code") + return expr - expr.shift(n) + @time_series def _handle_ts_corr(self, node: FunctionNode) -> pl.Expr: - """处理 ts_corr(x, y, window) -> rolling_corr(y, window).over(ts_code)。""" + """处理 ts_corr(x, y, window) -> rolling_corr(y, window)。""" if len(node.args) != 3: raise ValueError("ts_corr 需要 3 个参数: (x, y, window)") x = self.translate(node.args[0]) y = self.translate(node.args[1]) window = self._extract_window(node.args[2]) - return x.rolling_corr(y, window_size=window).over("ts_code") + return x.rolling_corr(y, window_size=window) + @time_series def _handle_ts_cov(self, node: FunctionNode) -> pl.Expr: - """处理 ts_cov(x, y, window) -> rolling_cov(y, window).over(ts_code)。""" + """处理 ts_cov(x, y, window) -> rolling_cov(y, window)。""" if len(node.args) != 3: raise ValueError("ts_cov 需要 3 个参数: (x, y, window)") x = self.translate(node.args[0]) y = self.translate(node.args[1]) window = self._extract_window(node.args[2]) - return x.rolling_cov(y, window_size=window).over("ts_code") + return x.rolling_cov(y, window_size=window) # ==================== 截面因子处理器 (cs_*) ==================== - # 所有截面因子强制注入 over("trade_date") 防串表 + # 所有截面因子使用 @cross_section 装饰器自动注入 over("trade_date") 防串表 + @cross_section def _handle_cs_rank(self, node: FunctionNode) -> pl.Expr: - """处理 cs_rank(expr) -> rank()/count().over(trade_date)。 + """处理 cs_rank(expr) -> rank()/count()。 将排名归一化到 [0, 1] 区间。 """ if len(node.args) != 1: raise ValueError("cs_rank 需要 1 个参数: (expr)") expr = self.translate(node.args[0]) - return (expr.rank() / expr.count()).over("trade_date") + return expr.rank() / expr.count() + @cross_section def _handle_cs_zscore(self, node: FunctionNode) -> pl.Expr: - """处理 cs_zscore(expr) -> (expr - mean())/std().over(trade_date)。""" + """处理 cs_zscore(expr) -> (expr - mean())/std()。""" if len(node.args) != 1: raise ValueError("cs_zscore 需要 1 个参数: (expr)") expr = self.translate(node.args[0]) - return ((expr - expr.mean()) / expr.std()).over("trade_date") + return (expr - expr.mean()) / expr.std() + @cross_section def _handle_cs_neutral(self, node: FunctionNode) -> pl.Expr: """处理 cs_neutral(expr, group) -> 分组中性化。""" if len(node.args) not in [1, 2]: raise ValueError("cs_neutral 需要 1-2 个参数: (expr, [group_col])") expr = self.translate(node.args[0]) # 简单实现:减去截面均值(可在未来扩展为分组中性化) - return (expr - expr.mean()).over("trade_date") + return expr - expr.mean() + + # ==================== 元素级数学函数 (element_wise) ==================== + # 这些函数对每个元素独立计算,不添加 over + + @element_wise + def _handle_log(self, node: FunctionNode) -> pl.Expr: + """处理 log(expr) -> 自然对数。""" + if len(node.args) != 1: + raise ValueError("log 需要 1 个参数: (expr)") + expr = self.translate(node.args[0]) + return expr.log() + + @element_wise + def _handle_exp(self, node: FunctionNode) -> pl.Expr: + """处理 exp(expr) -> 指数函数。""" + if len(node.args) != 1: + raise ValueError("exp 需要 1 个参数: (expr)") + expr = self.translate(node.args[0]) + return expr.exp() + + @element_wise + def _handle_sqrt(self, node: FunctionNode) -> pl.Expr: + """处理 sqrt(expr) -> 平方根。""" + if len(node.args) != 1: + raise ValueError("sqrt 需要 1 个参数: (expr)") + expr = self.translate(node.args[0]) + return expr.sqrt() + + @element_wise + def _handle_sign(self, node: FunctionNode) -> pl.Expr: + """处理 sign(expr) -> 符号函数。""" + if len(node.args) != 1: + raise ValueError("sign 需要 1 个参数: (expr)") + expr = self.translate(node.args[0]) + return expr.sign() + + @element_wise + def _handle_cos(self, node: FunctionNode) -> pl.Expr: + """处理 cos(expr) -> 余弦函数。""" + if len(node.args) != 1: + raise ValueError("cos 需要 1 个参数: (expr)") + expr = self.translate(node.args[0]) + return expr.cos() + + @element_wise + def _handle_sin(self, node: FunctionNode) -> pl.Expr: + """处理 sin(expr) -> 正弦函数。""" + if len(node.args) != 1: + raise ValueError("sin 需要 1 个参数: (expr)") + expr = self.translate(node.args[0]) + return expr.sin() # ==================== 辅助方法 ====================