{ "cells": [ { "metadata": {}, "cell_type": "markdown", "source": [ "# Learn-to-Rank 排序学习训练流程\n", "#\n", "本 Notebook 实现基于 LightGBM LambdaRank 的排序学习训练,用于股票排序任务。\n", "#\n", "## 核心特点\n", "#\n", "1. **Label 转换**: 将 `future_return_5` 按每日进行 20 分位数划分(qcut)\n", "2. **排序学习**: 使用 LambdaRank 目标函数,学习每日股票排序\n", "3. **NDCG 评估**: 使用 NDCG@1/5/10/20 评估排序质量\n", "4. **策略回测**: 基于排序分数构建 Top-k 选股策略" ] }, { "metadata": {}, "cell_type": "markdown", "source": "## 1. 导入依赖" }, { "metadata": { "ExecuteTime": { "end_time": "2026-03-13T18:10:00.178020Z", "start_time": "2026-03-13T18:09:58.791253Z" } }, "cell_type": "code", "source": [ "import os\n", "from datetime import datetime\n", "from typing import List, Tuple, Optional\n", "\n", "import numpy as np\n", "import polars as pl\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "from sklearn.metrics import ndcg_score\n", "\n", "from src.factors import FactorEngine\n", "from src.training import (\n", " DateSplitter,\n", " STFilter,\n", " StockPoolManager,\n", " Trainer,\n", " Winsorizer,\n", " NullFiller,\n", " StandardScaler,\n", " check_data_quality,\n", ")\n", "from src.training.components.models import LightGBMLambdaRankModel\n", "from src.training.config import TrainingConfig\n", "\n" ], "outputs": [], "execution_count": 1 }, { "metadata": {}, "cell_type": "markdown", "source": "## 2. 辅助函数" }, { "metadata": { "ExecuteTime": { "end_time": "2026-03-13T18:10:00.209449Z", "start_time": "2026-03-13T18:10:00.197996Z" } }, "cell_type": "code", "source": [ "def register_factors(\n", " engine: FactorEngine,\n", " selected_factors: List[str],\n", " factor_definitions: dict,\n", " label_factor: dict,\n", ") -> List[str]:\n", " \"\"\"注册因子(selected_factors 从 metadata 查询,factor_definitions 用 DSL 表达式注册)\"\"\"\n", " print(\"=\" * 80)\n", " print(\"注册因子\")\n", " print(\"=\" * 80)\n", "\n", " # 注册 SELECTED_FACTORS 中的因子(已在 metadata 中)\n", " print(\"\\n注册特征因子(从 metadata):\")\n", " for name in selected_factors:\n", " engine.add_factor(name)\n", " print(f\" - {name}\")\n", "\n", " # 注册 FACTOR_DEFINITIONS 中的因子(通过表达式,尚未在 metadata 中)\n", " print(\"\\n注册特征因子(表达式):\")\n", " for name, expr in factor_definitions.items():\n", " engine.add_factor(name, expr)\n", " print(f\" - {name}: {expr}\")\n", "\n", " # 注册 label 因子(通过表达式)\n", " print(\"\\n注册 Label 因子(表达式):\")\n", " for name, expr in label_factor.items():\n", " engine.add_factor(name, expr)\n", " print(f\" - {name}: {expr}\")\n", "\n", " # 特征列 = SELECTED_FACTORS + FACTOR_DEFINITIONS 的 keys\n", " feature_cols = selected_factors + list(factor_definitions.keys())\n", "\n", " print(f\"\\n特征因子数: {len(feature_cols)}\")\n", " print(f\" - 来自 metadata: {len(selected_factors)}\")\n", " print(f\" - 来自表达式: {len(factor_definitions)}\")\n", " print(f\"Label: {list(label_factor.keys())[0]}\")\n", " print(f\"已注册因子总数: {len(engine.list_registered())}\")\n", "\n", " return feature_cols\n", "\n", "\n", "def prepare_data(\n", " engine: FactorEngine,\n", " feature_cols: List[str],\n", " start_date: str,\n", " end_date: str,\n", ") -> pl.DataFrame:\n", " \"\"\"准备数据\"\"\"\n", " print(\"\\n\" + \"=\" * 80)\n", " print(\"准备数据\")\n", " print(\"=\" * 80)\n", "\n", " # 计算因子(全市场数据)\n", " print(f\"\\n计算因子: {start_date} - {end_date}\")\n", " factor_names = feature_cols + [LABEL_NAME] # 包含 label\n", "\n", " data = engine.compute(\n", " factor_names=factor_names,\n", " start_date=start_date,\n", " end_date=end_date,\n", " )\n", "\n", " print(f\"数据形状: {data.shape}\")\n", " print(f\"数据列: {data.columns}\")\n", " print(f\"\\n前5行预览:\")\n", " print(data.head())\n", "\n", " return data\n", "\n", "\n", "def prepare_ranking_data(\n", " df: pl.DataFrame,\n", " label_col: str = \"future_return_5\",\n", " date_col: str = \"trade_date\",\n", " n_quantiles: int = 20,\n", ") -> Tuple[pl.DataFrame, str]:\n", " \"\"\"准备排序学习数据\n", "\n", " 将连续 label 转换为分位数标签,用于排序学习任务。\n", "\n", " Args:\n", " df: 原始数据\n", " label_col: 原始标签列名\n", " date_col: 日期列名\n", " n_quantiles: 分位数数量\n", "\n", " Returns:\n", " (处理后的 DataFrame, 新的标签列名)\n", " \"\"\"\n", " print(\"\\n\" + \"=\" * 80)\n", " print(f\"准备排序学习数据(将 {label_col} 转换为 {n_quantiles} 分位数标签)\")\n", " print(\"=\" * 80)\n", "\n", " # 新的标签列名\n", " rank_col = f\"{label_col}_rank\"\n", "\n", " # 按日期分组进行分位数划分\n", " # 使用 rank 生成 0, 1, 2, ..., n_quantiles-1 的标签\n", " # 方法: 计算每天内的排名,然后映射到 n_quantiles 个分位数组\n", " df_ranked = (\n", " df.with_columns(\n", " # 计算每天内的排名 (1-based)\n", " pl.col(label_col).rank(method=\"min\").over(date_col).alias(\"_rank\")\n", " )\n", " .with_columns(\n", " # 将排名转换为分位数标签 (0 to n_quantiles-1)\n", " ((pl.col(\"_rank\") - 1) / pl.len().over(date_col) * n_quantiles)\n", " .floor()\n", " .cast(pl.Int64)\n", " .clip(0, n_quantiles - 1)\n", " .alias(rank_col)\n", " )\n", " .drop(\"_rank\")\n", " )\n", "\n", " # 检查转换结果\n", " print(f\"\\n原始 {label_col} 统计:\")\n", " print(df_ranked[label_col].describe())\n", "\n", " print(f\"\\n转换后 {rank_col} 统计:\")\n", " print(df_ranked[rank_col].describe())\n", "\n", " # 检查每日样本分布\n", " print(f\"\\n每日样本数统计:\")\n", " daily_counts = df_ranked.group_by(date_col).agg(pl.count().alias(\"count\"))\n", " print(daily_counts[\"count\"].describe())\n", "\n", " # 检查分位数分布(应该是均匀的)\n", " print(f\"\\n分位数标签分布:\")\n", " rank_dist = df_ranked[rank_col].value_counts().sort(rank_col)\n", " print(rank_dist)\n", "\n", " return df_ranked, rank_col\n", "\n", "\n", "def compute_group_array(df: pl.DataFrame, date_col: str = \"trade_date\") -> np.ndarray:\n", " \"\"\"计算 group 数组用于 LambdaRank\n", "\n", " 每个日期作为一个 query,group 数组表示每个 query 的样本数。\n", "\n", " Args:\n", " df: 数据框\n", " date_col: 日期列名\n", "\n", " Returns:\n", " group 数组\n", " \"\"\"\n", " group_counts = df.group_by(date_col, maintain_order=True).agg(\n", " pl.count().alias(\"count\")\n", " )\n", " return group_counts[\"count\"].to_numpy()\n", "\n", "\n", "def evaluate_ndcg_at_k(\n", " y_true: np.ndarray,\n", " y_pred: np.ndarray,\n", " group: np.ndarray,\n", " k_list: List[int] = [1, 5, 10, 20],\n", ") -> dict:\n", " \"\"\"计算 NDCG@k 指标\n", "\n", " Args:\n", " y_true: 真实标签\n", " y_pred: 预测分数\n", " group: 分组数组\n", " k_list: 要计算的 k 值列表\n", "\n", " Returns:\n", " NDCG 指标字典\n", " \"\"\"\n", " results = {}\n", "\n", " # 按 group 拆分数据\n", " start_idx = 0\n", " y_true_groups = []\n", " y_pred_groups = []\n", "\n", " for group_size in group:\n", " end_idx = start_idx + group_size\n", " y_true_groups.append(y_true[start_idx:end_idx])\n", " y_pred_groups.append(y_pred[start_idx:end_idx])\n", " start_idx = end_idx\n", "\n", " # 计算每个 k 值的平均 NDCG\n", " for k in k_list:\n", " ndcg_scores = []\n", " for yt, yp in zip(y_true_groups, y_pred_groups):\n", " if len(yt) > 1:\n", " try:\n", " score = ndcg_score([yt], [yp], k=k)\n", " ndcg_scores.append(score)\n", " except ValueError:\n", " # 标签都相同,无法计算\n", " pass\n", "\n", " results[f\"ndcg@{k}\"] = np.mean(ndcg_scores) if ndcg_scores else 0.0\n", "\n", " return results\n", "\n" ], "outputs": [], "execution_count": 2 }, { "metadata": {}, "cell_type": "markdown", "source": [ "## 3. 配置参数\n", "#\n", "### 3.1 因子定义" ] }, { "metadata": { "ExecuteTime": { "end_time": "2026-03-13T18:10:00.241454Z", "start_time": "2026-03-13T18:10:00.237620Z" } }, "cell_type": "code", "source": [ "# 特征因子定义字典(复用 regression.ipynb 的因子定义)\n", "LABEL_NAME = \"future_return_5_rank\"\n", "\n", "# 当前选择的因子列表(从 FACTOR_DEFINITIONS 中选择要使用的因子)\n", "SELECTED_FACTORS = [\n", " # ================= 1. 价格、趋势与路径依赖 =================\n", " \"ma_5\",\n", " \"ma_20\",\n", " \"ma_ratio_5_20\",\n", " \"bias_10\",\n", " \"high_low_ratio\",\n", " \"bbi_ratio\",\n", " \"return_5\",\n", " \"return_20\",\n", " \"kaufman_ER_20\",\n", " \"mom_acceleration_10_20\",\n", " \"drawdown_from_high_60\",\n", " \"up_days_ratio_20\",\n", " # ================= 2. 波动率、风险调整与高阶矩 =================\n", " \"volatility_5\",\n", " \"volatility_20\",\n", " \"volatility_ratio\",\n", " \"std_return_20\",\n", " \"sharpe_ratio_20\",\n", " \"min_ret_20\",\n", " \"volatility_squeeze_5_60\",\n", " # ================= 3. 日内微观结构与异象 =================\n", " \"overnight_intraday_diff\",\n", " \"upper_shadow_ratio\",\n", " \"capital_retention_20\",\n", " \"max_ret_20\",\n", " # ================= 4. 量能、流动性与量价背离 =================\n", " \"volume_ratio_5_20\",\n", " \"turnover_rate_mean_5\",\n", " \"turnover_deviation\",\n", " \"amihud_illiq_20\",\n", " \"turnover_cv_20\",\n", " \"pv_corr_20\",\n", " \"close_vwap_deviation\",\n", " # ================= 5. 基本面财务特征 =================\n", " \"roe\",\n", " \"roa\",\n", " \"profit_margin\",\n", " \"debt_to_equity\",\n", " \"current_ratio\",\n", " \"net_profit_yoy\",\n", " \"revenue_yoy\",\n", " \"healthy_expansion_velocity\",\n", " # ================= 6. 基本面估值与截面动量共振 =================\n", " \"EP\",\n", " \"BP\",\n", " \"CP\",\n", " \"market_cap_rank\",\n", " \"turnover_rank\",\n", " \"return_5_rank\",\n", " \"EP_rank\",\n", " \"pe_expansion_trend\",\n", " \"value_price_divergence\",\n", " \"active_market_cap\",\n", " \"ebit_rank\",\n", "]\n", "\n", "# 因子定义字典(完整因子库)\n", "FACTOR_DEFINITIONS = {\n", " # \"turnover_rate_volatility\": \"ts_std(log(turnover_rate), 20)\"\n", "}\n", "\n", "# Label 因子定义(不参与训练,用于计算目标)\n", "LABEL_FACTOR = {\n", " LABEL_NAME: \"(ts_delay(close, -5) / ts_delay(open, -1)) - 1\",\n", "}" ], "outputs": [], "execution_count": 3 }, { "metadata": {}, "cell_type": "markdown", "source": "### 3.2 训练参数配置" }, { "metadata": { "ExecuteTime": { "end_time": "2026-03-13T18:10:00.257506Z", "start_time": "2026-03-13T18:10:00.253934Z" } }, "cell_type": "code", "source": [ "# 日期范围配置(正确的 train/val/test 三分法)\n", "TRAIN_START = \"20200101\"\n", "TRAIN_END = \"20231231\"\n", "VAL_START = \"20240101\"\n", "VAL_END = \"20241231\"\n", "TEST_START = \"20250101\"\n", "TEST_END = \"20251231\"\n", "\n", "\n", "# 分位数配置\n", "N_QUANTILES = 20 # 将 label 分为 20 组\n", "\n", "# LambdaRank 模型参数配置\n", "MODEL_PARAMS = {\n", " \"objective\": \"lambdarank\",\n", " \"metric\": \"ndcg\",\n", " \"ndcg_at\": 10, # 评估 NDCG@k\n", " \"learning_rate\": 0.01,\n", " \"num_leaves\": 31,\n", " \"max_depth\": 4,\n", " \"min_data_in_leaf\": 20,\n", " \"n_estimators\": 2000,\n", " \"early_stopping_round\": 300,\n", " \"subsample\": 0.8,\n", " \"colsample_bytree\": 0.8,\n", " \"reg_alpha\": 0.1,\n", " \"reg_lambda\": 1.0,\n", " \"verbose\": -1,\n", " \"random_state\": 42,\n", " \"lambdarank_truncation_level\": 10,\n", " \"label_gain\": [i for i in range(1, N_QUANTILES + 1)],\n", "}\n", "\n", "\n", "# 股票池筛选函数\n", "def stock_pool_filter(df: pl.DataFrame) -> pl.Series:\n", " \"\"\"股票池筛选函数(单日数据)\n", "\n", " 筛选条件:\n", " 1. 排除创业板(代码以 300 开头)\n", " 2. 排除科创板(代码以 688 开头)\n", " 3. 排除北交所(代码以 8、9 或 4 开头)\n", " 4. 选取当日市值最小的500只股票\n", " \"\"\"\n", " code_filter = (\n", " ~df[\"ts_code\"].str.starts_with(\"30\")\n", " & ~df[\"ts_code\"].str.starts_with(\"68\")\n", " & ~df[\"ts_code\"].str.starts_with(\"8\")\n", " & ~df[\"ts_code\"].str.starts_with(\"9\")\n", " & ~df[\"ts_code\"].str.starts_with(\"4\")\n", " )\n", "\n", " valid_df = df.filter(code_filter)\n", " n = min(1000, len(valid_df))\n", " small_cap_codes = valid_df.sort(\"total_mv\").head(n)[\"ts_code\"]\n", "\n", " return df[\"ts_code\"].is_in(small_cap_codes)\n", "\n", "\n", "STOCK_FILTER_REQUIRED_COLUMNS = [\"total_mv\"]\n", "\n", "# 输出配置\n", "OUTPUT_DIR = \"output\"\n", "SAVE_PREDICTIONS = True\n", "PERSIST_MODEL = False\n", "\n", "# Top N 配置:每日推荐股票数量\n", "TOP_N = 5 # 可调整为 10, 20 等" ], "outputs": [], "execution_count": 4 }, { "metadata": {}, "cell_type": "markdown", "source": "## 4. 训练流程" }, { "metadata": { "ExecuteTime": { "end_time": "2026-03-13T18:10:11.844363Z", "start_time": "2026-03-13T18:10:00.265162Z" } }, "cell_type": "code", "source": [ "print(\"\\n\" + \"=\" * 80)\n", "print(\"LightGBM LambdaRank 排序学习训练\")\n", "print(\"=\" * 80)\n", "\n", "# 1. 创建 FactorEngine(启用 metadata 功能)\n", "print(\"\\n[1] 创建 FactorEngine\")\n", "engine = FactorEngine()\n", "\n", "# 2. 使用 metadata 定义因子\n", "print(\"\\n[2] 定义因子(从 metadata 注册)\")\n", "feature_cols = register_factors(\n", " engine, SELECTED_FACTORS, FACTOR_DEFINITIONS, LABEL_FACTOR\n", ")\n", "\n", "# 3. 准备数据\n", "print(\"\\n[3] 准备数据\")\n", "data = prepare_data(\n", " engine=engine,\n", " feature_cols=feature_cols,\n", " start_date=TRAIN_START,\n", " end_date=TEST_END,\n", ")\n", "\n", "# 4. 转换为排序学习格式(分位数标签)\n", "print(\"\\n[4] 转换为排序学习格式\")\n", "data, target_col = prepare_ranking_data(\n", " df=data,\n", " label_col=LABEL_NAME,\n", " n_quantiles=N_QUANTILES,\n", ")\n", "\n", "# 5. 打印配置信息\n", "print(f\"\\n[配置] 训练期: {TRAIN_START} - {TRAIN_END}\")\n", "print(f\"[配置] 验证期: {VAL_START} - {VAL_END}\")\n", "print(f\"[配置] 测试期: {TEST_START} - {TEST_END}\")\n", "print(f\"[配置] 特征数: {len(feature_cols)}\")\n", "print(f\"[配置] 目标变量: {target_col}({N_QUANTILES}分位数)\")\n", "\n", "# 6. 创建排序学习模型\n", "model = LightGBMLambdaRankModel(params=MODEL_PARAMS)\n", "\n", "# 7. 创建数据处理器(使用函数返回的完整特征列表)\n", "processors = [\n", " NullFiller(feature_cols=feature_cols, strategy=\"mean\"),\n", " Winsorizer(feature_cols=feature_cols, lower=0.01, upper=0.99),\n", " StandardScaler(feature_cols=feature_cols),\n", "]\n", "\n", "# 8. 创建数据划分器\n", "splitter = DateSplitter(\n", " train_start=TRAIN_START,\n", " train_end=TRAIN_END,\n", " val_start=VAL_START,\n", " val_end=VAL_END,\n", " test_start=TEST_START,\n", " test_end=TEST_END,\n", ")\n", "\n", "# 9. 创建股票池管理器\n", "pool_manager = StockPoolManager(\n", " filter_func=stock_pool_filter,\n", " required_columns=STOCK_FILTER_REQUIRED_COLUMNS,\n", " data_router=engine.router,\n", ")\n", "\n", "# 10. 创建 ST 过滤器\n", "st_filter = STFilter(data_router=engine.router)\n", "\n", "# 11. 创建训练器\n", "trainer = Trainer(\n", " model=model,\n", " pool_manager=pool_manager,\n", " processors=processors,\n", " filters=[st_filter],\n", " splitter=splitter,\n", " target_col=target_col,\n", " feature_cols=feature_cols,\n", " persist_model=PERSIST_MODEL,\n", ")" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "================================================================================\n", "LightGBM LambdaRank 排序学习训练\n", "================================================================================\n", "\n", "[1] 创建 FactorEngine\n", "\n", "[2] 定义因子(从 metadata 注册)\n", "================================================================================\n", "注册因子\n", "================================================================================\n", "\n", "注册特征因子(从 metadata):\n", " - ma_5\n", " - ma_20\n", " - ma_ratio_5_20\n", " - bias_10\n", " - high_low_ratio\n", " - bbi_ratio\n", " - return_5\n", " - return_20\n", " - kaufman_ER_20\n", " - mom_acceleration_10_20\n", " - drawdown_from_high_60\n", " - up_days_ratio_20\n", " - volatility_5\n", " - volatility_20\n", " - volatility_ratio\n", " - std_return_20\n", " - sharpe_ratio_20\n", " - min_ret_20\n", " - volatility_squeeze_5_60\n", " - overnight_intraday_diff\n", " - upper_shadow_ratio\n", " - capital_retention_20\n", " - max_ret_20\n", " - volume_ratio_5_20\n", " - turnover_rate_mean_5\n", " - turnover_deviation\n", " - amihud_illiq_20\n", " - turnover_cv_20\n", " - pv_corr_20\n", " - close_vwap_deviation\n", " - roe\n", " - roa\n", " - profit_margin\n", " - debt_to_equity\n", " - current_ratio\n", " - net_profit_yoy\n", " - revenue_yoy\n", " - healthy_expansion_velocity\n", " - EP\n", " - BP\n", " - CP\n", " - market_cap_rank\n", " - turnover_rank\n", " - return_5_rank\n", " - EP_rank\n", " - pe_expansion_trend\n", " - value_price_divergence\n", " - active_market_cap\n", " - ebit_rank\n", "\n", "注册特征因子(表达式):\n", " - turnover_rate_volatility: ts_std(log(turnover_rate), 20)\n", "\n", "注册 Label 因子(表达式):\n", " - future_return_5_rank: (ts_delay(close, -5) / ts_delay(open, -1)) - 1\n", "\n", "特征因子数: 50\n", " - 来自 metadata: 49\n", " - 来自表达式: 1\n", "Label: future_return_5_rank\n", "已注册因子总数: 64\n", "\n", "[3] 准备数据\n", "\n", "================================================================================\n", "准备数据\n", "================================================================================\n", "\n", "计算因子: 20200101 - 20251231\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "D:\\PyProject\\ProStock\\src\\data\\financial_loader.py:148: UserWarning: Sortedness of columns cannot be checked when 'by' groups provided\n", " merged = df_price.join_asof(\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "数据形状: (7044952, 71)\n", "数据列: ['ts_code', 'trade_date', 'open', 'high', 'turnover_rate', 'low', 'vol', 'amount', 'close', 'total_assets', 'total_mv', 'f_ann_date', 'ebit', 'revenue', 'n_income', 'total_liab', 'total_hldr_eqy_exc_min_int', 'total_cur_assets', 'total_cur_liab', 'n_cashflow_act', 'ma_5', 'ma_20', 'ma_ratio_5_20', 'bias_10', 'high_low_ratio', 'bbi_ratio', 'return_5', 'return_20', 'kaufman_ER_20', 'mom_acceleration_10_20', 'drawdown_from_high_60', 'up_days_ratio_20', 'volatility_5', 'volatility_20', 'volatility_ratio', 'std_return_20', 'sharpe_ratio_20', 'min_ret_20', 'volatility_squeeze_5_60', 'overnight_intraday_diff', 'upper_shadow_ratio', 'capital_retention_20', 'max_ret_20', 'volume_ratio_5_20', 'turnover_rate_mean_5', 'turnover_deviation', 'amihud_illiq_20', 'turnover_cv_20', 'pv_corr_20', 'close_vwap_deviation', 'roe', 'roa', 'profit_margin', 'debt_to_equity', 'current_ratio', 'net_profit_yoy', 'revenue_yoy', 'healthy_expansion_velocity', 'EP', 'BP', 'CP', 'market_cap_rank', 'turnover_rank', 'return_5_rank', 'EP_rank', 'pe_expansion_trend', 'value_price_divergence', 'active_market_cap', 'ebit_rank', 'turnover_rate_volatility', 'future_return_5_rank']\n", "\n", "前5行预览:\n", "shape: (5, 71)\n", "┌───────────┬────────────┬─────────┬─────────┬───┬────────────┬───────────┬────────────┬───────────┐\n", "│ ts_code ┆ trade_date ┆ open ┆ high ┆ … ┆ active_mar ┆ ebit_rank ┆ turnover_r ┆ future_re │\n", "│ --- ┆ --- ┆ --- ┆ --- ┆ ┆ ket_cap ┆ --- ┆ ate_volati ┆ turn_5_ra │\n", "│ str ┆ str ┆ f64 ┆ f64 ┆ ┆ --- ┆ f64 ┆ lity ┆ nk │\n", "│ ┆ ┆ ┆ ┆ ┆ f64 ┆ ┆ --- ┆ --- │\n", "│ ┆ ┆ ┆ ┆ ┆ ┆ ┆ f64 ┆ f64 │\n", "╞═══════════╪════════════╪═════════╪═════════╪═══╪════════════╪═══════════╪════════════╪═══════════╡\n", "│ 000001.SZ ┆ 20200102 ┆ 1817.67 ┆ 1850.42 ┆ … ┆ null ┆ null ┆ null ┆ -0.008857 │\n", "│ 000001.SZ ┆ 20200103 ┆ 1849.33 ┆ 1889.72 ┆ … ┆ null ┆ null ┆ null ┆ -0.01881 │\n", "│ 000001.SZ ┆ 20200106 ┆ 1856.97 ┆ 1893.0 ┆ … ┆ null ┆ null ┆ null ┆ -0.008171 │\n", "│ 000001.SZ ┆ 20200107 ┆ 1870.07 ┆ 1886.45 ┆ … ┆ null ┆ null ┆ null ┆ -0.014117 │\n", "│ 000001.SZ ┆ 20200108 ┆ 1855.88 ┆ 1861.34 ┆ … ┆ null ┆ null ┆ null ┆ -0.017252 │\n", "└───────────┴────────────┴─────────┴─────────┴───┴────────────┴───────────┴────────────┴───────────┘\n", "\n", "[4] 转换为排序学习格式\n", "\n", "================================================================================\n", "准备排序学习数据(将 future_return_5_rank 转换为 20 分位数标签)\n", "================================================================================\n", "\n", "原始 future_return_5_rank 统计:\n", "shape: (9, 2)\n", "┌────────────┬───────────┐\n", "│ statistic ┆ value │\n", "│ --- ┆ --- │\n", "│ str ┆ f64 │\n", "╞════════════╪═══════════╡\n", "│ count ┆ 7.01659e6 │\n", "│ null_count ┆ 28362.0 │\n", "│ mean ┆ 0.003779 │\n", "│ std ┆ 0.073221 │\n", "│ min ┆ -0.969459 │\n", "│ 25% ┆ -0.033163 │\n", "│ 50% ┆ -0.001483 │\n", "│ 75% ┆ 0.032547 │\n", "│ max ┆ 10.361925 │\n", "└────────────┴───────────┘\n", "\n", "转换后 future_return_5_rank_rank 统计:\n", "shape: (9, 2)\n", "┌────────────┬───────────┐\n", "│ statistic ┆ value │\n", "│ --- ┆ --- │\n", "│ str ┆ f64 │\n", "╞════════════╪═══════════╡\n", "│ count ┆ 7.01659e6 │\n", "│ null_count ┆ 28362.0 │\n", "│ mean ┆ 9.495412 │\n", "│ std ┆ 5.765668 │\n", "│ min ┆ 0.0 │\n", "│ 25% ┆ 4.0 │\n", "│ 50% ┆ 9.0 │\n", "│ 75% ┆ 14.0 │\n", "│ max ┆ 19.0 │\n", "└────────────┴───────────┘\n", "\n", "每日样本数统计:\n", "shape: (9, 2)\n", "┌────────────┬─────────────┐\n", "│ statistic ┆ value │\n", "│ --- ┆ --- │\n", "│ str ┆ f64 │\n", "╞════════════╪═════════════╡\n", "│ count ┆ 1455.0 │\n", "│ null_count ┆ 0.0 │\n", "│ mean ┆ 4841.891409 │\n", "│ std ┆ 560.948186 │\n", "│ min ┆ 3740.0 │\n", "│ 25% ┆ 4369.0 │\n", "│ 50% ┆ 5060.0 │\n", "│ 75% ┆ 5344.0 │\n", "│ max ┆ 5458.0 │\n", "└────────────┴─────────────┘\n", "\n", "分位数标签分布:\n", "shape: (21, 2)\n", "┌───────────────────────────┬────────┐\n", "│ future_return_5_rank_rank ┆ count │\n", "│ --- ┆ --- │\n", "│ i64 ┆ u32 │\n", "╞═══════════════════════════╪════════╡\n", "│ null ┆ 28362 │\n", "│ 0 ┆ 351599 │\n", "│ 1 ┆ 350894 │\n", "│ 2 ┆ 350944 │\n", "│ 3 ┆ 351077 │\n", "│ … ┆ … │\n", "│ 15 ┆ 350910 │\n", "│ 16 ┆ 350835 │\n", "│ 17 ┆ 350848 │\n", "│ 18 ┆ 350871 │\n", "│ 19 ┆ 349137 │\n", "└───────────────────────────┴────────┘\n", "\n", "[配置] 训练期: 20200101 - 20231231\n", "[配置] 验证期: 20240101 - 20241231\n", "[配置] 测试期: 20250101 - 20251231\n", "[配置] 特征数: 50\n", "[配置] 目标变量: future_return_5_rank_rank(20分位数)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "C:\\Users\\liaozhaorun\\AppData\\Local\\Temp\\ipykernel_26384\\2929956284.py:125: DeprecationWarning: `pl.count()` is deprecated. Please use `pl.len()` instead.\n", "(Deprecated in version 0.20.5)\n", " daily_counts = df_ranked.group_by(date_col).agg(pl.count().alias(\"count\"))\n" ] } ], "execution_count": 5 }, { "metadata": {}, "cell_type": "markdown", "source": "### 4.1 股票池筛选" }, { "metadata": { "ExecuteTime": { "end_time": "2026-03-13T18:10:34.925345Z", "start_time": "2026-03-13T18:10:11.853318Z" } }, "cell_type": "code", "source": [ "print(\"\\n\" + \"=\" * 80)\n", "print(\"股票池筛选\")\n", "print(\"=\" * 80)\n", "\n", "# 先执行 ST 过滤(在股票池筛选之前,与 Trainer.train() 保持一致)\n", "if st_filter:\n", " print(\"\\n[过滤] 应用 ST 过滤器...\")\n", " data = st_filter.filter(data)\n", " print(f\" ST 过滤后数据规模: {data.shape}\")\n", "\n", "if pool_manager:\n", " print(\"\\n执行每日独立筛选股票池...\")\n", " filtered_data = pool_manager.filter_and_select_daily(data)\n", " print(f\" 筛选前数据规模: {data.shape}\")\n", " print(f\" 筛选后数据规模: {filtered_data.shape}\")\n", " print(f\" 筛选前股票数: {data['ts_code'].n_unique()}\")\n", " print(f\" 筛选后股票数: {filtered_data['ts_code'].n_unique()}\")\n", " print(f\" 删除记录数: {len(data) - len(filtered_data)}\")\n", "else:\n", " filtered_data = data\n", " print(\" 未配置股票池管理器,跳过筛选\")" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "================================================================================\n", "股票池筛选\n", "================================================================================\n", "\n", "[过滤] 应用 ST 过滤器...\n", " ST 过滤后数据规模: (6823808, 72)\n", "\n", "执行每日独立筛选股票池...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "C:\\Users\\liaozhaorun\\AppData\\Local\\Temp\\ipykernel_26384\\4061767669.py:57: DeprecationWarning: `is_in` with a collection of the same datatype is ambiguous and deprecated.\n", "Please use `implode` to return to previous behavior.\n", "\n", "See https://github.com/pola-rs/polars/issues/22149 for more information.\n", " return df[\"ts_code\"].is_in(small_cap_codes)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " 筛选前数据规模: (6823808, 72)\n", " 筛选后数据规模: (1455000, 72)\n", " 筛选前股票数: 5678\n", " 筛选后股票数: 1934\n", " 删除记录数: 5368808\n" ] } ], "execution_count": 6 }, { "metadata": {}, "cell_type": "markdown", "source": "### 4.2 数据划分" }, { "metadata": { "ExecuteTime": { "end_time": "2026-03-13T18:10:34.997214Z", "start_time": "2026-03-13T18:10:34.932346Z" } }, "cell_type": "code", "source": [ "print(\"\\n\" + \"=\" * 80)\n", "print(\"数据划分\")\n", "print(\"=\" * 80)\n", "\n", "if splitter:\n", " train_data, val_data, test_data = splitter.split(filtered_data)\n", " print(f\"\\n训练集数据规模: {train_data.shape}\")\n", " print(f\"验证集数据规模: {val_data.shape}\")\n", " print(f\"测试集数据规模: {test_data.shape}\")\n", "\n", " # 计算各集的 group 数组\n", " train_group = compute_group_array(train_data)\n", " val_group = compute_group_array(val_data)\n", " test_group = compute_group_array(test_data)\n", "\n", " print(f\"\\n训练集 group 数量: {len(train_group)}\")\n", " print(f\"验证集 group 数量: {len(val_group)}\")\n", " print(f\"测试集 group 数量: {len(test_group)}\")\n", " print(f\"训练集日均样本数: {np.mean(train_group):.1f}\")\n", " print(f\"验证集日均样本数: {np.mean(val_group):.1f}\")\n", " print(f\"测试集日均样本数: {np.mean(test_group):.1f}\")\n", "else:\n", " raise ValueError(\"必须配置数据划分器\")" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "================================================================================\n", "数据划分\n", "================================================================================\n", "\n", "训练集数据规模: (970000, 72)\n", "验证集数据规模: (242000, 72)\n", "测试集数据规模: (243000, 72)\n", "\n", "训练集 group 数量: 970\n", "验证集 group 数量: 242\n", "测试集 group 数量: 243\n", "训练集日均样本数: 1000.0\n", "验证集日均样本数: 1000.0\n", "测试集日均样本数: 1000.0\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "C:\\Users\\liaozhaorun\\AppData\\Local\\Temp\\ipykernel_26384\\2929956284.py:149: DeprecationWarning: `pl.count()` is deprecated. Please use `pl.len()` instead.\n", "(Deprecated in version 0.20.5)\n", " pl.count().alias(\"count\")\n" ] } ], "execution_count": 7 }, { "metadata": {}, "cell_type": "markdown", "source": "### 4.3 数据质量检查" }, { "metadata": { "ExecuteTime": { "end_time": "2026-03-13T18:10:37.417614Z", "start_time": "2026-03-13T18:10:35.002702Z" } }, "cell_type": "code", "source": [ "print(\"\\n\" + \"=\" * 80)\n", "print(\"数据质量检查(必须在预处理之前)\")\n", "print(\"=\" * 80)\n", "\n", "print(\"\\n检查训练集...\")\n", "check_data_quality(train_data, feature_cols, raise_on_error=False)\n", "\n", "print(\"\\n检查验证集...\")\n", "check_data_quality(val_data, feature_cols, raise_on_error=True)\n", "\n", "print(\"\\n检查测试集...\")\n", "check_data_quality(test_data, feature_cols, raise_on_error=True)\n", "\n", "print(\"[成功] 数据质量检查通过,未发现异常\")\n" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "================================================================================\n", "数据质量检查(必须在预处理之前)\n", "================================================================================\n", "\n", "检查训练集...\n", "\n", "================================================================================\n", "数据质量检查报告\n", "================================================================================\n", "\n", "[严重] 发现 1657 个全空因子:\n", " (某天的某个因子所有值都是 null,可能是数据缺失或计算错误)\n", " - 日期 20200217: drawdown_from_high_60 (样本数: 1000)\n", " - 日期 20200217: volatility_squeeze_5_60 (样本数: 1000)\n", " - 日期 20200217: net_profit_yoy (样本数: 1000)\n", " - 日期 20200217: revenue_yoy (样本数: 1000)\n", " - 日期 20200217: healthy_expansion_velocity (样本数: 1000)\n", " - 日期 20200217: pe_expansion_trend (样本数: 1000)\n", " - 日期 20200217: value_price_divergence (样本数: 1000)\n", " - 日期 20200213: drawdown_from_high_60 (样本数: 1000)\n", " - 日期 20200213: volatility_squeeze_5_60 (样本数: 1000)\n", " - 日期 20200213: net_profit_yoy (样本数: 1000)\n", " ... 还有 1647 个\n", "\n", "--------------------------------------------------------------------------------\n", "建议处理方式:\n", " 1. 检查因子定义和数据源,确认计算逻辑是否正确\n", " 2. 如果是预期内的缺失(如新股无历史数据),考虑调整因子计算窗口\n", " 3. 如果是数据同步问题,重新同步相关数据\n", " 4. 可以使用 filter 排除问题日期或因子\n", "================================================================================\n", "\n", "检查验证集...\n", "\n", "检查测试集...\n", "[成功] 数据质量检查通过,未发现异常\n" ] } ], "execution_count": 8 }, { "metadata": {}, "cell_type": "markdown", "source": "### 4.4 数据预处理" }, { "metadata": { "ExecuteTime": { "end_time": "2026-03-13T18:10:37.997346Z", "start_time": "2026-03-13T18:10:37.438005Z" } }, "cell_type": "code", "source": [ "print(\"\\n\" + \"=\" * 80)\n", "print(\"数据预处理\")\n", "print(\"=\" * 80)\n", "\n", "fitted_processors = []\n", "if processors:\n", " print(\"\\n训练集处理...\")\n", " for i, processor in enumerate(processors, 1):\n", " print(f\" [{i}/{len(processors)}] {processor.__class__.__name__}\")\n", " train_data = processor.fit_transform(train_data)\n", " fitted_processors.append(processor)\n", "\n", " print(\"\\n验证集处理...\")\n", " for processor in fitted_processors:\n", " val_data = processor.transform(val_data)\n", "\n", " print(\"\\n测试集处理...\")\n", " for processor in fitted_processors:\n", " test_data = processor.transform(test_data)\n", "\n", "print(f\"\\n处理后训练集形状: {train_data.shape}\")\n", "print(f\"处理后验证集形状: {val_data.shape}\")\n", "print(f\"处理后测试集形状: {test_data.shape}\")" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "================================================================================\n", "数据预处理\n", "================================================================================\n", "\n", "训练集处理...\n", " [1/3] NullFiller\n", " [2/3] Winsorizer\n", " [3/3] StandardScaler\n", "\n", "验证集处理...\n", "\n", "测试集处理...\n", "\n", "处理后训练集形状: (970000, 72)\n", "处理后验证集形状: (242000, 72)\n", "处理后测试集形状: (243000, 72)\n" ] } ], "execution_count": 9 }, { "metadata": {}, "cell_type": "markdown", "source": "### 4.4 训练 LambdaRank 模型" }, { "metadata": { "ExecuteTime": { "end_time": "2026-03-13T18:10:39.279997Z", "start_time": "2026-03-13T18:10:38.028508Z" } }, "cell_type": "code", "source": [ "print(\"\\n\" + \"=\" * 80)\n", "print(\"训练 LambdaRank 模型\")\n", "print(\"=\" * 80)\n", "\n", "# 准备数据\n", "X_train = train_data.select(feature_cols)\n", "y_train = train_data.select(target_col).to_series()\n", "\n", "X_val = val_data.select(feature_cols)\n", "y_val = val_data.select(target_col).to_series()\n", "\n", "print(f\"\\n训练样本数: {len(X_train)}\")\n", "print(f\"验证样本数: {len(X_val)}\")\n", "print(f\"特征数: {len(feature_cols)}\")\n", "print(f\"目标变量: {target_col}\")\n", "\n", "print(\"\\n目标变量统计(训练集):\")\n", "print(y_train.describe())\n", "\n", "print(\"\\n开始训练...\")\n", "model.fit(\n", " X=X_train,\n", " y=y_train,\n", " group=train_group,\n", " eval_set=(X_val, y_val, val_group),\n", ")\n", "print(\"训练完成!\")" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "================================================================================\n", "训练 LambdaRank 模型\n", "================================================================================\n", "\n", "训练样本数: 970000\n", "验证样本数: 242000\n", "特征数: 50\n", "目标变量: future_return_5_rank_rank\n", "\n", "目标变量统计(训练集):\n", "shape: (9, 2)\n", "┌────────────┬──────────┐\n", "│ statistic ┆ value │\n", "│ --- ┆ --- │\n", "│ str ┆ f64 │\n", "╞════════════╪══════════╡\n", "│ count ┆ 969665.0 │\n", "│ null_count ┆ 335.0 │\n", "│ mean ┆ 9.810091 │\n", "│ std ┆ 5.346526 │\n", "│ min ┆ 0.0 │\n", "│ 25% ┆ 6.0 │\n", "│ 50% ┆ 10.0 │\n", "│ 75% ┆ 14.0 │\n", "│ max ┆ 19.0 │\n", "└────────────┴──────────┘\n", "\n", "开始训练...\n", "Training until validation scores don't improve for 50 rounds\n", "Early stopping, best iteration is:\n", "[5]\ttrain's ndcg@10: 0.59368\tval's ndcg@10: 0.546167\n", "训练完成!\n" ] } ], "execution_count": 10 }, { "metadata": {}, "cell_type": "markdown", "source": "### 4.5 训练指标曲线" }, { "metadata": { "ExecuteTime": { "end_time": "2026-03-13T18:10:39.394848Z", "start_time": "2026-03-13T18:10:39.285414Z" } }, "cell_type": "code", "source": [ "print(\"\\n\" + \"=\" * 80)\n", "print(\"训练指标曲线\")\n", "print(\"=\" * 80)\n", "\n", "# 从模型获取训练评估结果\n", "evals_result = model.get_evals_result()\n", "\n", "if evals_result is None or not evals_result:\n", " print(\"[警告] 没有可用的训练指标,请确保训练时使用了 eval_set 参数\")\n", "else:\n", " print(\"[成功] 已从模型获取训练评估结果\")\n", "\n", " # 获取评估的 NDCG 指标\n", " ndcg_metrics = [k for k in evals_result[\"train\"].keys() if \"ndcg\" in k]\n", " print(f\"\\n评估的 NDCG 指标: {ndcg_metrics}\")\n", "\n", " # 显示早停信息\n", " actual_rounds = len(list(evals_result[\"train\"].values())[0])\n", " expected_rounds = MODEL_PARAMS.get(\"n_estimators\", 1000)\n", " print(f\"\\n[早停信息]\")\n", " print(f\" 配置的最大轮数: {expected_rounds}\")\n", " print(f\" 实际训练轮数: {actual_rounds}\")\n", "\n", " best_iter = model.get_best_iteration()\n", " if best_iter is not None and best_iter < actual_rounds:\n", " print(f\" 早停状态: 已触发(最佳迭代: {best_iter})\")\n", " else:\n", " print(f\" 早停状态: 未触发(达到最大轮数)\")\n", "\n", " # 显示各 NDCG 指标的最终值\n", " print(f\"\\n最终 NDCG 指标:\")\n", " for metric in ndcg_metrics:\n", " train_ndcg = evals_result[\"train\"][metric][-1]\n", " val_ndcg = evals_result[\"val\"][metric][-1]\n", " print(f\" {metric}: 训练集={train_ndcg:.4f}, 验证集={val_ndcg:.4f}\")\n", "\n", " # 使用封装好的方法绘制所有指标\n", " print(\"\\n[绘图] 使用 LightGBM 原生接口绘制训练曲线...\")\n", " fig = model.plot_all_metrics(metrics=ndcg_metrics[:4], figsize=(14, 10))\n", " plt.show()\n", "\n", " print(f\"\\n[指标分析]\")\n", " print(f\" 各NDCG指标在验证集上的最佳值:\")\n", " for metric in ndcg_metrics:\n", " val_metric_list = evals_result[\"val\"][metric]\n", " best_iter_metric = val_metric_list.index(max(val_metric_list))\n", " best_val = max(val_metric_list)\n", " print(f\" {metric}: {best_val:.4f} (迭代 {best_iter_metric + 1})\")\n", " print(f\"\\n[重要提醒] 验证集仅用于早停/调参,测试集完全独立于训练过程!\")" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "================================================================================\n", "训练指标曲线\n", "================================================================================\n", "[成功] 已从模型获取训练评估结果\n", "\n", "评估的 NDCG 指标: ['ndcg@10']\n", "\n", "[早停信息]\n", " 配置的最大轮数: 2000\n", " 实际训练轮数: 55\n", " 早停状态: 已触发(最佳迭代: 5)\n", "\n", "最终 NDCG 指标:\n", " ndcg@10: 训练集=0.6331, 验证集=0.5393\n", "\n", "[绘图] 使用 LightGBM 原生接口绘制训练曲线...\n" ] }, { "data": { "text/plain": [ "
" ], "image/png": "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CG0BAAAAAHKI0BYAAAAAIIcIbQEAAAAAckjWQ9uf//znMXTo0CgtLY2DDz44nnrqqW2ev2LFirjggguiX79+UVJSEnvvvXfceeedWzz3//2//xd5eXnxpS99qYVGDwAAAADQvAoji26++ea46KKL4pprrkkD26uvvjpOOOGEmDp1avTu3Xuz86uqquK4445LX7vllltiwIABMWvWrOjatetm506aNCl+9atfxejRo3fTpwEAAAAAaOWVtldddVWcd955ce6558bIkSPT8LZDhw5x/fXXb/H85PiyZcvin//8Zxx++OFphe7RRx8dBxxwwEbnrV69Os4888y47rrrolu3brvp0wAAAAAAtOJK26RqdvLkyfHNb36z8Vh+fn68613viscff3yL77ntttvi0EMPTdsj3HrrrdGrV68444wz4uKLL46CgoLG85LX3/Oe96TX+v73v/+2Y6msrEyXBhUVFem6uro6XYDc1/Csemah9fDcQuvk2YXWx3MLrY/ntu3a3twia6HtkiVLora2Nvr06bPR8WT/1Vdf3eJ7pk+fHg888EBaRZv0sZ02bVp87nOfSz/spZdemp7z5z//OZ555pm0PcL2+uEPfxiXX375ZscffPDBtPIXaD3uvffebA8B2EGeW2idPLvQ+nhuofXx3LY9a9euzf2etjuqrq4u7Wd77bXXppW148aNi7lz58aVV16ZhrZz5syJL37xi+kf6GRis+2VVPsmvXWbVtoOGjQojj322OjRo0cLfRqgOSX/8SZ59pO+10VFRW4utAKeW2idPLvQ+nhuofXx3LZdDb/hn7Ohbc+ePdPgdeHChRsdT/b79u27xff069cvDWOatkIYMWJELFiwoLHdwqJFi2Ls2LGNryfVvBMnToyf/exnaQuEpu9tUFJSki6bSn6W8AdaF88ttD6eW2idPLvQ+nhuofXx3LY925s1Zm0isuLi4rRS9v7779+okjbZT/rWbkky+VjSEiE5r8Frr72WhrnJ9d75znfGiy++GM8991zjMn78+LSdQrK9pcAWAAAAACCXZLU9QtKS4Oyzz06D1YMOOiiuvvrqWLNmTZx77rnp62eddVYMGDAg7Tmb+OxnP5tWzCYtED7/+c/H66+/HldccUV84QtfSF/v1KlTjBo1aqOfUV5enrY42PQ4AAAAAEAuympoe9ppp8XixYvjkksu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18lvpvt79e4YBwzq2hjSjmimfrK7Ivm1+nfsmyz1rQiaBrkN1bhJi4Xk2L59O6dVtIcM7572k02qeXNB0oN1n76d0uXjhwxJg/KkFUFDJW4S5i6qqIyaurqorctEde0mfRw2SDL1qpq6qNrJcSQtGpJJwxpaQowe2CVtXQAAALRdQlsAaEUWVqyP838/OW2JkFRifuukEfHJI4a1ir6hmwa51dXV8a/b74yT33toFBW1TBVwc0ru8aDuHdLlg+PqJzdrKgl1k4A2CXFrajNpBXQS5tbU1jVuJy0Y0mPp8cxGgW/98Y3fO6BbWauYDA0AAGheQlsAaCWenb08DWwXrapMWx387IwD48i9ekVr1payyCTULchLPlNB6FYAAADsiqz/VennP/95DB06NEpLS+Pggw+Op556apvnr1ixIi644ILo169flJSUxN577x133nln4+s//OEPY8KECdGpU6fo3bt3vP/974+pU6fuhk8CAC3nL0/PidN+9UQa2O7dp2PcduHhrT6wBQAAIAdD25tvvjkuuuiiuPTSS+OZZ56JAw44IE444YRYtGjRFs+vqqqK4447LmbOnBm33HJLGsZed911MWDAgMZzHn744TTUfeKJJ+Lee+9Nf/Xy+OOPjzVr1uzGTwYAzSP5dfrLbns5vn7LC1FVWxfHj+wTf//c4TGkR7lbDAAA0EZltT3CVVddFeedd16ce+656f4111wTd9xxR1x//fXxjW98Y7Pzk+PLli2Lxx57rLH3XVKl29Tdd9+90f4NN9yQVtxOnjw5jjrqqC2Oo7KyMl0aVFRUpOsk8E0WIPc1PKueWdqSZWuq4os3Px9PzFie7n/h2D3igmOGR35+pk38WffcQuvk2YXWx3MLrY/ntu3a3r/L5WWSWTOyIKma7dChQ1oxm7QwaHD22WenLRBuvfXWzd5z0kknRffu3dP3Ja/36tUrzjjjjLj44oujoGDLsyhPmzYt9tprr3jxxRdj1KhRWzznsssui8svv3yz4zfddFP6swBgd5u7JuLXUwtiWWVelORn4mN71cXo7ln5n2wAAACaydq1a9M8c+XKldG5c+fcq7RdsmRJ1NbWRp8+9TNIN0j2X3311S2+Z/r06fHAAw/EmWeemfaxTQLZz33uc2lCnbRY2FRdXV186UtfisMPP3yrgW3im9/8ZtqmoWml7aBBg+LY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5J6G1gj79Y28vDw3CQAAAIDdpjCy6Kqrrorzzjsvzj333HT/mmuuiTvuuCOuv/76+MY3vrHZ+cnxZcuWxWOPPRZFRUXpsaSidleumaisrEyXBhUVFem6uro6XWhf1lXVxoOv1of8x+3by5+BVqLhWfXMQuvhuYXWybMLrY/nFlofz23btb25RV4mk8lEFiRVsx06dEgrZt///vc3Hj/77LPTFgi33nrrZu856aSTonv37un7ktd79eoVZ5xxRlx88cVRUFCwU9dMXHbZZXH55Zdvdvymm25Kr0f78sKyvPjN1ILoVpyJS8fWhkJbAAAAAJrD2rVr0zxz5cqV0blz59yrtF2yZEnU1tZGnz59Njqe7L/66qtbfM/06dPjgQceiDPPPDPtYztt2rT43Oc+lybUSTuEnblm4pvf/GbaUqFppe2gQYPi2GOPjR49euzyZ6V1eeCWFyNifpwybki856R9sz0ctlPyz4F77703baHSUIkP5DbPLbROnl1ofTy30Pp4btuuht/wz+n2CDuqrq4uevfuHddee21aWTtu3LiYO3duXHnllWlou7OSCc2SZVNJ8CP8aV+qaurigamL0+33HDDA998KeW6h9fHcQuvk2YXWx3MLrY/ntu3Z3qwxa6Ftz5490+B14cKFGx1P9vv27bvF9/Tr1y/9YMn7GowYMSIWLFiQtkbYmWtCU49PXxqr1tdEz44lMXZwNzcHAAAAgN0uP7KkuLg4rZS9//77N6qkTfYPPfTQLb7n8MMPT1siJOc1eO2119IwN7nezlwTmrr7pfnp+vj9+kRBfp6bAwAAAED7CW0TSR/Z6667Ln73u9/FlClT4rOf/WysWbMmzj333PT1s846K+032yB5fdmyZfHFL34xDWvvuOOOuOKKK+KCCy7Y7mvC1tTWZeLfL9dXab97lMpsAAAAALIjqz1tTzvttFi8eHFccsklaYuDMWPGxN133904kdjs2bMjP/+tXDmZHOyee+6JL3/5yzF69OgYMGBAGuBefPHF231N2JqnZy6LpWuqoktZURwy3AR0AAAAAGRH1iciu/DCC9NlSx566KHNjiVtDp544omdviZszV0vLUjX7xrRJ4oKslqEDgAAAEA7JpmCiMhkMnHPy/Wh7YlaIwAAAACQRUJbiIgX3lwZ81eujw7FBXHkXj3dEwAAAACyRmgLTVojHLtv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dthhccopp0RxsYAw2158c2VaaVuQnxefOnJYtocDAAAAALRUpe20adNixIgRcfbZZ8ezzz4bdXV16ZJsn3XWWbHffvul55Bd10ys72X7vgP6x8BuHXwdAAAAANBWK20/+9nPxv7775+GtJ07d97otYqKijS4veCCC+Kee+5p7nGynWYvXRt3vTg/3f70UcPdNwAAAABoy6Hto48+Gk899dRmgW0iOfa9730vDj744OYcHzvorpfmR10m4og9e8aIfpt/TwAAAABAG2qP0LVr15g5c+ZWX09eS84heybNXJ6uj967l68BAAAAANp6pe2nPvWptAXCd77znXjnO98Zffr0SY8vXLgw7r///vj+978fn//851tqrLyNTCYTz8yuD23HDe3mfgEAAABAWw9tv/vd70Z5eXlceeWV8ZWvfCXy8vIaw8K+ffvGxRdfHF//+tdbaqy8jelL1sSyNVVRUpgfo/p3cb8AAAAAoK2HtokkmE2WGTNmxIIFC9JjSWA7bNiwlhgfO2DyhtYIBwzqGsWFO9T5AgAAAADIETud7CUh7aGHHpouuxLY/vznP4+hQ4dGaWlpOolZMtHZ1txwww1pdW/TJXlfU6tXr44LL7wwBg4cGGVlZTFy5Mi45pproj2YNHNZuh4/RGsEAAAAAGitmrUcc86cOfGJT3xiu8+/+eab46KLLopLL700nnnmmTjggAPihBNOiEWLFm31PZ07d4758+c3LrNmzdro9eR6d999d/zhD3+IKVOmxJe+9KU0xL3tttuirZs8q77Sdrx+tgAAAADQajVraLts2bL43e9+t93nX3XVVXHeeefFueee21gR26FDh7j++uu3+p6kujZpx9CwNEyG1uCxxx6Ls88+O4455pi0gvfTn/50GgZvq4K3LVi6ujLtaZsYO1ilLQAAAAC0i562b1etOn369O2+VlVVVUyePDm++c1vNh7Lz8+Pd73rXfH4449v9X1J+4MhQ4ZEXV1djB07Nq644orYb7/9Gl8/7LDD0nEmFb/9+/ePhx56KF577bX4yU9+stVrVlZWpkuDioqKdF1dXZ0urcFT05ek6716l0d5UV6rGTc0l4Y/8/7sQ+vhuYXWybMLrY/nFlofz23btb25RV4mk8ls70WTUDWpdN3WW5LXa2tr3/Za8+bNiwEDBqSVsUlf3AZf//rX4+GHH44nn3xy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" }, "metadata": {}, "output_type": "display_data", "jetTransient": { "display_id": null } }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "[指标分析]\n", " 各NDCG指标在验证集上的最佳值:\n", " ndcg@10: 0.5462 (迭代 5)\n", "\n", "[重要提醒] 验证集仅用于早停/调参,测试集完全独立于训练过程!\n" ] } ], "execution_count": 11 }, { "metadata": {}, "cell_type": "markdown", "source": "### 4.6 模型评估" }, { "metadata": { "ExecuteTime": { "end_time": "2026-03-13T18:10:39.655028Z", "start_time": "2026-03-13T18:10:39.403487Z" } }, "cell_type": "code", "source": [ "print(\"\\n\" + \"=\" * 80)\n", "print(\"模型评估\")\n", "print(\"=\" * 80)\n", "\n", "# 准备测试集\n", "X_test = test_data.select(feature_cols)\n", "y_test = test_data.select(target_col).to_series()\n", "\n", "# 预测\n", "print(\"\\n生成预测...\")\n", "predictions = model.predict(X_test)\n", "\n", "# 添加预测列\n", "test_data = test_data.with_columns([pl.Series(\"prediction\", predictions)])\n", "\n", "# 计算 NDCG 指标\n", "print(\"\\n计算 NDCG 指标...\")\n", "ndcg_results = evaluate_ndcg_at_k(\n", " y_true=y_test.to_numpy(),\n", " y_pred=predictions,\n", " group=test_group,\n", " k_list=[1, 5, 10, 20],\n", ")\n", "\n", "print(\"\\nNDCG 评估结果:\")\n", "print(\"-\" * 40)\n", "for metric, value in ndcg_results.items():\n", " print(f\" {metric}: {value:.4f}\")\n", "\n", "# 特征重要性\n", "print(\"\\n特征重要性(Top 20):\")\n", "print(\"-\" * 40)\n", "importance = model.feature_importance()\n", "if importance is not None:\n", " top_features = importance.sort_values(ascending=False).head(20)\n", " for i, (feature, score) in enumerate(top_features.items(), 1):\n", " print(f\" {i:2d}. {feature:30s} {score:10.2f}\")" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "================================================================================\n", "模型评估\n", "================================================================================\n", "\n", "生成预测...\n", "\n", "计算 NDCG 指标...\n", "\n", "NDCG 评估结果:\n", "----------------------------------------\n", " ndcg@1: 0.4710\n", " ndcg@5: 0.4927\n", " ndcg@10: 0.5003\n", " ndcg@20: 0.5075\n", "\n", "特征重要性(Top 20):\n", "----------------------------------------\n", " 1. roe 345.77\n", " 2. ma_20 244.73\n", " 3. profit_margin 223.10\n", " 4. revenue_yoy 216.85\n", " 5. active_market_cap 214.10\n", " 6. std_return_20 149.44\n", " 7. close_vwap_deviation 136.42\n", " 8. max_ret_20 110.61\n", " 9. debt_to_equity 105.78\n", " 10. bbi_ratio 104.63\n", " 11. market_cap_rank 104.10\n", " 12. up_days_ratio_20 99.44\n", " 13. turnover_rank 80.60\n", " 14. min_ret_20 72.72\n", " 15. ebit_rank 64.03\n", " 16. drawdown_from_high_60 61.28\n", " 17. turnover_rate_mean_5 57.16\n", " 18. healthy_expansion_velocity 54.10\n", " 19. BP 51.36\n", " 20. EP 51.29\n" ] } ], "execution_count": 12 }, { "metadata": { "ExecuteTime": { "end_time": "2026-03-13T18:10:39.872615Z", "start_time": "2026-03-13T18:10:39.663834Z" } }, "cell_type": "code", "source": [ "# 确保输出目录存在\n", "os.makedirs(OUTPUT_DIR, exist_ok=True)\n", "\n", "# 生成时间戳\n", "start_dt = datetime.strptime(TEST_START, \"%Y%m%d\")\n", "end_dt = datetime.strptime(TEST_END, \"%Y%m%d\")\n", "date_str = f\"{start_dt.strftime('%Y%m%d')}_{end_dt.strftime('%Y%m%d')}\"\n", "\n", "# 保存每日 Top N\n", "print(f\"\\n[1/1] 保存每日 Top {TOP_N} 股票...\")\n", "topn_output_path = os.path.join(OUTPUT_DIR, \"rank_output.csv\")\n", "\n", "# 按日期分组,取每日 top N\n", "topn_by_date = []\n", "unique_dates = test_data[\"trade_date\"].unique().sort()\n", "for date in unique_dates:\n", " day_data = test_data.filter(test_data[\"trade_date\"] == date)\n", " # 按 prediction 降序排序,取前 N\n", " topn = day_data.sort(\"prediction\", descending=True).head(TOP_N)\n", " topn_by_date.append(topn)\n", "\n", "# 合并所有日期的 top N\n", "topn_results = pl.concat(topn_by_date)\n", "\n", "# 格式化日期并调整列顺序:日期、分数、股票\n", "topn_to_save = topn_results.select(\n", " [\n", " pl.col(\"trade_date\").str.slice(0, 4)\n", " + \"-\"\n", " + pl.col(\"trade_date\").str.slice(4, 2)\n", " + \"-\"\n", " + pl.col(\"trade_date\").str.slice(6, 2).alias(\"date\"),\n", " pl.col(\"prediction\").alias(\"score\"),\n", " pl.col(\"ts_code\"),\n", " ]\n", ")\n", "topn_to_save.write_csv(topn_output_path, include_header=True)\n", "print(f\" 保存路径: {topn_output_path}\")\n", "print(\n", " f\" 保存行数: {len(topn_to_save)}({len(unique_dates)}个交易日 x 每日top{TOP_N})\"\n", ")\n", "print(f\"\\n 预览(前15行):\")\n", "print(topn_to_save.head(15))\n", "\n", "print(\"\\n训练流程完成!\")" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "[1/1] 保存每日 Top 5 股票...\n", " 保存路径: output\\rank_output.csv\n", " 保存行数: 1215(243个交易日 x 每日top5)\n", "\n", " 预览(前15行):\n", "shape: (15, 3)\n", "┌────────────┬──────────┬───────────┐\n", "│ trade_date ┆ score ┆ ts_code │\n", "│ --- ┆ --- ┆ --- │\n", "│ str ┆ f64 ┆ str │\n", "╞════════════╪══════════╪═══════════╡\n", "│ 2025-01-02 ┆ 0.022198 ┆ 002480.SZ │\n", "│ 2025-01-02 ┆ 0.018886 ┆ 000826.SZ │\n", "│ 2025-01-02 ┆ 0.018845 ┆ 600683.SH │\n", "│ 2025-01-02 ┆ 0.016044 ┆ 000632.SZ │\n", "│ 2025-01-02 ┆ 0.014577 ┆ 002072.SZ │\n", "│ … ┆ … ┆ … │\n", "│ 2025-01-06 ┆ 0.03183 ┆ 600683.SH │\n", "│ 2025-01-06 ┆ 0.023015 ┆ 000701.SZ │\n", "│ 2025-01-06 ┆ 0.018886 ┆ 000826.SZ │\n", "│ 2025-01-06 ┆ 0.016044 ┆ 000632.SZ │\n", "│ 2025-01-06 ┆ 0.014577 ┆ 002072.SZ │\n", "└────────────┴──────────┴───────────┘\n", "\n", "训练流程完成!\n" ] } ], "execution_count": 13 }, { "metadata": {}, "cell_type": "markdown", "source": [ "## 5. 总结\n", "#\n", "本 Notebook 实现了完整的 Learn-to-Rank 训练流程:\n", "#\n", "### 核心步骤\n", "#\n", "1. **数据准备**: 计算 49 个特征因子,将 `future_return_5` 转换为 20 分位数标签\n", "2. **模型训练**: 使用 LightGBM LambdaRank 学习每日股票排序\n", "3. **模型评估**: 使用 NDCG@1/5/10/20 评估排序质量\n", "4. **策略分析**: 基于排序分数构建 Top-k 选股策略\n", "#\n", "### 关键参数\n", "#\n", "- **Objective**: lambdarank\n", "- **Metric**: ndcg\n", "- **Learning Rate**: 0.05\n", "- **Num Leaves**: 31\n", "- **N Quantiles**: 20\n", "#\n", "### 输出结果\n", "#\n", "- rank_output.csv: 每日Top-N推荐股票(格式:date, score, ts_code)\n", "- 特征重要性排名\n", "- Top-k 策略统计和图表\n", "- NDCG训练指标曲线\n", "#\n", "### 后续优化方向\n", "#\n", "1. **特征工程**: 尝试更多因子组合\n", "2. **超参数调优**: 使用网格搜索优化 LambdaRank 参数\n", "3. **模型集成**: 结合多个排序模型的预测\n", "4. **更复杂的分组**: 考虑按行业分组排序\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.0" } }, "nbformat": 4, "nbformat_minor": 4 }