2196 lines
243 KiB
Plaintext
2196 lines
243 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 11,
|
||
"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",
|
||
"e:\\PyProject\\NewStock\\main\\train\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"%load_ext autoreload\n",
|
||
"%autoreload 2\n",
|
||
"\n",
|
||
"import gc\n",
|
||
"import os\n",
|
||
"import sys\n",
|
||
"sys.path.append('../../')\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": 12,
|
||
"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: 8665405 entries, 0 to 8665404\n",
|
||
"Data columns (total 33 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 amount float64 \n",
|
||
" 9 turnover_rate float64 \n",
|
||
" 10 pe_ttm float64 \n",
|
||
" 11 circ_mv float64 \n",
|
||
" 12 total_mv float64 \n",
|
||
" 13 volume_ratio float64 \n",
|
||
" 14 is_st bool \n",
|
||
" 15 up_limit float64 \n",
|
||
" 16 down_limit float64 \n",
|
||
" 17 buy_sm_vol float64 \n",
|
||
" 18 sell_sm_vol float64 \n",
|
||
" 19 buy_lg_vol float64 \n",
|
||
" 20 sell_lg_vol float64 \n",
|
||
" 21 buy_elg_vol float64 \n",
|
||
" 22 sell_elg_vol float64 \n",
|
||
" 23 net_mf_vol float64 \n",
|
||
" 24 his_low float64 \n",
|
||
" 25 his_high float64 \n",
|
||
" 26 cost_5pct float64 \n",
|
||
" 27 cost_15pct float64 \n",
|
||
" 28 cost_50pct float64 \n",
|
||
" 29 cost_85pct float64 \n",
|
||
" 30 cost_95pct float64 \n",
|
||
" 31 weight_avg float64 \n",
|
||
" 32 winner_rate float64 \n",
|
||
"dtypes: bool(1), datetime64[ns](1), float64(30), object(1)\n",
|
||
"memory usage: 2.1+ 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('../../data/daily_data.h5', key='daily_data',\n",
|
||
" columns=['ts_code', 'trade_date', 'open', 'close', 'high', 'low', 'vol', 'pct_chg', 'amount'],\n",
|
||
" df=None)\n",
|
||
"\n",
|
||
"print('daily basic')\n",
|
||
"df = read_and_merge_h5_data('../../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('../../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('../../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('../../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": 13,
|
||
"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('../../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": 14,
|
||
"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 = '../../data/index_data.h5'\n",
|
||
"index_data = generate_index_indicators(h5_filename)\n",
|
||
"index_data = index_data.dropna()\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 15,
|
||
"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": 16,
|
||
"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('../../data/sw_daily.h5')\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 17,
|
||
"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', 'amount', '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": 18,
|
||
"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('../../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('../../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('../../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('../../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": 19,
|
||
"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": [
|
||
"Calculating cat_senti_mom_vol_spike...\n",
|
||
"Finished cat_senti_mom_vol_spike.\n",
|
||
"Calculating cat_senti_pre_breakout...\n",
|
||
"Calculating atr_10 as it's missing...\n",
|
||
"Calculating atr_40 as it's missing...\n",
|
||
"Finished cat_senti_pre_breakout.\n",
|
||
"计算因子 ts_turnover_rate_acceleration_5_20\n",
|
||
"计算因子 ts_vol_sustain_10_30\n",
|
||
"计算因子 cs_amount_outlier_10\n",
|
||
"计算因子 ts_ff_to_total_turnover_ratio\n",
|
||
"计算因子 ts_price_volume_trend_coherence_5_20\n",
|
||
"计算因子 ts_ff_turnover_rate_surge_10\n",
|
||
"使用 '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', 'amount', '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', 'cat_senti_mom_vol_spike',\n",
|
||
" 'cat_senti_pre_breakout', 'ts_turnover_rate_acceleration_5_20',\n",
|
||
" 'ts_vol_sustain_10_30', 'cs_amount_outlier_10',\n",
|
||
" 'ts_ff_to_total_turnover_ratio', 'ts_price_volume_trend_coherence_5_20',\n",
|
||
" 'ts_ff_turnover_rate_surge_10', '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: 4539678 entries, 0 to 4539677\n",
|
||
"Columns: 187 entries, ts_code to cs_rank_size\n",
|
||
"dtypes: bool(10), datetime64[ns](1), float64(169), int32(3), object(4)\n",
|
||
"memory usage: 6.0+ GB\n",
|
||
"None\n",
|
||
"['ts_code', 'trade_date', 'open', 'close', 'high', 'low', 'vol', 'pct_chg', 'amount', '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', 'cat_senti_mom_vol_spike', 'cat_senti_pre_breakout', 'ts_turnover_rate_acceleration_5_20', 'ts_vol_sustain_10_30', 'cs_amount_outlier_10', 'ts_ff_to_total_turnover_ratio', 'ts_price_volume_trend_coherence_5_20', 'ts_ff_turnover_rate_surge_10', '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": [
|
||
"import numpy as np\n",
|
||
"from main.factor.factor import *\n",
|
||
"\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",
|
||
"\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 = cat_senti_mom_vol_spike(\n",
|
||
" df,\n",
|
||
" return_period=3,\n",
|
||
" return_threshold=0.03, # 近3日涨幅超3%\n",
|
||
" volume_ratio_threshold=1.3,\n",
|
||
" current_pct_chg_min=0.0, # 当日必须收红\n",
|
||
" current_pct_chg_max=0.04,\n",
|
||
") # 当日涨幅不宜过大\n",
|
||
"\n",
|
||
"df = cat_senti_pre_breakout(\n",
|
||
" df,\n",
|
||
" atr_short_N=10,\n",
|
||
" atr_long_M=40,\n",
|
||
" vol_atrophy_N=10,\n",
|
||
" vol_atrophy_M=40,\n",
|
||
" price_stab_N=5,\n",
|
||
" price_stab_threshold=0.06,\n",
|
||
" current_pct_chg_min_signal=0.002,\n",
|
||
" current_pct_chg_max_signal=0.05,\n",
|
||
" volume_ratio_signal_threshold=1.1,\n",
|
||
")\n",
|
||
"\n",
|
||
"df = ts_turnover_rate_acceleration_5_20(df)\n",
|
||
"df = ts_vol_sustain_10_30(df)\n",
|
||
"# df = cs_turnover_rate_relative_strength_20(df)\n",
|
||
"df = cs_amount_outlier_10(df)\n",
|
||
"df = ts_ff_to_total_turnover_ratio(df)\n",
|
||
"df = ts_price_volume_trend_coherence_5_20(df)\n",
|
||
"# df = ts_turnover_rate_trend_strength_5(df)\n",
|
||
"df = ts_ff_turnover_rate_surge_10(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": 20,
|
||
"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": 21,
|
||
"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": 22,
|
||
"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": 23,
|
||
"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",
|
||
"\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=50, 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",
|
||
"filter_index = df['future_return'].between(df['future_return'].quantile(0.001), 0.6)\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": 24,
|
||
"id": "29221dde",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"199\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": 25,
|
||
"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": 26,
|
||
"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"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"['vol', 'pct_chg', 'turnover_rate', 'volume_ratio', 'winner_rate', 'cat_senti_mom_vol_spike', 'cat_senti_pre_breakout', 'ts_turnover_rate_acceleration_5_20', 'ts_vol_sustain_10_30', 'cs_amount_outlier_10', 'ts_ff_to_total_turnover_ratio', 'ts_price_volume_trend_coherence_5_20', 'ts_ff_turnover_rate_surge_10', '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', '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', '000852.SH_MACD', '000905.SH_MACD', '399006.SZ_MACD', '000852.SH_MACD_hist', '000905.SH_MACD_hist', '399006.SZ_MACD_hist', '000852.SH_RSI', '000905.SH_RSI', '399006.SZ_RSI', '000852.SH_Signal_line', '000905.SH_Signal_line', '399006.SZ_Signal_line', '000852.SH_amount_change_rate', '000905.SH_amount_change_rate', '399006.SZ_amount_change_rate', '000852.SH_amount_mean', '000905.SH_amount_mean', '399006.SZ_amount_mean', '000852.SH_daily_return', '000905.SH_daily_return', '399006.SZ_daily_return', '000852.SH_up_ratio_20d', '000905.SH_up_ratio_20d', '399006.SZ_up_ratio_20d', '000852.SH_volatility', '000905.SH_volatility', '399006.SZ_volatility', '000852.SH_volume_change_rate', '000905.SH_volume_change_rate', '399006.SZ_volume_change_rate']\n",
|
||
"去除极值\n",
|
||
"开始截面 MAD 去极值处理 (k=3.0)...\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"MAD Filtering: 100%|██████████| 138/138 [00:30<00:00, 4.50it/s]\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"截面 MAD 去极值处理完成。\n",
|
||
"开始截面 MAD 去极值处理 (k=3.0)...\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"MAD Filtering: 100%|██████████| 138/138 [00:25<00:00, 5.46it/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', 'cat_senti_mom_vol_spike', 'cat_senti_pre_breakout', 'ts_turnover_rate_acceleration_5_20', 'ts_vol_sustain_10_30', 'cs_amount_outlier_10', 'ts_ff_to_total_turnover_ratio', 'ts_price_volume_trend_coherence_5_20', 'ts_ff_turnover_rate_surge_10', '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', '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', '000852.SH_MACD', '000905.SH_MACD', '399006.SZ_MACD', '000852.SH_MACD_hist', '000905.SH_MACD_hist', '399006.SZ_MACD_hist', '000852.SH_RSI', '000905.SH_RSI', '399006.SZ_RSI', '000852.SH_Signal_line', '000905.SH_Signal_line', '399006.SZ_Signal_line', '000852.SH_amount_change_rate', '000905.SH_amount_change_rate', '399006.SZ_amount_change_rate', '000852.SH_amount_mean', '000905.SH_amount_mean', '399006.SZ_amount_mean', '000852.SH_daily_return', '000905.SH_daily_return', '399006.SZ_daily_return', '000852.SH_up_ratio_20d', '000905.SH_up_ratio_20d', '399006.SZ_up_ratio_20d', '000852.SH_volatility', '000905.SH_volatility', '399006.SZ_volatility', '000852.SH_volume_change_rate', '000905.SH_volume_change_rate', '399006.SZ_volume_change_rate']\n",
|
||
"df最小日期: 2019-01-02\n",
|
||
"df最大日期: 2025-05-23\n",
|
||
"2097932\n",
|
||
"train_data最小日期: 2020-01-02\n",
|
||
"train_data最大日期: 2022-12-30\n",
|
||
"1766694\n",
|
||
"test_data最小日期: 2023-01-03\n",
|
||
"test_data最大日期: 2025-05-23\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": 37,
|
||
"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 LGBMRanker, 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",
|
||
" train_data_split = train_data_split.sort_values('trade_date')\n",
|
||
" val_data_split = val_data_split.sort_values('trade_date')\n",
|
||
"\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': 'QueryRMSE', # 适用于二分类\n",
|
||
" 'eval_metric': 'NDCG', # 评估指标\n",
|
||
" 'iterations': 1500,\n",
|
||
" 'learning_rate': 0.03,\n",
|
||
" 'depth': 10, # 控制模型复杂度\n",
|
||
" # 'l2_leaf_reg': 0.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",
|
||
"\n",
|
||
" model = CatBoostRanker(**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': 'lambdarank',\n",
|
||
" 'metric': 'lambdarank',\n",
|
||
" 'learning_rate': 0.05,\n",
|
||
" # 'num_leaves': 1024,\n",
|
||
" # 'min_data_in_leaf': 256,\n",
|
||
" 'max_depth': 10,\n",
|
||
" # 'max_bin': 1024,\n",
|
||
" 'feature_fraction': 0.7,\n",
|
||
" 'bagging_fraction': 0.7,\n",
|
||
" 'bagging_freq': 5,\n",
|
||
" 'lambda_l1': 5,\n",
|
||
" 'lambda_l2': 5,\n",
|
||
" 'boosting': 'gbdt',\n",
|
||
" 'verbosity': -1,\n",
|
||
" 'extra_trees': True,\n",
|
||
" # 'max_position': 5,\n",
|
||
" # 'ndcg_at': 50,\n",
|
||
" 'quant_train_renew_leaf': True,\n",
|
||
" # 'lambdarank_truncation_level': 100,\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(300, first_metric_only=False)\n",
|
||
" ]\n",
|
||
" # 训练模型\n",
|
||
" model = lgb.train(\n",
|
||
" params, train_dataset, num_boost_round=3000,\n",
|
||
" valid_sets=[train_dataset, val_dataset], valid_names=['train', 'valid'],\n",
|
||
" callbacks=callbacks\n",
|
||
" )\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",
|
||
" # 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\": 600, # in seconds\n",
|
||
" # \"metric\": \"ndcg@1\",\n",
|
||
" # \"task\": \"rank\",\n",
|
||
" # \"estimator_list\": [\n",
|
||
" # \"catboost\",\n",
|
||
" # \"lgbm\",\n",
|
||
" # \"xgboost\"\n",
|
||
" # ], \n",
|
||
" # \"ensemble\": {\n",
|
||
" # \"final_estimator\": LGBMRanker(),\n",
|
||
" # \"passthrough\": False,\n",
|
||
" # },\n",
|
||
" # }\n",
|
||
" # model.fit(X_train=X_train, y_train=y_train, groups=train_groups,\n",
|
||
" # X_val=X_val, y_val=y_val,groups_val=val_groups,\n",
|
||
" # mlflow_logging=False, **automl_settings)\n",
|
||
"\n",
|
||
"\n",
|
||
" return model, scaler, None # 返回训练好的模型、scaler 和 pca 对象"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 38,
|
||
"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: 364000\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",
|
||
"363995 603351.SH 2022-12-30 12.566524\n",
|
||
"363996 002363.SZ 2022-12-30 12.588302\n",
|
||
"363997 605218.SH 2022-12-30 11.710093\n",
|
||
"363998 603519.SH 2022-12-30 12.592329\n",
|
||
"363999 600293.SH 2022-12-30 12.593635\n",
|
||
"\n",
|
||
"[364000 rows x 3 columns]\n",
|
||
"原始样本数: 364000, 去除标签为空后样本数: 364000\n",
|
||
"Training until validation scores don't improve for 300 rounds\n",
|
||
"Early stopping, best iteration is:\n",
|
||
"[368]\ttrain's ndcg@1: 0.694512\ttrain's ndcg@2: 0.65148\ttrain's ndcg@3: 0.62293\ttrain's ndcg@4: 0.606404\ttrain's ndcg@5: 0.592129\tvalid's ndcg@1: 0.456055\tvalid's ndcg@2: 0.458545\tvalid's ndcg@3: 0.457883\tvalid's ndcg@4: 0.445194\tvalid's ndcg@5: 0.439297\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"image/png": "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",
|
||
"text/plain": [
|
||
"<Figure size 640x480 with 1 Axes>"
|
||
]
|
||
},
|
||
"metadata": {},
|
||
"output_type": "display_data"
|
||
},
|
||
{
|
||
"data": {
|
||
"image/png": "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PLfb29trPSdDCCGEKArSwBDiDWBiYsLChQtZuHBhrttr1qzJTz/9lOu2zp0753l72aCgoFzv/uTq6sru3bufP2AhhBBCvLFkDoYQQgghhBCi0EgDQwghhBBCCFFoZIiUECKHVatWFXUIQgghhCimpAdDCCGEEEIIUWikgSGEEEIIIYQoNNLAEEKI19zff//NRx99hI2NDaampri5uXH06FF1u0ajyfU1d+7cIoxaCCGErpIGhhDFiIuLCyEhIYVer5+fH507dy70esWLu337Nk2aNMHQ0JBdu3Zx9uxZgoOD1WecAFy9elXrtXLlSjQaDe+//34RRi6EEEJXySRvIQQLFizI81kYomjNnj0bR0dHrQfoVahQQauMvb291vK2bdto0aIFb7311iuJUQghhHic9GAI8ZJlZGQUdQjPZGVlRcmSJYs6DJGL7du3U69ePbp164adnR3u7u4sX748z/L//vsvO3fupG/fvq8wSiGEEOL/SA+G0GlZWVnMmzePZcuWceXKFcqUKcOnn37KmDFjGDlyJJs3b+b27duUKVOGgQMHEhAQ8Mw6NRoN33zzDbt27SIyMpIxY8YwceJEBgwYwL59+7h27RpOTk4MHjyY4cOHq/v5+flx584dmjZtSnBwMOnp6Xz44YeEhIRgaGiY67FWrFjB6NGj2bx5M61atXpqXJs2bSIoKIj4+HjMzMxwd3dn27ZtmJubq8cOCwvj0qVLOb4hB/D09CQqKgqAgwcPEhAQwNGjRyldujRdunRh5syZmJubP/P6PK7hzEgeGhRsH11xadY7APz5558sXryYkSNHMm7cOGJjYxk2bBhGRkb4+vrm2O/bb7+lRIkSdO3a9VWHLIQQQgDSwBA6LiAggOXLlzN//nyaNm3K1atX+eOPP1i4cCHbt29n48aNODk5ceXKFa5cuZLvegMDA5k1axYhISEYGBiQlZVF+fLl+eGHH7CxseHQoUMMGDAABwcHunfvru63f/9+HBwc2L9/P/Hx8fTo0YPatWvTv3//HMeYM2cOc+bMYc+ePTRo0OCp8Vy9epWePXsyZ84cunTpwt27dzlw4ECuw6IcHR25evWqunzt2jVat25N8+bNAUhISMDb25tp06axcuVKbty4gb+/P/7+/lrDeB6XlpZGWlqaupycnAyAsZ6Cvr4MzcpNds9XVlYWdevWJSgoCIAaNWpw6tQpFi9eTK9evXLs97///Y+ePXuir69faL1n2fUUh9448YjkrHiRfBUvupyv/J6zRpGB10JH3b17F1tbW77++mv69euntW3YsGH8/vvv7N27F41GU6B6NRoNI0aMYP78+U8t5+/vz7Vr19i0aRPwqAcjKiqKhIQE9PX1AejevTt6enps2LABeDTJe8SIEVy9epU1a9YQERFB9erVnxnTb7/9Rt26dbl06RLOzs45tj/eg/G4Bw8e4OXlha2tLdu2bUNPT49+/fqhr6/P0qVL1XIHDx7E09OTe/fuYWJikqP+wMBA9QPy49avX4+Zmdkz49dl/fv3p1atWvj7+6vrdu3axQ8//MDKlSu1yv7++++MHz+e+fPn59oLJYQQQryI1NRUevXqRVJSEpaWlnmWkx4MobPOnTtHWlparkOL/Pz8aNOmDVWrVsXb25uOHTvStm3bfNddr169HOsWLVrEypUrSUxM5P79+6Snp1O7dm2tMtWrV1cbFwAODg6cPn1aq0xwcDD37t3j6NGj+Z7EW6tWLVq1aoWbmxvt2rWjbdu2fPDBB1p3IspNnz59uHv3LhEREejpPZqydfLkSU6dOsW6devUcoqikJWVxcWLF3F1dc1RT0BAACNHjlSXk5OTcXR0ZNpxPR4a6ucoL+BMYDsAWrZsyV9//UWHDh3Ubfv27aNKlSpa6wA2b95MnTp1GDJkSKHGkpGRQUREBG3atMlzuJ54vUjOihfJV/Giy/nKHoHwLNLAEDrL1NQ0z2116tTh4sWL7Nq1i71799K9e3dat26t9jY8y5NzETZs2MDo0aMJDg7Gw8ODEiVKMHfuXA4fPqxV7slfVBqNhqysLK11zZo1Y+fOnWzcuJEvvvgiX/Ho6+sTERHBoUOH2LNnD1999RXjx4/n8OHDeX7TPW3aNH766SeOHDlCiRIl1PUpKSl8+umnDBs2LMc+Tk5OudZlbGyMsbFxjvU/j22NjY1Nvs5BV40aNYrGjRszd+5cunfvzpEjR1ixYgXLli3Ter8kJyezefNmgoODX9ofPENDQ537Y1rcSc6KF8lX8aKL+crv+cpdpITOqly5MqampkRGRua63dLSkh49erB8+XK+//57Nm/ezH///fdcx4qJiaFx48YMHjwYd3d3KlWqREJCwnPV1aBBA3bt2sWMGTOYN29evvfTaDQ0adKEoKAgjh8/jpGREVu3bs217ObNm5kyZQobN26kYsWKWtvq1KnD2bNnqVSpUo6XkZHRc52TyFv9+vXZunUr3333HTVq1GDq1KmEhITg4+OjVW7Dhg0oikLPnj2LKFIhhBDiEenBEDrLxMSEsWPH8vnnn2NkZESTJk24ceMGv//+O0lJSTg4OODu7o6enh4//PAD9vb2z30r18qVK7N69Wp++uknKlSowJo1a4iNjX3ucfKNGzcmPDyc9u3bY2BgwIgRI55a/vDhw0RGRtK2bVvs7Ow4fPgwN27cyHU405kzZ+jduzdjx46levXqXLt2DQAjIyOsra0ZO3YsjRo1wt/fn379+mFubs7Zs2eJiIjg66+/fq7zEU/XsWNHOnbs+NQyAwYMYMCAAa8oIiGEECJv0sAQOm3ixIkYGBgwadIk/vnnHxwcHBg4cCClS5dmzpw5XLhwAX19ferXr094eLg6D6GgPv30U44fP06PHj3QaDT07NmTwYMHs2vXrueOvWnTpuzcuZMOHTqgr6/P0KFD8yxraWnJzz//TEhICMnJyTg7OxMcHEz79u1zlD169CipqalMmzaNadOmqeuzb1Nbs2ZNoqOjGT9+PM2aNUNRFCpWrEiPHj2e+1yEEEII8eaQu0gJIV655ORkrKysuHnzpszBKAYyMjIIDw+nQ4cOOjfeuLiSnBUvkq/iRZfzlf33+1l3kZI5GEIIIYQQQohCIw0MIQpg3bp1WFhY5PrKz/MoXpbExMQ847KwsCAxMbHIYhNCCCGEbpE5GEIUwHvvvUfDhg1z3VaU3aRly5blxIkTT90uhBBCCPEqSANDiAIoUaKE1jMhXhcGBgZUqlSpqMMQQgghhJAhUkIIIYQQQojCIw0MIR6TmprK+++/j6WlJRqNhjt37uDi4kJISEhRhyZ01N9//81HH32EjY0NpqamuLm5cfToUa0y586d47333sPKygpzc3Pq168v826EEEIUGRkiJcRjvv32Ww4cOMChQ4coXbo0VlZWxMbGYm5urpbRaDRs3bqVzp07F12gQifcvn2bJk2a0KJFC3bt2oWtrS0XLlygVKlSapmEhASaNm1K3759CQoKwtLSkt9//x0TE5MijFwIIYQukwaG0Anp6ekYGRk9s1xCQgKurq7UqFFDXWdra/syQ8tVfuN9lV7HmN50s2fPxtHRkdDQUHXdk09/Hz9+PB06dGDOnDnquooVK76yGIUQQognyRApUSx5eXnh7++Pv78/VlZWlC5dmokTJ5L93EgXFxemTp1K7969sbS0ZMCAAQBs3ryZ6tWrY2xsjIuLC8HBwVp1BgcH8/PPP6PRaPDy8lLryh4i5eLiAkCXLl3QaDTq8tMEBgZSu3Ztli5diqOjI2ZmZnTv3p2kpCS1jJ+fH507d2b69OmULVuWqlWrAnDlyhW6d+9OyZIlsba2plOnTly6dEndLyoqigYNGmBubk7JkiVp0qQJly9fBuDkyZO0aNGCEiVKYGlpSd26ddWhNdkxPS4kJETrfJ43JlF4tm/fTr169ejWrRt2dna4u7uzfPlydXtWVhY7d+6kSpUqtGvXDjs7Oxo2bEhYWFjRBS2EEELnSQ+GKLa+/fZb+vbty5EjRzh69CgDBgzAycmJ/v37AzBv3jwmTZrE5MmTATh27Bjdu3cnMDCQHj16cOjQIQYPHoyNjQ1+fn5s2bKFL774gjNnzrBly5Zcv62PjY3Fzs6O0NBQvL290dfXz1es8fHxbNy4kR9//JHk5GT69u3L4MGDWbdunVomMjISS0tLIiIigEdPCm3Xrh0eHh4cOHAAAwMDpk2bhre3N6dOnUJPT4/OnTvTv39/vvvuO9LT0zly5AgajQYAHx8f3N3dWbx4Mfr6+pw4caLAt9ItaEwF7eFoODOShwbmzy6oYy7NegeAP//8k8WLFzNy5EjGjRtHbGwsw4YNw8jICF9fX65fv05KSgqzZs1i2rRpzJ49m927d9O1a1f279+Pp6dnEZ+JEEIIXSQNDFFsOTo6Mn/+fDQaDVWrVuX06dPMnz9fbWC0bNmSUaNGqeV9fHxo1aoVEydOBKBKlSqcPXuWuXPn4ufnh7W1NWZmZhgZGWFvb5/rMbOHS5UsWTLPMrl58OABq1evply5cgB89dVXvPPOOwQHB6v1mJubs2LFCvVD+tq1a8nKymLFihVqoyE0NJSSJUsSFRVFvXr1SEpKomPHjuqQGFdXV/WYiYmJjBkzhmrVqgFQuXLlfMebraAxtW3bNtd60tLSSEtLU5eTk5MBMNZT0NdXChzXmy4jIwN41ENRt25dgoKCAKhRowanTp1i8eLF9OrVS72m7777Lv7+/gBUr16dgwcP8s0339C4ceNCjSf7X/H6k5wVL5Kv4kWX85Xfc5YGhii2GjVqpH7IBfDw8CA4OJjMzEwA6tWrp1X+3LlzdOrUSWtdkyZNCAkJITMzM9+9Ec/DyclJbVxkx5qVlUVcXJzawHBzc9PqATh58iTx8fE5nrvx4MEDEhISaNu2LX5+frRr1442bdrQunVrunfvjoODAwAjR46kX79+rFmzhtatW9OtW7cCj80vaEx5mTlzpvoh+XET3LMwM8ssUEy6IDw8HHjUkLWwsFCXAR4+fMiFCxcIDw8nIyMDfX199PX1tcoYGRlx6tQprXWFIbsnSxQfkrPiRfJVvOhivlJTU/NVThoY4o31+J2fioMn401JSaFu3bpaw6iyZfekhIaGMmzYMHbv3s3333/PhAkTiIiIoFGjRgQGBtKrVy927tzJrl27mDx5Mhs2bKBLly7o6emp81Wy5fatxPPElJuAgABGjhypLicnJ+Po6EiLFi2wsbHJcz9d17JlS/766y86dOigrtu3bx9VqlRR19WvXx9Aq8zKlSupVauW1roXkZGRQUREBG3atCnSJ9aL/JOcFS+Sr+JFl/OVPQLhWaSBIYqtw4cPay3/+uuvVK5cOc+eCFdXV2JiYrTWxcTEUKVKlQL1XhgaGqq9JPmVmJjIP//8Q9myZdVY9fT01InTualTpw7ff/89dnZ2WFpa5lnO3d0dd3d3AgIC8PDwYP369TRq1Ah4NAysSpUqfPbZZ/Ts2ZPQ0FC6dOmCra0t165dQ1EUtRfoxIkTzzyP/Mb0JGNjY4yNjXOsNzQ01LlfzgUxatQoGjduzNy5c+nevTtHjhxhxYoVLFu2TL1un3/+OT169MDLy4sWLVqwe/dudu7cSVRUVKFfW8lX8SM5K14kX8WLLuYrv+crd5ESxVZiYiIjR44kLi6O7777jq+++orhw4fnWX7UqFFERkYydepUzp8/z7fffsvXX3/N6NGjC3RcFxcXIiMjuXbtGrdv387XPiYmJvj6+nLy5EkOHDjAsGHD6N69+1Pncfj4+FC6dGk6derEgQMHuHjxIlFRUQwbNoy//vqLixcvEhAQwC+//MLly5fZs2cPFy5cwNXVlfv37+Pv709UVBSXL18mJiaG2NhYdY6Gl5cXN27cYM6cOSQkJLBo0SJ27dr1zPN4VkyicNWvX5+tW7fy3XffUaNGDaZOnUpISAg+Pj5qmS5durBkyRLmzJmDm5sbK1asYPPmzTRt2rQIIxdCCKHLpAdDFFu9e/fm/v37NGjQAH19fYYPH67ejjY3derUYePGjUyaNImpU6fi4ODAlClT8PPzK9Bxg4ODGTlyJMuXL6dcuXL5ukVrpUqV6Nq1Kx06dOC///6jY8eOfPPNN0/dx8zMjJ9//pmxY8fStWtX7t69S7ly5WjVqhWWlpbcv3+fP/74g2+//ZZbt27h4ODAkCFD+PTTT3n48CG3bt2id+/e/Pvvv5QuXZquXbuq8yBcXV355ptvmDFjBlOnTuX9999n9OjRLFu27IViEoWvY8eOdOzY8all+vTpQ58+fV5RREIIIcTTaZQnB2ILUQx4eXlRu3Zt9fkUr7PAwEDCwsLyNQRJVyQnJ2NlZcXNmzdlDkYxkJGRQXh4OB06dNC54QDFleSseJF8FS+6nK/sv99JSUlP/WJRhkgJIYQQQgghCo0MkRLiBVWvXl19evaTli5d+oqjEUIIIYQoWtLAEMVSVFRUUYegyn4eQW7KlClDiRIlCAwMfLVBCSGEEEIUEWlgCPGCnJ2dizoEIYQQQojXhszBEEIIIYQQQhQaaWAIIcRr7O+//+ajjz7CxsYGU1NT3NzcOHr0qFaZc+fO8d5772FlZYW5uTn169cnMTGxiCIWQgih66SBId5oLi4uRXorWz8/Pzp37lxkxxfF2+3bt2nSpAmGhobs2rWLs2fPEhwcTKlSpdQyCQkJNG3alGrVqhEVFcWpU6eYOHEiJiYmRRi5EEIIXVakDQwvLy9GjBjxwvVcunQJjUYjzxl4AVFRUWg0Gu7cuVPUoRRLeb0HFyxYwKpVq15ZHNl5fPJ17dq1fO0/c+ZM6tevT4kSJbCzs6Nz587ExcVplXnw4AFDhgzBxsYGCwsL3n//ff7999+XcTo6b/bs2Tg6OhIaGkqDBg2oUKECbdu2pWLFimqZ8ePH06FDB+bMmYO7uzsVK1bkvffew87OrggjF0IIocukB+MJ6enpRR1CnjIzM8nKyirQPq/z+RQHL3r9rKysKFmyZOEEUwBxcXFcvXpVfeX3w2Z0dDRDhgzh119/JSIigoyMDNq2bcu9e/fUMp999hk//vgjP/zwA9HR0fzzzz907dr1ZZ2KTtu+fTv16tWjW7du2NnZ4e7uzvLly9XtWVlZ7Ny5kypVqtCuXTvs7Oxo2LAhYWFhRRe0EEIInVdkd5Hy8/MjOjqa6OhoFixYAMBvv/3GvHnz2LNnDykpKZQvX55x48bxySefPLWuChUqAODu7g6Ap6cnUVFRuT7tuXPnzpQsWVL9VtnFxYW+ffty4cIFwsLC6Nq1q9qz8v333zNixAiuXLlC06ZNCQ0NxcHBAXj0h33atGksW7aMGzdu4OrqyqxZs/D29gagcePGNGvWjNmzZ6vHvnHjBmXLliUyMpLmzZuTlpbG+PHj+e6777hz5w41atRg9uzZeHl5AbBq1SpGjBjB6tWr+eKLLzh//jzx8fG4uLg89breuXOH+vXrs2jRIoyNjbl48SJr1qxhwYIFxMXFYW5uTsuWLQkJCcHOzo5Lly7RokULAHXoha+vL6tWrSIrK4vZs2ezbNkyrl27RpUqVZg4cSIffPDBM3OcmZnJgAED2LdvH9euXcPJyYnBgwczfPhwrXIrV64kODiY+Ph4rK2tef/99/n6668BuHPnDmPHjiUsLIykpCQqVarErFmz6NixIwAHDx4kICCAo0ePUrp0abp06cLMmTMxNzfPNaY7d+4wevRotm3bRlpaGvXq1WP+/PnUqlUL+L+nbvv7+zN9+nQuX75MVlYWu3fvZtq0aZw5cwZ9fX08PDxYsGCB+k1yXu/B7Hxkf+BLS0tjzJgxbNiwgeTkZPX49evXBx71QLRo0YK9e/cyduxYzp49S+3atQkNDaVq1arPvObZ7Ozsnqths3v3bq3lVatWYWdnx7Fjx2jevDlJSUn873//Y/369bRs2RKA0NBQXF1d+fXXX2nUqFGBjtdwZiQPDXLPlS67NOsdAP78808WL17MyJEjGTduHLGxsQwbNgwjIyN8fX25fv06KSkpzJo1i2nTpjF79mx2795N165d2b9/P56enkV8JkIIIXRRkTUwFixYwPnz56lRowZTpkwBICgoiLNnz7Jr1y5Kly5NfHw89+/ff2ZdR44coUGDBuzdu5fq1atjZGRUoFjmzZvHpEmTmDx5MgAHDhwgNTWVefPmsWbNGvT09Pjoo48YPXo069atU+MPDg5m6dKluLu7s3LlSt577z1+//13KleujI+PD3PmzGHWrFloNBoAvv/+e8qWLUuzZs0A8Pf35+zZs2zYsIGyZcuydetWvL29OX36NJUrVwYgNTWV2bNns2LFCmxsbPL1TXRkZCSWlpZERESo6zIyMpg6dSpVq1bl+vXrjBw5Ej8/P8LDw3F0dGTz5s28//77xMXFYWlpiampKfBoyMzatWtZsmQJlStX5ueff+ajjz7C1tb2mR9esrKyKF++PD/88AM2NjYcOnSIAQMG4ODgQPfu3QHUD0+zZs2iffv2JCUlERMTo+7fvn177t69y9q1a6lYsSJnz55FX18feDT23Nvbm2nTprFy5Upu3LiBv78//v7+hIaG5hpTt27dMDU1ZdeuXVhZWbF06VJatWrF+fPnsba2BiA+Pp7NmzezZcsW9Vj37t1j5MiR1KxZk5SUFCZNmkSXLl04ceIEenp6+X4Pfv7552zevJlvv/0WZ2dn5syZQ7t27dTGVbbx48cTHByMra0tAwcOpE+fPup1yY/atWuTlpZGjRo1CAwMpEmTJvne93FJSUkAamzHjh0jIyOD1q1bq2WqVauGk5MTv/zyS54NjLS0NNLS0tTl5ORkAIz1FPT1leeK7U2W/VyVrKws6tatS1BQEAA1atTg1KlTLF68mF69eqnX9N1338Xf3x949ODHgwcP8s0339C4ceNCjSev572I14/krHiRfBUvupyv/J5zkTUwrKysMDIywszMDHt7e+DR3VLc3d2pV68ewFO/qX+cra0tADY2NmpdBdGyZUtGjRqlLh84cICMjAyWLFmifkPt7++vNoTgUaNk7NixfPjhh8CjsdL79+8nJCSERYsW0b17d0aMGMHBgwfVBsX69evp2bMnGo2GxMREQkNDSUxMpGzZsgCMHj2a3bt3ExoayowZM4BHifzmm2/Ub9jzw9zcnBUrVmh9yO3Tp4/681tvvcXChQupX78+KSkpWFhYqB8gH//mOy0tjRkzZrB37148PDzUfQ8ePMjSpUuf2cAwNDRUPxjBo2/5f/nlFzZu3Kg2MKZNm8aoUaO0ejWyv83fu3cvR44c4dy5c1SpUkU9fraZM2fi4+OjzuOpXLkyCxcuxNPTk8WLF+eY5Hrw4EGOHDnC9evXMTY2Bh7lMSwsjE2bNjFgwADg0bCo1atXq+8rgPfff1+rrpUrV2Jra8vZs2epUaNGvt6D9+7dY/HixaxatYr27dsDsHz5ciIiIvjf//7HmDFj1LLTp09Xr+8XX3zBO++8w4MHD545cdfBwYElS5ZQr1490tLSWLFiBV5eXhw+fJg6deo8dd8nZWVlMWLECJo0aUKNGjUAuHbtGkZGRjl6R8qUKfPUeR4zZ87Uei9km+CehZlZZoHi0gXh4eEAlCxZEgsLC3UZ4OHDh1y4cEF9wKO+vj76+vpaZYyMjDh16pTWusLw+JcWoniQnBUvkq/iRRfzlZqamq9yr9WD9gYNGsT777/Pb7/9Rtu2bencuXOhfQP3NNkNmseZmZlpTaR0cHDg+vXrwKNvX//5558c3wo3adKEkydPAo8aPW3btmXdunU0a9aMixcv8ssvv7B06VIATp8+TWZmpvrBOVtaWho2NjbqspGRETVr1izQ+bi5ueX4Bv3YsWMEBgZy8uRJbt++rc7lSExM5O233861nvj4eFJTU2nTpo3W+vT0dHUo0LMsWrSIlStXkpiYyP3790lPT6d27doAXL9+nX/++YdWrVrluu+JEycoX758jmuU7eTJk5w6dUrtVQJQFIWsrCwuXryIq6trjvIpKSla1xfg/v37JCQkqMvOzs5ajQuACxcuMGnSJA4fPszNmze1rl/2h+9nSUhIICMjQ+t9Y2hoSIMGDTh37pxW2cdznj0s7/r16zg5OT31GFWrVtUaStW4cWMSEhKYP38+a9asyVec2YYMGcKZM2c4ePBggfbLTUBAACNHjlSXk5OTcXR0ZNpxPR4a6r9w/W+aM4HtgEdffvz111906NBB3bZv3z6qVKmirstukD9eZuXKldSqVUtr3YvIyMggIiKCNm3aYGhoWCh1ipdLcla8SL6KF13OV/YIhGd5rRoY7du35/Lly4SHhxMREUGrVq0YMmQI8+bNe6769PT0UBTt4Re5de3kNl7/yTeMRqPJUdez+Pj4MGzYML766ivWr1+Pm5sbbm5uAKSkpKCvr8+xY8fUYTjZLCws1J9NTU3VIVb59eT53Lt3j3bt2tGuXTvWrVuHra0tiYmJtGvX7qmTmFNSUgDYuXMn5cqV09qW3QPwNBs2bGD06NEEBwfj4eFBiRIlmDt3LocPH1bP7WmetT0lJYVPP/2UYcOG5diW2wfxlJQUHBwciIqKyrHt8W/kc3s/vPvuuzg7O7N8+XLKli1LVlYWNWrUeGmT6B9//2Xnv6AT/LM1aNCgwI0Ef39/duzYwc8//0z58uXV9fb29qSnp3Pnzh2ta/bvv/8+tffQ2Ng41/fMz2Nb52jwif8zatQoGjduzNy5c+nevTtHjhxhxYoVLFu2TH2PfP755/To0QMvLy9atGjB7t272blzJ1FRUYX+h8/Q0FDn/pgWd5Kz4kXyVbzoYr7ye75F2sAwMjIiM1N7eIStrS2+vr74+vrSrFkzxowZ88wGRva39bnVdfXqVXU5MzOTM2fOqBOan5elpSVly5YlJiZGa5hQTEwMDRo0UJc7derEgAED2L17N+vXr6d3797qNnd3dzIzM7l+/bo6hOpl+eOPP7h16xazZs3C0dERIMeDunK7hm+//TbGxsYkJiY+12TRmJgYGjduzODBg9V1j/cUlChRAhcXFyIjI3PNSc2aNfnrr784f/58rr0YderU4ezZs1SqVClf8dSpU4dr165hYGCQ7+F3ALdu3SIuLo7ly5eruXryA3te78HHVaxYESMjI2JiYnB2dgYeNXhjY2ML5XbNeTlx4oTaC/IsiqIwdOhQtm7dSlRUlDp5PVvdunUxNDQkMjJSHTYWFxdHYmKiOoxOFJ769euzdetWAgICmDJlChUqVCAkJAQfHx+1TJcuXViyZAkzZ85k2LBhVK1alc2bN9O0adMijFwIIYQuK9IGhouLC4cPH+bSpUtYWFiwcOFC6tatS/Xq1UlLS2PHjh05hrnkxs7ODlNTU3bv3k358uUxMTHBysqKli1bMnLkSHbu3EnFihX58ssvC+05D2PGjGHy5MlUrFhRvcvPiRMntIbrmJub07lzZyZOnMi5c+fo2bOnuq1KlSr4+PjQu3dvgoODcXd358aNG0RGRlKzZk3eeeedQokTHn2bb2RkxFdffcXAgQM5c+YMU6dO1Srj7OyMRqNhx44ddOjQAVNTU0qUKMHo0aP57LPPyMrKomnTpuokbEtLS3x9fZ963MqVK7N69Wp++uknKlSowJo1a4iNjdX60BoYGMjAgQOxs7NTJ3THxMQwdOhQPD09ad68Oe+//z5ffvkllSpV4o8//kCj0eDt7c3YsWNp1KgR/v7+9OvXD3Nzc86ePUtERIR6F6rHtW7dGg8PDzp37sycOXOoUqUK//zzDzt37qRLly65DpWDR3fWsrGxYdmyZTg4OJCYmMgXX3yhVSav9+DjzM3NGTRoEGPGjMHa2honJyfmzJlDamoqffv2feq1zK+QkBAqVKhA9erVefDgAStWrGDfvn3s2bMnX/sPGTKE9evXs23bNkqUKKHOq7CyssLU1BQrKyv69u3LyJEjsba2xtLSkqFDh+Lh4VHgO0iJ/OnYsaN617S89OnTR2uelRBCCFGklCIUFxenNGrUSDE1NVUAZerUqYqrq6tiamqqWFtbK506dVL+/PPPfNW1fPlyxdHRUdHT01M8PT0VRVGU9PR0ZdCgQYq1tbViZ2enzJw5U+nUqZPi6+ur7ufs7KzMnz9fq67Q0FDFyspKa93WrVuVxy9XZmamEhgYqJQrV04xNDRUatWqpezatStHXOHh4QqgNG/ePMe29PR0ZdKkSYqLi4tiaGioODg4KF26dFFOnTqVZxzP4uvrq3Tq1CnH+vXr1ysuLi6KsbGx4uHhoWzfvl0BlOPHj6tlpkyZotjb2ysajUa9RllZWUpISIhStWpVxdDQULG1tVXatWunREdHPzOWBw8eKH5+foqVlZVSsmRJZdCgQcoXX3yh1KpVS6vckiVL1PodHByUoUOHqttu3bqlfPLJJ4qNjY1iYmKi1KhRQ9mxY4e6/ciRI0qbNm0UCwsLxdzcXKlZs6Yyffp0dfuT+U1OTlaGDh2qlC1bVjE0NFQcHR0VHx8fJTExUVEURZk8eXKO+BRFUSIiIhRXV1fF2NhYqVmzphIVFaUAytatW9Uyub0Hn8zH/fv3laFDhyqlS5dWjI2NlSZNmihHjhxRt+/fv18BlNu3b6vrjh8/rgDKxYsXn3nNZ8+erVSsWFExMTFRrK2tFS8vL2Xfvn3P3C8bkOsrNDRU6xwGDx6slCpVSjEzM1O6dOmiXL16Nd/HUBRFSUpKUgDl5s2bBdpPFI309HQlLCxMSU9PL+pQRD5JzooXyVfxosv5yv77nZSU9NRyGkUp4MQCIYR4QcnJyVhZWXHz5k2Zg1EMZGRkEB4eTocOHXRuvHFxJTkrXiRfxYsu5yv773dSUhKWlpZ5lpMneQshhBBCCCEKTbFoYMyYMQMLC4tcX9nPE9AVeV0HCwsLDhw48EpjGThwYJ6xDBw48JXGoiuqV6+e5zV/fP5PbhITE5/6/klMTHxFZyGEEEKIN9lrdZvavAwcOFB9MNuTnnUr0zfNiRMn8tz25K1kX7YpU6YwevToXLc9rdtMPL/sh6vlpkyZMk/dt2zZsk99/2Q/8FEIIYQQ4kUUiwaGtbW1+qRpXZffW7K+CnZ2dtjZ2RV1GDol+/a2z8PAwOC1ev8IIYQQ4s1ULIZICSGEEEIIIYoHaWAIIYQQQgghCo00MIQQQgghhBCFRhoYQgghhBBCiEIjDQwhhBBCCCFEoZEGhhBCCCGEEKLQFIvb1Aoh3iyKogBw9+5dDA0Nizga8SwZGRmkpqaSnJws+SomJGfFi+SreNHlfCUnJwP/93c8L9LAEEK8crdu3QKgQoUKRRyJEEIIIQrq7t27WFlZ5bldGhhCiFcu+8GZiYmJT/0FJV4PycnJODo6cuXKFSwtLYs6HJEPkrPiRfJVvOhyvhRF4e7du5QtW/ap5aSBIYR45fT0Hk3/srKy0rlfzsWZpaWl5KuYkZwVL5Kv4kVX85WfLwZlkrcQQgghhBCi0EgDQwghhBBCCFFopIEhhHjljI2NmTx5MsbGxkUdisgHyVfxIzkrXiRfxYvk69k0yrPuMyWEEEIIIYQQ+SQ9GEIIIYQQQohCIw0MIYQQQgghRKGRBoYQQgghhBCi0EgDQwghhBBCCFFopIEhhHilFi1ahIuLCyYmJjRs2JAjR44UdUg66+eff+bdd9+lbNmyaDQawsLCtLYrisKkSZNwcHDA1NSU1q1bc+HCBa0y//33Hz4+PlhaWlKyZEn69u1LSkrKKzwL3TBz5kzq169PiRIlsLOzo3PnzsTFxWmVefDgAUOGDMHGxgYLCwvef/99/v33X60yiYmJvPPOO5iZmWFnZ8eYMWN4+PDhqzwVnbF48WJq1qypPozNw8ODXbt2qdslX6+3WbNmodFoGDFihLpOcpZ/0sAQQrwy33//PSNHjmTy5Mn89ttv1KpVi3bt2nH9+vWiDk0n3bt3j1q1arFo0aJct8+ZM4eFCxeyZMkSDh8+jLm5Oe3atePBgwdqGR8fH37//XciIiLYsWMHP//8MwMGDHhVp6AzoqOjGTJkCL/++isRERFkZGTQtm1b7t27p5b57LPP+PHHH/nhhx+Ijo7mn3/+oWvXrur2zMxM3nnnHdLT0zl06BDffvstq1atYtKkSUVxSm+88uXLM2vWLI4dO8bRo0dp2bIlnTp14vfffwckX6+z2NhYli5dSs2aNbXWS84KQBFCiFekQYMGypAhQ9TlzMxMpWzZssrMmTOLMCqhKIoCKFu3blWXs7KyFHt7e2Xu3Lnqujt37ijGxsbKd999pyiKopw9e1YBlNjYWLXMrl27FI1Go/z999+vLHZddP36dQVQoqOjFUV5lBtDQ0Plhx9+UMucO3dOAZRffvlFURRFCQ8PV/T09JRr166pZRYvXqxYWloqaWlpr/YEdFSpUqWUFStWSL5eY3fv3lUqV66sREREKJ6ensrw4cMVRZH/YwUlPRhCiFciPT2dY8eO0bp1a3Wdnp4erVu35pdffinCyERuLl68yLVr17TyZWVlRcOGDdV8/fLLL5QsWZJ69eqpZVq3bo2enh6HDx9+5THrkqSkJACsra0BOHbsGBkZGVr5qlatGk5OTlr5cnNzo0yZMmqZdu3akZycrH6rLl6OzMxMNmzYwL179/Dw8JB8vcaGDBnCO++8o5UbkP9jBWVQ1AEIIXTDzZs3yczM1PrFC1CmTBn++OOPIopK5OXatWsAueYre9u1a9ews7PT2m5gYIC1tbVaRhS+rKwsRowYQZMmTahRowbwKBdGRkaULFlSq+yT+cotn9nbROE7ffo0Hh4ePHjwAAsLC7Zu3crbb7/NiRMnJF+voQ0bNvDbb78RGxubY5v8HysYaWAIIYQQxciQIUM4c+YMBw8eLOpQxDNUrVqVEydOkJSUxKZNm/D19SU6OrqowxK5uHLlCsOHDyciIgITE5OiDqfYkyFSQohXonTp0ujr6+e448a///6Lvb19EUUl8pKdk6fly97ePscE/YcPH/Lff/9JTl8Sf39/duzYwf79+ylfvry63t7envT0dO7cuaNV/sl85ZbP7G2i8BkZGVGpUiXq1q3LzJkzqVWrFgsWLJB8vYaOHTvG9evXqVOnDgYGBhgYGBAdHc3ChQsxMDCgTJkykrMCkAaGEOKVMDIyom7dukRGRqrrsrKyiIyMxMPDowgjE7mpUKEC9vb2WvlKTk7m8OHDar48PDy4c+cOx44dU8vs27ePrKwsGjZs+MpjfpMpioK/vz9bt25l3759VKhQQWt73bp1MTQ01MpXXFwciYmJWvk6ffq0VqMwIiICS0tL3n777VdzIjouKyuLtLQ0yddrqFWrVpw+fZoTJ06or3r16uHj46P+LDkrgKKeZS6E0B0bNmxQjI2NlVWrVilnz55VBgwYoJQsWVLrjhvi1bl7965y/Phx5fjx4wqgfPnll8rx48eVy5cvK4qiKLNmzVJKliypbNu2TTl16pTSqVMnpUKFCsr9+/fVOry9vRV3d3fl8OHDysGDB5XKlSsrPXv2LKpTemMNGjRIsbKyUqKiopSrV6+qr9TUVLXMwIEDFScnJ2Xfvn3K0aNHFQ8PD8XDw0Pd/vDhQ6VGjRpK27ZtlRMnTii7d+9WbG1tlYCAgKI4pTfeF198oURHRysXL15UTp06pXzxxReKRqNR9uzZoyiK5Ks4ePwuUooiOSsIaWAIIV6pr776SnFyclKMjIyUBg0aKL/++mtRh6Sz9u/frwA5Xr6+voqiPLpV7cSJE5UyZcooxsbGSqtWrZS4uDitOm7duqX07NlTsbCwUCwtLZVPPvlEuXv3bhGczZsttzwBSmhoqFrm/v37yuDBg5VSpUopZmZmSpcuXZSrV69q1XPp0iWlffv2iqmpqVK6dGll1KhRSkZGxis+G93Qp08fxdnZWTEyMlJsbW2VVq1aqY0LRZF8FQdPNjAkZ/mnURRFKZq+EyGEEEIIIcSbRuZgCCGEEEIIIQqNNDCEEEIIIYQQhUYaGEIIIYQQQohCIw0MIYQQQgghRKGRBoYQQgghhBCi0EgDQwghhBBCCFFopIEhhBBCCCGEKDTSwBBCCCFEvnh5eTFixIiiDkMI8ZqTBoYQQghRCPz8/NBoNDle8fHxhVL/qlWrKFmyZKHU9by2bNnC1KlTizSGp4mKikKj0XDnzp2iDkUInWZQ1AEIIYQQbwpvb29CQ0O11tna2hZRNHnLyMjA0NCwwPtZW1u/hGgKR0ZGRlGHIIT4/6QHQwghhCgkxsbG2Nvba7309fUB2LZtG3Xq1MHExIS33nqLoKAgHj58qO775Zdf4ubmhrm5OY6OjgwePJiUlBTg0Tfzn3zyCUlJSWrPSGBgIAAajYawsDCtOEqWLMmqVasAuHTpEhqNhu+//x5PT09MTExYt24dACtWrMDV1RUTExOqVavGN99889Tze3KIlIuLC9OmTaN3795YWFjg7OzM9u3buXHjBp06dcLCwoKaNWty9OhRdZ/snpiwsDAqV66MiYkJ7dq148qVK1rHWrx4MRUrVsTIyIiqVauyZs0are0ajYbFixfz3nvvYW5uTv/+/WnRogUApUqVQqPR4OfnB8Du3btp2rQpJUuWxMbGho4dO5KQkKDWlX2NtmzZQosWLTAzM6NWrVr88ssvWseMiYnBy8sLMzMzSpUqRbt27bh9+zYAWVlZzJw5kwoVKmBqakqtWrXYtGnTU6+nEG8sRQghhBAvzNfXV+nUqVOu237++WfF0tJSWbVqlZKQkKDs2bNHcXFxUQIDA9Uy8+fPV/bt26dcvHhRiYyMVKpWraoMGjRIURRFSUtLU0JCQhRLS0vl6tWrytWrV5W7d+8qiqIogLJ161at41lZWSmhoaGKoijKxYsXFUBxcXFRNm/erPz555/KP//8o6xdu1ZxcHBQ123evFmxtrZWVq1alec5enp6KsOHD1eXnZ2dFWtra2XJkiXK+fPnlUGDBimWlpaKt7e3snHjRiUuLk7p3Lmz4urqqmRlZSmKoiihoaGKoaGhUq9ePeXQoUPK0aNHlQYNGiiNGzdW692yZYtiaGioLFq0SImLi1OCg4MVfX19Zd++fWoZQLGzs1NWrlypJCQkKJcuXVI2b96sAEpcXJxy9epV5c6dO4qiKMqmTZuUzZs3KxcuXFCOHz+uvPvuu4qbm5uSmZmpdY2qVaum7NixQ4mLi1M++OADxdnZWcnIyFAURVGOHz+uGBsbK4MGDVJOnDihnDlzRvnqq6+UGzduKIqiKNOmTVOqVaum7N69W0lISFBCQ0MVY2NjJSoqKs/rKcSbShoYQgghRCHw9fVV9PX1FXNzc/X1wQcfKIqiKK1atVJmzJihVX7NmjWKg4NDnvX98MMPio2NjbocGhqqWFlZ5SiX3wZGSEiIVpmKFSsq69ev11o3depUxcPDI8+YcmtgfPTRR+ry1atXFUCZOHGiuu6XX35RAOXq1avqeQDKr7/+qpY5d+6cAiiHDx9WFEVRGjdurPTv31/r2N26dVM6dOigdd4jRozQKrN//34FUG7fvp3nOSiKoty4cUMBlNOnTyuK8n/XaMWKFWqZ33//XQGUc+fOKYqiKD179lSaNGmSa30PHjxQzMzMlEOHDmmt79u3r9KzZ8+nxiLEm0jmYAghhBCFpEWLFixevFhdNjc3B+DkyZPExMQwffp0dVtmZiYPHjwgNTUVMzMz9u7dy8yZM/njjz9ITk7m4cOHWttfVL169dSf7927R0JCAn379qV///7q+ocPH2JlZVWgemvWrKn+XKZMGQDc3NxyrLt+/Tr29vYAGBgYUL9+fbVMtWrVKFmyJOfOnaNBgwacO3eOAQMGaB2nSZMmLFiwIM9zepoLFy4wadIkDh8+zM2bN8nKygIgMTGRGjVq5HouDg4OatzVqlXjxIkTdOvWLdf64+PjSU1NpU2bNlrr09PTcXd3z1eMQrxJpIEhhBBCFBJzc3MqVaqUY31KSgpBQUF07do1xzYTExMuXbpEx44dGTRoENOnT8fa2pqDBw/St29f0tPTn9rA0Gg0KIqitS63Cc/ZjZ3seACWL19Ow4YNtcplzxnJr8cni2s0mjzXZX+oL0yPn9PTvPvuuzg7O7N8+XLKli1LVlYWNWrUID09Xavc0+I2NTXNs/7s67lz507KlSuntc3Y2DhfMQrxJpEGhhBCCPGS1alTh7i4uFwbHwDHjh0jKyuL4OBg9PQe3X9l48aNWmWMjIzIzMzMsa+trS1Xr15Vly9cuEBqaupT4ylTpgxly5blzz//xMfHp6Cn88IePnzI0aNHadCgAQBxcXHcuXMHV1dXAFxdXYmJicHX11fdJyYmhrfffvup9RoZGQFoXadbt24RFxfH8uXLadasGQAHDx4scMw1a9YkMjKSoKCgHNvefvttjI2NSUxMxNPTs8B1C/GmkQaGEEII8ZJNmjSJjh074uTkxAcffICenh4nT57kzJkzTJs2jUqVKpGRkcFXX33Fu+++S0xMDEuWLNGqw8XFhZSUFCIjI6lVqxZmZmaYmZnRsmVLvv76azw8PMjMzGTs2LH5ugVtUFAQw4YNw8rKCm9vb9LS0jh69Ci3b99m5MiRL+tSAI96CoYOHcrChQsxMDDA39+fRo0aqQ2OMWPG0L17d9zd3WndujU//vgjW7ZsYe/evU+t19nZGY1Gw44dO+jQoQOmpqaUKlUKGxsbli1bhoODA4mJiXzxxRcFjjkgIAA3NzcGDx7MwIEDMTIyYv/+/XTr1o3SpUszevRoPvvsM7KysmjatClJSUnExMRgaWmp1VASQhfIbWqFEEKIl6xdu3bs2LGDPXv2UL9+fRo1asT8+fNxdnYGoFatWnz55ZfMnj2bGjVqsG7dOmbOnKlVR+PGjRk4cCA9evTA1taWOXPmABAcHIyjoyPNmjWjV69ejB49Ol9zNvr168eKFSsIDQ3Fzc0NT09PVq1aRYUKFQr/AjzBzMyMsWPH0qtXL5o0aYKFhQXff/+9ur1z584sWLCAefPmUb16dZYuXUpoaCheXl5PrbdcuXIEBQXxxRdfUKZMGfz9/dHT02PDhg0cO3aMGjVq8NlnnzF37twCx1ylShX27NnDyZMnadCgAR4eHmzbtg0Dg0ff1U6dOpWJEycyc+ZMXF1d8Y26f/EAAACuSURBVPb2ZufOna/kegrxutEoTw7cFEIIIYR4SVatWsWIESPkadtCvMGkB0MIIYQQQghRaKSBIYQQQgghhCg0MkRKCCGEEEIIUWikB0MIIYQQQghRaKSBIYQQQgghhCg00sAQQgghhBBCFBppYAghhBBCCCEKjTQwhBBCCCGEEIVGGhhCCCGEEEKIQiMNDCGEEEIIIUShkQaGEEIIIYQQotBIA0MIIYQQQghRaP4f+196xIsqUloAAAAASUVORK5CYII=",
|
||
"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(500, '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='label')\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 39,
|
||
"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",
|
||
"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(1, '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": 40,
|
||
"id": "09b1799e",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"199\n",
|
||
"['vol', 'pct_chg', 'turnover_rate', 'volume_ratio', 'winner_rate', 'cat_senti_mom_vol_spike', 'cat_senti_pre_breakout', 'ts_turnover_rate_acceleration_5_20', 'ts_vol_sustain_10_30', 'cs_amount_outlier_10', 'ts_ff_to_total_turnover_ratio', 'ts_price_volume_trend_coherence_5_20', 'ts_ff_turnover_rate_surge_10', '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', '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', '000852.SH_MACD', '000905.SH_MACD', '399006.SZ_MACD', '000852.SH_MACD_hist', '000905.SH_MACD_hist', '399006.SZ_MACD_hist', '000852.SH_RSI', '000905.SH_RSI', '399006.SZ_RSI', '000852.SH_Signal_line', '000905.SH_Signal_line', '399006.SZ_Signal_line', '000852.SH_amount_change_rate', '000905.SH_amount_change_rate', '399006.SZ_amount_change_rate', '000852.SH_amount_mean', '000905.SH_amount_mean', '399006.SZ_amount_mean', '000852.SH_daily_return', '000905.SH_daily_return', '399006.SZ_daily_return', '000852.SH_up_ratio_20d', '000905.SH_up_ratio_20d', '399006.SZ_up_ratio_20d', '000852.SH_volatility', '000905.SH_volatility', '399006.SZ_volatility', '000852.SH_volume_change_rate', '000905.SH_volume_change_rate', '399006.SZ_volume_change_rate']\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"print(len(feature_columns))\n",
|
||
"print(feature_columns)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 41,
|
||
"id": "bceabd1f",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"警告: DataFrame 中没有 'group_id' 列。假设整个 DataFrame 是一个需要排序的组。\n",
|
||
"\n",
|
||
"NDCG 结果\n",
|
||
"{'ndcg@1': 1.0, 'ndcg@3': 1.0, 'ndcg@5': 1.0}\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"import pandas as pd\n",
|
||
"import numpy as np\n",
|
||
"\n",
|
||
"def calculate_ndcg(df: pd.DataFrame, score_col: str, label_col: str, group_id: str = 'trade_date', k_values: list = [1, 3, 5, 10]):\n",
|
||
" \"\"\"\n",
|
||
" 计算 DataFrame 中 score 列和 label 列的 NDCG 值。\n",
|
||
"\n",
|
||
" Args:\n",
|
||
" df (pd.DataFrame): 包含 score (排序学习预测分数) 和 label (相关性标签) 的 DataFrame。\n",
|
||
" 假设每个需要排序的组(例如,每天的股票)在 DataFrame 中是连续的。\n",
|
||
" score_col (str): 包含模型预测分数的列名。\n",
|
||
" label_col (str): 包含相关性标签的列名。标签值越高表示相关性越高。\n",
|
||
" k_values (list): 一个整数列表,表示计算 NDCG 的 top-k 值。\n",
|
||
" 例如,[1, 3, 5] 将计算 NDCG@1, NDCG@3 和 NDCG@5。\n",
|
||
"\n",
|
||
" Returns:\n",
|
||
" dict: 一个字典,包含每个 k 值对应的平均 NDCG 值。\n",
|
||
" 例如: {'ndcg@1': 0.85, 'ndcg@3': 0.78, 'ndcg@5': 0.72, 'ndcg@10': 0.65}\n",
|
||
" \"\"\"\n",
|
||
" ndcg_scores = {f'ndcg@{k}': [] for k in k_values}\n",
|
||
"\n",
|
||
" def dcg_at_k(r, k):\n",
|
||
" r = np.asfarray(r)[:k] if len(r) > 0 else np.zeros(k)\n",
|
||
" return np.sum(r / np.log2(np.arange(2, r.size + 2)))\n",
|
||
"\n",
|
||
" def ndcg_at_k(r, k):\n",
|
||
" dcg_max = dcg_at_k(sorted(r, reverse=True), k)\n",
|
||
" if not dcg_max:\n",
|
||
" return 0.\n",
|
||
" return dcg_at_k(r, k) / dcg_max\n",
|
||
"\n",
|
||
" # 假设 DataFrame 已经按照需要排序的组(例如,'trade_date')进行了分组,\n",
|
||
" # 并且每个组内的顺序不重要,我们只需要计算每个组的 NDCG。\n",
|
||
" # 如果需要按特定组计算 NDCG,请先对 DataFrame 进行分组。\n",
|
||
" if group_id not in df.columns:\n",
|
||
" print(\"警告: DataFrame 中没有 'group_id' 列。假设整个 DataFrame 是一个需要排序的组。\")\n",
|
||
" group_df = df.sort_values(by=score_col, ascending=False)\n",
|
||
" relevant_labels = group_df[label_col].values\n",
|
||
" for k in k_values:\n",
|
||
" ndcg_scores[f'ndcg@{k}'].append(ndcg_at_k(relevant_labels, k))\n",
|
||
" else:\n",
|
||
" for _, group_df in df.groupby(group_id):\n",
|
||
" group_df_sorted = group_df.sort_values(by=score_col, ascending=False)\n",
|
||
" relevant_labels = group_df_sorted[label_col].values\n",
|
||
" for k in k_values:\n",
|
||
" ndcg_scores[f'ndcg@{k}'].append(ndcg_at_k(relevant_labels, k))\n",
|
||
"\n",
|
||
" avg_ndcg = {k: np.mean(v) if v else np.nan for k, v in ndcg_scores.items()}\n",
|
||
" return avg_ndcg\n",
|
||
"\n",
|
||
"\n",
|
||
"ndcg_results_single_group = calculate_ndcg(score_df, score_col='score', label_col='label', k_values=[1, 3, 5], group_id=None)\n",
|
||
"print(\"\\nNDCG 结果\")\n",
|
||
"print(ndcg_results_single_group)\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 42,
|
||
"id": "44f64679",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
" ts_code trade_date open close high low vol pct_chg \\\n",
|
||
"1626778 002652.SZ 2019-01-02 19.59 19.64 19.89 19.28 20196.79 1.03 \n",
|
||
"1626779 002652.SZ 2019-01-03 19.74 19.44 19.84 19.33 15731.99 -1.02 \n",
|
||
"1626780 002652.SZ 2019-01-04 19.33 19.94 19.99 19.08 21099.93 2.57 \n",
|
||
"1626781 002652.SZ 2019-01-07 20.04 21.95 21.95 20.04 83534.19 10.08 \n",
|
||
"1626782 002652.SZ 2019-01-08 23.21 21.65 23.87 21.65 149377.97 -1.37 \n",
|
||
"... ... ... ... ... ... ... ... ... \n",
|
||
"1628321 002652.SZ 2025-05-19 15.05 15.05 15.21 14.80 142648.00 1.69 \n",
|
||
"1628322 002652.SZ 2025-05-20 15.11 15.31 15.36 14.85 131075.23 1.73 \n",
|
||
"1628323 002652.SZ 2025-05-21 15.51 15.26 15.56 15.11 147109.00 -0.33 \n",
|
||
"1628324 002652.SZ 2025-05-22 15.11 14.95 15.46 14.75 129187.20 -2.03 \n",
|
||
"1628325 002652.SZ 2025-05-23 14.95 14.70 15.11 14.70 139145.40 -1.67 \n",
|
||
"\n",
|
||
" amount turnover_rate ... cs_rank_vol_x_profit_margin \\\n",
|
||
"1626778 7867.047 0.3964 ... 0.608839 \n",
|
||
"1626779 6121.460 0.3088 ... 0.586710 \n",
|
||
"1626780 8245.083 0.4141 ... 0.682847 \n",
|
||
"1626781 35514.117 1.6394 ... 0.987591 \n",
|
||
"1626782 67160.354 2.9317 ... 0.765693 \n",
|
||
"... ... ... ... ... \n",
|
||
"1628321 42651.655 2.7857 ... 0.758644 \n",
|
||
"1628322 39438.290 2.5597 ... 0.834661 \n",
|
||
"1628323 44703.816 2.8729 ... 0.365327 \n",
|
||
"1628324 38679.608 2.5229 ... 0.810362 \n",
|
||
"1628325 41151.946 2.7173 ... 0.738293 \n",
|
||
"\n",
|
||
" cs_rank_lg_flow_price_concordance cs_rank_turnover_per_winner \\\n",
|
||
"1626778 0.203142 0.864865 \n",
|
||
"1626779 0.156684 0.763417 \n",
|
||
"1626780 0.184009 0.660949 \n",
|
||
"1626781 0.734940 0.700000 \n",
|
||
"1626782 0.874042 0.914234 \n",
|
||
"... ... ... \n",
|
||
"1628321 0.106051 0.544548 \n",
|
||
"1628322 0.202523 0.478420 \n",
|
||
"1628323 0.580870 0.520757 \n",
|
||
"1628324 0.808369 0.476918 \n",
|
||
"1628325 0.617735 0.404517 \n",
|
||
"\n",
|
||
" cs_rank_ind_cap_neutral_pe cs_rank_volume_ratio \\\n",
|
||
"1626778 NaN 0.646930 \n",
|
||
"1626779 NaN 0.251279 \n",
|
||
"1626780 NaN 0.311724 \n",
|
||
"1626781 NaN 0.988313 \n",
|
||
"1626782 NaN 0.990142 \n",
|
||
"... ... ... \n",
|
||
"1628321 NaN 0.695645 \n",
|
||
"1628322 NaN 0.542497 \n",
|
||
"1628323 NaN 0.678180 \n",
|
||
"1628324 NaN 0.524743 \n",
|
||
"1628325 NaN 0.585852 \n",
|
||
"\n",
|
||
" cs_rank_elg_buy_sell_sm_ratio cs_rank_cost_dist_vol_ratio \\\n",
|
||
"1626778 0.341855 0.678941 \n",
|
||
"1626779 0.318912 0.402916 \n",
|
||
"1626780 0.260036 0.460713 \n",
|
||
"1626781 0.796350 0.988501 \n",
|
||
"1626782 0.598905 0.991571 \n",
|
||
"... ... ... \n",
|
||
"1628321 0.287899 0.788896 \n",
|
||
"1628322 0.116534 0.705843 \n",
|
||
"1628323 0.492860 0.783793 \n",
|
||
"1628324 0.130521 0.696446 \n",
|
||
"1628325 0.134175 0.735636 \n",
|
||
"\n",
|
||
" cs_rank_size future_return label \n",
|
||
"1626778 0.258948 0.092159 45.0 \n",
|
||
"1626779 0.258123 0.075103 41.0 \n",
|
||
"1626780 0.257664 0.058175 41.0 \n",
|
||
"1626781 0.290146 -0.034169 4.0 \n",
|
||
"1626782 0.282482 -0.023095 4.0 \n",
|
||
"... ... ... ... \n",
|
||
"1628321 0.032912 NaN NaN \n",
|
||
"1628322 0.034861 NaN NaN \n",
|
||
"1628323 0.035204 NaN NaN \n",
|
||
"1628324 0.034208 NaN NaN \n",
|
||
"1628325 0.032547 NaN NaN \n",
|
||
"\n",
|
||
"[1548 rows x 189 columns]\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"print(df[df['ts_code'] == '002652.SZ'])"
|
||
]
|
||
}
|
||
],
|
||
"metadata": {
|
||
"kernelspec": {
|
||
"display_name": "new_trader",
|
||
"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.11.11"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 5
|
||
}
|