2141 lines
228 KiB
Plaintext
2141 lines
228 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 3,
|
||
"id": "79a7758178bafdd3",
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2025-04-03T12:46:06.987506Z",
|
||
"start_time": "2025-04-03T12:46:06.259551Z"
|
||
},
|
||
"jupyter": {
|
||
"source_hidden": true
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"The autoreload extension is already loaded. To reload it, use:\n",
|
||
" %reload_ext autoreload\n",
|
||
"/mnt/d/PyProject/NewStock\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"%load_ext autoreload\n",
|
||
"%autoreload 2\n",
|
||
"\n",
|
||
"import gc\n",
|
||
"import os\n",
|
||
"import sys\n",
|
||
"sys.path.append('/mnt/d/PyProject/NewStock/')\n",
|
||
"print(os.getcwd())\n",
|
||
"import pandas as pd\n",
|
||
"from main.factor.factor import get_rolling_factor, get_simple_factor\n",
|
||
"from main.utils.factor import read_industry_data\n",
|
||
"from main.utils.factor_processor import calculate_score\n",
|
||
"from main.utils.utils import read_and_merge_h5_data, merge_with_industry_data\n",
|
||
"\n",
|
||
"import warnings\n",
|
||
"\n",
|
||
"warnings.filterwarnings(\"ignore\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 4,
|
||
"id": "a79cafb06a7e0e43",
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2025-04-03T12:47:00.212859Z",
|
||
"start_time": "2025-04-03T12:46:06.998047Z"
|
||
},
|
||
"scrolled": true
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"daily data\n",
|
||
"daily basic\n",
|
||
"inner merge on ['ts_code', 'trade_date']\n",
|
||
"stk limit\n",
|
||
"left merge on ['ts_code', 'trade_date']\n",
|
||
"money flow\n",
|
||
"left merge on ['ts_code', 'trade_date']\n",
|
||
"cyq perf\n",
|
||
"left merge on ['ts_code', 'trade_date']\n",
|
||
"<class 'pandas.core.frame.DataFrame'>\n",
|
||
"RangeIndex: 8692146 entries, 0 to 8692145\n",
|
||
"Data columns (total 32 columns):\n",
|
||
" # Column Dtype \n",
|
||
"--- ------ ----- \n",
|
||
" 0 ts_code object \n",
|
||
" 1 trade_date datetime64[ns]\n",
|
||
" 2 open float64 \n",
|
||
" 3 close float64 \n",
|
||
" 4 high float64 \n",
|
||
" 5 low float64 \n",
|
||
" 6 vol float64 \n",
|
||
" 7 pct_chg float64 \n",
|
||
" 8 turnover_rate float64 \n",
|
||
" 9 pe_ttm float64 \n",
|
||
" 10 circ_mv float64 \n",
|
||
" 11 total_mv float64 \n",
|
||
" 12 volume_ratio float64 \n",
|
||
" 13 is_st bool \n",
|
||
" 14 up_limit float64 \n",
|
||
" 15 down_limit float64 \n",
|
||
" 16 buy_sm_vol float64 \n",
|
||
" 17 sell_sm_vol float64 \n",
|
||
" 18 buy_lg_vol float64 \n",
|
||
" 19 sell_lg_vol float64 \n",
|
||
" 20 buy_elg_vol float64 \n",
|
||
" 21 sell_elg_vol float64 \n",
|
||
" 22 net_mf_vol float64 \n",
|
||
" 23 his_low float64 \n",
|
||
" 24 his_high float64 \n",
|
||
" 25 cost_5pct float64 \n",
|
||
" 26 cost_15pct float64 \n",
|
||
" 27 cost_50pct float64 \n",
|
||
" 28 cost_85pct float64 \n",
|
||
" 29 cost_95pct float64 \n",
|
||
" 30 weight_avg float64 \n",
|
||
" 31 winner_rate float64 \n",
|
||
"dtypes: bool(1), datetime64[ns](1), float64(29), object(1)\n",
|
||
"memory usage: 2.0+ GB\n",
|
||
"None\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"from main.utils.utils import read_and_merge_h5_data\n",
|
||
"\n",
|
||
"print('daily data')\n",
|
||
"df = read_and_merge_h5_data('/mnt/d/PyProject/NewStock/data/daily_data.h5', key='daily_data',\n",
|
||
" columns=['ts_code', 'trade_date', 'open', 'close', 'high', 'low', 'vol', 'pct_chg'],\n",
|
||
" df=None)\n",
|
||
"\n",
|
||
"print('daily basic')\n",
|
||
"df = read_and_merge_h5_data('/mnt/d/PyProject/NewStock/data/daily_basic.h5', key='daily_basic',\n",
|
||
" columns=['ts_code', 'trade_date', 'turnover_rate', 'pe_ttm', 'circ_mv', 'total_mv', 'volume_ratio',\n",
|
||
" 'is_st'], df=df, join='inner')\n",
|
||
"\n",
|
||
"print('stk limit')\n",
|
||
"df = read_and_merge_h5_data('/mnt/d/PyProject/NewStock/data/stk_limit.h5', key='stk_limit',\n",
|
||
" columns=['ts_code', 'trade_date', 'pre_close', 'up_limit', 'down_limit'],\n",
|
||
" df=df)\n",
|
||
"print('money flow')\n",
|
||
"df = read_and_merge_h5_data('/mnt/d/PyProject/NewStock/data/money_flow.h5', key='money_flow',\n",
|
||
" columns=['ts_code', 'trade_date', 'buy_sm_vol', 'sell_sm_vol', 'buy_lg_vol', 'sell_lg_vol',\n",
|
||
" 'buy_elg_vol', 'sell_elg_vol', 'net_mf_vol'],\n",
|
||
" df=df)\n",
|
||
"print('cyq perf')\n",
|
||
"df = read_and_merge_h5_data('/mnt/d/PyProject/NewStock/data/cyq_perf.h5', key='cyq_perf',\n",
|
||
" columns=['ts_code', 'trade_date', 'his_low', 'his_high', 'cost_5pct', 'cost_15pct',\n",
|
||
" 'cost_50pct',\n",
|
||
" 'cost_85pct', 'cost_95pct', 'weight_avg', 'winner_rate'],\n",
|
||
" df=df)\n",
|
||
"print(df.info())"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 5,
|
||
"id": "cac01788dac10678",
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2025-04-03T12:47:10.527104Z",
|
||
"start_time": "2025-04-03T12:47:00.488715Z"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"industry\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"print('industry')\n",
|
||
"industry_df = read_and_merge_h5_data('/mnt/d/PyProject/NewStock/data/industry_data.h5', key='industry_data',\n",
|
||
" columns=['ts_code', 'l2_code', 'in_date'],\n",
|
||
" df=None, on=['ts_code'], join='left')\n",
|
||
"\n",
|
||
"\n",
|
||
"def merge_with_industry_data(df, industry_df):\n",
|
||
" # 确保日期字段是 datetime 类型\n",
|
||
" df['trade_date'] = pd.to_datetime(df['trade_date'])\n",
|
||
" industry_df['in_date'] = pd.to_datetime(industry_df['in_date'])\n",
|
||
"\n",
|
||
" # 对 industry_df 按 ts_code 和 in_date 排序\n",
|
||
" industry_df_sorted = industry_df.sort_values(['in_date', 'ts_code'])\n",
|
||
"\n",
|
||
" # 对原始 df 按 ts_code 和 trade_date 排序\n",
|
||
" df_sorted = df.sort_values(['trade_date', 'ts_code'])\n",
|
||
"\n",
|
||
" # 使用 merge_asof 进行向后合并\n",
|
||
" merged = pd.merge_asof(\n",
|
||
" df_sorted,\n",
|
||
" industry_df_sorted,\n",
|
||
" by='ts_code', # 按 ts_code 分组\n",
|
||
" left_on='trade_date',\n",
|
||
" right_on='in_date',\n",
|
||
" direction='backward'\n",
|
||
" )\n",
|
||
"\n",
|
||
" # 获取每个 ts_code 的最早 in_date 记录\n",
|
||
" min_in_date_per_ts = (industry_df_sorted\n",
|
||
" .groupby('ts_code')\n",
|
||
" .first()\n",
|
||
" .reset_index()[['ts_code', 'l2_code']])\n",
|
||
"\n",
|
||
" # 填充未匹配到的记录(trade_date 早于所有 in_date 的情况)\n",
|
||
" merged['l2_code'] = merged['l2_code'].fillna(\n",
|
||
" merged['ts_code'].map(min_in_date_per_ts.set_index('ts_code')['l2_code'])\n",
|
||
" )\n",
|
||
"\n",
|
||
" # 保留需要的列并重置索引\n",
|
||
" result = merged.reset_index(drop=True)\n",
|
||
" return result\n",
|
||
"\n",
|
||
"\n",
|
||
"# 使用示例\n",
|
||
"df = merge_with_industry_data(df, industry_df)\n",
|
||
"# print(mdf[mdf['ts_code'] == '600751.SH'][['ts_code', 'trade_date', 'l2_code']])"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 6,
|
||
"id": "c4e9e1d31da6dba6",
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2025-04-03T12:47:10.719252Z",
|
||
"start_time": "2025-04-03T12:47:10.541247Z"
|
||
},
|
||
"jupyter": {
|
||
"source_hidden": true
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"from main.factor.factor import *\n",
|
||
"\n",
|
||
"def calculate_indicators(df):\n",
|
||
" \"\"\"\n",
|
||
" 计算四个指标:当日涨跌幅、5日移动平均、RSI、MACD。\n",
|
||
" \"\"\"\n",
|
||
" df = df.sort_values('trade_date')\n",
|
||
" df['daily_return'] = (df['close'] - df['pre_close']) / df['pre_close'] * 100\n",
|
||
" # df['5_day_ma'] = df['close'].rolling(window=5).mean()\n",
|
||
" delta = df['close'].diff()\n",
|
||
" gain = delta.where(delta > 0, 0)\n",
|
||
" loss = -delta.where(delta < 0, 0)\n",
|
||
" avg_gain = gain.rolling(window=14).mean()\n",
|
||
" avg_loss = loss.rolling(window=14).mean()\n",
|
||
" rs = avg_gain / avg_loss\n",
|
||
" df['RSI'] = 100 - (100 / (1 + rs))\n",
|
||
"\n",
|
||
" # 计算MACD\n",
|
||
" ema12 = df['close'].ewm(span=12, adjust=False).mean()\n",
|
||
" ema26 = df['close'].ewm(span=26, adjust=False).mean()\n",
|
||
" df['MACD'] = ema12 - ema26\n",
|
||
" df['Signal_line'] = df['MACD'].ewm(span=9, adjust=False).mean()\n",
|
||
" df['MACD_hist'] = df['MACD'] - df['Signal_line']\n",
|
||
"\n",
|
||
" # 4. 情绪因子1:市场上涨比例(Up Ratio)\n",
|
||
" df['up_ratio'] = df['daily_return'].apply(lambda x: 1 if x > 0 else 0)\n",
|
||
" df['up_ratio_20d'] = df['up_ratio'].rolling(window=20).mean() # 过去20天上涨比例\n",
|
||
"\n",
|
||
" # 5. 情绪因子2:成交量变化率(Volume Change Rate)\n",
|
||
" df['volume_mean'] = df['vol'].rolling(window=20).mean() # 过去20天的平均成交量\n",
|
||
" df['volume_change_rate'] = (df['vol'] - df['volume_mean']) / df['volume_mean'] * 100 # 成交量变化率\n",
|
||
"\n",
|
||
" # 6. 情绪因子3:波动率(Volatility)\n",
|
||
" df['volatility'] = df['daily_return'].rolling(window=20).std() # 过去20天的日收益率标准差\n",
|
||
"\n",
|
||
" # 7. 情绪因子4:成交额变化率(Amount Change Rate)\n",
|
||
" df['amount_mean'] = df['amount'].rolling(window=20).mean() # 过去20天的平均成交额\n",
|
||
" df['amount_change_rate'] = (df['amount'] - df['amount_mean']) / df['amount_mean'] * 100 # 成交额变化率\n",
|
||
"\n",
|
||
" # df = sentiment_panic_greed_index(df)\n",
|
||
" # df = sentiment_market_breadth_proxy(df)\n",
|
||
" # df = sentiment_reversal_indicator(df)\n",
|
||
"\n",
|
||
" return df\n",
|
||
"\n",
|
||
"\n",
|
||
"def generate_index_indicators(h5_filename):\n",
|
||
" df = pd.read_hdf(h5_filename, key='index_data')\n",
|
||
" df['trade_date'] = pd.to_datetime(df['trade_date'], format='%Y%m%d')\n",
|
||
" df = df.sort_values('trade_date')\n",
|
||
"\n",
|
||
" # 计算每个ts_code的相关指标\n",
|
||
" df_indicators = []\n",
|
||
" for ts_code in df['ts_code'].unique():\n",
|
||
" df_index = df[df['ts_code'] == ts_code].copy()\n",
|
||
" df_index = calculate_indicators(df_index)\n",
|
||
" df_indicators.append(df_index)\n",
|
||
"\n",
|
||
" # 合并所有指数的结果\n",
|
||
" df_all_indicators = pd.concat(df_indicators, ignore_index=True)\n",
|
||
"\n",
|
||
" # 保留trade_date列,并将同一天的数据按ts_code合并成一行\n",
|
||
" df_final = df_all_indicators.pivot_table(\n",
|
||
" index='trade_date',\n",
|
||
" columns='ts_code',\n",
|
||
" values=['daily_return', \n",
|
||
" 'RSI', 'MACD', 'Signal_line', 'MACD_hist', \n",
|
||
" # 'sentiment_panic_greed_index',\n",
|
||
" 'up_ratio_20d', 'volume_change_rate', 'volatility',\n",
|
||
" 'amount_change_rate', 'amount_mean'],\n",
|
||
" aggfunc='last'\n",
|
||
" )\n",
|
||
"\n",
|
||
" df_final.columns = [f\"{col[1]}_{col[0]}\" for col in df_final.columns]\n",
|
||
" df_final = df_final.reset_index()\n",
|
||
"\n",
|
||
" return df_final\n",
|
||
"\n",
|
||
"\n",
|
||
"# 使用函数\n",
|
||
"h5_filename = '/mnt/d/PyProject/NewStock/data/index_data.h5'\n",
|
||
"index_data = generate_index_indicators(h5_filename)\n",
|
||
"index_data = index_data.dropna()\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 7,
|
||
"id": "a735bc02ceb4d872",
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2025-04-03T12:47:10.821169Z",
|
||
"start_time": "2025-04-03T12:47:10.751831Z"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"import talib\n",
|
||
"import numpy as np"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 8,
|
||
"id": "53f86ddc0677a6d7",
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2025-04-03T12:47:15.944254Z",
|
||
"start_time": "2025-04-03T12:47:10.826179Z"
|
||
},
|
||
"jupyter": {
|
||
"source_hidden": true
|
||
},
|
||
"scrolled": true
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"from main.utils.factor import get_act_factor\n",
|
||
"\n",
|
||
"\n",
|
||
"def read_industry_data(h5_filename):\n",
|
||
" # 读取 H5 文件中所有的行业数据\n",
|
||
" industry_data = pd.read_hdf(h5_filename, key='sw_daily', columns=[\n",
|
||
" 'ts_code', 'trade_date', 'open', 'close', 'high', 'low', 'pe', 'pb', 'vol'\n",
|
||
" ]) # 假设 H5 文件的键是 'industry_data'\n",
|
||
" industry_data = industry_data.sort_values(by=['ts_code', 'trade_date'])\n",
|
||
" industry_data = industry_data.reindex()\n",
|
||
" industry_data['trade_date'] = pd.to_datetime(industry_data['trade_date'], format='%Y%m%d')\n",
|
||
"\n",
|
||
" grouped = industry_data.groupby('ts_code', group_keys=False)\n",
|
||
" industry_data['obv'] = grouped.apply(\n",
|
||
" lambda x: pd.Series(talib.OBV(x['close'].values, x['vol'].values), index=x.index)\n",
|
||
" )\n",
|
||
" industry_data['return_5'] = grouped['close'].apply(lambda x: x / x.shift(5) - 1)\n",
|
||
" industry_data['return_20'] = grouped['close'].apply(lambda x: x / x.shift(20) - 1)\n",
|
||
"\n",
|
||
" industry_data = get_act_factor(industry_data, cat=False)\n",
|
||
" industry_data = industry_data.sort_values(by=['trade_date', 'ts_code'])\n",
|
||
"\n",
|
||
" # # 计算每天每个 ts_code 的因子和当天所有 ts_code 的中位数的偏差\n",
|
||
" # factor_columns = ['obv', 'return_5', 'return_20', 'act_factor1', 'act_factor2', 'act_factor3', 'act_factor4'] # 因子列\n",
|
||
" # \n",
|
||
" # for factor in factor_columns:\n",
|
||
" # if factor in industry_data.columns:\n",
|
||
" # # 计算每天每个 ts_code 的因子值与当天所有 ts_code 的中位数的偏差\n",
|
||
" # industry_data[f'{factor}_deviation'] = industry_data.groupby('trade_date')[factor].transform(\n",
|
||
" # lambda x: x - x.mean())\n",
|
||
"\n",
|
||
" industry_data['return_5_percentile'] = industry_data.groupby('trade_date')['return_5'].transform(\n",
|
||
" lambda x: x.rank(pct=True))\n",
|
||
" industry_data['return_20_percentile'] = industry_data.groupby('trade_date')['return_20'].transform(\n",
|
||
" lambda x: x.rank(pct=True))\n",
|
||
"\n",
|
||
" # cs_rank_intraday_range(industry_data)\n",
|
||
" # cs_rank_close_pos_in_range(industry_data)\n",
|
||
"\n",
|
||
" industry_data = industry_data.drop(columns=['open', 'close', 'high', 'low', 'pe', 'pb', 'vol'])\n",
|
||
"\n",
|
||
" industry_data = industry_data.rename(\n",
|
||
" columns={col: f'industry_{col}' for col in industry_data.columns if col not in ['ts_code', 'trade_date']})\n",
|
||
"\n",
|
||
" industry_data = industry_data.rename(columns={'ts_code': 'cat_l2_code'})\n",
|
||
" return industry_data\n",
|
||
"\n",
|
||
"\n",
|
||
"industry_df = read_industry_data('/mnt/d/PyProject/NewStock/data/sw_daily.h5')\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 9,
|
||
"id": "dbe2fd8021b9417f",
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2025-04-03T12:47:15.969344Z",
|
||
"start_time": "2025-04-03T12:47:15.963327Z"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"['ts_code', 'open', 'close', 'high', 'low', 'circ_mv', 'total_mv', 'is_st', 'up_limit', 'down_limit', 'buy_sm_vol', 'sell_sm_vol', 'buy_lg_vol', 'sell_lg_vol', 'buy_elg_vol', 'sell_elg_vol', 'net_mf_vol', 'his_low', 'his_high', 'cost_5pct', 'cost_15pct', 'cost_50pct', 'cost_85pct', 'cost_95pct', 'weight_avg', 'in_date']\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"origin_columns = df.columns.tolist()\n",
|
||
"origin_columns = [col for col in origin_columns if\n",
|
||
" col not in ['turnover_rate', 'pe_ttm', 'volume_ratio', 'vol', 'pct_chg', 'l2_code', 'winner_rate']]\n",
|
||
"origin_columns = [col for col in origin_columns if col not in index_data.columns]\n",
|
||
"origin_columns = [col for col in origin_columns if 'cyq' not in col]\n",
|
||
"print(origin_columns)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 10,
|
||
"id": "85c3e3d0235ffffa",
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2025-04-03T12:47:16.089879Z",
|
||
"start_time": "2025-04-03T12:47:15.990101Z"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"fina_indicator_df = read_and_merge_h5_data('/mnt/d/PyProject/NewStock/data/fina_indicator.h5', key='fina_indicator',\n",
|
||
" columns=['ts_code', 'ann_date', 'undist_profit_ps', 'ocfps', 'bps'],\n",
|
||
" df=None)\n",
|
||
"cashflow_df = read_and_merge_h5_data('/mnt/d/PyProject/NewStock/data/cashflow.h5', key='cashflow',\n",
|
||
" columns=['ts_code', 'ann_date', 'n_cashflow_act'],\n",
|
||
" df=None)\n",
|
||
"balancesheet_df = read_and_merge_h5_data('/mnt/d/PyProject/NewStock/data/balancesheet.h5', key='balancesheet',\n",
|
||
" columns=['ts_code', 'ann_date', 'money_cap', 'total_liab'],\n",
|
||
" df=None)\n",
|
||
"top_list_df = read_and_merge_h5_data('/mnt/d/PyProject/NewStock/data/top_list.h5', key='top_list',\n",
|
||
" columns=['ts_code', 'trade_date', 'reason'],\n",
|
||
" df=None)\n",
|
||
"\n",
|
||
"top_list_df = top_list_df.sort_values(by='trade_date', ascending=False).drop_duplicates(subset=['ts_code', 'trade_date'], keep='first').sort_values(by='trade_date')\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 11,
|
||
"id": "92d84ce15a562ec6",
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2025-04-03T13:08:01.612695Z",
|
||
"start_time": "2025-04-03T12:47:16.121802Z"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"使用 'ann_date' 作为财务数据生效日期。\n",
|
||
"警告: 从 financial_data_subset 中移除了 366 行,因为其 'ts_code' 或 'ann_date' 列存在空值。\n",
|
||
"使用 'ann_date' 作为财务数据生效日期。\n",
|
||
"警告: 从 financial_data_subset 中移除了 366 行,因为其 'ts_code' 或 'ann_date' 列存在空值。\n",
|
||
"开始计算因子: AR, BR (原地修改)...\n",
|
||
"因子 AR, BR 计算成功。\n",
|
||
"因子 AR, BR 计算流程结束。\n",
|
||
"使用 'ann_date' 作为财务数据生效日期。\n",
|
||
"使用 'ann_date' 作为财务数据生效日期。\n",
|
||
"使用 'ann_date' 作为财务数据生效日期。\n",
|
||
"使用 'ann_date' 作为财务数据生效日期。\n",
|
||
"警告: 从 financial_data_subset 中移除了 366 行,因为其 'ts_code' 或 'ann_date' 列存在空值。\n",
|
||
"计算 BBI...\n",
|
||
"--- 计算日级别偏离度 (使用 pct_chg) ---\n",
|
||
"--- 计算日级别动量基准 (使用 pct_chg) ---\n",
|
||
"日级别动量基准计算完成 (使用 pct_chg)。\n",
|
||
"日级别偏离度计算完成 (使用 pct_chg)。\n",
|
||
"--- 计算日级别行业偏离度 (使用 pct_chg 和行业基准) ---\n",
|
||
"--- 计算日级别行业动量基准 (使用 pct_chg 和 cat_l2_code) ---\n",
|
||
"错误: 计算日级别行业动量基准需要以下列: ['pct_chg', 'cat_l2_code', 'trade_date', 'ts_code']。\n",
|
||
"错误: 计算日级别行业偏离度需要以下列: ['pct_chg', 'daily_industry_positive_benchmark', 'daily_industry_negative_benchmark']。请先运行 daily_industry_momentum_benchmark(df)。\n",
|
||
"Index(['ts_code', 'trade_date', 'open', 'close', 'high', 'low', 'vol',\n",
|
||
" 'pct_chg', 'turnover_rate', 'pe_ttm', 'circ_mv', 'total_mv',\n",
|
||
" 'volume_ratio', 'is_st', 'up_limit', 'down_limit', 'buy_sm_vol',\n",
|
||
" 'sell_sm_vol', 'buy_lg_vol', 'sell_lg_vol', 'buy_elg_vol',\n",
|
||
" 'sell_elg_vol', 'net_mf_vol', 'his_low', 'his_high', 'cost_5pct',\n",
|
||
" 'cost_15pct', 'cost_50pct', 'cost_85pct', 'cost_95pct', 'weight_avg',\n",
|
||
" 'winner_rate', 'l2_code', 'undist_profit_ps', 'ocfps', 'AR', 'BR',\n",
|
||
" 'AR_BR', 'log_circ_mv', 'cashflow_to_ev_factor', 'book_to_price_ratio',\n",
|
||
" 'turnover_rate_mean_5', 'variance_20', 'bbi_ratio_factor',\n",
|
||
" 'daily_deviation', 'lg_elg_net_buy_vol', 'flow_lg_elg_intensity',\n",
|
||
" 'sm_net_buy_vol', 'flow_divergence_diff', 'flow_divergence_ratio',\n",
|
||
" 'total_buy_vol', 'lg_elg_buy_prop', 'flow_struct_buy_change',\n",
|
||
" 'lg_elg_net_buy_vol_change', 'flow_lg_elg_accel',\n",
|
||
" 'chip_concentration_range', 'chip_skewness', 'floating_chip_proxy',\n",
|
||
" 'cost_support_15pct_change', 'cat_winner_price_zone',\n",
|
||
" 'flow_chip_consistency', 'profit_taking_vs_absorb', '_is_positive',\n",
|
||
" '_is_negative', 'cat_is_positive', '_pos_returns', '_neg_returns',\n",
|
||
" '_pos_returns_sq', '_neg_returns_sq', 'upside_vol', 'downside_vol',\n",
|
||
" 'vol_ratio', 'return_skew', 'return_kurtosis', 'volume_change_rate',\n",
|
||
" 'cat_volume_breakout', 'turnover_deviation', 'cat_turnover_spike',\n",
|
||
" 'avg_volume_ratio', 'cat_volume_ratio_breakout', 'vol_spike',\n",
|
||
" 'vol_std_5', 'atr_14', 'atr_6', 'obv'],\n",
|
||
" dtype='object')\n",
|
||
"Calculating lg_flow_mom_corr_20_60...\n",
|
||
"Finished lg_flow_mom_corr_20_60.\n",
|
||
"Calculating lg_flow_accel...\n",
|
||
"Finished lg_flow_accel.\n",
|
||
"Calculating profit_pressure...\n",
|
||
"Finished profit_pressure.\n",
|
||
"Calculating underwater_resistance...\n",
|
||
"Finished underwater_resistance.\n",
|
||
"Calculating cost_conc_std_20...\n",
|
||
"Finished cost_conc_std_20.\n",
|
||
"Calculating profit_decay_20...\n",
|
||
"Finished profit_decay_20.\n",
|
||
"Calculating vol_amp_loss_20...\n",
|
||
"Finished vol_amp_loss_20.\n",
|
||
"Calculating vol_drop_profit_cnt_5...\n",
|
||
"Finished vol_drop_profit_cnt_5.\n",
|
||
"Calculating lg_flow_vol_interact_20...\n",
|
||
"Finished lg_flow_vol_interact_20.\n",
|
||
"Calculating cost_break_confirm_cnt_5...\n",
|
||
"Finished cost_break_confirm_cnt_5.\n",
|
||
"Calculating atr_norm_channel_pos_14...\n",
|
||
"Finished atr_norm_channel_pos_14.\n",
|
||
"Calculating turnover_diff_skew_20...\n",
|
||
"Finished turnover_diff_skew_20.\n",
|
||
"Calculating lg_sm_flow_diverge_20...\n",
|
||
"Finished lg_sm_flow_diverge_20.\n",
|
||
"Calculating pullback_strong_20_20...\n",
|
||
"Finished pullback_strong_20_20.\n",
|
||
"Calculating vol_wgt_hist_pos_20...\n",
|
||
"Finished vol_wgt_hist_pos_20.\n",
|
||
"Calculating vol_adj_roc_20...\n",
|
||
"Finished vol_adj_roc_20.\n",
|
||
"Calculating cs_rank_net_lg_flow_val...\n",
|
||
"Finished cs_rank_net_lg_flow_val.\n",
|
||
"Calculating cs_rank_flow_divergence...\n",
|
||
"Finished cs_rank_flow_divergence.\n",
|
||
"Calculating cs_rank_ind_adj_lg_flow...\n",
|
||
"Finished cs_rank_ind_adj_lg_flow.\n",
|
||
"Calculating cs_rank_elg_buy_ratio...\n",
|
||
"Finished cs_rank_elg_buy_ratio.\n",
|
||
"Calculating cs_rank_rel_profit_margin...\n",
|
||
"Finished cs_rank_rel_profit_margin.\n",
|
||
"Calculating cs_rank_cost_breadth...\n",
|
||
"Finished cs_rank_cost_breadth.\n",
|
||
"Calculating cs_rank_dist_to_upper_cost...\n",
|
||
"Finished cs_rank_dist_to_upper_cost.\n",
|
||
"Calculating cs_rank_winner_rate...\n",
|
||
"Finished cs_rank_winner_rate.\n",
|
||
"Calculating cs_rank_intraday_range...\n",
|
||
"Finished cs_rank_intraday_range.\n",
|
||
"Calculating cs_rank_close_pos_in_range...\n",
|
||
"Finished cs_rank_close_pos_in_range.\n",
|
||
"Calculating cs_rank_opening_gap...\n",
|
||
"Error calculating cs_rank_opening_gap: Missing 'pre_close' column. Assigning NaN.\n",
|
||
"Calculating cs_rank_pos_in_hist_range...\n",
|
||
"Finished cs_rank_pos_in_hist_range.\n",
|
||
"Calculating cs_rank_vol_x_profit_margin...\n",
|
||
"Finished cs_rank_vol_x_profit_margin.\n",
|
||
"Calculating cs_rank_lg_flow_price_concordance...\n",
|
||
"Finished cs_rank_lg_flow_price_concordance.\n",
|
||
"Calculating cs_rank_turnover_per_winner...\n",
|
||
"Finished cs_rank_turnover_per_winner.\n",
|
||
"Calculating cs_rank_ind_cap_neutral_pe (Placeholder - requires statsmodels)...\n",
|
||
"Finished cs_rank_ind_cap_neutral_pe (Placeholder).\n",
|
||
"Calculating cs_rank_volume_ratio...\n",
|
||
"Finished cs_rank_volume_ratio.\n",
|
||
"Calculating cs_rank_elg_buy_sell_sm_ratio...\n",
|
||
"Finished cs_rank_elg_buy_sell_sm_ratio.\n",
|
||
"Calculating cs_rank_cost_dist_vol_ratio...\n",
|
||
"Finished cs_rank_cost_dist_vol_ratio.\n",
|
||
"Calculating cs_rank_size...\n",
|
||
"Finished cs_rank_size.\n",
|
||
"<class 'pandas.core.frame.DataFrame'>\n",
|
||
"RangeIndex: 4554725 entries, 0 to 4554724\n",
|
||
"Columns: 178 entries, ts_code to cs_rank_size\n",
|
||
"dtypes: bool(10), datetime64[ns](1), float64(162), int64(3), object(2)\n",
|
||
"memory usage: 5.7+ GB\n",
|
||
"None\n",
|
||
"['ts_code', 'trade_date', 'open', 'close', 'high', 'low', 'vol', 'pct_chg', 'turnover_rate', 'pe_ttm', 'circ_mv', 'total_mv', 'volume_ratio', 'is_st', 'up_limit', 'down_limit', 'buy_sm_vol', 'sell_sm_vol', 'buy_lg_vol', 'sell_lg_vol', 'buy_elg_vol', 'sell_elg_vol', 'net_mf_vol', 'his_low', 'his_high', 'cost_5pct', 'cost_15pct', 'cost_50pct', 'cost_85pct', 'cost_95pct', 'weight_avg', 'winner_rate', 'cat_l2_code', 'undist_profit_ps', 'ocfps', 'AR', 'BR', 'AR_BR', 'log_circ_mv', 'cashflow_to_ev_factor', 'book_to_price_ratio', 'turnover_rate_mean_5', 'variance_20', 'bbi_ratio_factor', 'daily_deviation', 'lg_elg_net_buy_vol', 'flow_lg_elg_intensity', 'sm_net_buy_vol', 'flow_divergence_diff', 'flow_divergence_ratio', 'total_buy_vol', 'lg_elg_buy_prop', 'flow_struct_buy_change', 'lg_elg_net_buy_vol_change', 'flow_lg_elg_accel', 'chip_concentration_range', 'chip_skewness', 'floating_chip_proxy', 'cost_support_15pct_change', 'cat_winner_price_zone', 'flow_chip_consistency', 'profit_taking_vs_absorb', 'cat_is_positive', 'upside_vol', 'downside_vol', 'vol_ratio', 'return_skew', 'return_kurtosis', 'volume_change_rate', 'cat_volume_breakout', 'turnover_deviation', 'cat_turnover_spike', 'avg_volume_ratio', 'cat_volume_ratio_breakout', 'vol_spike', 'vol_std_5', 'atr_14', 'atr_6', 'obv', 'maobv_6', 'rsi_3', 'return_5', 'return_20', 'std_return_5', 'std_return_90', 'std_return_90_2', 'act_factor1', 'act_factor2', 'act_factor3', 'act_factor4', 'rank_act_factor1', 'rank_act_factor2', 'rank_act_factor3', 'cov', 'delta_cov', 'alpha_22_improved', 'alpha_003', 'alpha_007', 'alpha_013', 'vol_break', 'weight_roc5', 'price_cost_divergence', 'smallcap_concentration', 'cost_stability', 'high_cost_break_days', 'liquidity_risk', 'turnover_std', 'mv_volatility', 'volume_growth', 'mv_growth', 'momentum_factor', 'resonance_factor', 'log_close', 'cat_vol_spike', 'up', 'down', 'obv_maobv_6', 'std_return_5_over_std_return_90', 'std_return_90_minus_std_return_90_2', 'cat_af2', 'cat_af3', 'cat_af4', 'act_factor5', 'act_factor6', 'active_buy_volume_large', 'active_buy_volume_big', 'active_buy_volume_small', 'buy_lg_vol_minus_sell_lg_vol', 'buy_elg_vol_minus_sell_elg_vol', 'ctrl_strength', 'low_cost_dev', 'asymmetry', 'lock_factor', 'cat_vol_break', 'cost_atr_adj', 'cat_golden_resonance', 'mv_turnover_ratio', 'mv_adjusted_volume', 'mv_weighted_turnover', 'nonlinear_mv_volume', 'mv_volume_ratio', 'mv_momentum', 'lg_flow_mom_corr_20_60', 'lg_flow_accel', 'profit_pressure', 'underwater_resistance', 'cost_conc_std_20', 'profit_decay_20', 'vol_amp_loss_20', 'vol_drop_profit_cnt_5', 'lg_flow_vol_interact_20', 'cost_break_confirm_cnt_5', 'atr_norm_channel_pos_14', 'turnover_diff_skew_20', 'lg_sm_flow_diverge_20', 'pullback_strong_20_20', 'vol_wgt_hist_pos_20', 'vol_adj_roc_20', 'cs_rank_net_lg_flow_val', 'cs_rank_flow_divergence', 'cs_rank_ind_adj_lg_flow', 'cs_rank_elg_buy_ratio', 'cs_rank_rel_profit_margin', 'cs_rank_cost_breadth', 'cs_rank_dist_to_upper_cost', 'cs_rank_winner_rate', 'cs_rank_intraday_range', 'cs_rank_close_pos_in_range', 'cs_rank_opening_gap', 'cs_rank_pos_in_hist_range', 'cs_rank_vol_x_profit_margin', 'cs_rank_lg_flow_price_concordance', 'cs_rank_turnover_per_winner', 'cs_rank_ind_cap_neutral_pe', 'cs_rank_volume_ratio', 'cs_rank_elg_buy_sell_sm_ratio', 'cs_rank_cost_dist_vol_ratio', 'cs_rank_size']\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"\n",
|
||
"import numpy as np\n",
|
||
"from main.factor.factor import *\n",
|
||
"\n",
|
||
"def filter_data(df):\n",
|
||
" # df = df.groupby('trade_date').apply(lambda x: x.nlargest(1000, 'act_factor1'))\n",
|
||
" df = df[~df['is_st']]\n",
|
||
" df = df[~df['ts_code'].str.endswith('BJ')]\n",
|
||
" df = df[~df['ts_code'].str.startswith('30')]\n",
|
||
" df = df[~df['ts_code'].str.startswith('68')]\n",
|
||
" df = df[~df['ts_code'].str.startswith('8')]\n",
|
||
" df = df[df['trade_date'] >= '2019-01-01']\n",
|
||
" if 'in_date' in df.columns:\n",
|
||
" df = df.drop(columns=['in_date'])\n",
|
||
" df = df.reset_index(drop=True)\n",
|
||
" return df\n",
|
||
"\n",
|
||
"gc.collect()\n",
|
||
"\n",
|
||
"df = filter_data(df)\n",
|
||
"df = df.sort_values(by=['ts_code', 'trade_date'])\n",
|
||
"\n",
|
||
"# df = price_minus_deduction_price(df, n=120)\n",
|
||
"# df = price_deduction_price_diff_ratio_to_sma(df, n=120)\n",
|
||
"# df = cat_price_vs_sma_vs_deduction_price(df, n=120)\n",
|
||
"# df = cat_reason(df, top_list_df)\n",
|
||
"# df = cat_is_on_top_list(df, top_list_df)\n",
|
||
"\n",
|
||
"df = add_financial_factor(df, fina_indicator_df, factor_value_col='undist_profit_ps')\n",
|
||
"df = add_financial_factor(df, fina_indicator_df, factor_value_col='ocfps')\n",
|
||
"calculate_arbr(df, N=26)\n",
|
||
"df['log_circ_mv'] = np.log(df['circ_mv'])\n",
|
||
"df = calculate_cashflow_to_ev_factor(df, cashflow_df, balancesheet_df)\n",
|
||
"df = caculate_book_to_price_ratio(df, fina_indicator_df)\n",
|
||
"df = turnover_rate_n(df, n=5)\n",
|
||
"df = variance_n(df, n=20)\n",
|
||
"df = bbi_ratio_factor(df)\n",
|
||
"df = daily_deviation(df)\n",
|
||
"df = daily_industry_deviation(df)\n",
|
||
"df, _ = get_rolling_factor(df)\n",
|
||
"df, _ = get_simple_factor(df)\n",
|
||
"\n",
|
||
"df = df.rename(columns={'l1_code': 'cat_l1_code'})\n",
|
||
"df = df.rename(columns={'l2_code': 'cat_l2_code'})\n",
|
||
"\n",
|
||
"lg_flow_mom_corr(df, N=20, M=60)\n",
|
||
"lg_flow_accel(df)\n",
|
||
"profit_pressure(df)\n",
|
||
"underwater_resistance(df)\n",
|
||
"cost_conc_std(df, N=20)\n",
|
||
"profit_decay(df, N=20)\n",
|
||
"vol_amp_loss(df, N=20)\n",
|
||
"vol_drop_profit_cnt(df, N=20, M=5)\n",
|
||
"lg_flow_vol_interact(df, N=20)\n",
|
||
"cost_break_confirm_cnt(df, M=5)\n",
|
||
"atr_norm_channel_pos(df, N=14)\n",
|
||
"turnover_diff_skew(df, N=20)\n",
|
||
"lg_sm_flow_diverge(df, N=20)\n",
|
||
"pullback_strong(df, N=20, M=20)\n",
|
||
"vol_wgt_hist_pos(df, N=20)\n",
|
||
"vol_adj_roc(df, N=20)\n",
|
||
"\n",
|
||
"cs_rank_net_lg_flow_val(df)\n",
|
||
"cs_rank_flow_divergence(df)\n",
|
||
"cs_rank_industry_adj_lg_flow(df) # Needs cat_l2_code\n",
|
||
"cs_rank_elg_buy_ratio(df)\n",
|
||
"cs_rank_rel_profit_margin(df)\n",
|
||
"cs_rank_cost_breadth(df)\n",
|
||
"cs_rank_dist_to_upper_cost(df)\n",
|
||
"cs_rank_winner_rate(df)\n",
|
||
"cs_rank_intraday_range(df)\n",
|
||
"cs_rank_close_pos_in_range(df)\n",
|
||
"cs_rank_opening_gap(df) # Needs pre_close\n",
|
||
"cs_rank_pos_in_hist_range(df) # Needs his_low, his_high\n",
|
||
"cs_rank_vol_x_profit_margin(df)\n",
|
||
"cs_rank_lg_flow_price_concordance(df)\n",
|
||
"cs_rank_turnover_per_winner(df)\n",
|
||
"cs_rank_ind_cap_neutral_pe(df) # Placeholder - needs external libraries\n",
|
||
"cs_rank_volume_ratio(df) # Needs volume_ratio\n",
|
||
"cs_rank_elg_buy_sell_sm_ratio(df)\n",
|
||
"cs_rank_cost_dist_vol_ratio(df) # Needs volume_ratio\n",
|
||
"cs_rank_size(df) # Needs circ_mv\n",
|
||
"\n",
|
||
"\n",
|
||
"# df = df.merge(index_data, on='trade_date', how='left')\n",
|
||
"\n",
|
||
"print(df.info())\n",
|
||
"print(df.columns.tolist())"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 12,
|
||
"id": "b87b938028afa206",
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2025-04-03T13:08:03.658725Z",
|
||
"start_time": "2025-04-03T13:08:02.469611Z"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"from scipy.stats import ks_2samp, wasserstein_distance\n",
|
||
"\n",
|
||
"\n",
|
||
"def remove_shifted_features(train_data, test_data, feature_columns, ks_threshold=0.05, wasserstein_threshold=0.1,\n",
|
||
" importance_threshold=0.05):\n",
|
||
" dropped_features = []\n",
|
||
"\n",
|
||
" # **统计数据漂移**\n",
|
||
" numeric_columns = train_data.select_dtypes(include=['float64', 'int64']).columns\n",
|
||
" numeric_columns = [col for col in numeric_columns if col in feature_columns]\n",
|
||
" for feature in numeric_columns:\n",
|
||
" ks_stat, p_value = ks_2samp(train_data[feature], test_data[feature])\n",
|
||
" wasserstein_dist = wasserstein_distance(train_data[feature], test_data[feature])\n",
|
||
"\n",
|
||
" if p_value < ks_threshold or wasserstein_dist > wasserstein_threshold:\n",
|
||
" dropped_features.append(feature)\n",
|
||
"\n",
|
||
" print(f\"检测到 {len(dropped_features)} 个可能漂移的特征: {dropped_features}\")\n",
|
||
"\n",
|
||
" # **应用阈值进行最终筛选**\n",
|
||
" filtered_features = [f for f in feature_columns if f not in dropped_features]\n",
|
||
"\n",
|
||
" return filtered_features, dropped_features\n",
|
||
"\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 13,
|
||
"id": "f4f16d63ad18d1bc",
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2025-04-03T13:08:03.670700Z",
|
||
"start_time": "2025-04-03T13:08:03.665739Z"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"import pandas as pd\n",
|
||
"import numpy as np\n",
|
||
"import statsmodels.api as sm # 用于中性化回归\n",
|
||
"from tqdm import tqdm # 可选,用于显示进度条\n",
|
||
"\n",
|
||
"# --- 常量 ---\n",
|
||
"epsilon = 1e-10 # 防止除零\n",
|
||
"\n",
|
||
"# --- 1. 中位数去极值 (MAD) ---\n",
|
||
"\n",
|
||
"def cs_mad_filter(df: pd.DataFrame,\n",
|
||
" features: list,\n",
|
||
" k: float = 3.0,\n",
|
||
" scale_factor: float = 1.4826):\n",
|
||
" \"\"\"\n",
|
||
" 对指定特征列进行截面 MAD 去极值处理 (原地修改)。\n",
|
||
"\n",
|
||
" 方法: 对每日截面数据,计算 median 和 MAD,\n",
|
||
" 将超出 [median - k * scale * MAD, median + k * scale * MAD] 范围的值\n",
|
||
" 替换为边界值 (Winsorization)。\n",
|
||
" scale_factor=1.4826 使得 MAD 约等于正态分布的标准差。\n",
|
||
"\n",
|
||
" Args:\n",
|
||
" df (pd.DataFrame): 输入 DataFrame,需包含 'trade_date' 和 features 列。\n",
|
||
" features (list): 需要处理的特征列名列表。\n",
|
||
" k (float): MAD 的倍数,用于确定边界。默认为 3.0。\n",
|
||
" scale_factor (float): MAD 的缩放因子。默认为 1.4826。\n",
|
||
"\n",
|
||
" WARNING: 此函数会原地修改输入的 DataFrame 'df'。\n",
|
||
" \"\"\"\n",
|
||
" print(f\"开始截面 MAD 去极值处理 (k={k})...\")\n",
|
||
" if not all(col in df.columns for col in features):\n",
|
||
" missing = [col for col in features if col not in df.columns]\n",
|
||
" print(f\"错误: DataFrame 中缺少以下特征列: {missing}。跳过去极值处理。\")\n",
|
||
" return\n",
|
||
"\n",
|
||
" grouped = df.groupby('trade_date')\n",
|
||
"\n",
|
||
" for col in tqdm(features, desc=\"MAD Filtering\"):\n",
|
||
" try:\n",
|
||
" # 计算截面中位数\n",
|
||
" median = grouped[col].transform('median')\n",
|
||
" # 计算截面 MAD (Median Absolute Deviation from Median)\n",
|
||
" mad = (df[col] - median).abs().groupby(df['trade_date']).transform('median')\n",
|
||
"\n",
|
||
" # 计算上下边界\n",
|
||
" lower_bound = median - k * scale_factor * mad\n",
|
||
" upper_bound = median + k * scale_factor * mad\n",
|
||
"\n",
|
||
" # 原地应用 clip\n",
|
||
" df[col] = np.clip(df[col], lower_bound, upper_bound)\n",
|
||
"\n",
|
||
" except KeyError:\n",
|
||
" print(f\"警告: 列 '{col}' 可能不存在或在分组中出错,跳过此列的 MAD 处理。\")\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"警告: 处理列 '{col}' 时发生错误: {e},跳过此列的 MAD 处理。\")\n",
|
||
"\n",
|
||
" print(\"截面 MAD 去极值处理完成。\")\n",
|
||
"\n",
|
||
"\n",
|
||
"# --- 2. 行业市值中性化 ---\n",
|
||
"\n",
|
||
"def cs_neutralize_industry_cap(df: pd.DataFrame,\n",
|
||
" features: list,\n",
|
||
" industry_col: str = 'cat_l2_code',\n",
|
||
" market_cap_col: str = 'circ_mv'):\n",
|
||
" \"\"\"\n",
|
||
" 对指定特征列进行截面行业和对数市值中性化 (原地修改)。\n",
|
||
" 使用 OLS 回归: feature ~ 1 + log(market_cap) + C(industry)\n",
|
||
" 将回归残差写回原特征列。\n",
|
||
"\n",
|
||
" Args:\n",
|
||
" df (pd.DataFrame): 输入 DataFrame,需包含 'trade_date', features 列,\n",
|
||
" industry_col, market_cap_col。\n",
|
||
" features (list): 需要处理的特征列名列表。\n",
|
||
" industry_col (str): 行业分类列名。\n",
|
||
" market_cap_col (str): 流通市值列名。\n",
|
||
"\n",
|
||
" WARNING: 此函数会原地修改输入的 DataFrame 'df' 的 features 列。\n",
|
||
" 计算量较大,可能耗时较长。\n",
|
||
" 需要安装 statsmodels 库 (pip install statsmodels)。\n",
|
||
" \"\"\"\n",
|
||
" print(\"开始截面行业市值中性化...\")\n",
|
||
" required_cols = features + ['trade_date', industry_col, market_cap_col]\n",
|
||
" if not all(col in df.columns for col in required_cols):\n",
|
||
" missing = [col for col in required_cols if col not in df.columns]\n",
|
||
" print(f\"错误: DataFrame 中缺少必需列: {missing}。无法进行中性化。\")\n",
|
||
" return\n",
|
||
"\n",
|
||
" # 预处理:计算 log 市值,处理 industry code 可能的 NaN\n",
|
||
" log_cap_col = '_log_market_cap'\n",
|
||
" df[log_cap_col] = np.log1p(df[market_cap_col]) # log1p 处理 0 值\n",
|
||
" # df[industry_col] = df[industry_col].cat.add_categories('UnknownIndustry')\n",
|
||
" # df[industry_col] = df[industry_col].fillna('UnknownIndustry') # 填充行业 NaN\n",
|
||
" # df[industry_col] = df[industry_col].astype('category') # 转为类别,ols 会自动处理\n",
|
||
"\n",
|
||
" dates = df['trade_date'].unique()\n",
|
||
" all_residuals = [] # 用于收集所有日期的残差\n",
|
||
"\n",
|
||
" for date in tqdm(dates, desc=\"Neutralizing\"):\n",
|
||
" daily_data = df.loc[df['trade_date'] == date, features + [log_cap_col, industry_col]].copy() # 使用 .loc 获取副本\n",
|
||
"\n",
|
||
" # 准备自变量 X (常数项 + log市值 + 行业哑变量)\n",
|
||
" X = daily_data[[log_cap_col]]\n",
|
||
" X = sm.add_constant(X, prepend=True) # 添加常数项\n",
|
||
" # 创建行业哑变量 (drop_first=True 避免共线性)\n",
|
||
" industry_dummies = pd.get_dummies(daily_data[industry_col], prefix=industry_col, drop_first=True)\n",
|
||
" industry_dummies = industry_dummies.astype(int)\n",
|
||
" X = pd.concat([X, industry_dummies], axis=1)\n",
|
||
"\n",
|
||
" daily_residuals = daily_data[[col for col in features]].copy() # 创建用于存储残差的df\n",
|
||
"\n",
|
||
" for col in features:\n",
|
||
" Y = daily_data[col]\n",
|
||
"\n",
|
||
" # 处理 NaN 值,确保 X 和 Y 在相同位置有有效值\n",
|
||
" valid_mask = Y.notna() & X.notna().all(axis=1)\n",
|
||
" if valid_mask.sum() < (X.shape[1] + 1): # 数据点不足以估计模型\n",
|
||
" print(f\"警告: 日期 {date}, 特征 {col} 有效数据不足 ({valid_mask.sum()}个),无法中性化,填充 NaN。\")\n",
|
||
" daily_residuals[col] = np.nan\n",
|
||
" continue\n",
|
||
"\n",
|
||
" Y_valid = Y[valid_mask]\n",
|
||
" X_valid = X[valid_mask]\n",
|
||
"\n",
|
||
" # 执行 OLS 回归\n",
|
||
" try:\n",
|
||
" model = sm.OLS(Y_valid.to_numpy(), X_valid.to_numpy())\n",
|
||
" results = model.fit()\n",
|
||
" # 将残差填回对应位置\n",
|
||
" daily_residuals.loc[valid_mask, col] = results.resid\n",
|
||
" daily_residuals.loc[~valid_mask, col] = np.nan # 原本无效的位置填充 NaN\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"警告: 日期 {date}, 特征 {col} 回归失败: {e},填充 NaN。\")\n",
|
||
" daily_residuals[col] = np.nan\n",
|
||
" break\n",
|
||
"\n",
|
||
" all_residuals.append(daily_residuals)\n",
|
||
"\n",
|
||
" # 合并所有日期的残差结果\n",
|
||
" if all_residuals:\n",
|
||
" residuals_df = pd.concat(all_residuals)\n",
|
||
" # 将残差结果更新回原始 df (原地修改)\n",
|
||
" # 使用 update 比 merge 更适合基于索引的原地更新\n",
|
||
" # 确保 residuals_df 的索引与 df 中对应部分一致\n",
|
||
" df.update(residuals_df)\n",
|
||
" else:\n",
|
||
" print(\"没有有效的残差结果可以合并。\")\n",
|
||
"\n",
|
||
"\n",
|
||
" # 清理临时列\n",
|
||
" df.drop(columns=[log_cap_col], inplace=True)\n",
|
||
" print(\"截面行业市值中性化完成。\")\n",
|
||
"\n",
|
||
"\n",
|
||
"# --- 3. Z-Score 标准化 ---\n",
|
||
"\n",
|
||
"def cs_zscore_standardize(df: pd.DataFrame, features: list, epsilon: float = 1e-10):\n",
|
||
" \"\"\"\n",
|
||
" 对指定特征列进行截面 Z-Score 标准化 (原地修改)。\n",
|
||
" 方法: Z = (value - cross_sectional_mean) / (cross_sectional_std + epsilon)\n",
|
||
"\n",
|
||
" Args:\n",
|
||
" df (pd.DataFrame): 输入 DataFrame,需包含 'trade_date' 和 features 列。\n",
|
||
" features (list): 需要处理的特征列名列表。\n",
|
||
" epsilon (float): 防止除以零的小常数。\n",
|
||
"\n",
|
||
" WARNING: 此函数会原地修改输入的 DataFrame 'df'。\n",
|
||
" \"\"\"\n",
|
||
" print(\"开始截面 Z-Score 标准化...\")\n",
|
||
" if not all(col in df.columns for col in features):\n",
|
||
" missing = [col for col in features if col not in df.columns]\n",
|
||
" print(f\"错误: DataFrame 中缺少以下特征列: {missing}。跳过标准化处理。\")\n",
|
||
" return\n",
|
||
"\n",
|
||
" grouped = df.groupby('trade_date')\n",
|
||
"\n",
|
||
" for col in tqdm(features, desc=\"Standardizing\"):\n",
|
||
" try:\n",
|
||
" # 使用 transform 计算截面均值和标准差\n",
|
||
" mean = grouped[col].transform('mean')\n",
|
||
" std = grouped[col].transform('std')\n",
|
||
"\n",
|
||
" # 计算 Z-Score 并原地赋值\n",
|
||
" df[col] = (df[col] - mean) / (std + epsilon)\n",
|
||
"\n",
|
||
" except KeyError:\n",
|
||
" print(f\"警告: 列 '{col}' 可能不存在或在分组中出错,跳过此列的标准化处理。\")\n",
|
||
" except Exception as e:\n",
|
||
" print(f\"警告: 处理列 '{col}' 时发生错误: {e},跳过此列的标准化处理。\")\n",
|
||
"\n",
|
||
" print(\"截面 Z-Score 标准化完成。\")\n",
|
||
"\n",
|
||
"def fill_nan_with_daily_median(df: pd.DataFrame, feature_columns: list[str]) -> pd.DataFrame:\n",
|
||
" \"\"\"\n",
|
||
" 对指定特征列进行每日截面中位数填充缺失值 (NaN)。\n",
|
||
"\n",
|
||
" 参数:\n",
|
||
" df (pd.DataFrame): 包含多日数据的DataFrame,需要包含 'trade_date' 和 feature_columns 中的列。\n",
|
||
" feature_columns (list[str]): 需要进行缺失值填充的特征列名称列表。\n",
|
||
"\n",
|
||
" 返回:\n",
|
||
" pd.DataFrame: 包含缺失值填充后特征列的DataFrame。在输入DataFrame的副本上操作。\n",
|
||
" \"\"\"\n",
|
||
" processed_df = df.copy() # 在副本上操作,保留原始数据\n",
|
||
"\n",
|
||
" # 确保 trade_date 是 datetime 类型以便正确分组\n",
|
||
" processed_df['trade_date'] = pd.to_datetime(processed_df['trade_date'])\n",
|
||
"\n",
|
||
" def _fill_daily_nan(group):\n",
|
||
" # group 是某一个交易日的 DataFrame\n",
|
||
"\n",
|
||
" # 遍历指定的特征列\n",
|
||
" for feature_col in feature_columns:\n",
|
||
" # 检查列是否存在于当前分组中\n",
|
||
" if feature_col in group.columns:\n",
|
||
" # 计算当日该特征的中位数\n",
|
||
" median_val = group[feature_col].median()\n",
|
||
"\n",
|
||
" # 使用当日中位数填充该特征列的 NaN 值\n",
|
||
" # inplace=True 会直接修改 group DataFrame\n",
|
||
" group[feature_col].fillna(median_val, inplace=True)\n",
|
||
" # else:\n",
|
||
" # print(f\"Warning: Feature column '{feature_col}' not found in daily group for {group['trade_date'].iloc[0]}. Skipping.\")\n",
|
||
"\n",
|
||
" return group\n",
|
||
"\n",
|
||
" # 按交易日期分组,并应用每日填充函数\n",
|
||
" # group_keys=False 避免将分组键添加到结果索引中\n",
|
||
" filled_df = processed_df.groupby('trade_date', group_keys=False).apply(_fill_daily_nan)\n",
|
||
"\n",
|
||
" return filled_df"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 14,
|
||
"id": "40e6b68a91b30c79",
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2025-04-03T13:08:04.694262Z",
|
||
"start_time": "2025-04-03T13:08:03.694904Z"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"import pandas as pd\n",
|
||
"\n",
|
||
"\n",
|
||
"def remove_outliers_label_percentile(label: pd.Series, lower_percentile: float = 0.01, upper_percentile: float = 0.99,\n",
|
||
" log=True):\n",
|
||
" if not (0 <= lower_percentile < upper_percentile <= 1):\n",
|
||
" raise ValueError(\"Percentile values must satisfy 0 <= lower_percentile < upper_percentile <= 1.\")\n",
|
||
"\n",
|
||
" # Calculate lower and upper bounds based on percentiles\n",
|
||
" lower_bound = label.quantile(lower_percentile)\n",
|
||
" upper_bound = label.quantile(upper_percentile)\n",
|
||
"\n",
|
||
" # Filter out values outside the bounds\n",
|
||
" filtered_label = label[(label >= lower_bound) & (label <= upper_bound)]\n",
|
||
"\n",
|
||
" # Print the number of removed outliers\n",
|
||
" if log:\n",
|
||
" print(f\"Removed {len(label) - len(filtered_label)} outliers.\")\n",
|
||
" return filtered_label\n",
|
||
"\n",
|
||
"\n",
|
||
"def calculate_risk_adjusted_target(df, days=5):\n",
|
||
" df = df.sort_values(by=['ts_code', 'trade_date'])\n",
|
||
"\n",
|
||
" df['future_close'] = df.groupby('ts_code')['close'].shift(-days)\n",
|
||
" df['future_open'] = df.groupby('ts_code')['open'].shift(-1)\n",
|
||
" df['future_return'] = (df['future_close'] - df['future_open']) / df['future_open']\n",
|
||
"\n",
|
||
" df['future_volatility'] = df.groupby('ts_code')['future_return'].rolling(days, min_periods=1).std().reset_index(\n",
|
||
" level=0, drop=True)\n",
|
||
" sharpe_ratio = df['future_return'] * df['future_volatility']\n",
|
||
" sharpe_ratio.replace([np.inf, -np.inf], np.nan, inplace=True)\n",
|
||
"\n",
|
||
" return sharpe_ratio\n",
|
||
"\n",
|
||
"\n",
|
||
"def calculate_score(df, days=5, lambda_param=1.0):\n",
|
||
" def calculate_max_drawdown(prices):\n",
|
||
" peak = prices.iloc[0] # 初始化峰值\n",
|
||
" max_drawdown = 0 # 初始化最大回撤\n",
|
||
"\n",
|
||
" for price in prices:\n",
|
||
" if price > peak:\n",
|
||
" peak = price # 更新峰值\n",
|
||
" else:\n",
|
||
" drawdown = (peak - price) / peak # 计算当前回撤\n",
|
||
" max_drawdown = max(max_drawdown, drawdown) # 更新最大回撤\n",
|
||
"\n",
|
||
" return max_drawdown\n",
|
||
"\n",
|
||
" def compute_stock_score(stock_df):\n",
|
||
" stock_df = stock_df.sort_values(by=['trade_date'])\n",
|
||
" future_return = stock_df['future_return']\n",
|
||
" # 使用已有的 pct_chg 字段计算波动率\n",
|
||
" volatility = stock_df['pct_chg'].rolling(days).std().shift(-days)\n",
|
||
" max_drawdown = stock_df['close'].rolling(days).apply(calculate_max_drawdown, raw=False).shift(-days)\n",
|
||
" score = future_return - lambda_param * max_drawdown\n",
|
||
" return score\n",
|
||
"\n",
|
||
" # # 确保 DataFrame 按照股票代码和交易日期排序\n",
|
||
" # df = df.sort_values(by=['ts_code', 'trade_date'])\n",
|
||
"\n",
|
||
" # 对每个股票分别计算 score\n",
|
||
" df['score'] = df.groupby('ts_code').apply(compute_stock_score).reset_index(level=0, drop=True)\n",
|
||
"\n",
|
||
" return df['score']\n",
|
||
"\n",
|
||
"\n",
|
||
"def remove_highly_correlated_features(df, feature_columns, threshold=0.9):\n",
|
||
" numeric_features = df[feature_columns].select_dtypes(include=[np.number]).columns.tolist()\n",
|
||
" if not numeric_features:\n",
|
||
" raise ValueError(\"No numeric features found in the provided data.\")\n",
|
||
"\n",
|
||
" corr_matrix = df[numeric_features].corr().abs()\n",
|
||
" upper = corr_matrix.where(np.triu(np.ones(corr_matrix.shape), k=1).astype(bool))\n",
|
||
" to_drop = [column for column in upper.columns if any(upper[column] > threshold)]\n",
|
||
" remaining_features = [col for col in feature_columns if col not in to_drop\n",
|
||
" or 'act' in col or 'af' in col]\n",
|
||
" return remaining_features\n",
|
||
"\n",
|
||
"\n",
|
||
"def cross_sectional_standardization(df, features):\n",
|
||
" df_sorted = df.sort_values(by='trade_date') # 按时间排序\n",
|
||
" df_standardized = df_sorted.copy()\n",
|
||
"\n",
|
||
" for date in df_sorted['trade_date'].unique():\n",
|
||
" # 获取当前时间点的数据\n",
|
||
" current_data = df_standardized[df_standardized['trade_date'] == date]\n",
|
||
"\n",
|
||
" # 只对指定特征进行标准化\n",
|
||
" scaler = StandardScaler()\n",
|
||
" standardized_values = scaler.fit_transform(current_data[features])\n",
|
||
"\n",
|
||
" # 将标准化结果重新赋值回去\n",
|
||
" df_standardized.loc[df_standardized['trade_date'] == date, features] = standardized_values\n",
|
||
"\n",
|
||
" return df_standardized\n",
|
||
"\n",
|
||
"\n",
|
||
"import numpy as np\n",
|
||
"import pandas as pd\n",
|
||
"\n",
|
||
"\n",
|
||
"def neutralize_manual_revised(df: pd.DataFrame, features: list, industry_col: str, mkt_cap_col: str) -> pd.DataFrame:\n",
|
||
" \"\"\"\n",
|
||
" 手动实现简单回归以提升速度,通过构建 Series 确保索引对齐。\n",
|
||
" 对特征在行业内部进行市值中性化。\n",
|
||
"\n",
|
||
" Args:\n",
|
||
" df: 输入的 DataFrame,包含特征、行业分类和市值列。\n",
|
||
" features: 需要进行中性化的特征列名列表。\n",
|
||
" industry_col: 行业分类列的列名。\n",
|
||
" mkt_cap_col: 市值列的列名。\n",
|
||
"\n",
|
||
" Returns:\n",
|
||
" 中性化后的 DataFrame。\n",
|
||
" \"\"\"\n",
|
||
"\n",
|
||
" df[mkt_cap_col] = pd.to_numeric(df[mkt_cap_col], errors='coerce')\n",
|
||
" df_cleaned = df.dropna(subset=[mkt_cap_col]).copy()\n",
|
||
" df_cleaned = df_cleaned[df_cleaned[mkt_cap_col] > 0].copy()\n",
|
||
"\n",
|
||
" if df_cleaned.empty:\n",
|
||
" print(\"警告: 清理市值异常值后 DataFrame 为空。\")\n",
|
||
" return df # 返回原始或空df,取决于清理前的状态\n",
|
||
"\n",
|
||
" processed_df = df\n",
|
||
"\n",
|
||
" for col in features:\n",
|
||
" if col not in df_cleaned.columns:\n",
|
||
" print(f\"警告: 特征列 '{col}' 不存在于清理后的 DataFrame 中,已跳过。\")\n",
|
||
" # 对于原始 df 中该列不存在的,在结果 df 中也保持原样(可能全是NaN)\n",
|
||
" processed_df[col] = df[col] if col in df.columns else np.nan\n",
|
||
" continue\n",
|
||
"\n",
|
||
" # 跳过对控制变量本身进行中性化\n",
|
||
" if col == mkt_cap_col or col == industry_col:\n",
|
||
" print(f\"警告: 特征列 '{col}' 是控制变量或内部使用的列,跳过中性化。\")\n",
|
||
" # 在结果 df 中也保持原样\n",
|
||
" processed_df[col] = df[col] if col in df.columns else np.nan\n",
|
||
" continue\n",
|
||
"\n",
|
||
" residual_series = pd.Series(index=df_cleaned.index, dtype=float)\n",
|
||
"\n",
|
||
" # 在分组前处理特征列的 NaN,只对有因子值的行进行回归计算\n",
|
||
" df_subset_factor = df_cleaned.dropna(subset=[col]).copy()\n",
|
||
"\n",
|
||
" if not df_subset_factor.empty:\n",
|
||
" for industry, group in df_subset_factor.groupby(industry_col):\n",
|
||
" x = group[mkt_cap_col] # 市值对数\n",
|
||
" y = group[col] # 因子值\n",
|
||
"\n",
|
||
" # 确保有足够的数据点 (>1) 且市值对数有方差 (>0) 进行回归计算\n",
|
||
" # 检查 np.var > 一个很小的正数,避免浮点数误差导致的零方差判断问题\n",
|
||
" if len(group) > 1 and np.var(x) > 1e-9:\n",
|
||
" try:\n",
|
||
" beta = np.cov(y, x)[0, 1] / np.var(x)\n",
|
||
" alpha = np.mean(y) - beta * np.mean(x)\n",
|
||
"\n",
|
||
" # 计算残差\n",
|
||
" resid = y - (alpha + beta * x)\n",
|
||
"\n",
|
||
" # 将计算出的残差存储到 residual_series 中,通过索引自动对齐\n",
|
||
" residual_series.loc[resid.index] = resid\n",
|
||
"\n",
|
||
" except Exception as e:\n",
|
||
" # 捕获可能的计算异常,例如np.cov或np.var因为极端数据报错\n",
|
||
" print(f\"警告: 在行业 {industry} 计算回归时发生错误: {e}。该组残差将设为原始值或 NaN。\")\n",
|
||
" # 此时该组的残差会保持 residual_series 初始化时的 NaN 或后续处理\n",
|
||
" # 也可以选择保留原始值:residual_series.loc[group.index] = group[col]\n",
|
||
"\n",
|
||
" else:\n",
|
||
" residual_series.loc[group.index] = group[col] # 保留原始因子值\n",
|
||
" processed_df.loc[residual_series.index, col] = residual_series\n",
|
||
"\n",
|
||
"\n",
|
||
" else:\n",
|
||
" processed_df[col] = np.nan # 或 df[col] if col in df.columns else np.nan\n",
|
||
"\n",
|
||
" return processed_df\n",
|
||
"\n",
|
||
"\n",
|
||
"import gc\n",
|
||
"\n",
|
||
"gc.collect()\n",
|
||
"\n",
|
||
"\n",
|
||
"def mad_filter(df, features, n=3):\n",
|
||
" for col in features:\n",
|
||
" median = df[col].median()\n",
|
||
" mad = np.median(np.abs(df[col] - median))\n",
|
||
" upper = median + n * mad\n",
|
||
" lower = median - n * mad\n",
|
||
" df[col] = np.clip(df[col], lower, upper) # 截断极值\n",
|
||
" return df\n",
|
||
"\n",
|
||
"\n",
|
||
"def percentile_filter(df, features, lower_percentile=0.01, upper_percentile=0.99):\n",
|
||
" for col in features:\n",
|
||
" # 按日期分组计算上下百分位数\n",
|
||
" lower_bound = df.groupby('trade_date')[col].transform(\n",
|
||
" lambda x: x.quantile(lower_percentile)\n",
|
||
" )\n",
|
||
" upper_bound = df.groupby('trade_date')[col].transform(\n",
|
||
" lambda x: x.quantile(upper_percentile)\n",
|
||
" )\n",
|
||
" # 截断超出范围的值\n",
|
||
" df[col] = np.clip(df[col], lower_bound, upper_bound)\n",
|
||
" return df\n",
|
||
"\n",
|
||
"\n",
|
||
"from scipy.stats import iqr\n",
|
||
"\n",
|
||
"\n",
|
||
"def iqr_filter(df, features):\n",
|
||
" for col in features:\n",
|
||
" df[col] = df.groupby('trade_date')[col].transform(\n",
|
||
" lambda x: (x - x.median()) / iqr(x) if iqr(x) != 0 else x\n",
|
||
" )\n",
|
||
" return df\n",
|
||
"\n",
|
||
"\n",
|
||
"def quantile_filter(df, features, lower_quantile=0.01, upper_quantile=0.99, window=60):\n",
|
||
" df = df.copy()\n",
|
||
" for col in features:\n",
|
||
" # 计算 rolling 统计量,需要按日期进行 groupby\n",
|
||
" rolling_lower = df.groupby('trade_date')[col].transform(lambda x: x.rolling(window=min(len(x), window)).quantile(lower_quantile))\n",
|
||
" rolling_upper = df.groupby('trade_date')[col].transform(lambda x: x.rolling(window=min(len(x), window)).quantile(upper_quantile))\n",
|
||
"\n",
|
||
" # 对数据进行裁剪\n",
|
||
" df[col] = np.clip(df[col], rolling_lower, rolling_upper)\n",
|
||
" \n",
|
||
" return df\n",
|
||
"\n",
|
||
"def select_top_features_by_rankic(df: pd.DataFrame, feature_columns: list, n: int, target_column: str = 'future_return') -> list:\n",
|
||
" \"\"\"\n",
|
||
" 计算给定特征与目标列的 RankIC,并返回 RankIC 绝对值最高的 n 个特征。\n",
|
||
"\n",
|
||
" Args:\n",
|
||
" df: 包含特征列和目标列的 Pandas DataFrame。\n",
|
||
" feature_columns: 包含所有待评估特征列名的列表。\n",
|
||
" n: 希望选取的 RankIC 绝对值最高的特征数量。\n",
|
||
" target_column: 目标列的名称,用于计算 RankIC。默认为 'future_return'。\n",
|
||
"\n",
|
||
" Returns:\n",
|
||
" 包含 RankIC 绝对值最高的 n 个特征列名的列表。\n",
|
||
" \"\"\"\n",
|
||
" numeric_columns = df.select_dtypes(include=['float64', 'int64']).columns\n",
|
||
" numeric_columns = [col for col in numeric_columns if col in feature_columns]\n",
|
||
" if target_column not in df.columns:\n",
|
||
" raise ValueError(f\"目标列 '{target_column}' 不存在于 DataFrame 中。\")\n",
|
||
"\n",
|
||
" rankic_scores = {}\n",
|
||
" for feature in numeric_columns:\n",
|
||
" if feature not in df.columns:\n",
|
||
" print(f\"警告: 特征列 '{feature}' 不存在于 DataFrame 中,已跳过。\")\n",
|
||
" continue\n",
|
||
"\n",
|
||
" # 计算特征与目标列的 RankIC (斯皮尔曼相关系数)\n",
|
||
" # dropna() 是为了处理缺失值,确保相关性计算不失败\n",
|
||
" valid_data = df[[feature, target_column]].dropna()\n",
|
||
" if len(valid_data) > 1: # 确保有足够的数据点进行相关性计算\n",
|
||
" # 计算斯皮尔曼相关性\n",
|
||
" correlation = valid_data[feature].corr(valid_data[target_column], method='spearman')\n",
|
||
" rankic_scores[feature] = abs(correlation) # 使用绝对值来衡量相关性强度\n",
|
||
" else:\n",
|
||
" rankic_scores[feature] = 0 # 数据不足,RankIC设为0或跳过\n",
|
||
"\n",
|
||
" # 将 RankIC 分数转换为 Series 便于排序\n",
|
||
" rankic_series = pd.Series(rankic_scores)\n",
|
||
"\n",
|
||
" # 按 RankIC 绝对值降序排序,选取前 n 个特征\n",
|
||
" # handle case where n might be larger than available features\n",
|
||
" n_actual = min(n, len(rankic_series))\n",
|
||
" top_features = rankic_series.sort_values(ascending=False).head(n_actual).index.tolist()\n",
|
||
" top_features = [col for col in feature_columns if col in top_features or col not in numeric_columns]\n",
|
||
" return top_features\n",
|
||
"\n",
|
||
"def create_deviation_within_dates(df, feature_columns):\n",
|
||
" groupby_col = 'cat_l2_code' # 使用 trade_date 进行分组\n",
|
||
" new_columns = {}\n",
|
||
" ret_feature_columns = feature_columns[:]\n",
|
||
"\n",
|
||
" # 自动选择所有数值型特征\n",
|
||
" num_features = [col for col in feature_columns if 'cat' not in col and 'index' not in col]\n",
|
||
"\n",
|
||
" # num_features = ['vol', 'pct_chg', 'turnover_rate', 'volume_ratio', 'cat_vol_spike', 'obv', 'maobv_6', 'return_5', 'return_10', 'return_20', 'std_return_5', 'std_return_15', 'std_return_90', 'std_return_90_2', 'act_factor1', 'act_factor2', 'act_factor3', 'act_factor4', 'act_factor5', 'act_factor6', 'rank_act_factor1', 'rank_act_factor2', 'rank_act_factor3', 'active_buy_volume_large', 'active_buy_volume_big', 'active_buy_volume_small', 'alpha_022', 'alpha_003', 'alpha_007', 'alpha_013']\n",
|
||
" num_features = [col for col in num_features if 'cat' not in col and 'industry' not in col]\n",
|
||
" num_features = [col for col in num_features if 'limit' not in col]\n",
|
||
" num_features = [col for col in num_features if 'cyq' not in col]\n",
|
||
"\n",
|
||
" # 遍历所有数值型特征\n",
|
||
" for feature in num_features:\n",
|
||
" if feature == 'trade_date': # 不需要对 'trade_date' 计算偏差\n",
|
||
" continue\n",
|
||
"\n",
|
||
" # grouped_mean = df.groupby(['trade_date'])[feature].transform('mean')\n",
|
||
" # deviation_col_name = f'deviation_mean_{feature}'\n",
|
||
" # new_columns[deviation_col_name] = df[feature] - grouped_mean\n",
|
||
" # ret_feature_columns.append(deviation_col_name)\n",
|
||
"\n",
|
||
" grouped_mean = df.groupby(['trade_date', groupby_col])[feature].transform('mean')\n",
|
||
" deviation_col_name = f'deviation_mean_{feature}'\n",
|
||
" new_columns[deviation_col_name] = df[feature] - grouped_mean\n",
|
||
" ret_feature_columns.append(deviation_col_name)\n",
|
||
"\n",
|
||
" # 将新计算的偏差特征与原始 DataFrame 合并\n",
|
||
" df = pd.concat([df, pd.DataFrame(new_columns)], axis=1)\n",
|
||
"\n",
|
||
" # for feature in ['obv', 'return_20', 'act_factor1', 'act_factor2', 'act_factor3', 'act_factor4']:\n",
|
||
" # df[f'deviation_industry_{feature}'] = df[feature] - df[f'industry_{feature}']\n",
|
||
"\n",
|
||
" return df, ret_feature_columns\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 15,
|
||
"id": "47c12bb34062ae7a",
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2025-04-03T14:57:50.841165Z",
|
||
"start_time": "2025-04-03T14:49:25.889057Z"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"days = 5\n",
|
||
"validation_days = 120\n",
|
||
"\n",
|
||
"import gc\n",
|
||
"\n",
|
||
"gc.collect()\n",
|
||
"\n",
|
||
"df = df.sort_values(by=['ts_code', 'trade_date'])\n",
|
||
"df['future_return'] = df.groupby('ts_code', group_keys=False)['close'].apply(lambda x: x.shift(-days) / x - 1)\n",
|
||
"# df['future_return'] = (df.groupby('ts_code')['close'].shift(-days) - df.groupby('ts_code')['open'].shift(-1)) / \\\n",
|
||
"# df.groupby('ts_code')['open'].shift(-1)\n",
|
||
"df['future_return'] = np.log1p(df['future_return'])\n",
|
||
"\n",
|
||
"df['future_return_2'] = df.groupby('ts_code', group_keys=False)['close'].apply(lambda x: x.shift(-2) / x - 1)\n",
|
||
"# df['future_return'] = (df.groupby('ts_code')['close'].shift(-days) - df.groupby('ts_code')['open'].shift(-1)) / \\\n",
|
||
"# df.groupby('ts_code')['open'].shift(-1)\n",
|
||
"df['future_return_2'] = np.log1p(df['future_return_2'])\n",
|
||
"\n",
|
||
"df['cat_up_limit'] = df['pct_chg'] > 5\n",
|
||
"# df['label'] = (df.groupby('ts_code')['cat_up_limit']\n",
|
||
"# .rolling(window=5, min_periods=1).sum()\n",
|
||
"# .groupby('ts_code') # 再次按 ts_code 分组\n",
|
||
"# .shift(-5)\n",
|
||
"# .fillna(0) # 填充每个股票组最后的 NaN\n",
|
||
"# .astype(int)\n",
|
||
"# .reset_index(level=0, drop=True))\n",
|
||
"df['label'] = df.groupby('trade_date', group_keys=False)['future_return'].transform(\n",
|
||
" lambda x: pd.qcut(x, q=20, labels=False, duplicates='drop')\n",
|
||
")\n",
|
||
"filter_index = df['future_return'].between(df['future_return'].quantile(0.01), df['future_return'].quantile(0.99))\n",
|
||
"\n",
|
||
"# for col in [col for col in df.columns]:\n",
|
||
"# train_data[col] = train_data[col].astype('str')\n",
|
||
"# test_data[col] = test_data[col].astype('str')"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 16,
|
||
"id": "8f4dc587",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
" ts_code trade_date open close high low vol pct_chg \\\n",
|
||
"979441 002193.SZ 2019-01-02 18.02 17.82 18.21 17.73 22513.62 -1.00 \n",
|
||
"979442 002193.SZ 2019-01-03 17.82 17.78 17.94 17.65 19523.21 -0.22 \n",
|
||
"979443 002193.SZ 2019-01-04 17.47 18.04 18.10 17.34 28094.69 1.46 \n",
|
||
"979444 002193.SZ 2019-01-07 18.17 18.31 18.39 18.00 23866.88 1.50 \n",
|
||
"979445 002193.SZ 2019-01-08 18.25 18.52 18.66 18.13 22853.46 1.15 \n",
|
||
"... ... ... ... ... ... ... ... ... \n",
|
||
"980979 002193.SZ 2025-05-26 11.57 11.70 11.76 11.45 59545.98 1.65 \n",
|
||
"980980 002193.SZ 2025-05-27 11.70 11.80 11.93 11.55 64820.00 0.85 \n",
|
||
"980981 002193.SZ 2025-05-28 11.82 11.66 11.95 11.55 59952.42 -1.19 \n",
|
||
"980982 002193.SZ 2025-05-29 11.68 11.86 11.93 11.55 87290.42 1.72 \n",
|
||
"980983 002193.SZ 2025-05-30 11.86 11.76 12.01 11.70 82645.94 -0.84 \n",
|
||
"\n",
|
||
" turnover_rate pe_ttm ... cs_rank_turnover_per_winner \\\n",
|
||
"979441 0.9771 23.0129 ... 0.990869 \n",
|
||
"979442 0.8473 22.9598 ... 0.934283 \n",
|
||
"979443 1.2193 23.3049 ... 0.925182 \n",
|
||
"979444 1.0358 23.6499 ... 0.843796 \n",
|
||
"979445 0.9918 23.9154 ... 0.834672 \n",
|
||
"... ... ... ... ... \n",
|
||
"980979 2.2752 NaN ... 0.435548 \n",
|
||
"980980 2.4767 NaN ... 0.484214 \n",
|
||
"980981 2.2908 NaN ... 0.438352 \n",
|
||
"980982 3.3353 NaN ... 0.583250 \n",
|
||
"980983 3.1579 NaN ... 0.491362 \n",
|
||
"\n",
|
||
" cs_rank_ind_cap_neutral_pe cs_rank_volume_ratio \\\n",
|
||
"979441 NaN 0.710344 \n",
|
||
"979442 NaN 0.444444 \n",
|
||
"979443 NaN 0.489226 \n",
|
||
"979444 NaN 0.250000 \n",
|
||
"979445 NaN 0.510588 \n",
|
||
"... ... ... \n",
|
||
"980979 NaN 0.284385 \n",
|
||
"980980 NaN 0.421735 \n",
|
||
"980981 NaN 0.466434 \n",
|
||
"980982 NaN 0.671486 \n",
|
||
"980983 NaN 0.684385 \n",
|
||
"\n",
|
||
" cs_rank_elg_buy_sell_sm_ratio cs_rank_cost_dist_vol_ratio \\\n",
|
||
"979441 0.341855 0.261603 \n",
|
||
"979442 0.318912 0.185342 \n",
|
||
"979443 0.260036 0.211959 \n",
|
||
"979444 0.251095 0.145266 \n",
|
||
"979445 0.286679 0.202299 \n",
|
||
"... ... ... \n",
|
||
"980979 0.153987 0.327907 \n",
|
||
"980980 0.156198 0.377202 \n",
|
||
"980981 0.153373 0.362579 \n",
|
||
"980982 0.126122 0.490861 \n",
|
||
"980983 0.441528 0.486047 \n",
|
||
"\n",
|
||
" cs_rank_size future_return future_return_2 cat_up_limit label \n",
|
||
"979441 0.262235 0.033658 0.012270 False 8.0 \n",
|
||
"979442 0.264695 0.029373 0.029373 False 7.0 \n",
|
||
"979443 0.259489 0.025179 0.026260 False 10.0 \n",
|
||
"979444 0.255474 0.014638 0.006532 False 13.0 \n",
|
||
"979445 0.259854 0.003235 -0.011404 False 6.0 \n",
|
||
"... ... ... ... ... ... \n",
|
||
"980979 0.028904 NaN -0.003425 False NaN \n",
|
||
"980980 0.028913 NaN 0.005072 False NaN \n",
|
||
"980981 0.028913 NaN 0.008540 False NaN \n",
|
||
"980982 0.029246 NaN NaN False NaN \n",
|
||
"980983 0.029568 NaN NaN False NaN \n",
|
||
"\n",
|
||
"[1543 rows x 182 columns]\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"print(df[df['ts_code'] == '002193.SZ'])"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 17,
|
||
"id": "29221dde",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"162\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"feature_columns = [col for col in df.head(10)\n",
|
||
" .merge(industry_df, on=['cat_l2_code', 'trade_date'], how='left')\n",
|
||
" # .merge(index_data, on='trade_date', how='left')\n",
|
||
" .columns\n",
|
||
" ]\n",
|
||
"feature_columns = [col for col in feature_columns if col not in ['trade_date',\n",
|
||
" 'ts_code',\n",
|
||
" 'label']]\n",
|
||
"feature_columns = [col for col in feature_columns if 'future' not in col]\n",
|
||
"feature_columns = [col for col in feature_columns if 'label' not in col]\n",
|
||
"feature_columns = [col for col in feature_columns if 'score' not in col]\n",
|
||
"feature_columns = [col for col in feature_columns if 'gen' not in col]\n",
|
||
"feature_columns = [col for col in feature_columns if 'is_st' not in col]\n",
|
||
"feature_columns = [col for col in feature_columns if 'pe_ttm' not in col]\n",
|
||
"# feature_columns = [col for col in feature_columns if 'volatility' not in col]\n",
|
||
"# feature_columns = [col for col in feature_columns if 'circ_mv' not in col]\n",
|
||
"feature_columns = [col for col in feature_columns if 'code' not in col]\n",
|
||
"feature_columns = [col for col in feature_columns if col not in origin_columns]\n",
|
||
"feature_columns = [col for col in feature_columns if not col.startswith('_')]\n",
|
||
"# feature_columns = [col for col in feature_columns if col not in ['ts_code', 'trade_date', 'vol_std_5', 'cov', 'delta_cov', 'alpha_22_improved', 'alpha_007', 'consecutive_up_limit', 'mv_volatility', 'volume_growth', 'mv_growth', 'arbr']]\n",
|
||
"feature_columns = [col for col in feature_columns if col not in ['intraday_lg_flow_corr_20', \n",
|
||
" 'cap_neutral_cost_metric', \n",
|
||
" 'hurst_net_mf_vol_60', \n",
|
||
" 'complex_factor_deap_1', \n",
|
||
" 'lg_buy_consolidation_20',\n",
|
||
" 'cs_rank_ind_cap_neutral_pe',\n",
|
||
" 'cs_rank_opening_gap',\n",
|
||
" 'cs_rank_ind_adj_lg_flow']]\n",
|
||
"feature_columns = [col for col in feature_columns if col not in ['cat_reason', 'cat_is_on_top_list']]\n",
|
||
"print(len(feature_columns))"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 18,
|
||
"id": "03ee5daf",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"# df = fill_nan_with_daily_median(df, feature_columns)\n",
|
||
"for feature_col in [col for col in feature_columns if col in df.columns]:\n",
|
||
" # median_val = df[feature_col].median()\n",
|
||
" df[feature_col].fillna(0, inplace=True)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 19,
|
||
"id": "b76ea08a",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
" ts_code trade_date log_circ_mv\n",
|
||
"0 000001.SZ 2019-01-02 16.574219\n",
|
||
"1 000001.SZ 2019-01-03 16.583965\n",
|
||
"2 000001.SZ 2019-01-04 16.633371\n",
|
||
"['vol', 'pct_chg', 'turnover_rate', 'volume_ratio', 'winner_rate', 'undist_profit_ps', 'ocfps', 'AR', 'BR', 'AR_BR', 'log_circ_mv', 'cashflow_to_ev_factor', 'book_to_price_ratio', 'turnover_rate_mean_5', 'variance_20', 'bbi_ratio_factor', 'daily_deviation', 'lg_elg_net_buy_vol', 'flow_lg_elg_intensity', 'sm_net_buy_vol', 'total_buy_vol', 'lg_elg_buy_prop', 'flow_struct_buy_change', 'lg_elg_net_buy_vol_change', 'flow_lg_elg_accel', 'chip_concentration_range', 'chip_skewness', 'floating_chip_proxy', 'cost_support_15pct_change', 'cat_winner_price_zone', 'flow_chip_consistency', 'profit_taking_vs_absorb', 'cat_is_positive', 'upside_vol', 'downside_vol', 'vol_ratio', 'return_skew', 'return_kurtosis', 'volume_change_rate', 'cat_volume_breakout', 'turnover_deviation', 'cat_turnover_spike', 'avg_volume_ratio', 'cat_volume_ratio_breakout', 'vol_spike', 'vol_std_5', 'atr_14', 'atr_6', 'obv', 'maobv_6', 'rsi_3', 'return_5', 'return_20', 'std_return_5', 'std_return_90', 'std_return_90_2', 'act_factor1', 'act_factor2', 'act_factor3', 'act_factor4', 'rank_act_factor1', 'rank_act_factor2', 'rank_act_factor3', 'cov', 'delta_cov', 'alpha_22_improved', 'alpha_003', 'alpha_007', 'alpha_013', 'vol_break', 'weight_roc5', 'smallcap_concentration', 'cost_stability', 'high_cost_break_days', 'liquidity_risk', 'turnover_std', 'mv_volatility', 'volume_growth', 'mv_growth', 'momentum_factor', 'resonance_factor', 'log_close', 'cat_vol_spike', 'up', 'down', 'obv_maobv_6', 'std_return_5_over_std_return_90', 'std_return_90_minus_std_return_90_2', 'cat_af2', 'cat_af3', 'cat_af4', 'act_factor5', 'act_factor6', 'active_buy_volume_large', 'active_buy_volume_big', 'active_buy_volume_small', 'buy_lg_vol_minus_sell_lg_vol', 'buy_elg_vol_minus_sell_elg_vol', 'ctrl_strength', 'low_cost_dev', 'asymmetry', 'lock_factor', 'cat_vol_break', 'cost_atr_adj', 'cat_golden_resonance', 'mv_turnover_ratio', 'mv_adjusted_volume', 'mv_weighted_turnover', 'nonlinear_mv_volume', 'mv_volume_ratio', 'mv_momentum', 'lg_flow_mom_corr_20_60', 'lg_flow_accel', 'profit_pressure', 'underwater_resistance', 'cost_conc_std_20', 'profit_decay_20', 'vol_amp_loss_20', 'vol_drop_profit_cnt_5', 'lg_flow_vol_interact_20', 'cost_break_confirm_cnt_5', 'atr_norm_channel_pos_14', 'turnover_diff_skew_20', 'lg_sm_flow_diverge_20', 'pullback_strong_20_20', 'vol_wgt_hist_pos_20', 'vol_adj_roc_20', 'cs_rank_net_lg_flow_val', 'cs_rank_elg_buy_ratio', 'cs_rank_rel_profit_margin', 'cs_rank_cost_breadth', 'cs_rank_dist_to_upper_cost', 'cs_rank_winner_rate', 'cs_rank_intraday_range', 'cs_rank_close_pos_in_range', 'cs_rank_pos_in_hist_range', 'cs_rank_vol_x_profit_margin', 'cs_rank_lg_flow_price_concordance', 'cs_rank_turnover_per_winner', 'cs_rank_volume_ratio', 'cs_rank_elg_buy_sell_sm_ratio', 'cs_rank_cost_dist_vol_ratio', 'cs_rank_size', 'cat_up_limit', 'industry_obv', 'industry_return_5', 'industry_return_20', 'industry__ema_5', 'industry__ema_13', 'industry__ema_20', 'industry__ema_60', 'industry_act_factor1', 'industry_act_factor2', 'industry_act_factor3', 'industry_act_factor4', 'industry_act_factor5', 'industry_act_factor6', 'industry_rank_act_factor1', 'industry_rank_act_factor2', 'industry_rank_act_factor3', 'industry_return_5_percentile', 'industry_return_20_percentile']\n",
|
||
"去除极值\n",
|
||
"开始截面 MAD 去极值处理 (k=3.0)...\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"MAD Filtering: 100%|██████████| 132/132 [00:14<00:00, 9.17it/s]\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"截面 MAD 去极值处理完成。\n",
|
||
"开始截面 MAD 去极值处理 (k=3.0)...\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"MAD Filtering: 100%|██████████| 132/132 [00:12<00:00, 10.64it/s]\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"截面 MAD 去极值处理完成。\n",
|
||
"开始截面 MAD 去极值处理 (k=3.0)...\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"MAD Filtering: 0it [00:00, ?it/s]\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"截面 MAD 去极值处理完成。\n",
|
||
"开始截面 MAD 去极值处理 (k=3.0)...\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"MAD Filtering: 0it [00:00, ?it/s]\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"截面 MAD 去极值处理完成。\n",
|
||
"feature_columns: ['vol', 'pct_chg', 'turnover_rate', 'volume_ratio', 'winner_rate', 'undist_profit_ps', 'ocfps', 'AR', 'BR', 'AR_BR', 'log_circ_mv', 'cashflow_to_ev_factor', 'book_to_price_ratio', 'turnover_rate_mean_5', 'variance_20', 'bbi_ratio_factor', 'daily_deviation', 'lg_elg_net_buy_vol', 'flow_lg_elg_intensity', 'sm_net_buy_vol', 'total_buy_vol', 'lg_elg_buy_prop', 'flow_struct_buy_change', 'lg_elg_net_buy_vol_change', 'flow_lg_elg_accel', 'chip_concentration_range', 'chip_skewness', 'floating_chip_proxy', 'cost_support_15pct_change', 'cat_winner_price_zone', 'flow_chip_consistency', 'profit_taking_vs_absorb', 'cat_is_positive', 'upside_vol', 'downside_vol', 'vol_ratio', 'return_skew', 'return_kurtosis', 'volume_change_rate', 'cat_volume_breakout', 'turnover_deviation', 'cat_turnover_spike', 'avg_volume_ratio', 'cat_volume_ratio_breakout', 'vol_spike', 'vol_std_5', 'atr_14', 'atr_6', 'obv', 'maobv_6', 'rsi_3', 'return_5', 'return_20', 'std_return_5', 'std_return_90', 'std_return_90_2', 'act_factor1', 'act_factor2', 'act_factor3', 'act_factor4', 'rank_act_factor1', 'rank_act_factor2', 'rank_act_factor3', 'cov', 'delta_cov', 'alpha_22_improved', 'alpha_003', 'alpha_007', 'alpha_013', 'vol_break', 'weight_roc5', 'smallcap_concentration', 'cost_stability', 'high_cost_break_days', 'liquidity_risk', 'turnover_std', 'mv_volatility', 'volume_growth', 'mv_growth', 'momentum_factor', 'resonance_factor', 'log_close', 'cat_vol_spike', 'up', 'down', 'obv_maobv_6', 'std_return_5_over_std_return_90', 'std_return_90_minus_std_return_90_2', 'cat_af2', 'cat_af3', 'cat_af4', 'act_factor5', 'act_factor6', 'active_buy_volume_large', 'active_buy_volume_big', 'active_buy_volume_small', 'buy_lg_vol_minus_sell_lg_vol', 'buy_elg_vol_minus_sell_elg_vol', 'ctrl_strength', 'low_cost_dev', 'asymmetry', 'lock_factor', 'cat_vol_break', 'cost_atr_adj', 'cat_golden_resonance', 'mv_turnover_ratio', 'mv_adjusted_volume', 'mv_weighted_turnover', 'nonlinear_mv_volume', 'mv_volume_ratio', 'mv_momentum', 'lg_flow_mom_corr_20_60', 'lg_flow_accel', 'profit_pressure', 'underwater_resistance', 'cost_conc_std_20', 'profit_decay_20', 'vol_amp_loss_20', 'vol_drop_profit_cnt_5', 'lg_flow_vol_interact_20', 'cost_break_confirm_cnt_5', 'atr_norm_channel_pos_14', 'turnover_diff_skew_20', 'lg_sm_flow_diverge_20', 'pullback_strong_20_20', 'vol_wgt_hist_pos_20', 'vol_adj_roc_20', 'cs_rank_net_lg_flow_val', 'cs_rank_elg_buy_ratio', 'cs_rank_rel_profit_margin', 'cs_rank_cost_breadth', 'cs_rank_dist_to_upper_cost', 'cs_rank_winner_rate', 'cs_rank_intraday_range', 'cs_rank_close_pos_in_range', 'cs_rank_pos_in_hist_range', 'cs_rank_vol_x_profit_margin', 'cs_rank_lg_flow_price_concordance', 'cs_rank_turnover_per_winner', 'cs_rank_volume_ratio', 'cs_rank_elg_buy_sell_sm_ratio', 'cs_rank_cost_dist_vol_ratio', 'cs_rank_size', 'cat_up_limit', 'industry_obv', 'industry_return_5', 'industry_return_20', 'industry__ema_5', 'industry__ema_13', 'industry__ema_20', 'industry__ema_60', 'industry_act_factor1', 'industry_act_factor2', 'industry_act_factor3', 'industry_act_factor4', 'industry_act_factor5', 'industry_act_factor6', 'industry_rank_act_factor1', 'industry_rank_act_factor2', 'industry_rank_act_factor3', 'industry_return_5_percentile', 'industry_return_20_percentile']\n",
|
||
"df最小日期: 2019-01-02\n",
|
||
"df最大日期: 2025-05-30\n",
|
||
"2057465\n",
|
||
"train_data最小日期: 2020-01-02\n",
|
||
"train_data最大日期: 2022-12-30\n",
|
||
"1781706\n",
|
||
"test_data最小日期: 2023-01-03\n",
|
||
"test_data最大日期: 2025-05-30\n",
|
||
" ts_code trade_date log_circ_mv\n",
|
||
"0 000001.SZ 2019-01-02 16.574219\n",
|
||
"1 000001.SZ 2019-01-03 16.583965\n",
|
||
"2 000001.SZ 2019-01-04 16.633371\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"split_date = '2023-01-01'\n",
|
||
"train_data = df[filter_index & (df['trade_date'] <= split_date) & (df['trade_date'] >= '2020-01-01')]\n",
|
||
"test_data = df[(df['trade_date'] >= split_date)]\n",
|
||
"\n",
|
||
"print(df[['ts_code', 'trade_date', 'log_circ_mv']].head(3))\n",
|
||
"\n",
|
||
"industry_df = industry_df.sort_values(by=['trade_date'])\n",
|
||
"index_data = index_data.sort_values(by=['trade_date'])\n",
|
||
"\n",
|
||
"# train_data = train_data.merge(industry_df, on=['cat_l2_code', 'trade_date'], how='left')\n",
|
||
"# train_data = train_data.merge(index_data, on='trade_date', how='left')\n",
|
||
"# test_data = test_data.merge(industry_df, on=['cat_l2_code', 'trade_date'], how='left')\n",
|
||
"# test_data = test_data.merge(index_data, on='trade_date', how='left')\n",
|
||
"\n",
|
||
"train_data, test_data = train_data.replace([np.inf, -np.inf], np.nan), test_data.replace([np.inf, -np.inf], np.nan)\n",
|
||
"\n",
|
||
"# feature_columns_new = feature_columns[:]\n",
|
||
"# train_data, _ = create_deviation_within_dates(train_data, [col for col in feature_columns if col in train_data.columns])\n",
|
||
"# test_data, _ = create_deviation_within_dates(test_data, [col for col in feature_columns if col in train_data.columns])\n",
|
||
"\n",
|
||
"# feature_columns = [\n",
|
||
"# 'undist_profit_ps', \n",
|
||
"# 'AR_BR',\n",
|
||
"# 'pe_ttm',\n",
|
||
"# 'alpha_22_improved', \n",
|
||
"# 'alpha_003', \n",
|
||
"# 'alpha_007', \n",
|
||
"# 'alpha_013', \n",
|
||
"# 'cat_up_limit', \n",
|
||
"# 'cat_down_limit', \n",
|
||
"# 'up_limit_count_10d', \n",
|
||
"# 'down_limit_count_10d', \n",
|
||
"# 'consecutive_up_limit', \n",
|
||
"# 'vol_break', \n",
|
||
"# 'weight_roc5', \n",
|
||
"# 'price_cost_divergence', \n",
|
||
"# 'smallcap_concentration', \n",
|
||
"# 'cost_stability', \n",
|
||
"# 'high_cost_break_days', \n",
|
||
"# 'liquidity_risk', \n",
|
||
"# 'turnover_std', \n",
|
||
"# 'mv_volatility', \n",
|
||
"# 'volume_growth', \n",
|
||
"# 'mv_growth', \n",
|
||
"# 'lg_flow_mom_corr_20_60', \n",
|
||
"# 'lg_flow_accel', \n",
|
||
"# 'profit_pressure', \n",
|
||
"# 'underwater_resistance', \n",
|
||
"# 'cost_conc_std_20', \n",
|
||
"# 'profit_decay_20', \n",
|
||
"# 'vol_amp_loss_20', \n",
|
||
"# 'vol_drop_profit_cnt_5', \n",
|
||
"# 'lg_flow_vol_interact_20', \n",
|
||
"# 'cost_break_confirm_cnt_5', \n",
|
||
"# 'atr_norm_channel_pos_14', \n",
|
||
"# 'turnover_diff_skew_20', \n",
|
||
"# 'lg_sm_flow_diverge_20', \n",
|
||
"# 'pullback_strong_20_20', \n",
|
||
"# 'vol_wgt_hist_pos_20', \n",
|
||
"# 'vol_adj_roc_20',\n",
|
||
"# 'cashflow_to_ev_factor',\n",
|
||
"# 'ocfps',\n",
|
||
"# 'book_to_price_ratio',\n",
|
||
"# 'turnover_rate_mean_5',\n",
|
||
"# 'variance_20',\n",
|
||
"# 'bbi_ratio_factor'\n",
|
||
"# ]\n",
|
||
"# feature_columns = [col for col in feature_columns if col in train_data.columns]\n",
|
||
"# feature_columns = [col for col in feature_columns if not col.startswith('_')]\n",
|
||
"\n",
|
||
"numeric_columns = df.select_dtypes(include=['float64', 'int64']).columns\n",
|
||
"numeric_columns = [col for col in numeric_columns if col in feature_columns]\n",
|
||
"# feature_columns = select_top_features_by_rankic(df, numeric_columns, n=10)\n",
|
||
"print(feature_columns)\n",
|
||
"\n",
|
||
"# train_data = fill_nan_with_daily_median(train_data, feature_columns)\n",
|
||
"# test_data = fill_nan_with_daily_median(test_data, feature_columns)\n",
|
||
"\n",
|
||
"train_data = train_data.dropna(subset=[col for col in feature_columns if col in train_data.columns])\n",
|
||
"train_data = train_data.dropna(subset=['label'])\n",
|
||
"train_data = train_data.reset_index(drop=True)\n",
|
||
"# print(test_data.tail())\n",
|
||
"test_data = test_data.dropna(subset=[col for col in feature_columns if col in train_data.columns])\n",
|
||
"# test_data = test_data.dropna(subset=['label'])\n",
|
||
"test_data = test_data.reset_index(drop=True)\n",
|
||
"\n",
|
||
"transform_feature_columns = feature_columns\n",
|
||
"transform_feature_columns = [col for col in transform_feature_columns if col in feature_columns and not col.startswith('cat') and col in train_data.columns]\n",
|
||
"# transform_feature_columns.remove('undist_profit_ps')\n",
|
||
"print('去除极值')\n",
|
||
"cs_mad_filter(train_data, transform_feature_columns)\n",
|
||
"# print('中性化')\n",
|
||
"# cs_neutralize_industry_cap(train_data, transform_feature_columns)\n",
|
||
"# print('标准化')\n",
|
||
"# cs_zscore_standardize(train_data, transform_feature_columns)\n",
|
||
"\n",
|
||
"cs_mad_filter(test_data, transform_feature_columns)\n",
|
||
"# cs_neutralize_industry_cap(test_data, transform_feature_columns)\n",
|
||
"# cs_zscore_standardize(test_data, transform_feature_columns)\n",
|
||
"\n",
|
||
"mad_filter_feature_columns = [col for col in feature_columns if col not in transform_feature_columns and not col.startswith('cat') and col in train_data.columns]\n",
|
||
"cs_mad_filter(train_data, mad_filter_feature_columns)\n",
|
||
"cs_mad_filter(test_data, mad_filter_feature_columns)\n",
|
||
"\n",
|
||
"\n",
|
||
"print(f'feature_columns: {feature_columns}')\n",
|
||
"\n",
|
||
"\n",
|
||
"print(f\"df最小日期: {df['trade_date'].min().strftime('%Y-%m-%d')}\")\n",
|
||
"print(f\"df最大日期: {df['trade_date'].max().strftime('%Y-%m-%d')}\")\n",
|
||
"print(len(train_data))\n",
|
||
"print(f\"train_data最小日期: {train_data['trade_date'].min().strftime('%Y-%m-%d')}\")\n",
|
||
"print(f\"train_data最大日期: {train_data['trade_date'].max().strftime('%Y-%m-%d')}\")\n",
|
||
"print(len(test_data))\n",
|
||
"print(f\"test_data最小日期: {test_data['trade_date'].min().strftime('%Y-%m-%d')}\")\n",
|
||
"print(f\"test_data最大日期: {test_data['trade_date'].max().strftime('%Y-%m-%d')}\")\n",
|
||
"\n",
|
||
"cat_columns = [col for col in feature_columns if col.startswith('cat')]\n",
|
||
"for col in cat_columns:\n",
|
||
" train_data[col] = train_data[col].astype('category')\n",
|
||
" test_data[col] = test_data[col].astype('category')\n",
|
||
"\n",
|
||
"print(df[['ts_code', 'trade_date', 'log_circ_mv']].head(3))\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 20,
|
||
"id": "2e4b027e",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"class RmseObjective(object):\n",
|
||
"\n",
|
||
" def is_max_optimal(self):\n",
|
||
" return False\n",
|
||
"\n",
|
||
" def get_final_error(self, error, weight):\n",
|
||
" return np.sqrt(error / (weight + 1e-38))\n",
|
||
"\n",
|
||
"\n",
|
||
" def evaluate(self, approxes, target, weight):\n",
|
||
" assert len(approxes) == 1\n",
|
||
" assert len(target) == len(approxes[0])\n",
|
||
"\n",
|
||
" approx = approxes[0]\n",
|
||
"\n",
|
||
" error_sum = 0.0\n",
|
||
" weight_sum = 0.0\n",
|
||
"\n",
|
||
" for i in range(len(approx)):\n",
|
||
" w = 1.0 if weight is None else weight[i]\n",
|
||
" weight_sum += w\n",
|
||
" error_sum += w * ((approx[i] - target[i])**2)\n",
|
||
"\n",
|
||
" return error_sum, weight_sum\n",
|
||
"\n",
|
||
" def calc_ders_range(self, approxes, targets, weights):\n",
|
||
" assert len(approxes) == len(targets)\n",
|
||
" if weights is not None:\n",
|
||
" assert len(weights) == len(approxes)\n",
|
||
"\n",
|
||
" result = []\n",
|
||
" for index in range(len(targets)):\n",
|
||
" der1 = targets[index] - approxes[index]\n",
|
||
" der2 = -1\n",
|
||
"\n",
|
||
" if weights is not None:\n",
|
||
" der1 *= weights[index]\n",
|
||
" der2 *= weights[index]\n",
|
||
"\n",
|
||
" result.append((der1, der2))\n",
|
||
" return result\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 23,
|
||
"id": "3ff2d1c5",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"from sklearn.preprocessing import StandardScaler\n",
|
||
"from sklearn.linear_model import LogisticRegression\n",
|
||
"import matplotlib.pyplot as plt # 保持 matplotlib 导入,尽管LightGBM的绘图功能已移除\n",
|
||
"from sklearn.decomposition import PCA\n",
|
||
"import pandas as pd\n",
|
||
"import numpy as np\n",
|
||
"import datetime # 用于日期计算\n",
|
||
"from catboost import CatBoostClassifier, CatBoostRanker, CatBoostRegressor\n",
|
||
"from catboost import Pool\n",
|
||
"import lightgbm as lgb\n",
|
||
"from lightgbm import LGBMClassifier, LGBMRegressor\n",
|
||
"\n",
|
||
"def train_model(train_data_df, feature_columns,\n",
|
||
" print_info=True, # 调整参数名,更通用\n",
|
||
" validation_days=180, use_pca=False, split_date=None,\n",
|
||
" target_column='label', type='light'): # 增加目标列参数\n",
|
||
"\n",
|
||
" print('train data size: ', len(train_data_df))\n",
|
||
" print(train_data_df[['ts_code', 'trade_date', 'log_circ_mv']])\n",
|
||
" # 确保数据按时间排序\n",
|
||
" train_data_df = train_data_df.sort_values(by='trade_date')\n",
|
||
"\n",
|
||
" # 识别数值型特征列\n",
|
||
" numeric_feature_columns = train_data_df[feature_columns].select_dtypes(include=['float64', 'int64']).columns.tolist()\n",
|
||
"\n",
|
||
" # 去除标签为空的样本\n",
|
||
" initial_len = len(train_data_df)\n",
|
||
" train_data_df = train_data_df.dropna(subset=[target_column])\n",
|
||
"\n",
|
||
" if print_info:\n",
|
||
" print(f'原始样本数: {initial_len}, 去除标签为空后样本数: {len(train_data_df)}')\n",
|
||
"\n",
|
||
" # 提取特征和标签,只取数值型特征用于线性回归\n",
|
||
" \n",
|
||
" if split_date is None:\n",
|
||
" all_dates = train_data_df['trade_date'].unique() # 获取所有唯一的 trade_date\n",
|
||
" split_date = all_dates[-validation_days] # 划分点为倒数第 validation_days 天\n",
|
||
" train_data_split = train_data_df[train_data_df['trade_date'] < split_date] # 训练集\n",
|
||
" val_data_split = train_data_df[train_data_df['trade_date'] >= split_date] # 验证集\n",
|
||
" \n",
|
||
" X_train = train_data_split[feature_columns]\n",
|
||
" y_train = train_data_split[target_column]\n",
|
||
" \n",
|
||
" X_val = val_data_split[feature_columns]\n",
|
||
" y_val = val_data_split[target_column]\n",
|
||
"\n",
|
||
"\n",
|
||
" # # 标准化数值特征 (使用 StandardScaler 对训练集fit并transform, 对验证集只transform)\n",
|
||
" scaler = StandardScaler()\n",
|
||
" # X_train = scaler.fit_transform(X_train)\n",
|
||
"\n",
|
||
" # 训练线性回归模型\n",
|
||
" # model = LogisticRegression(random_state=42)\n",
|
||
" \n",
|
||
" # # 使用处理后的特征和样本权重进行训练\n",
|
||
" # model.fit(X_train, y_train)\n",
|
||
"\n",
|
||
"\n",
|
||
" if type == 'cat':\n",
|
||
" params = {\n",
|
||
" 'loss_function': 'RMSE', # 适用于二分类\n",
|
||
" 'eval_metric': 'RMSE', # 评估指标\n",
|
||
" 'iterations': 1500,\n",
|
||
" 'learning_rate': 0.01,\n",
|
||
" 'depth': 8, # 控制模型复杂度\n",
|
||
" 'l2_leaf_reg': 1, # L2 正则化\n",
|
||
" 'verbose': 5000,\n",
|
||
" 'early_stopping_rounds': 300,\n",
|
||
" 'one_hot_max_size': 50,\n",
|
||
" # 'class_weights': [0.6, 1.2],\n",
|
||
" 'task_type': 'GPU',\n",
|
||
" 'has_time': True,\n",
|
||
" 'random_seed': 7\n",
|
||
" }\n",
|
||
" cat_features = [i for i, col in enumerate(feature_columns) if col.startswith('cat')]\n",
|
||
" group_train = train_data_split['trade_date'].factorize()[0]\n",
|
||
" group_val = val_data_split['trade_date'].factorize()[0]\n",
|
||
" train_pool = Pool(\n",
|
||
" data=X_train,\n",
|
||
" label=y_train,\n",
|
||
" # group_id=group_train,\n",
|
||
" cat_features=cat_features\n",
|
||
" )\n",
|
||
" val_pool = Pool(\n",
|
||
" data=X_val,\n",
|
||
" label=y_val,\n",
|
||
" # group_id=group_val,\n",
|
||
" cat_features=cat_features\n",
|
||
" )\n",
|
||
"\n",
|
||
" model = CatBoostRegressor(**params)\n",
|
||
" model.fit(train_pool,\n",
|
||
" eval_set=val_pool, \n",
|
||
" plot=True, \n",
|
||
" use_best_model=True\n",
|
||
" )\n",
|
||
" elif type == 'light':\n",
|
||
" label_gain = list(range(len(train_data_split['label'].unique())))\n",
|
||
" params = {\n",
|
||
" 'label_gain': [gain * gain for gain in label_gain],\n",
|
||
" 'objective': 'rmse',\n",
|
||
" 'metric': 'rmse',\n",
|
||
" 'learning_rate': 0.01,\n",
|
||
" 'num_leaves': 1024,\n",
|
||
" # 'min_data_in_leaf': 128,\n",
|
||
" 'max_depth': 100,\n",
|
||
" 'max_bin': 1024,\n",
|
||
" 'feature_fraction': 0.7,\n",
|
||
" 'bagging_fraction': 0.7,\n",
|
||
" 'bagging_freq': 5,\n",
|
||
" 'lambda_l1': 1,\n",
|
||
" 'lambda_l2': 1,\n",
|
||
" 'boosting': 'gbdt',\n",
|
||
" 'verbosity': -1,\n",
|
||
" 'extra_trees': True,\n",
|
||
" # 'max_position': 5,\n",
|
||
" # 'ndcg_at': 1,\n",
|
||
" 'quant_train_renew_leaf': True,\n",
|
||
" # 'lambdarank_truncation_level': 1,\n",
|
||
" # 'lambdarank_position_bias_regularization': 1,\n",
|
||
" 'seed': 7\n",
|
||
" }\n",
|
||
" train_groups = train_data_split.groupby('trade_date').size().tolist()\n",
|
||
" val_groups = val_data_split.groupby('trade_date').size().tolist()\n",
|
||
"\n",
|
||
" categorical_feature = [col for col in feature_columns if 'cat' in col]\n",
|
||
" train_dataset = lgb.Dataset(\n",
|
||
" X_train, label=y_train, \n",
|
||
" group=train_groups,\n",
|
||
" categorical_feature=categorical_feature\n",
|
||
" )\n",
|
||
" val_dataset = lgb.Dataset(\n",
|
||
" X_val, label=y_val, \n",
|
||
" group=val_groups,\n",
|
||
" categorical_feature=categorical_feature\n",
|
||
" )\n",
|
||
"\n",
|
||
" evals = {}\n",
|
||
" callbacks = [lgb.log_evaluation(period=1000),\n",
|
||
" lgb.callback.record_evaluation(evals),\n",
|
||
" lgb.early_stopping(100, first_metric_only=True)\n",
|
||
" ]\n",
|
||
" # 训练模型\n",
|
||
" model = lgb.train(\n",
|
||
" params, train_dataset, num_boost_round=1000,\n",
|
||
" valid_sets=[train_dataset, val_dataset], valid_names=['train', 'valid'],\n",
|
||
" callbacks=callbacks\n",
|
||
" )\n",
|
||
" # from flaml import AutoML\n",
|
||
" # from sklearn.datasets import fetch_california_housing\n",
|
||
"\n",
|
||
" # # Initialize an AutoML instance\n",
|
||
" # model = AutoML()\n",
|
||
" # # Specify automl goal and constraint\n",
|
||
" # automl_settings = {\n",
|
||
" # \"time_budget\": 3000, # in seconds\n",
|
||
" # \"metric\": \"r2\",\n",
|
||
" # \"task\": \"regression\",\n",
|
||
" # \"estimator_list\": [\n",
|
||
" # # \"catboost\",\n",
|
||
" # \"lgbm\",\n",
|
||
" # # \"xgboost\"\n",
|
||
" # ], \n",
|
||
" # # \"ensemble\": {\n",
|
||
" # # \"final_estimator\": LGBMRegressor(),\n",
|
||
" # # \"passthrough\": False,\n",
|
||
" # # },\n",
|
||
" # }\n",
|
||
" # model.fit(X_train=X_train, y_train=y_train, X_val=X_val, y_val=y_val, mlflow_logging=False, **automl_settings)\n",
|
||
"\n",
|
||
" # 打印特征重要性(如果需要)\n",
|
||
" if True:\n",
|
||
" lgb.plot_metric(evals)\n",
|
||
" lgb.plot_importance(model, importance_type='split', max_num_features=20)\n",
|
||
" plt.show()\n",
|
||
"\n",
|
||
"\n",
|
||
" return model, scaler, None # 返回训练好的模型、scaler 和 pca 对象"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 24,
|
||
"id": "c6eb5cd4-e714-420a-ac48-39af3e11ee81",
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2025-04-03T15:03:18.426481Z",
|
||
"start_time": "2025-04-03T15:02:19.926352Z"
|
||
}
|
||
},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"train data size: 728000\n",
|
||
" ts_code trade_date log_circ_mv\n",
|
||
"0 600306.SH 2020-01-02 11.552040\n",
|
||
"1 603269.SH 2020-01-02 11.324801\n",
|
||
"2 002633.SZ 2020-01-02 11.759023\n",
|
||
"3 603991.SH 2020-01-02 11.181150\n",
|
||
"4 000691.SZ 2020-01-02 11.677910\n",
|
||
"... ... ... ...\n",
|
||
"727995 002235.SZ 2022-12-30 12.914708\n",
|
||
"727996 605598.SH 2022-12-30 11.783580\n",
|
||
"727997 002613.SZ 2022-12-30 12.489464\n",
|
||
"727998 600800.SH 2022-12-30 12.571911\n",
|
||
"727999 603068.SH 2022-12-30 12.967134\n",
|
||
"\n",
|
||
"[728000 rows x 3 columns]\n",
|
||
"原始样本数: 728000, 去除标签为空后样本数: 728000\n",
|
||
"Training until validation scores don't improve for 100 rounds\n",
|
||
"Early stopping, best iteration is:\n",
|
||
"[67]\ttrain's rmse: 0.0373319\tvalid's rmse: 0.0405067\n",
|
||
"Evaluated only: rmse\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"image/png": "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fvklfvnlF6xZswYAEBAQUGd3l1qtRmhoKHr27OlYlp6ejsLCQqSnp8Nmszne5KioKBgMhlaonIiIiOqTlZUFf39/V3fDtQHpscceQ15eHubPn4/s7Gz0798f27Ztc0zETk9PdxpSGz58ONavX4/XXnsNr7zyCqKjo7Fp0yb06dPnpl53/vz5WLt2rePxgAEDAEgTru++++5bL4yIiIiapKFpNq3N5TsvZ86cibS0NFRUVODAgQMYOnSo47ldu3bh888/d2o/ceJEnD17FhUVFThx4gTGjBlz3e2npqZi1qxZTss+//xziKJY58ZwRERE1Hhr1qxBeHh4nUPwx48fjyeffBIXL17E+PHjERISAoPBgMGDB+OHH3647jblu9iSkpIQFxcHnU6HQYMG4fDhwy1RSh0uD0hERERUD1EEKs3136yWhp9rjlv1+YduZOLEiSgoKMDOnTsdywoLC7Ft2zZMmTIFpaWlGDNmDBITE3H48GGMHj0a48aNQ3p6eqO2X1paiscffxy9evVCUlISFixYgDlz5jTp7bxZbnWYPxERkduwWoC3614uSwHAr6Vf+5VMQON9w2b+/v544IEHsH79etx7770AgK+++gqBgYEYOXIkFAoF+vXr52i/aNEifPPNN/j2228xc+bMG25//fr1sNvt+Mc//gG9Xo/Y2FhcuXKlVS4VxhEkIiIiarIpU6bgv//9r+NqE+vWrcPjjz8OhUKB0tJSzJkzB7169YKfnx8MBgNOnz7d6BGkM2fOIDY21ulcRg0d6d7cOIJERETUFqn10kiOjN1uh6mkBEYfn5Y7D5K68VeIGDduHERRxHfffYfBgwdjz549eP/99wEAc+bMQUJCAv72t78hKioKXl5e+M1vftPos4W7EgMSERFRWyQI9e/mstsBtU16rg2cKFKn0+GRRx7BunXrcOHCBfTs2RMDBw4EAPz000+YPn06Hn74YQDSnKKbOWlzTEwM/vnPf6K8vNxxWa/9+/c3ew31cf07S0RERO3alClT8N133+HTTz/FlClTHMujo6Px9ddf48iRIzh69CgmT558UxednTx5MgRBwDPPPINTp05hy5Yt+Nvf/tYSJdTBgERERES35J577kGHDh1w9uxZTJ482bF86dKl8Pf3x/DhwzFu3DiMGjXKMbrUGAaDAf/+979x4sQJDBgwAK+++irefffd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Clz7vdT5Sn8g6fyk372auYyK6nUUStT9fa77B4rTvUJRq42inpvU1iYCFy+loHv3aChVKuc2tV+roW0olNLuI5VO+j+j0kkjLo7HWum+Zn6Mo7aa3UvVj9X61vm/ZrXCos2QRs8Vylr/DgxDzYkBidoPQZBOUeAdAITddnPr1hxOKg9OdvmQvoiqKiv279+H24cOhUoBKUiZ86VwZrVcm3NVVX7tZ6f76p8rSqTXvFo9CdKVFOprH/IqbfUXmAoqQYmRZgtUGe9JIxGK6g/9mi82Ra1lNfMLnJbJvyjqWVZVLgXUSrMUGOo7uqXmZ9EGlBVJYcJSIL2esrq/ok0KNY4gVOtn0X7Dt0AN4C4AONci73Dj6AOlEUlruRSsRHt1fZrqXSA14fYGyxTVu4lr3geF0vmLXKmBTVDh9PmL6NWnP5QaXa3dLBrZz9XbV9QEapXzz8rq11Oq2+wXsN1qxenyLeg6cgyUbn5eIGo9DEjkGYTqffEqrfRX7nWIVisKTlyF2OWOG8+5uh67HSjNlo4SMWVeC1XV5/dwHCorn4cB1D8/o2auh2NkpPbukcprgcFWWXduhN0KVNbdZSIAMAJAeSMm1bc3tb/YlWqIChUsFVXQG4wQVNrq0bbaI2/1jMI5Jo7WvimrJ6zW7E7VXftZpa07aiIopd0m/l2l+1Zit1pxsXgLeg5iaCBqCgYkopaiUFybj9Da7PZru49qH+LrmHAqna+kylqOA/t+xtDBg6ASxLq7k2omsMrPcyLW3uVUe+KqbJm9SgoONRPka0Y+5Ltoav/s5S9NxNd3uDbyV1VRK8SonYLPtSBUa7SjnrkXVVYrfqg+Gsbdzz5MRLeOAYnIHSkUgKJ6xOw6Z0UQrVbknyiC2O3uWxstIyJyM24+c5OIiIjo5jEgEREREckwIBERERHJMCARERERyTAgEREREckwIBERERHJMCARERERyTAgEREREckwIBERERHJMCARERERyTAgEREREckwIBERERHJMCARERERyTAgEREREckwIBERERHJMCARERERyTAgEREREckwIBERERHJMCARERERyTAgEREREckwIBERERHJMCARERERyTAgEREREckwIBERERHJMCARERERyTAgEREREckwIBERERHJMCARERERybSJgLRq1SpERkZCp9Nh6NChOHjw4HXbb9y4ETEx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EACDwYBf//rXyMnJcVGPm9c777wDQRAwa9YsxzJ3qjcjIwO//e1vERAQAC8vL/Tt2xe//PKL43lRFDF//nyEhYXBy8sL8fHxOH/+vAt73HQ2mw2vv/46unbtCi8vL3Tv3h2LFi1yuu5Se633xx9/xLhx4xAeHg5BELBp0yan5xtTV2FhIaZMmQKj0Qg/Pz889dRTKC0tbcUqGu969VqtVvzlL39B37594e3tjfDwcDzxxBPIzMx02kZ7qfdG/7a1PfvssxAEAcuWLXNa7k61nj59Gg899BB8fX3h7e2NwYMHIz093fF8a3w+MyDdpA0bNmD27Nl44403kJycjH79+mHUqFHIzc11ddduye7du/HCCy9g//79SEhIgNVqxf333w+z2exo88c//hH/93//h40bN2L37t3IzMzEI4884sJeN49Dhw7ho48+wm233ea03F3qvXr1Ku644w6o1Wps3boVp06dwpIlS+Dv7+9o89577+GDDz7A6tWrceDAAXh7e2PUqFEoLy93Yc+b5t1338WHH36IlStX4vTp03j33Xfx3nvvYcWKFY427bVes9mMfv36YdWqVfU+35i6pkyZgpMnTyIhIQGbN2/Gjz/+iGeeeaa1Srgp16vXYrEgOTkZr7/+OpKTk/H111/j7NmzeOihh5zatZd6b/RvW+Obb77B/v37671MhrvUevHiRdx5552IiYnBrl27cOzYMbz++uvQ6XSONq3y+SzSTRkyZIj4wgsvOB7bbDYxPDxcXLx4sQt71fxyc3NFAOLu3btFURTFoqIiUa1Wixs3bnS0OX36tAhA3Ldvn6u6ectKSkrE6OhoMSEhQRwxYoT40ksviaLoXvX+5S9/Ee+8884Gn7fb7WJoaKj417/+1bGsqKhI1Gq14r///e/W6GKzGjt2rPjkk086LXvkkUfEKVOmiKLoPvUCEL/55hvH48bUderUKRGAeOjQIUebrVu3ioIgiBkZGa3W96aQ11ufgwcPigDEtLQ0URTbb70N1XrlyhWxY8eO4okTJ8QuXbqI77//vuM5d6r1scceE3/72982uE5rfT5zBOkmVFZWIikpCfHx8Y5lCoUC8fHx2Ldvnwt71vyKi4sBXLsob1JSEqxWq1PtMTEx6Ny5c7uu/YUXXsDYsWOd6gLcq95vv/0WgwYNwsSJExEcHIwBAwbg448/djyfkpKC7Oxsp1p9fX0xdOjQdlcrAAwfPhyJiYk4d+4cAODo0aPYu3cvHnjgAQDuV2+NxtS1b98++Pn5YdCgQY428fHxUCgUOHDgQKv3ubkVFxdDEAT4+fkBcK967XY7pk6dirlz5yI2NrbO8+5Sq91ux3fffYcePXpg1KhRCA4OxtChQ512w7XW5zMD0k3Iz8+HzWZDSEiI0/KQkBBkZ2e7qFfNz263Y9asWbjjjjvQp08fAEB2djY0Go3jg6dGe679yy+/RHJyMhYvXlznOXeq99KlS/jwww8RHR2N7du347nnnsOLL76ItWvXAoCjHnf5vX755Zfx+OOPIyYmBmq1GgMGDMCsWbMwZcoUAO5Xb43G1JWdnY3g4GCn51UqFTp06NCuawekOSl/+ctfMGnSJMdFTd2p3nfffRcqlQovvvhivc+7S625ubkoLS3FO++8g9GjR+P777/Hww8/jEceeQS7d+8G0Hqfz6pm2xK5jRdeeAEnTpzA3r17Xd2VFnP58mW89NJLSEhIcNqv7Y7sdjsGDRqEt99+GwAwYMAAnDhxAqtXr8a0adNc3Lvm95///Afr1q3D+vXrERsbiyNHjmDWrFkIDw93y3pJmrD96KOPQhRFfPjhh67uTrNLSkrC8uXLkZycDEEQXN2dFmW32wEA48ePxx//+EcAQP/+/fHzzz9j9erVGDFiRKv1hSNINyEwMBBKpbLOTPmcnByEhoa6qFfNa+bMmdi8eTN27tyJTp06OZaHhoaisrISRUVFTu3ba+1JSUnIzc3FwIEDoVKpoFKpsHv3bnzwwQdQqVQICQlxm3rDwsLQu3dvp2W9evVyHBFSU4+7/F7PnTvXMYrUt29fTJ06FX/84x8dI4XuVm+NxtQVGhpa54CSqqoqFBYWttvaa8JRWloaEhISHKNHgPvUu2fPHuTm5qJz586Oz6u0tDT86U9/QmRkJAD3qTUwMBAqleqGn1mt8fnMgHQTNBoN4uLikJiY6Fhmt9uRmJiIYcOGubBnt04URcycORPffPMNduzYga5duzo9HxcXB7Va7VT72bNnkZ6e3i5rv/fee3H8+HEcOXLEcRs0aBCmTJni+Nld6r3jjjvqnLLh3Llz6NKlCwCga9euCA0NdarVZDLhwIED7a5WQDq6SaFw/mhTKpWOv0zdrd4ajalr2LBhKCoqQlJSkqPNjh07YLfbMXTo0Fbv862qCUfnz5/HDz/8gICAAKfn3aXeqVOn4tixY06fV+Hh4Zg7dy62b98OwH1q1Wg0GDx48HU/s1rt+6jZpnt7iC+//FLUarXi559/Lp46dUp85plnRD8/PzE7O9vVXbslzz33nOjr6yvu2rVLzMrKctwsFoujzbPPPit27txZ3LFjh/jLL7+Iw4YNE4cNG+bCXjev2kexiaL71Hvw4EFRpVKJb731lnj+/Hlx3bp1ol6vF//1r3852rzzzjuin5+f+L///U88duyYOH78eLFr165iWVmZC3veNNOmTRM7duwobt68WUxJSRG//vprMTAwUPzzn//saNNe6y0pKREPHz4sHj58WAQgLl26VDx8+LDjqK3G1DV69GhxwIAB4oEDB8S9e/eK0dHR4qRJk1xV0nVdr97KykrxoYceEjt16iQeOXLE6XOroqLCsY32Uu+N/m3l5EexiaL71Pr111+LarVaXLNmjXj+/HlxxYoVolKpFPfs2ePYRmt8PjMgNcGKFSvEzp07ixqNRhwyZIi4f/9+V3fplgGo9/bZZ5852pSVlYnPP/+86O/vL+r1evHhhx8Ws7KyXNfpZiYPSO5U7//93/+Jffr0EbVarRgTEyOuWbPG6Xm73S6+/vrrYkhIiKjVasV7771XPHv2rIt6e2tMJpP40ksviZ07dxZ1Op3YrVs38dVXX3X60myv9e7cubPe/6fTpk0TRbFxdRUUFIiTJk0SDQaDaDQaxRkzZoglJSUuqObGrldvSkpKg59bO3fudGyjvdR7o39bufoCkjvV+sknn4hRUVGiTqcT+/XrJ27atMlpG63x+SyIYq3TyxIRERER5yARERERyTEgEREREckwIBERERHJMCARERERyTAgEREREckwIBERERHJMCARERERyTAgERE1UmRkJJYtW+bqbhBRK2BAIqI2afr06ZgwYQIA4O6778asWbNa7bU///xz+Pn51Vl+6NAhPPPMM63WDyJyHZWrO0BE1FoqKyuh0WiavH5QUFAz9oaI2jKOIBFRmzZ9+nTs3r0by5cvhyAIEAQBqampAIATJ07ggQcegMFgQEhICKZOnYr8/HzHunfffTdmzpyJWbNmITAwEKNGjQIALF26FH379oW3tzciIiLw/PPPo7S0FACwa9cuzJgxA8XFxY7XW7BgAYC6u9jS09Mxfvx4GAwGGI1GPProo8jJyXE8v2DBAvTv3x///Oc/ERkZCV9fXzz++OMoKSlxtPnqq6/Qt29feHl5ISAgAPHx8TCbzS30bhJRYzEgEVGbtnz5cgwbNgxPP/00srKykJWVhYiICBQVFeGee+7BgAED8Msvv2Dbtm3IycnBo48+6rT+2rVrodFo8NNPP2H16tUAAIVCgQ8++AAnT57E2rVrsWPHDvz5z38GAAwfPhzLli2D0Wh0vN6cOXPq9Mtut2P8+PEoLCzE7t27kZCQgEuXLuGxxx5zanfx4kVs2rQJmzdvxubNm7F792688847AICsrCxMmjQJTz75JE6fPo1du3bhkUceAS+RSeR63MVGRG2ar68vNBoN9Ho9QkNDHctXrlyJAQMG4O2333Ys+/TTTxEREYFz586hR48eAIDo6Gi89957TtusPZ8pMjIS/+///T88++yz+Pvf/w6NRgNfX18IguD0enKJiYk4fvw4UlJSEBERAQD44osvEBsbi0OHDmHw4MEApCD1+eefw8fHBwAwdepUJCYm4q233kJWVhaqqqrwyCOPoEuXLgCAvn373sK7RUTNhSNIRNQuHT16FDt37oTBYHDcYmJiAEijNjXi4uLqrPvDDz/g3nvvRceOHeHj44OpU6eioKAAFoul0a9/+vRpREREOMIRAPTu3Rt+fn44ffq0Y1lkZKQjHAFAWFgYcnNzAQD9+vXDvffei759+2LixIn4+OOPcfXq1ca/CUTUYhiQiKhdKi0txbhx43DkyBGn2/nz53HXXXc52nl7ezutl5qaigcffBC33XYb/vvf/yIpKQmrVq0CIE3ibm5qtdrpsSAIsNvtAAClUomEhARs3boVvXv3xooVK9CzZ0+kpKQ0ez+I6OYwIBFRm6fRaGCz2ZyWDRw4ECdPnkRkZCSioqKcbvJQVFtSUhLsdjuWLFmC22+/HT169EBmZuYNX0+uV69euHz5Mi5fvuxYdurUKRQVFaF3796Nrk0QBNxxxx1YuHAhDh8+DI1Gg2+++abR6xNRy2BAIqI2LzIyEgcOHEBqairy8/Nht9vxwgsvoLCwEJMmTcKhQ4dw8eJFbN++HTNmzLhuuImKioLVasWKFStw6dIl/POf/3RM3q79eqWlpUhMTER+fn69u97i4+PRt29fTJkyBcnJyTh48CCeeOIJjBgxAoMGDWpUXQcOHMDbb7+NX375Benp6fj666+Rl5eHXr163dwbRETNjgGJiNq8OXPmQKlUonfv3ggKCkJ6ejrCw8Px008/wWaz4f7770ffvn0xa9Ys+Pn5QaFo+KOtX79+WLp0Kd5991306dMH69atw+LFi53aDB8+HM8++ywee+wxBAUF1ZnkDUgjP//73//g7++Pu+66C/Hx8ejWrRs2bNjQ6LqMRiN+/PFHjBkzBj169MBrr72GJUuW4IEHHmj8m0NELUIQeTwpERERkROOIBERERHJMCARERERyTAgEREREckwIBERERHJMCARERERyTAgEREREckwIBERERHJMCARERERyTAgEREREckwIBERERHJMCARERERyTAgEREREcn8fxjeNy4vzTeWAAAAAElFTkSuQmCC",
|
||
"text/plain": [
|
||
"<Figure size 640x480 with 1 Axes>"
|
||
]
|
||
},
|
||
"metadata": {},
|
||
"output_type": "display_data"
|
||
},
|
||
{
|
||
"data": {
|
||
"image/png": "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7+r/o+sKK/OT6pn3g/fhNny0/Tp16sSff/5JTEwMsbGxdOjQgeHDhxMREfHE4zZq1Ijk5GS2b9/O7t276dGjBx07dmT9+vVP3K+kHj3XmZmZNG7cmFWrVuVL+6T5LUVpG3jQ05h3bh5WkvdX79696d27N//++y/lypVDpVIxb948rffXzz//rLXPv//+q9lWVGvWrGHgwIH88MMPWsMRC2JsbFzg/0dDQ0P5ctIhaX/dexXOwbBhw1i9ejVbtmzBxsaG69evAw+GLpuampKUlMTq1asJCAigQoUKnDlzhlGjRtG6dWsaN26syeezzz7D398fPT09Nm7cyGeffca6devyDZkujleh/csyaX/depr2L056nd7FS6VS0aJFC8LCwvj1118xMjJi06ZNGBkZkZurPXTDzc2NM2fOaA25OXbsWImOX7duXYyNjUlJSaFmzZpaLycnJwDNFfFHy2Nra6s1uS4jI4Pk5OQSlac4ypUrh6OjIzdu3GDnzp2aCfHVqlXDwcGBuLg4rbLFx8c/VY/T85L3A/3hNizoeTa2trb069ePlStXEhkZyZIlS4qUv6WlJT179mTp0qWsXbuWDRs28N9//wEP/oMU9P56NDgu6furUaNG/PHHH9jZ2eV7f1lZWQEU+F4vats8T/b29pibm7N27VpMTEw0vYze3t6cPXtWq0cmNjYWS0tL6tatW6S8v//+e/r378/3339P5875J8IKIUSexYsXk56eTtu2bXF0dNS81q5dCzz4DN29eze+vr7UqVOHjz/+mO7du/PTTz9p5bN9+3ZatWqFl5cX27ZtY8uWLWXqFu9CCG0660GJj48nLi4OX19f7OzsiI+P5+rVq7i5uXHv3j127txJQkICFSpUwMrKit69ezNp0iQGDRrExIkTuXTpUqFX0gtjYWHBmDFjGDVqFGq1mpYtW5Kens7hw4extLSkX79+ODs7o1Kp2Lp1KwEBAZiammJubk779u2Jjo6ma9euWFtbExISUuxu4qexc+dOFEWhdu3aJCYmMnbsWOrUqaOZ56JSqQgODubTTz/F1dWVatWqMWXKFCpVqlSmPoxNTU157bXXmDVrFtWqVePKlStMnjxZK01ISAiNGzemXr16ZGVlsXXrVtzc3ArNe968eTg6OuLp6Ymenh4//PADDg4OmjlELi4umvk0xsbGlC9fnpEjRxIY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",
|
||
"text/plain": [
|
||
"<Figure size 640x480 with 1 Axes>"
|
||
]
|
||
},
|
||
"metadata": {},
|
||
"output_type": "display_data"
|
||
}
|
||
],
|
||
"source": [
|
||
"\n",
|
||
"gc.collect()\n",
|
||
"\n",
|
||
"use_pca = False\n",
|
||
"type = 'light'\n",
|
||
"# feature_contri = [2 if feat.startswith('act_factor') or 'buy' in feat or 'sell' in feat else 1 for feat in feature_columns]\n",
|
||
"# light_params['feature_contri'] = feature_contri\n",
|
||
"# print(f'feature_contri: {feature_contri}')\n",
|
||
"model, scaler, pca = train_model(train_data\n",
|
||
" .dropna(subset=['label']).groupby('trade_date', group_keys=False)\n",
|
||
" .apply(lambda x: x.nsmallest(1000, 'total_mv'))\n",
|
||
" .merge(industry_df, on=['cat_l2_code', 'trade_date'], how='left')\n",
|
||
" .merge(index_data, on='trade_date', how='left'), \n",
|
||
" feature_columns, type=type, target_column='future_return_2')\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 25,
|
||
"id": "5d1522a7538db91b",
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2025-04-03T15:04:39.656944Z",
|
||
"start_time": "2025-04-03T15:04:39.298483Z"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"score_df = test_data.groupby('trade_date', group_keys=False).apply(lambda x: x.nsmallest(1000, 'total_mv'))\n",
|
||
"# score_df = fill_nan_with_daily_median(score_df, ['pe_ttm'])\n",
|
||
"# score_df = score_df[score_df['pe_ttm'] > 0]\n",
|
||
"score_df = score_df.merge(industry_df, on=['cat_l2_code', 'trade_date'], how='left')\n",
|
||
"score_df = score_df.merge(index_data, on='trade_date', how='left')\n",
|
||
"# score_df = score_df.groupby('trade_date', group_keys=False).apply(lambda x: x.nsmallest(50, 'total_mv')).reset_index()\n",
|
||
"numeric_columns = score_df.select_dtypes(include=['float64', 'int64']).columns\n",
|
||
"numeric_columns = [col for col in feature_columns if col in numeric_columns]\n",
|
||
"\n",
|
||
"import pandas as pd\n",
|
||
"\n",
|
||
"def filter_high_volatility_daily(df: pd.DataFrame, threshold: float, window: int = 5,\n",
|
||
" date_col: str = 'trade_date', code_col: str = 'ts_code',\n",
|
||
" close_col: str = 'close') -> pd.DataFrame:\n",
|
||
" df = df.sort_values([code_col, date_col])\n",
|
||
"\n",
|
||
" def get_daily_std_rank(group):\n",
|
||
" rolling_std = group[close_col].rolling(window=window, min_periods=window).std()\n",
|
||
" # 在每个日期内计算标准差的百分比排名\n",
|
||
" return rolling_std.groupby(group[date_col]).rank(pct=True, ascending=False)\n",
|
||
"\n",
|
||
" # 直接在 apply 中计算并添加 'std_rank' 列\n",
|
||
" df_with_std = df.groupby(code_col, group_keys=False).apply(\n",
|
||
" lambda x: x.assign(std_rank=get_daily_std_rank(x))\n",
|
||
" )\n",
|
||
"\n",
|
||
" # 过滤掉每天标准差排名在前 top_percent 的股票\n",
|
||
" threshold_rank = threshold / 100.0\n",
|
||
" df_filtered = df_with_std[df_with_std['std_rank'] > threshold_rank].drop(\n",
|
||
" columns=['std_rank']\n",
|
||
" )\n",
|
||
"\n",
|
||
" return df_filtered\n",
|
||
"\n",
|
||
"# threshold = 10\n",
|
||
"# score_df = filter_high_volatility_daily(score_df, threshold=threshold)\n",
|
||
"# print(len(score_df))\n",
|
||
"\n",
|
||
"if type == 'cat':\n",
|
||
" score_df['score'] = model.predict(score_df[feature_columns])\n",
|
||
"elif type == 'light':\n",
|
||
" score_df['score'] = model.predict(score_df[feature_columns])\n",
|
||
"score_df['score_ranks'] = score_df.groupby('trade_date')['score'].rank(ascending=True)\n",
|
||
"\n",
|
||
"score_df = score_df.groupby('trade_date', group_keys=False).apply(\n",
|
||
" lambda x: \n",
|
||
" x[\n",
|
||
" # (x['score'] <= x['score'].quantile(0.99)) & \n",
|
||
" (x['score'] >= x['score'].quantile(0.90))\n",
|
||
" ] # 计算90%分位数作为阈值,筛选分数>=阈值的行\n",
|
||
").reset_index(drop=True) # drop=True 避免添加旧索引列\n",
|
||
"# df_to_drop = score_df.loc[score_df.groupby('trade_date')['score'].idxmax()]\n",
|
||
"# score_df = score_df.drop(df_to_drop.index)\n",
|
||
"save_df = score_df.groupby('trade_date', group_keys=False).apply(lambda x: x.nlargest(2, 'score')).reset_index()\n",
|
||
"# save_df = score_df.groupby('trade_date', group_keys=False).apply(lambda x: x.nsmallest(2, 'total_mv')).reset_index()\n",
|
||
"save_df = save_df.sort_values(['trade_date', 'score'])\n",
|
||
"save_df[['trade_date', 'score', 'ts_code']].to_csv('predictions_test.tsv', index=False)\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 26,
|
||
"id": "09b1799e",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"162\n",
|
||
"['vol', 'pct_chg', 'turnover_rate', 'volume_ratio', 'winner_rate', 'undist_profit_ps', 'ocfps', 'AR', 'BR', 'AR_BR', 'log_circ_mv', 'cashflow_to_ev_factor', 'book_to_price_ratio', 'turnover_rate_mean_5', 'variance_20', 'bbi_ratio_factor', 'daily_deviation', 'lg_elg_net_buy_vol', 'flow_lg_elg_intensity', 'sm_net_buy_vol', 'total_buy_vol', 'lg_elg_buy_prop', 'flow_struct_buy_change', 'lg_elg_net_buy_vol_change', 'flow_lg_elg_accel', 'chip_concentration_range', 'chip_skewness', 'floating_chip_proxy', 'cost_support_15pct_change', 'cat_winner_price_zone', 'flow_chip_consistency', 'profit_taking_vs_absorb', 'cat_is_positive', 'upside_vol', 'downside_vol', 'vol_ratio', 'return_skew', 'return_kurtosis', 'volume_change_rate', 'cat_volume_breakout', 'turnover_deviation', 'cat_turnover_spike', 'avg_volume_ratio', 'cat_volume_ratio_breakout', 'vol_spike', 'vol_std_5', 'atr_14', 'atr_6', 'obv', 'maobv_6', 'rsi_3', 'return_5', 'return_20', 'std_return_5', 'std_return_90', 'std_return_90_2', 'act_factor1', 'act_factor2', 'act_factor3', 'act_factor4', 'rank_act_factor1', 'rank_act_factor2', 'rank_act_factor3', 'cov', 'delta_cov', 'alpha_22_improved', 'alpha_003', 'alpha_007', 'alpha_013', 'vol_break', 'weight_roc5', 'smallcap_concentration', 'cost_stability', 'high_cost_break_days', 'liquidity_risk', 'turnover_std', 'mv_volatility', 'volume_growth', 'mv_growth', 'momentum_factor', 'resonance_factor', 'log_close', 'cat_vol_spike', 'up', 'down', 'obv_maobv_6', 'std_return_5_over_std_return_90', 'std_return_90_minus_std_return_90_2', 'cat_af2', 'cat_af3', 'cat_af4', 'act_factor5', 'act_factor6', 'active_buy_volume_large', 'active_buy_volume_big', 'active_buy_volume_small', 'buy_lg_vol_minus_sell_lg_vol', 'buy_elg_vol_minus_sell_elg_vol', 'ctrl_strength', 'low_cost_dev', 'asymmetry', 'lock_factor', 'cat_vol_break', 'cost_atr_adj', 'cat_golden_resonance', 'mv_turnover_ratio', 'mv_adjusted_volume', 'mv_weighted_turnover', 'nonlinear_mv_volume', 'mv_volume_ratio', 'mv_momentum', 'lg_flow_mom_corr_20_60', 'lg_flow_accel', 'profit_pressure', 'underwater_resistance', 'cost_conc_std_20', 'profit_decay_20', 'vol_amp_loss_20', 'vol_drop_profit_cnt_5', 'lg_flow_vol_interact_20', 'cost_break_confirm_cnt_5', 'atr_norm_channel_pos_14', 'turnover_diff_skew_20', 'lg_sm_flow_diverge_20', 'pullback_strong_20_20', 'vol_wgt_hist_pos_20', 'vol_adj_roc_20', 'cs_rank_net_lg_flow_val', 'cs_rank_elg_buy_ratio', 'cs_rank_rel_profit_margin', 'cs_rank_cost_breadth', 'cs_rank_dist_to_upper_cost', 'cs_rank_winner_rate', 'cs_rank_intraday_range', 'cs_rank_close_pos_in_range', 'cs_rank_pos_in_hist_range', 'cs_rank_vol_x_profit_margin', 'cs_rank_lg_flow_price_concordance', 'cs_rank_turnover_per_winner', 'cs_rank_volume_ratio', 'cs_rank_elg_buy_sell_sm_ratio', 'cs_rank_cost_dist_vol_ratio', 'cs_rank_size', 'cat_up_limit', 'industry_obv', 'industry_return_5', 'industry_return_20', 'industry__ema_5', 'industry__ema_13', 'industry__ema_20', 'industry__ema_60', 'industry_act_factor1', 'industry_act_factor2', 'industry_act_factor3', 'industry_act_factor4', 'industry_act_factor5', 'industry_act_factor6', 'industry_rank_act_factor1', 'industry_rank_act_factor2', 'industry_rank_act_factor3', 'industry_return_5_percentile', 'industry_return_20_percentile']\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"print(len(feature_columns))\n",
|
||
"print(feature_columns)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 27,
|
||
"id": "bceabd1f",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"# print(model.get_feature_importance())"
|
||
]
|
||
}
|
||
],
|
||
"metadata": {
|
||
"kernelspec": {
|
||
"display_name": "stock",
|
||
"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.13.2"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 5
|
||
}
|