diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..ae9beb9 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // 使用 IntelliSense 了解相关属性。 + // 悬停以查看现有属性的描述。 + // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python 调试程序: 当前文件", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "cwd": "${fileDirname}" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3a22111 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "terminal.integrated.env.windows": { + "PYTHONPATH": "${workspaceFolder};${env:PYTHONPATH}" + }, +"jupyter.notebookFileRoot": "${fileDirname}", +"python.dataScience.notebookFileRoot": "${workspaceFolder}" + } \ No newline at end of file diff --git a/code/test.py b/code/test.py deleted file mode 100644 index 797c6f4..0000000 --- a/code/test.py +++ /dev/null @@ -1,5 +0,0 @@ -import torch -print(torch.__version__) - -print(torch.version.cuda) -print(torch.backends.cudnn.version()) diff --git a/code/train/Rank.ipynb b/code/train/Rank.ipynb deleted file mode 100644 index 0aefe55..0000000 --- a/code/train/Rank.ipynb +++ /dev/null @@ -1,1727 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "id": "79a7758178bafdd3", - "metadata": { - "jupyter": { - "source_hidden": true - }, - "ExecuteTime": { - "end_time": "2025-04-03T12:46:06.987506Z", - "start_time": "2025-04-03T12:46:06.259551Z" - } - }, - "source": [ - "# %load_ext autoreload\n", - "# %autoreload 2\n", - "\n", - "import pandas as pd\n", - "import warnings\n", - "\n", - "warnings.filterwarnings(\"ignore\")\n", - "\n", - "pd.set_option('display.max_columns', None)\n" - ], - "outputs": [], - "execution_count": 1 - }, - { - "cell_type": "code", - "id": "a79cafb06a7e0e43", - "metadata": { - "scrolled": true, - "ExecuteTime": { - "end_time": "2025-04-03T12:47:00.212859Z", - "start_time": "2025-04-03T12:46:06.998047Z" - } - }, - "source": [ - "from 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'],\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', '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())" - ], - "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", - "\n", - "RangeIndex: 8477357 entries, 0 to 8477356\n", - "Data columns (total 31 columns):\n", - " # Column Dtype \n", - "--- ------ ----- \n", - " 0 ts_code object \n", - " 1 trade_date datetime64[ns]\n", - " 2 open float64 \n", - " 3 close float64 \n", - " 4 high float64 \n", - " 5 low float64 \n", - " 6 vol float64 \n", - " 7 pct_chg float64 \n", - " 8 turnover_rate float64 \n", - " 9 pe_ttm float64 \n", - " 10 circ_mv float64 \n", - " 11 volume_ratio float64 \n", - " 12 is_st bool \n", - " 13 up_limit float64 \n", - " 14 down_limit float64 \n", - " 15 buy_sm_vol float64 \n", - " 16 sell_sm_vol float64 \n", - " 17 buy_lg_vol float64 \n", - " 18 sell_lg_vol float64 \n", - " 19 buy_elg_vol float64 \n", - " 20 sell_elg_vol float64 \n", - " 21 net_mf_vol float64 \n", - " 22 his_low float64 \n", - " 23 his_high float64 \n", - " 24 cost_5pct float64 \n", - " 25 cost_15pct float64 \n", - " 26 cost_50pct float64 \n", - " 27 cost_85pct float64 \n", - " 28 cost_95pct float64 \n", - " 29 weight_avg float64 \n", - " 30 winner_rate float64 \n", - "dtypes: bool(1), datetime64[ns](1), float64(28), object(1)\n", - "memory usage: 1.9+ GB\n", - "None\n" - ] - } - ], - "execution_count": 2 - }, - { - "cell_type": "code", - "id": "cac01788dac10678", - "metadata": { - "ExecuteTime": { - "end_time": "2025-04-03T12:47:10.527104Z", - "start_time": "2025-04-03T12:47:00.488715Z" - } - }, - "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']])" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "industry\n" - ] - } - ], - "execution_count": 3 - }, - { - "cell_type": "code", - "id": "c4e9e1d31da6dba6", - "metadata": { - "jupyter": { - "source_hidden": true - }, - "ExecuteTime": { - "end_time": "2025-04-03T12:47:10.719252Z", - "start_time": "2025-04-03T12:47:10.541247Z" - } - }, - "source": [ - "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", - " 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', 'RSI', 'MACD', 'Signal_line',\n", - " 'MACD_hist', '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" - ], - "outputs": [], - "execution_count": 4 - }, - { - "cell_type": "code", - "id": "a735bc02ceb4d872", - "metadata": { - "ExecuteTime": { - "end_time": "2025-04-03T12:47:10.821169Z", - "start_time": "2025-04-03T12:47:10.751831Z" - } - }, - "source": [ - "import numpy as np\n", - "import talib\n", - "\n", - "def get_rolling_factor(df):\n", - " old_columns = df.columns.tolist()[:]\n", - "\n", - "\n", - " # 按股票和日期排序(如果尚未排序)\n", - " df = df.sort_values(by=['ts_code', 'trade_date'])\n", - "\n", - " grouped = df.groupby('ts_code', group_keys=False)\n", - "\n", - " # 提前计算布尔掩码\n", - " window = 5\n", - " return_threshold = 0.0\n", - "\n", - " df['_is_upside'] = df['pct_chg'] > return_threshold\n", - " df['_is_downside'] = df['pct_chg'] < -return_threshold\n", - " \n", - " # 1. 上行波动率 (20日)\n", - " def rolling_upside_volatility(series, _is_upside, window):\n", - " # 提取正收益\n", - " positive_returns = series.where(_is_upside, np.nan)\n", - " # 计算滚动窗口标准差\n", - " return positive_returns.rolling(window=window, min_periods=2).std()\n", - " \n", - " df[f'upside_volatility_{window}'] = grouped.apply(\n", - " lambda x: rolling_upside_volatility(x['pct_chg'], x['_is_upside'], window)\n", - " ).reset_index(level=0, drop=True)\n", - " \n", - " # 2. 下行波动率 (20日)\n", - " def rolling_downside_volatility(series, _is_downside, window):\n", - " # 提取负收益\n", - " negative_returns = series.where(_is_downside, np.nan)\n", - " # 计算滚动窗口标准差\n", - " return negative_returns.rolling(window=window, min_periods=2).std()\n", - " \n", - " df[f'downside_volatility_{window}'] = grouped.apply(\n", - " lambda x: rolling_downside_volatility(x['pct_chg'], x['_is_downside'], window)\n", - " ).reset_index(level=0, drop=True)\n", - " \n", - " # 3. 上行/下行波动率比率 (20日)\n", - " df[f'volatility_ratio_{window}'] = df[f'upside_volatility_{window}'] / (df[f'downside_volatility_{window}'] + 1e-8)\n", - " \n", - " # 4. 上行半方差 (20日)\n", - " def rolling_upside_semi_variance(series, _is_upside, window, threshold):\n", - " # 提取正收益\n", - " positive_returns = series.where(_is_upside, np.nan)\n", - " # 计算平方偏差\n", - " squared_deviation = (positive_returns - threshold)**2\n", - " # 计算滚动窗口均值\n", - " return squared_deviation.rolling(window=window, min_periods=2).mean()\n", - " \n", - " df[f'upside_semi_variance_{window}'] = grouped.apply(\n", - " lambda x: rolling_upside_semi_variance(x['pct_chg'], x['_is_upside'], window, return_threshold)\n", - " ).reset_index(level=0, drop=True)\n", - " \n", - " # 5. 下行半方差 (20日)\n", - " def rolling_downside_semi_variance(series, _is_downside, window, threshold):\n", - " # 提取负收益\n", - " negative_returns = series.where(_is_downside, np.nan)\n", - " # 计算平方偏差\n", - " squared_deviation = (negative_returns - (-threshold))**2\n", - " # 计算滚动窗口均值\n", - " return squared_deviation.rolling(window=window, min_periods=2).mean()\n", - " \n", - " df[f'downside_semi_variance_{window}'] = grouped.apply(\n", - " lambda x: rolling_downside_semi_variance(x['pct_chg'], x['_is_downside'], window, return_threshold)\n", - " ).reset_index(level=0, drop=True)\n", - "\n", - " # 8. 正负收益天数比率 (20日)\n", - " df[f'positive_negative_days_ratio_{window}'] = grouped['pct_chg'].rolling(window=window, min_periods=2).apply(lambda x: np.sum(x > 0) / (np.sum(x < 0) + 1e-8)).reset_index(level=0, drop=True)\n", - "\n", - " # 9. 正收益幅度均值 (20日)\n", - " def average_positive_return_magnitude(series):\n", - " positive_returns = series[series > return_threshold]\n", - " if positive_returns.empty:\n", - " return 0\n", - " return positive_returns.mean()\n", - " df[f'avg_positive_return_magnitude_{window}'] = grouped['pct_chg'].rolling(window=window, min_periods=2).apply(average_positive_return_magnitude).reset_index(level=0, drop=True)\n", - "\n", - " # 10. 负收益幅度均值 (20日)\n", - " def average_negative_return_magnitude(series):\n", - " negative_returns = series[series < -return_threshold]\n", - " if negative_returns.empty:\n", - " return 0\n", - " return np.abs(negative_returns.mean())\n", - " df[f'avg_negative_return_magnitude_{window}'] = grouped['pct_chg'].rolling(window=window, min_periods=2).apply(average_negative_return_magnitude).reset_index(level=0, drop=True)\n", - "\n", - " # df[\"gap_next_open\"] = (df[\"open\"].shift(-1) - df[\"close\"]) / df[\"close\"]\n", - "\n", - " df['return_skew'] = grouped['pct_chg'].rolling(window=5).skew().reset_index(0, drop=True)\n", - " df['return_kurtosis'] = grouped['pct_chg'].rolling(window=5).kurt().reset_index(0, drop=True)\n", - "\n", - " # 因子 1:短期成交量变化率\n", - " df['volume_change_rate'] = (\n", - " grouped['vol'].rolling(window=2).mean() /\n", - " grouped['vol'].rolling(window=10).mean() - 1\n", - " ).reset_index(level=0, drop=True) # 确保索引对齐\n", - "\n", - " # 因子 2:成交量突破信号\n", - " max_volume = grouped['vol'].rolling(window=5).max().reset_index(level=0, drop=True) # 确保索引对齐\n", - " df['cat_volume_breakout'] = (df['vol'] > max_volume)\n", - "\n", - " # 因子 3:换手率均线偏离度\n", - " mean_turnover = grouped['turnover_rate'].rolling(window=3).mean().reset_index(level=0, drop=True)\n", - " std_turnover = grouped['turnover_rate'].rolling(window=3).std().reset_index(level=0, drop=True)\n", - " df['turnover_deviation'] = (df['turnover_rate'] - mean_turnover) / std_turnover\n", - "\n", - " # 因子 4:换手率激增信号\n", - " df['cat_turnover_spike'] = (df['turnover_rate'] > mean_turnover + 2 * std_turnover)\n", - "\n", - " # 因子 5:量比均值\n", - " df['avg_volume_ratio'] = grouped['volume_ratio'].rolling(window=3).mean().reset_index(level=0, drop=True)\n", - "\n", - " # 因子 6:量比突破信号\n", - " max_volume_ratio = grouped['volume_ratio'].rolling(window=5).max().reset_index(level=0, drop=True)\n", - " df['cat_volume_ratio_breakout'] = (df['volume_ratio'] > max_volume_ratio)\n", - "\n", - " df['vol_spike'] = grouped.apply(\n", - " lambda x: pd.Series(x['vol'].rolling(20).mean(), index=x.index)\n", - " )\n", - " df['vol_std_5'] = df['vol'].pct_change().rolling(5).std()\n", - "\n", - " # 计算 ATR\n", - " df['atr_14'] = grouped.apply(\n", - " lambda x: pd.Series(talib.ATR(x['high'].values, x['low'].values, x['close'].values, timeperiod=14),\n", - " index=x.index)\n", - " )\n", - " df['atr_6'] = grouped.apply(\n", - " lambda x: pd.Series(talib.ATR(x['high'].values, x['low'].values, x['close'].values, timeperiod=6),\n", - " index=x.index)\n", - " )\n", - "\n", - " # 计算 OBV 及其均线\n", - " df['obv'] = grouped.apply(\n", - " lambda x: pd.Series(talib.OBV(x['close'].values, x['vol'].values), index=x.index)\n", - " )\n", - " df['maobv_6'] = grouped.apply(\n", - " lambda x: pd.Series(talib.SMA(x['obv'].values, timeperiod=6), index=x.index)\n", - " )\n", - "\n", - " df['rsi_3'] = grouped.apply(\n", - " lambda x: pd.Series(talib.RSI(x['close'].values, timeperiod=3), index=x.index)\n", - " )\n", - " # df['rsi_6'] = grouped.apply(\n", - " # lambda x: pd.Series(talib.RSI(x['close'].values, timeperiod=6), index=x.index)\n", - " # )\n", - " # df['rsi_9'] = grouped.apply(\n", - " # lambda x: pd.Series(talib.RSI(x['close'].values, timeperiod=9), index=x.index)\n", - " # )\n", - "\n", - " # 计算 return_10 和 return_20\n", - " df['return_5'] = grouped['close'].apply(lambda x: x / x.shift(5) - 1)\n", - " # df['return_10'] = grouped['close'].apply(lambda x: x / x.shift(10) - 1)\n", - " df['return_20'] = grouped['close'].apply(lambda x: x / x.shift(20) - 1)\n", - "\n", - " # df['avg_close_5'] = grouped['close'].apply(lambda x: x.rolling(window=5).mean() / x)\n", - "\n", - " # 计算标准差指标\n", - " df['std_return_5'] = grouped['close'].apply(lambda x: x.pct_change().rolling(window=5).std())\n", - " # df['std_return_15'] = grouped['close'].apply(lambda x: x.pct_change().rolling(window=15).std())\n", - " # df['std_return_25'] = grouped['close'].apply(lambda x: x.pct_change().rolling(window=25).std())\n", - " df['std_return_90'] = grouped['close'].apply(lambda x: x.pct_change().rolling(window=90).std())\n", - " df['std_return_90_2'] = grouped['close'].apply(lambda x: x.shift(10).pct_change().rolling(window=90).std())\n", - "\n", - " # 计算 EMA 指标\n", - " df['_ema_5'] = grouped['close'].apply(\n", - " lambda x: pd.Series(talib.EMA(x.values, timeperiod=5), index=x.index)\n", - " )\n", - " df['_ema_13'] = grouped['close'].apply(\n", - " lambda x: pd.Series(talib.EMA(x.values, timeperiod=13), index=x.index)\n", - " )\n", - " df['_ema_20'] = grouped['close'].apply(\n", - " lambda x: pd.Series(talib.EMA(x.values, timeperiod=20), index=x.index)\n", - " )\n", - " df['_ema_60'] = grouped['close'].apply(\n", - " lambda x: pd.Series(talib.EMA(x.values, timeperiod=60), index=x.index)\n", - " )\n", - "\n", - " # 计算 act_factor1, act_factor2, act_factor3, act_factor4\n", - " df['act_factor1'] = grouped['_ema_5'].apply(\n", - " lambda x: np.arctan((x / x.shift(1) - 1) * 100) * 57.3 / 50\n", - " )\n", - " df['act_factor2'] = grouped['_ema_13'].apply(\n", - " lambda x: np.arctan((x / x.shift(1) - 1) * 100) * 57.3 / 40\n", - " )\n", - " df['act_factor3'] = grouped['_ema_20'].apply(\n", - " lambda x: np.arctan((x / x.shift(1) - 1) * 100) * 57.3 / 21\n", - " )\n", - " df['act_factor4'] = grouped['_ema_60'].apply(\n", - " lambda x: np.arctan((x / x.shift(1) - 1) * 100) * 57.3 / 10\n", - " )\n", - "\n", - " # 根据 trade_date 截面计算排名\n", - " df['rank_act_factor1'] = df.groupby('trade_date', group_keys=False)['act_factor1'].rank(ascending=False, pct=True)\n", - " df['rank_act_factor2'] = df.groupby('trade_date', group_keys=False)['act_factor2'].rank(ascending=False, pct=True)\n", - " df['rank_act_factor3'] = df.groupby('trade_date', group_keys=False)['act_factor3'].rank(ascending=False, pct=True)\n", - "\n", - " df['log(circ_mv)'] = np.log(df['circ_mv'])\n", - "\n", - " def rolling_covariance(x, y, window):\n", - " return x.rolling(window).cov(y)\n", - "\n", - " def delta(series, period):\n", - " return series.diff(period)\n", - "\n", - " def rank(series):\n", - " return series.rank(pct=True)\n", - "\n", - " def stddev(series, window):\n", - " return series.rolling(window).std()\n", - "\n", - " window_high_volume = 5\n", - " window_close_stddev = 20\n", - " period_delta = 5\n", - " df['cov'] = rolling_covariance(df['high'], df['vol'], window_high_volume)\n", - " df['delta_cov'] = delta(df['cov'], period_delta)\n", - " df['_rank_stddev'] = rank(stddev(df['close'], window_close_stddev))\n", - " df['alpha_22_improved'] = -1 * df['delta_cov'] * df['_rank_stddev']\n", - "\n", - " df['alpha_003'] = np.where(df['high'] != df['low'],\n", - " (df['close'] - df['open']) / (df['high'] - df['low']),\n", - " 0)\n", - "\n", - " df['alpha_007'] = grouped.apply(lambda x: x['close'].rolling(5).corr(x['vol'])).reset_index(level=0, drop=True)\n", - " df['alpha_007'] = df.groupby('trade_date', group_keys=False)['alpha_007'].rank(ascending=True, pct=True)\n", - "\n", - " df['alpha_013'] = grouped['close'].transform(lambda x: x.rolling(5).sum() - x.rolling(20).sum())\n", - " df['alpha_013'] = df.groupby('trade_date', group_keys=False)['alpha_013'].rank(ascending=True, pct=True)\n", - "\n", - " df['cat_up_limit'] = (df['close'] == df['up_limit']) # 是否涨停(1表示涨停,0表示未涨停)\n", - " df['cat_down_limit'] = (df['close'] == df['down_limit']) # 是否跌停(1表示跌停,0表示未跌停)\n", - " df['up_limit_count_10d'] = grouped['cat_up_limit'].rolling(window=10, min_periods=1).sum().reset_index(level=0,\n", - " drop=True)\n", - " df['down_limit_count_10d'] = grouped['cat_down_limit'].rolling(window=10, min_periods=1).sum().reset_index(level=0,\n", - " drop=True)\n", - "\n", - " # 3. 最近连续涨跌停天数\n", - " def calculate_consecutive_limits(series):\n", - " \"\"\"\n", - " 计算连续涨停/跌停天数。\n", - " \"\"\"\n", - " consecutive_up = series * (series.groupby((series != series.shift()).cumsum()).cumcount() + 1)\n", - " consecutive_down = series * (series.groupby((series != series.shift()).cumsum()).cumcount() + 1)\n", - " return consecutive_up, consecutive_down\n", - "\n", - " # 连续涨停天数\n", - " df['consecutive_up_limit'] = grouped['cat_up_limit'].apply(\n", - " lambda x: calculate_consecutive_limits(x)[0]\n", - " ).reset_index(level=0, drop=True)\n", - "\n", - " df['vol_break'] = np.where((df['close'] > df['cost_85pct']) & (df['volume_ratio'] > 2), 1, 0)\n", - "\n", - " df['weight_roc5'] = grouped['weight_avg'].apply(lambda x: x.pct_change(5))\n", - "\n", - " def rolling_corr(group):\n", - " roc_close = group['close'].pct_change()\n", - " roc_weight = group['weight_avg'].pct_change()\n", - " return roc_close.rolling(10).corr(roc_weight)\n", - "\n", - " df['price_cost_divergence'] = grouped.apply(rolling_corr)\n", - "\n", - " df['smallcap_concentration'] = (1 / df['log(circ_mv)']) * (df['cost_85pct'] - df['cost_15pct'])\n", - "\n", - " # 16. 筹码稳定性指数 (20日波动率)\n", - " df['weight_std20'] = grouped['weight_avg'].apply(lambda x: x.rolling(20).std())\n", - " df['cost_stability'] = df['weight_std20'] / grouped['weight_avg'].transform(lambda x: x.rolling(20).mean())\n", - "\n", - " # 17. 成本区间突破标记\n", - " df['high_cost_break_days'] = grouped.apply(lambda g: g['close'].gt(g['cost_95pct']).rolling(5).sum())\n", - "\n", - " # 20. 筹码-流动性风险\n", - " df['liquidity_risk'] = (df['cost_95pct'] - df['cost_5pct']) * (\n", - " 1 / grouped['vol'].transform(lambda x: x.rolling(10).mean()))\n", - "\n", - " # 7. 市值波动率因子\n", - " df['turnover_std'] = grouped['turnover_rate'].rolling(window=20).std().reset_index(level=0, drop=True)\n", - " df['mv_volatility'] = grouped.apply(lambda x: x['turnover_std'] / x['log(circ_mv)']).reset_index(level=0, drop=True)\n", - "\n", - " # 8. 市值成长性因子\n", - " df['volume_growth'] = grouped['vol'].pct_change(periods=20).reset_index(level=0, drop=True)\n", - " df['mv_growth'] = grouped.apply(lambda x: x['volume_growth'] / x['log(circ_mv)']).reset_index(level=0, drop=True)\n", - "\n", - " df[\"ar\"] = df[\"high\"].div(df[\"open\"]).rolling(3).sum() / df[\"open\"].div(df[\"low\"]).rolling(3).sum() * 100\n", - " # 计算 BR 指标\n", - " df[\"pre_close\"] = df[\"close\"].shift(1)\n", - " df[\"br_up\"] = (df[\"high\"] - df[\"pre_close\"]).clip(lower=0)\n", - " df[\"br_down\"] = (df[\"pre_close\"] - df[\"low\"]).clip(lower=0)\n", - " df[\"br\"] = df[\"br_up\"].rolling(3).sum() / df[\"br_down\"].rolling(3).sum() * 100\n", - " df['arbr'] = df['ar'] - df['br']\n", - " df.drop(columns=[\"pre_close\", \"br_up\", \"br_down\", 'ar', 'br'], inplace=True)\n", - "\n", - " df.drop(columns=['weight_std20'], inplace=True, errors='ignore')\n", - " new_columns = [col for col in df.columns.tolist()[:] if col not in old_columns]\n", - "\n", - " return df, new_columns\n", - "\n", - "\n", - "def get_simple_factor(df):\n", - " old_columns = df.columns.tolist()[:]\n", - " df = df.sort_values(by=['ts_code', 'trade_date'])\n", - "\n", - " alpha = 0.5\n", - " df['momentum_factor'] = df['volume_change_rate'] + alpha * df['turnover_deviation']\n", - " df['resonance_factor'] = df['volume_ratio'] * df['pct_chg']\n", - " df['log_close'] = np.log(df['close'])\n", - "\n", - " df['cat_vol_spike'] = df['vol'] > 2 * df['vol_spike']\n", - "\n", - " df['up'] = (df['high'] - df[['close', 'open']].max(axis=1)) / df['close']\n", - " df['down'] = (df[['close', 'open']].min(axis=1) - df['low']) / df['close']\n", - "\n", - " df['obv-maobv_6'] = df['obv'] - df['maobv_6']\n", - "\n", - " # 计算比值指标\n", - " df['std_return_5 / std_return_90'] = df['std_return_5'] / df['std_return_90']\n", - " # df['std_return_5 / std_return_25'] = df['std_return_5'] / df['std_return_25']\n", - "\n", - " # 计算标准差差值\n", - " df['std_return_90 - std_return_90_2'] = df['std_return_90'] - df['std_return_90_2']\n", - "\n", - " # df['cat_af1'] = df['act_factor1'] > 0\n", - " df['cat_af2'] = df['act_factor2'] > df['act_factor1']\n", - " df['cat_af3'] = df['act_factor3'] > df['act_factor2']\n", - " df['cat_af4'] = df['act_factor4'] > df['act_factor3']\n", - "\n", - " # 计算 act_factor5 和 act_factor6\n", - " df['act_factor5'] = df['act_factor1'] + df['act_factor2'] + df['act_factor3'] + df['act_factor4']\n", - " df['act_factor6'] = (df['act_factor1'] - df['act_factor2']) / np.sqrt(\n", - " df['act_factor1'] ** 2 + df['act_factor2'] ** 2)\n", - "\n", - " df['active_buy_volume_large'] = df['buy_lg_vol'] / df['net_mf_vol']\n", - " df['active_buy_volume_big'] = df['buy_elg_vol'] / df['net_mf_vol']\n", - " df['active_buy_volume_small'] = df['buy_sm_vol'] / df['net_mf_vol']\n", - "\n", - " df['buy_lg_vol_minus_sell_lg_vol'] = (df['buy_lg_vol'] - df['sell_lg_vol']) / df['net_mf_vol']\n", - " df['buy_elg_vol_minus_sell_elg_vol'] = (df['buy_elg_vol'] - df['sell_elg_vol']) / df['net_mf_vol']\n", - "\n", - " df['log(circ_mv)'] = np.log(df['circ_mv'])\n", - "\n", - " df['ctrl_strength'] = (df['cost_85pct'] - df['cost_15pct']) / (df['his_high'] - df['his_low'])\n", - "\n", - " df['low_cost_dev'] = (df['close'] - df['cost_5pct']) / (df['cost_50pct'] - df['cost_5pct'])\n", - "\n", - " df['asymmetry'] = (df['cost_95pct'] - df['cost_50pct']) / (df['cost_50pct'] - df['cost_5pct'])\n", - "\n", - " df['lock_factor'] = df['turnover_rate'] * (\n", - " 1 - (df['cost_95pct'] - df['cost_5pct']) / (df['his_high'] - df['his_low']))\n", - "\n", - " df['cat_vol_break'] = (df['close'] > df['cost_85pct']) & (df['volume_ratio'] > 2)\n", - "\n", - " df['cost_atr_adj'] = (df['cost_95pct'] - df['cost_5pct']) / df['atr_14']\n", - "\n", - " # 12. 小盘股筹码集中度\n", - " df['smallcap_concentration'] = (1 / df['log(circ_mv)']) * (df['cost_85pct'] - df['cost_15pct'])\n", - "\n", - " df['cat_golden_resonance'] = ((df['close'] > df['weight_avg']) &\n", - " (df['volume_ratio'] > 1.5) &\n", - " (df['winner_rate'] > 0.7))\n", - "\n", - " df['mv_turnover_ratio'] = df['turnover_rate'] / df['log(circ_mv)']\n", - "\n", - " df['mv_adjusted_volume'] = df['vol'] / df['log(circ_mv)']\n", - "\n", - " df['mv_weighted_turnover'] = df['turnover_rate'] * (1 / df['log(circ_mv)'])\n", - "\n", - " df['nonlinear_mv_volume'] = df['vol'] / df['log(circ_mv)']\n", - "\n", - " df['mv_volume_ratio'] = df['volume_ratio'] / df['log(circ_mv)']\n", - "\n", - " df['mv_momentum'] = df['turnover_rate'] * df['volume_ratio'] / df['log(circ_mv)']\n", - "\n", - " drop_columns = [col for col in df.columns if col.startswith('_')]\n", - " df.drop(columns=drop_columns, inplace=True, errors='ignore')\n", - "\n", - " new_columns = [col for col in df.columns.tolist()[:] if col not in old_columns]\n", - " return df, new_columns\n" - ], - "outputs": [], - "execution_count": 5 - }, - { - "cell_type": "code", - "id": "53f86ddc0677a6d7", - "metadata": { - "jupyter": { - "source_hidden": true - }, - "scrolled": true, - "ExecuteTime": { - "end_time": "2025-04-03T12:47:15.944254Z", - "start_time": "2025-04-03T12:47:10.826179Z" - } - }, - "source": [ - "from 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", - " 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" - ], - "outputs": [], - "execution_count": 6 - }, - { - "cell_type": "code", - "id": "dbe2fd8021b9417f", - "metadata": { - "ExecuteTime": { - "end_time": "2025-04-03T12:47:15.969344Z", - "start_time": "2025-04-03T12:47:15.963327Z" - } - }, - "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)" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "['ts_code', 'open', 'close', 'high', 'low', 'circ_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" - ] - } - ], - "execution_count": 7 - }, - { - "cell_type": "code", - "id": "85c3e3d0235ffffa", - "metadata": { - "ExecuteTime": { - "end_time": "2025-04-03T12:47:16.089879Z", - "start_time": "2025-04-03T12:47:15.990101Z" - } - }, - "source": [ - "print(df[df['is_st']][['ts_code', 'trade_date', 'is_st']])" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " ts_code trade_date is_st\n", - "29 000037.SZ 2017-01-03 True\n", - "72 000408.SZ 2017-01-03 True\n", - "95 000504.SZ 2017-01-03 True\n", - "96 000505.SZ 2017-01-03 True\n", - "101 000511.SZ 2017-01-03 True\n", - "... ... ... ...\n", - "8476334 603869.SH 2025-03-28 True\n", - "8476339 603879.SH 2025-03-28 True\n", - "8476386 603959.SH 2025-03-28 True\n", - "8476769 688282.SH 2025-03-28 True\n", - "8476773 688287.SH 2025-03-28 True\n", - "\n", - "[192712 rows x 3 columns]\n" - ] - } - ], - "execution_count": 8 - }, - { - "cell_type": "code", - "id": "92d84ce15a562ec6", - "metadata": { - "ExecuteTime": { - "end_time": "2025-04-03T13:08:01.612695Z", - "start_time": "2025-04-03T12:47:16.121802Z" - } - }, - "source": [ - "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'] >= '20180101']\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", - "df = filter_data(df)\n", - "# df = get_technical_factor(df)\n", - "# df = get_act_factor(df)\n", - "# df = get_money_flow_factor(df)\n", - "# df = get_alpha_factor(df)\n", - "# df = get_limit_factor(df)\n", - "# df = get_cyp_perf_factor(df)\n", - "# df = get_mv_factors(df)\n", - "df, _ = get_rolling_factor(df)\n", - "df, _ = get_simple_factor(df)\n", - "# df = df.merge(industry_df, on=['l2_code', 'trade_date'], how='left')\n", - "df = df.rename(columns={'l2_code': 'cat_l2_code'})\n", - "# df = df.merge(index_data, on='trade_date', how='left')\n", - "\n", - "print(df.info())" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Index: 5118212 entries, 0 to 5118211\n", - "Columns: 123 entries, ts_code to mv_momentum\n", - "dtypes: bool(12), datetime64[ns](1), float64(106), int32(1), int64(1), object(2)\n", - "memory usage: 4.3+ GB\n", - "None\n" - ] - } - ], - "execution_count": 9 - }, - { - "cell_type": "code", - "id": "b87b938028afa206", - "metadata": { - "ExecuteTime": { - "end_time": "2025-04-03T13:08:03.658725Z", - "start_time": "2025-04-03T13:08:02.469611Z" - } - }, - "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" - ], - "outputs": [], - "execution_count": 10 - }, - { - "cell_type": "code", - "id": "f4f16d63ad18d1bc", - "metadata": { - "ExecuteTime": { - "end_time": "2025-04-03T13:08:03.670700Z", - "start_time": "2025-04-03T13:08:03.665739Z" - } - }, - "source": [ - "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" - ], - "outputs": [], - "execution_count": 11 - }, - { - "cell_type": "code", - "id": "40e6b68a91b30c79", - "metadata": { - "ExecuteTime": { - "end_time": "2025-04-03T13:08:04.694262Z", - "start_time": "2025-04-03T13:08:03.694904Z" - } - }, - "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", - "import pandas as pd\n", - "from sklearn.preprocessing import StandardScaler\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", - "import statsmodels.api as sm\n", - "\n", - "from concurrent.futures import ProcessPoolExecutor\n", - "\n", - "\n", - "def neutralize_manual(df, features, industry_col, mkt_cap_col):\n", - " \"\"\" 手动实现简单回归以提升速度 \"\"\"\n", - "\n", - " for col in features:\n", - " residuals = []\n", - " for _, group in df.groupby(industry_col):\n", - " if len(group) > 1:\n", - " x = np.log(group[mkt_cap_col]) # 市值对数\n", - " y = group[col] # 因子值\n", - " beta = np.cov(y, x)[0, 1] / np.var(x) # 计算斜率\n", - " alpha = np.mean(y) - beta * np.mean(x) # 计算截距\n", - " resid = y - (alpha + beta * x) # 计算残差\n", - " residuals.extend(resid)\n", - " else:\n", - " residuals.extend(group[col]) # 样本不足时保留原值\n", - "\n", - " df[col] = residuals\n", - "\n", - " return 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" - ], - "outputs": [], - "execution_count": 12 - }, - { - "cell_type": "code", - "id": "47c12bb34062ae7a", - "metadata": { - "ExecuteTime": { - "end_time": "2025-04-03T14:57:50.841165Z", - "start_time": "2025-04-03T14:49:25.889057Z" - } - }, - "source": [ - "days = 2\n", - "validation_days = 120\n", - "\n", - "import gc\n", - "\n", - "gc.collect()\n", - "\n", - "df = df.sort_values(by=['ts_code', 'trade_date'])\n", - "# df['future_return'] = df.groupby('ts_code', group_keys=False)['close'].apply(lambda x: x.shift(-days) / x - 1)\n", - "df['future_return'] = (df.groupby('ts_code')['close'].shift(-days) - df.groupby('ts_code')['open'].shift(-1)) / \\\n", - " df.groupby('ts_code')['open'].shift(-1)\n", - "df['future_volatility'] = (\n", - " df.groupby('ts_code')['pct_chg']\n", - " .transform(lambda x: x.rolling(days).std().shift(-days))\n", - ")\n", - "\n", - "# df['future_score'] = (\n", - "# 0.7 * df['future_return']\n", - "# * 0.3 * df['future_volatility']\n", - "# )\n", - "df['future_score'] = calculate_score(df, days=2, lambda_param=0.3)\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_volatility'].between(df['future_volatility'].quantile(0.01),\n", - " df['future_volatility'].quantile(0.99)) | filter_index\n", - "\n", - "# df['label'] = df.groupby('trade_date', group_keys=False)['future_volatility'].transform(\n", - "# lambda x: pd.qcut(x, q=30, labels=False, duplicates='drop')\n", - "# )\n", - "\n", - "df['label'] = df.groupby('trade_date', group_keys=False)['future_score'].transform(\n", - " lambda x: pd.qcut(x, q=20, labels=False, duplicates='drop')\n", - ")\n", - "\n", - "\n", - "# df['1_score'] = df.groupby('ts_code', group_keys=False)['future_score'].shift(days)\n", - "# df['2_score'] = df.groupby('ts_code', group_keys=False)['future_score'].shift(1 + days)\n", - "# df['3_score'] = df.groupby('ts_code', group_keys=False)['future_score'].shift(3 + days - 1)\n", - "\n", - "def symmetric_log_transform(values):\n", - " return np.sign(values) * np.log1p(np.abs(values))\n", - "\n", - "\n", - "train_data = df[filter_index & (df['trade_date'] <= '2023-08-01') & (df['trade_date'] >= '2000-01-01')]\n", - "test_data = df[(df['trade_date'] >= '2023-08-01')]\n", - "\n", - "\n", - "def select_pre_zt_stocks_dynamic(stock_df):\n", - " # 排序数据\n", - " stock_df = stock_df.sort_values(by=['trade_date', 'ts_code'])\n", - " stock_df = stock_df.groupby('trade_date', group_keys=False).apply(\n", - " lambda x: x.nlargest(1000, 'return_20')\n", - " )\n", - "\n", - " return stock_df\n", - "\n", - "\n", - "train_data = select_pre_zt_stocks_dynamic(train_data)\n", - "test_data = select_pre_zt_stocks_dynamic(test_data)\n", - "\n", - "train_data, _ = get_simple_factor(train_data)\n", - "test_data, _ = get_simple_factor(test_data)\n", - "\n", - "# train_data['label'] = train_data.groupby('trade_date', group_keys=False)['future_score'].transform(\n", - "# lambda x: pd.qcut(x, q=50, labels=False, duplicates='drop')\n", - "# )\n", - "# test_data['label'] = test_data.groupby('trade_date', group_keys=False)['future_score'].transform(\n", - "# lambda x: pd.qcut(x, q=50, labels=False, duplicates='drop')\n", - "# )\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, feature_columns)\n", - "# test_data, _ = create_deviation_within_dates(test_data, feature_columns)\n", - "\n", - "feature_columns = [col for col in train_data.columns if col in train_data.columns]\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 'cat_l2_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", - "print(f'feature_columns size: {len(feature_columns)}')\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", - "print('去极值')\n", - "train_data = quantile_filter(train_data, numeric_columns) # 去极值\n", - "# print('中性化')\n", - "# train_data = neutralize_manual(train_data, numeric_columns, industry_col='cat_l2_code', mkt_cap_col='log(circ_mv)') # 中性化\n", - "print('去极值')\n", - "test_data = quantile_filter(test_data, numeric_columns) # 去极值\n", - "# print('中性化')\n", - "# test_data = neutralize_manual(test_data, numeric_columns, industry_col='cat_l2_code', mkt_cap_col='log(circ_mv)')\n", - "all_dates = train_data['trade_date'].unique() # 获取所有唯一的 trade_date\n", - "split_date = all_dates[-validation_days] # 划分点为倒数第 validation_days 天\n", - "train_data_split = train_data[train_data['trade_date'] < split_date] # 训练集\n", - "val_data_split = train_data[train_data['trade_date'] >= split_date] # 验证集\n", - "\n", - "feature_columns, _ = remove_shifted_features(\n", - " train_data_split,\n", - " val_data_split,\n", - " feature_columns)\n", - "\n", - "feature_columns = remove_highly_correlated_features(train_data,\n", - " feature_columns)\n", - "keep_columns = [col for col in train_data.columns if\n", - " col in feature_columns or col in ['ts_code', 'trade_date', 'label', 'future_return',\n", - " 'future_score', 'future_volatility']]\n", - "# train_data = train_data[keep_columns]\n", - "print(f'feature_columns: {feature_columns}')\n", - "\n", - "train_data = train_data.dropna(subset=feature_columns)\n", - "train_data = train_data.dropna(subset=['label'])\n", - "train_data = train_data.reset_index(drop=True)\n", - "\n", - "# print(test_data.tail())\n", - "test_data = test_data.dropna(subset=feature_columns)\n", - "# test_data = test_data.dropna(subset=['label'])\n", - "test_data = test_data.reset_index(drop=True)\n", - "\n", - "print(len(train_data))\n", - "print(f\"最小日期: {train_data['trade_date'].min().strftime('%Y-%m-%d')}\")\n", - "print(f\"最大日期: {train_data['trade_date'].max().strftime('%Y-%m-%d')}\")\n", - "print(len(test_data))\n", - "print(f\"最小日期: {test_data['trade_date'].min().strftime('%Y-%m-%d')}\")\n", - "print(f\"最大日期: {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", - "\n", - "\n", - "# feature_columns_new.remove('cat_l2_code')" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "feature_columns size: 114\n", - "去极值\n", - "去极值\n", - "检测到 13 个可能漂移的特征: ['vol', 'pct_chg', 'turnover_rate', 'obv', 'log(circ_mv)', 'alpha_003', 'log_close', 'up', 'down', 'mv_turnover_ratio', 'mv_adjusted_volume', 'mv_weighted_turnover', 'nonlinear_mv_volume']\n", - "feature_columns: ['pe_ttm', 'volume_ratio', 'winner_rate', 'upside_volatility_5', 'downside_volatility_5', 'volatility_ratio_5', 'upside_semi_variance_5', 'downside_semi_variance_5', 'positive_negative_days_ratio_5', 'avg_positive_return_magnitude_5', 'avg_negative_return_magnitude_5', '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', 'maobv_6', 'rsi_3', 'return_5', 'return_20', 'std_return_5', 'std_return_90', 'act_factor1', 'act_factor2', 'act_factor3', 'act_factor4', 'rank_act_factor1', 'rank_act_factor2', 'rank_act_factor3', 'cov', 'delta_cov', 'alpha_007', 'alpha_013', 'cat_up_limit', 'cat_down_limit', 'up_limit_count_10d', 'down_limit_count_10d', 'consecutive_up_limit', 'vol_break', 'weight_roc5', 'smallcap_concentration', 'cost_stability', 'high_cost_break_days', 'liquidity_risk', 'turnover_std', 'mv_volatility', 'volume_growth', 'mv_growth', 'arbr', 'momentum_factor', 'resonance_factor', 'cat_vol_spike', 'obv-maobv_6', 'std_return_5 / std_return_90', 'std_return_90 - 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_momentum', 'industry_obv', 'industry_return_5', 'industry_return_20', 'industry__ema_5', 'industry_act_factor1', 'industry_act_factor2', 'industry_act_factor3', 'industry_act_factor4', 'industry_act_factor5', 'industry_act_factor6', 'industry_rank_act_factor1', 'industry_rank_act_factor2', 'industry_rank_act_factor3', 'industry_return_5_percentile', 'industry_return_20_percentile']\n", - "590201\n", - "最小日期: 2018-06-04\n", - "最大日期: 2023-08-01\n", - "179005\n", - "最小日期: 2023-08-01\n", - "最大日期: 2025-03-28\n" - ] - } - ], - "execution_count": 55 - }, - { - "cell_type": "code", - "id": "8f134d435f71e9e2", - "metadata": { - "jupyter": { - "source_hidden": true - }, - "ExecuteTime": { - "end_time": "2025-04-03T14:57:51.050696Z", - "start_time": "2025-04-03T14:57:51.034030Z" - } - }, - "source": [ - "from sklearn.preprocessing import StandardScaler\n", - "import lightgbm as lgb\n", - "import matplotlib.pyplot as plt\n", - "from sklearn.decomposition import PCA\n", - "\n", - "\n", - "def train_light_model(train_data_df, params, feature_columns, callbacks, evals,\n", - " print_feature_importance=True, num_boost_round=100,\n", - " validation_days=180, use_pca=False, split_date=None): # 新增参数:validation_days\n", - " # 确保数据按时间排序\n", - " train_data_df = train_data_df.sort_values(by='trade_date')\n", - "\n", - " numeric_columns = train_data_df.select_dtypes(include=['float64', 'int64']).columns\n", - " numeric_columns = [col for col in numeric_columns if col in feature_columns]\n", - " # X_train.loc[:, numeric_columns] = scaler.fit_transform(X_train[numeric_columns])\n", - " # X_val.loc[:, numeric_columns] = scaler.transform(X_val[numeric_columns])\n", - " train_data_df = cross_sectional_standardization(train_data_df, numeric_columns)\n", - "\n", - " # 去除标签为空的样本\n", - " train_data_df = train_data_df.dropna(subset=['label'])\n", - " print('原始训练集大小: ', len(train_data_df))\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", - " # 打印划分结果\n", - " print(f\"划分后的训练集大小: {len(train_data_split)}, 验证集大小: {len(val_data_split)}\")\n", - "\n", - " # 提取特征和标签\n", - " X_train = train_data_split[feature_columns]\n", - " y_train = train_data_split['label']\n", - "\n", - " X_val = val_data_split[feature_columns]\n", - " y_val = val_data_split['label']\n", - "\n", - " # 标准化数值特征\n", - " scaler = StandardScaler()\n", - "\n", - " # 计算每个 trade_date 内的样本数(LTR 需要 group 信息)\n", - " train_groups = train_data_split.groupby('trade_date').size().tolist()\n", - " val_groups = val_data_split.groupby('trade_date').size().tolist()\n", - "\n", - " # 处理类别特征\n", - " categorical_feature = [col for col in feature_columns if 'cat' in col]\n", - "\n", - " pca = None\n", - " if use_pca:\n", - " pca = PCA(n_components=0.95) # 或指定 n_components=固定值(如 10)\n", - " numeric_features = [col for col in feature_columns if col not in categorical_feature]\n", - " numeric_pca = pca.fit_transform(X_train[numeric_features])\n", - " X_train = pd.concat([pd.DataFrame(numeric_pca, index=X_train.index), X_train[categorical_feature]], axis=1)\n", - "\n", - " numeric_pca = pca.transform(X_val[numeric_features])\n", - " X_val = pd.concat([pd.DataFrame(numeric_pca, index=X_val.index), X_val[categorical_feature]], axis=1)\n", - "\n", - " # 计算权重(基于时间)\n", - " # trade_date = train_data_split['trade_date'] # 交易日期\n", - " # weights = (trade_date - trade_date.min()).dt.days / (trade_date.max() - trade_date.min()).days + 1\n", - " # weights = train_data_split.groupby('trade_date')['std_return_5'].transform(\n", - " # lambda x: x / x.mean()\n", - " # )\n", - " ud = sorted(train_data_split[\"trade_date\"].unique().tolist())\n", - " date_weights = {date: weight * weight for date, weight in zip(ud, np.linspace(1, 10, len(ud)))}\n", - " params['weight'] = train_data_split[\"trade_date\"].map(date_weights).tolist()\n", - "\n", - " print('feature_columns size: ', len(X_train.columns.tolist()))\n", - "\n", - " train_dataset = lgb.Dataset(\n", - " X_train, label=y_train, group=train_groups,\n", - " categorical_feature=categorical_feature\n", - " )\n", - "\n", - " # weights = val_data_split.groupby('trade_date')['std_return_5'].transform(\n", - " # lambda x: x / x.mean()\n", - " # )\n", - " val_dataset = lgb.Dataset(\n", - " X_val, label=y_val, group=val_groups,\n", - " categorical_feature=categorical_feature\n", - " )\n", - "\n", - " # 训练模型\n", - " model = lgb.train(\n", - " params, train_dataset, num_boost_round=num_boost_round,\n", - " valid_sets=[train_dataset, val_dataset], valid_names=['train', 'valid'],\n", - " callbacks=callbacks\n", - " )\n", - "\n", - " # 打印特征重要性(如果需要)\n", - " if print_feature_importance:\n", - " lgb.plot_metric(evals)\n", - " lgb.plot_importance(model, importance_type='split', max_num_features=20)\n", - " plt.show()\n", - "\n", - " return model, scaler, pca\n", - "\n", - "\n", - "from catboost import CatBoostRanker, Pool\n", - "import numpy as np\n", - "\n", - "\n", - "def train_catboost(train_data_df, test_data_df, feature_columns, params=None, plot=False):\n", - " X_train = train_data_df[feature_columns]\n", - " y_train = train_data_df['label']\n", - "\n", - " X_val = test_data_df[feature_columns]\n", - " y_val = test_data_df['label']\n", - "\n", - " scaler = StandardScaler()\n", - " numeric_columns = X_train.select_dtypes(include=['float64', 'int64']).columns\n", - " X_train.loc[:, numeric_columns] = scaler.fit_transform(X_train[numeric_columns])\n", - " X_val.loc[:, numeric_columns] = scaler.transform(X_val[numeric_columns])\n", - "\n", - " group_train = train_data_df['trade_date'].factorize()[0]\n", - " group_val = test_data_df['trade_date'].factorize()[0]\n", - "\n", - " cat_features = [i for i, col in enumerate(feature_columns) if col.startswith('cat')]\n", - " print(f'cat_features: {cat_features}')\n", - "\n", - " train_pool = Pool(\n", - " data=X_train,\n", - " label=y_train,\n", - " group_id=group_train,\n", - " cat_features=cat_features\n", - " )\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", - " # CatBoost 排序学习模型\n", - " model = CatBoostRanker(**params)\n", - " model.fit(train_pool, eval_set=val_pool, plot=plot, use_best_model=True)\n", - "\n", - " return model, scaler\n" - ], - "outputs": [], - "execution_count": 56 - }, - { - "cell_type": "code", - "id": "c6eb5cd4-e714-420a-ac48-39af3e11ee81", - "metadata": { - "ExecuteTime": { - "end_time": "2025-04-03T15:03:18.426481Z", - "start_time": "2025-04-03T15:02:19.926352Z" - } - }, - "source": [ - "print('train data size: ', len(train_data))\n", - "\n", - "label_gain = list(range(len(train_data['label'].unique())))\n", - "label_gain = [gain * gain for gain in label_gain]\n", - "light_params = {\n", - " 'label_gain': label_gain,\n", - " 'objective': 'lambdarank',\n", - " 'metric': 'ndcg',\n", - " 'learning_rate': 0.03,\n", - " 'num_leaves': 32,\n", - " # 'min_data_in_leaf': 128,\n", - " 'max_depth': 8,\n", - " 'max_bin': 32,\n", - " 'feature_fraction': 0.7,\n", - " 'bagging_fraction': 0.7,\n", - " 'bagging_freq': 5,\n", - " 'lambda_l1': 0.1,\n", - " 'lambda_l2': 0.1,\n", - " 'boosting': 'goss',\n", - " 'verbosity': -1,\n", - " 'extra_trees': True,\n", - " 'max_position': 5,\n", - " 'ndcg_at': 1,\n", - " 'quant_train_renew_leaf': True,\n", - " 'lambdarank_truncation_level': 1,\n", - " 'lambdarank_position_bias_regularization': 1,\n", - " 'seed': 7\n", - "}\n", - "evals = {}\n", - "\n", - "gc.collect()\n", - "\n", - "use_pca = False\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_light_model(train_data.dropna(subset=['label']),\n", - " light_params, feature_columns,\n", - " [lgb.log_evaluation(period=100),\n", - " lgb.callback.record_evaluation(evals),\n", - " lgb.early_stopping(100, first_metric_only=True)\n", - " ], evals,\n", - " num_boost_round=1000, validation_days=120,\n", - " print_feature_importance=True, use_pca=use_pca)\n", - "\n", - "print('train data size: ', len(train_data))" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "train data size: 590201\n", - "原始训练集大小: 590201\n", - "划分后的训练集大小: 537198, 验证集大小: 53003\n", - "feature_columns size: 94\n", - "Training until validation scores don't improve for 100 rounds\n", - "[100]\ttrain's ndcg@1: 0.727839\tvalid's ndcg@1: 0.35097\n", - "Early stopping, best iteration is:\n", - "[67]\ttrain's ndcg@1: 0.702439\tvalid's ndcg@1: 0.403463\n", - "Evaluated only: ndcg@1\n" - ] - }, - { - "data": { - "text/plain": [ - "
" - ], - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjcAAAHHCAYAAABDUnkqAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQAAkbpJREFUeJzs3Xd4k9XbwPFvku6WbjqAQtl7L9mgIIqiOBEQARUXuPi5UJn6ihNxggPEheLAybIgUxCw7L03bSmle6XJ8/5xmqShg7aUpk3uz3X1SvKsnJO2yZ1z7nOOTtM0DSGEEEIIJ6F3dAGEEEIIISqSBDdCCCGEcCoS3AghhBDCqUhwI4QQQginIsGNEEIIIZyKBDdCCCGEcCoS3AghhBDCqUhwI4QQQginIsGNEEIIIZyKBDdCCObPn49Op+P48eNX7TmmTp2KTqerNtd1tOPHj6PT6Zg/f365ztfpdEydOrVCyyREdSHBjRCVyBJE6HQ61q9fX2i/pmlERUWh0+m4+eaby/UcH3/8cbk/EEXZLFiwgFmzZjm6GEKIS0hwI4QDeHl5sWDBgkLb16xZw+nTp/H09Cz3tcsT3IwcOZKsrCzq1atX7ud1lJdffpmsrCyHPPfVDG7q1atHVlYWI0eOLNf5WVlZvPzyyxVcKiGqBwluhHCAQYMG8eOPP5KXl2e3fcGCBXTs2JGIiIhKKUdGRgYABoMBLy+vatW9Yym7m5sbXl5eDi7N5WVnZ2M2m0t9vE6nw8vLC4PBUK7n8/Lyws3NrVznClHdSXAjhAMMGzaMCxcuEBMTY92Wm5vLTz/9xPDhw4s8x2w2M2vWLFq2bImXlxfh4eE8/PDDXLx40XpMdHQ0e/bsYc2aNdbur759+wK2LrE1a9bw2GOPERYWRp06dez2XZpzs3TpUvr06UONGjXw9/enc+fORbY4XWr9+vV07twZLy8vGjZsyCeffFLomJJySi7NF7Hk1ezdu5fhw4cTFBREz5497fZdev748eP59ddfadWqFZ6enrRs2ZJly5YVeq7Vq1fTqVMnu7KWJo+nb9++LF68mBMnTlhf6+joaOs1dTod33//PS+//DK1a9fGx8eH1NRUkpKSeOaZZ2jdujV+fn74+/tz4403smPHjsu+PqNHj8bPz48zZ84wZMgQ/Pz8qFmzJs888wwmk6lUr+Hhw4cZPXo0gYGBBAQEMGbMGDIzM+3OzcrK4oknniA0NJQaNWpwyy23cObMGcnjEdWGhPVCOEB0dDTdunXju+++48YbbwRUIJGSksI999zD+++/X+ichx9+mPnz5zNmzBieeOIJjh07xocffsi2bdv4559/cHd3Z9asWTz++OP4+fnx0ksvARAeHm53nccee4yaNWsyefJka+tHUebPn8/9999Py5YtmThxIoGBgWzbto1ly5YVG4AB7Nq1i+uvv56aNWsydepU8vLymDJlSqFylMddd91F48aNee2119A0rcRj169fz6JFi3jssceoUaMG77//PnfccQcnT54kJCQEgG3btnHDDTcQGRnJtGnTMJlMTJ8+nZo1a162LC+99BIpKSmcPn2ad999FwA/Pz+7Y1555RU8PDx45plnyMnJwcPDg7179/Lrr79y1113Ub9+feLj4/nkk0/o06cPe/fupVatWiU+r8lkYuDAgXTt2pW3336bFStW8M4779CwYUMeffTRy5b77rvvpn79+syYMYOtW7fy+eefExYWxhtvvGE9ZvTo0fzwww+MHDmSa665hjVr1nDTTTdd9tpCVBmaEKLSfPHFFxqgbdmyRfvwww+1GjVqaJmZmZqmadpdd92l9evXT9M0TatXr5520003Wc9bt26dBmjffvut3fWWLVtWaHvLli21Pn36FPvcPXv21PLy8orcd+zYMU3TNC05OVmrUaOG1rVrVy0rK8vuWLPZXGIdhwwZonl5eWknTpywbtu7d69mMBi0gm85x44d0wDtiy++KHQNQJsyZYr18ZQpUzRAGzZsWKFjLfsuPd/Dw0M7fPiwdduOHTs0QPvggw+s2wYPHqz5+PhoZ86csW47dOiQ5ubmVuiaRbnpppu0evXqFdq+atUqDdAaNGhg/f1aZGdnayaTyW7bsWPHNE9PT2369Ol22y59fUaNGqUBdsdpmqa1b99e69ixY6HXoKjX8P7777c77rbbbtNCQkKsj2NjYzVAe+qpp+yOGz16dKFrClFVSbeUEA5y9913k5WVxZ9//klaWhp//vlnsS0iP/74IwEBAQwYMIDExETrT8eOHfHz82PVqlWlft6xY8deNo8jJiaGtLQ0XnjhhUL5LCV115hMJpYvX86QIUOoW7eudXvz5s0ZOHBgqctYnEceeaTUx/bv35+GDRtaH7dp0wZ/f3+OHj1qLeuK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ob311lvWbcnJyZqnp6f23XffVUYRK8xNN92k3X///Xbbbr/9dm3EiBGapjlXXQHtl19+sT4uTd327t2rAdqWLVusxyxdulTT6XTamTNnKq3sZXVpXYuyefNmDdBOnDihaZrz1fX06dNa7dq1td27d2v16tXT3n33Xeu+6lpXTSu6vkOHDtXuvffeYs+prPdol2m5yc3NJTY2lv79+1u36fV6+vfvz8aNGx1YsoqXkpIC2BYJjY2NxWg02tW9WbNm1K1bt9rWfdy4cdx00012dQLnq+vvv/9Op06duOuuuwgLC6N9+/Z89tln1v3Hjh0jLi7Orr4BAQF07dq12tW3e/furFy5koMHDwKwY8cO1q9fz4033gg4V10vVZq6bdy4kcDAQDp16mQ9pn///uj1ejZt2lTpZa5IKSkp6HQ6AgMDAeeqq9lsZuTIkTz77LO0bNmy0H5nq+vixYtp0qQJAwcOJCwsjK5du9p1XVXWe7TLBDeJiYmYTCbCw8PttoeHhxMXF+egUlU8s9nMU089RY8ePWjVqhUAcXFxeHh4WN84LKpr3b///nu2bt3KjBkzCu1ztroePXqU2bNn07hxY5YvX86jjz7KE088wZdffglgrZMz/F2/8MIL3HPPPTRr1gx3d3fat2/PU089xYgRIwDnquulSlO3uLg4wsLC7Pa7ubkRHBxcreufnZ3N888/z7Bhw6wLLDpTXd944w3c3Nx44oknitzvTHVNSEggPT2d119/nRtuuIG//vqL2267jdtvv501a9YAlfce7XKrgju7cePGsXv3btavX+/oolwVp06d4sknnyQmJsauD9dZmc1mOnXqxGuvvQZA+/bt2b17N3PmzGHUqFEOLl3F+uGHH/j2229ZsGABLVu2ZPv27Tz11FPUqlXL6eoqFKPRyN13342macyePdvRxalwsbGxvPfee2zduhWdTufo4lx1ZrMZgFtvvZWnn34agHbt2rFhwwbmzJlDnz59Kq0sLtNyExoaisFgKJSRHR8fT0REhINKVbHGjx/Pn3/+yapVq6hTp451e0REBLm5uSQnJ9sdXx3rHhsbS0JCAh06dMDNzQ03NzfWrFnD+++/j5ubG+Hh4U5TV4DIyEhatGhht6158+bWkQeWOjnD3/Wzzz5rbb1p3bo1I0eO5Omnn7a20DlTXS9VmrpFREQUGvyQl5dHUlJStay/JbA5ceIEMTEx1lYbcJ66rlu3joSEBOrWrWt9vzpx4gT/+9//iI6OBpynrqA+Z93c3C77nlUZ79EuE9x4eHjQsWNHVq5cad1mNptZuXIl3bp1c2DJrpymaYwfP55ffvmFv//+m/r169vt79ixI+7u7nZ1P3DgACdPnqx2db/uuuvYtWsX27dvt/506tSJESNGWO87S10BevToUWhY/8GDB6lXrx4A9evXJyIiwq6+qampbNq0qdrVNzMzE73e/i3JYDBYvw06U10vVZq6devWjeTkZGJjY63H/P3335jNZrp27VrpZb4SlsDm0KFDrFixgpCQELv9zlLXkSNHsnPnTrv3q1q1avHss8+yfPlywHnqCupztnPnziW+Z1Xa51GFpSZXA99//73m6empzZ8/X9u7d6/20EMPaYGBgVpcXJyji3ZFHn30US0gIEBbvXq1du7cOetPZmam9ZhHHnlEq1u3rvb3339r//33n9atWzetW7duDix1xSk4WkrTnKuumzdv1tzc3LT/+7//0w4dOqR9++23mo+Pj/bNN99Yj3n99de1wMBA7bffftN27typ3XrrrVr9+vW1rKwsB5a87EaNGqXVrl1b+/PPP7Vjx45pixYt0kJDQ7XnnnvOekx1rmtaWpq2bds2bdu2bRqgzZw5U9u2bZt1hFBp6nbDDTdo7du31zZt2qStX79ea9y4sTZs2DBHValYJdU1NzdXu+WWW7Q6depo27dvt3vPysnJsV7DGepalEtHS2la9amrpl2+vosWLdLc3d21Tz/9VDt06JD2wQcfaAaDQVu3bp31GpXxHu1SwY2madoHH3yg1a1bV/Pw8NC6dOmi/fvvv44u0hUDivz54osvrMdkZWVpjz32mBYUFKT5+Phot912m3bu3DnHFboCXRrcOFtd//jjD61Vq1aap6en1qxZM+3TTz+12282m7VJkyZp4eHhmqenp3bddddpBw4ccFBpyy81NVV78skntbp162peXl5agwYNtJdeesnuA68613XVqlVF/p+OGjVK07TS1e3ChQvasGHDND8/P83f318bM2aMlpaW5oDalKykuh47dqzY96xVq1ZZr+EMdS1KUcFNdamrppWuvnPnztUaNWqkeXl5aW3bttV+/fVXu2tUxnu0TtMKTP8phBBCCFHNuUzOjRBCCCFcgwQ3QgghhHAqEtwIIYQQwqlIcCOEEEIIpyLBjRBCCCGcigQ3QgghhHAqEtwIIYQQwqlIcCOEcAnR0dHMmjXL0cUQQlQCCW6EEBVu9OjRDBkyBIC+ffvy1FNPVdpzz58/n8DAwELbt2zZwkMPPVRp5RBCOI6bowsghBClkZubi4eHR7nPr1mzZgWWRghRlUnLjRDiqhn9/+3dXUjTXQDH8e9faVDOzSjpjdEIW7n6Z/YCZWCRozAKQUiTWGQQSHXhhXTVhUHZCyiZFUEQmXQR9ApCha1c4IWpvVA2KNQxoWEtM1rdlHueC3lGy+fFeNzzwPh94Fzs7Jyd/zkX48c5Z2z3bvx+P42NjRiGgWEYBINBAF6+fElxcTFWq5VZs2bh9XqJRCLxvhs2bODAgQNUV1czc+ZMNm/eDEBDQwOmaZKRkYHD4WDfvn1Eo1EA2tvbqays5NOnT/HxamtrgfHHUqFQiJKSEqxWKzabjbKyMoaGhuLv19bWsnz5clpaWnA6ndjtdnbs2MHnz5/jba5du4ZpmkydOpUZM2bg8Xj48uVLklZTRCZK4UZEkqaxsZG1a9eyd+9ewuEw4XAYh8PByMgIGzduJD8/n+7ubu7evcvQ0BBlZWUJ/Zubm7FYLHR0dHD+/HkA0tLSOH36NL29vTQ3N/PgwQMOHjwIQEFBAadOncJms8XHq6mpGfdcsViMkpIShoeH8fv9tLW10d/fT3l5eUK7vr4+bt26RWtrK62trfj9fo4fPw5AOBymoqKCPXv2EAgEaG9vp7S0FP1dn8j/T8dSIpI0drsdi8XCtGnTmD17drz+zJkz5OfnU1dXF6+7ePEiDoeD169f43K5AFi4cCEnT55M+Mwf7+84nU6OHDlCVVUV586dw2KxYLfbMQwjYbyf+Xw+Xrx4wcDAAA6HA4DLly+zZMkSurq6WL16NTAWgi5dukRmZiYAXq8Xn8/H0aNHCYfDfP/+ndLSUubPnw+AaZr/YrVEZLJo50ZE/nPPnz/n4cOHWK3WeFm8eDEwtlvyh5UrV47re//+fYqKipg3bx6ZmZl4vV4+fPjA169fJzx+IBDA4XDEgw2A2+0mKyuLQCAQr3M6nfFgAzBnzhzevXsHQF5eHkVFRZimyfbt27lw4QIfP36c+CKISNIo3IjIfy4ajbJt2zaePXuWUN68eUNhYWG8XUZGRkK/YDDI1q1bWbZsGdevX6enp4ezZ88CYxeOJ9uUKVMSXhuGQSwWAyA9PZ22tjbu3LmD2+2mqamJRYsWMTAwMOnPISK/RuFGRJLKYrEwOjqaULdixQp6e3txOp3k5OQklJ8DzY96enqIxWLU19ezZs0aXC4Xb9++/cfxfpabm8vg4CCDg4PxulevXjEyMoLb7Z7w3AzDYN26dRw+fJinT59isVi4efPmhPuLSHIo3IhIUjmdTjo7OwkGg0QiEWKxGPv372d4eJiKigq6urro6+vj3r17VFZW/m0wycnJ4du3bzQ1NdHf309LS0v8ovGP40WjUXw+H5FI5E+PqzweD6ZpsnPnTp48ecLjx4/ZtWsX69evZ9WqVROaV2dnJ3V1dXR3dxMKhbhx4wbv378nNzf31xZIRCadwo2IJFVNTQ3p6em43W6ys7MJhULMnTuXjo4ORkdH2bRpE6ZpUl1dTVZWFmlpf/21lJeXR0NDAydOnGDp0qVcuXKFY8eOJbQpKCigqqqK8vJysrOzx11IhrEdl9u3bzN9+nQKCwvxeDwsWLCAq1evTnheNpuNR48esWXLFlwuF4cOHaK+vp7i4uKJL46IJIXxm363KCIiIilEOzciIiKSUhRuREREJKUo3IiIiEhKUbgRERGRlKJwIyIiIilF4UZERERSisKNiIiIpBSFGxEREUkpCjciIiKSUhRuREREJKUo3IiIiEhKUbgRERGRlPI7Uovbmf+ANF0AAAAASUVORK5CYII=" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "
" - ], - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAwwAAAHHCAYAAAASz98lAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQAA88xJREFUeJzs3Xtczuf/wPHX3VHnlEhEDkkOEYYck0NEjtPQkDkMa05zyiHlmNPEmG1YOS4zhzE5FIvNMTbmmDHJNhaGVJNb3b8//Lq/bhV34U55Px+PHvpc9/W5Pu/Pu3Tf1+e6rs9HoVKpVAghhBBCCCFELvQKOwAhhBBCCCHEm0s6DEIIIYQQQog8SYdBCCGEEEIIkSfpMAghhBBCCCHyJB0GIYQQQgghRJ6kwyCEEEIIIYTIk3QYhBBCCCGEEHmSDoMQQgghhBAiT9JhEEIIIYQQQuRJOgxCCCHEWyQyMhKFQkFiYmJhhyKEKCKkwyCEEKJYy/6AnNvXxIkTX8sxDx8+TEhICPfu3Xst7b/N0tPTCQkJIS4urrBDEeKtYVDYAQghhBC6MH36dCpVqqRRVqtWrddyrMOHDxMaGkpAQADW1tav5RgF1bdvX3r16oWxsXFhh1Ig6enphIaGAuDp6Vm4wQjxlpAOgxBCiLdChw4daNCgQWGH8VLS0tIwMzN7qTb09fXR19d/RRHpTlZWFo8ePSrsMIR4K8mUJCGEEALYtWsXzZs3x8zMDAsLCzp27Mi5c+c06vz2228EBARQuXJlSpQogb29PR988AF37txR1wkJCWHcuHEAVKpUST39KTExkcTERBQKBZGRkTmOr1AoCAkJ0WhHoVBw/vx5+vTpQ8mSJWnWrJn69XXr1lG/fn1MTEywsbGhV69eXL9+/YXnmdsaBicnJzp16kRcXBwNGjTAxMSE2rVrq6f9bNmyhdq1a1OiRAnq16/Pr7/+qtFmQEAA5ubm/PHHH3h7e2NmZoaDgwPTp09HpVJp1E1LS+OTTz7B0dERY2NjXFxcWLBgQY56CoWCwMBA1q9fT82aNTE2NuaLL77Azs4OgNDQUHVus/Omzc/n6dxevnxZPQpkZWXFgAEDSE9Pz5GzdevW0bBhQ0xNTSlZsiQtWrRg7969GnW0+f0RoqiSEQYhhBBvhfv373P79m2NslKlSgGwdu1a+vfvj7e3N3PnziU9PZ3ly5fTrFkzfv31V5ycnACIiYnhjz/+YMCAAdjb23Pu3Dm++uorzp07x9GjR1EoFHTv3p1Lly7xzTffsGjRIvUx7OzsuHXrVr7j7tmzJ87OzsyePVv9oXrWrFlMnToVPz8/Bg0axK1bt/jss89o0aIFv/76a4GmQV2+fJk+ffrw4Ycf8v7777NgwQJ8fX354osvmDRpEsOHDwdgzpw5+Pn5kZCQgJ7e/647ZmZm0r59exo3bsy8efPYvXs306ZN4/Hjx0yfPh0AlUpF586d+fHHHxk4cCB169Zlz549jBs3jr/++otFixZpxLR//36+/fZbAgMDKVWqFHXq1GH58uUMGzaMbt260b17dwDc3NwA7X4+T/Pz86NSpUrMmTOHX375hZUrV1K6dGnmzp2rrhMaGkpISAhNmjRh+vTpGBkZcezYMfbv30+7du0A7X9/hCiyVEIIIUQxFhERoQJy/VKpVKoHDx6orK2tVYMHD9bY7+bNmyorKyuN8vT09Bztf/PNNypAdfDgQXXZ/PnzVYDq6tWrGnWvXr2qAlQRERE52gFU06ZNU29PmzZNBah69+6tUS8xMVGlr6+vmjVrlkb5mTNnVAYGBjnK88rH07FVrFhRBagOHz6sLtuzZ48KUJmYmKiuXbumLv/yyy9VgOrHH39Ul/Xv318FqD7++GN1WVZWlqpjx44qIyMj1a1bt1QqlUq1bds2FaCaOXOmRkzvvvuuSqFQqC5fvqyRDz09PdW5c+c06t66dStHrrJp+/PJzu0HH3ygUbdbt24qW1tb9fbvv/+u0tPTU3Xr1k2VmZmpUTcrK0ulUuXv90eIokqmJAkhhHgrLFu2jJiYGI0veHJV+t69e/Tu3Zvbt2+rv/T19WnUqBE//vijug0TExP19w8fPuT27ds0btwYgF9++eW1xD106FCN7S1btpCVlYWfn59GvPb29jg7O2vEmx81atTAw8NDvd2oUSMAvLy8qFChQo7yP/74I0cbgYGB6u+zpxQ9evSI2NhYAKKjo9HX12fEiBEa+33yySeoVCp27dqlUd6yZUtq1Kih9Tnk9+fzbG6bN2/OnTt3SElJAWDbtm1kZWURHBysMZqSfX6Qv98fIYoqmZIkhBDirdCwYcNcFz3//vvvwJMPxrmxtLRUf//vv/8SGhpKVFQUycnJGvXu37//CqP9n2fv7PT777+jUqlwdnbOtb6hoWGBjvN0pwDAysoKAEdHx1zL7969q1Gup6dH5cqVNcqqVasGoF4vce3aNRwcHLCwsNCo5+rqqn79ac+e+4vk9+fz7DmXLFkSeHJulpaWXLlyBT09ved2WvLz+yNEUSUdBiGEEG+1rKws4Mk8dHt7+xyvGxj8763Sz8+Pw4cPM27cOOrWrYu5uTlZWVm0b99e3c7zPDuHPltmZmae+zx91Tw7XoVCwa5du3K925G5ufkL48hNXndOyqtc9cwi5dfh2XN/kfz+fF7FueXn90eIokp+i4UQQrzVqlSpAkDp0qVp06ZNnvXu3r3Lvn37CA0NJTg4WF2efYX5aXl1DLKvYD/7QLdnr6y/KF6VSkWlSpXUV/DfBFlZWfzxxx8aMV26dAlAvei3YsWKxMbG8uDBA41RhosXL6pff5G8cpufn4+2qlSpQlZWFufPn6du3bp51oEX//4IUZTJGgYhhBBvNW9vbywtLZk9ezZKpTLH69l3Nsq+Gv3s1efw8PAc+2Q/K+HZjoGlpSWlSpXi4MGDGuWff/651vF2794dfX19QkNDc8SiUqly3EJUl5YuXaoRy9KlSzE0NKR169YA+Pj4kJmZqVEPYNGiRSgUCjp06PDCY5iamgI5c5ufn4+2unbtip6eHtOnT88xQpF9HG1/f4QoymSEQQghxFvN0tKS5cuX07dvX+rVq0evXr2ws7MjKSmJnTt30rRpU5YuXYqlpSUtWrRg3rx5KJVKypUrx969e7l69WqONuvXrw/A5MmT6dWrF4aGhvj6+mJmZsagQYMICwtj0KBBNGjQgIMHD6qvxGujSpUqzJw5k6CgIBITE+natSsWFhZcvXqVrVu3MmTIEMaOHfvK8qOtEiVKsHv3bvr370+jRo3YtWsXO3fuZNKkSepnJ/j6+tKqVSsmT55MYmIiderUYe/evXz//feMGjVKfbX+eUxMTKhRowYbN26kWrVq2NjYUKtWLWrVqqX1z0dbVatWZfLkycyYMYPmzZvTvXt3jI2NiY+Px8HBgTlz5mj9+yNEkVZId2cSQgghdCL7NqLx8fHPrffjjz+qvL29VVZWVqoSJUqoqlSpogoICFCdOHFCXefPP/9UdevWTWVtba2ysrJS9ezZU/X333/nepvPGTNmqMqVK6fS09PTuI1penq6auDAgSorKyuVhYWFys/PT5WcnJznbVWzb0n6rM2bN6uaNWumMjMzU5mZmamqV6+u+uijj1QJCQla5ePZ26p27NgxR11A9dFHH2mUZd8adv78+eqy/v37q8zMzFRXrlxRtWvXTmVqaqoqU6aMatq0aTluR/rgwQPV6NGjVQ4ODipDQ0OVs7Ozav78+erblD7v2NkOHz6sql+/vsrIyEgjb9r+fPLKbW65UalUqq+//lrl7u6uMjY2VpUsWVLVsmVLVUxMjEYdbX5/hCiqFCqVDlYtCSGEEKLYCggI4LvvviM1NbWwQxFCvAayhkEIIYQQQgiRJ+kwCCGEEEIIIfIkHQYhhBBCCCFEnmQNgxBCCCGEECJPMsIghBBCCCGEyJN0GIQQQgghhBB5kge3CSFeSlZWFn///TcWFhYoFIrCDkcIIYQQWlCpVDx48AAHBwf09J4/hiAdBiHES/n7779xdHQs7DCEEEIIUQDXr1+nfPnyz60jHQYhxEuxsLAA4OrVq9jY2BRyNMWXUqlk7969tGvXDkNDw8IOp9iSPOuO5Fo3JM+6URTznJKSgqOjo/p9/HmkwyCEeCnZ05AsLCywtLQs5GiKL6VSiampKZaWlkXmzagokjzrjuRaNyTPulGU86zNdGJZ9CyEEEIIIYTIk3QYhBBCCCGEEHmSDoMQQgghhBAiT9JhEEIIIYQQQuRJOgxCCCGEEEKIPEmHQQghhBBCCJEn6TAIIYQQQggh8iQdBiGEEEIIIUSepMMghBBCCCGEyJN0GIQQQgghhNCRkJAQFAqFxlf16tXVrz98+JCPPvoIW1tbzM3N6dGjB//8808hRiwdBiGKnbi4OBQKBffu3SvsUIQQQgiRi5o1a3Ljxg31188//6x+bfTo0ezYsYNNmzZx4MAB/v77b7p3716I0YJBoR5dCFFoHj16hJGRUWGHIYQQQrx1DAwMsLe3z1F+//59Vq1axYYNG/Dy8gIgIiICV1dXjh49SuPGjXUdKiAjDOItsnv3bpo1a4a1tTW2trZ06tSJK1euAE8+PAcGBlK2bFlKlChBxYoVmTNnDgAffPABnTp10mhLqVRSunRpVq1aBYCnpycff/wxo0aNomTJkpQpU4YVK1aQlpbGgAEDsLCwoGrVquzatUvdRvZIwJ49e3B3d8fExAQvLy+Sk5PZtWsXrq6uWFpa0qdPH9LT09X7ZWVlMWfOHCpVqoSJiQl16tThu+++AyAxMZFWrVoBULJkSRQKBQEBAeoYAwMDGTVqFKVKlcLb21urcxNCCCHEq/X777/j4OBA5cqV8ff3JykpCYCTJ0+iVCpp06aNum716tWpUKECR44cKaxwZYRBvD3S0tIYM2YMbm5upKamEhwcTLdu3Th16hRLlixh+/btfPvtt1SoUIHr169z/fp1AAYNGkSLFi24ceMGZcuWBeCHH34gPT2d9957T93+6tWrGT9+PMePH2fjxo0MGzaMrVu30q1bNyZNmsSiRYvo27cvSUlJmJqaqvcLCQlh6dKlmJqa4ufnh5+fH8bGxmzYsIHU1FS6devGZ599xoQJEwCYM2cO69at44svvsDZ2ZmDBw/y/vvvY2dnR7Nmzdi8eTM9evQgISEBS0tLTExMNGIcNmwYhw4dAuDOnTtanZs2Gs3Zx2MDswL8ZIQ2jPVVzGsItUL2kJGpKOxwii3Js+5IrnVD8qwb2XnWRqNGjYiMjMTFxYUbN24QGhpK8+bNOXv2LDdv3sTIyAhra2uNfcqUKcPNmzdffeBakg6DeGv06NFDY/vrr7/Gzs6O8+fPk5SUhLOzM82aNUOhUFCxYkV1vSZNmuDi4sLatWsZP3488GR4sGfPnpibm6vr1alThylTpgAQFBREWFgYpUqVYvDgwQAEBwezfPlyfvvtN40hxZkzZ9K0aVMABg4cSFBQEFeuXKFy5coAvPvuu/z4449MmDCBjIwMZs+eTWxsLB4eHgBUrlyZn3/+mS+//JKWLVtiY2MDQOnSpXP8wXF2dmbevHkaZdqc29MyMjLIyMhQb6ekpABgrKdCX1+Ve/LFSzPWU2n8K14PybPuSK51Q/KsG9n5VSqVL6z79OiBq6sr9erVo2rVqnzzzTeUKFEi13ZUKhWZmZlata+t/LQlHQbx1vj9998JDg7m2LFj3L59m6ysLACSkpIICAigbdu2uLi40L59ezp16kS7du3U+w4aNIivvvqK8ePH888//7Br1y7279+v0b6bm5v6e319fWxtbaldu7a6rEyZMgAkJyfnuV+ZMmUwNTVVdxayy44fPw7A5cuXSU9Pp23bthptPHr0CHd39xfmoH79+jnKtDm3p82ZM4fQ0NAc5VPcszA1zXxhDOLlzGiQVdghvBUkz7ojudYNybNuxMTEFGi/0qVLs3fvXurUqcOjR4/49ttvNS7cXbt2jbt37xIdHf2qQtWY7vwi0mEQbw1fX18qVqzIihUrcHBwICsri1q1avHo0SPq1avH1atX2bVrF7Gxsfj5+dGmTRv12oB+/foxceJEjhw5wuHDh6lUqRLNmzfXaN/Q0FBjW6FQaJQpFE+GgrM7Krnt9+w+2WXZ+6SmpgKwc+dOypUrp1HP2Nj4hTkwM8s5ZUibc3taUFAQY8aMUW+npKTg6OhIq1atsLW1fWEMomCUSiUxMTG0bds2x++IeHUkz7ojudYNybNuvEyeU1NTuXPnDk2bNqVv377MmDEDAwMDfHx8AEhISODWrVsMGDCARo0avbKYs2cIaEM6DOKtcOfOHRISElixYoX6w/DTtzADsLS05L333uO9997j3XffpX379vz777/Y2Nhga2tL165diYiI4MiRIwwYMKAwToMaNWpgbGxMUlISLVu2zLVO9p2PMjO1u9qf33MzNjbOtXNiaGgob0Y6IHnWDcmz7kiudUPyrBva5Hns2LHqi5h///0306ZNQ19fn/fff59SpUoxcOBAxo8fT+nSpbG0tOTjjz/Gw8ODZs2avfJYtSUdBvFWKFmyJLa2tnz11VeULVuWpKQkJk6cqH79008/pWzZsri7u6Onp8emTZuwt7fXWAMwaNAgOnXqRGZmJv379y+EswALCwvGjh3L6NGjycrKolmzZty/f59Dhw5haWlJ//79qVixIgqFgh9++AEfHx9MTEzyXI+Q7U04NyGEEOJt8Oeff9K7d2/u3LmjvmHJ0aNHsbOzA2DRokXo6enRo0cPMjIy8Pb25vPPPy/UmKXDIN4Kenp6REVFMWLECGrVqoWLiwtLlizB09MTePJBfN68efz+++/o6+vzzjvvEB0djZ7e/+483KZNG8qWLUvNmjVxcHAopDOBGTNmYGdnx5w5c/jjjz+wtramXr16TJo0CYBy5coRGhrKxIkTGTBgAP369SMyMvK5bb4p5yaEEEIUd1FRUc99vUSJEixbtoxly5bpKKIXU6hUKlk2L4QWUlNTKVeuHBEREYX+xMVX7WXOLSUlBSsrK27fvi1rGF4jpVJJdHQ0Pj4+Mq3gNZI8647kWjckz7pRFPOc/f59//59LC0tn1tXRhiEeIGsrCxu377NwoULsba2pnPnzoUd0itTnM9NCCGEEK+GdBiEeIGkpCQqVapE+fLliYyMxMCg+Py3Kc7nJoQQQohXQz4dCPECTk5OFNeZe8X53IQQQgjxaui9uIoQQgghhBDibSUdBiGEEEIIIUSepMMghBBCCCGEyJN0GF6BxMREFAoFp06dKuxQxHN4enoyatSowg5DCCGEEIUoLCwMhUKR4zPBkSNH8PLywszMDEtLS1q0aMF///1XOEG+YaTD8BwBAQF07dq1sMMAIC4uDoVCwb179wo7FABCQkJQKBQaX9WrV893O5UqVSI2NrbAcbxpeXFyciI8PLyww1D79ttvqVu3LqamplSsWJH58+fnqBMXF0e9evUwNjamatWqL3zImxBCCFFUxcfH8+WXX+Lm5qZRfuTIEdq3b0+7du04fvw48fHxBAYGajzA9W0md0kqZCqViszMTJ3ezlKpVL6Sh4rUrFlT48N+fs/ht99+4+7du7Rs2fKlY3ndHj16hJGRUZE63q5du/D39+ezzz6jXbt2XLhwgcGDB2NiYkJgYCAAV69epWPHjgwdOpT169ezb98+Bg0aRNmyZfH29n4VpyKEEEK8EVJTU/H392fFihXMnDlT47XRo0czYsQIJk6cqC5zcXHRdYhvLOk2Ad999x21a9fGxMQEW1tb2rRpw7hx41i9ejXff/+9+gp6XFwcAMePH8fd3Z0SJUrQoEEDfv31V62PlX1FfNeuXdSvXx9jY2N+/vlnsrKymDNnDpUqVcLExIQ6derw3XffAU+mPLVq1QqAkiVLolAoCAgIAHK/ol23bl1CQkLU2wqFguXLl9O5c2fMzMyYNWsWISEh1K1bl7Vr1+Lk5ISVlRW9evXiwYMHWp+LgYEB9vb26q9SpUppvS/A999/T/v27V/Yebl27Rq+vr6ULFkSMzMzatasSXR09HPzkpaWRr9+/TA3N6ds2bIsXLgwX7E5OTkxY8YM+vXrh6WlJUOGDAHg559/pnnz5piYmODo6MiIESNIS0sDnkx5unbtGqNHj1b/zgDqXD8tPDwcJycn9Xb2aNasWbNwcHDAxcVFPdVty5YttGrVClNTU+rUqcORI0e0Ooe1a9fStWtXhg4dSuXKlenYsSNBQUHMnTtXfSvVL774gkqVKrFw4UJcXV0JDAzk3XffZdGiRfnKlxBCCPGm++ijj+jYsSNt2rTRKE9OTubYsWOULl2aJk2aUKZMGVq2bMnPP/9cSJG+ed76EYYbN27Qu3dv5s2bR7du3Xjw4AE//fQT/fr1IykpiZSUFCIiIgCwsbEhNTWVTp060bZtW9atW8fVq1cZOXJkvo87ceJEFixYQOXKlSlZsiRz5sxh3bp1fPHFFzg7O3Pw4EHef/997OzsaNasGZs3b6ZHjx4kJCRgaWmJiYlJvo4XEhJCWFgY4eHhGBgY8PXXX3PlyhW2bdvGDz/8wN27d/Hz8yMsLIxZs2Zp1ebvv/+Og4MDJUqUwMPDgzlz5lChQgWtY9q+fTtjxox5Yb2PPvqIR48ecfDgQczMzDh//jzm5uY4OjrmmZdx48Zx4MABvv/+e0qXLs2kSZP45Zdfcnxwf54FCxYQHBzMtGnTALhy5Qrt27dn5syZfP3119y6dYvAwEACAwOJiIhgy5Yt1KlThyFDhjB48GCtj5Nt3759WFpaEhMTo1E+efJkFixYgLOzM5MnT6Z3795cvnz5hSM6GRkZmJqaapSZmJjw559/cu3aNZycnDhy5EiOP5ze3t7PXeuRkZFBRkaGejslJQWAFnNjeWxops2pigIw1lMxowHUn76bjCxFYYdTbEmedUdyrRvFOc9nQ7QfCd+4cSMnT57kyJEjKJVKVCoVWVlZKJVKLl26BDz5rDR37lzc3NxYv349rVu35tdff8XZ2fmF7SuVSo1/i4L8xCodhhs3ePz4Md27d6dixYoA1K5dG3jy4SojIwN7e3t1/cjISLKysli1ahUlSpSgZs2a/PnnnwwbNixfx50+fTpt27YFnnwAmz17NrGxsXh4eABQuXJlfv75Z7788ktatmyJjY0NAKVLl8ba2jrf59mnTx8GDBigUZaVlUVkZCQWFhYA9O3bl3379mnVYWjUqBGRkZG4uLhw48YNQkNDad68OWfPnlW39zx//fUXv/32Gx06dHhh3aSkJHr06KH+uVSuXFn9Wm55SU1NZdWqVaxbt47WrVsDsHr1asqXL//CYz3Ny8uLTz75RL09aNAg/P391R+mnZ2dWbJkCS1btmT58uXY2Nigr6+PhYWFxu+MtszMzFi5cqV6KlJiYiIAY8eOpWPHjgCEhoZSs2ZNLl++/MI1I97e3owePZqAgABatWrF5cuX1SMtN27cwMnJiZs3b1KmTBmN/cqUKUNKSgr//fdfrh3TOXPmEBoamqN8insWpqaZ+T5vkT8zGmQVdghvBcmz7kiudaM45jk6Olqrerdu3WLs2LGEhoayf/9+AO7cucPVq1eJjo7m4sWLALRq1Qo7Oztu3LiBl5cX33//PcHBwfTt21frmJ696PcmS09P17ruW99hqFOnDq1bt6Z27dp4e3vTrl073n33XUqWLJlr/QsXLuDm5kaJEiXUZdkf8vOjQYMG6u8vX75Menq6ugOR7dGjR7i7u+e77RcdL5uTk5PGh/uyZcuSnJysVXtPf9B3c3OjUaNGVKxYkW+//ZaBAwe+cP/t27fTrFkzrTo/I0aMYNiwYezdu5c2bdrQo0ePHIuVnnblyhUePXpEo0aN1GU2Njb5nov4bM5Onz7Nb7/9xvr169Vl2Vcorl69iqura77af1bt2rVzXbfw9LmWLVsWeDJ8+qIOw+DBg7ly5QqdOnVCqVRiaWnJyJEjCQkJealFXEFBQRojQykpKTg6OjLzVz0eG+oXuF3xfE+uEmYx9YResbtK+CaRPOuO5Fo3inOetR1h+P7777l//77GRcDMzEzOnz/Prl27OHv2LBMnTqRTp074+Pio66xbtw4DAwONsrwolUpiYmJo27btK1knqgvZMwS08dZ3GPT19YmJieHw4cPs3buXzz77jMmTJ3Ps2LHXelwzs/9N3UhNTQVg586dlCtXTqOesbHxc9vR09NTz0fPltsQ09PHy/bsL7RCoSArq2BXIKytralWrRqXL1/Wqv727dvp3LmzVnUHDRqEt7c3O3fuZO/evcyZM4eFCxfy8ccfFyhWbT2bs9TUVD788ENGjBiRo+7zpmK9zM8INH9O2esitPk5KRQK5s6dy+zZs7l58yZ2dnbs27cP+N8ojb29Pf/884/Gfv/8889zp70ZGxvn+nt5cEIbbG1tXxiXKBilUkl0dDQng1+87kcUnORZdyTXuiF5fjLifubMGY2yAQMGUL16dSZMmICLiwsODg5cuXJFI0eXL1+mQ4cO+cqboaFhkclzfuJ86zsM8OSDVdOmTWnatCnBwcFUrFiRrVu3YmRkRGam5hQLV1dX1q5dy8OHD9WjDEePHn2p49eoUQNjY2OSkpLyvGNQ9pXnZ+PJHjrLlpKSwtWrV18qnoJITU3lypUrWg3bpaam8uOPP7J8+XKt23d0dGTo0KEMHTqUoKAgVqxYwccff5xrXqpUqYKhoSHHjh1Tf5C/e/culy5deqk7MtWrV4/z589TtWrVPOvk9jtjZ2fHzZs3UalU6g/8unxmh76+vroj+s033+Dh4YGdnR3wZHTs2SHdmJiYAo2aCSGEEG8iCwsLatWqpVFmZmaGra2tunzcuHFMmzaNOnXqULduXVavXs3FixfVN6B52731HYZjx46xb98+2rVrR+nSpTl27Bi3bt3C1dWVhw8fsmfPHhISErC1tcXKyoo+ffowefJkBg8eTFBQEImJiSxYsOClYrCwsGDs2LGMHj2arKwsmjVrxv379zl06BCWlpb079+fihUrolAo+OGHH/Dx8cHExARzc3O8vLyIjIzE19cXa2trgoOD0dd//dNCxo4di6+vLxUrVuTvv/9m2rRp6Ovr07t37xfuu3v3bqpVq6Zxl6DnGTVqFB06dKBatWrcvXuXH3/8UT39J6+8DBw4kHHjxmFra0vp0qWZPHnyS99LecKECTRu3JjAwEAGDRqkXoAdExPD0qVLgSfTvA4ePEivXr0wNjamVKlSeHp6cuvWLebNm8e7777L7t272bVrF5aWli8Vz4vcvn2b7777Dk9PTx4+fEhERASbNm3iwIED6jpDhw5l6dKljB8/ng8++ID9+/fz7bffsnPnztcamxBCCPEmGTVqFA8fPmT06NH8+++/1KlTh5iYGKpUqVLYob0R3vrbqlpaWnLw4EF8fHyoVq0aU6ZMYeHChXTo0IHBgwfj4uJCgwYNsLOz49ChQ5ibm7Njxw7OnDmDu7s7kydPZu7cuS8dx4wZM5g6dSpz5szB1dWV9u3bs3PnTipVqgRAuXLlCA0NZeLEiZQpU0Z9H/2goCBatmxJp06d6NixI127dtXJL/eff/5J7969cXFxwc/PD1tbW44ePaq+cv0833//vdbTkeDJ6MFHH32kzku1atX4/PPPgbzzMn/+fJo3b46vry9t2rShWbNm1K9fv2An+//c3Nw4cOAAly5donnz5ri7uxMcHIyDg4O6zvTp00lMTKRKlSrqXLi6uvL555+zbNky6tSpw/Hjxxk7duxLxaKt1atX06BBA5o2bcq5c+eIi4ujYcOG6tcrVarEzp07iYmJoU6dOixcuJCVK1fKMxiEEEIUa3FxcTluSz9x4kSuX79OWloahw8fplmzZoUT3BtIoXp2crUQr9Hjx48pU6YMu3bt0vjgKoqulJQUrKysuH37tqxheI2y5yH7+PgUmfmxRZHkWXck17ohedaNopjn7Pfv+/fvv3DWw1s/wiB0699//2X06NG88847hR2KEEIIIYTQgnQYXrGhQ4dibm6e69fQoUMLOzytJCUl5XkO5ubmJCUlPXf/9evX57lvq1atmDJlinrxLzy5RWte9WfPnv3Kz++nn3567vkVFbrOmxBCCCHeTm/9oudXbfr06XnOT3/di1xfFQcHh+fexefpOfu56dy5s8YzEJ6W2zDdypUr+e+//3Ktn/1gtlepQYMGOr1L0eui67wJIYQQ4u0kHYZXrHTp0pQuXbqww3gpBgYGz7116ItYWFho9bTnbM8+e+J1MzExeanze1PoOm9CCCGEeDvJlCQhhBBCCCFEnqTDIIQQQggh3hphYWEoFApGjRqlUX7kyBG8vLwwMzPD0tKSFi1a5Dn1920jHQYhXoG4uDgUCgX37t0r7FC04uTklOP+00IIIURxFx8fz5dffombm5tG+ZEjR2jfvj3t2rXj+PHjxMfHExgY+NIPfS0uJAuiyPL09MxxdaA4UigUbNu27ZW2GR8fz5AhQ15pm0IIIcSbLDU1FX9/f1asWEHJkiU1Xhs9ejQjRoxg4sSJ1KxZU/1gWmNj40KK9s0iHQbxRnr06FGxPFZ+vM647OzsMDU1fW3tCyGEEG+ajz76iI4dO9KmTRuN8uTkZI4dO0bp0qVp0qQJZcqUoWXLlvz888+FFOmbR+6SJN4Inp6e1KpVCwMDA9atW0ft2rX57LPPGDduHD/99BNmZma0a9eORYsWUapUKQICAjhw4AAHDhxg8eLFAFy9epW4uDhGjRqlMTVo27ZtdOvWjeyHmoeEhLBt2zYCAwOZNWsW165dIysrC4VCwYoVK9i5cyd79uyhXLlyLFy4kM6dO+f7fNLT0+nRowcpKSns3LlTHdPTIwWjRo3i1KlTxMXF5ZmDq1evAtCtWzcAKlasSGJiIgDLly9nwYIFXL9+nUqVKjFlyhT69u0LgEqlIjQ0lK+//pp//vkHW1tb3n33XZYsWQI8mZI0atQoRo0a9cK62mo0Zx+PDczynSuhHWN9FfMaQq2QPWRkKl68gygQybPuSK51ozjnOTGso9Z1o6Ki+OWXX4iPj8/x2h9//AE8+XywYMEC6taty5o1a2jdujVnz57F2dn5lcVcVEmHQbwxVq9ezbBhwzh06BD37t3Dy8uLQYMGsWjRIv777z8mTJiAn58f+/fvZ/HixVy6dIlatWoxffp04MlVc21dvnyZzZs3s2XLFvT19dXloaGhzJs3j/nz5/PZZ5/h7+/PtWvX8vVcg3v37tGxY0fMzc2JiYnJ15X8p3MAT56nULp0aSIiImjfvr061q1btzJy5EjCw8Np06YNP/zwAwMGDKB8+fK0atWKzZs3s2jRIqKioqhZsyY3b97k9OnTuR4zP3UBMjIyyMjIUG+npKQAYKynQl9fpfW5ivwx1lNp/CteD8mz7kiudaM451mpVGpV7/r164wcOZLo6Gj09fVRKpWoVCqysrJQKpXqEf1Bgwbx/vvvAzBv3jxiY2NZsWIFs2bN0joWbWN6E+QnVukwiDeGs7Mz8+bNA2DmzJm4u7trPLH466+/xtHRkUuXLlGtWjWMjIwwNTXF3t4+38d69OgRa9asydHJCAgIoHfv3gDMnj2bJUuWcPz4cdq3b69Vuzdv3uS9997D2dmZDRs2YGRklK+4ns7B06ytrTXOc8GCBQQEBDB8+HAAxowZw9GjR1mwYAGtWrUiKSkJe3t72rRpg6GhIRUqVKBhw4a5HjM/dQHmzJlDaGhojvIp7lmYmmbm63xF/s1okFXYIbwVJM+6I7nWjeKY5+joaK3qHT16lOTkZI33tqysLH766SeWLVvGsmXLgCefDZ5u08rKimPHjml9HICYmBit6xa29PR0retKh0G8MerXr6/+/vTp0/z444+Ym5vnqHflyhWqVav2UseqWLFiriMST981Ifu2asnJyVq327ZtWxo2bMjGjRs1Ri609XQOnufChQs5Fi03bdpUPT2rZ8+ehIeHU7lyZdq3b4+Pjw++vr4YGOT8L5+fugBBQUGMGTNGvZ2SkoKjoyMzf9XjsWH+z1lox1hPxYwGWUw9oUdGVvGaVvAmkTzrjuRaN4pzns+GeGtVr3nz5vj5+WmUDR48GBcXF8aOHUvNmjWZMWMGJiYm+Pj4qOtMmzYNb29vjbK8KJVKYmJiaNu2LYaGhvk7kUKSPUNAG9JhEG8MM7P/zX9PTU3F19eXuXPn5qhXtmzZPNvQ09NTr1XIltuQ29PHetqz/8kVCgVZWdpflenYsSObN2/m/Pnz1K5d+5XFlV+Ojo4kJCQQGxtLTEwMw4cPZ/78+Rw4cCDHOeanLoCxsXGud404OKENtra2ryR+kZNSqSQ6OpqTwe2LzJtRUSR51h3JtW5Inp9M7312arG5uTl2dna4u7sDMG7cOKZNm0a9evWoW7cuq1evJiEhgc2bN+crb4aGhkUmz/mJUzoM4o1Ur149Nm/ejJOTU55Xuo2MjMjM1JwCY2dnx4MHD0hLS1N/+D516tTrDlctLCwMc3NzWrduTVxcHDVq1FDHdfbsWY26p06d0uo/q6GhYY7zdHV15dChQ/Tv319ddujQIfXxAExMTPD19cXX15ePPvqI6tWrc+bMGerVq5fjGPmpK4QQQhQ3o0aN4uHDh4wePZp///2XOnXqEBMTQ5UqVQo7tDeCdBjEG+mjjz5ixYoV9O7dm/Hjx2NjY8Ply5eJiopi5cqV6Ovr4+TkxLFjx0hMTMTc3BwbGxsaNWqEqakpkyZNYsSIERw7dozIyEidxr5gwQIyMzPx8vIiLi6O6tWr4+Xlxfz581mzZg0eHh6sW7eOs2fPqq9sPI+TkxP79u2jadOmGBsbU7JkScaNG4efnx/u7u60adOGHTt2sGXLFmJjYwGIjIwkMzNTnY9169ZhYmJCxYoVc7Sfn7pCCCFEcZB9h8KnTZw4kYkTJ+o+mCJAnsMg3kgODg4cOnSIzMxM2rVrR+3atRk1ahTW1tbqpy6OHTsWfX19atSogZ2dHUlJSdjY2LBu3Tqio6OpXbs233zzDSEhITqPf9GiRfj5+eHl5cWlS5fw9vZm6tSpjB8/nnfeeYcHDx7Qr18/rdpauHAhMTExODo6qjsYXbt2ZfHixSxYsICaNWvy5ZdfEhERgaenJ/BkkfSKFSto2rQpbm5uxMbGsmPHjlynDOWnrhBCCCHePgrVsxOrhRAiH1JSUrCysuL27dvSyXiNsuch+/j4FJn5sUWR5Fl3JNe6IXnWjaKY5+z37/v372NpafncujLCIIQQQgghhMiTdBiE0MLQoUMxNzfP9Wvo0KGFHZ4QQgghxGsji56F0ML06dMZO3Zsrq+9aBhPCCGEEKIokw6DEFooXbo0pUuXLuwwhBBCCCF0TqYkCSGEEEIIIfIkHQYh3gKRkZFYW1sXdhhCCCGKkbCwMBQKBaNGjVKXffXVV3h6emJpaYlCoeDevXuFFp94daTDIIQQQggh8iU+Pp4vv/wSNzc3jfL09HTat2/PpEmTCiky8TpIh0GIYk6pVBZov0ePHr3iSIQQQhQHqamp+Pv7s2LFCkqWLKnx2qhRo5g4cSKNGzcupOjE6yAdBiGKmN27d9OsWTOsra2xtbWlU6dOXLlyBYDExEQUCgUbN26kZcuWlChRgvXr16v33bZtG87OzpQoUQJvb2+uX7+ufi0kJIS6deuycuVKKlWqRIkSJXR+bkIIId58H330ER07dqRNmzaFHYrQEblLkhBFTFpaGmPGjMHNzY3U1FSCg4Pp1q0bp06dUteZOHEiCxcuxN3dnRIlSrBnzx7S09OZNWsWa9aswcjIiOHDh9OrVy8OHTqk3u/y5cts3ryZLVu2oK+vn6+4Gs3Zx2MDs1d1muIZxvoq5jWEWiF7yMhUFHY4xZbkWXck17qhbZ4Twzpq1V5UVBS//PIL8fHxrypEUQRIh0GIIqZHjx4a219//TV2dnacP38ec3Nz4MmQcPfu3TXqKZVKli5dSqNGjQBYvXo1rq6uHD9+nIYNGwJPpiGtWbMGOzu7PI+fkZFBRkaGejslJQUAYz0V+vqqlz9BkStjPZXGv+L1kDzrjuRaN7TNszbTV69fv87IkSOJjo5GX18fpVKJSqUiKysrx/6PHz9Wt1vQqbFFSfY5FqVzzU+s0mEQooj5/fffCQ4O5tixY9y+fZusrCwAkpKSqFGjBgANGjTIsZ+BgQHvvPOOert69epYW1tz4cIFdYehYsWKz+0sAMyZM4fQ0NAc5VPcszA1zSzweQntzGiQVdghvBUkz7ojudaNF+U5Ojr6hW0cPXqU5ORk9XsGQFZWFj/99BPLli1j06ZN6tHpM2fOALB37171xay3QUxMTGGHoLX09HSt60qHQYgixtfXl4oVK7JixQocHBzIysqiVq1aGouUzcwKNjVIm/2CgoIYM2aMejslJQVHR0datWqFra1tgY4rXkypVBITE0Pbtm0xNDQs7HCKLcmz7kiudeNV5rl58+b4+flplA0ePBgXFxfGjh1LrVq11OXZ7yft2rV7K27rXRR/n7NnCGhDOgxCFCF37twhISGBFStW0Lx5cwB+/vlnrfZ9/PgxJ06cUF8ZSkhI4N69e7i6uuYrBmNjY4yNjXOUGxoaFpk/kkWZ5Fk3JM+6I7nWjVeRZxsbG2xsbDTKzM3NsbOzw93dHYCbN29y8+ZNEhMTAbh48SIWFhZUqFAhx77FUVH6fc5PnHKXJCGKkJIlS2Jra8tXX33F5cuX2b9/v8bV/ucxNDTk448/5tixY5w8eZKAgAAaN26sMbQshBBCvIwvvvgCd3d3Bg8eDECLFi1wd3dn+/bthRyZeBkywiBEEaKnp0dUVBQjRoygVq1auLi4sGTJEjw9PV+4r6mpKRMmTKBPnz789ddfNG/enFWrVr3+oIUQQhRbcXFxGtshISGEhIQUSizi9ZEOgxBFTJs2bTh//rxGmUqlyvX7bAEBAQQEBADkuHtSNvkjL4QQQojcyJQkIYQQQgghRJ6kwyCEEEIIIYTIk3QYhBBCCCGEEHmSDoMQQgghhBAiT9JhEEIIIYQQQuRJOgxCCCGEEEJDWFgYCoWCUaNGqcsePnzIRx99hK2tLebm5vTo0YN//vmn8IIUOiMdBlGkeXp6avwxy6/ExEQUCgWnTp16ZTG9KSIjI7G2ti7sMIQQQhQx8fHxfPnll7i5uWmUjx49mh07drBp0yYOHDjA33//neetukXxIh0GUaRt2bKFGTNmFHYYavIhXQghRFGWmpqKv78/K1asoGTJkury+/fvs2rVKj799FO8vLyoX78+ERERHD58mKNHjxZixEIXpMMgijQbGxssLCwKO4x8e/ToUWGHIIQQQuTw0Ucf0bFjR9q0aaNRfvLkSZRKpUZ59erVqVChAkeOHNF1mELH5EnPokjz9PSkbt26hIeH4+TkxJAhQ7h8+TKbNm2iZMmSTJkyhSFDhqjrHz9+nA8//JALFy5Qq1YtJk+erNFeZGQko0aN4t69e+qybdu20a1bN/UTlE+fPs2oUaM4ceIECoUCZ2dnvvzyS1JTUxkwYAAACoUCgGnTphESEoKTkxMDBw7k999/Z9u2bXTv3p2kpCRq1KjB0qVL1ce6desW5cqVY9euXbRu3fq553737l1GjhzJjh07yMjIoGXLlixZsgRnZ2eNetu2bWPcuHFcv36dli1bsnLlShwdHbl06RIuLi5cuHCB6tWrq+svWrSIpUuXcuXKlXz8JKDRnH08NjDL1z5Ce8b6KuY1hFohe8jIVBR2OMWW5Fl3JNe6kZ1nbURFRfHLL78QHx+f47WbN29iZGSUYxS9TJky3Lx58xVEKt5k0mEQxcrChQuZMWMGkyZN4rvvvmPYsGG0bNkSFxcXUlNT6dSpE23btmXdunVcvXqVkSNH5vsY/v7+uLu7s3z5cvT19Tl16hSGhoY0adKE8PBwgoODSUhIAMDc3Fy934IFCwgODmbatGkAHDt2jMDAQBYuXIixsTEA69ato1y5cnh5eb0wjoCAAH7//Xe2b9+OpaUlEyZMwMfHh/Pnz2NoaAhAeno6s2bNYs2aNRgZGTF8+HB69erFoUOHqFatGg0aNGD9+vUa07rWr19Pnz598jxuRkYGGRkZ6u2UlBQAjPVU6OurtE2jyCdjPZXGv+L1kDzrjuRaN7Lzq1Qqn1vv+vXrjBw5kujoaPT19VEqlahUKrKyslAqlTx+/DjXdlQqFZmZmS9sv7jLPv+ilIf8xCodBlGs+Pj4MHz4cAAmTJjAokWL+PHHH3FxcWHDhg1kZWWxatUqSpQoQc2aNfnzzz8ZNmxYvo6RlJTEuHHj1Ffln76ib2VlhUKhwN7ePsd+Xl5efPLJJ+rtcuXKERgYyPfff4+fnx/wZIQjICBAPUKRl+yOwqFDh2jSpAnw5IO+o6Mj27Zto2fPnsCTPwZLly6lUaNGAKxevRpXV1eOHz9Ow4YN8ff3Z+nSpeoOw6VLlzh58iTr1q3L89hz5swhNDQ0R/kU9yxMTTOfG7d4eTMaZBV2CG8FybPuSK51IyYm5rmvHz16lOTkZBo2/N9wRFZWFj/99BPLli1j2rRpPHr0iG+//VbjYti1a9e4e/cu0dHRry32ouRFeX6TpKena11XOgyiWHn6jg7ZH9yTk5MBuHDhAm5ubpQoUUJdx8PDI9/HGDNmDIMGDWLt2rW0adOGnj17UqVKlRfu16BBA43tEiVK0LdvX77++mv8/Pz45ZdfOHv2LNu3b39hWxcuXMDAwEDdEQCwtbVVTzHKZmBgwDvvvKPerl69OtbW1ly4cIGGDRvSq1cvxo4dy9GjR2ncuDHr16+nXr16GlOUnhUUFMSYMWPU2ykpKTg6OjLzVz0eG+q/MHZRMMZ6KmY0yGLqCT0ysmT6xusiedYdybVuZOe5bdu26tHn3DRv3lx98Srb4MGDcXFxYezYsU/+zs+ciYGBAT4+PgAkJCRw69YtBgwYoPF+9DZSKpXExMS8MM9vkuwZAtqQDoMoVp79T6pQKMjK0v7qlZ6ennqtQrZnh+xCQkLo06cPO3fuZNeuXUybNo2oqCi6dev23LbNzHLO7x80aBB169blzz//JCIiAi8vLypWrKh1vC/L3t4eLy8vNmzYQOPGjdmwYcMLR1yMjY3VU6iednBCG2xtbV9XqG89pVJJdHQ0J4PbF5k3o6JI8qw7kmvdyM6zoaHhc/NsY2ODjY2NRpm5uTl2dna4u7sDMHDgQMaPH0/p0qWxtLTk448/xsPDg2bNmr3WcyhKXpTnN0l+4pS7JIm3hqurK7/99hsPHz5Ulz17Kzg7OzsePHhAWlqauiy3ZzRUq1aN0aNHs3fvXrp3705ERAQARkZGZGZqPy2ndu3aNGjQgBUrVrBhwwY++OADrc/l8ePHHDt2TF12584dEhISqFGjhrrs8ePHnDhxQr2dkJDAvXv3cHV1VZf5+/uzceNGjhw5wh9//EGvXr20jl8IIcTbY9GiRXTq1IkePXrQokUL7O3t2bJlS2GHJXRAOgzirdGnTx8UCgWDBw/m/PnzREdHs2DBAo06jRo1wtTUlEmTJnHlyhU2bNhAZGSk+vX//vuPwMBA4uLiuHbtGocOHSI+Pl79AdzJyYnU1FT27dvH7du3tZofOGjQIMLCwlCpVC8cpcjm7OxMly5dGDx4MD///DOnT5/m/fffp1y5cnTp0kVdz9DQkI8//phjx45x8uRJAgICaNy4scYc1e7du/PgwQOGDRtGq1atcHBw0CoGIYQQxVtcXBzh4eHq7RIlSrBs2TL+/fdf0tLS2LJlS65r9kTxIx0G8dYwNzdnx44dnDlzBnd3dyZPnszcuXM16tjY2LBu3Tqio6OpXbs233zzDSEhIerX9fX1uXPnDv369aNatWr4+fnRoUMH9SLgJk2aMHToUN577z3s7OyYN2/eC+Pq3bs3BgYG9O7dW2N9xYtERERQv359OnXqhIeHByqVSj3snM3U1JQJEybQp08fmjZtirm5ORs3btRox8LCAl9fX06fPo2/v7/WxxdCCCHE20GhenbCthBCpxITE6lSpQrx8fHUq1evsMPJt5SUFKysrLh9+7asYXiNsuch+/j4FJn5sUWR5Fl3JNe6IXnWjaKY5+z37/v372NpafncurLoWYhColQquXPnDlOmTKFx48ZFsrMghBBCiOJPpiQJUUgOHTpE2bJliY+P54svvtB47aeffsLc3DzPLyGEEEIIXZERBiEKiaenZ45buGZr0KBBrndnEkIIIYTQNekwCPEGMjExoWrVqoUdhhBCCCGETEkSQgghhBBC5E06DEIIIYQQRdjy5ctxc3PD0tISS0tLPDw82LVrl/r1u3fvEhAQgL29PWZmZtSrV4/NmzcXYsSiqJEOgxBCCCFEEVa+fHnCwsI4efIkJ06cwMvLiy5dunDu3DkAwsPDuXTpEtu3b+fMmTN0794dPz8/fv3110KOXBQV0mEQogiJi4tDoVBw7969wg5FCCHEG8LX1xcfHx+cnZ2pVq0as2bNwtzcnKNHjwKQkJDA8OHDadiwIZUrV2bKlClYW1tz8uTJQo5cFBXSYRBCCCGEKCYyMzOJiooiLS0NDw8PAFxcXPjuu+/4999/ycrKIioqiocPH+Lp6Vm4wYoiQ+6SJHTK09OT2rVro6+vz+rVqzEyMmLmzJn06dOHwMBAvvvuO8qUKcNnn32Gt7c3FSpUYPLkyQwbNkzdxq+//kr9+vW5evUqFStWfO7xFAoFX3zxBTt27GD//v1UrFiRr7/+Gjs7OwYNGkR8fDx16tRh7dq1VKlSRb3f8uXLWbBgAdevX6dSpUpMmTKFvn37vnS733//PaGhoZw/fx4HBwf69+/P5MmTMTAwULe7YsUKdu7cyZ49eyhXrhwLFy6kc+fOJCYm0qpVKwBKliwJQP/+/YmMjMTJyYlRo0YxatQo9bHq1q1L165dCQkJeamYtdVozj4eG5jlez+hHWN9FfMaQq2QPWRkKgo7nGJL8qw7kusXSwzrqHXdM2fO4OHhwcOHDzE3N2fr1q3UqFEDpVLJuHHjWL16Nba2thgYGGBqasrWrVvlbnxCa9JhEDq3evVqxo8fz/Hjx9m4cSPDhg1j69atdOvWjUmTJrFo0SL69u1LUlISvXv3ZsOGDRodhvXr19O0adMXdhayzZgxg08//ZRPP/2UCRMm0KdPHypXrkxQUBAVKlTggw8+IDAwUL1AbOvWrYwcOZLw8HDatGnDDz/8wIABAyhfvrz6A3tB2v3pp5/o168fS5YsoXnz5ly5coUhQ4YAMG3aNHW7oaGhzJs3j/nz5/PZZ5/h7+/PtWvXcHR0ZPPmzfTo0YOEhAQsLS0xMTHJV+7zG3NuMjIyyMjIUG+npKQAYKynQl8/9+dKiJdnrKfS+Fe8HpJn3ZFcv5hSqdS6buXKlYmPjyclJYXNmzfTv39/YmNjcXZ2ZsOGDdy9e5fdu3dja2vL9u3b8fPzY//+/dSuXfs1nsHbI/tnlZ+fWWHLT6wKVV5PjhLiNfD09CQzM5OffvoJeDJ0amVlRffu3VmzZg0AN2/epGzZshw5coQSJUpQr149EhMTqVChAllZWVSoUIEpU6YwdOjQFx5PoVAwZcoUZsyYAcDRo0fx8PBg1apVfPDBBwBERUUxYMAA/vvvPwCaNm1KzZo1+eqrr9Tt+Pn5kZaWxs6dOwvcbps2bWjdujVBQUHqdtetW8f48eP5+++/c203LS0Nc3Nzdu3aRfv27YmLi6NVq1bcvXsXa2trdTvajjDkN+bchISEEBoamqN8w4YNmJqa5rmfEEII3QkODsbe3p5u3boxbNgwlixZQoUKFTReL1u2rMYFOfF2SU9Pp0+fPty/fx9LS8vn1pURBqFzbm5u6u/19fWxtbXVuMJRpkwZAJKTk+ncuTOurq5s2LCBiRMncuDAAZKTk+nZs2eBjpfd9rPHe/jwISkpKVhaWnLhwgX1lf9sTZs2ZfHixS/V7unTpzl06BCzZs1S18nMzOThw4ekp6erP2w/3a6ZmRmWlpYkJydrfb7Pk9+YcxMUFMSYMWPU2ykpKTg6OtKqVStsbW1fSZwiJ6VSSUxMDG3btsXQ0LCwwym2JM+6I7l+vcLDwylTpgz16tUDnryPPf33ftmyZZQvXx4fH5/CCrFYKYq/z9kzBLQhHQahc8/+R1IoFBplCsWTuaxZWVkA+Pv7qzsMGzZsoH379vn6YJpb28873utqNzU1ldDQULp3756jrRIlSuTabnY7L4pNT0+PZwcLcxtqfBW5MDY2xtjYONe2i8ofyaJM8qwbkmfdkVy/vKCgIDp06ECFChV48OABGzZs4MCBA+zZs4datWpRtmxZRo4cycKFC7G1tWXbtm3Exsbyww8/SO5fsaL0+5yfOKXDIN54ffr0YcqUKZw8eZLvvvuOL7744rUez9XVlUOHDtG/f3912aFDh6hRo8ZLtVuvXj0SEhJeapGZkZER8GRk4ml2dnbcuHFDvZ2SksLVq1cLfBwhhBBFR3JyMv369ePGjRtYWVnh5ubGnj17aNu2LUqlkqlTp7J37158fX1JTU2latWqrF69WkYXhNakwyDeeE5OTjRp0oSBAweSmZlJ586dX+vxxo0bh5+fH+7u7rRp04YdO3awZcsWYmNjX6rd4OBgOnXqRIUKFXj33XfR09Pj9OnTnD17lpkzZ2rVRsWKFVEoFPzwww/4+PhgYmKCubk5Xl5eREZG4uvri7W1NcHBwejr679UvEIIIYqGVatWPfd1BwcHvv322yJz5Vu8eeQ5DKJI8Pf35/Tp03Tr1i3fdwbKr65du7J48WIWLFhAzZo1+fLLL4mIiHjp+1V7e3vzww8/sHfvXt555x0aN27MokWLtL7bE0C5cuUIDQ1l4sSJlClThsDAQODJcHTLli3p1KkTHTt2pGvXrgW6NaoQQgghxLPkLklCiJeSkpKClZUVt2/flkXPr5FSqSQ6OhofHx+5SvgaSZ51R3KtG5Jn3SiKec5+/9bmLkkywiCEEEIIIYTIk3QYRJG1fv16zM3Nc/2qWbNmYYcnhBBCCFEsyKJnUWR17tyZRo0a5fpaURkOFEIIIYR400mHQRRZFhYWWFhYFHYYQgghhBDFmkxJEkIIIYQQQuRJOgxCFAGRkZFYW1urt0NCQqhbt+5LtZmYmIhCoeDUqVMv1Y4QQojCtXz5ctzc3LC0tMTS0hIPDw927dqlfv3u3bsEBARgb2+PmZkZ9erVY/PmzYUYsShqpMMgxDNexYfx123s2LHs27fvpdpwdHTkxo0b1KpVC4C4uDgUCgX37t17BREKIYTQlfLlyxMWFsbJkyc5ceIEXl5edOnShXPnzgEQHh7OpUuX2L59O2fOnKF79+74+fnx66+/FnLkoqiQDoMQRZC5uflLP/NAX18fe3t7DAxkKZMQQhRlvr6++Pj44OzsTLVq1Zg1axbm5uYcPXoUgISEBIYPH07Dhg2pXLkyU6ZMwdrampMnTxZy5KKokA6DKJaysrKYN28eVatWxdjYmAoVKjBr1iwAJkyYQLVq1TA1NaVy5cpMnToVpVIJPJn6ExoayunTp1EoFCgUCiIjI597rNym9ty7dw+FQkFcXBzwv6v3O3fuxM3NjRIlStC4cWPOnj1boPN7dhQkICCArl27Mnv2bMqUKYO1tTXTp0/n8ePHjBs3DhsbG8qXL09ERESucScmJtKqVSsASpYsiUKhICAgoECxCSGEKDyZmZlERUWRlpaGh4cHAC4uLnz33Xf8+++/ZGVlERUVxcOHD/H09CzcYEWRIZcWRbEUFBTEihUrWLRoEc2aNePGjRtcvHgReHJ3pcjISBwcHDhz5gyDBw/GwsKC8ePH895773H27Fl2795NbGwsAFZWVq8srnHjxrF48WLs7e2ZNGkSvr6+XLp06ZXcBnb//v2UL1+egwcPcujQIQYOHMjhw4dp0aIFx44dY+PGjXz44Ye0bduW8uXLa+zr6OjI5s2b6dGjBwkJCVhaWmJiYpLrcTIyMsjIyFBvp6SkANBibiyPDc1e+jxE7oz1VMxoAPWn7yYjS1HY4RRbkmfdkVy/2NkQb63rnjlzhhYtWvDw4UPMzc3ZtGkTzs7OKJVKxo0bR0REBLa2thgYGGBqasqmTZuoWLGi+oKZeDnZeSxK+cxPrNJhEMXOgwcPWLx4MUuXLqV///4AVKlShWbNmgEwZcoUdV0nJyfGjh1LVFQU48ePx8TEBHNzcwwMDLC3t3/lsU2bNo22bdsCsHr1asqXL8/WrVvx8/N76bZtbGxYsmQJenp6uLi4MG/ePNLT05k0aRLwpBMVFhbGzz//TK9evTT21dfXx8bGBoDSpUtrLLB+1pw5cwgNDc1RPsU9C1PTzJc+D/F8MxpkFXYIbwXJs+5IrvMWHR2tdV2lUsmCBQtIS0vjyJEj9O3bl1mzZuHo6MiGDRtISkoiNDQUS0tLjh07Rs+ePZk9ezZOTk6v7wTeQjExMYUdgtbS09O1risdBlHsXLhwgYyMDFq3bp3r6xs3bmTJkiVcuXKF1NRUHj9+jKWlpU5iyx4ehicf8F1cXLhw4cIrabtmzZro6f1vlmGZMmXUC5rhSafA1taW5OTklzpOUFAQY8aMUW+npKTg6OjIzF/1eGyo/1Jti7w9uRqbxdQTenI19jWSPOuO5PrF8jPC8LQRI0bQvn17Tp8+jaenJ9HR0cTHx1OnTh0APvroI9q3b8+5c+cYPnz4qwz5raVUKomJiaFt27ZF5uGx2TMEtCEdBlHs5DWVBuDIkSP4+/sTGhqKt7c3VlZWREVFsXDhwgIfL/tDukqlUpcVxpDks3+gFApFrmVZWS93Nc/Y2BhjY+Mc5QcntHnphdgib0qlkujoaE4Gty8yb0ZFkeRZdyTXr5dKpUKpVPLo0SMAjIyMNPKcfcMLyf2rZWhoWGRymp84ZdGzKHacnZ0xMTHJ9bajhw8fpmLFikyePJkGDRrg7OzMtWvXNOoYGRmRman91Bo7OzsAbty4oS7L69kG2XesgCf3xb506RKurq5aH+t1MjIyAsjXuQshhCh8QUFBHDx4kMTERM6cOUNQUBBxcXH4+/tTvXp1ypYty0cffcTx48e5cuUKCxcuJCYmhq5duxZ26KKIkBEGUeyUKFGCCRMmMH78eIyMjGjatCm3bt3i3LlzODs7k5SURFRUFO+88w47d+5k69atGvs7OTlx9epVTp06Rfny5bGwsMj1ino2ExMTGjduTFhYGJUqVSI5OVljncTTpk+fjq2tLWXKlGHy5MmUKlXqjfmDXbFiRRQKBT/88AM+Pj7q9RxCCCHebMnJyfTr148bN25gZWWFm5sbe/bsoW3btiiVSqZOncrevXvx9fUlNTWVqlWrsnr1anx8fAo7dFFEyAiDKJamTp3KJ598QnBwMK6urrz33nskJyfTuXNnRo8eTWBgIHXr1uXw4cNMnTpVY98ePXrQvn17WrVqhZ2dHd98880Lj/f111/z+PFj6tevz6hRo5g5c2au9cLCwhg5ciT169fn5s2b7NixQ31lv7CVK1eO0NBQJk6cSJkyZQgMDCzskIQQQmhh1apVJCYmkpGRQXJyMrGxseobbAA4ODjw7bff8s8//5CWlsbp06fp27dvIUYsihqF6umJ10KI1yIuLo5WrVpx9+7d596BqChKSUnBysqK27dvyxqG1yh7vrePj0+RmR9bFEmedUdyrRuSZ90oinnOfv++f//+C2/+IiMMQgghhBBCiDxJh0GIF1i/fj3m5ua5ftWsWfOVHKNDhw55HmP27Nmv5BhCCCGEEAUhi56FeIHOnTvTqFGjXF/TdtjR09OT583+W7lyJf/991+ur2U/UE0IIYQQojBIh0GIF7CwsMDCwuK1HqNcuXKvtX0hhBBCiIKSKUlCCCGEEEKIPEmHQQghhBDiDbV8+XLc3NywtLTE0tISDw8Pdu3aBUBiYiIKhSLXr02bNhVy5KI4kQ7DW8TT05NRo0YVeP/sP0x5PcVYaLp48SKNGzemRIkS1K1bt7DDEUIIUQSVL1+esLAwTp48yYkTJ/Dy8qJLly6cO3cOR0dHbty4ofEVGhqKubk5HTp0KOzQRTEiHYa3yJYtW5gxY0Zhh6EWGRlZZJ5JEBAQkO8nMk+bNg0zMzMSEhLYt2/fK4nDycmJ8PDwV9JWtp07d9KoUSNMTEwoWbLkG/PkaSGEEODr64uPjw/Ozs5Uq1aNWbNmYW5uztGjR9HX18fe3l7ja+vWrfj5+WFubl7YoYtiRBY9v0WK6t12Hj169MY8DTk/rly5QseOHalYsWJhh5JDdk43b97M4MGDmT17Nl5eXjx+/JizZ88WdnhCCCFykZmZyaZNm0hLS8PDwyPH6ydPnuTUqVMsW7asEKITxZl0GN4inp6e1K1bl/DwcJycnBgyZAiXL19m06ZNlCxZkilTpjBkyBB1/ePHj/Phhx9y4cIFatWqxeTJkzXai4yMZNSoUdy7d09dtm3bNrp166a+hejp06cZNWoUJ06cQKFQ4OzszJdffklqaioDBgwAQKFQAE+uyIeEhODk5MTAgQP5/fff2bZtG927dycpKYkaNWqwdOlS9bFu3bpFuXLl2LVrF61bt37uua9du5bFixeTkJCAmZkZXl5ehIeHU7p0aXWdc+fOMWHCBA4ePIhKpaJu3bpERkaydu1aVq9erRHrjz/+iKenZ57Hy6538uRJpk+frj63CRMmsHXrVv7880/s7e3x9/cnODhY4/asO3bsYPr06Zw5cwZzc3OaN2/O1q1b8fT05Nq1a4wePZrRo0cDqPO8efNmgoODuXz5MmXLluXjjz/mk08+UbeZW05XrlzJyJEjmT9/PgMHDlTXrVGjxnNzmZdGc/bx2MCsQPuKFzPWVzGvIdQK2UNGpqKwwym2JM+687bnOjGso9Z1z5w5g4eHBw8fPsTc3JytW7fm+rd61apVuLq60qRJk1cZqhDSYXibLVy4kBkzZjBp0iS+++47hg0bRsuWLXFxcSE1NZVOnTrRtm1b1q1bx9WrVxk5cmS+j+Hv74+7uzvLly9HX1+fU6dOYWhoSJMmTQgPDyc4OJiEhAQAjeHTBQsWEBwczLRp0wA4duwYgYGBLFy4EGNjYwDWrVtHuXLl8PLyemEcSqWSGTNm4OLiQnJyMmPGjCEgIIDo6GgA/vrrL1q0aIGnpyf79+/H0tKSQ4cO8fjxY8aOHcuFCxdISUkhIiICePFozY0bN2jTpg3t27dn7Nix6nOzsLAgMjISBwcHzpw5w+DBg7GwsGD8+PHAk+lB3bp1Y/LkyaxZs4ZHjx6pY9yyZQt16tRhyJAhDB48WH2skydP4ufnR0hICO+99x6HDx9m+PDh2NraEhAQkGdOf/nlF/766y/09PRwd3fn5s2b1K1bl/nz51OrVq08zy0jI4OMjAz1dkpKCgDGeir09fN+1oR4OcZ6Ko1/xeshedadtz3XSqVS67qVK1cmPj6elJQUNm/eTP/+/YmNjdXoNPz3339s2LCBSZMmabSd/X1+jifyryjmOT+xSofhLebj48Pw4cMBmDBhAosWLeLHH3/ExcWFDRs2kJWVxapVqyhRogQ1a9bkzz//ZNiwYfk6RlJSEuPGjaN69eoAODs7q1+zsrJCoVBgb2+fYz8vLy+NK+TlypUjMDCQ77//Hj8/P+DJCEdAQID6av7zfPDBB+rvK1euzJIlS3jnnXdITU3F3NycZcuWYWVlRVRUlPpqf7Vq1dT7mJiYkJGRkWusubG3t8fAwABzc3ONfaZMmaL+3snJibFjxxIVFaXuMMyaNYtevXoRGhqqrlenTh3gSSdFX18fCwsLjTY//fRTWrduzdSpU9Vxnz9/nvnz52t0GJ7NaXx8PAAhISF8+umnODk5sXDhQjw9Pbl06VKenaI5c+ZoxKc+N/csTE0ztcqPKLgZDbIKO4S3guRZd97WXGdfDMqvpk2bsmfPHsaPH69+D4cnI99paWnY29vn2nZMTEyBYxXaK0p5Tk9P17qudBjeYm5uburvsz+4JycnA3DhwgXc3NwoUaKEuk5u8yVfZMyYMQwaNIi1a9fSpk0bevbsSZUqVV64X4MGDTS2S5QoQd++ffn666/x8/Pjl19+4ezZs2zfvl2rOE6ePElISAinT5/m7t27ZGU9eYPKnup06tQpmjdvrvWTmwtq48aNLFmyhCtXrpCamsrjx4+xtLRUv37q1CmN0QNtXLhwgS5dumiUNW3alPDwcDIzM9HX1wdy5jQ7B5MnT6ZHjx4AREREUL58eTZt2sSHH36Y6/GCgoIYM2aMejslJQVHR0dm/qrHY0P9fMUutGesp2JGgyymntAjI+vtm76hK5Jn3Xnbc302xLvA+4aHh1OmTBl8fHzUZZ9++im+vr707t1bo65SqSQmJoa2bdu+9ve4t1lRzHP2DAFtSIfhLfbsL7RCoVB/iNSGnp6eeg59tmeHt0JCQujTpw87d+5k165dTJs2jaioKLp16/bcts3Mcs6FHzRoEHXr1uXPP/8kIiICLy8vrRYUp6Wl4e3tjbe3N+vXr8fOzo6kpCS8vb159OgR8GQE4XU7cuQI/v7+hIaG4u3trR7RWLhwobrO64zj2ZyWLVsW0FyzYGxsTOXKlUlKSsqzHWNjY/W0sKcdnNAGW1vbVxSteJZSqSQ6OpqTwe2LzJtRUSR51h3JtXaCgoLo0KEDFSpU4MGDB2zYsIEDBw6wZ88edd4uX77MTz/9RHR0dJ65NDQ0lDzrQFHKc37ilNuqily5urry22+/8fDhQ3XZ0aNHNerY2dnx4MED0tLS1GW5PaOhWrVqjB49mr1799K9e3f1OgAjIyMyM7WfwlK7dm0aNGjAihUr2LBhg8Y0o+e5ePEid+7cISwsjObNm1O9enX1SEo2Nzc3fvrppzzn8+U31twcPnyYihUrMnnyZBo0aICzszPXrl3LEcfzbsGaWxyurq4cOnRIo+zQoUNUq1ZNPbqQm/r162NsbKxeQwJP3sATExPfyDs7CSHE2yg5OZl+/frh4uJC69atiY+PZ8+ePbRt21Zd5+uvv6Z8+fK0a9euECMVxZl0GESu+vTpg0KhYPDgwZw/f57o6GgWLFigUadRo0aYmpoyadIkrly5woYNG4iMjFS//t9//xEYGEhcXBzXrl3j0KFDxMfH4+rqCjyZw5+amsq+ffu4ffu2VnPpBg0aRFhYGCqV6oWjFNkqVKiAkZERn332GX/88Qfbt2/P8TyKwMBAUlJS6NWrFydOnOD3339n7dq16g/TTk5O/PbbbyQkJHD79u0CLWpydnYmKSmJqKgorly5wpIlS9i6datGnWnTpvHNN98wbdo0Lly4wJkzZ5g7d676dScnJw4ePMhff/3F7du3Afjkk0/Yt28fM2bM4NKlS6xevZqlS5cyduzY58ZjaWnJ0KFDmTZtGnv37iUhIUG9RqVnz575Pj8hhBCv3qpVq0hMTCQjI4Pk5GRiY2M1OgsAs2fPJikpCT09+VgnXg/5zRK5Mjc3Z8eOHZw5cwZ3d3cmT56s8cEVnizCXbduHdHR0dSuXZtvvvmGkJAQ9ev6+vrcuXOHfv36Ua1aNfz8/OjQoYN6wWyTJk0YOnQo7733HnZ2dsybN++FcfXu3RsDAwN69+6tsb7ieezs7IiMjGTTpk3UqFGDsLCwHJ0fW1tb9u/fT2pqKi1btqR+/fqsWLFCPVw3ePBgXFxcaNCgAXZ2djmu6Gujc+fOjB49msDAQOrWrcvhw4fVC5WzeXp6smnTJrZv307dunXx8vLi+PHj6tenT59OYmIiVapUwc7ODoB69erx7bffEhUVRa1atQgODmb69OkaC57zMn/+fHr16kXfvn155513uHbtGvv376dkyZL5Pj8hhBBCFE8K1bOT0IV4g2V/WI6Pj6devXqFHY7gyaIpKysrbt++LWsYXqPs+d4+Pj5FZn5sUSR51h3JtW5InnWjKOY5+/37/v37GjdgyY0sehZFglKp5M6dO0yZMoXGjRtLZ0EIIYQQQkdkSpIoEg4dOkTZsmWJj4/niy++0Hjtp59+wtzcPM+v12H27Nl5Hq9Dhw6v5ZhCCCGEEIVBRhhEkeDp6ZnjFq7ZGjRokOvdmV6noUOHqh8g9yxd3KJVCCGEEEJXpMMgijwTExOqVq2q02Pa2Njk+SRkIYQQQojiRKYkCSGEEEIIIfIkHQYhnuLk5ER4eHhhh6EzcXFxKBQK7t27V9ihCCHEW2f58uW4ublhaWmJpaUlHh4e7Nq1S6POkSNH8PLywszMDEtLS1q0aMF///1XSBGLt5V0GESxlpiYiEKh0Pkahzex4+Hp6cmoUaMKOwwhhBD/r3z58oSFhXHy5ElOnDiBl5cXXbp04dy5c8CTzkL79u1p164dx48fJz4+nsDAQHlAm9A5WcMgBPDo0SOMjIx0eszMzEwUCoX84RdCiLeUr6+vxvasWbNYvnw5R48epWbNmowePZoRI0YwceJEdR0XFxddhymEjDCI4iErK4t58+ZRtWpVjI2NqVChArNmzaJSpUoAuLu7o1Ao8PT0BCAgIICuXbsya9YsHBwc8v0HWKVSERISQoUKFTA2NsbBwYERI0YAT67kX7t2jdGjR6NQKFAoFABERkZibW3N9u3bqVGjBsbGxiQlJZGRkcHYsWMpV64cZmZmNGrUiLi4OPWxsvfbs2cPrq6umJub0759e27cuKGu8/jxY0aMGIG1tTW2trZMmDCB/v3707VrV/X5HjhwgMWLF6tjSkxMVO9/8uRJGjRogKmpKU2aNCEhISGfPwEhhBAvIzMzk6ioKNLS0vDw8CA5OZljx45RunRpmjRpQpkyZWjZsiU///xzYYcq3kIywiCKhaCgIFasWMGiRYto1qwZN27c4OLFixw/fpyGDRsSGxtLzZo1NUYR9u3bh6WlJTExMfk+3ubNm1m0aBFRUVHUrFmTmzdvcvr0aQC2bNlCnTp1GDJkCIMHD9bYLz09nblz57Jy5UpsbW0pXbo0gYGBnD9/nqioKBwcHNi6dSvt27fnzJkzODs7q/dbsGABa9euRU9Pj/fff5+xY8eyfv16AObOncv69euJiIjA1dWVxYsXs23bNlq1agXA4sWLuXTpErVq1WL69OkA2NnZqTsNkydPZuHChdjZ2TF06FA++OADDh06lK+cNJqzj8cGZvnOpdCOsb6KeQ2hVsgeMjIVhR1OsSV51p3inOvEsI5a1z1z5gweHh48fPgQc3Nztm7dSo0aNTh69CgAISEhLFiwgLp167JmzRpat27N2bNn1e8PQujCK+sw3Lt3D2tr61fVnBBae/DgAYsXL2bp0qX0798fgCpVqtCsWTP1B2JbW1vs7e019jMzM2PlypUFmoqUlJSEvb09bdq0wdDQkAoVKtCwYUPgyS1X9fX1sbCwyHFMpVLJ559/Tp06ddTtREREkJSUhIODAwBjx45l9+7dREREMHv2bPV+X3zxBVWqVAEgMDBQ/cEf4LPPPiMoKIhu3boBsHTpUqKjo9WvW1lZYWRkhKmpaY6Y4MkweMuWLQGYOHEiHTt25OHDh5QoUSJH3YyMDDIyMtTbKSkpABjrqdDXz/1ZGeLlGeupNP4Vr4fkWXeKc66VSqXWdStXrkx8fDwpKSls3ryZ/v37Exsby6NHjwAYNGgQ77//PgDz5s0jNjaWFStWMGvWrHzFkp+YRP4VxTznJ9YCdRjmzp2Lk5MT7733HgB+fn5s3rwZe3t7oqOj1R+GhNCFCxcukJGRQevWrfO1X+3atQu8bqFnz56Eh4dTuXJl2rdvj4+PD76+vhgYPP+/lJGREW5uburtM2fOkJmZSbVq1TTqZWRkYGtrq942NTVVdxYAypYtS3JyMgD379/nn3/+UXdYAPT19alfvz5ZWVlanc/TMZUtWxaA5ORkKlSokKPunDlzCA0NzVE+xT0LU9NMrY4nCm5GA+1+puLlSJ51pzjm+ukLNvnRtGlT9uzZw/jx4+nRowfwZI3dsxeAjh07lu9jFGQ0XeRfUcpzenq61nUL1GH44osv1FMhYmJiiImJYdeuXXz77beMGzeOvXv3FqRZIQqkoE9WNjMr+PQZR0dHEhISiI2NJSYmhuHDhzN//nwOHDiAoaFhnvuZmJio1zQApKamoq+vz8mTJ9HX19eoa25urv7+2TYVCkWeT74uiKfbz44vr85GUFAQY8aMUW+npKTg6OhIq1atNDo54tVSKpXExMTQtm3b5/6OiZcjedYdyXXuwsPDKVOmDAEBAYSGhmJiYoKPj4/69WnTpuHt7a1R9jySZ90oinnOniGgjQJ1GG7evImjoyMAP/zwA35+frRr1w4nJycaNWpUkCaFKDBnZ2dMTEzYt28fgwYN0ngtewQhM/PVX/k2MTHB19cXX19fPvroI6pXr86ZM2eoV68eRkZGWh3T3d2dzMxMkpOTad68eYHisLKyokyZMsTHx9OiRQvgyfn+8ssv1K1bV11P25hexNjYGGNj4xzlhoaGReaPZFEmedYNybPuvM25DgoKokOHDlSoUIEHDx6wYcMGDhw4wJ49ezAyMmLcuHFMmzaNevXqUbduXVavXk1CQgKbN2/Od87e5jzrUlHKc37iLFCHoWTJkly/fh1HR0d2797NzJkzgSd3jnkdH8yEeJ4SJUowYcIExo8fj5GREU2bNuXWrVucO3eO/v37Y2Jiwu7duylfvjwlSpTAysrqpY8ZGRlJZmYmjRo1wtTUlHXr1mFiYkLFihWBJ89hOHjwIL169cLY2JhSpUrl2k61atXw9/enX79+LFy4EHd3d27dusW+fftwc3OjY0ftFs59/PHHzJkzh6pVq1K9enU+++wz7t69qzGa4eTkxLFjx0hMTMTc3BwbG5uXzoMQQoiCS05Opl+/fty4cQMrKyvc3NzYs2cPbdu2BWDUqFE8fPiQ0aNH8++//1KnTh1iYmI0pqgKoQsF6jB0796dPn364OzszJ07d+jQoQMAv/76K1WrVn2lAQqhjalTp2JgYEBwcDB///03ZcuWZejQoRgYGLBkyRKmT59OcHAwzZs317hlaUFZW1sTFhbGmDFjyMzMpHbt2uzYsUM9JWf69Ol8+OGHVKlShYyMjOdOH4qIiGDmzJl88skn/PXXX5QqVYrGjRvTqVMnreOZMGECN2/epF+/fujr6zNkyBC8vb01pjmNHTuW/v37U6NGDf777z+uXr1a8AQIIYR4aatWrXphnYkTJ2o8h0GIwqBQFWAitFKpZPHixVy/fp2AgADc3d0BWLRoERYWFjmmhQghdCsrKwtXV1f8/PyYMWPGaz1WSkoKVlZW3L59W9YwvEZKpZLo6Gh8fHyKzHB3USR51h3JtW5InnWjKOY5+/37/v37WFpaPrdugUYYDA0NGTt2bI7y0aNHF6Q5IcRLunbtGnv37qVly5ZkZGSwdOlSrl69Sp8+fQo7NCGEEEIUcQV+0vPatWtp1qwZDg4OXLt2DXiysv/7779/ZcEJUVjWr1+Publ5rl81a9Ys7PBy0NPTIzIyknfeeYemTZty5swZYmNjcXV1LezQhBBCCFHEFWiEYfny5QQHBzNq1ChmzZqlXuhsbW1NeHg4Xbp0eaVBCqFrnTt3zvOOX2/iUKOjo2O+n8wshBBCCKGNAnUYPvvsM1asWEHXrl0JCwtTlzdo0CDXqUpCFDUWFhZYWFgUdhhCCCGEEIWuQFOSrl69ql7o/DRjY2PS0tJeOighhBBCCCHEm6FAHYZKlSpx6tSpHOW7d++WOdNCCCGEeOstX74cNzc3LC0tsbS0xMPDg127dqlf9/T0RKFQaHwNHTq0ECMWIm8FmpI0ZswYPvroIx4+fIhKpeL48eN88803zJkzh5UrV77qGIV4LeLi4mjVqhV3797F2tq6sMPRGU9PT+rWrUt4eHhhhyKEEMVW+fLlCQsLw9nZGZVKxerVq+nSpQu//vqr+uYZgwcPZvr06ep9TE1NCytcIZ6rQB2GQYMGYWJiwpQpU0hPT6dPnz44ODiwePFievXq9apjFEIUQF4doi1btryRC7eFEKI48fX11dieNWsWy5cv5+jRo+oOg6mpKfb29oURnhD5ku8pSY8fP2bNmjW0adOG33//ndTUVG7evMmff/7JwIEDX0eMQoinPHr06KX2t7GxkQXdQgihQ5mZmURFRZGWloaHh4e6fP369ZQqVYpatWoRFBREenp6IUYpRN7yPcJgYGDA0KFDuXDhAvCkdyxDaG+n3bt3M3PmTM6ePYu+vj4eHh4sXryYKlWq0KRJE5o3b87cuXPV9W/duoWDgwP79u2jRYsW3Lhxg0GDBrF//37s7e2ZNWsWkyZNYtSoUYwaNeq5x+7Tpw+ZmZls3LhRXaZUKilbtiyffvop/fr1IyMjg3HjxhEVFUVKSgoNGjRg0aJFvPPOO7m2GRISwrZt2zTW54SHhxMeHk5iYiIAAQEB3Lt3j4YNG7J48WIyMjIYM2YMkyZNIigoiFWrVmFqasqMGTMYMGCAup3r16/zySefsHfvXvT09GjevDmLFy/GycnphXnOPuY777zDsmXLMDY25urVq6xdu5bFixeTkJCAmZkZXl5ehIeHU7p0aRITE2nVqhUAJUuWBKB///5ERkbmmJJ09+5dRo4cyY4dO8jIyKBly5YsWbIEZ2fnF8b2tEZz9vHYwCxf+wjtGeurmNcQaoXsISNTUdjhFFuSZ90pqrlODOuodd0zZ87g4eHBw4cPMTc3Z+vWrdSoUQN48j5WsWJFHBwc+O2335gwYQIJCQls2bLldYUuRIEVaEpSw4YN+fXXX6lYseKrjkcUIWlpaYwZMwY3NzdSU1MJDg6mW7dunDp1Cn9/f+bNm0dYWBgKxZM3go0bN+Lg4EDz5s0B6NevH7dv3yYuLg5DQ0PGjBlDcnKyVsf29/enZ8+epKamYm5uDsCePXtIT0+nW7duAIwfP57NmzezevVqKlasyLx58/D29uby5cvY2NgU+Lz3799P+fLlOXjwIIcOHWLgwIEcPnyYFi1acOzYMTZu3MiHH35I27ZtKV++PEqlEm9vbzw8PPjpp58wMDBg5syZtG/fnt9++w0jI6MXHnPfvn1YWloSExOjLlMqlcyYMQMXFxeSk5MZM2YMAQEBREdH4+joyObNm+nRowcJCQlYWlpiYmKSa9sBAQH8/vvvbN++HUtLSyZMmICPjw/nz5/PdepSRkYGGRkZ6u2UlBQAjPVU6Our8ptOoSVjPZXGv+L1kDzrTlHNtVKp1Lpu5cqViY+PJyUlhc2bN9O/f39iY2OpUaOGxkWl6tWrY2dnh7e3NxcvXqRKlSqvPN78xC3yryjmOT+xFqjDMHz4cD755BP+/PNP6tevj5mZ5lVFNze3gjQripgePXpobH/99dfY2dlx/vx5/Pz8GDVqFD///LO6g7BhwwZ69+6NQqHg4sWLxMbGEh8fT4MGDQBYuXKl1le1vb29MTMzY+vWrfTt21fdfufOnbGwsCAtLY3ly5cTGRlJhw4dAFixYgUxMTGsWrWKcePGFfi8bWxsWLJkCXp6eri4uDBv3jzS09OZNGkSAEFBQYSFhfHzzz/Tq1cvNm7cSFZWFitXrlR3niIiIrC2tiYuLo527dq98JhmZmasXLlSo3PxwQcfqL+vXLkyS5Ys4Z133lF3orI7RaVLl85zUXd2R+HQoUM0adIEeDJE7ujoyLZt2+jZs2eOfebMmUNoaGiO8inuWZiaZr7wXMTLmdEgq7BDeCtInnWnqOU6Ojq6QPs1bdqUPXv2MH78eIYPH57j9YcPHwIQFRWV663rX9bTF5zE61OU8pyfKXAF6jBkL2weMWKEukyhUKBSqVAoFOonP4vi7ffffyc4OJhjx45x+/ZtsrKe/NFPSkqiVq1atGvXjvXr19O8eXOuXr3KkSNH+PLLLwFISEjAwMCAevXqqdurWrWqevrMixgYGODn58f69evp27cvaWlpfP/990RFRQFw5coVlEolTZs2Ve9jaGhIw4YN1dPpCqpmzZro6f1v+U+ZMmWoVauWeltfXx9bW1v1aMnp06e5fPlyjnUDDx8+5MqVK1ods3bt2jlGIk6ePElISAinT5/m7t27GvnPHvJ+kQsXLmBgYKDxVGtbW1tcXFzyzFNQUBBjxoxRb6ekpODo6MjMX/V4bKiv1XFF/hnrqZjRIIupJ/TIyCo60zeKGsmz7hTVXJ8N8S7wvuHh4ZQpUwYfH58crx0+fBh4slj6VV54VSqVxMTE0LZtW7nhxWtUFPOcPUNAGwXqMFy9erUgu4lixtfXl4oVK7JixQocHBzIysqiVq1a6kW5/v7+jBgxgs8++4wNGzZQu3Ztateu/cqO7+/vT8uWLUlOTiYmJgYTExPat29f4Pb09PRQqTSHxnMbrnv2D4FCoci1LPsDfGpqKvXr12f9+vU52rKzs9MqtmdH8dLS0vD29sbb25v169djZ2dHUlIS3t7eL70o+kWMjY0xNjbOUX5wQhtsbW1f67HfZkqlkujoaE4Gty8yb0ZFkeRZd4p7roOCgujQoQMVKlTgwYMHbNiwgQMHDrBnzx6SkpLYsGEDPj4+2Nra8ttvvzF69GhatGhB/fr1X0s8hoaGxTLPb5qilOf8xFmgDoOsXRB37twhISGBFStWqKcc/fzzzxp1unTpwpAhQ9i9ezcbNmygX79+6tdcXFx4/Pgxv/76q/qP4+XLl7l7967WMTRp0gRHR0c2btzIrl276Nmzp/qXv0qVKhgZGXHo0CH176tSqSQ+Pj7PBdV2dnbcvHlTPVIG5PqAwvyqV68eGzdupHTp0lhaWr50ewAXL17kzp07hIWF4ejoCMCJEyc06mSPSDxvxM/V1ZXHjx9z7Ngx9ZSk7J+ttqMUQgghckpOTqZfv37cuHEDKysr3Nzc2LNnD23btuX69evExsYSHh5OWloajo6O9OjRgylTphR22ELkqkAdhjVr1jz39ac/GIriqWTJktja2vLVV19RtmxZkpKSmDhxokYdMzMzunbtytSpU7lw4QK9e/dWv1a9enXatGnDkCFDWL58OYaGhnzyySeYmJioP6xro0+fPnzxxRdcunSJH3/8UePYw4YNY9y4cdjY2FChQgX1WoO8bv/r6enJrVu3mDdvHu+++y67d+9m165dL/0h39/fn/nz59OlSxemT59O+fLluXbtGlu2bGH8+PGUL18+321WqFABIyMjPvvsM4YOHcrZs2eZMWOGRp2KFSuiUCj44Ycf8PHxwcTERL1APJuzszNdunRh8ODBfPnll1hYWDBx4kTKlStHly5dXuq8hRDibbZq1ao8X3N0dOTAgQM6jEaIl5Pv5zAAjBw5UuNr+PDhBAQEMGTIkBfeDlMUD3p6ekRFRXHy5Elq1arF6NGjmT9/fo56/v7+nD59mubNm1OhQgWN19asWUOZMmVo0aIF3bp1Y/DgwVhYWFCiRAmt4/D39+f8+fOUK1dOY70CQFhYGD169KBv377Uq1ePy5cvs2fPnjzXSbi6uvL555+zbNky6tSpw/Hjxxk7dqzWseTF1NSUgwcPUqFCBbp3746rqysDBw7k4cOHBe6M2NnZERkZyaZNm6hRowZhYWEsWLBAo065cuUIDQ1l4sSJlClThsDAwFzbioiIoH79+nTq1AkPDw9UKhXR0dFFZkhVCCGEEK+XQvXspO0C+v3339VXdL29C74gSLy9/vzzTxwdHYmNjaV169aFHY7QUkpKClZWVty+fVvWMLxG2fO9fXx8pDP3GkmedUdyrRuSZ90oinnOfv++f//+Cy9gFmhKUm6cnZ0JCwvj/fff5+LFi6+qWVGM7d+/n9TUVGrXrs2NGzcYP348Tk5OtGjRorBDE0IIIYQQ/69AU5LyYmBgwN9///0qmxTFmFKpZNKkSdSsWZNu3bphZ2enfojb+vXrMTc3z/WrZs2ahR36K5XXeZqbm/PTTz8VdnhCCCGEeMsVaIRh+/btGtsqlYobN26wdOnSHPPIhchL9m1Bc9O5c2eNZwM8ragM9WnreXdiKleunO4CEUIIIYTIRYE6DF27dtXYVigU2NnZ4eXlxcKFC19FXOItZ2FhkeNBZ8VV1apVCzsEIYQQQog8FajDkP1AKiGEEEIIIUTxVqA1DNOnTyc9PT1H+X///cf06dNfOighhBBCiDfV8uXLcXNzw9LSEktLSzw8PNi1a1eOeiqVig4dOqBQKNi2bZvuAxXiFSlQhyE0NJTU1NQc5enp6YSGhr50UEIUddq+OSQmJqJQKF7JE6ULIiAgIMcUQyGEEM9Xvnx5wsLCOHnyJCdOnMDLy4suXbpw7tw5jXrh4eH5ehipEG+qAk1JUqlUuf4HOH36NDY2Ni8dlBBCCCHEm8rX11dje9asWSxfvpyjR4+q7+R36tQpFi5cyIkTJyhbtmxhhCnEK5OvDkPJkiVRKBQoFAqqVaum0WnIzMwkNTWVoUOHvvIghRD/o1KpyMzMxMDglT1GRQghRAFlZmayadMm0tLS8PDwAJ7MuOjTpw/Lli3D3t6+kCMU4uXl6xNHeHg4KpWKDz74gNDQUKysrNSvGRkZ4eTkpP7PIkRR9dVXXxESEsKff/6Jnt7/Zu116dIFW1tbvv76a5YvX86CBQu4fv06lSpVYsqUKfTt27fAx7x48SLDhw/nl19+oWrVqixbtoyWLVsCEBcXR6tWrYiOjmbKlCmcOXOGvXv30qJFC+bOnctXX33FzZs3qVatGlOnTuXdd98FnryJDRkyhP3793Pz5k0qVKjA8OHDGTlyZJ5xxMfH4+Pjw9ixY5kwYUK+zqHRnH08NjArcA7E8xnrq5jXEGqF7CEjU6Y4vC6SZ915E3OdGNZR67pnzpzBw8ODhw8fYm5uztatW6lRowYAo0ePpkmTJnTp0uV1hSqETuWrw9C/f38AKlWqRJMmTYrd/fCFAOjZsycff/wxP/74I61btwbg33//Zffu3URHR7N161ZGjhxJeHg4bdq04YcffmDAgAGUL1+eVq1aFeiY48aNIzw8nBo1avDpp5/i6+vL1atXsbW1VdeZOHEiCxYsoHLlypQsWZI5c+awbt06vvjiC5ydnTl48CDvv/8+dnZ2tGzZkqysLMqXL8+mTZuwtbXl8OHDDBkyhLJly+Ln55cjhv3799O9e3fmzZvHkCFD8ow1IyODjIwM9XZKSgoAxnoq9PVVBTp/8WLGeiqNf8XrIXnWnTcx10qlUuu6lStXJj4+npSUFDZv3kz//v2JjY3lypUr7N+/n+PHj2u09/jx43y1/6pkH7Mwjv02KYp5zk+sCpVK9VL/Ux8+fMijR480yiwtLV+mSSEKXdeuXbG1tWXVqlXAk1GH0NBQrl+/TvPmzalZsyZfffWVur6fnx9paWns3LkTeLLoeevWrS9cUJyYmEilSpUICwtTX9F//PgxlSpV4uOPP2b8+PHqEYZt27apr1ZlZGRgY2NDbGysxqjeoEGDSE9PZ8OGDbkeLzAwkJs3b/Ldd98BTxY937t3j/79+9OvXz9WrlzJe++999yYQ0JCcr25wYYNGzA1NX3uvkIIUVwFBwdjb2+PkZERO3fu1Ji2nZWVhZ6eHq6ursyaNasQoxTif7Knzt2/f/+Fn90LNAk6PT2d8ePH8+2333Lnzp0cr2dmZhakWSHeGP7+/gwePJjPP/8cY2Nj1q9fT69evdDT0+PChQs5rsA3bdqUxYsXF/h4T3/oNzAwoEGDBly4cEGjToMGDdTfX758mfT0dNq2batR59GjR7i7u6u3ly1bxtdff01SUhL//fcfjx49om7duhr7HDt2jB9++IHvvvtOqzsmBQUFMWbMGPV2SkoKjo6OtGrVSmNERLxaSqWSmJgY2rZtK6O7r5HkWXeKW67Dw8MpU6YMs2bN4vbt2xqv1atXjwULFtCxY0cqVaqk07iKW57fVEUxz9kzBLRRoA7DuHHj+PHHH1m+fDl9+/Zl2bJl/PXXX3z55ZeEhYUVpEkh3ii+vr6oVCp27tzJO++8w08//cSiRYsKNSYzs/+tD8i+rfHOnTspV66cRj1jY2MAoqKiGDt2LAsXLsTDwwMLCwvmz5/PsWPHNOpXqVJFvTajY8eOL/xDZ2xsrD7G0wwNDYvMH8miTPKsG5Jn3SmKuQ4KCqJDhw5UqFCBBw8esGHDBg4cOMCePXtwdHTE0dExxz6VKlWiWrVqhRDtE0Uxz0VRUcpzfuIs0HMYduzYweeff06PHj0wMDCgefPmTJkyhdmzZ7N+/fqCNCnEG6VEiRJ0796d9evX88033+Di4kK9evUAcHV15dChQxr1Dx06pF7sVhBHjx5Vf//48WNOnjyJq6trnvVr1KiBsbExSUlJVK1aVeMr+43q0KFDNGnShOHDh+Pu7k7VqlW5cuVKjrZKlSrF/v37uXz5Mn5+fkVq/qUQQhSG5ORk+vXrh4uLC61btyY+Pp49e/bkGPUVorgo0AjDv//+S+XKlYEn6xX+/fdfAJo1a8awYcNeXXRCFCJ/f386derEuXPneP/999Xl48aNw8/PD3d3d9q0acOOHTvYsmULsbGxBT7WsmXLcHZ2xtXVlUWLFnH37l0++OCDPOtbWFgwduxYRo8eTVZWFs2aNeP+/fscOnQIS0tL+vfvj7OzM2vWrGHPnj1UqlSJtWvXEh8fn+tweOnSpdm/fz+tWrWid+/eREVFyW1bhRAiD9nr27T1kstFhSh0BRphqFy5MlevXgWgevXqfPvtt8CTkQdra+tXFpwQhcnLywsbGxsSEhLo06ePurxr164sXryYBQsWULNmTb788ksiIiLw9PQs8LHCwsIICwujTp06/Pzzz2zfvp1SpUo9d58ZM2YwdepU5syZg6urK+3bt2fnzp3qDsGHH35I9+7dee+992jUqBF37txh+PDhebZnb2/P/v37OXPmDP7+/rIWSQghhBBAAe+StGjRIvT19RkxYgSxsbHq+d5KpZJPP/30ufd5F0IULykpKVhZWXH79m1Z9PwaKZVKoqOj8fHxKTLzY4siybPuSK51Q/KsG0Uxz9nv36/tLkmjR49Wf9+mTRsuXrzIyZMnqVq1Km5ubgVpUgghhBBCCPEGKtCUpKc9fPiQihUr0r17d+ksCPGM2bNnY25unutXhw4dCjs8IYQQQogXKtAIQ2ZmJrNnz+aLL77gn3/+4dKlS1SuXJmpU6fi5OTEwIEDX3WcQhRJQ4cOzfWpygAmJiY6jkYIIYQQIv8K1GGYNWsWq1evZt68eQwePFhdXqtWLcLDw6XDIMT/s7GxwcbGprDDEEIIIYQosAJNSVqzZg1fffUV/v7+6Ovrq8vr1KnDxYsXX1lwQgghhBBCiMJVoA7DX3/9RdWqVXOUZ2VlyUOfhBBCCFGsLV++HDc3NywtLbG0tMTDw4Ndu3blqKdSqejQoQMKhYJt27bpPlAhXpECdRhq1KjBTz/9lKP8u+++w93d/aWDEkJoJzIyMtdnnzg5OREeHq7zeIQQ4m1Qvnx5wsLCOHnyJCdOnMDLy4suXbpw7tw5jXrh4eEoFIpCilKIV6dAaxiCg4Pp378/f/31F1lZWWzZsoWEhATWrFnDDz/88KpjFEIIIYR4Y/j6+mpsz5o1i+XLl3P06FFq1qwJwKlTp1i4cCEnTpygbNmyhRGmEK9MvkYY/vjjD1QqFV26dGHHjh3ExsZiZmZGcHAwFy5cYMeOHbRt2/Z1xSpEkeTp6UlgYCCBgYFYWVlRqlQppk6dSvYzEzMyMhg7dizlypXDzMyMRo0aERcX98J24+LiGDBgAPfv30ehUKBQKAgJCcHT05Nr164xevRodTn8bzTihx9+wMXFBVNTU959913S09NZvXo1Tk5OlCxZkhEjRshTnoUQQkuZmZlERUWRlpaGh4cHAOnp6fTp04dly5Zhb29fyBEK8fLyNcLg7OzMjRs3KF26NM2bN8fGxoYzZ85QpkyZ1xWfEMXC6tWrGThwIMePH+fEiRMMGTKEChUqMHjwYAIDAzl//jxRUVE4ODiwdetW2rdvz5kzZ3B2ds6zzSZNmhAeHk5wcDAJCQkAmJubM2LECOrUqcOQIUM07mIGT97ElixZQlRUFA8ePKB79+5069YNa2troqOj+eOPP+jRowdNmzblvffey/W4GRkZZGRkqLdTUlIAaDE3lseGZi+bKpEHYz0VMxpA/em7yciSKQ6vi+RZd97EXJ8N8da67pkzZ2jRogUPHz7E3NycTZs24ezsjFKpZOTIkTRu3BgfHx/12s7Hjx8XyjrP7GPKGtPXqyjmOT+x5qvDkH1FNNuuXbtIS0vLTxNCvJUcHR1ZtGgRCoUCFxcXzpw5w6JFi/D29iYiIoKkpCQcHBwAGDt2LLt37yYiIoLZs2fn2aaRkRFWVlYoFIocV7D09fWxsLDIUa5UKlm+fDlVqlQB4N1332Xt2rX8888/mJubU6NGDVq1asWPP/6YZ4dhzpw5hIaG5iif4p6FqamMTLxuMxpkFXYIbwXJs+68SbmOjo7Wuq5SqWTBggWkpaVx5MgR+vbty6xZs7hx4wY7d+7k008/1Wjv5MmTGBoavo6wtRITE1Nox36bFKU8p6ena123QGsYsj3bgRBC5K5x48YaC988PDxYuHAhZ86cITMzk2rVqmnUz8jIwNbW9pXHYWpqqu4sAJQpUwYnJyfMzc01ypKTk/NsIygoiDFjxqi3U1JScHR0ZOavejw21M9zP/FynlyNzWLqCb035mpscSR51p03Mdf5GWF42ogRI2jfvj2nT5/GxMSEmzdv8v7772vUmTdvHs2aNSM2NvZVhKo1pVJJTEwMbdu2LdQOS3FXFPOcPUNAG/nqMDw9H/rpMiFEwaSmpqKvr8/Jkyc1nmkCaHyIf1We/SOmUChyLcvKyvuKn7GxMcbGxjnKD05o81o6OeIJpVJJdHQ0J4PbF5k3o6JI8qw7xS3XKpUKpVLJjBkzGDJkiMZrtWvXZtGiRfj6+hbauRoaGhaLPL/pilKe8xNnvqckBQQEqD8sPHz4kKFDh2JmpjlvecuWLflpVohi79ixYxrbR48exdnZGXd3dzIzM0lOTqZ58+b5btfIyCjXBcp5lQshhHh5QUFBdOjQgQoVKvDgwQM2bNhAXFwce/bswd7ePteFzhUqVKBSpUqFEK0QLy9fHYb+/ftrbD873CaEyF1SUhJjxozhww8/5JdffuGzzz5j4cKFVKtWDX9/f/r168fChQtxd3fn1q1b7Nu3Dzc3Nzp27Pjcdp2cnEhNTWXfvn3UqVMHU1NTTE1NcXJy4uDBg/Tq1QtjY2NKlSqlozMVQojiLzk5mX79+nHjxg2srKxwc3Njz549cqdIUWzlq8MQERHxuuIQoljr168f//33Hw0bNkRfX5+RI0eqh6wjIiKYOXMmn3zyCX/99RelSpWicePGdOrU6YXtNmnShKFDh/Lee+9x584dpk2bRkhICNOnT+fDDz+kSpUqZGRkyHojIYR4hVatWpWv+vI3WBR1CpX8FgvxWnl6elK3bt1i++TllJQUrKysuH37tqxheI2y53v7+PgUmfmxRZHkWXck17ohedaNopjn7Pfv+/fvY2lp+dy6+XpwmxBCCCGEEOLtIh0GId5gHTp0wNzcPNev5z2jQQghhBDiVXmp5zAIIV4sLi6uwPuuXLmS//77L9fXbGxsCtyuEEIIIYS2pMMgxBusXLlyhR2CEEIIId5yMiVJCCGEEEIIkSfpMAghhBBC/L/ly5fj5uaGpaUllpaWeHh4sGvXLvXr2besNjExwc7Oji5dunDx4sVCjFiI1086DG8hhULBtm3bdHKsyMhIrK2tdXKs18nT05NRo0YVdhjPlZiYiEKh4NSpU3nWiYuLQ6FQcO/ePZ3FJYQQRUn58uUJCwvj5MmTnDhxAi8vL7p06cK5c+cAqF+/PhEREVy4cIE9e/agUqlo164dmZmZhRy5EK+PrGEoxkJCQti2bVuOD5A3btygZMmShROUKFRNmjRRP5lUCCFETr6+vhrbs2bNYvny5Rw9epSaNWuqH7oJ4OTkxMyZM6lTpw6JiYlUqVJF1+EKoRPSYXgL2dvbF3YIopAYGRnJz18IIbSUmZnJpk2bSEtLw8PDI8fraWlpREREUKlSJRwdHQshQiF0QzoMb7jdu3czc+ZMzp49i76+Ph4eHixevFh9FePPP/9k3Lhx7Nmzh4yMDFxdXVm2bBkXLlwgNDQUeDIFCSAiIoKAgAAUCgVbt26la9euNGnShObNmzN37lz1MW/duoWDgwP79u2jRYsWZGRkMHnyZL755hvu3btHrVq1mDt3Lp6enlqfx7Zt2xg3bhzXr1+nZcuWrFy5Uv3HNSAggHv37mlMkxo1ahSnTp0iLi6ONWvWMHr0aP7++2+MjY3Vdbp27YqFhQVr167N87iXLl3CxcWFCxcuUL16dXX5okWLWLp0KVeuXAHgwIEDjBs3jtOnT2NjY0P//v2ZOXMmBga5/xd5OofZrK2tCQ8PJyAggMTERCpVqsTGjRv57LPPOHHiBLVq1WL9+vXcv3+fYcOGcfHiRZo3b86aNWuws7NTt7Ny5UoWLlzI1atXcXJyYsSIEQwfPlzrXF+8eJHhw4fzyy+/ULVqVZYtW0bLli2BJ1OSWrVqxd27d9VTxVasWMH06dO5c+cO3t7eNG/enOnTp+d72lKjOft4bGCWr32E9oz1VcxrCLVC9pCRqSjscIotybPu6DrXiWEdta575swZPDw8ePjwIebm5mzdupUaNWqoX//8888ZP348aWlpuLi4EBMTg5GR0esIW4g3gnQY3nBpaWmMGTMGNzc3UlNTCQ4Oplu3bpw6dYr09HRatmxJuXLl2L59O/b29vzyyy9kZWXx3nvvcfbsWXbv3k1sbCxArtNQ/P39mTdvHmFhYeqOxcaNG3FwcKB58+YABAYGcv78eaKionBwcGDr1q20b9+eM2fO4Ozs/MJzSE9PZ9asWaxZswYjIyOGDx9Or169OHTokFY56NmzJyNGjGD79u307NkTgOTkZHbu3MnevXufu2+1atVo0KAB69evZ8aMGery9evX06dPHwD++usvfHx8CAgIYM2aNVy8eJHBgwdTokQJQkJCtIoxL9OmTSM8PJwKFSrwwQcf0KdPHywsLFi8eDGmpqb4+fkRHBzM8uXL1XEFBwezdOlS3N3d+fXXXxk8eDBmZmb0799fq2OOGzeO8PBwatSowaeffoqvry9Xr17F1tY2R91Dhw4xdOhQ5s6dS+fOnYmNjWXq1KnPbT8jI4OMjAz1dkpKCgDGeir09VXapkbkk7GeSuNf8Xr8X3v3Hpfz/T9+/HF11BktHejgkOQQmVMMOaSEOTPaaAtzyGGOa4YMYwjDhjnUDiWzxpjIsTbnamMhOYxlP3xiQwrp8P794db13aXSlUMpz/vtdt243u/X+/16vp/XVV3P6/V6v9+S59JT2rnOzs7Wum2tWrWIj48nPT2dqKgohg4dyt69e9VFw4ABA/D09OT69essWbKE/v37ExcXR6VKlV5U+E8t/7hLcvyi5MpjnksSqxQML7m+fftqPN+wYQNWVlacOXOGw4cPc+PGDeLj49U38apTp466rampKXp6ek+cgjJgwAAmTJjAwYMH1QVCREQEgwYNQqVSkZqaSmhoKKmpqdjZ2QEwefJkdu3aRWhoqFZ3G87OzmblypW0bNkSgK+//hpXV1eOHz9OixYtit3eyMiIwYMHExoaqi4YvvvuOxwcHLQa5fDz82PlypXqguHcuXMkJiby3XffAY++KbK3t2flypWoVCrq1avH1atXmTZtGjNnzkRH5+mvDTB58mS8vb0BGD9+PIMGDWLfvn20adMGgICAAMLCwtTtZ82aRUhICH369AGgZs2anDlzhjVr1mhdMAQGBqrfN6tWrWLXrl2sX7+eqVOnFmi7YsUKunbtyuTJk4FHBdbhw4f5+eefi9z//Pnz1aNX//Wxex7GxnLS34s2p1leWYfwSpA8l57SynV0dPRTbdemTRtiYmKYOnVqoaO9/v7+vP322wQHB9OuXbtnDfOF2bNnT1mH8EooT3m+d++e1m2lYHjJnT9/npkzZ3Ls2DFu3rxJXt6jX6ypqamcOHECd3f3Z7rjr5WVFV26dCE8PJy2bdty6dIljhw5wpo1a4BHw7K5ubnUrVtXY7usrKxCv7EujJ6eHs2bN1c/r1evHpUrVyY5OVmrggFg+PDhNG/enP/3//4f1atXJywsTD29qjhvvfUWkydP5ujRo7Rq1Yrw8HCaNm2qnqKUnJyMh4eHxr7atGlDRkYGf//9Nw4ODlrFWBg3Nzf1/62trQFo1KiRxrK0tDTg0WjSxYsXCQgIYPjw4eo2OTk5JTpJ+b/zbPX09GjWrBnJycmFtk1JSaF3794ay1q0aPHEgiEoKIiJEyeqn6enp2Nvb8/c33XI0dfVOk5RMoY6CnOa5TEjQYesPJkq86JInktPaef6VLD3U2+7bNkyrK2t8fX1LbAuKysLHR0d6tevX+j6spadnc2ePXvw8vJCX1+/rMOpsMpjnvNnCGhDCoaXXI8ePXB0dGTt2rXY2dmRl5dHw4YNefjwIUZGRs+lDz8/P8aNG8eKFSuIiIigUaNG6g+1GRkZ6OrqkpiYiK6u5odBU1PT59K/jo4OiqI5JP34MJm7uzuNGzfmm2++oUuXLpw+fZodO3ZotX8bGxs6duxIREQErVq1IiIiglGjRj1TzCqVqtiYAY1fGvkFyePL8ovAjIwM4NE5BfmjMfkez31ZMjQ01DiXJN8v0zprXUSKksvOziY6OprEmT7l5o9ReSR5Lj0va66DgoLo2rUrDg4O3L17l4iICOLi4oiJieHKlSts2rSJLl26YGVlxd9//82CBQswMjKiR48eL9VxPE5fX/+ljq+iKE95Lkmcch+Gl9g///xDSkoKH3/8MZ06dcLV1ZVbt26p17u5uXHixAn+/fffQrc3MDDQ6rrQPXv25MGDB+zatYuIiAj8/PzU69zd3cnNzSUtLY06depoPLS92k5OTg4JCQnq5ykpKdy+fRtXV1fg0SjHtWvXNLYp7F4Cw4YNIywsjNDQUDp37lyiK1L4+fmxadMmjhw5wp9//slbb72lXufq6sqRI0c0CoBDhw5hZmZGjRo1Ct3f4zGfP3++REN7hbG2tsbOzo4///yzQK5r1qyp9X6OHj2q/n9OTg6JiYnqXD/OxcWF+Ph4jWWPPxdCiFdJWloaQ4YMwcXFhU6dOhEfH09MTAxeXl5UqlSJX3/9FV9fX+rUqcPAgQMxMzPj8OHDVKtWraxDF+KFkRGGl1iVKlWwtLTkq6++wtbWltTUVD788EP1+kGDBvHpp5/Sq1cv5s+fj62tLb///jt2dnZ4eHjg5OTEpUuXOHHiBDVq1MDMzKzQb4ZNTEzo1asXM2bMIDk5mUGDBqnX1a1bFz8/P4YMGUJISAju7u7cuHGDffv24ebmRrduxV91Ql9fn7Fjx7J8+XL09PQIDAykVatW6ulIHTt2ZNGiRXzzzTd4eHjw3XffcerUKdzd3TX2M3jwYCZPnszatWv55ptvSpTLPn36MGrUKEaNGkWHDh3U52MAjB49mmXLljF27FgCAwNJSUlh1qxZTJw4scjzFzp27MjKlSvx8PAgNzeXadOmPZdvFGbPns24ceOwsLDAx8eHrKwsEhISuHXrlsY0oCf54osvcHZ2xtXVlaVLl3Lr1i3ee++9QtuOHTuWdu3aqU+O3r9/Pzt37tRqqpcQQlRE69evL3KdnZ3dU58LIUR5JiMMLzEdHR0iIyNJTEykYcOGfPDBByxatEi93sDAgN27d1OtWjV8fX1p1KgRCxYsUE9f6du3Lz4+PnTo0AErKys2btxYZF9+fn6cPHmStm3bFpizHxoaypAhQ5g0aRIuLi706tWL+Ph4ref2GxsbM23aNAYPHkybNm0wNTVl06ZN6vXe3t7MmDGDqVOn0rx5c+7evcuQIUMK7MfCwoK+fftiamqqcTlTbZiZmdGjRw9OnjypMYICUL16daKjozl+/DiNGzdm5MiRBAQE8PHHHxe5v5CQEOzt7Wnbtq26kDE2Ni5RTIUZNmwY69atIzQ0lEaNGtG+fXvCwsJKNMKwYMECFixYQOPGjTl48CDbtm3jtddeK7RtmzZtWL16NUuWLKFx48bs2rWLDz744KW80ocQQgghyoZKeXwithAvsU6dOtGgQQOWL19e1qFUWMOHD+fs2bP8+uuvWrVPT0/HwsKCmzdvyjkML1D+fG9fX99yMz+2PJI8lx7JdemQPJeO8pjn/L/fd+7cwdzc/IltZUqSKBdu3bpFbGwssbGxfPnll2UdToWyePFivLy8MDExYefOnXz99deSYyGEEEKoyZQk8Uy6du2KqalpoQ9t7tGgLXd3d/z9/fnss89wcXHRWNegQYMiYwgPD39uMZS1Tz/9tMjj7Nq161Pv9/jx43h5edGoUSNWr17N8uXLGTZs2HOMXAghhBDlmYwwiGeybt067t+/X+i6Z7k/xOMuX75c5Lro6Ogi71aYf++DimDkyJEMGDCg0HXPcond77///qm3FUIIIUTFJwWDeCbVq1cv6xBwdHQs6xBKRdWqVZ9rESaEEEIIoQ2ZkiSEEEIIIYQoUoUoGDw9PZkwYUJZh1GksLAwKleuXNZhaHBycmLZsmUvbP/BwcE0adLkhe2/ND3++j3PY7t8+TIqlarQG9WVFX9//xJftlYIIcqDVatW4ebmhrm5Oebm5nh4eLBz504A/v33X8aOHYuLiwtGRkY4ODgwbtw47ty5U8ZRC1H2ZEqSeCEmT57M2LFjyzoMIYQQQq1GjRosWLAAZ2dnFEXh66+/pmfPnvz+++8oisLVq1dZvHgx9evX56+//mLkyJFcvXqVH374oaxDF6JMScEgXoj8q/cIIYQQL4sePXpoPJ83bx6rVq3i6NGjBAQEEBUVpV5Xu3Zt5s2bx9tvv01OTg56evKRSby6KsSUJICcnBwCAwOxsLDgtddeY8aMGeTfk06lUrF161aN9pUrVyYsLAyAjh07EhgYqLH+xo0bGBgYsG/fvmL7zsrKYvLkyVSvXh0TExNatmxJbGzsE7eZO3cu1apVw8zMjGHDhvHhhx9qNc1l9+7dVKpUidu3b2ssHz9+PB07dlQ/j4qKokGDBhgaGuLk5ERISEix+y6KSqVizZo1dO/eHWNjY1xdXTly5AgXLlzA09MTExMTWrduzcWLF9XbPD5tJ3+ay+LFi7G1tcXS0pIxY8ZoXN2ouNfp4cOHBAYGYmtrS6VKlXB0dGT+/PnFxq8oCsHBwTg4OGBoaIidnR3jxo1Tr3+a168k1q1bh6urK5UqVaJevXrF3uNg27ZtODs7U6lSJTp06MDXX3+NSqUq8Jo/Lj09HSMjI/Xwer4tW7ZgZmbGvXv3AEhKSqJjx44YGRlhaWnJiBEjyMjIeKZjFEKI8iY3N5fIyEgyMzPx8PAotE3+Da2kWBCvugrzE/D1118TEBDA8ePHSUhIYMSIETg4ODB8+PBitx02bBiBgYGEhIRgaGgIwHfffUf16tU1PoQXJTAwkDNnzhAZGYmdnR1btmzBx8eHpKQknJ2dC7QPDw9n3rx5fPnll7Rp04bIyEhCQkKoWbNmsX116tSJypUrExUVRUBAAPDol96mTZuYN28eAImJiQwYMIDg4GAGDhzI4cOHGT16NJaWlvj7+xfbR2HmzJnDkiVLWLJkCdOmTWPw4MHUqlWLoKAgHBwceO+99wgMDCzwYfW/Dhw4gK2tLQcOHODChQsMHDiQJk2aaPUaASxfvpxt27bx/fff4+DgwJUrV7hy5Uqx20VFRbF06VIiIyNp0KAB169f5+TJk+r1JX39SiI8PJyZM2eycuVK3N3d+f333xk+fDgmJiYMHTq0QPtLly7Rr18/xo8fz7Bhw/j999+ZPHmyVn2Zm5vTvXt3IiIiNO7LEB4eTq9evTA2NiYzMxNvb288PDyIj48nLS1N/f7PL8yeVsv5+8jRM3mmfYiiGeoqLGwBDYNjyMpVlXU4FZbkufQ8z1xfXtBN67ZJSUl4eHjw4MEDTE1N2bJlC/Xr1y/Q7ubNm8yZM4cRI0Y8U2xCVAQVpmCwt7dn6dKlqFQqXFxcSEpKYunSpVp9GO3Tpw+BgYH89NNP6uvch4WF4e/vj0r15F9iqamphIaGkpqaip2dHfBo/v6uXbsIDQ0t9OZlK1asICAggHfffReAmTNnsnv3bq2+5dXV1eWtt94iIiJCXTDs27eP27dv07dvXwCWLFlCp06dmDFjBgB169blzJkzLFq06KkLhnfffVedm2nTpuHh4cGMGTPw9vYGHo1w5B9PUapUqcLKlSvR1dWlXr16dOvWjX379mldMKSmpuLs7Mwbb7yBSqXS+nKqqamp2NjY0LlzZ/T19XFwcKBFixbqdSV9/Upi1qxZhISE0KdPHwBq1qzJmTNnWLNmTaEFw5o1a3BxcWHRokUAuLi4cOrUKXUxWBw/Pz/eeecd7t27h7GxMenp6ezYsYMtW7YAEBERwYMHD/jmm28wMXn04X7lypX06NGDzz77TKv7VmRlZZGVlaV+np6eDoChjoKurqJVnKLkDHUUjX/FiyF5Lj3PM9dF3YunMLVq1SI+Pp709HSioqIYOnQoe/fu1Sga0tPT8fX1xdXVlenTp5do/y+b/NjL8zGUB+UxzyWJtcIUDK1atdL4cO/h4UFISAi5ubnFblupUiXeeecdNmzYwIABA/jtt984deoU27ZtK3bbpKQkcnNzqVu3rsbyrKwsLC0tC90mJSWF0aNHayxr0aIF+/fvL7Y/ePShsFWrVly9ehU7OzvCw8Pp1q2b+ko+ycnJ9OzZU2ObNm3asGzZMnJzc9HV1dWqn/9yc3NT/z//Q2WjRo00lj148ID09HTMzc0L3UeDBg00+ra1tSUpKUnrGPz9/fHy8sLFxQUfHx+6d+9Oly5dit2uf//+LFu2jFq1auHj44Ovry89evRAT0/vqV4/bWVmZnLx4kUCAgI0iqKcnBwsLCwK3SYlJYXmzZtrLMsvbrTh6+uLvr4+27Zt46233iIqKgpzc3M6d+4MPHpvNG7cWF0swKP3Rl5eHikpKVoVDPPnz2f27NkFln/snoexcfE/b+LZzGmWV9YhvBIkz6XneeQ6Ojr6qbZr06YNMTExTJ06Vf13+f79+wQHB2NoaEhAQAB79ux55vheBhXlOF525SnP+VOVtVFhCoYnUalU6vMZ8j1eVQ0bNowmTZrw999/ExoaSseOHbX6BjsjIwNdXV0SExMLfBB/USf9Nm/enNq1axMZGcmoUaPYsmXLM08nKY6+vr76//mFWWHL8vKK/sX/3/b52/y3fXGvU9OmTbl06RI7d+5k7969DBgwgM6dOxd79Qp7e3tSUlLYu3cve/bsYfTo0SxatIi4uLgX+vrljxitXbuWli1baqx7mqJNGwYGBvTr14+IiAj1SNTAgQOf6/zboKAgJk6cqH6enp6Ovb09HTp0eOYiSxQtOzubPXv24OXlVeBnSTw/kufS87LketmyZVhbW+Pr60t6ejrdunXD2tqabdu2YWxsXGZxPS8vS54ruvKY5/wZAtqoMAXDsWPHNJ4fPXoUZ2dndHV1sbKy4tq1a+p158+fL1BVNWrUiGbNmrF27VoiIiJYuXKlVv26u7uTm5tLWloabdu21WobFxcX4uPjGTJkiHpZfHy8Vtvm8/PzIzw8nBo1aqCjo0O3bv83f9PV1ZVDhw5ptD906BB169Z9YR9UnwdtXidzc3MGDhzIwIED6devHz4+Pvz777/F3gHZyMiIHj160KNHD8aMGUO9evVISkp6qtdPW9bW1tjZ2fHnn3/i5+en1TYuLi4Fvil7mveGl5cXp0+fZv/+/cydO1e9ztXVlbCwMDIzM9WjDIcOHUJHRwcXFxet9m9oaKg+1+e/9PX1y80vyfJM8lw6JM+lpzRzHRQURNeuXXFwcODu3btEREQQFxdHTEwM9+/fp1u3bty7d4/w8HDu37/P/fv3gUd/n17mv5/akPd06ShPeS5JnBWmYEhNTWXixIm8//77/Pbbb6xYsUJ9ZaCOHTuycuVKPDw8yM3NZdq0aYUmKf/kTxMTE3r37q1Vv3Xr1sXPz48hQ4YQEhKCu7s7N27cYN++fbi5uWl8kM83duxYhg8fTrNmzWjdujWbNm3ijz/+oFatWlofr5+fH8HBwcybN49+/fppfICbNGkSzZs3Z86cOQwcOJAjR46wcuXKYq/OU9aKe52WLFmCra0t7u7u6OjosHnzZmxsbIq9KV5YWBi5ubm0bNkSY2NjvvvuO4yMjHB0dMTS0rLEr19JzJ49m3HjxmFhYYGPjw9ZWVkkJCRw69YtjW/p873//vvqE8sDAgI4ceKEevSouPNp8rVr1w4bGxv8/PyoWbOmxuiGn58fs2bNYujQoQQHB3Pjxg3Gjh3LO++8o9V0JCGEKM/S0tIYMmQI165dw8LCAjc3N2JiYvDy8iI2Nlb95WOdOnU0trt06RJOTk5lELEQL4cKc1nVIUOGcP/+fVq0aMGYMWMYP368+soGISEh2Nvb07ZtWwYPHszkyZMLHWYcNGgQenp6DBo0iEqVKmndd2hoKEOGDGHSpEm4uLjQq1cv4uPjcXBwKLS9n58fQUFBTJ48WT3Nxt/fv0R91qlThxYtWvDHH38U+Pa6adOmfP/990RGRtKwYUNmzpzJJ5988tQnPJeW4l4nMzMzFi5cSLNmzWjevDmXL18mOjoaHZ0nv40rV67M2rVradOmDW5ubuzdu5ft27erp8+U9PUriWHDhrFu3TpCQ0Np1KgR7du3JywsrMgrYtWsWZMffviBH3/8ETc3N1atWsX06dMBCv1WvzAqlYpBgwZx8uTJAu8NY2NjYmJi+Pfff2nevDn9+vWjU6dOWo+oCSFEebZ+/XouX75MVlYWaWlp7N27Fy8vLwA8PT1RFKXQhxQL4lWnUh6fNP4Ku3z5MrVr1yY+Pp6mTZuWat9eXl7Y2Njw7bfflmq/4uU3b948Vq9erdUlZMtCeno6FhYW3Lx5U85heIGys7OJjo5Wn9guXgzJc+mRXJcOyXPpKI95zv/7nX+/kSepMFOSnkV2djb//PMPH3/8Ma1atXrhxcK9e/dYvXo13t7e6OrqsnHjRvUJuUJ8+eWXNG/eHEtLSw4dOsSiRYsK3FhQCCGEEKK0VJgpSc/i0KFD2NraEh8fz+rVqzXW/frrr5iamhb5eBoqlYro6GjatWvH66+/zvbt24mKilJf+vJJ/f3666/PfLz/FR4eXmRfDRo0eK59vShleQwv4rU6f/48PXv2pH79+syZM4dJkyYRHBwMQNeuXYvs71nvGSGEEEIIURgZYeD/5i0WplmzZpw4ceK59mdkZMTevXuLXP+k/qpXr/5cY3nzzTcLXPIzX3kZUivLY3gRr9XSpUtZunRpoevWrVunvmrH44q7UpQQQgghxNOQgqEYRkZGBa6W8KKVZn9mZmaYmZmVWn8vQlkeQ2m/N553wSiEEEIIURyZkiSEEEIIIYQokhQMQgghhHglrFq1Cjc3N8zNzTE3N8fDw4OdO3cC8O+//zJ27FhcXFwwMjLCwcGBcePGcefOnTKOWoiyJwWDEKUgNjYWlUrF7du3X3hfYWFhGjezCw4OpkmTJi+8XyGEeNnVqFGDBQsWkJiYSEJCAh07dqRnz56cPn2aq1evcvXqVRYvXsypU6cICwtj165dBAQElHXYQpQ5OYdBvHKCg4PZunXrcz+Z/WUxcOBAfH19yzoMIYR46fTo0UPj+bx581i1ahVHjx4lICCAqKgo9bratWszb9483n77bXJyctDTk49M4tUl734hKhgjIyOMjIzKOgwhhHip5ebmsnnzZjIzM/Hw8Ci0Tf4NraRYEK86+QkQ5VJeXh6LFy/mq6++4sqVK1hbW/P+++8zffp0pk2bxpYtW/j777+xsbHBz8+PmTNnoq+vT1hYGLNnzwYe3Q8DIDQ0FH9//yL7Gjx4MLm5uWzatEm9LDs7G1tbW5YsWcKQIUPIyspiypQpREZGkp6eTrNmzVi6dCnNmzcv8bH99ddfBAYGcvDgQR4+fIiTkxOLFi3C19eX2NhYOnTowM8//0xQUBDnzp2jSZMmrFu3joYNGwKPpiRNmDChyOlPFy9exMvLC19fX1asWMHDhw+ZPn06Gzdu5Pbt2zRs2JDPPvsMT0/PEsXdcv4+cvRMSny8QjuGugoLW0DD4BiyclVlHU6FJXkuPc8z15cXdNO6bVJSEh4eHjx48ABTU1O2bNlC/fr1C7S7efMmc+bMYcSIEc8UmxAVgRQMolwKCgpi7dq1LF26lDfeeINr165x9uxZ4NFlVsPCwrCzsyMpKYnhw4djZmbG1KlTGThwIKdOnWLXrl3qe2FYWFg8sS8/Pz/69+9PRkaG+mZ9MTEx3Lt3j969ewMwdepUoqKi+Prrr3F0dGThwoV4e3tz4cKFEt8fYcyYMTx8+JBffvkFExMTzpw5U+AmgVOmTOHzzz/HxsaGjz76iB49enDu3Lli7zvxxx9/4O3tTUBAAHPnzgUgMDCQM2fOEBkZiZ2dHVu2bMHHx4ekpCScnZ0L7CMrK4usrCz18/T0dAAMdRR0dQu/n4l4doY6isa/4sWQPJee55nr7OxsrdvWqlWL+Ph40tPTiYqKYujQoezdu1ejaEhPT8fX1xdXV1emT59eov2/bPJjL8/HUB6UxzyXJFaVUtQdy4R4Sd29excrKytWrlzJsGHDim2/ePFiIiMjSUhIAEp+DkNOTo56NOGdd94BHo065OXlERkZSWZmJlWqVCEsLIzBgwcDj34InZycmDBhAlOmTFGPDNy6dUvjhOTCuLm50bdvX2bNmlVgXf5+IiMjGThwIPDoyh41atQgLCyMAQMGFBhhyD/eL7/8ku7duzN9+nQmTZoEQGpqKrVq1SI1NRU7Ozt1P507d6ZFixaF3j06ODhYPUrzXxERERgbGxefUCGEeInMnDkTGxsbRo8eDcD9+/cJDg7G0NCQjz/+GAMDgzKOUIgX4969ewwePFg99e5JZIRBlDvJyclkZWXRqVOnQtdv2rSJ5cuXc/HiRTIyMsjJySn2B+FJ9PT0GDBgAOHh4bzzzjtkZmby008/ERkZCTya4pOdnU2bNm3U2+jr69OiRQuSk5NL3N+4ceMYNWoUu3fvpnPnzvTt2xc3NzeNNv+db1u1alVcXFye2FdqaipeXl7MmzePCRMmqJcnJSWRm5tL3bp1NdpnZWVhaWlZ6L6CgoKYOHGi+nl6ejr29vbM/V2HHH3dkhyqKAFDHYU5zfKYkaBDVp5MlXlRJM+l53nm+lSw91Nvu2zZMqytrfH19SU9PZ1u3bphbW3Ntm3bKsSXINnZ2ezZswcvL69iR6HF0yuPec6fIaANKRhEufOkE3qPHDmCn58fs2fPxtvbGwsLCyIjIwkJCXmmPv38/Gjfvj1paWns2bMHIyMjfHx8nmmfRRk2bBje3t7s2LGD3bt3M3/+fEJCQhg7duxT79PKygo7Ozs2btzIe++9py6gMjIy0NXVJTExEV1dzQ/7j0+DymdoaIihoWGB5b9M61xkkSGeXXZ2NtHR0STO9Ck3f4zKI8lz6SmLXAcFBdG1a1ccHBy4e/cuERERxMXFERMTw/379+nWrRv37t0jPDyc+/fvc//+feDR79DHf0eWN/r6+vKeLgXlKc8liVPuwyDKHWdnZ4yMjNi3b1+BdYcPH8bR0ZHp06fTrFkznJ2d+euvvzTaGBgYkJubW6I+W7dujb29PZs2bSI8PJz+/furf9Bq166NgYEBhw4dUrfPzs4mPj6+0BPptGFvb8/IkSP58ccfmTRpEmvXrtVYf/ToUfX/b926xblz53B1dS1yf0ZGRvz8889UqlQJb29v7t69C4C7uzu5ubmkpaVRp04djYeNjc1TxS6EEC+rtLQ0hgwZgouLC506dSI+Pp6YmBi8vLz47bffOHbsGElJSdSpUwdbW1v148qVK2UduhBlSkYYRLlTqVIlpk2bxtSpUzEwMKBNmzbcuHGD06dP4+zsTGpqKpGRkTRv3pwdO3awZcsWje2dnJy4dOkSJ06coEaNGpiZmRX6jfnjBg8ezOrVqzl37hwHDhxQLzcxMWHUqFFMmTKFqlWr4uDgwMKFC7l3795T3fBnwoQJdO3albp163Lr1i0OHDhQoBj45JNPsLS0xNramunTp/Paa6/Rq1evJ+7XxMSEHTt20LVrV7p27cquXbuoW7cufn5+DBkyhJCQENzd3blx4wb79u3Dzc2Nbt20v/KIEEK87NavX1/kOk9PT+S0TiEKJyMMolyaMWMGkyZNYubMmbi6ujJw4EDS0tJ48803+eCDDwgMDKRJkyYcPnyYGTNmaGzbt29ffHx86NChA1ZWVmzcuFGrPv38/Dhz5gzVq1fXOF8BYMGCBfTt25d33nmHpk2bcuHCBWJiYqhSpUqJjy03N5cxY8bg6uqKj48PdevW5csvvyzQ3/jx43n99de5fv0627dv1+rEPFNTU3bu3ImiKHTr1o3MzExCQ0MZMmQIkyZNwsXFhV69ehEfH4+Dg0OJYxdCCCFExSNXSRKiHCnJ1ZZKS3p6OhYWFty8eVPOYXiB8ud7+/r6lpv5seWR5Ln0SK5Lh+S5dJTHPOf//dbmKkkywiCEEEIIIYQokhQM4pUXHh6OqalpoY8GDRo89/66du1aZH+F3fdACCGEEKIsyUnP4pX35ptv0rJly0LXvYhhxXXr1qkv1fe44u4KLSflCSGEEKK0ScEgXnlmZmaYmZmVWn/Vq1cvtb6EEEIIIZ6VTEkSQgghhBBCFEkKBiGEEEK8ElatWoWbmxvm5uaYm5vj4eHBzp07Afj3338ZO3YsLi4uGBkZ4eDgwLhx47hz504ZRy1E2ZOCoQLw9PRkwoQJRa5XqVRs3bpV6/3FxsaiUqm4ffv2M8f2MiguPy/S887l5cuXUalUnDhx4rnsTwghXiU1atRgwYIFJCYmkpCQQMeOHenZsyenT5/m6tWrXL16lcWLF3Pq1CnCwsLYtWvXU92AU4iKRs5heAVcu3btqW4g9jJ6Ge9DIIQQonzo0aOHxvN58+axatUqjh49SkBAAFFRUep1tWvXZt68ebz99tvk5OSgpycfmcSrS979rwAbG5uyDqHcefjwoVZ3ThZCCFE+5ebmsnnzZjIzM/Hw8Ci0Tf4NraRYEK86+QmoIPLy8pg6dSrr1q3DwMCAkSNHEhwcDDyakrRlyxZ69eoFwOHDhxk9ejRnz56lYcOGfPzxx/Tu3Zvff/+dJk2aqPeZmJjItGnTOHPmDE2aNCE0NBQXFxet4tm+fTuffPIJSUlJmJqa0rZtW7Zs2QLArVu3GD9+PNu3bycrK4v27duzfPlynJ2dAfjrr78IDAzk4MGDPHz4ECcnJxYtWkT9+vXp0KEDgHrEZOjQoYSFhRUbT05ODoGBgXz77bfo6+szatQoPvnkE1QqFQBOTk4EBARw/vx5tm7dSp8+fQgLC+PgwYMEBQWRkJDAa6+9Ru/evZk/fz4mJiYAfPvtt3z++eekpKRgYmJCx44dWbZsGdWqVSs0jnv37tG3b1/S09PZsWNHsaMkx48f5/333yc5OZmGDRsyffp0jfW5ubmMGDGC/fv3c/36dRwcHBg9ejTjx48H4JdffqFTp05cuXJFo3CcMGECiYmJ/Prrr0Xm29fXt9i8/lfL+fvI0TMp0TZCe4a6CgtbQMPgGLJyVWUdToUleS49zzPXlxd007ptUlISHh4ePHjwAFNTU7Zs2UL9+vULtLt58yZz5sxhxIgRzxSbEBWBFAwVxNdff83EiRM5duwYR44cwd/fnzZt2uDl5aXRLj09nR49euDr60tERAR//fVXkfP7p0+fTkhICFZWVowcOZL33nuPQ4cOFRvLjh076N27N9OnT+ebb77h4cOHREdHq9f7+/tz/vx5tm3bhrm5OdOmTcPX15czZ86gr6/PmDFjePjwIb/88gsmJiacOXMGU1NT7O3tiYqKom/fvqSkpGBubo6RkZHW+QkICOD48eMkJCQwYsQIHBwcGD58uLrN4sWLmTlzJrNmzQLg4sWL+Pj4MHfuXDZs2MCNGzcIDAwkMDCQ0NBQ4NGt4OfMmYOLiwtpaWlMnDgRf39/jePNd/v2bbp164apqSl79uzB2Nj4iTFnZGTQvXt3vLy8+O6777h06ZK6EMiXl5dHjRo12Lx5M5aWlhw+fJgRI0Zga2vLgAEDaNeuHbVq1eLbb79lypQp6pjDw8NZuHAhQJH5LkpWVhZZWVnq5+np6QAY6ijo6so9Il4UQx1F41/xYkieS8/zzHV2drbWbWvVqkV8fDzp6elERUUxdOhQ9u7dq1E0pKen4+vri6urK9OnTy/R/l82+bGX52MoD8pjnksSq0qRu0CVe56enuTm5vLrr7+ql7Vo0YKOHTuyYMECjRGG1atX8/HHH/P3339TqVIl4NGNxIYPH64eYcg/T2Dv3r106tQJgOjoaLp168b9+/fV2xWldevW1KpVi++++67AuvPnz1O3bl0OHTpE69atAfjnn3+wt7fn66+/pn///ri5udG3b1/1B/f/eppzGDw9PUlLS+P06dPqEYUPP/yQbdu2cebMGeDRCIO7u7t6FARg2LBh6OrqsmbNGvWygwcP0r59ezIzMwvNQ0JCAs2bN+fu3buYmpqq401OTmbgwIE4OzsTERGh1XSnr776io8++kjjtVq9ejWjRo0qMBr0X4GBgVy/fp0ffvgBgIULFxIWFqY+1h9//JGhQ4dy/fp1TExMnpjvwgQHBzN79uwCyyMiIootgoQQ4mUzc+ZMbGxsGD16NAD3798nODgYQ0NDPv74Y5meKiqse/fuMXjwYPXUuyeREYYKws3NTeO5ra0taWlpBdqlpKTg5uam8WG3RYsWxe7T1tYWgLS0NBwcHJ4Yy4kTJzS+uf+v5ORk9PT0NO6sbGlpiYuLC8nJyQCMGzeOUaNGsXv3bjp37kzfvn0LHF9JtWrVSl0sAHh4eBASEkJubi66uroANGvWTGObkydP8scffxAeHq5epigKeXl5XLp0CVdXVxITEwkODubkyZPcunWLvLw8AFJTUzW+rfLy8qJFixZs2rRJ3V9xkpOTC7xWhc2z/eKLL9iwYQOpqancv3+fhw8fahQT/v7+fPzxxxw9epRWrVoRFhbGgAED1NOqSprvoKAgJk6cqH6enp6Ovb09HTp0wNLSUqtjEyWXnZ3Nnj178PLyeiF3IBePSJ5Lz8uS62XLlmFtbY2vry/p6el069YNa2trtm3bViG+BHlZ8lzRlcc8588Q0IYUDBXE429OlUql/vD6PPaZ/2Fbm31qO02oKMOGDcPb25sdO3awe/du5s+fT0hICGPHjn2m/RYn/wN0voyMDN5//33GjRtXoK2DgwOZmZl4e3vj7e1NeHg4VlZWpKam4u3tzcOHDzXad+vWjaioKM6cOUOjRo2eW8yRkZFMnjyZkJAQPDw8MDMzY9GiRRw7dkzdplq1avTo0YPQ0FBq1qzJzp07iY2NVa8vab4NDQ0xNDQssFxfX7/c/JIszyTPpUPyXHpKM9dBQUF07doVBwcH7t69S0REBHFxccTExHD//n26devGvXv3CA8P5/79+9y/fx8AKysrrb/seVnJe7p0lKc8lyROuQ/DK8bFxYWkpCSNOejx8fHPtQ83Nzf27dtX6DpXV1dycnI0PtD+888/pKSkaHwjb29vz8iRI/nxxx+ZNGkSa9euBVAPDefm5pYopv/2B3D06FGcnZ2f+AegadOmnDlzhjp16hR4GBgYcPbsWf755x8WLFhA27ZtqVevXqGjOgALFixg6NChdOrUST01qDiurq788ccfPHjwQCPu/8qf2jV69Gjc3d2pU6cOFy9eLLCvYcOGsWnTJr766itq165NmzZtNNYXlW8hhKhI0tLSGDJkCC4uLnTq1In4+HhiYmLw8vLit99+49ixYyQlJVGnTh1sbW3VjytXrpR16EKUKSkYXjGDBw8mLy+PESNGkJycTExMDIsXLwbQmLLzLGbNmsXGjRuZNWsWycnJJCUl8dlnnwHg7OxMz549GT58OAcPHuTkyZO8/fbbVK9enZ49ewKPruATExPDpUuX+O233zhw4ACurq4AODo6olKp+Pnnn7lx4wYZGRlaxZSamsrEiRNJSUlh48aNrFixosAJxI+bNm0ahw8fJjAwkBMnTnD+/Hl++uknAgMDgUejDAYGBqxYsYI///yTbdu2MWfOnCL3t3jxYvz8/OjYsSNnz54tNubBgwejUqkYPnw4Z86cITo6Wv1a5XN2diYhIYGYmBjOnTvHjBkzCi0Avb29MTc3Z+7cubz77rsa656UbyGEqEjWr1/P5cuXycrKIi0tjb1796ovDuLp6YmiKIU+nJycyjZwIcqYFAyvGHNzc7Zv386JEydo0qQJ06dPZ+bMmQDFnsysLU9PTzZv3sy2bdto0qQJHTt25Pjx4+r1oaGhvP7663Tv3h0PDw8URSE6Olo9NJabm8uYMWNwdXXFx8eHunXr8uWXXwJQvXp1Zs+ezYcffoi1tbX6w3txhgwZwv3792nRogVjxoxh/PjxxV4qz83Njbi4OM6dO0fbtm1xd3dn5syZ2NnZAY+GqMPCwti8eTP169dnwYIFBT7QP27p0qUMGDCAjh07cu7cuSe2NTU1Zfv27SQlJeHu7s706dPVhVe+999/nz59+jBw4EBatmzJP//8oz5x7790dHTw9/cnNzeXIUOGaKx7Ur6FEEIIIeQqSYLw8HDeffdd7ty588znH4iXV0BAADdu3GDbtm3Pdb/p6elYWFhw8+ZNOen5BcrOziY6OhpfX99yMz+2PJI8lx7JdemQPJeO8pjn/L/fcpUkUahvvvmGWrVqUb16dU6ePMm0adMYMGCAFAsV1J07d0hKSiIiIuK5FwtCCCGEqPhkStIr6Pr167z99tu4urrywQcf0L9/f7766iutt2/QoAGmpqaFPv57CdLSkJqaWmQspqampKamlmo8JfHpp58WGXfXrl2fWz89e/akS5cujBw5ssCN/IQQQgghiiMjDK+gqVOnMnXq1KfePjo6usi7A1pbWz/1fp+GnZ0dJ06ceOL6l9XIkSMZMGBAoeue52jPfy+hKoQQQghRUlIwiBJzdHQs6xDU9PT0qFOnTlmH8VSqVq1K1apVyzoMIYQQQognkilJQgghhBBCiCJJwSDEC6ZSqdi6dWup9RccHEyTJk1KrT8hhCgvVq1ahZubG+bm5pibm+Ph4cHOnTsB+Pfffxk7diwuLi4YGRnh4ODAuHHjuHPnThlHLUTZk4JBiHLiRRQeYWFhqFQqjcfzuh+HEEK8bGrUqMGCBQtITEwkISGBjh070rNnT06fPs3Vq1e5evUqixcv5tSpU4SFhbFr1y4CAgLKOmwhypycwyDEK87c3JyUlBT18+d1x28hhHjZ9OjRQ+P5vHnzWLVqFUePHiUgIICoqCj1utq1azNv3jzefvttcnJy0NOTj0zi1SUjDEI8wVdffYWdnR15eXkay3v27Ml7770HPBrirl27NgYGBri4uPDtt98+VV8PHz4kMDAQW1tbKlWqhKOjI/PnzwfAyckJgN69e6NSqdTPARYsWIC1tTVmZmYEBATw4MGDEvWrUqmwsbFRP0r7SldCCFEWcnNziYyMJDMzEw8Pj0Lb5N/QSooF8aqTnwAhnqB///6MHTuWAwcO0KlTJ+DRPNddu3YRHR3Nli1bGD9+PMuWLaNz5878/PPPvPvuu9SoUYMOHTqUqK/ly5ezbds2vv/+exwcHLhy5QpXrlwBID4+nmrVqhEaGoqPjw+6uroAfP/99wQHB/PFF1/wxhtv8O2337J8+XJq1aqldb8ZGRk4OjqSl5dH06ZN+fTTT2nQoEGR7bOyssjKylI/T09PB6DdZ3vJ0Tcp0TEL7RnqKMxpBq9/sousPBkFelEkz6Xneeb6VLC31m2TkpJo164dDx48wNTUlM2bN+Ps7FzgcuE3b95kzpw5BAQEFHkp8fIgP/byfAzlQXnMc0liVSmKorzAWIQo93r16oWlpSXr168HHo06zJ49mytXrtC2bVsaNGigceO7AQMGkJmZyY4dO4BH3+Bv2bKFXr16PbGfcePGcfr0afbu3VvotKDC9tO6dWvc3d354osv1MtatWrFgwcPnnh/inxHjhzh/PnzuLm5cefOHRYvXswvv/zC6dOnqVGjRqHbBAcHM3v27ALLIyIiMDY2LrZPIYQoS9nZ2dy8eZPMzEyOHDnCnj17mDdvHvb29uo29+7dY9asWZiZmfHRRx/JCIOokO7du8fgwYPVI2lPIj8BQhTDz8+P4cOH8+WXX2JoaEh4eDhvvfUWOjo6JCcnM2LECI32bdq04fPPPy9xP/7+/nh5eeHi4oKPjw/du3enS5cuT9wmOTmZkSNHaizz8PDgwIEDWvXp4eGhMRTfunVrXF1dWbNmDXPmzCl0m6CgICZOnKh+np6ejr29PXN/1yFHX1erfkXJPfo2No8ZCTryzfcLJHkuPc8z1yUZYfivcePG4ePjw8mTJ3n//fcBuHv3Lt26dcPe3p6tW7eW+wtBZGdns2fPHry8vNDX1y/rcCqs8pjn/BkC2pCCQYhi9OjRA0VR2LFjB82bN+fXX39l6dKlz72fpk2bcunSJXbu3MnevXsZMGAAnTt35ocffnjufRVFX18fd3d3Lly4UGQbQ0NDDA0NCyz/ZVpnLC0tX2R4r7Ts7Gyio6NJnOlTbv4YlUeS59LzsuRaURSys7PR19cnPT2dbt26YWhoyPbt2yvUqKm+vr68p0tBecpzSeKUk56FKEalSpXo06cP4eHhbNy4ERcXF5o2bQqAq6srhw4d0mh/6NAh6tev/1R9mZubM3DgQNauXcumTZuIiori33//BR79YOfm5mq0d3V15dixYxrLjh49+lR9w6OTAJOSkrC1tX3qfQghxMsqKCiIX375hcuXL5OUlERQUBCxsbH4+fmRnp5Oly5dyMzMZP369aSnp3P9+nWuX79e4HevEK8aGWEQQgt+fn50796d06dP8/bbb6uXT5kyhQEDBuDu7k7nzp3Zvn07P/74I3v37i1xH0uWLMHW1hZ3d3d0dHTYvHkzNjY2VK5cGXh0paR9+/bRpk0bDA0NqVKlCuPHj8ff359mzZrRpk0bwsPDOX36tNYnPX/yySe0atWKOnXqcPv2bRYtWsRff/3FsGHDShy/EEK87NLS0hgyZAjXrl3DwsICNzc3YmJi8PLyIjY2Vv0FTJ06dTS2u3TpksbV6YR41UjBIIQWOnbsSNWqVUlJSWHw4MHq5b169eLzzz9n8eLFjB8/npo1axIaGoqnp2eJ+zAzM2PhwoWcP38eXV1dmjdvTnR0NDo6jwYCQ0JCmDhxImvXrqV69epcvnyZgQMHcvHiRaZOncqDBw/o27cvo0aNIiYmRqs+b926xfDhw7l+/TpVqlTh9ddf5/Dhw089QiKEEC+z/ItXFMbT0xO5DowQhZOrJAkhnkl6ejoWFhbcvHlTzmF4gfLne/v6+pab+bHlkeS59EiuS4fkuXSUxzzn//3W5ipJcg6DEEIIIYQQokhSMAhRSj799FNMTU0LfXTt2vWF9FlUf6ampvz6668vpE8hhBBCVCxyDoMQpWTkyJEMGDCg0HVGRkYvpM8n3bytevXqL6RPIYQQQlQsUjAIUUqqVq1K1apVS7XPx6/0IYQQQghRUjIlSQghhBBCCFEkKRiEEEIIUe6tWrUKNzc3zM3NMTc3x8PDg507d6rXf/XVV3h6emJubo5KpeL27dtlF6wQ5YwUDC9QcHAwTZo0eWKby5cvo1KpnjjXXJQvsbGxL+yPkbxfhBCicDVq1GDBggUkJiaSkJBAx44d6dmzJ6dPnwbg3r17+Pj48NFHH5VxpEKUP1IwvECTJ09m37596uf+/v706tVLo429vT3Xrl2jYcOGpRxdyWhT/IhHWrdurb6LKEBYWJj6bs0vm+DgYFQqlcajXr16ZR2WEEKUWI8ePfD19cXZ2Zm6desyb948TE1NOXr0KAATJkzgww8/pFWrVmUcqRDlj5z0/ALlX77ySXR1dbGxsSmliAp6+PAhBgYGpdafoijk5uaip1dx33oGBgZl+pqWVIMGDdi7d6/6eUV+bYQQr4bc3Fw2b95MZmYmHh4eZR2OEOVemX4y2LVrF3PnzuXUqVPo6uri4eHB559/Tu3atWndujVt27bls88+U7e/ceMGdnZ27Nu3j3bt2nHt2jWGDRvG/v37sbGxYd68eXz00UdMmDCBCRMmFNu/SqXiyy+/ZNu2bcTGxmJra8vChQvp16+fuk1SUhLjx4/nyJEjGBsb07dvX5YsWaIuBGJjY5k6dSqnT59GX1+fBg0aEBERgaOjI8HBwWzdupUTJ04QHBzM119/re4X4MCBAzg5OVGzZk1+//133NzccHBwYPr06YwaNUodw++//87rr7/OpUuXcHR05Pbt20yePJmffvqJrKwsmjVrxtKlS2ncuHGxx5wfU2BgIPPmzeOvv/4iLy/vifsMCwtj9uzZGrGHhobi6empjj1/9OH27dtUqVKFAwcO4OnpSWxsLB06dCA6OpqPP/6YpKQkdu/eTXBwMG5ublSqVIl169ZhYGDAyJEjCQ4OLvYY8uNYvXo127dvZ//+/Tg6OrJhwwasrKwYNmwY8fHxNG7cmG+//ZbatWsDcPHiRSZOnMjRo0fJzMzE1dWV+fPn07lzZ/V+tXlPqVQq1q5dy44dO4iJiaF69eqEhITw5ptvqt8THTp04NatW5w4cYJ3331XI3ezZs1Sf7O/ZcsWjVGnypUrs2zZMvz9/QE4fvw477//PsnJyTRs2JDp06cXyMWpU6eYMmUKv/76KyYmJnTp0oWlS5fy2muvaZVLPT2951LgtJy/jxw9k2fejyicoa7CwhbQMDiGrFxVWYdTYUmeS4+2ub68oJvW+0xKSsLDw4MHDx5gamrKli1bqF+//vMIV4hXWpkWDJmZmUycOBE3NzcyMjKYOXMmvXv35sSJE/j5+bFw4UIWLFig/qC1adMm7OzsaNu2LQBDhgzh5s2bxMbGoq+vz8SJE0lLSytRDDNmzGDBggV8/vnnfPvtt7z11lskJSXh6upKZmYm3t7eeHh4EB8fT1paGsOGDSMwMJCwsDBycnLo1asXw4cPZ+PGjTx8+JDjx4+r4/2vyZMnk5ycTHp6OqGhocCjy2xevXpV3UZHR4dBgwYRERGhUTCEh4fTpk0bHB0dAejfvz9GRkbs3LkTCwsL1qxZQ6dOnTh37pxWl+28cOECUVFR/Pjjj+jq6ha7z4EDB3Lq1Cl27dql/ibawsKC//3vf1rn+cMPP2Tx4sXUqlWLKlWqAPD1118zceJEjh07xpEjR/D396dNmzZ4eXlptc85c+awZMkSlixZwrRp0xg8eDC1atUiKCgIBwcH3nvvPQIDA9UnvWVkZODr68u8efMwNDTkm2++oUePHqSkpODg4ABo/56aPXs2CxcuZNGiRaxYsQI/Pz/++uuvAvlv3bo1y5YtY+bMmaSkpAAUO+qULyMjg+7du+Pl5cV3333HpUuXGD9+vEab27dv07FjR4YNG8bSpUu5f/8+06ZNY8CAAezfv1+rfs6fP4+dnR2VKlXCw8OD+fPnq/NRmKysLLKystTP09PTATDUUdDVVbTqU5ScoY6i8a94MSTPpUfbXGdnZ2u9z1q1ahEfH096ejpRUVEMHTqUvXv3ahQNOTk56v2WZN/lVf4xvgrHWpbKY55LEmuZFgx9+/bVeJ7/DfGZM2cYMGAAEyZM4ODBg+oCISIigkGDBqFSqTh79ix79+4lPj6eZs2aAbBu3TqcnZ1LFEP//v0ZNmwY8OgD6J49e1ixYgVffvklERERPHjwgG+++QYTk0ffnK5cuZIePXrw2Wefoa+vz507d+jevbv6W2xXV9dC+zE1NcXIyIisrKwnfpvr5+dHSEgIqampODg4kJeXR2RkJB9//DEABw8e5Pjx46SlpWFoaAjA4sWL2bp1Kz/88AMjRowo9pgfPnzIN998g5WVldb7NDU1faZvoj/55JMChYCbmxuzZs0CwNnZmZUrV7Jv3z6tC4Z3331XfSO0adOm4eHhwYwZM/D29gZg/Pjx6m/3ARo3bqwxCjNnzhy2bNnCtm3bCAwMLNF7yt/fn0GDBgGP7uC8fPlyjh8/jo+Pj0Y7AwMDLCwsUKlUJc5dREQEeXl5rF+/nkqVKtGgQQP+/vtvjWJy5cqVuLu78+mnn6qXbdiwAXt7e86dO0fdunWf2EfLli0JCwvDxcWFa9euMXv2bNq2bcupU6cwMzMrdJv58+erR5z+62P3PIyNc0t0jKLk5jTLK+sQXgmS59JTXK6jo6Ofar9t2rQhJiaGqVOnMnr0aPXypKQkAHbv3q31FzgVwZ49e8o6hFdCecrzvXv3tG5bpgXD+fPnmTlzJseOHePmzZvk5T36pZGamkrDhg3p0qUL4eHhtG3blkuXLnHkyBHWrFkDQEpKCnp6ejRt2lS9vzp16qi/vdbW43MbPTw81FegSU5OpnHjxupiAR79AsrLyyMlJYV27drh7++Pt7c3Xl5edO7cmQEDBmBra/s06QCgSZMmuLq6EhERwYcffkhcXBxpaWn0798fgJMnT5KRkYGlpaXGdvfv3+fixYta9eHo6KguFp7XPouT/wH8v9zc3DSe29ralmiE6L/bW1tbA9CoUSONZQ8ePCA9PR1zc3MyMjIIDg5mx44dXLt2jZycHO7fv09qaipQsvfUf/s2MTHB3Ny8xKNbxUlOTlZP28r3+Pv15MmTHDhwoNA/ehcvXiy2YOjatav6/25ubrRs2RJHR0e+//57AgICCt0mKCiIiRMnqp+np6djb2/P3N91yNHX1erYRMkZ6ijMaZbHjAQdsvJkqsyLInkuPdrm+lSw91P3sWzZMqytrfH19VUvy/+b3qVLl5f2ghTPU3Z2Nnv27MHLywt9ff2yDqfCKo95zp8hoI0yLRh69OiBo6Mja9euxc7Ojry8PBo2bMjDhw+BR9+2jxs3jhUrVhAREUGjRo00PhC+DEJDQxk3bhy7du1i06ZNfPzxx+zZs+eZrsLg5+enLhgiIiLw8fFRf5jPyMjA1taW2NjYAttp+4vvvwXQs+xTR+fRRbYU5f+Gk4sa3nq8T6DAD5RKpVIXjdr47/b508AKW5a/z8mTJ7Nnzx4WL15MnTp1MDIyol+/fur3W0k8a+z52/w3d1DyocyMjAz1iNfjnqZwrVy5MnXr1uXChQtFtjE0NFSPRP3XL9M6Fyg6xfOTnZ1NdHQ0iTN9ys0fo/JI8lx6nneug4KC6Nq1Kw4ODty9e5eIiAji4uKIiYlBX1+f69evc/36dS5fvgzA2bNnMTMzw8HBQavpvOWdvr6+vKdLQXnKc0niLLPLqv7zzz+kpKTw8ccf06lTJ1xdXbl165ZGm549e/LgwQN27dpFREQEfn5+6nUuLi7k5OTw+++/q5dduHChwD6Kk3+5tf8+z59W5OrqysmTJ8nMzFSvP3ToEDo6Ori4uKiXubu7ExQUxOHDh2nYsCERERGF9mVgYEBubvFTNgYPHsypU6dITEzkhx9+0Djupk2bcv36dfT09KhTp47GQ9uTXB+nzT4Liz1/lOLatWvqZS/z/QEOHTqEv78/vXv3plGjRtjY2Kj/cMDze089rqjX3crKSiN358+f1xgedHV15Y8//uDBgwfqZY+/X5s2bcrp06dxcnIq8NoVVqQVJyMjg4sXLz7TKJkQQpSFtLQ0hgwZgouLC506dSI+Pp6YmBj1NNfVq1fj7u7O8OHDAWjXrh3u7u5s27atLMMWolwos4KhSpUqWFpa8tVXX3HhwgX279+vMc0BHn0r3atXL2bMmEFycrJ6zjhAvXr16Ny5MyNGjOD48eP8/vvvjBgxAiMjo0JPOi7K5s2b2bBhA+fOnWPWrFkcP36cwMBA4NE3/ZUqVWLo0KGcOnWKAwcOMHbsWN555x2sra25dOkSQUFBHDlyhL/++ovdu3dz/vz5Is9jcHJy4o8//iAlJYWbN28W+W2yk5MTrVu3JiAggNzcXPXVdwA6d+6Mh4cHvXr1Yvfu3Vy+fJnDhw8zffp0EhIStD7u/9Jmn05OTly6dIkTJ05w8+ZNsrKyMDIyolWrVixYsIDk5GTi4uLU51q8jJydnfnxxx85ceIEJ0+eZPDgwRqjAs/rPfU4JycnMjIy2LdvHzdv3lQXBR07dmTlypX8/vvvJCQkMHLkSI1qf/DgwahUKoYPH86ZM2eIjo5m8eLFGvseM2YM//77L4MGDSI+Pp6LFy8SExPDu+++q1VxOnnyZOLi4tSvee/evdHV1dX4WRNCiPJg/fr1XL58maysLNLS0ti7d6/GOXHBwcEoilLgkX9VOiFE0cqsYNDR0SEyMpLExEQaNmzIBx98wKJFiwq08/Pz4+TJk7Rt27bAlVu++eYbrK2tadeuHb1792b48OGYmZlpzPkuzuzZs4mMjMTNzY1vvvmGjRs3qq+mYGxsTExMDP/++y/NmzenX79+dOrUiZUrV6rXnz17lr59+1K3bl1GjBjBmDFjeP/99wvta/jw4bi4uNCsWTOsrKw4dOhQkXHlH3fv3r0xMjJSL1epVERHR9OuXTveffdd6taty1tvvcVff/2lnsdfUtrss2/fvvj4+NChQwesrKzYuHEj8OgE25ycHF5//XUmTJjA3LlznyqG0rBkyRKqVKlC69at6dGjB97e3hrnK8DzeU89rnXr1owcOZKBAwdiZWXFwoULAQgJCcHe3p62bdsyePBgJk+ejLGxsXo7U1NTtm/fTlJSEu7u7kyfPr3A1CM7OzsOHTpEbm4uXbp0oVGjRkyYMIHKlSurp4w9yd9//82gQYNwcXFhwIABWFpacvToUY1zXIQQQgjxalMpj0+iLsf+/vtv7O3t2bt3L506dSq2fWHXwRfiv0r6nnoVpaenY2Fhwc2bN+Uchhcof763r69vuZkfWx5JnkuP5Lp0SJ5LR3nMc/7f7zt37mBubv7EtuX6lq779+8nIyODRo0ace3aNaZOnYqTkxPt2rUr69BEOSXvKSGEEEIITWU2Jel5yM7O5qOPPqJBgwb07t0bKysr9Q23wsPDMTU1LfTRoEGDsg79hWnQoEGRxx0eHl7W4WmlLF+7J72nypvU1NQi82hqaqq+nKwQQgghxJOU6xEGb29v9U26Hvfmm2/SsmXLQtflf/irQLOx1KKjo4s8mfppz3Eobdq8di/Kk95T5Y2dnd0Tr1plZ2dXesEIIYQQotwq1wXDk5iZmRV5p9qKzNHRsaxDeGav6mv3vOVfJlcIIYQQ4lmU6ylJQgghhBBCiBdLCoZXhKenJxMmTCjrMAq4fPkyKpXqiVNnYmNjUalU3L59u9TiehJtYn6a/Tx+nGFhYVrfvVsIIV51q1atws3NDXNzc8zNzfHw8GDnzp3q9V999RWenp6Ym5u/VH9ThCgPpGAQZcre3p5r167RsGHDsg7lhfL39y9w+d7ijn3gwIGcO3dO/Tw4OJgmTZo817iCg4NRqVQaj3r16j3XPoQQojTUqFGDBQsWkJiYSEJCAh07dqRnz56cPn0agHv37uHj48NHH31UxpEKUf5U2HMYRPmgq6uLjY1NWYdRJoo7diMjI42b9r0oDRo0YO/evernenrya0EIUf706NFD4/m8efNYtWoVR48epUGDBupR9tjY2NIPTohyTkYYKqDMzEyGDBmCqakptra2hISEaKy/desWQ4YMoUqVKhgbG9O1a1fOnz8PPLpylJWVFT/88IO6fZMmTbC1tVU/P3jwIIaGhty7dw94dAO8devW0bt3b4yNjXF2dmbbtm0a/fn5+WFlZYWRkRHOzs6EhoYChU/viY6Opm7duhgZGdGhQwcuX75c4BgPHjxI27ZtMTIywt7ennHjxpGZmVlsbj766KNCr8DUuHFjPvnkEwDy8vL45JNPqFGjBoaGhjRp0oRdu3YVuc/c3FwCAgKoWbMmRkZGuLi48Pnnn6vXBwcH8/XXX/PTTz+pv8WPjY0tdmrTf6ckhYWFMXv2bE6ePKneR1hYGO+99x7du3fX2C47O5tq1aqxfv36YvMBjwoEGxsb9eO1117TajshhHhZ5ebmEhkZSWZmJh4eHmUdjhDlnnyVWAFNmTKFuLg4fvrpJ6pVq8ZHH33Eb7/9pp7O4u/vz/nz59m2bRvm5uZMmzYNX19fzpw5g76+Pu3atSM2NpZ+/fpx69YtkpOTMTIy4uzZs9SrV4+4uDiaN2+OsbGxus/Zs2ezcOFCFi1axIoVK/Dz8+Ovv/6iatWqzJgxgzNnzrBz505ee+01Lly4wP379wuN/cqVK/Tp04cxY8YwYsQIEhISmDRpkkabixcv4uPjw9y5c9mwYQM3btwgMDCQwMBAdSFSFD8/P+bPn8/FixepXbs2AKdPn+aPP/4gKioKgM8//5yQkBDWrFmDu7s7GzZs4M033+T06dM4OzsX2GdeXh41atRg8+bNWFpacvjwYUaMGIGtrS0DBgxg8uTJJCcnk56ero6vatWqXL16VbsXlEfTk06dOsWuXbvUowEWFhbUrVuXdu3ace3aNXVR9/PPP3Pv3j0GDhyo1b7Pnz+PnZ0dlSpVwsPDg/nz5+Pg4KB1bPlazt9Hjp5JibcT2jHUVVjYAhoGx5CVqyrrcCosyXPp0TbXlxd003qfSUlJeHh48ODBA0xNTdmyZQv169d/HuEK8UqTgqGCycjIYP369Xz33Xd06tQJgK+//poaNWoAqAuFQ4cO0bp1a+DRjdLs7e3ZunUr/fv3x9PTkzVr1gDwyy+/4O7ujo2NDbGxsdSrV4/Y2Fjat2+v0a+/vz+DBg0C4NNPP2X58uUcP34cHx8fUlNTcXd3p1mzZgA4OTkVGf+qVauoXbu2elTExcWFpKQkPvvsM3Wb+fPn4+fnpx5ednZ2Zvny5bRv355Vq1ZRqVKlIvffoEEDGjduTEREBDNmzFAff8uWLdWXIF28eDHTpk3jrbfeAuCzzz7jwIEDLFu2jC+++KLAPvX19Zk9e7b6ec2aNTly5Ajff/89AwYMwNTUFCMjI7Kysp56+pWRkRGmpqbq0YB8rVu3xsXFhW+//ZapU6cCEBoaSv/+/TE1NS12vy1btiQsLAwXFxeuXbvG7Nmzadu2LadOnSry0rZZWVlkZWWpn6enpwNgqKOgq1vx7m3ysjDUUTT+FS+G5Ln0aJvrou4tVJhatWoRHx9Peno6UVFRDB06lL1792oUDTk5Oer9lmTf5VX+Mb4Kx1qWymOeSxKrFAwVzMWLF3n48KHGtJuqVavi4uICQHJyMnp6ehrrLS0tcXFxITk5GYD27dszfvx4bty4QVxcHJ6enuqCISAggMOHD6s/nOZzc3NT/9/ExARzc3PS0tIAGDVqFH379uW3336jS5cu9OrVS12sPC45ObnAlKHHh5NPnjzJH3/8oXHnakVRyMvL49KlS7i6uj4xR35+fmzYsIEZM2agKAobN25k4sSJwKMPv1evXqVNmzYa27Rp04aTJ08Wuc8vvviCDRs2kJqayv3793n48OFzP0G5KMOGDeOrr75i6tSp/O9//2Pnzp3s379fq227du2q/r+bmxstW7bE0dGR77//noCAgEK3mT9/vkaBlO9j9zyMjXOf7iCE1uY0yyvrEF4JkufSU1yuo6Ojn2q/bdq0ISYmhqlTpzJ69Gj18qSkJAB2796t1RcrFcWePXvKOoRXQnnKc/7Ucm1IwSAKaNSoEVWrViUuLo64uDjmzZuHjY0Nn332GfHx8WRnZxf4wP/4HZhVKhV5eY/+CHTt2pW//vqL6Oho9uzZQ6dOnRgzZgyLFy9+qvgyMjJ4//33GTduXIF12kylGTRoENOmTeO3337j/v37XLlyRevpO4WJjIxk8uTJhISE4OHhgZmZGYsWLeLYsWNPvc+SGDJkCB9++CFHjhzh8OHD1KxZk7Zt2z7VvipXrkzdunW5cOFCkW2CgoLUBRY8KrLs7e3p0KEDlpaWT9WvKF52djZ79uzBy8vrhd/x/FUmeS49pZHrZcuWYW1tja+vr3qZicmjqZNdunR5JS5dLe/p0lEe85w/Q0AbUjBUMLVr10ZfX59jx46pPzzfunWLc+fO0b59e1xdXcnJyeHYsWPqD/3//PMPKSkp6iFblUpF27Zt+emnnzh9+jRvvPEGxsbGZGVlsWbNGpo1a6b+hastKysrhg4dytChQ2nbti1TpkwptGBwdXXVOGEa4OjRoxrPmzZtypkzZ576LsY1atSgffv2hIeHc//+fby8vKhWrRoA5ubm2NnZcejQIY1pV4cOHaJFixaF7i9/etd/v8G6ePGiRhsDAwNyc5/t2/ei9mFpaUmvXr0IDQ3lyJEjvPvuu0/dR0ZGBhcvXuSdd94pso2hoSGGhoYFluvr65ebX5LlmeS5dEieS8/zynVQUBBdu3bFwcGBu3fvEhERQVxcHDExMejr63P9+nWuX7+uvpDG2bNnMTMzw8HBgapVqz5z/y87eU+XjvKU55LEKVdJqmBMTU0JCAhgypQp7N+/n1OnTuHv74+OzqOX2tnZmZ49ezJ8+HAOHjzIyZMnefvtt6levTo9e/ZU78fT05ONGzfSpEkTTE1N0dHRoV27doSHhxc4f6E4M2fO5KeffuLChQucPn2an3/+uchpQyNHjuT8+fNMmTKFlJQUIiIiCAsL02gzbdo0Dh8+TGBgICdOnOD8+fP89NNPBAYGah2Tn58fkZGRbN68GT8/P411U6ZM4bPPPmPTpk2kpKTw4YcfcuLECcaPH1/ovpydnUlISCAmJoZz584xY8YM4uPjNdo4OTnxxx9/kJKSws2bN59qjqOTkxOXLl3ixIkT3Lx5U+M8gmHDhvH111+TnJzM0KFDtd7n5MmTiYuL4/Llyxw+fJjevXujq6urPh9FCCHKi7S0NIYMGYKLiwudOnUiPj6emJgYvLy8AFi9ejXu7u4MHz4cgHbt2uHu7l7gSyohREFSMFRAixYtom3btvTo0YPOnTvzxhtv8Prrr6vXh4aG8vrrr9O9e3c8PDxQFIXo6GiNSrN9+/bk5ubi6empXubp6VlgmTYMDAwICgrCzc2Ndu3aoaurS2RkZKFtHRwciIqKYuvWrTRu3JjVq1fz6aefarRxc3MjLi6Oc+fO0bZtW9zd3Zk5cyZ2dnZax9SvXz/++ecf7t27V+CGauPGjWPixIlMmjSJRo0asWvXLrZt21boFZIA3n//ffr06cPAgQNp2bIl//zzj8ZoA8Dw4cNxcXGhWbNmWFlZcejQIa1jzde3b198fHzo0KEDVlZWbNy4Ub2uc+fO2Nra4u3tXaI8/P333wwaNAgXFxcGDBiApaUlR48excrKqsTxCSFEWVq/fj2XL18mKyuLtLQ09u7dqy4W4NElrhVFKfDw9/cvu6CFKCdUiqLIpSCEKOcyMjKoXr06oaGh9OnTp1T7Tk9Px8LCgps3b8o5DC9QdnY20dHR+Pr6lpvh7vJI8lx6JNelQ/JcOspjnvP/ft+5cwdzc/MntpVzGIQox/Ly8rh58yYhISFUrlyZN998s6xDEkIIIUQFIwWDqFB+/fVXjUuFPi4jI6MUo3nxUlNTqVmzJjVq1CAsLAw9PT2NdU+6YdGZM2ee6gZtQgghhHi1SMEgKpRmzZpx4sSJsg6j1Dg5OVHUrEI7O7sn5qIk5zoIIYQQ4tUlBYOoUIyMjJ76cqsVjZ6enuRCCCGEEM9MrpIkhBBCCCGEKJIUDEIIIYR4qa1atQo3NzfMzc0xNzfHw8ODnTt3qtc/ePCAMWPGYGlpiampKX379uV///tfGUYsRMUiBYModzw9PZkwYUJZh/HSiY2NRaVScfv27bIORQghnqsaNWqwYMECEhMTSUhIoGPHjvTs2ZPTp08D8MEHH7B9+3Y2b95MXFwcV69eLfVLTAtRkck5DEK8BDw9PWnSpAnLli0r61CEEOKl06NHD43n8+bNY9WqVRw9epQaNWqwfv16IiIi6NixI/DoBqWurq4cPXqUVq1alUXIQlQoMsIghBBCiHIjNzeXyMhIMjMz8fDwIDExkezsbDp37qxuU69ePRwcHDhy5EgZRipExSEjDKJcu3XrFuPHj2f79u1kZWXRvn17li9fjrOzM4qiUK1aNVatWkW/fv0AaNKkCf/73/+4du0aAAcPHqRTp07cunULY2PjJ/Z1+/Ztpk2bxtatW7lz5w516tRhwYIFdO/eHYCoqChmzpzJhQsXsLW1ZezYsUyaNEm9/ZdffsnSpUu5cuUKFhYWtG3blh9++AF/f3/i4uKIi4vj888/B+DSpUs4OTk9MZ7o6GgmTJjAlStXaNWqFUOHDi3Q5uDBgwQFBZGQkMBrr71G7969mT9/PiYmJnz00Ufs27ePY8eOaWzTuHFj+vbty8yZM5+c/Me0nL+PHD2TEm0jtGeoq7CwBTQMjiErV1XW4VRYkufSk59rbSUlJeHh4cGDBw8wNTVly5Yt1K9fnxMnTmBgYEDlypU12ltbW3P9+vXnG7QQrygpGES55u/vz/nz59m2bRvm5uZMmzYNX19fzpw5g76+Pu3atSM2NpZ+/fpx69YtkpOTMTIy4uzZs9SrV4+4uDiaN29ebLGQl5dH165duXv3Lt999x21a9fmzJkz6OrqApCYmMiAAQMIDg5m4MCBHD58mNGjR2NpaYm/vz8JCQmMGzeOb7/9ltatW/Pvv//y66+/AvD5559z7tw5GjZsyCeffAKAlZXVE+O5cuUKffr0YcyYMYwYMYKEhASN4gTg4sWL+Pj4MHfuXDZs2MCNGzcIDAwkMDCQ0NBQ/Pz8mD9/PhcvXqR27doAnD59mj/++IOoqKgi+87KyiIrK0v9PD09HQBDHQVd3cLvCSGenaGOovGveDEkz6UnP8fZ2dlata9Vqxbx8fGkp6cTFRXF0KFD2bt3Lzk5OYXuR1EUcnNztd5/RZV//K96Hl608pjnksQqBYMot/ILhUOHDtG6dWsAwsPDsbe3Z+vWrfTv3x9PT0/WrFkDwC+//IK7uzs2NjbExsZSr149YmNjad++fbF97d27l+PHj5OcnEzdunWBR3+88i1ZsoROnToxY8YMAOrWrcuZM2dYtGgR/v7+pKamYmJiQvfu3TEzM8PR0RF3d3cALCwsMDAwwNjYGBsbG62OfdWqVdSuXZuQkBAAXFxcSEpK4rPPPlO3mT9/Pn5+fuoTxJ2dnVm+fDnt27dn1apVNGjQgMaNGxMREaGOOzw8nJYtWz7x/g3z589n9uzZBZZ/7J6HsXGuVvGLpzenWV5Zh/BKkDyXnj179pR4mzZt2hATE8PUqVN54403ePjwId9//z2mpqbqNn/99Re3bt0iOjr6eYZbbj1NnkXJlac837t3T+u2UjCIcis5ORk9PT1atmypXmZpaYmLiwvJyckAtG/fnvHjx3Pjxg3i4uLw9PRUFwwBAQEcPnyYqVOnFtvXiRMnqFGjhrpYKCyWnj17aixr06YNy5YtIzc3Fy8vLxwdHalVqxY+Pj74+PjQu3fvYkc2nnTs/z1uAA8PD43nJ0+e5I8//iA8PFy9TFEU8vLyuHTpEq6urvj5+bFhwwZmzJiBoihs3LiRiRMnPrHvoKAgjTbp6enY29sz93cdcvR1n+p4RPEMdRTmNMtjRoIOWXkyVeZFkTyXnvxce3l5oa+vX+Ltly1bhrW1NaNGjWLOnDno6enh6+sLQEpKCjdu3ODdd98t8LvyVZOdnc2ePXueOs9CO+Uxz/kzBLQhBYOo0Bo1akTVqlXV5wjMmzcPGxsbPvvsM+Lj48nOzlaPTjyJkZHRM8VhZmbGb7/9RmxsLLt372bmzJkEBwcTHx9fYN7t85KRkcH777/PuHHjCqxzcHAAYNCgQUybNo3ffvuN+/fvc+XKFQYOHPjE/RoaGmJoaFhg+S/TOmNpafl8ghcFZGdnEx0dTeJMn3Lzx6g8kjyXnvxc6+vrF5vroKAgunbtioODA3fv3iUiIoK4uDhiYmJ47bXXCAgIYOrUqVSrVg1zc3PGjh2Lh4cHb7zxRikdzctPmzyLZ1ee8lySOKVgEOWWq6srOTk5HDt2TP2h/59//iElJYX69esDoFKpaNu2LT/99BOnT5/mjTfewNjYmKysLNasWUOzZs0wMSn+RF03Nzf+/vtvzp07V+gog6urK4cOHdJYdujQIerWras+z0FPT4/OnTvTuXNnZs2aReXKldm/fz99+vTBwMCA3Fztp/O4urqybds2jWVHjx7VeN60aVPOnDnzxOlFNWrUoH379oSHh3P//n28vLyoVq2a1nEIIURpSEtLY8iQIVy7dg0LCwvc3NyIiYnBy8sLgKVLl6Kjo0Pfvn3JysrC29ubL7/8soyjFqLikIJBlFvOzs707NmT4cOHs2bNGszMzPjwww+pXr26xvQgT09PJk2aRLNmzdTzW9u1a0d4eDhTpkzRqq/27dvTrl07+vbty5IlS6hTpw5nz55FpVLh4+PDpEmTaN68OXPmzGHgwIEcOXKElStXqv9g/fzzz/z555+0a9eOKlWqEB0dTV5eHi4uLgA4OTlx7NgxLl++jKmpKVWrVkVHp+irHo8cOZKQkBCmTJnCsGHDSExMJCwsTKPNtGnTaNWqFYGBgQwbNgwTExPOnDnDnj17WLlypbqdn58fs2bN4uHDhyxdulSrfAghRGlav379E9dXqlSJL774gi+++KKUIhLi1SL3YRDlWmhoKK+//jrdu3fHw8MDRVHUQ9z52rdvT25uLp6enuplnp6eBZYVJyoqiubNmzNo0CDq16/P1KlT1aMCTZs25fvvvycyMpKGDRsyc+ZMPvnkE/z9/QGoXLkyP/74Ix07dsTV1ZXVq1ezceNGGjRoAMDkyZPR1dWlfv36WFlZkZqa+sRYHBwciIqKYuvWrTRu3JjVq1fz6aefarRxc3MjLi6Oc+fO0bZtW9zd3Zk5cyZ2dnYa7fr168c///zDvXv36NWrl9b5EEIIIcSrQaUoilw7Tgjx1NLT07GwsODmzZtyDsMLlD/f29fXt9zMjy2PJM+lR3JdOiTPpaM85jn/7/edO3cwNzd/YlsZYRBCCCGEEEIUSQoGIXh0/wFTU9NCH/nThkrTyJEji4xn5MiRpR6PEEIIIV5dctKzEMCbb75Z5LW6y2Jo8ZNPPmHy5MmFritu2FAIIYQQ4nmSgkEIHt0nwczMrKzDUKtWrZpc3lQIIYQQLwWZkiSEEEIIIYQokhQMQgghhBBCiCJJwSCEEEIIIYQokhQMQgghhBBCiCJJwSCEEEIIIYQokhQMQgghhBBCiCLJZVWFEM9EURQA7t69Wyb3rHhVZGdnc+/ePdLT0yXPL5DkufRIrkuH5Ll0lMc8p6enA//3d/xJpGAQQjyTf/75B4CaNWuWcSRCCCGEKKm7d+9iYWHxxDZSMAghnknVqlUBSE1NLfYXjnh66enp2Nvbc+XKFbnb9wskeS49kuvSIXkuHeUxz4qicPfuXezs7IptKwWDEOKZ6Og8OhXKwsKi3PySLM/Mzc0lz6VA8lx6JNelQ/JcOspbnrX9ok9OehZCCCGEEEIUSQoGIYQQQgghRJGkYBBCPBNDQ0NmzZqFoaFhWYdSoUmeS4fkufRIrkuH5Ll0VPQ8qxRtrqUkhBBCCCGEeCXJCIMQQgghhBCiSFIwCCGEEEIIIYokBYMQQgghhBCiSFIwCCGEEEIIIYokBYMQ4pl88cUXODk5UalSJVq2bMnx48fLOqRy7ZdffqFHjx7Y2dmhUqnYunWrxnpFUZg5cya2trYYGRnRuXNnzp8/XzbBlmPz58+nefPmmJmZUa1aNXr16kVKSopGmwcPHjBmzBgsLS0xNTWlb9++/O9//yujiMunVatW4ebmpr6ZlYeHBzt37lSvlxy/GAsWLEClUjFhwgT1Msn1swsODkalUmk86tWrp15fkXMsBYMQ4qlt2rSJiRMnMmvWLH777TcaN26Mt7c3aWlpZR1auZWZmUnjxo354osvCl2/cOFCli9fzurVqzl27BgmJiZ4e3vz4MGDUo60fIuLi2PMmDEcPXqUPXv2kJ2dTZcuXcjMzFS3+eCDD9i+fTubN28mLi6Oq1ev0qdPnzKMuvypUaMGCxYsIDExkYSEBDp27EjPnj05ffo0IDl+EeLj41mzZg1ubm4ayyXXz0eDBg24du2a+nHw4EH1ugqdY0UIIZ5SixYtlDFjxqif5+bmKnZ2dsr8+fPLMKqKA1C2bNmifp6Xl6fY2NgoixYtUi+7ffu2YmhoqGzcuLEMIqw40tLSFECJi4tTFOVRXvX19ZXNmzer2yQnJyuAcuTIkbIKs0KoUqWKsm7dOsnxC3D37l3F2dlZ2bNnj9K+fXtl/PjxiqLI+/l5mTVrltK4ceNC11X0HMsIgxDiqTx8+JDExEQ6d+6sXqajo0Pnzp05cuRIGUZWcV26dInr169r5NzCwoKWLVtKzp/RnTt3AKhatSoAiYmJZGdna+S6Xr16ODg4SK6fUm5uLpGRkWRmZuLh4SE5fgHGjBlDt27dNHIK8n5+ns6fP4+dnR21atXCz8+P1NRUoOLnWK+sAxBClE83b94kNzcXa2trjeXW1tacPXu2jKKq2K5fvw5QaM7z14mSy8vLY8KECbRp04aGDRsCj3JtYGBA5cqVNdpKrksuKSkJDw8PHjx4gKmpKVu2bKF+/fqcOHFCcvwcRUZG8ttvvxEfH19gnbyfn4+WLVsSFhaGi4sL165dY/bs2bRt25ZTp05V+BxLwSCEEOKVNmbMGE6dOqUxF1k8Py4uLpw4cYI7d+7www8/MHToUOLi4so6rArlypUrjB8/nj179lCpUqWyDqfC6tq1q/r/bm5utGzZEkdHR77//nuMjIzKMLIXT6YkCSGeymuvvYaurm6BK0D873//w8bGpoyiqtjy8yo5f34CAwP5+eefOXDgADVq1FAvt7Gx4eHDh9y+fVujveS65AwMDKhTpw6vv/468+fPp3Hjxnz++eeS4+coMTGRtLQ0mjZtip6eHnp6esTFxbF8+XL09PSwtraWXL8AlStXpm7duly4cKHCv5+lYBBCPBUDAwNef/119u3bp16Wl5fHvn378PDwKMPIKq6aNWtiY2OjkfP09HSOHTsmOS8hRVEIDAxky5Yt7N+/n5o1a2qsf/3119HX19fIdUpKCqmpqZLrZ5SXl0dWVpbk+Dnq1KkTSUlJnDhxQv1o1qwZfn5+6v9Lrp+/jIwMLl68iK2tbYV/P8uUJCHEU5s4cSJDhw6lWbNmtGjRgmXLlpGZmcm7775b1qGVWxkZGVy4cEH9/NKlS5w4cYKqVavi4ODAhAkTmDt3Ls7OztSsWZMZM2ZgZ2dHr169yi7ocmjMmDFERETw008/YWZmpp5jbGFhgZGRERYWFgQEBDBx4kSqVq2Kubk5Y8eOxcPDg1atWpVx9OVHUFAQXbt2xcHBgbt37xIREUFsbCwxMTGS4+fIzMxMff5NPhMTEywtLdXLJdfPbvLkyfTo0QNHR0euXr3KrFmz0NXVZdCgQRX//VzWl2kSQpRvK1asUBwcHBQDAwOlRYsWytGjR8s6pHLtwIEDClDgMXToUEVRHl1adcaMGYq1tbViaGiodOrUSUlJSSnboMuhwnIMKKGhoeo29+/fV0aPHq1UqVJFMTY2Vnr37q1cu3at7IIuh9577z3F0dFRMTAwUKysrJROnTopu3fvVq+XHL84/72sqqJIrp+HgQMHKra2toqBgYFSvXp1ZeDAgcqFCxfU6ytyjlWKoihlVKsIIYQQQgghXnJyDoMQQgghhBCiSFIwCCGEEEIIIYokBYMQQgghhBCiSFIwCCGEEEIIIYokBYMQQgghhBCiSFIwCCGEEEIIIYokBYMQQgghhBCiSFIwCCGEEK8gT09PJkyYUNZhCCHKASkYhBBCiMf4+/ujUqkKPC5cuPBc9h8WFkblypWfy76e1o8//sicOXPKNIYniY2NRaVScfv27bIORYhXnl5ZByCEEEK8jHx8fAgNDdVYZmVlVUbRFC07Oxt9ff0Sb1e1atUXEM3zkZ2dXdYhCCH+Q0YYhBBCiEIYGhpiY2Oj8dDV1QXgp59+omnTplSqVIlatWoxe/ZscnJy1NsuWbKERo0aYWJigr29PaNHjyYjIwN49M35u+++y507d9QjF8HBwQCoVCq2bt2qEUflypUJCwsD4PLly6hUKjZt2kT79u2pVKkS4eHhAKxbtw5XV1cqVapEvXr1+PLLL594fI9PSXJycmLu3LkMGTIEU1NTHB0d2bZtGzdu3KBnz56Ympri5uZGQkKCepv8kZKtW7fi7OxMpUqV8Pb25sqVKxp9rVq1itq1a2NgYICLiwvffvutxnqVSsWqVat48803MTExYfjw4XTo0AGAKlWqoFKp8Pf3B2DXrl288cYbVK5cGUtLS7p3787FixfV+8rP0Y8//kiHDh0wNjamcePGHDlyRKPPQ4cO4enpibGxMVWqVMHb25tbt24BkJeXx/z586lZsyZGRkY0btyYH3744Yn5FKJCU4QQQgihYejQoUrPnj0LXffLL78o5ubmSlhYmHLx4kVl9+7dipOTkxIcHKxus3TpUmX//v3KpUuXlH379ikuLi7KqFGjFEVRlKysLGXZsmWKubm5cu3aNeXatWvK3bt3FUVRFEDZsmWLRn8WFhZKaGiooiiKcunSJQVQnJyclKioKOXPP/9Url69qnz33XeKra2tellUVJRStWpVJSwsrMhjbN++vTJ+/Hj1c0dHR6Vq1arK6tWrlXPnzimjRo1SzM3NFR8fH+X7779XUlJSlF69eimurq5KXl6eoiiKEhoaqujr6yvNmjVTDh8+rCQkJCgtWrRQWrdurd7vjz/+qOjr6ytffPGFkpKSooSEhCi6urrK/v371W0ApVq1asqGDRuUixcvKpcvX1aioqIUQElJSVGuXbum3L59W1EURfnhhx+UqKgo5fz588rvv/+u9OjRQ2nUqJGSm5urkaN69eopP//8s5KSkqL069dPcXR0VLKzsxVFUZTff/9dMTQ0VEaNGqWcOHFCOXXqlLJixQrlxo0biqIoyty5c5V69eopu3btUi5evKiEhoYqhoaGSmxsbJH5FKIik4JBCCGEeMzQoUMVXV1dxcTERP3o16+foiiK0qlTJ+XTTz/VaP/tt98qtra2Re5v8+bNiqWlpfp5aGioYmFhUaCdtgXDsmXLNNrUrl1biYiI0Fg2Z84cxcPDo8iYCisY3n77bfXza9euKYAyY8YM9bIjR44ogHLt2jX1cQDK0aNH1W2Sk5MVQDl27JiiKIrSunVrZfjw4Rp99+/fX/H19dU47gkTJmi0OXDggAIot27dKvIYFEVRbty4oQBKUlKSoij/l6N169ap25w+fVoBlOTkZEVRFGXQoEFKmzZtCt3fgwcPFGNjY+Xw4cMaywMCApRBgwY9MRYhKio5h0EIIYQoRIcOHVi1apX6uYmJCQAnT57k0KFDzJs3T70uNzeXBw8ecO/ePYyNjdm7dy/z58/n7NmzpKenk5OTo7H+WTVr1kz9/8zMTC5evEhAQADDhw9XL8/JycHCwqJE+3Vzc1P/39raGoBGjRoVWJaWloaNjQ0Aenp6NG/eXN2mXr16VK5cmeTkZFq0aEFycjIjRozQ6KdNmzZ8/vnnRR7Tk5w/f56ZM2dy7Ngxbt68SV5eHgCpqak0bNiw0GOxtbVVx12vXj1OnDhB//79C93/hQsXuHfvHl5eXhrLHz58iLu7u1YxClHRSMEghBBCFMLExIQ6deoUWJ6RkcHs2bPp06dPgXWVKlXi8uXLdO/enVGjRjFv3jyqVq3KwYMHCQgI4OHDh08sGFQqFYqiaCwr7ATg/OIlPx6AtWvX0rJlS412+edcaOu/J0+rVKoil+V/SH+e/ntMT9KjRw8cHR1Zu3YtdnZ25OXl0bBhQx4+fKjR7klxGxkZFbn//Hzu2LGD6tWra6wzNDTUKkYhKhopGIQQQogSaNq0KSkpKYUWEwCJiYnk5eUREhKCjs6ja4t8//33Gm0MDAzIzc0tsK2VlRXXrl1TPz9//jz37t17YjzW1tbY2dnx559/4ufnV9LDeWY5OTkkJCTQokULAFJSUrh9+zaurq4AuLq6cujQIYYOHare5tChQ9SvX/+J+zUwMADQyNM///xDSkoKa9eupW3btgAcPHiwxDG7ubmxb98+Zs+eXWBd/fr1MTQ0JDU1lfbt25d430JURFIwCCGEECUwc+ZMunfvjoODA/369UNHR4eTJ09y6tQp5s6dS506dcjOzmbFihX06NGDQ4cOsXr1ao19ODk5kZGRwb59+2jcuDHGxsYYGxvTsWNHVq5ciYeHB7m5uUybNk2rS6bOnj2bcePGYWFhgY+PD1lZWSQkJHDr1i0mTpz4olIBPPomf+zYsSxfvhw9PT0CAwNp1aqVuoCYMmUKAwYMwN3dnc6dO7N9+3Z+/PFH9u7d+8T9Ojo6olKp+Pnnn/H19cXIyIgqVapgaWnJV199ha2tLampqXz44YcljjkoKIhGjRoxevRoRo4ciYGBAQcOHKB///689tprTJ48mQ8++IC8vDzeeOMN7ty5w6FDhzA3N9cofIR4VchlVYUQQogS8Pb25ueff2b37t00b96cVq1asXTpUhwdHQFo3LgxS5Ys4bPPPqNhw4aEh4czf/58jX20bt2akSNHMnDgQKysrFi4cCEAISEh2Nvb07ZtWwYPHszkyZO1Oudh2LBhrFu3jtDQUBo1akT79u0JCwujZs2azz8BjzE2NmbatGkMHjyYNm3aYGpqyqZNm9Tre/Xqxeeff87ixYtp0KABa9asITQ0FE9Pzyfut3r16syePZsPP/wQa2trAgMD0dHRITIyksTERBo2bMgHH3zAokWLShxz3bp12b17NydPnqRFixZ4eHjw008/oaf36HvUOXPmMGPGDObPn4+rqys+Pj7s2LGjVPIpxMtIpTw+WVIIIYQQQgthYWFMmDBB7sYsRAUnIwxCCCGEEEKIIknBIIQQQgghhCiSTEkSQgghhBBCFElGGIQQQgghhBBFkoJBCCGEEEIIUSQpGIQQQgghhBBFkoJBCCGEEEIIUSQpGIQQQgghhBBFkoJBCCGEEEIIUSQpGIQQQgghhBBFkoJBCCGEEEIIUSQpGIQQQgghhBBF+v+Q7soZW6ItgAAAAABJRU5ErkJggg==" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "train data size: 590201\n" - ] - } - ], - "execution_count": 62 - }, - { - "cell_type": "code", - "id": "5d1522a7538db91b", - "metadata": { - "ExecuteTime": { - "end_time": "2025-04-03T15:04:39.656944Z", - "start_time": "2025-04-03T15:04:39.298483Z" - } - }, - "source": [ - "# train_data = train_data.sort_values(by='trade_date')\n", - "# all_dates = train_data['trade_date'].unique() # 获取所有唯一的 trade_date\n", - "# split_date = all_dates[-120] # 划分点为倒数第 validation_days 天\n", - "# print(split_date)\n", - "# print(all_dates)\n", - "# val_data_split = train_data[train_data['trade_date'] >= split_date] # 验证集\n", - "\n", - "score_df = test_data\n", - "numeric_columns = score_df.select_dtypes(include=['float64', 'int64']).columns\n", - "numeric_columns = [col for col in numeric_columns if col in feature_columns]\n", - "# score_df.loc[:, numeric_columns] = scaler.transform(score_df[numeric_columns])\n", - "score_df = cross_sectional_standardization(score_df, numeric_columns)\n", - "\n", - "if use_pca and pca is not None:\n", - " categorical_feature = [col for col in feature_columns if 'cat' in col]\n", - " numeric_features = [col for col in feature_columns if col not in categorical_feature]\n", - " numeric_pca = pca.transform(score_df[numeric_features])\n", - " score_df = pd.concat([pd.DataFrame(numeric_pca), score_df[categorical_feature],\n", - " score_df[['trade_date', 'ts_code', 'future_return', 'future_score', 'label']]], axis=1)\n", - " score_df['score'] = model.predict(score_df[[col for col in score_df.columns if\n", - " col not in ['trade_date', 'ts_code', 'future_return', 'future_score',\n", - " 'label']]])\n", - "else:\n", - " score_df['score'] = model.predict(score_df[feature_columns])\n", - "# train_data['score'] = catboost_model.predict(train_data[feature_columns])\n", - "score_df = score_df.loc[score_df.groupby('trade_date')['score'].idxmax()]\n", - "# score_df = score_df[score_df['score'] > 0]\n", - "score_df[['trade_date', 'score', 'ts_code']].to_csv('predictions_test.tsv', index=False)\n", - "print(score_df['label'].mean())\n", - "print(score_df[['trade_date', 'ts_code', 'future_return', 'future_score', 'label']].head(10))" - ], - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'test_data' is not defined", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mNameError\u001B[0m Traceback (most recent call last)", - "Cell \u001B[1;32mIn[1], line 8\u001B[0m\n\u001B[0;32m 1\u001B[0m \u001B[38;5;66;03m# train_data = train_data.sort_values(by='trade_date')\u001B[39;00m\n\u001B[0;32m 2\u001B[0m \u001B[38;5;66;03m# all_dates = train_data['trade_date'].unique() # 获取所有唯一的 trade_date\u001B[39;00m\n\u001B[0;32m 3\u001B[0m \u001B[38;5;66;03m# split_date = all_dates[-120] # 划分点为倒数第 validation_days 天\u001B[39;00m\n\u001B[0;32m 4\u001B[0m \u001B[38;5;66;03m# print(split_date)\u001B[39;00m\n\u001B[0;32m 5\u001B[0m \u001B[38;5;66;03m# print(all_dates)\u001B[39;00m\n\u001B[0;32m 6\u001B[0m \u001B[38;5;66;03m# val_data_split = train_data[train_data['trade_date'] >= split_date] # 验证集\u001B[39;00m\n\u001B[1;32m----> 8\u001B[0m score_df \u001B[38;5;241m=\u001B[39m test_data\n\u001B[0;32m 9\u001B[0m numeric_columns \u001B[38;5;241m=\u001B[39m score_df\u001B[38;5;241m.\u001B[39mselect_dtypes(include\u001B[38;5;241m=\u001B[39m[\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mfloat64\u001B[39m\u001B[38;5;124m'\u001B[39m, \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mint64\u001B[39m\u001B[38;5;124m'\u001B[39m])\u001B[38;5;241m.\u001B[39mcolumns\n\u001B[0;32m 10\u001B[0m numeric_columns \u001B[38;5;241m=\u001B[39m [col \u001B[38;5;28;01mfor\u001B[39;00m col \u001B[38;5;129;01min\u001B[39;00m numeric_columns \u001B[38;5;28;01mif\u001B[39;00m col \u001B[38;5;129;01min\u001B[39;00m feature_columns]\n", - "\u001B[1;31mNameError\u001B[0m: name 'test_data' is not defined" - ] - } - ], - "execution_count": 1 - }, - { - "cell_type": "code", - "id": "d86af99d15cb3bdd", - "metadata": { - "ExecuteTime": { - "end_time": "2025-04-03T15:03:25.791021Z", - "start_time": "2025-04-03T15:03:25.537833Z" - } - }, - "source": [ - "print(df[(df['ts_code'] == '603577.SH') & (df['trade_date'] >= '2018-06-04')][\n", - " ['trade_date', 'ts_code', 'close', 'open', 'future_return']])" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " trade_date ts_code close open future_return\n", - "255411 2018-06-04 603577.SH 24.57 23.98 -0.044408\n", - "257982 2018-06-05 603577.SH 23.45 24.32 -0.037819\n", - "260550 2018-06-06 603577.SH 23.24 22.74 -0.052262\n", - "263114 2018-06-07 603577.SH 21.88 22.77 -0.028451\n", - "265676 2018-06-08 603577.SH 21.58 21.44 -0.002314\n", - "... ... ... ... ... ...\n", - "5105528 2025-03-24 603577.SH 20.82 20.67 -0.001921\n", - "5108613 2025-03-25 603577.SH 20.33 20.82 -0.005411\n", - "5111699 2025-03-26 603577.SH 20.78 20.33 -0.032771\n", - "5114784 2025-03-27 603577.SH 20.22 20.75 NaN\n", - "5117867 2025-03-28 603577.SH 20.07 20.20 NaN\n", - "\n", - "[1655 rows x 5 columns]\n" - ] - } - ], - "execution_count": 64 - }, - { - "cell_type": "code", - "id": "ef9d068e-67f7-412c-bbd8-cdee7492dbc9", - "metadata": { - "ExecuteTime": { - "end_time": "2025-04-03T15:03:25.893508Z", - "start_time": "2025-04-03T15:03:25.878525Z" - } - }, - "source": [ - "print(train_data[\"future_score\"].corr(train_data[\"label\"]))\n", - "print(test_data[\"future_score\"].corr(test_data[\"label\"]))\n" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.7684793250465913\n", - "0.6965949564132026\n" - ] - } - ], - "execution_count": 65 - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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 -} diff --git a/code/train/predictions_test.tsv b/code/train/predictions_test.tsv deleted file mode 100644 index cdac322..0000000 --- a/code/train/predictions_test.tsv +++ /dev/null @@ -1,550 +0,0 @@ -trade_date,score,ts_code -2022-12-29,0.5993496915237099,002033.SZ -2022-12-30,0.5982905658824365,002095.SZ -2023-01-03,0.43226183271936897,603882.SH -2023-01-04,0.38835119822057007,603778.SH -2023-01-05,0.6164292223678971,600113.SH -2023-01-06,0.4724355189732064,002235.SZ -2023-01-09,0.5134253220843772,002235.SZ -2023-01-10,0.6207586842564803,605133.SH -2023-01-11,0.5010433264248937,603613.SH -2023-01-12,0.7015949128879678,002929.SZ -2023-01-13,0.498544619104055,002823.SZ -2023-01-16,0.4970324220479829,600658.SH -2023-01-17,0.4115451783077775,000915.SZ -2023-01-18,0.5051002083292306,002319.SZ -2023-01-19,0.5926594478068454,002787.SZ -2023-01-20,0.5600276908127407,002297.SZ -2023-01-30,0.32895150606899143,002393.SZ -2023-01-31,0.47280242914852283,601028.SH -2023-02-01,0.7083201481019531,002253.SZ -2023-02-02,0.41838398522919074,000534.SZ -2023-02-03,0.4253220985948224,001266.SZ -2023-02-06,0.6513266912105381,002806.SZ -2023-02-07,0.47054220485721643,002995.SZ -2023-02-08,0.46324632676699906,002748.SZ -2023-02-09,0.6827350634512621,002354.SZ -2023-02-10,0.4821352678819063,603300.SH -2023-02-13,0.4217998924592719,603255.SH -2023-02-14,0.41516502997541105,002354.SZ -2023-02-15,0.523038598093662,000584.SZ -2023-02-16,0.4810251131099421,603939.SH -2023-02-17,0.46474941799229114,000983.SZ -2023-02-20,0.6359527503415816,002229.SZ -2023-02-21,0.2619253505063698,601212.SH -2023-02-22,0.40658765528751784,601728.SH -2023-02-23,0.4536434446282197,600331.SH -2023-02-24,0.4195896415311142,002112.SZ -2023-02-27,0.5217013872423114,002633.SZ -2023-02-28,0.6374768326230712,603848.SH -2023-03-01,0.2923378034053084,603528.SH -2023-03-02,0.5800443754990299,601985.SH -2023-03-03,0.47947515982865924,601566.SH -2023-03-06,0.47737030446971823,600066.SH -2023-03-07,0.3046203388776064,603722.SH -2023-03-08,0.36582859188869005,601989.SH -2023-03-09,0.48435052711258203,000617.SZ -2023-03-10,0.36191241071341823,601188.SH -2023-03-13,0.5728794198521735,600755.SH -2023-03-14,0.4712816920394215,002796.SZ -2023-03-15,0.54608440483593,601727.SH -2023-03-16,0.529528195405985,002552.SZ -2023-03-17,0.6026333408351228,601728.SH -2023-03-20,0.6293021786167095,000032.SZ -2023-03-21,0.4285123137413733,601138.SH -2023-03-22,0.17692098945207105,600449.SH -2023-03-23,0.522735444504978,603083.SH -2023-03-24,0.3358637305583264,603236.SH -2023-03-27,0.7491971748091738,603019.SH -2023-03-28,0.7742172550592232,002236.SZ -2023-03-29,0.6024815895542033,601858.SH -2023-03-30,0.7174781312512657,000688.SZ -2023-03-31,0.7162578431071085,002153.SZ -2023-04-03,0.36128500062338526,600329.SH -2023-04-04,0.38924629987793036,000975.SZ -2023-04-06,0.5607887349405704,600839.SH -2023-04-07,0.5264099142951268,600271.SH -2023-04-10,0.6887949110691621,002275.SZ -2023-04-11,-0.013340990648672061,002222.SZ -2023-04-12,0.7012128435045554,002292.SZ -2023-04-13,0.6712018186785565,603083.SH -2023-04-14,0.49448523181796267,002654.SZ -2023-04-17,0.2606690807343395,600603.SH -2023-04-18,0.3506372217417399,600970.SH -2023-04-19,0.4883574224012403,603019.SH -2023-04-20,0.5145774440150311,600895.SH -2023-04-21,0.5869040515080466,603258.SH -2023-04-24,0.21583248767382618,603168.SH -2023-04-25,0.5893195075497377,601595.SH -2023-04-26,0.6971837449043878,002607.SZ -2023-04-27,0.6749787938163464,600072.SH -2023-04-28,0.42265788643909996,002531.SZ -2023-05-04,0.7147352354273715,601333.SH -2023-05-05,0.5126569134092169,601900.SH -2023-05-08,0.6388788590952813,601801.SH -2023-05-09,0.4628154880114463,601988.SH -2023-05-10,0.3680072646021139,603042.SH -2023-05-11,0.5793990274548498,000156.SZ -2023-05-12,0.6361002944919518,600846.SH -2023-05-15,0.5939493214011368,002749.SZ -2023-05-16,0.7151151942359552,600137.SH -2023-05-17,0.33308578670102396,603516.SH -2023-05-18,0.7246909479156244,603070.SH -2023-05-19,0.6163873089697439,603863.SH -2023-05-22,0.46732429612549925,603685.SH -2023-05-23,0.4012003242341679,001289.SZ -2023-05-24,0.45583236124726323,002403.SZ -2023-05-25,0.4179847845015258,603618.SH -2023-05-26,0.49333506152988005,603699.SH -2023-05-29,0.20693126993200314,000600.SZ -2023-05-30,0.3216088340945225,002217.SZ -2023-05-31,0.3975875358537317,603888.SH -2023-06-01,0.6699778999036027,603888.SH -2023-06-02,0.4766387067466558,002649.SZ -2023-06-05,0.42010051011352373,603721.SH -2023-06-06,0.49617555019114346,600825.SH -2023-06-07,0.46993149983245075,000810.SZ -2023-06-08,0.45527019920556644,600326.SH -2023-06-09,0.3838883478919911,603685.SH -2023-06-12,0.049849788522711665,003000.SZ -2023-06-13,0.4463959137482046,002351.SZ -2023-06-14,0.2724278555068798,002213.SZ -2023-06-15,0.40225157256614297,603083.SH -2023-06-16,0.5093788699168762,000977.SZ -2023-06-19,0.6005876028877897,600072.SH -2023-06-20,0.6362663028040699,605118.SH -2023-06-21,0.6267770435866252,002865.SZ -2023-06-26,0.4276111800347391,000539.SZ -2023-06-27,0.45229780826455573,000539.SZ -2023-06-28,0.439381781937665,002530.SZ -2023-06-29,0.5405172453678457,603489.SH -2023-06-30,0.3067701763761109,603255.SH -2023-07-03,0.6011176016260282,002852.SZ -2023-07-04,0.36621877921674334,600301.SH -2023-07-05,0.43634158461187017,603118.SH -2023-07-06,0.5027049980817844,600237.SH -2023-07-07,0.5318395655297332,601116.SH -2023-07-10,0.17576252976088974,002115.SZ -2023-07-11,0.3368101850262645,605377.SH -2023-07-12,0.2777538991748908,600851.SH -2023-07-13,0.5815872018719028,001210.SZ -2023-07-14,0.37666579836351755,002150.SZ -2023-07-17,0.5207998829241304,603879.SH -2023-07-18,0.5376239361188119,002723.SZ -2023-07-19,0.5273840265099533,603938.SH -2023-07-20,0.6858890521699894,002316.SZ -2023-07-21,0.5261123602949379,003012.SZ -2023-07-24,0.547147880532049,601966.SH -2023-07-25,0.13788677283224893,603755.SH -2023-07-26,0.13550085701030903,600606.SH -2023-07-27,0.4785736932592976,002304.SZ -2023-07-28,0.6704107966995335,000987.SZ -2023-07-31,0.27871862372662504,000932.SZ -2023-08-01,0.4049111188564386,002671.SZ -2023-08-02,0.4060731245124435,000627.SZ -2023-08-03,0.41265478848539217,600881.SH -2023-08-04,0.21506937225407133,000514.SZ -2023-08-07,0.6627610179095694,600743.SH -2023-08-08,0.4355172724619028,000627.SZ -2023-08-09,0.1816113802306989,000813.SZ -2023-08-10,0.6154262293658468,000813.SZ -2023-08-11,0.5367191420113877,002589.SZ -2023-08-14,0.6326609540795168,000668.SZ -2023-08-15,0.5320539234012944,600713.SH -2023-08-16,0.3150745165344159,603665.SH -2023-08-17,0.7663699019981299,001299.SZ -2023-08-18,0.20779298609823746,603389.SH -2023-08-21,0.5123346116515574,002006.SZ -2023-08-22,0.5017363613028969,000006.SZ -2023-08-23,0.18857297707037118,603220.SH -2023-08-24,0.6937184703493717,002178.SZ -2023-08-25,0.46923563320730854,600456.SH -2023-08-28,0.6928686399989369,600745.SH -2023-08-29,0.4486262249333005,603080.SH -2023-08-30,0.36638385265763895,600257.SH -2023-08-31,0.2620421554320702,603059.SH -2023-09-01,0.45650430215252724,601086.SH -2023-09-04,0.45300828900435075,603220.SH -2023-09-05,0.33836919086447914,002178.SZ -2023-09-06,0.3523776950519563,600702.SH -2023-09-07,0.30264785324247073,002725.SZ -2023-09-08,0.4374410877017653,002455.SZ -2023-09-11,0.40357917933365633,003019.SZ -2023-09-12,0.33552548994157866,603629.SH -2023-09-13,0.20089687418027624,003043.SZ -2023-09-14,0.2628097252542422,605028.SH -2023-09-15,0.6488324013929223,600155.SH -2023-09-18,0.3689895119626205,600706.SH -2023-09-19,0.6361407903443482,000506.SZ -2023-09-20,0.4920713764815601,603667.SH -2023-09-21,0.5447514269089377,600814.SH -2023-09-22,0.6853983507399647,002555.SZ -2023-09-25,0.7361312667283009,600603.SH -2023-09-26,0.6018758704099867,002401.SZ -2023-09-27,0.584498352856423,605598.SH -2023-09-28,0.5493103652031055,002605.SZ -2023-10-09,0.14042322562796788,000936.SZ -2023-10-10,0.6232596725993803,603616.SH -2023-10-11,0.6791881628803146,603108.SH -2023-10-12,0.4775924021857536,000700.SZ -2023-10-13,0.4607444966146002,603863.SH -2023-10-16,0.3605587332601004,601127.SH -2023-10-17,0.6942097443890859,603269.SH -2023-10-18,0.48235452929172296,603123.SH -2023-10-19,0.500976676364544,002843.SZ -2023-10-20,0.5980063208858518,600133.SH -2023-10-23,0.5472315903049835,000712.SZ -2023-10-24,0.33222159500351023,002385.SZ -2023-10-25,0.26306038823677497,000797.SZ -2023-10-26,0.36557105118084904,603528.SH -2023-10-27,0.4998454678651184,002232.SZ -2023-10-30,0.47785381969370666,603110.SH -2023-10-31,0.29586764546156813,001319.SZ -2023-11-01,0.21471136454108097,600792.SH -2023-11-02,0.5475478410642985,600200.SH -2023-11-03,0.5167221298806568,002238.SZ -2023-11-06,0.3168057635451115,002420.SZ -2023-11-07,-0.008519117226339919,603108.SH -2023-11-08,0.5772228219786281,002981.SZ -2023-11-09,0.5412574517146544,000096.SZ -2023-11-10,0.7088352669535767,002590.SZ -2023-11-13,0.6091298687606299,002875.SZ -2023-11-14,0.6138540836612464,600246.SH -2023-11-15,0.5703935902150659,600476.SH -2023-11-16,0.6842630957277974,600506.SH -2023-11-17,0.051723230294036034,002243.SZ -2023-11-20,0.4372901317153261,001300.SZ -2023-11-21,0.6329997553518004,603488.SH -2023-11-22,0.6106038500441885,605218.SH -2023-11-23,0.5661810065113655,002183.SZ -2023-11-24,0.5626845716842617,001300.SZ -2023-11-27,0.3699263484177057,600104.SH -2023-11-28,0.28307222625592743,002919.SZ -2023-11-29,0.6035537659610918,600605.SH -2023-11-30,0.653341344692497,603211.SH -2023-12-01,0.5479395522480961,002992.SZ -2023-12-04,0.239409803949225,002561.SZ -2023-12-05,0.5668249585663452,600692.SH -2023-12-06,0.4780046612711487,002728.SZ -2023-12-07,0.6151978929354516,600133.SH -2023-12-08,0.46494086099626936,600398.SH -2023-12-11,0.4061355033271394,603893.SH -2023-12-12,0.454319344141846,002647.SZ -2023-12-13,0.5056906168352251,600310.SH -2023-12-14,0.5027794895552392,600560.SH -2023-12-15,0.5925314880074004,002722.SZ -2023-12-18,0.5124347230383429,600630.SH -2023-12-19,0.5045301623066814,600159.SH -2023-12-20,0.6247718523819663,605289.SH -2023-12-21,-0.4803141323936112,603721.SH -2023-12-22,0.27722679618769086,002942.SZ -2023-12-25,0.3437093338598872,002952.SZ -2023-12-26,0.5487536060015243,603615.SH -2023-12-27,0.40743460382054403,603305.SH -2023-12-28,0.6070769622657148,600630.SH -2023-12-29,0.6582481158932626,002975.SZ -2024-01-02,0.6624801006633897,603577.SH -2024-01-03,0.506637896838537,600506.SH -2024-01-04,0.5294025542238122,601801.SH -2024-01-05,0.6101332210007661,002357.SZ -2024-01-08,0.45447435436913936,002872.SZ -2024-01-09,0.601915036202202,601188.SH -2024-01-10,0.46295663419356203,001339.SZ -2024-01-11,0.4549854971211364,603908.SH -2024-01-12,0.36842497172059696,002140.SZ -2024-01-15,0.43295477058318227,002868.SZ -2024-01-16,0.34964387965090865,002779.SZ -2024-01-17,0.6667126529120687,603800.SH -2024-01-18,0.5436862698431507,002085.SZ -2024-01-19,0.41105775665050137,002211.SZ -2024-01-22,0.08992699031869929,600309.SH -2024-01-23,0.3984935911993216,601988.SH -2024-01-24,0.47817300180187694,003027.SZ -2024-01-25,0.3487405928974409,601318.SH -2024-01-26,0.3673715251392754,603122.SH -2024-01-29,0.579027323650313,601668.SH -2024-01-30,0.7927634486628847,601989.SH -2024-01-31,0.7179008397683367,003816.SZ -2024-02-01,0.6744052120546438,601186.SH -2024-02-02,-0.09247964089462996,601319.SH -2024-02-05,0.5804815004605282,601319.SH -2024-02-06,0.45023793044060356,601658.SH -2024-02-07,0.2621645071447008,603288.SH -2024-02-08,0.27767628925434124,600036.SH -2024-02-19,0.4365016246580571,601100.SH -2024-02-20,0.6326656591037746,600754.SH -2024-02-21,0.48020036053803067,000695.SZ -2024-02-22,0.285551951897634,601398.SH -2024-02-23,0.3176395264650533,603196.SH -2024-02-26,-0.026515860428770643,002882.SZ -2024-02-27,0.3072774878902384,002862.SZ -2024-02-28,0.40650267884715247,002915.SZ -2024-02-29,0.374012583347856,600610.SH -2024-03-01,0.5097516871976601,600522.SH -2024-03-04,0.45743528169930314,600498.SH -2024-03-05,0.29520354685673805,603191.SH -2024-03-06,0.6332834077496753,000628.SZ -2024-03-07,0.4615916248623856,603099.SH -2024-03-08,0.506962555062412,601918.SH -2024-03-11,0.5961455851418274,000628.SZ -2024-03-12,0.21785357748956846,002591.SZ -2024-03-13,0.5203002232589222,600990.SH -2024-03-14,0.5896398729013633,002313.SZ -2024-03-15,0.6015442752305709,603261.SH -2024-03-18,0.7297475381443272,001300.SZ -2024-03-19,0.5183335586197945,002544.SZ -2024-03-20,0.7230921381216405,600178.SH -2024-03-21,0.7167433324190414,600818.SH -2024-03-22,0.4761109649551903,600006.SH -2024-03-25,0.44463988917863584,603408.SH -2024-03-26,0.5976105195705241,600083.SH -2024-03-27,0.2715303340494668,000020.SZ -2024-03-28,0.5210975672972903,600577.SH -2024-03-29,0.8590064547094538,603679.SH -2024-04-01,0.2574204839222071,600200.SH -2024-04-02,0.4960141109250312,603339.SH -2024-04-03,0.576219107136073,002753.SZ -2024-04-08,0.36707129328605576,000833.SZ -2024-04-09,0.4840662729473024,605198.SH -2024-04-10,0.5551658762335594,002842.SZ -2024-04-11,0.5739287309474366,000528.SZ -2024-04-12,0.3597546589711768,603988.SH -2024-04-15,0.43616618245028455,600377.SH -2024-04-16,0.5652581119540648,600566.SH -2024-04-17,0.3107497799300893,600820.SH -2024-04-18,0.278225553139572,600177.SH -2024-04-19,0.5298867340143326,600761.SH -2024-04-22,0.35277109900199877,000333.SZ -2024-04-23,0.2717244611562532,603112.SH -2024-04-24,0.46861984657329575,001696.SZ -2024-04-25,0.45949404291871215,002364.SZ -2024-04-26,0.28992311742373694,002296.SZ -2024-04-29,0.40398504188143913,605255.SH -2024-04-30,0.42757165709326495,002085.SZ -2024-05-06,0.4266611853832811,000546.SZ -2024-05-07,0.3840360967098266,601069.SH -2024-05-08,0.4620099265747788,000698.SZ -2024-05-09,0.2481887655386492,001205.SZ -2024-05-10,0.6154721428668257,600645.SH -2024-05-13,0.27657800476342925,603986.SH -2024-05-14,0.43889305910264775,605266.SH -2024-05-15,0.5616666330619532,002805.SZ -2024-05-16,0.30399319047340156,002244.SZ -2024-05-17,0.39283219107712214,600376.SH -2024-05-20,0.29090758221269003,002975.SZ -2024-05-21,0.5606517618909638,002074.SZ -2024-05-22,0.7621495610963794,600675.SH -2024-05-23,0.46402194151059933,603062.SH -2024-05-24,0.43546631287178944,001201.SZ -2024-05-27,0.40113230199193795,002737.SZ -2024-05-28,0.18649522050725045,600000.SH -2024-05-29,0.4642902384655265,000958.SZ -2024-05-30,0.33734597168313746,002016.SZ -2024-05-31,0.6197774925920987,603598.SH -2024-06-03,0.5431165555013201,601020.SH -2024-06-04,0.31316944674769853,002913.SZ -2024-06-05,0.6035291959051149,002149.SZ -2024-06-06,0.2540892303993234,603665.SH -2024-06-07,0.47842845250564225,601398.SH -2024-06-11,0.3146902368949125,600683.SH -2024-06-12,0.6432549051280668,600123.SH -2024-06-13,0.23800231645439224,002408.SZ -2024-06-14,0.15226813323151922,002856.SZ -2024-06-17,0.47565645731093625,002709.SZ -2024-06-18,0.44932612957892926,600601.SH -2024-06-19,0.39542884763678066,600269.SH -2024-06-20,0.327102036042336,600686.SH -2024-06-21,0.3833796567203426,600094.SH -2024-06-24,0.5982665645963915,002943.SZ -2024-06-25,0.20899979538875846,601988.SH -2024-06-26,0.6901592527781292,000863.SZ -2024-06-27,0.6221234326649174,600216.SH -2024-06-28,0.5252595668594158,601668.SH -2024-07-01,0.6450838481295202,003816.SZ -2024-07-02,0.4650441286452329,600556.SH -2024-07-03,0.2570346528746614,600310.SH -2024-07-04,0.5072867070953532,601288.SH -2024-07-05,0.40509078780149366,000820.SZ -2024-07-08,0.6794583459324044,002384.SZ -2024-07-09,0.558963551001815,000679.SZ -2024-07-10,0.3347949941192904,000813.SZ -2024-07-11,0.5471614704740059,002947.SZ -2024-07-12,0.606586425153978,002947.SZ -2024-07-15,0.491428038903955,002920.SZ -2024-07-16,0.3520867128567497,002829.SZ -2024-07-17,0.4368484139541634,002114.SZ -2024-07-18,0.5711362079713318,601818.SH -2024-07-19,0.570553006364647,601816.SH -2024-07-22,0.5446790556747791,600085.SH -2024-07-23,0.4576036360785198,600216.SH -2024-07-24,0.4684923603589487,605117.SH -2024-07-25,0.36326756116546477,600686.SH -2024-07-26,0.7417069213473678,002202.SZ -2024-07-29,0.8486578272435568,600611.SH -2024-07-30,0.4642324976741237,600079.SH -2024-07-31,0.45181776568715054,600012.SH -2024-08-01,0.427232828082015,603261.SH -2024-08-02,0.5973908054488837,600611.SH -2024-08-05,0.3621832514143976,001215.SZ -2024-08-06,0.6152880440422925,000521.SZ -2024-08-07,0.41444619068423183,002652.SZ -2024-08-08,0.6478614590135714,603863.SH -2024-08-09,0.5930494234277425,002792.SZ -2024-08-12,0.25873562692266217,002737.SZ -2024-08-13,0.5729850282121722,603900.SH -2024-08-14,0.2865185974792763,001223.SZ -2024-08-15,0.5834369717958423,002792.SZ -2024-08-16,0.5710851526661445,603966.SH -2024-08-19,0.4036113851301617,600843.SH -2024-08-20,0.14065947878455815,601328.SH -2024-08-21,0.3457278358065422,601288.SH -2024-08-22,0.45521959430168984,002717.SZ -2024-08-23,0.6962370369795378,601398.SH -2024-08-26,0.6840685708080309,601658.SH -2024-08-27,0.6143024097340646,002957.SZ -2024-08-28,0.6139291482796375,601298.SH -2024-08-29,0.1337266871366976,600352.SH -2024-08-30,0.3951676679126667,600599.SH -2024-09-02,0.4684921271140201,600671.SH -2024-09-03,0.4078753152276658,002717.SZ -2024-09-04,0.3067002855286549,603989.SH -2024-09-05,0.4342289124879712,600406.SH -2024-09-06,0.6808950214517175,600561.SH -2024-09-09,0.43976049446893795,603338.SH -2024-09-10,0.5149110083057741,601096.SH -2024-09-11,0.6734113595487885,600007.SH -2024-09-12,0.7683594358215743,000628.SZ -2024-09-13,0.45977943135802823,002640.SZ -2024-09-18,0.3647308972416072,600482.SH -2024-09-19,0.2813665788012203,600053.SH -2024-09-20,0.46565889001722827,605086.SH -2024-09-23,0.44690986217371853,600048.SH -2024-09-24,0.5632940959647639,002803.SZ -2024-09-25,0.5767833157235442,002622.SZ -2024-09-26,0.4016590689510123,600739.SH -2024-09-27,0.5587044842683202,002311.SZ -2024-09-30,0.43872813516535136,002667.SZ -2024-10-08,0.21652184419342826,001298.SZ -2024-10-09,0.24193695906773188,600809.SH -2024-10-10,0.21177605015643433,601858.SH -2024-10-11,0.23008772929050933,600764.SH -2024-10-14,0.30580248320188735,601398.SH -2024-10-15,0.3298408339375664,001367.SZ -2024-10-16,0.3660842238302505,000617.SZ -2024-10-17,0.5018187879115691,600837.SH -2024-10-18,0.22539525121316248,000514.SZ -2024-10-21,0.5550755061413811,600418.SH -2024-10-22,0.6054618626426457,002488.SZ -2024-10-23,0.7102335664067001,002529.SZ -2024-10-24,0.4202034910245759,601929.SH -2024-10-25,0.37200409737852075,600438.SH -2024-10-28,0.20058072722568346,600653.SH -2024-10-29,0.66492012749881,002628.SZ -2024-10-30,0.6114912867512079,600353.SH -2024-10-31,0.6222592653415162,002693.SZ -2024-11-01,0.6491176289083889,600843.SH -2024-11-04,0.32739829303036627,002178.SZ -2024-11-05,0.759688936684659,600363.SH -2024-11-06,0.7002870933948189,603955.SH -2024-11-07,0.6233511178174305,002146.SZ -2024-11-08,0.42009301025642376,002542.SZ -2024-11-11,0.5565950654983003,002146.SZ -2024-11-12,0.5162774812844746,002208.SZ -2024-11-13,0.18855339088498282,002436.SZ -2024-11-14,0.33502003120522583,002331.SZ -2024-11-15,0.3444118110953233,605117.SH -2024-11-18,0.2375219448592142,605108.SH -2024-11-19,0.36795547875739953,002806.SZ -2024-11-20,0.6197454506964482,603529.SH -2024-11-21,0.5892913531640508,603015.SH -2024-11-22,0.4899996166040043,002175.SZ -2024-11-25,0.053452906549405044,600279.SH -2024-11-26,0.4865888640770184,002759.SZ -2024-11-27,0.6359984075734232,002820.SZ -2024-11-28,0.5845277604948084,603660.SH -2024-11-29,0.47680425412521155,001368.SZ -2024-12-02,0.6219213214295504,002640.SZ -2024-12-03,0.3803484646619281,002682.SZ -2024-12-04,0.5021729841555644,600825.SH -2024-12-05,0.4523940690895692,002607.SZ -2024-12-06,0.4670859538386592,002607.SZ -2024-12-09,0.3701075272107646,000882.SZ -2024-12-10,0.5343702375509868,603536.SH -2024-12-11,0.355506285480863,002177.SZ -2024-12-12,0.6322659533895655,002076.SZ -2024-12-13,0.2290905336564884,002042.SZ -2024-12-16,0.562049425347173,000892.SZ -2024-12-17,0.44457814702247433,002227.SZ -2024-12-18,0.548681923527933,002339.SZ -2024-12-19,0.4216722859146472,603042.SH -2024-12-20,0.5352746358597549,000681.SZ -2024-12-23,0.4216281498853076,601985.SH -2024-12-24,0.35525757086955895,002364.SZ -2024-12-25,0.4646707077082279,600000.SH -2024-12-26,0.6180913472078771,603988.SH -2024-12-27,0.41709449819514827,600865.SH -2024-12-30,0.2849460219241718,000703.SZ -2024-12-31,0.5260981389670845,000759.SZ -2025-01-02,0.3401356086119528,603119.SH -2025-01-03,0.509584346569425,605218.SH -2025-01-06,0.448801551657659,002218.SZ -2025-01-07,0.3353427306743042,002946.SZ -2025-01-08,0.45256872671906395,002187.SZ -2025-01-09,0.24080883076721746,002713.SZ -2025-01-10,0.3586572514416752,600605.SH -2025-01-13,0.39270667594047576,603291.SH -2025-01-14,0.45992646415377564,600292.SH -2025-01-15,0.6283116743234524,002917.SZ -2025-01-16,0.42470390001857816,002529.SZ -2025-01-17,0.6132519800275194,603306.SH -2025-01-20,0.5185912224767291,002175.SZ -2025-01-21,0.6196849792216136,603686.SH -2025-01-22,0.4402928705741018,603912.SH -2025-01-23,0.1991054141392674,000573.SZ -2025-01-24,0.5744429678225592,002629.SZ -2025-01-27,0.6708520363113031,603803.SH -2025-02-05,0.5225638923392779,002629.SZ -2025-02-06,0.7204347328958394,002779.SZ -2025-02-07,0.5784909103136393,002137.SZ -2025-02-10,0.4616488838492335,603958.SH -2025-02-11,0.3299565974394537,002691.SZ -2025-02-12,0.531256628487983,600602.SH -2025-02-13,0.3697957752701452,002369.SZ -2025-02-14,0.34205785645354486,002401.SZ -2025-02-17,0.6382159196768067,605488.SH -2025-02-18,0.454465198035325,600183.SH -2025-02-19,0.4626953105114797,603286.SH -2025-02-20,0.5335048642887046,603319.SH -2025-02-21,0.6624046002142201,605488.SH -2025-02-24,0.6104076951455467,603319.SH -2025-02-25,0.7143736802604856,000096.SZ -2025-02-26,0.7304919691471087,002335.SZ -2025-02-27,0.5489810788910849,600622.SH -2025-02-28,0.5090242119766623,603037.SH -2025-03-03,0.4566864089278842,603278.SH -2025-03-04,0.42810543306501575,603823.SH -2025-03-05,0.6007593312117221,002725.SZ -2025-03-06,0.4659316168581486,003033.SZ -2025-03-07,0.5126705833678915,603108.SH -2025-03-10,0.6053442608509896,600416.SH -2025-03-11,0.4270764095716286,002755.SZ -2025-03-12,0.6309604913581539,002802.SZ -2025-03-13,0.4074738089247502,000570.SZ -2025-03-14,0.7959726877406155,002342.SZ -2025-03-17,0.45598110409066034,600101.SH -2025-03-18,0.4044499886067394,600610.SH -2025-03-19,0.5597196066632053,603757.SH -2025-03-20,0.6572997838613929,002227.SZ -2025-03-21,0.3434871219772223,000815.SZ -2025-03-24,0.48052908404470174,605336.SH -2025-03-25,0.4770085150157279,600243.SH -2025-03-26,0.660278423238859,001256.SZ -2025-03-27,0.3297001064267294,600491.SH -2025-03-28,0.5576338314263632,603949.SH -2025-03-31,0.2710726347586525,002132.SZ -2025-04-01,0.4716112567435462,600095.SH -2025-04-02,0.8038165991136271,002639.SZ -2025-04-03,0.7280761776931406,600215.SH -2025-04-07,0.3907944194714457,600844.SH -2025-04-08,0.44531097808775816,002306.SZ -2025-04-09,0.6994155866649565,600180.SH diff --git a/main/__init__.py b/main/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/main/__pycache__/__init__.cpython-310.pyc b/main/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..c0a14b5 Binary files /dev/null and b/main/__pycache__/__init__.cpython-310.pyc differ diff --git a/main/__pycache__/__init__.cpython-311.pyc b/main/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..14e3aa5 Binary files /dev/null and b/main/__pycache__/__init__.cpython-311.pyc differ diff --git a/code/data/cyq_perf.ipynb b/main/data/cyq_perf.ipynb similarity index 100% rename from code/data/cyq_perf.ipynb rename to main/data/cyq_perf.ipynb diff --git a/code/data/daily_basic.ipynb b/main/data/daily_basic.ipynb similarity index 100% rename from code/data/daily_basic.ipynb rename to main/data/daily_basic.ipynb diff --git a/code/data/daily_data.ipynb b/main/data/daily_data.ipynb similarity index 100% rename from code/data/daily_data.ipynb rename to main/data/daily_data.ipynb diff --git a/code/data/daily_data.py b/main/data/daily_data.py similarity index 100% rename from code/data/daily_data.py rename to main/data/daily_data.py diff --git a/code/data/index_and_industry.ipynb b/main/data/index_and_industry.ipynb similarity index 100% rename from code/data/index_and_industry.ipynb rename to main/data/index_and_industry.ipynb diff --git a/code/data/industry_daily.ipynb b/main/data/industry_daily.ipynb similarity index 100% rename from code/data/industry_daily.ipynb rename to main/data/industry_daily.ipynb diff --git a/code/data/industry_data.ipynb b/main/data/industry_data.ipynb similarity index 100% rename from code/data/industry_data.ipynb rename to main/data/industry_data.ipynb diff --git a/code/data/is_st.ipynb b/main/data/is_st.ipynb similarity index 100% rename from code/data/is_st.ipynb rename to main/data/is_st.ipynb diff --git a/code/data/kpl_concept.ipynb b/main/data/kpl_concept.ipynb similarity index 100% rename from code/data/kpl_concept.ipynb rename to main/data/kpl_concept.ipynb diff --git a/code/data/money_flow.ipynb b/main/data/money_flow.ipynb similarity index 100% rename from code/data/money_flow.ipynb rename to main/data/money_flow.ipynb diff --git a/code/data/name_change.ipynb b/main/data/name_change.ipynb similarity index 100% rename from code/data/name_change.ipynb rename to main/data/name_change.ipynb diff --git a/code/data/stk_limit.ipynb b/main/data/stk_limit.ipynb similarity index 100% rename from code/data/stk_limit.ipynb rename to main/data/stk_limit.ipynb diff --git a/main/data/ths_index.ipynb b/main/data/ths_index.ipynb new file mode 100644 index 0000000..dfb9543 --- /dev/null +++ b/main/data/ths_index.ipynb @@ -0,0 +1,1926 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "initial_id", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-12T15:31:25.004019Z", + "start_time": "2025-03-12T15:31:24.322440Z" + } + }, + "outputs": [], + "source": [ + "from operator import index\n", + "\n", + "import tushare as ts\n", + "import pandas as pd\n", + "import time\n", + "\n", + "ts.set_token('3a0741c702ee7e5e5f2bf1f0846bafaafe4e320833240b2a7e4a685f')\n", + "pro = ts.pro_api()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "972a5ac9f79fe373", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-12T15:31:40.917015Z", + "start_time": "2025-03-12T15:31:35.958771Z" + } + }, + "outputs": [], + "source": [ + "pro = ts.pro_api()\n", + "ths_index_df = pro.ths_index()\n", + "ths_index_df.to_hdf('../../data/ths_index.h5', key='ths_index', mode='w', format='table', data_columns=True)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "1b5a82fbf4e380de", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-12T15:30:20.421604Z", + "start_time": "2025-03-12T15:30:20.224851Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['882001.TI', '882002.TI', '882003.TI', '882004.TI', '882005.TI', '882006.TI', '882007.TI', '882008.TI', '882009.TI', '882010.TI', '882011.TI', '882012.TI', '882013.TI', '882014.TI', '882015.TI', '882016.TI', '882017.TI', '882018.TI', '882019.TI', '882020.TI', '882021.TI', '882022.TI', '882023.TI', '882024.TI', '882025.TI', '882026.TI', '882027.TI', '882028.TI', '882029.TI', '882030.TI', '882031.TI', '882032.TI', '882033.TI', '700030.TI', '700031.TI', '700032.TI', '700033.TI', '700034.TI', '700035.TI', '700036.TI', '700037.TI', '700038.TI', '700039.TI', '700040.TI', '700041.TI', '700042.TI', '700043.TI', '700044.TI', '700045.TI', '700046.TI', '700047.TI', '700048.TI', '700049.TI', '700050.TI', '700051.TI', '700051R.TI', '700052.TI', '700052R.TI', '700053.TI', '700053R.TI', '700054.TI', '700054R.TI', '700055.TI', '700056.TI', '700001.TI', '700002.TI', '700003.TI', '700004.TI', '700005.TI', '700008.TI', '700009.TI', '700010.TI', '700012.TI', '700012R.TI', '700013.TI', '700013R.TI', '700014.TI', '700014R.TI', '700018.TI', '700019.TI', '700020.TI', '700021.TI', '700022.TI', '700023.TI', '700057.TI', '700058.TI', '700059.TI', '700060.TI', '700061.TI', '700062.TI', '700063.TI', '700064.TI', '700065.TI', '700066.TI', '700067.TI', '700068.TI', '700069.TI', '700070.TI', '700071.TI', '700072.TI', '700073.TI', '700074.TI', '700075.TI', '700076.TI', '700077.TI', '700078.TI', '700079.TI', '700080.TI', '700081.TI', '700082.TI', '864001.TI', '864002.TI', '864003.TI', '864004.TI', '873901.TI', '873902.TI', '883400.TI', '883401.TI', '883403.TI', '883404.TI', '883405.TI', '883406.TI', '883407.TI', '883408.TI', '883409.TI', '883410.TI', '883411.TI', '883412.TI', '883413.TI', '883414.TI', '883415.TI', '883416.TI', '883417.TI', '883418.TI', '883419.TI', '883420.TI', '883421.TI', '883422.TI', '883423.TI', '883424.TI', '883425.TI', '883426.TI', '883900.TI', '883901.TI', '883902.TI', '883903.TI', '883904.TI', '883905.TI', '883906.TI', '883907.TI', '883908.TI', '883910.TI', '883911.TI', '883912.TI', '883913.TI', '883915.TI', '883916.TI', '883917.TI', '883918.TI', '883919.TI', '883920.TI', '883921.TI', '883922.TI', '883923.TI', '883924.TI', '883925.TI', '883926.TI', '883927.TI', '883928.TI', '883929.TI', '883930.TI', '883931.TI', '883932.TI', '883933.TI', '883934.TI', '883935.TI', '883936.TI', '883937.TI', '883938.TI', '883939.TI', '883940.TI', '883941.TI', '883942.TI', '883943.TI', '883944.TI', '883945.TI', '883946.TI', '883947.TI', '883948.TI', '883949.TI', '883950.TI', '883951.TI', '883952.TI', '883953.TI', '883954.TI', '883955.TI', '883956.TI', '883957.TI', '883958.TI', '883959.TI', '883960.TI', '883961.TI', '883962.TI', '883963.TI', '883964.TI', '883965.TI', '883966.TI', '883967.TI', '883968.TI', '883969.TI', '883970.TI', '883971.TI', '883972.TI', '883973.TI', '883974.TI', '883975.TI', '883976.TI', '883977.TI', '883978.TI', '883979.TI', '883980.TI', '883984.TI', '883985.TI', '883986.TI', '883987.TI', '883988.TI', '883990.TI', '883992.TI', '883993.TI', '883994.TI', '883995.TI', '883996.TI', '883997.TI', '883998.TI', '883999.TI', '991001.TI', '700301.TI', '700302.TI', '700303.TI', '700304.TI', '700305.TI', '700306.TI', '700307.TI', '700308.TI', '700309.TI', '700310.TI', '700311.TI', '700320.TI', '700321.TI', '700322.TI', '700323.TI', '700324.TI', '700325.TI', '700326.TI', '700327.TI', '700328.TI', '700329.TI', '700330.TI', '700331.TI', '700332.TI', '700333.TI', '700334.TI', '700335.TI', '700336.TI', '700337.TI', '700338.TI', '700339.TI', '700340.TI', '700341.TI', '700342.TI', '700343.TI', '700344.TI', '700360.TI', '700361.TI', '700362.TI', '700363.TI', '700364.TI', '700365.TI', '700366.TI', '700367.TI', '700368.TI', '700369.TI', '700370.TI', '700371.TI', '700372.TI', '700373.TI', '700374.TI', '700375.TI', '700376.TI', '700377.TI', '700378.TI', '700379.TI', '700380.TI', '700381.TI', '700382.TI', '700383.TI', '700384.TI', '700385.TI', '700386.TI', '700387.TI', '700388.TI', '700389.TI', '700390.TI', '700391.TI', '700392.TI', '700393.TI', '700394.TI', '700395.TI', '700396.TI', '700397.TI', '700398.TI', '700399.TI', '700400.TI', '700401.TI', '700402.TI', '700403.TI', '700404.TI', '700405.TI', '700406.TI', '700407.TI', '700408.TI', '700409.TI', '700410.TI', '700411.TI', '700412.TI', '700413.TI', '700414.TI', '700415.TI', '700416.TI', '700417.TI', '700418.TI', '700419.TI', '700420.TI', '700421.TI', '700422.TI', '700423.TI', '700424.TI', '700425.TI', '700450.TI', '700451.TI', '700452.TI', '700453.TI', '700454.TI', '700455.TI', '700456.TI', '700457.TI', '700458.TI', '700459.TI', '700460.TI', '700461.TI', '700462.TI', '700463.TI', '700464.TI', '700465.TI', '700466.TI', '700467.TI', '700468.TI', '700469.TI', '700470.TI', '700471.TI', '700472.TI', '700473.TI', '700474.TI', '700475.TI', '700476.TI', '700477.TI', '700478.TI', '700479.TI', '700480.TI', '700481.TI', '700482.TI', '700483.TI', '700484.TI', '700485.TI', '700486.TI', '700487.TI', '700488.TI', '700489.TI', '700490.TI', '700491.TI', '700492.TI', '700493.TI', '700494.TI', '700495.TI', '700496.TI', '700497.TI', '700498.TI', '700499.TI', '700500.TI', '700501.TI', '700502.TI', '700503.TI', '700504.TI', '700505.TI', '700506.TI', '700507.TI', '700508.TI', '700509.TI', '700510.TI', '700511.TI', '700512.TI', '700513.TI', '700514.TI', '700515.TI', '700516.TI', '700517.TI', '700518.TI', '700519.TI', '700520.TI', '700521.TI', '700522.TI', '700523.TI', '700524.TI', '700525.TI', '700526.TI', '700527.TI', '700528.TI', '700529.TI', '700530.TI', '700531.TI', '700532.TI', '700533.TI', '700534.TI', '700535.TI', '700536.TI', '700537.TI', '700538.TI', '700539.TI', '700540.TI', '700541.TI', '700542.TI', '700543.TI', '700544.TI', '700545.TI', '700546.TI', '700547.TI', '700548.TI', '700549.TI', '700550.TI', '700551.TI', '700552.TI', '700553.TI', '700554.TI', '700555.TI', '700556.TI', '700557.TI', '700558.TI', '700559.TI', '700560.TI', '700561.TI', '700562.TI', '700563.TI', '700564.TI', '700565.TI', '700566.TI', '700567.TI', '700568.TI', '700569.TI', '700570.TI', '700571.TI', '700572.TI', '700573.TI', '700574.TI', '700575.TI', '700576.TI', '700577.TI', '700578.TI', '700579.TI', '700580.TI', '700581.TI', '700582.TI', '700583.TI', '700584.TI', '700585.TI', '700586.TI', '700587.TI', '700588.TI', '700589.TI', '700590.TI', '700591.TI', '700592.TI', '861001.TI', '861002.TI', '861003.TI', '861004.TI', '861005.TI', '861006.TI', '861007.TI', '861008.TI', '861009.TI', '861010.TI', '861011.TI', '861012.TI', '861013.TI', '861014.TI', '861015.TI', '861016.TI', '861017.TI', '861018.TI', '861019.TI', '861020.TI', '861021.TI', '861022.TI', '861023.TI', '861024.TI', '861025.TI', '861026.TI', '861027.TI', '861028.TI', '861029.TI', '861031.TI', '861032.TI', '861033.TI', '861034.TI', '861035.TI', '861036.TI', '861037.TI', '861038.TI', '861039.TI', '861040.TI', '861041.TI', '861042.TI', '861043.TI', '861044.TI', '861045.TI', '861047.TI', '861048.TI', '861049.TI', '861050.TI', '861051.TI', '861052.TI', '861053.TI', '861054.TI', '861055.TI', '861056.TI', '861057.TI', '861058.TI', '861059.TI', '861060.TI', '861061.TI', '861062.TI', '861063.TI', '861064.TI', '861065.TI', '861066.TI', '861067.TI', '861068.TI', '861070.TI', '861071.TI', '861072.TI', '861073.TI', '861074.TI', '861075.TI', '861076.TI', '861077.TI', '861078.TI', '861079.TI', '861080.TI', '861081.TI', '861082.TI', '861083.TI', '861084.TI', '861085.TI', '861086.TI', '861087.TI', '861088.TI', '861089.TI', '861090.TI', '861091.TI', '861092.TI', '861093.TI', '861094.TI', '861095.TI', '861096.TI', '861097.TI', '861098.TI', '861099.TI', '861100.TI', '861101.TI', '861102.TI', '861103.TI', '861104.TI', '861105.TI', '861106.TI', '861107.TI', '861108.TI', '861109.TI', '861110.TI', '861111.TI', '861112.TI', '861113.TI', '861114.TI', '861115.TI', '861116.TI', '861117.TI', '861118.TI', '861119.TI', '861120.TI', '861121.TI', '861122.TI', '861123.TI', '861124.TI', '861125.TI', '861126.TI', '861127.TI', '861128.TI', '861129.TI', '861130.TI', '861131.TI', '861132.TI', '861133.TI', '861134.TI', '861135.TI', '861136.TI', '861137.TI', '861138.TI', '861139.TI', '861140.TI', '861141.TI', '861142.TI', '861143.TI', '861144.TI', '861145.TI', '861146.TI', '861147.TI', '861148.TI', '861149.TI', '861151.TI', '861152.TI', '861153.TI', '861154.TI', '861155.TI', '861156.TI', '861157.TI', '861158.TI', '861159.TI', '861160.TI', '861161.TI', '861162.TI', '861163.TI', '861164.TI', '861165.TI', '861166.TI', '861167.TI', '861168.TI', '861169.TI', '861170.TI', '861171.TI', '861172.TI', '861173.TI', '861177.TI', '861178.TI', '861179.TI', '861180.TI', '861181.TI', '861182.TI', '861183.TI', '861184.TI', '861185.TI', '861186.TI', '861187.TI', '861188.TI', '861189.TI', '861190.TI', '861191.TI', '861192.TI', '861193.TI', '861194.TI', '861195.TI', '861196.TI', '861197.TI', '861198.TI', '861199.TI', '861200.TI', '861201.TI', '861202.TI', '861203.TI', '861204.TI', '861205.TI', '861206.TI', '861208.TI', '861209.TI', '861210.TI', '861211.TI', '861212.TI', '861213.TI', '861214.TI', '861215.TI', '861216.TI', '861217.TI', '861218.TI', '861219.TI', '861220.TI', '861221.TI', '861222.TI', '861224.TI', '861225.TI', '861226.TI', '861227.TI', '861228.TI', '861229.TI', '861230.TI', '861231.TI', '861232.TI', '861233.TI', '861234.TI', '861235.TI', '861236.TI', '861237.TI', '861238.TI', '861239.TI', '861240.TI', '861241.TI', '861242.TI', '861243.TI', '861244.TI', '861245.TI', '861246.TI', '861247.TI', '861248.TI', '861249.TI', '861250.TI', '861251.TI', '861263.TI', '861264.TI', '861265.TI', '861266.TI', '861267.TI', '861268.TI', '861269.TI', '861270.TI', '861271.TI', '861272.TI', '861273.TI', '861274.TI', '861275.TI', '861276.TI', '861277.TI', '861278.TI', '861279.TI', '861280.TI', '861281.TI', '861282.TI', '861283.TI', '861284.TI', '861285.TI', '861286.TI', '861287.TI', '861288.TI', '861289.TI', '861290.TI', '861291.TI', '861292.TI', '861293.TI', '871001.TI', '871002.TI', '871003.TI', '871004.TI', '871005.TI', '871006.TI', '871007.TI', '871008.TI', '871009.TI', '871010.TI', '871011.TI', '871012.TI', '871013.TI', '871014.TI', '871015.TI', '871016.TI', '871017.TI', '871018.TI', '871019.TI', '871020.TI', '871021.TI', '871022.TI', '871023.TI', '871024.TI', '871025.TI', '871026.TI', '871027.TI', '871028.TI', '871029.TI', '871031.TI', '871032.TI', '871033.TI', '871034.TI', '871035.TI', '871037.TI', '871038.TI', '871039.TI', '871040.TI', '871041.TI', '871042.TI', '871043.TI', '871044.TI', '871045.TI', '871047.TI', '871048.TI', '871049.TI', '871051.TI', '871052.TI', '871053.TI', '871054.TI', '871055.TI', '871056.TI', '871057.TI', '871058.TI', '871060.TI', '871061.TI', '871062.TI', '871063.TI', '871064.TI', '871065.TI', '871066.TI', '871067.TI', '871068.TI', '871070.TI', '871071.TI', '871072.TI', '871073.TI', '871074.TI', '871075.TI', '871076.TI', '871077.TI', '877001.TI', '877002.TI', '877003.TI', '877004.TI', '877005.TI', '877006.TI', '877007.TI', '877008.TI', '877009.TI', '877010.TI', '877011.TI', '877012.TI', '877013.TI', '877014.TI', '877015.TI', '877016.TI', '877017.TI', '877018.TI', '877019.TI', '877020.TI', '877021.TI', '877022.TI', '877023.TI', '877024.TI', '877025.TI', '877026.TI', '877027.TI', '877028.TI', '877029.TI', '877030.TI', '877031.TI', '877032.TI', '877033.TI', '877034.TI', '877035.TI', '877036.TI', '877037.TI', '877038.TI', '877039.TI', '877040.TI', '877041.TI', '877042.TI', '877043.TI', '877044.TI', '877045.TI', '877046.TI', '877047.TI', '877048.TI', '877049.TI', '877050.TI', '877051.TI', '877052.TI', '877053.TI', '877054.TI', '877055.TI', '877056.TI', '877057.TI', '877058.TI', '877059.TI', '877060.TI', '877061.TI', '877062.TI', '877063.TI', '877064.TI', '877065.TI', '877066.TI', '877067.TI', '877068.TI', '877069.TI', '877070.TI', '877071.TI', '877072.TI', '877073.TI', '877074.TI', '877075.TI', '877076.TI', '877077.TI', '877078.TI', '877079.TI', '877080.TI', '877081.TI', '877082.TI', '877083.TI', '877084.TI', '877085.TI', '877086.TI', '877087.TI', '877088.TI', '877089.TI', '877090.TI', '877091.TI', '877092.TI', '877093.TI', '877094.TI', '877095.TI', '877096.TI', '877097.TI', '877098.TI', '877099.TI', '877100.TI', '877101.TI', '877102.TI', '877103.TI', '877104.TI', '877105.TI', '877106.TI', '877107.TI', '877108.TI', '877109.TI', '877110.TI', '877111.TI', '877112.TI', '877113.TI', '877114.TI', '877115.TI', '877116.TI', '877117.TI', '877118.TI', '877119.TI', '877120.TI', '877121.TI', '877122.TI', '877123.TI', '877124.TI', '877125.TI', '877126.TI', '877127.TI', '877128.TI', '877129.TI', '877130.TI', '877131.TI', '877132.TI', '877133.TI', '877134.TI', '877135.TI', '877136.TI', '877137.TI', '877138.TI', '877139.TI', '881101.TI', '881102.TI', '881103.TI', '881105.TI', '881107.TI', '881108.TI', '881109.TI', '881112.TI', '881114.TI', '881115.TI', '881116.TI', '881117.TI', '881118.TI', '881121.TI', '881122.TI', '881123.TI', '881124.TI', '881125.TI', '881126.TI', '881128.TI', '881129.TI', '881130.TI', '881131.TI', '881132.TI', '881133.TI', '881134.TI', '881135.TI', '881136.TI', '881137.TI', '881138.TI', '881139.TI', '881140.TI', '881141.TI', '881142.TI', '881143.TI', '881144.TI', '881145.TI', '881146.TI', '881148.TI', '881149.TI', '881151.TI', '881152.TI', '881153.TI', '881155.TI', '881156.TI', '881157.TI', '881158.TI', '881159.TI', '881160.TI', '881162.TI', '881164.TI', '881165.TI', '881166.TI', '881167.TI', '881168.TI', '881169.TI', '881170.TI', '881171.TI', '881172.TI', '881173.TI', '881174.TI', '881175.TI', '881177.TI', '881178.TI', '881179.TI', '881180.TI', '881181.TI', '881182.TI', '881263.TI', '881264.TI', '881265.TI', '881266.TI', '881267.TI', '881268.TI', '881269.TI', '881270.TI', '881271.TI', '881272.TI', '881273.TI', '881274.TI', '881275.TI', '881276.TI', '881277.TI', '881278.TI', '881279.TI', '881280.TI', '881281.TI', '881282.TI', '881283.TI', '881284.TI', '884001.TI', '884002.TI', '884003.TI', '884004.TI', '884005.TI', '884006.TI', '884009.TI', '884010.TI', '884011.TI', '884012.TI', '884013.TI', '884014.TI', '884015.TI', '884016.TI', '884018.TI', '884020.TI', '884021.TI', '884022.TI', '884023.TI', '884024.TI', '884025.TI', '884026.TI', '884027.TI', '884028.TI', '884030.TI', '884031.TI', '884032.TI', '884033.TI', '884034.TI', '884035.TI', '884036.TI', '884039.TI', '884041.TI', '884043.TI', '884044.TI', '884045.TI', '884046.TI', '884048.TI', '884050.TI', '884051.TI', '884052.TI', '884053.TI', '884054.TI', '884055.TI', '884056.TI', '884057.TI', '884058.TI', '884059.TI', '884060.TI', '884062.TI', '884063.TI', '884064.TI', '884065.TI', '884066.TI', '884067.TI', '884068.TI', '884069.TI', '884071.TI', '884073.TI', '884074.TI', '884075.TI', '884076.TI', '884077.TI', '884078.TI', '884080.TI', '884081.TI', '884082.TI', '884083.TI', '884084.TI', '884085.TI', '884086.TI', '884088.TI', '884089.TI', '884090.TI', '884091.TI', '884092.TI', '884093.TI', '884094.TI', '884095.TI', '884096.TI', '884098.TI', '884099.TI', '884100.TI', '884101.TI', '884105.TI', '884106.TI', '884107.TI', '884112.TI', '884113.TI', '884115.TI', '884116.TI', '884117.TI', '884118.TI', '884119.TI', '884120.TI', '884123.TI', '884124.TI', '884125.TI', '884126.TI', '884128.TI', '884130.TI', '884131.TI', '884132.TI', '884136.TI', '884137.TI', '884140.TI', '884141.TI', '884142.TI', '884143.TI', '884144.TI', '884145.TI', '884146.TI', '884147.TI', '884149.TI', '884150.TI', '884152.TI', '884153.TI', '884154.TI', '884155.TI', '884156.TI', '884157.TI', '884158.TI', '884159.TI', '884160.TI', '884161.TI', '884162.TI', '884163.TI', '884164.TI', '884165.TI', '884167.TI', '884168.TI', '884172.TI', '884176.TI', '884177.TI', '884178.TI', '884180.TI', '884181.TI', '884182.TI', '884183.TI', '884184.TI', '884185.TI', '884186.TI', '884188.TI', '884189.TI', '884191.TI', '884192.TI', '884193.TI', '884195.TI', '884197.TI', '884199.TI', '884200.TI', '884201.TI', '884202.TI', '884203.TI', '884205.TI', '884206.TI', '884207.TI', '884208.TI', '884209.TI', '884210.TI', '884211.TI', '884212.TI', '884213.TI', '884214.TI', '884215.TI', '884217.TI', '884218.TI', '884219.TI', '884220.TI', '884221.TI', '884225.TI', '884227.TI', '884228.TI', '884229.TI', '884230.TI', '884231.TI', '884232.TI', '884233.TI', '884234.TI', '884235.TI', '884236.TI', '884237.TI', '884238.TI', '884239.TI', '884240.TI', '884242.TI', '884243.TI', '884244.TI', '884245.TI', '884246.TI', '884247.TI', '884248.TI', '884249.TI', '884250.TI', '884251.TI', '884252.TI', '884253.TI', '884254.TI', '884255.TI', '884256.TI', '884257.TI', '884258.TI', '884259.TI', '884260.TI', '884261.TI', '884262.TI', '884263.TI', '884264.TI', '884265.TI', '884266.TI', '884267.TI', '884268.TI', '884269.TI', '884270.TI', '884271.TI', '884272.TI', '884273.TI', '884274.TI', '884275.TI', '884276.TI', '884277.TI', '884278.TI', '884279.TI', '884280.TI', '884281.TI', '884282.TI', '884283.TI', '884284.TI', '884285.TI', '884286.TI', '884287.TI', '884288.TI', '884289.TI', '884290.TI', '884291.TI', '884292.TI', '884293.TI', '884294.TI', '884295.TI', '884296.TI', '884297.TI', '884298.TI', '884299.TI', '884300.TI', '884301.TI', '884302.TI', '884303.TI', '884304.TI', '884305.TI', '884306.TI', '884307.TI', '884308.TI', '884309.TI', '884310.TI', '884311.TI', '884312.TI', '884313.TI', '884314.TI', '884315.TI', '883300.TI', '883301.TI', '883302.TI', '883303.TI', '883304.TI', '885311.TI', '885312.TI', '885333.TI', '885338.TI', '885343.TI', '885345.TI', '885355.TI', '885372.TI', '885376.TI', '885378.TI', '885386.TI', '885398.TI', '885402.TI', '885406.TI', '885410.TI', '885412.TI', '885413.TI', '885418.TI', '885420.TI', '885423.TI', '885425.TI', '885426.TI', '885427.TI', '885428.TI', '885430.TI', '885431.TI', '885439.TI', '885454.TI', '885456.TI', '885457.TI', '885459.TI', '885461.TI', '885462.TI', '885467.TI', '885472.TI', '885478.TI', '885480.TI', '885487.TI', '885490.TI', '885493.TI', '885494.TI', '885497.TI', '885502.TI', '885505.TI', '885508.TI', '885514.TI', '885517.TI', '885520.TI', '885521.TI', '885522.TI', '885525.TI', '885530.TI', '885531.TI', '885537.TI', '885539.TI', '885540.TI', '885543.TI', '885545.TI', '885551.TI', '885552.TI', '885555.TI', '885556.TI', '885562.TI', '885563.TI', '885564.TI', '885566.TI', '885570.TI', '885571.TI', '885572.TI', '885573.TI', '885574.TI', '885578.TI', '885580.TI', '885586.TI', '885587.TI', '885591.TI', '885595.TI', '885598.TI', '885603.TI', '885611.TI', '885615.TI', '885617.TI', '885620.TI', '885623.TI', '885629.TI', '885633.TI', '885640.TI', '885641.TI', '885642.TI', '885650.TI', '885652.TI', '885661.TI', '885662.TI', '885663.TI', '885690.TI', '885692.TI', '885694.TI', '885697.TI', '885699.TI', '885700.TI', '885705.TI', '885706.TI', '885709.TI', '885710.TI', '885728.TI', '885730.TI', '885733.TI', '885734.TI', '885736.TI', '885737.TI', '885738.TI', '885739.TI', '885740.TI', '885741.TI', '885742.TI', '885743.TI', '885744.TI', '885747.TI', '885748.TI', '885749.TI', '885750.TI', '885753.TI', '885756.TI', '885757.TI', '885758.TI', '885759.TI', '885760.TI', '885761.TI', '885764.TI', '885765.TI', '885766.TI', '885767.TI', '885768.TI', '885769.TI', '885770.TI', '885771.TI', '885772.TI', '885774.TI', '885775.TI', '885779.TI', '885780.TI', '885781.TI', '885782.TI', '885783.TI', '885785.TI', '885786.TI', '885787.TI', '885788.TI', '885789.TI', '885791.TI', '885792.TI', '885795.TI', '885797.TI', '885800.TI', '885805.TI', '885806.TI', '885808.TI', '885809.TI', '885810.TI', '885811.TI', '885812.TI', '885814.TI', '885818.TI', '885819.TI', '885820.TI', '885823.TI', '885825.TI', '885827.TI', '885829.TI', '885832.TI', '885834.TI', '885835.TI', '885838.TI', '885839.TI', '885840.TI', '885842.TI', '885843.TI', '885844.TI', '885845.TI', '885846.TI', '885849.TI', '885851.TI', '885852.TI', '885854.TI', '885856.TI', '885860.TI', '885861.TI', '885863.TI', '885864.TI', '885865.TI', '885866.TI', '885868.TI', '885873.TI', '885874.TI', '885875.TI', '885876.TI', '885877.TI', '885878.TI', '885879.TI', '885881.TI', '885882.TI', '885883.TI', '885884.TI', '885886.TI', '885887.TI', '885888.TI', '885890.TI', '885893.TI', '885894.TI', '885897.TI', '885898.TI', '885899.TI', '885900.TI', '885901.TI', '885902.TI', '885903.TI', '885904.TI', '885905.TI', '885907.TI', '885908.TI', '885909.TI', '885910.TI', '885911.TI', '885912.TI', '885913.TI', '885914.TI', '885915.TI', '885916.TI', '885918.TI', '885919.TI', '885920.TI', '885921.TI', '885922.TI', '885923.TI', '885924.TI', '885925.TI', '885926.TI', '885927.TI', '885928.TI', '885929.TI', '885930.TI', '885931.TI', '885933.TI', '885934.TI', '885935.TI', '885936.TI', '885937.TI', '885938.TI', '885939.TI', '885940.TI', '885942.TI', '885943.TI', '885944.TI', '885945.TI', '885946.TI', '885947.TI', '885948.TI', '885950.TI', '885951.TI', '885952.TI', '885953.TI', '885955.TI', '885956.TI', '885957.TI', '885958.TI', '885959.TI', '885960.TI', '885961.TI', '885962.TI', '885963.TI', '885964.TI', '885965.TI', '885966.TI', '885967.TI', '885968.TI', '885969.TI', '885970.TI', '885971.TI', '885972.TI', '885973.TI', '885974.TI', '885975.TI', '885976.TI', '885977.TI', '885978.TI', '885980.TI', '885981.TI', '885982.TI', '885985.TI', '885986.TI', '885987.TI', '885988.TI', '885989.TI', '885990.TI', '885991.TI', '885992.TI', '885994.TI', '885995.TI', '885996.TI', '885997.TI', '885998.TI', '885999.TI', '886000.TI', '886001.TI', '886002.TI', '886003.TI', '886004.TI', '886005.TI', '886006.TI', '886007.TI', '886008.TI', '886009.TI', '886010.TI', '886011.TI', '886012.TI', '886013.TI', '886015.TI', '886016.TI', '886017.TI', '886018.TI', '886019.TI', '886020.TI', '886021.TI', '886023.TI', '886026.TI', '886028.TI', '886030.TI', '886031.TI', '886032.TI', '886033.TI', '886034.TI', '886035.TI', '886036.TI', '886037.TI', '886038.TI', '886039.TI', '886040.TI', '886041.TI', '886042.TI', '886043.TI', '886044.TI', '886045.TI', '886046.TI', '886047.TI', '886048.TI', '886049.TI', '886050.TI', '886051.TI', '886052.TI', '886053.TI', '886054.TI', '886055.TI', '886056.TI', '886057.TI', '886058.TI', '886059.TI', '886060.TI', '886061.TI', '886062.TI', '886063.TI', '886064.TI', '886065.TI', '886066.TI', '886067.TI', '886068.TI', '886069.TI', '886070.TI', '886071.TI', '886072.TI', '886073.TI', '886074.TI', '886075.TI', '886076.TI', '886077.TI', '886078.TI', '886080.TI', '886081.TI', '886082.TI', '886084.TI', '886085.TI', '886086.TI', '886087.TI', '886088.TI', '886089.TI', '886090.TI', '886091.TI', '886093.TI', '886094.TI', '886095.TI', '886096.TI', '886097.TI', '886098.TI', '886099.TI', '886100.TI', '865067.TI', '865069.TI', '865070.TI', '886101.TI', '885362.TI', '886102.TI', '886103.TI']\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "import time\n", + "\n", + "h5_filename = '../../data/ths_member.h5'\n", + "key = '/ths_member'\n", + "\n", + "ths_index_list = ths_index_df['ts_code'].unique().tolist()\n", + "print(ths_index_list)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "f448da220816bf98", + "metadata": { + "ExecuteTime": { + "start_time": "2025-03-12T15:30:20.436796Z" + }, + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "任务 882002.TI 完成\n", + "任务 882001.TI 完成\n", + "任务 882003.TI 完成\n", + "任务 882004.TI 完成\n", + "任务 882005.TI 完成\n", + "任务 882006.TI 完成\n", + "任务 882008.TI 完成\n", + "任务 882007.TI 完成\n", + "任务 882009.TI 完成\n", + "任务 882010.TI 完成\n", + "任务 882011.TI 完成\n", + "任务 882012.TI 完成\n", + "任务 882013.TI 完成\n", + "任务 882014.TI 完成\n", + "任务 882016.TI 完成\n", + "任务 882015.TI 完成\n", + "任务 882018.TI 完成\n", + "任务 882017.TI 完成\n", + "任务 882019.TI 完成\n", + "任务 882020.TI 完成\n", + "任务 882021.TI 完成\n", + "任务 882022.TI 完成\n", + "任务 882023.TI 完成\n", + "任务 882024.TI 完成\n", + "任务 882026.TI 完成\n", + "任务 882025.TI 完成\n", + "任务 882028.TI 完成\n", + "任务 882027.TI 完成\n", + "任务 882030.TI 完成\n", + "任务 882029.TI 完成\n", + "任务 882032.TI 完成\n", + "任务 882031.TI 完成\n", + "任务 882033.TI 完成\n", + "任务 700030.TI 完成\n", + "任务 700031.TI 完成\n", + "任务 700032.TI 完成\n", + "任务 700033.TI 完成\n", + "任务 700034.TI 完成\n", + "任务 700035.TI 完成\n", + "任务 700036.TI 完成\n", + "任务 700037.TI 完成\n", + "任务 700038.TI 完成\n", + "任务 700039.TI 完成\n", + "任务 700040.TI 完成\n", + "任务 700041.TI 完成\n", + "任务 700042.TI 完成\n", + "任务 700043.TI 完成\n", + "任务 700044.TI 完成\n", + "任务 700045.TI 完成\n", + "任务 700046.TI 完成\n", + "任务 700047.TI 完成\n", + "任务 700048.TI 完成\n", + "任务 700050.TI 完成\n", + "任务 700049.TI 完成\n", + "任务 700051.TI 完成\n", + "任务 700051R.TI 完成\n", + "任务 700052.TI 完成\n", + "任务 700052R.TI 完成\n", + "任务 700053.TI 完成\n", + "任务 700053R.TI 完成\n", + "任务 700054R.TI 完成\n", + "任务 700054.TI 完成\n", + "任务 700055.TI 完成\n", + "任务 700056.TI 完成\n", + "任务 700002.TI 完成\n", + "任务 700001.TI 完成\n", + "任务 700004.TI 完成\n", + "任务 700003.TI 完成\n", + "任务 700005.TI 完成\n", + "任务 700008.TI 完成\n", + "任务 700009.TI 完成\n", + "任务 700010.TI 完成\n", + "任务 700012.TI 完成\n", + "任务 700012R.TI 完成\n", + "任务 700013.TI 完成\n", + "任务 700013R.TI 完成\n", + "任务 700014.TI 完成\n", + "任务 700014R.TI 完成\n", + "任务 700018.TI 完成\n", + "任务 700019.TI 完成\n", + "任务 700020.TI 完成\n", + "任务 700021.TI 完成\n", + "任务 700022.TI 完成\n", + "任务 700023.TI 完成\n", + "任务 700057.TI 完成\n", + "任务 700058.TI 完成\n", + "任务 700059.TI 完成\n", + "任务 700060.TI 完成\n", + "任务 700061.TI 完成\n", + "任务 700062.TI 完成\n", + "任务 700063.TI 完成\n", + "任务 700064.TI 完成\n", + "任务 700065.TI 完成\n", + "任务 700066.TI 完成\n", + "任务 700067.TI 完成\n", + "任务 700068.TI 完成\n", + "任务 700069.TI 完成\n", + "任务 700070.TI 完成\n", + "任务 700071.TI 完成\n", + "任务 700072.TI 完成\n", + "任务 700073.TI 完成\n", + "任务 700074.TI 完成\n", + "任务 700075.TI 完成\n", + "任务 700076.TI 完成\n", + "任务 700077.TI 完成\n", + "任务 700078.TI 完成\n", + "任务 700079.TI 完成\n", + "任务 700080.TI 完成\n", + "任务 700081.TI 完成\n", + "任务 864001.TI 完成\n", + "任务 700082.TI 完成\n", + "任务 864002.TI 完成\n", + "任务 864003.TI 完成\n", + "任务 864004.TI 完成\n", + "任务 873901.TI 完成\n", + "任务 873902.TI 完成\n", + "任务 883400.TI 完成\n", + "任务 883401.TI 完成\n", + "任务 883403.TI 完成\n", + "任务 883404.TI 完成\n", + "任务 883405.TI 完成\n", + "任务 883406.TI 完成\n", + "任务 883407.TI 完成\n", + "任务 883408.TI 完成\n", + "任务 883409.TI 完成\n", + "任务 883410.TI 完成\n", + "任务 883411.TI 完成\n", + "任务 883412.TI 完成\n", + "任务 883413.TI 完成\n", + "任务 883414.TI 完成\n", + "任务 883415.TI 完成\n", + "任务 883416.TI 完成\n", + "任务 883417.TI 完成\n", + "任务 883418.TI 完成\n", + "任务 883419.TI 完成\n", + "任务 883420.TI 完成\n", + "任务 883421.TI 完成\n", + "任务 883422.TI 完成\n", + "任务 883423.TI 完成\n", + "任务 883424.TI 完成\n", + "任务 883425.TI 完成\n", + "任务 883426.TI 完成\n", + "任务 883900.TI 完成\n", + "任务 883901.TI 完成\n", + "任务 883902.TI 完成\n", + "任务 883903.TI 完成\n", + "任务 883904.TI 完成\n", + "任务 883905.TI 完成\n", + "任务 883906.TI 完成\n", + "任务 883907.TI 完成\n", + "任务 883908.TI 完成\n", + "任务 883910.TI 完成\n", + "任务 883911.TI 完成\n", + "任务 883912.TI 完成\n", + "任务 883913.TI 完成\n", + "任务 883915.TI 完成\n", + "任务 883916.TI 完成\n", + "任务 883917.TI 完成\n", + "任务 883919.TI 完成\n", + "任务 883918.TI 完成\n", + "任务 883920.TI 完成\n", + "任务 883921.TI 完成\n", + "任务 883922.TI 完成\n", + "任务 883923.TI 完成\n", + "任务 883924.TI 完成\n", + "任务 883925.TI 完成\n", + "任务 883927.TI 完成\n", + "任务 883926.TI 完成\n", + "任务 883928.TI 完成\n", + "任务 883929.TI 完成\n", + "任务 883930.TI 完成\n", + "任务 883931.TI 完成\n", + "任务 883932.TI 完成\n", + "任务 883933.TI 完成\n", + "任务 883934.TI 完成\n", + "任务 883935.TI 完成\n", + "任务 883936.TI 完成\n", + "任务 883937.TI 完成\n", + "任务 883938.TI 完成\n", + "任务 883939.TI 完成\n", + "任务 883940.TI 完成\n", + "任务 883941.TI 完成\n", + "任务 883942.TI 完成\n", + "任务 883943.TI 完成\n", + "任务 883944.TI 完成\n", + "任务 883945.TI 完成\n", + "任务 883946.TI 完成\n", + "任务 883947.TI 完成\n", + "任务 883948.TI 完成\n", + "任务 883949.TI 完成\n", + "任务 883950.TI 完成\n", + "任务 883951.TI 完成\n", + "任务 883952.TI 完成\n", + "任务 883953.TI 完成\n", + "任务 883954.TI 完成\n", + "任务 883955.TI 完成\n", + "任务 883956.TI 完成\n", + "任务 883957.TI 完成\n", + "任务 883958.TI 完成\n", + "任务 883960.TI 完成\n", + "任务 883959.TI 完成\n", + "任务 883961.TI 完成\n", + "任务 883962.TI 完成\n", + "任务 883963.TI 完成\n", + "任务 883964.TI 完成\n", + "任务 883965.TI 完成\n", + "任务 883966.TI 完成\n", + "任务 883967.TI 完成\n", + "任务 883968.TI 完成\n", + "任务 883969.TI 完成\n", + "任务 883970.TI 完成\n", + "任务 883972.TI 完成\n", + "任务 883971.TI 完成\n", + "任务 883973.TI 完成\n", + "任务 883974.TI 完成\n", + "任务 883976.TI 完成\n", + "任务 883975.TI 完成\n", + "任务 883977.TI 完成\n", + "任务 883978.TI 完成\n", + "任务 883979.TI 完成\n", + "任务 883980.TI 完成\n", + "任务 883984.TI 完成\n", + "任务 883985.TI 完成\n", + "任务 883986.TI 完成\n", + "任务 883987.TI 完成\n", + "任务 883988.TI 完成\n", + "任务 883990.TI 完成\n", + "任务 883992.TI 完成\n", + "任务 883993.TI 完成\n", + "任务 883995.TI 完成\n", + "任务 883994.TI 完成\n", + "任务 883996.TI 完成\n", + "任务 883997.TI 完成\n", + "任务 883998.TI 完成\n", + "任务 883999.TI 完成\n", + "任务 700301.TI 完成\n", + "任务 991001.TI 完成\n", + "任务 700302.TI 完成\n", + "任务 700303.TI 完成\n", + "任务 700304.TI 完成\n", + "任务 700305.TI 完成\n", + "任务 700306.TI 完成\n", + "任务 700307.TI 完成\n", + "任务 700308.TI 完成\n", + "任务 700309.TI 完成\n", + "任务 700310.TI 完成\n", + "任务 700311.TI 完成\n", + "任务 700320.TI 完成\n", + "任务 700321.TI 完成\n", + "任务 700322.TI 完成\n", + "任务 700323.TI 完成\n", + "任务 700324.TI 完成\n", + "任务 700325.TI 完成\n", + "任务 700326.TI 完成\n", + "任务 700327.TI 完成\n", + "任务 700328.TI 完成\n", + "任务 700329.TI 完成\n", + "任务 700330.TI 完成\n", + "任务 700331.TI 完成\n", + "任务 700332.TI 完成\n", + "任务 700333.TI 完成\n", + "任务 700334.TI 完成\n", + "任务 700335.TI 完成\n", + "任务 700336.TI 完成\n", + "任务 700337.TI 完成\n", + "任务 700338.TI 完成\n", + "任务 700339.TI 完成\n", + "任务 700340.TI 完成\n", + "任务 700341.TI 完成\n", + "任务 700342.TI 完成\n", + "任务 700343.TI 完成\n", + "任务 700360.TI 完成\n", + "任务 700344.TI 完成\n", + "任务 700362.TI 完成\n", + "任务 700361.TI 完成\n", + "任务 700363.TI 完成\n", + "任务 700364.TI 完成\n", + "任务 700366.TI 完成\n", + "任务 700365.TI 完成\n", + "任务 700367.TI 完成\n", + "任务 700368.TI 完成\n", + "任务 700369.TI 完成\n", + "任务 700370.TI 完成\n", + "任务 700371.TI 完成\n", + "任务 700372.TI 完成\n", + "任务 700373.TI 完成\n", + "任务 700374.TI 完成\n", + "任务 700375.TI 完成\n", + "任务 700376.TI 完成\n", + "任务 700377.TI 完成\n", + "任务 700378.TI 完成\n", + "任务 700379.TI 完成\n", + "任务 700380.TI 完成\n", + "任务 700381.TI 完成\n", + "任务 700382.TI 完成\n", + "任务 700383.TI 完成\n", + "任务 700384.TI 完成\n", + "任务 700385.TI 完成\n", + "任务 700386.TI 完成\n", + "任务 700387.TI 完成\n", + "任务 700388.TI 完成\n", + "任务 700389.TI 完成\n", + "任务 700390.TI 完成\n", + "任务 700391.TI 完成\n", + "任务 700392.TI 完成\n", + "任务 700393.TI 完成\n", + "任务 700394.TI 完成\n", + "任务 700395.TI 完成\n", + "任务 700396.TI 完成\n", + "任务 700397.TI 完成\n", + "任务 700398.TI 完成\n", + "任务 700399.TI 完成\n", + "任务 700400.TI 完成\n", + "任务 700401.TI 完成\n", + "任务 700402.TI 完成\n", + "任务 700403.TI 完成\n", + "任务 700404.TI 完成\n", + "任务 700405.TI 完成\n", + "任务 700406.TI 完成\n", + "任务 700407.TI 完成\n", + "任务 700408.TI 完成\n", + "任务 700409.TI 完成\n", + "任务 700410.TI 完成\n", + "任务 700411.TI 完成\n", + "任务 700412.TI 完成\n", + "任务 700413.TI 完成\n", + "任务 700414.TI 完成\n", + "任务 700415.TI 完成\n", + "任务 700416.TI 完成\n", + "任务 700417.TI 完成\n", + "任务 700418.TI 完成\n", + "任务 700419.TI 完成\n", + "任务 700420.TI 完成\n", + "任务 700421.TI 完成\n", + "任务 700422.TI 完成\n", + "任务 700423.TI 完成\n", + "任务 700424.TI 完成\n", + "任务 700425.TI 完成\n", + "任务 700450.TI 完成\n", + "任务 700451.TI 完成\n", + "任务 700452.TI 完成\n", + "任务 700453.TI 完成\n", + "任务 700454.TI 完成\n", + "任务 700455.TI 完成\n", + "任务 700456.TI 完成\n", + "任务 700457.TI 完成\n", + "任务 700458.TI 完成\n", + "任务 700459.TI 完成\n", + "任务 700460.TI 完成\n", + "任务 700461.TI 完成\n", + "任务 700462.TI 完成\n", + "任务 700463.TI 完成\n", + "任务 700464.TI 完成\n", + "任务 700465.TI 完成\n", + "任务 700466.TI 完成\n", + "任务 700467.TI 完成\n", + "任务 700468.TI 完成\n", + "任务 700469.TI 完成\n", + "任务 700470.TI 完成\n", + "任务 700471.TI 完成\n", + "任务 700472.TI 完成\n", + "任务 700473.TI 完成\n", + "任务 700474.TI 完成\n", + "任务 700475.TI 完成\n", + "任务 700476.TI 完成\n", + "任务 700477.TI 完成\n", + "任务 700478.TI 完成\n", + "任务 700479.TI 完成\n", + "任务 700480.TI 完成\n", + "任务 700481.TI 完成\n", + "任务 700482.TI 完成\n", + "任务 700483.TI 完成\n", + "任务 700484.TI 完成\n", + "任务 700485.TI 完成\n", + "任务 700486.TI 完成\n", + "任务 700487.TI 完成\n", + "任务 700488.TI 完成\n", + "任务 700489.TI 完成\n", + "任务 700490.TI 完成\n", + "任务 700491.TI 完成\n", + "任务 700492.TI 完成\n", + "任务 700493.TI 完成\n", + "任务 700494.TI 完成\n", + "任务 700495.TI 完成\n", + "任务 700496.TI 完成\n", + "任务 700497.TI 完成\n", + "任务 700498.TI 完成\n", + "任务 700499.TI 完成\n", + "任务 700500.TI 完成\n", + "任务 700501.TI 完成\n", + "任务 700503.TI 完成\n", + "任务 700502.TI 完成\n", + "任务 700505.TI 完成\n", + "任务 700504.TI 完成\n", + "任务 700506.TI 完成\n", + "任务 700507.TI 完成\n", + "任务 700508.TI 完成\n", + "任务 700509.TI 完成\n", + "任务 700510.TI 完成\n", + "任务 700511.TI 完成\n", + "任务 700513.TI 完成\n", + "任务 700512.TI 完成\n", + "任务 700514.TI 完成\n", + "任务 700515.TI 完成\n", + "任务 700516.TI 完成\n", + "任务 700517.TI 完成\n", + "任务 700518.TI 完成\n", + "任务 700519.TI 完成\n", + "任务 700520.TI 完成\n", + "任务 700521.TI 完成\n", + "任务 700522.TI 完成\n", + "任务 700523.TI 完成\n", + "任务 700524.TI 完成\n", + "任务 700525.TI 完成\n", + "任务 700526.TI 完成\n", + "任务 700527.TI 完成\n", + "任务 700528.TI 完成\n", + "任务 700529.TI 完成\n", + "任务 700530.TI 完成\n", + "任务 700531.TI 完成\n", + "任务 700532.TI 完成\n", + "任务 700533.TI 完成\n", + "任务 700534.TI 完成\n", + "任务 700535.TI 完成\n", + "任务 700536.TI 完成\n", + "任务 700537.TI 完成\n", + "任务 700538.TI 完成\n", + "任务 700539.TI 完成\n", + "任务 700540.TI 完成\n", + "任务 700541.TI 完成\n", + "任务 700542.TI 完成\n", + "任务 700543.TI 完成\n", + "任务 700544.TI 完成\n", + "任务 700545.TI 完成\n", + "任务 700546.TI 完成\n", + "任务 700547.TI 完成\n", + "任务 700548.TI 完成\n", + "任务 700549.TI 完成\n", + "任务 700550.TI 完成\n", + "任务 700551.TI 完成\n", + "任务 700552.TI 完成\n", + "任务 700553.TI 完成\n", + "任务 700554.TI 完成\n", + "任务 700555.TI 完成\n", + "任务 700556.TI 完成\n", + "任务 700557.TI 完成\n", + "任务 700558.TI 完成\n", + "任务 700559.TI 完成\n", + "任务 700560.TI 完成\n", + "任务 700561.TI 完成\n", + "任务 700562.TI 完成\n", + "任务 700563.TI 完成\n", + "任务 700564.TI 完成\n", + "任务 700565.TI 完成\n", + "任务 700566.TI 完成\n", + "任务 700567.TI 完成\n", + "任务 700568.TI 完成\n", + "任务 700569.TI 完成\n", + "任务 700570.TI 完成\n", + "任务 700571.TI 完成\n", + "任务 700572.TI 完成\n", + "任务 700573.TI 完成\n", + "任务 700574.TI 完成\n", + "任务 700575.TI 完成\n", + "任务 700576.TI 完成\n", + "任务 700577.TI 完成\n", + "任务 700578.TI 完成\n", + "任务 700579.TI 完成\n", + "任务 700580.TI 完成\n", + "任务 700581.TI 完成\n", + "任务 700582.TI 完成\n", + "任务 700583.TI 完成\n", + "任务 700584.TI 完成\n", + "任务 700585.TI 完成\n", + "任务 700586.TI 完成\n", + "任务 700588.TI 完成\n", + "任务 700587.TI 完成\n", + "任务 700589.TI 完成\n", + "任务 700590.TI 完成\n", + "任务 700592.TI 完成\n", + "任务 700591.TI 完成\n", + "任务 861002.TI 完成\n", + "任务 861001.TI 完成\n", + "任务 861004.TI 完成\n", + "任务 861003.TI 完成\n", + "任务 861005.TI 完成\n", + "任务 861006.TI 完成\n", + "任务 861007.TI 完成\n", + "任务 861008.TI 完成\n", + "任务 861009.TI 完成\n", + "任务 861010.TI 完成\n", + "任务 861011.TI 完成\n", + "任务 861012.TI 完成\n", + "任务 861013.TI 完成\n", + "任务 861014.TI 完成\n", + "任务 861015.TI 完成\n", + "任务 861016.TI 完成\n", + "任务 861017.TI 完成\n", + "任务 861018.TI 完成\n", + "任务 861019.TI 完成\n", + "任务 861020.TI 完成\n", + "任务 861021.TI 完成\n", + "任务 861022.TI 完成\n", + "任务 861023.TI 完成\n", + "任务 861024.TI 完成\n", + "任务 861025.TI 完成\n", + "任务 861026.TI 完成\n", + "任务 861028.TI 完成\n", + "任务 861027.TI 完成\n", + "任务 861029.TI 完成\n", + "任务 861031.TI 完成\n", + "任务 861032.TI 完成\n", + "任务 861033.TI 完成\n", + "任务 861034.TI 完成\n", + "任务 861035.TI 完成\n", + "任务 861037.TI 完成\n", + "任务 861036.TI 完成\n", + "任务 861038.TI 完成\n", + "任务 861039.TI 完成\n", + "任务 861041.TI 完成\n", + "任务 861040.TI 完成\n", + "任务 861043.TI 完成\n", + "任务 861042.TI 完成\n", + "任务 861044.TI 完成\n", + "任务 861045.TI 完成\n", + "任务 861048.TI 完成\n", + "任务 861047.TI 完成\n", + "任务 861049.TI 完成\n", + "任务 861050.TI 完成\n", + "任务 861051.TI 完成\n", + "任务 861052.TI 完成\n", + "任务 861053.TI 完成\n", + "任务 861054.TI 完成\n", + "任务 861055.TI 完成\n", + "任务 861056.TI 完成\n", + "任务 861057.TI 完成\n", + "任务 861058.TI 完成\n", + "任务 861059.TI 完成\n", + "任务 861060.TI 完成\n", + "任务 861061.TI 完成\n", + "任务 861062.TI 完成\n", + "任务 861063.TI 完成\n", + "任务 861064.TI 完成\n", + "任务 861065.TI 完成\n", + "任务 861066.TI 完成\n", + "任务 861067.TI 完成\n", + "任务 861068.TI 完成\n", + "任务 861070.TI 完成\n", + "任务 861071.TI 完成\n", + "任务 861072.TI 完成\n", + "任务 861073.TI 完成\n", + "任务 861074.TI 完成\n", + "任务 861075.TI 完成\n", + "任务 861076.TI 完成\n", + "任务 861077.TI 完成\n", + "任务 861078.TI 完成\n", + "任务 861079.TI 完成\n", + "任务 861080.TI 完成\n", + "任务 861081.TI 完成\n", + "任务 861082.TI 完成\n", + "任务 861083.TI 完成\n", + "任务 861084.TI 完成\n", + "任务 861085.TI 完成\n", + "任务 861086.TI 完成\n", + "任务 861087.TI 完成\n", + "任务 861088.TI 完成\n", + "任务 861089.TI 完成\n", + "任务 861090.TI 完成\n", + "任务 861091.TI 完成\n", + "任务 861092.TI 完成\n", + "任务 861093.TI 完成\n", + "任务 861094.TI 完成\n", + "任务 861095.TI 完成\n", + "任务 861096.TI 完成\n", + "任务 861097.TI 完成\n", + "任务 861098.TI 完成\n", + "任务 861099.TI 完成\n", + "任务 861100.TI 完成\n", + "任务 861101.TI 完成\n", + "任务 861102.TI 完成\n", + "任务 861103.TI 完成\n", + "任务 861104.TI 完成\n", + "任务 861105.TI 完成\n", + "任务 861106.TI 完成\n", + "任务 861107.TI 完成\n", + "任务 861108.TI 完成\n", + "任务 861109.TI 完成\n", + "任务 861111.TI 完成\n", + "任务 861110.TI 完成\n", + "任务 861112.TI 完成\n", + "任务 861113.TI 完成\n", + "任务 861114.TI 完成\n", + "任务 861115.TI 完成\n", + "任务 861116.TI 完成\n", + "任务 861117.TI 完成\n", + "任务 861118.TI 完成\n", + "任务 861119.TI 完成\n", + "任务 861120.TI 完成\n", + "任务 861121.TI 完成\n", + "任务 861122.TI 完成\n", + "任务 861123.TI 完成\n", + "任务 861124.TI 完成\n", + "任务 861125.TI 完成\n", + "任务 861127.TI 完成\n", + "任务 861126.TI 完成\n", + "任务 861128.TI 完成\n", + "任务 861129.TI 完成\n", + "任务 861131.TI 完成\n", + "任务 861130.TI 完成\n", + "任务 861133.TI 完成\n", + "任务 861132.TI 完成\n", + "任务 861134.TI 完成\n", + "任务 861135.TI 完成\n", + "任务 861137.TI 完成\n", + "任务 861136.TI 完成\n", + "任务 861139.TI 完成\n", + "任务 861138.TI 完成\n", + "任务 861140.TI 完成\n", + "任务 861141.TI 完成\n", + "任务 861142.TI 完成\n", + "任务 861143.TI 完成\n", + "任务 861144.TI 完成\n", + "任务 861145.TI 完成\n", + "任务 861146.TI 完成\n", + "任务 861147.TI 完成\n", + "任务 861149.TI 完成\n", + "任务 861148.TI 完成\n", + "任务 861151.TI 完成\n", + "任务 861152.TI 完成\n", + "任务 861154.TI 完成\n", + "任务 861153.TI 完成\n", + "任务 861156.TI 完成\n", + "任务 861155.TI 完成\n", + "任务 861157.TI 完成\n", + "任务 861158.TI 完成\n", + "任务 861159.TI 完成\n", + "任务 861160.TI 完成\n", + "任务 861161.TI 完成\n", + "任务 861162.TI 完成\n", + "任务 861164.TI 完成\n", + "任务 861163.TI 完成\n", + "任务 861166.TI 完成\n", + "任务 861165.TI 完成\n", + "任务 861168.TI 完成\n", + "任务 861167.TI 完成\n", + "任务 861169.TI 完成\n", + "任务 861170.TI 完成\n", + "任务 861172.TI 完成\n", + "任务 861171.TI 完成\n", + "任务 861173.TI 完成\n", + "任务 861177.TI 完成\n", + "任务 861179.TI 完成\n", + "任务 861178.TI 完成\n", + "任务 861180.TI 完成\n", + "任务 861181.TI 完成\n", + "任务 861182.TI 完成\n", + "任务 861183.TI 完成\n", + "任务 861184.TI 完成\n", + "任务 861185.TI 完成\n", + "任务 861186.TI 完成\n", + "任务 861187.TI 完成\n", + "任务 861188.TI 完成\n", + "任务 861189.TI 完成\n", + "任务 861190.TI 完成\n", + "任务 861191.TI 完成\n", + "任务 861192.TI 完成\n", + "任务 861193.TI 完成\n", + "任务 861194.TI 完成\n", + "任务 861195.TI 完成\n", + "任务 861196.TI 完成\n", + "任务 861197.TI 完成\n", + "任务 861199.TI 完成\n", + "任务 861198.TI 完成\n", + "任务 861200.TI 完成\n", + "任务 861201.TI 完成\n", + "任务 861203.TI 完成\n", + "任务 861202.TI 完成\n", + "任务 861204.TI 完成\n", + "任务 861205.TI 完成\n", + "任务 861208.TI 完成\n", + "任务 861206.TI 完成\n", + "任务 861209.TI 完成\n", + "任务 861210.TI 完成\n", + "任务 861211.TI 完成\n", + "任务 861212.TI 完成\n", + "任务 861214.TI 完成\n", + "任务 861213.TI 完成\n", + "任务 861215.TI 完成\n", + "任务 861216.TI 完成\n", + "任务 861217.TI 完成\n", + "任务 861218.TI 完成\n", + "任务 861219.TI 完成\n", + "任务 861220.TI 完成\n", + "任务 861221.TI 完成\n", + "任务 861222.TI 完成\n", + "任务 861224.TI 完成\n", + "任务 861225.TI 完成\n", + "任务 861226.TI 完成\n", + "任务 861227.TI 完成\n", + "任务 861229.TI 完成\n", + "任务 861228.TI 完成\n", + "任务 861231.TI 完成\n", + "任务 861230.TI 完成\n", + "任务 861232.TI 完成\n", + "任务 861233.TI 完成\n", + "任务 861234.TI 完成\n", + "任务 861235.TI 完成\n", + "任务 861237.TI 完成\n", + "任务 861236.TI 完成\n", + "任务 861239.TI 完成\n", + "任务 861238.TI 完成\n", + "任务 861240.TI 完成\n", + "任务 861241.TI 完成\n", + "任务 861242.TI 完成\n", + "任务 861243.TI 完成\n", + "任务 861245.TI 完成\n", + "任务 861244.TI 完成\n", + "任务 861246.TI 完成\n", + "任务 861247.TI 完成\n", + "任务 861248.TI 完成\n", + "任务 861249.TI 完成\n", + "任务 861250.TI 完成\n", + "任务 861251.TI 完成\n", + "任务 861263.TI 完成\n", + "任务 861264.TI 完成\n", + "任务 861266.TI 完成\n", + "任务 861265.TI 完成\n", + "任务 861268.TI 完成\n", + "任务 861267.TI 完成\n", + "任务 861270.TI 完成\n", + "任务 861269.TI 完成\n", + "任务 861271.TI 完成\n", + "任务 861272.TI 完成\n", + "任务 861273.TI 完成\n", + "任务 861274.TI 完成\n", + "任务 861275.TI 完成\n", + "任务 861276.TI 完成\n", + "任务 861277.TI 完成\n", + "任务 861278.TI 完成\n", + "任务 861279.TI 完成\n", + "任务 861280.TI 完成\n", + "任务 861281.TI 完成\n", + "任务 861282.TI 完成\n", + "任务 861283.TI 完成\n", + "任务 861284.TI 完成\n", + "任务 861285.TI 完成\n", + "任务 861286.TI 完成\n", + "任务 861287.TI 完成\n", + "任务 861288.TI 完成\n", + "任务 861289.TI 完成\n", + "任务 861290.TI 完成\n", + "任务 861291.TI 完成\n", + "任务 861292.TI 完成\n", + "任务 861293.TI 完成\n", + "任务 871001.TI 完成\n", + "任务 871003.TI 完成\n", + "任务 871002.TI 完成\n", + "任务 871005.TI 完成\n", + "任务 871004.TI 完成\n", + "任务 871006.TI 完成\n", + "任务 871007.TI 完成\n", + "任务 871008.TI 完成\n", + "任务 871009.TI 完成\n", + "任务 871010.TI 完成\n", + "任务 871011.TI 完成\n", + "任务 871012.TI 完成\n", + "任务 871013.TI 完成\n", + "任务 871014.TI 完成\n", + "任务 871015.TI 完成\n", + "任务 871016.TI 完成\n", + "任务 871017.TI 完成\n", + "任务 871018.TI 完成\n", + "任务 871019.TI 完成\n", + "任务 871020.TI 完成\n", + "任务 871021.TI 完成\n", + "任务 871022.TI 完成\n", + "任务 871023.TI 完成\n", + "任务 871024.TI 完成\n", + "任务 871025.TI 完成\n", + "任务 871026.TI 完成\n", + "任务 871027.TI 完成\n", + "任务 871028.TI 完成\n", + "任务 871029.TI 完成\n", + "任务 871031.TI 完成\n", + "任务 871032.TI 完成\n", + "任务 871033.TI 完成\n", + "任务 871034.TI 完成\n", + "任务 871035.TI 完成\n", + "任务 871037.TI 完成\n", + "任务 871038.TI 完成\n", + "任务 871039.TI 完成\n", + "任务 871040.TI 完成\n", + "任务 871041.TI 完成\n", + "任务 871042.TI 完成\n", + "任务 871043.TI 完成\n", + "任务 871044.TI 完成\n", + "任务 871045.TI 完成\n", + "任务 871047.TI 完成\n", + "任务 871048.TI 完成\n", + "任务 871049.TI 完成\n", + "任务 871051.TI 完成\n", + "任务 871052.TI 完成\n", + "任务 871053.TI 完成\n", + "任务 871054.TI 完成\n", + "任务 871055.TI 完成\n", + "任务 871056.TI 完成\n", + "任务 871057.TI 完成\n", + "任务 871058.TI 完成\n", + "任务 871060.TI 完成\n", + "任务 871061.TI 完成\n", + "任务 871062.TI 完成\n", + "任务 871063.TI 完成\n", + "任务 871064.TI 完成\n", + "任务 871065.TI 完成\n", + "任务 871066.TI 完成\n", + "任务 871067.TI 完成\n", + "任务 871068.TI 完成\n", + "任务 871070.TI 完成\n", + "任务 871071.TI 完成\n", + "任务 871072.TI 完成\n", + "任务 871073.TI 完成\n", + "任务 871074.TI 完成\n", + "任务 871075.TI 完成\n", + "任务 871076.TI 完成\n", + "任务 871077.TI 完成\n", + "任务 877001.TI 完成\n", + "任务 877002.TI 完成\n", + "任务 877003.TI 完成\n", + "任务 877004.TI 完成\n", + "任务 877005.TI 完成\n", + "任务 877006.TI 完成\n", + "任务 877007.TI 完成\n", + "任务 877008.TI 完成\n", + "任务 877009.TI 完成\n", + "任务 877010.TI 完成\n", + "任务 877011.TI 完成\n", + "任务 877012.TI 完成\n", + "任务 877013.TI 完成\n", + "任务 877014.TI 完成\n", + "任务 877015.TI 完成\n", + "任务 877016.TI 完成\n", + "任务 877017.TI 完成\n", + "任务 877018.TI 完成\n", + "任务 877019.TI 完成\n", + "任务 877020.TI 完成\n", + "任务 877021.TI 完成\n", + "任务 877022.TI 完成\n", + "任务 877023.TI 完成\n", + "任务 877024.TI 完成\n", + "任务 877025.TI 完成\n", + "任务 877026.TI 完成\n", + "任务 877027.TI 完成\n", + "任务 877028.TI 完成\n", + "任务 877029.TI 完成\n", + "任务 877030.TI 完成\n", + "任务 877031.TI 完成\n", + "任务 877032.TI 完成\n", + "任务 877033.TI 完成\n", + "任务 877034.TI 完成\n", + "任务 877035.TI 完成\n", + "任务 877036.TI 完成\n", + "任务 877037.TI 完成\n", + "任务 877038.TI 完成\n", + "任务 877039.TI 完成\n", + "任务 877040.TI 完成\n", + "任务 877041.TI 完成\n", + "任务 877042.TI 完成\n", + "任务 877043.TI 完成\n", + "任务 877044.TI 完成\n", + "任务 877045.TI 完成\n", + "任务 877046.TI 完成\n", + "任务 877047.TI 完成\n", + "任务 877048.TI 完成\n", + "任务 877049.TI 完成\n", + "任务 877050.TI 完成\n", + "任务 877051.TI 完成\n", + "任务 877052.TI 完成\n", + "任务 877053.TI 完成\n", + "任务 877054.TI 完成\n", + "任务 877055.TI 完成\n", + "任务 877056.TI 完成\n", + "任务 877057.TI 完成\n", + "任务 877058.TI 完成\n", + "任务 877059.TI 完成\n", + "任务 877060.TI 完成\n", + "任务 877061.TI 完成\n", + "任务 877062.TI 完成\n", + "任务 877063.TI 完成\n", + "任务 877064.TI 完成\n", + "任务 877065.TI 完成\n", + "任务 877066.TI 完成\n", + "任务 877067.TI 完成\n", + "任务 877068.TI 完成\n", + "任务 877069.TI 完成\n", + "任务 877070.TI 完成\n", + "任务 877071.TI 完成\n", + "任务 877072.TI 完成\n", + "任务 877073.TI 完成\n", + "任务 877074.TI 完成\n", + "任务 877075.TI 完成\n", + "任务 877076.TI 完成\n", + "任务 877077.TI 完成\n", + "任务 877078.TI 完成\n", + "任务 877079.TI 完成\n", + "任务 877080.TI 完成\n", + "任务 877081.TI 完成\n", + "任务 877082.TI 完成\n", + "任务 877083.TI 完成\n", + "任务 877084.TI 完成\n", + "任务 877085.TI 完成\n", + "任务 877086.TI 完成\n", + "任务 877087.TI 完成\n", + "任务 877088.TI 完成\n", + "任务 877089.TI 完成\n", + "任务 877090.TI 完成\n", + "任务 877091.TI 完成\n", + "任务 877092.TI 完成\n", + "任务 877093.TI 完成\n", + "任务 877095.TI 完成\n", + "任务 877094.TI 完成\n", + "任务 877096.TI 完成\n", + "任务 877097.TI 完成\n", + "任务 877098.TI 完成\n", + "任务 877099.TI 完成\n", + "任务 877101.TI 完成\n", + "任务 877100.TI 完成\n", + "任务 877103.TI 完成\n", + "任务 877102.TI 完成\n", + "任务 877105.TI 完成\n", + "任务 877104.TI 完成\n", + "任务 877106.TI 完成\n", + "任务 877107.TI 完成\n", + "任务 877109.TI 完成\n", + "任务 877108.TI 完成\n", + "任务 877110.TI 完成\n", + "任务 877111.TI 完成\n", + "任务 877112.TI 完成\n", + "任务 877113.TI 完成\n", + "任务 877114.TI 完成\n", + "任务 877115.TI 完成\n", + "任务 877116.TI 完成\n", + "任务 877117.TI 完成\n", + "任务 877118.TI 完成\n", + "任务 877119.TI 完成\n", + "任务 877120.TI 完成\n", + "任务 877121.TI 完成\n", + "任务 877122.TI 完成\n", + "任务 877123.TI 完成\n", + "任务 877124.TI 完成\n", + "任务 877125.TI 完成\n", + "任务 877126.TI 完成\n", + "任务 877127.TI 完成\n", + "任务 877128.TI 完成\n", + "任务 877129.TI 完成\n", + "任务 877130.TI 完成\n", + "任务 877131.TI 完成\n", + "任务 877132.TI 完成\n", + "任务 877133.TI 完成\n", + "任务 877134.TI 完成\n", + "任务 877135.TI 完成\n", + "任务 877136.TI 完成\n", + "任务 877137.TI 完成\n", + "任务 877138.TI 完成\n", + "任务 877139.TI 完成\n", + "任务 881101.TI 完成\n", + "任务 881102.TI 完成\n", + "任务 881103.TI 完成\n", + "任务 881105.TI 完成\n", + "任务 881107.TI 完成\n", + "任务 881108.TI 完成\n", + "任务 881109.TI 完成\n", + "任务 881112.TI 完成\n", + "任务 881114.TI 完成\n", + "任务 881115.TI 完成\n", + "任务 881116.TI 完成\n", + "任务 881117.TI 完成\n", + "任务 881118.TI 完成\n", + "任务 881121.TI 完成\n", + "任务 881122.TI 完成\n", + "任务 881123.TI 完成\n", + "任务 881124.TI 完成\n", + "任务 881125.TI 完成\n", + "任务 881126.TI 完成\n", + "任务 881128.TI 完成\n", + "任务 881129.TI 完成\n", + "任务 881130.TI 完成\n", + "任务 881131.TI 完成\n", + "任务 881132.TI 完成\n", + "任务 881133.TI 完成\n", + "任务 881134.TI 完成\n", + "任务 881135.TI 完成\n", + "任务 881136.TI 完成\n", + "任务 881137.TI 完成\n", + "任务 881138.TI 完成\n", + "任务 881139.TI 完成\n", + "任务 881140.TI 完成\n", + "任务 881141.TI 完成\n", + "任务 881142.TI 完成\n", + "任务 881143.TI 完成\n", + "任务 881144.TI 完成\n", + "任务 881145.TI 完成\n", + "任务 881146.TI 完成\n", + "任务 881148.TI 完成\n", + "任务 881149.TI 完成\n", + "任务 881151.TI 完成\n", + "任务 881152.TI 完成\n", + "任务 881153.TI 完成\n", + "任务 881155.TI 完成\n", + "任务 881156.TI 完成\n", + "任务 881157.TI 完成\n", + "任务 881158.TI 完成\n", + "任务 881159.TI 完成\n", + "任务 881160.TI 完成\n", + "任务 881162.TI 完成\n", + "任务 881164.TI 完成\n", + "任务 881166.TI 完成\n", + "任务 881165.TI 完成\n", + "任务 881167.TI 完成\n", + "任务 881168.TI 完成\n", + "任务 881169.TI 完成\n", + "任务 881170.TI 完成\n", + "任务 881171.TI 完成\n", + "任务 881172.TI 完成\n", + "任务 881173.TI 完成\n", + "任务 881174.TI 完成\n", + "任务 881175.TI 完成\n", + "任务 881177.TI 完成\n", + "任务 881178.TI 完成\n", + "任务 881179.TI 完成\n", + "任务 881181.TI 完成\n", + "任务 881180.TI 完成\n", + "任务 881182.TI 完成\n", + "任务 881263.TI 完成\n", + "任务 881264.TI 完成\n", + "任务 881265.TI 完成\n", + "任务 881266.TI 完成\n", + "任务 881267.TI 完成\n", + "任务 881268.TI 完成\n", + "任务 881269.TI 完成\n", + "任务 881270.TI 完成\n", + "任务 881271.TI 完成\n", + "任务 881273.TI 完成\n", + "任务 881272.TI 完成\n", + "任务 881275.TI 完成\n", + "任务 881274.TI 完成\n", + "任务 881276.TI 完成\n", + "任务 881277.TI 完成\n", + "任务 881279.TI 完成\n", + "任务 881278.TI 完成\n", + "任务 881280.TI 完成\n", + "任务 881281.TI 完成\n", + "任务 881282.TI 完成\n", + "任务 881283.TI 完成\n", + "任务 881284.TI 完成\n", + "任务 884001.TI 完成\n", + "任务 884002.TI 完成\n", + "任务 884003.TI 完成\n", + "任务 884004.TI 完成\n", + "任务 884005.TI 完成\n", + "任务 884006.TI 完成\n", + "任务 884009.TI 完成\n", + "任务 884010.TI 完成\n", + "任务 884011.TI 完成\n", + "任务 884012.TI 完成\n", + "任务 884013.TI 完成\n", + "任务 884014.TI 完成\n", + "任务 884015.TI 完成\n", + "任务 884016.TI 完成\n", + "任务 884018.TI 完成\n", + "任务 884020.TI 完成\n", + "任务 884021.TI 完成\n", + "任务 884022.TI 完成\n", + "任务 884023.TI 完成\n", + "任务 884024.TI 完成\n", + "任务 884025.TI 完成\n", + "任务 884026.TI 完成\n", + "任务 884027.TI 完成\n", + "任务 884028.TI 完成\n", + "任务 884030.TI 完成\n", + "任务 884031.TI 完成\n", + "任务 884032.TI 完成\n", + "任务 884033.TI 完成\n", + "任务 884034.TI 完成\n", + "任务 884035.TI 完成\n", + "任务 884036.TI 完成\n", + "任务 884039.TI 完成\n", + "任务 884041.TI 完成\n", + "任务 884043.TI 完成\n", + "任务 884044.TI 完成\n", + "任务 884045.TI 完成\n", + "任务 884046.TI 完成\n", + "任务 884048.TI 完成\n", + "任务 884050.TI 完成\n", + "任务 884051.TI 完成\n", + "任务 884052.TI 完成\n", + "任务 884053.TI 完成\n", + "任务 884054.TI 完成\n", + "任务 884055.TI 完成\n", + "任务 884056.TI 完成\n", + "任务 884058.TI 完成\n", + "任务 884057.TI 完成\n", + "任务 884060.TI 完成\n", + "任务 884059.TI 完成\n", + "任务 884062.TI 完成\n", + "任务 884063.TI 完成\n", + "任务 884064.TI 完成\n", + "任务 884065.TI 完成\n", + "任务 884066.TI 完成\n", + "任务 884067.TI 完成\n", + "任务 884068.TI 完成\n", + "任务 884069.TI 完成\n", + "任务 884071.TI 完成\n", + "任务 884073.TI 完成\n", + "任务 884074.TI 完成\n", + "任务 884075.TI 完成\n", + "任务 884076.TI 完成\n", + "任务 884077.TI 完成\n", + "任务 884078.TI 完成\n", + "任务 884080.TI 完成\n", + "任务 884081.TI 完成\n", + "任务 884082.TI 完成\n", + "任务 884083.TI 完成\n", + "任务 884084.TI 完成\n", + "任务 884085.TI 完成\n", + "任务 884086.TI 完成\n", + "任务 884088.TI 完成\n", + "任务 884089.TI 完成\n", + "任务 884090.TI 完成\n", + "任务 884091.TI 完成\n", + "任务 884092.TI 完成\n", + "任务 884093.TI 完成\n", + "任务 884094.TI 完成\n", + "任务 884095.TI 完成\n", + "任务 884096.TI 完成\n", + "任务 884098.TI 完成\n", + "任务 884099.TI 完成\n", + "任务 884100.TI 完成\n", + "任务 884101.TI 完成\n", + "任务 884105.TI 完成\n", + "任务 884106.TI 完成\n", + "任务 884107.TI 完成\n", + "任务 884112.TI 完成\n", + "任务 884113.TI 完成\n", + "任务 884115.TI 完成\n", + "任务 884116.TI 完成\n", + "任务 884117.TI 完成\n", + "任务 884118.TI 完成\n", + "任务 884119.TI 完成\n", + "任务 884120.TI 完成\n", + "任务 884123.TI 完成\n", + "任务 884124.TI 完成\n", + "任务 884125.TI 完成\n", + "任务 884126.TI 完成\n", + "任务 884128.TI 完成\n", + "任务 884130.TI 完成\n", + "任务 884131.TI 完成\n", + "任务 884132.TI 完成\n", + "任务 884136.TI 完成\n", + "任务 884137.TI 完成\n", + "任务 884140.TI 完成\n", + "任务 884141.TI 完成\n", + "任务 884142.TI 完成\n", + "任务 884143.TI 完成\n", + "任务 884144.TI 完成\n", + "任务 884145.TI 完成\n", + "任务 884146.TI 完成\n", + "任务 884147.TI 完成\n", + "任务 884149.TI 完成\n", + "任务 884150.TI 完成\n", + "任务 884152.TI 完成\n", + "任务 884153.TI 完成\n", + "任务 884154.TI 完成\n", + "任务 884155.TI 完成\n", + "任务 884156.TI 完成\n", + "任务 884157.TI 完成\n", + "任务 884158.TI 完成\n", + "任务 884159.TI 完成\n", + "任务 884160.TI 完成\n", + "任务 884161.TI 完成\n", + "任务 884162.TI 完成\n", + "任务 884163.TI 完成\n", + "任务 884164.TI 完成\n", + "任务 884165.TI 完成\n", + "任务 884167.TI 完成\n", + "任务 884168.TI 完成\n", + "任务 884172.TI 完成\n", + "任务 884176.TI 完成\n", + "任务 884177.TI 完成\n", + "任务 884178.TI 完成\n", + "任务 884180.TI 完成\n", + "任务 884181.TI 完成\n", + "任务 884182.TI 完成\n", + "任务 884183.TI 完成\n", + "任务 884184.TI 完成\n", + "任务 884185.TI 完成\n", + "任务 884186.TI 完成\n", + "任务 884188.TI 完成\n", + "任务 884189.TI 完成\n", + "任务 884191.TI 完成\n", + "任务 884192.TI 完成\n", + "任务 884193.TI 完成\n", + "任务 884195.TI 完成\n", + "任务 884197.TI 完成\n", + "任务 884199.TI 完成\n", + "任务 884200.TI 完成\n", + "任务 884201.TI 完成\n", + "任务 884202.TI 完成\n", + "任务 884203.TI 完成\n", + "任务 884205.TI 完成\n", + "任务 884206.TI 完成\n", + "任务 884207.TI 完成\n", + "任务 884208.TI 完成\n", + "任务 884209.TI 完成\n", + "任务 884210.TI 完成\n", + "任务 884211.TI 完成\n", + "任务 884212.TI 完成\n", + "任务 884213.TI 完成\n", + "任务 884214.TI 完成\n", + "任务 884215.TI 完成\n", + "任务 884217.TI 完成\n", + "任务 884218.TI 完成\n", + "任务 884219.TI 完成\n", + "任务 884220.TI 完成\n", + "任务 884221.TI 完成\n", + "任务 884225.TI 完成\n", + "任务 884227.TI 完成\n", + "任务 884228.TI 完成\n", + "任务 884229.TI 完成\n", + "任务 884230.TI 完成\n", + "任务 884231.TI 完成\n", + "任务 884232.TI 完成\n", + "任务 884233.TI 完成\n", + "任务 884234.TI 完成\n", + "任务 884235.TI 完成\n", + "任务 884236.TI 完成\n", + "任务 884237.TI 完成\n", + "任务 884238.TI 完成\n", + "任务 884239.TI 完成\n", + "任务 884240.TI 完成\n", + "任务 884242.TI 完成\n", + "任务 884243.TI 完成\n", + "任务 884244.TI 完成\n", + "任务 884245.TI 完成\n", + "任务 884246.TI 完成\n", + "任务 884247.TI 完成\n", + "任务 884248.TI 完成\n", + "任务 884249.TI 完成\n", + "任务 884250.TI 完成\n", + "任务 884251.TI 完成\n", + "任务 884252.TI 完成\n", + "任务 884253.TI 完成\n", + "任务 884254.TI 完成\n", + "任务 884255.TI 完成\n", + "任务 884256.TI 完成\n", + "任务 884257.TI 完成\n", + "任务 884258.TI 完成\n", + "任务 884259.TI 完成\n", + "任务 884260.TI 完成\n", + "任务 884261.TI 完成\n", + "任务 884262.TI 完成\n", + "任务 884263.TI 完成\n", + "任务 884264.TI 完成\n", + "任务 884265.TI 完成\n", + "任务 884266.TI 完成\n", + "任务 884267.TI 完成\n", + "任务 884268.TI 完成\n", + "任务 884269.TI 完成\n", + "任务 884270.TI 完成\n", + "任务 884271.TI 完成\n", + "任务 884273.TI 完成\n", + "任务 884272.TI 完成\n", + "任务 884274.TI 完成\n", + "任务 884275.TI 完成\n", + "任务 884277.TI 完成\n", + "任务 884276.TI 完成\n", + "任务 884278.TI 完成\n", + "任务 884279.TI 完成\n", + "任务 884280.TI 完成\n", + "任务 884281.TI 完成\n", + "任务 884283.TI 完成\n", + "任务 884282.TI 完成\n", + "任务 884284.TI 完成\n", + "任务 884285.TI 完成\n", + "任务 884287.TI 完成\n", + "任务 884286.TI 完成\n", + "任务 884288.TI 完成\n", + "任务 884289.TI 完成\n", + "任务 884290.TI 完成\n", + "任务 884291.TI 完成\n", + "任务 884292.TI 完成\n", + "任务 884293.TI 完成\n", + "任务 884295.TI 完成\n", + "任务 884294.TI 完成\n", + "任务 884296.TI 完成\n", + "任务 884297.TI 完成\n", + "任务 884298.TI 完成\n", + "任务 884299.TI 完成\n", + "任务 884301.TI 完成\n", + "任务 884300.TI 完成\n", + "任务 884302.TI 完成\n", + "任务 884303.TI 完成\n", + "任务 884304.TI 完成\n", + "任务 884305.TI 完成\n", + "任务 884306.TI 完成\n", + "任务 884307.TI 完成\n", + "任务 884308.TI 完成\n", + "任务 884309.TI 完成\n", + "任务 884310.TI 完成\n", + "任务 884311.TI 完成\n", + "任务 884312.TI 完成\n", + "任务 884313.TI 完成\n", + "任务 884314.TI 完成\n", + "任务 884315.TI 完成\n", + "任务 883301.TI 完成\n", + "任务 883300.TI 完成\n", + "任务 883303.TI 完成\n", + "任务 883302.TI 完成\n", + "任务 885311.TI 完成\n", + "任务 883304.TI 完成\n", + "任务 885312.TI 完成\n", + "任务 885333.TI 完成\n", + "任务 885343.TI 完成\n", + "任务 885338.TI 完成\n", + "任务 885345.TI 完成\n", + "任务 885355.TI 完成\n", + "任务 885372.TI 完成\n", + "任务 885376.TI 完成\n", + "任务 885378.TI 完成\n", + "任务 885386.TI 完成\n", + "任务 885398.TI 完成\n", + "任务 885402.TI 完成\n", + "任务 885406.TI 完成\n", + "任务 885410.TI 完成\n", + "任务 885412.TI 完成\n", + "任务 885413.TI 完成\n", + "任务 885418.TI 完成\n", + "任务 885420.TI 完成\n", + "任务 885423.TI 完成\n", + "任务 885425.TI 完成\n", + "任务 885426.TI 完成\n", + "任务 885427.TI 完成\n", + "任务 885428.TI 完成\n", + "任务 885430.TI 完成\n", + "任务 885431.TI 完成\n", + "任务 885439.TI 完成\n", + "任务 885454.TI 完成\n", + "任务 885456.TI 完成\n", + "任务 885457.TI 完成\n", + "任务 885459.TI 完成\n", + "任务 885461.TI 完成\n", + "任务 885462.TI 完成\n", + "任务 885467.TI 完成\n", + "任务 885472.TI 完成\n", + "任务 885478.TI 完成\n", + "任务 885480.TI 完成\n", + "任务 885487.TI 完成\n", + "任务 885490.TI 完成\n", + "任务 885493.TI 完成\n", + "任务 885494.TI 完成\n", + "任务 885497.TI 完成\n", + "任务 885502.TI 完成\n", + "任务 885505.TI 完成\n", + "任务 885508.TI 完成\n", + "任务 885514.TI 完成\n", + "任务 885517.TI 完成\n", + "任务 885520.TI 完成\n", + "任务 885521.TI 完成\n", + "任务 885522.TI 完成\n", + "任务 885525.TI 完成\n", + "任务 885530.TI 完成\n", + "任务 885531.TI 完成\n", + "任务 885537.TI 完成\n", + "任务 885539.TI 完成\n", + "任务 885540.TI 完成\n", + "任务 885543.TI 完成\n", + "任务 885545.TI 完成\n", + "任务 885551.TI 完成\n", + "任务 885552.TI 完成\n", + "任务 885555.TI 完成\n", + "任务 885556.TI 完成\n", + "任务 885562.TI 完成\n", + "任务 885563.TI 完成\n", + "任务 885564.TI 完成\n", + "任务 885566.TI 完成\n", + "任务 885570.TI 完成\n", + "任务 885571.TI 完成\n", + "任务 885572.TI 完成\n", + "任务 885573.TI 完成\n", + "任务 885574.TI 完成\n", + "任务 885578.TI 完成\n", + "任务 885580.TI 完成\n", + "任务 885586.TI 完成\n", + "任务 885587.TI 完成\n", + "任务 885591.TI 完成\n", + "任务 885595.TI 完成\n", + "任务 885598.TI 完成\n", + "任务 885603.TI 完成\n", + "任务 885611.TI 完成\n", + "任务 885615.TI 完成\n", + "任务 885617.TI 完成\n", + "任务 885620.TI 完成\n", + "任务 885623.TI 完成\n", + "任务 885629.TI 完成\n", + "任务 885633.TI 完成\n", + "任务 885640.TI 完成\n", + "任务 885641.TI 完成\n", + "任务 885642.TI 完成\n", + "任务 885650.TI 完成\n", + "任务 885652.TI 完成\n", + "任务 885661.TI 完成\n", + "任务 885662.TI 完成\n", + "任务 885663.TI 完成\n", + "任务 885690.TI 完成\n", + "任务 885692.TI 完成\n", + "任务 885694.TI 完成\n", + "任务 885697.TI 完成\n", + "任务 885699.TI 完成\n", + "任务 885700.TI 完成\n", + "任务 885705.TI 完成\n", + "任务 885706.TI 完成\n", + "任务 885709.TI 完成\n", + "任务 885710.TI 完成\n", + "任务 885728.TI 完成\n", + "任务 885730.TI 完成\n", + "任务 885733.TI 完成\n", + "任务 885734.TI 完成\n", + "任务 885736.TI 完成\n", + "任务 885737.TI 完成\n", + "任务 885738.TI 完成\n", + "任务 885739.TI 完成\n", + "任务 885740.TI 完成\n", + "任务 885741.TI 完成\n", + "任务 885742.TI 完成\n", + "任务 885743.TI 完成\n", + "任务 885744.TI 完成\n", + "任务 885747.TI 完成\n", + "任务 885748.TI 完成\n", + "任务 885749.TI 完成\n", + "任务 885750.TI 完成\n", + "任务 885753.TI 完成\n", + "任务 885756.TI 完成\n", + "任务 885757.TI 完成\n", + "任务 885758.TI 完成\n", + "任务 885759.TI 完成\n", + "任务 885760.TI 完成\n", + "任务 885761.TI 完成\n", + "任务 885764.TI 完成\n", + "任务 885765.TI 完成\n", + "任务 885766.TI 完成\n", + "任务 885767.TI 完成\n", + "任务 885768.TI 完成\n", + "任务 885769.TI 完成\n", + "任务 885770.TI 完成\n", + "任务 885771.TI 完成\n", + "任务 885772.TI 完成\n", + "任务 885774.TI 完成\n", + "任务 885775.TI 完成\n", + "任务 885779.TI 完成\n", + "任务 885780.TI 完成\n", + "任务 885781.TI 完成\n", + "任务 885782.TI 完成\n", + "任务 885783.TI 完成\n", + "任务 885785.TI 完成\n", + "任务 885786.TI 完成\n", + "任务 885787.TI 完成\n", + "任务 885788.TI 完成\n", + "任务 885789.TI 完成\n", + "任务 885791.TI 完成\n", + "任务 885792.TI 完成\n", + "任务 885795.TI 完成\n", + "任务 885797.TI 完成\n", + "任务 885800.TI 完成\n", + "任务 885805.TI 完成\n", + "任务 885806.TI 完成\n", + "任务 885808.TI 完成\n", + "任务 885809.TI 完成\n", + "任务 885810.TI 完成\n", + "任务 885811.TI 完成\n", + "任务 885812.TI 完成\n", + "任务 885814.TI 完成\n", + "任务 885818.TI 完成\n", + "任务 885819.TI 完成\n", + "任务 885820.TI 完成\n", + "任务 885823.TI 完成\n", + "任务 885825.TI 完成\n", + "任务 885827.TI 完成\n", + "任务 885829.TI 完成\n", + "任务 885832.TI 完成\n", + "任务 885834.TI 完成\n", + "任务 885835.TI 完成\n", + "任务 885838.TI 完成\n", + "任务 885839.TI 完成\n", + "任务 885840.TI 完成\n", + "任务 885842.TI 完成\n", + "任务 885843.TI 完成\n", + "任务 885844.TI 完成\n", + "任务 885845.TI 完成\n", + "任务 885846.TI 完成\n", + "任务 885849.TI 完成\n", + "任务 885851.TI 完成\n", + "任务 885852.TI 完成\n", + "任务 885854.TI 完成\n", + "任务 885856.TI 完成\n", + "任务 885860.TI 完成\n", + "任务 885861.TI 完成\n", + "任务 885863.TI 完成\n", + "任务 885864.TI 完成\n", + "任务 885865.TI 完成\n", + "任务 885866.TI 完成\n", + "任务 885868.TI 完成\n", + "任务 885873.TI 完成\n", + "任务 885874.TI 完成\n", + "任务 885875.TI 完成\n", + "任务 885876.TI 完成\n", + "任务 885877.TI 完成\n", + "任务 885878.TI 完成\n", + "任务 885879.TI 完成\n", + "任务 885881.TI 完成\n", + "任务 885882.TI 完成\n", + "任务 885883.TI 完成\n", + "任务 885884.TI 完成\n", + "任务 885886.TI 完成\n", + "任务 885887.TI 完成\n", + "任务 885890.TI 完成\n", + "任务 885888.TI 完成\n", + "任务 885893.TI 完成\n", + "任务 885894.TI 完成\n", + "任务 885897.TI 完成\n", + "任务 885898.TI 完成\n", + "任务 885899.TI 完成\n", + "任务 885900.TI 完成\n", + "任务 885901.TI 完成\n", + "任务 885902.TI 完成\n", + "任务 885903.TI 完成\n", + "任务 885904.TI 完成\n", + "任务 885905.TI 完成\n", + "任务 885907.TI 完成\n", + "任务 885908.TI 完成\n", + "任务 885909.TI 完成\n", + "任务 885910.TI 完成\n", + "任务 885911.TI 完成\n", + "任务 885912.TI 完成\n", + "任务 885913.TI 完成\n", + "任务 885914.TI 完成\n", + "任务 885915.TI 完成\n", + "任务 885916.TI 完成\n", + "任务 885918.TI 完成\n", + "任务 885919.TI 完成\n", + "任务 885920.TI 完成\n", + "任务 885921.TI 完成\n", + "任务 885922.TI 完成\n", + "任务 885923.TI 完成\n", + "任务 885924.TI 完成\n", + "任务 885925.TI 完成\n", + "任务 885926.TI 完成\n", + "任务 885927.TI 完成\n", + "任务 885928.TI 完成\n", + "任务 885929.TI 完成\n", + "任务 885930.TI 完成\n", + "任务 885931.TI 完成\n", + "任务 885933.TI 完成\n", + "任务 885934.TI 完成\n", + "任务 885935.TI 完成\n", + "任务 885937.TI 完成\n", + "任务 885936.TI 完成\n", + "任务 885939.TI 完成\n", + "任务 885938.TI 完成\n", + "任务 885942.TI 完成\n", + "任务 885940.TI 完成\n", + "任务 885943.TI 完成\n", + "任务 885944.TI 完成\n", + "任务 885945.TI 完成\n", + "任务 885946.TI 完成\n", + "任务 885947.TI 完成\n", + "任务 885948.TI 完成\n", + "任务 885950.TI 完成\n", + "任务 885951.TI 完成\n", + "任务 885952.TI 完成\n", + "任务 885953.TI 完成\n", + "任务 885955.TI 完成\n", + "任务 885956.TI 完成\n", + "任务 885957.TI 完成\n", + "任务 885958.TI 完成\n", + "任务 885959.TI 完成\n", + "任务 885960.TI 完成\n", + "任务 885961.TI 完成\n", + "任务 885962.TI 完成\n", + "任务 885963.TI 完成\n", + "任务 885964.TI 完成\n", + "任务 885966.TI 完成\n", + "任务 885965.TI 完成\n", + "任务 885967.TI 完成\n", + "任务 885968.TI 完成\n", + "任务 885969.TI 完成\n", + "任务 885970.TI 完成\n", + "任务 885971.TI 完成\n", + "任务 885972.TI 完成\n", + "任务 885973.TI 完成\n", + "任务 885974.TI 完成\n", + "任务 885975.TI 完成\n", + "任务 885976.TI 完成\n", + "任务 885977.TI 完成\n", + "任务 885978.TI 完成\n", + "任务 885980.TI 完成\n", + "任务 885981.TI 完成\n", + "任务 885982.TI 完成\n", + "任务 885985.TI 完成\n", + "任务 885986.TI 完成\n", + "任务 885987.TI 完成\n", + "任务 885988.TI 完成\n", + "任务 885989.TI 完成\n", + "任务 885990.TI 完成\n", + "任务 885991.TI 完成\n", + "任务 885992.TI 完成\n", + "任务 885994.TI 完成\n", + "任务 885995.TI 完成\n", + "任务 885996.TI 完成\n", + "任务 885997.TI 完成\n", + "任务 885998.TI 完成\n", + "任务 885999.TI 完成\n", + "任务 886000.TI 完成\n", + "任务 886001.TI 完成\n", + "任务 886002.TI 完成\n", + "任务 886003.TI 完成\n", + "任务 886004.TI 完成\n", + "任务 886005.TI 完成\n", + "任务 886006.TI 完成\n", + "任务 886007.TI 完成\n", + "任务 886008.TI 完成\n", + "任务 886009.TI 完成\n", + "任务 886010.TI 完成\n", + "任务 886011.TI 完成\n", + "任务 886012.TI 完成\n", + "任务 886013.TI 完成\n", + "任务 886015.TI 完成\n", + "任务 886016.TI 完成\n", + "任务 886017.TI 完成\n", + "任务 886018.TI 完成\n", + "任务 886019.TI 完成\n", + "任务 886020.TI 完成\n", + "任务 886021.TI 完成\n", + "任务 886023.TI 完成\n", + "任务 886026.TI 完成\n", + "任务 886028.TI 完成\n", + "任务 886030.TI 完成\n", + "任务 886031.TI 完成\n", + "任务 886032.TI 完成\n", + "任务 886033.TI 完成\n", + "任务 886034.TI 完成\n", + "任务 886035.TI 完成\n", + "任务 886036.TI 完成\n", + "任务 886037.TI 完成\n", + "任务 886038.TI 完成\n", + "任务 886039.TI 完成\n", + "任务 886040.TI 完成\n", + "任务 886041.TI 完成\n", + "任务 886042.TI 完成\n", + "任务 886043.TI 完成\n", + "任务 886044.TI 完成\n", + "任务 886045.TI 完成\n", + "任务 886046.TI 完成\n", + "任务 886047.TI 完成\n", + "任务 886048.TI 完成\n", + "任务 886049.TI 完成\n", + "任务 886050.TI 完成\n", + "任务 886052.TI 完成\n", + "任务 886051.TI 完成\n", + "任务 886054.TI 完成\n", + "任务 886053.TI 完成\n", + "任务 886055.TI 完成\n", + "任务 886056.TI 完成\n", + "任务 886057.TI 完成\n", + "任务 886058.TI 完成\n", + "任务 886059.TI 完成\n", + "任务 886060.TI 完成\n", + "任务 886061.TI 完成\n", + "任务 886062.TI 完成\n", + "任务 886063.TI 完成\n", + "任务 886064.TI 完成\n", + "任务 886065.TI 完成\n", + "任务 886066.TI 完成\n", + "任务 886067.TI 完成\n", + "任务 886068.TI 完成\n", + "任务 886069.TI 完成\n", + "任务 886070.TI 完成\n", + "任务 886071.TI 完成\n", + "任务 886072.TI 完成\n", + "任务 886073.TI 完成\n", + "任务 886074.TI 完成\n", + "任务 886075.TI 完成\n", + "任务 886076.TI 完成\n", + "任务 886077.TI 完成\n", + "任务 886078.TI 完成\n", + "任务 886080.TI 完成\n", + "任务 886081.TI 完成\n", + "任务 886082.TI 完成\n", + "任务 886084.TI 完成\n", + "任务 886085.TI 完成\n", + "任务 886086.TI 完成\n", + "任务 886087.TI 完成\n", + "任务 886088.TI 完成\n", + "任务 886089.TI 完成\n", + "任务 886090.TI 完成\n", + "任务 886091.TI 完成\n", + "任务 886093.TI 完成\n", + "任务 886094.TI 完成\n", + "任务 886095.TI 完成\n", + "任务 886096.TI 完成\n", + "任务 886097.TI 完成\n", + "任务 886098.TI 完成\n", + "任务 886099.TI 完成\n", + "任务 886100.TI 完成\n", + "任务 865067.TI 完成\n", + "任务 865069.TI 完成\n", + "任务 865070.TI 完成\n", + "任务 886101.TI 完成\n", + "任务 885362.TI 完成\n", + "任务 886102.TI 完成\n", + "任务 886103.TI 完成\n" + ] + } + ], + "source": [ + "from concurrent.futures import ThreadPoolExecutor, as_completed\n", + "\n", + "all_daily_data = []\n", + "\n", + "# API 调用计数和时间控制变量\n", + "api_call_count = 0\n", + "batch_start_time = time.time()\n", + "\n", + "\n", + "def get_data(ts_code):\n", + " time.sleep(0.1)\n", + " data = pro.ths_member(ts_code=ts_code)\n", + " if data is not None and not data.empty:\n", + " return data\n", + "\n", + "\n", + "with ThreadPoolExecutor(max_workers=2) as executor:\n", + " future_to_code = {executor.submit(get_data, ts): ts for ts in ths_index_list}\n", + "\n", + " for future in as_completed(future_to_code):\n", + " trade_date = future_to_code[future]\n", + " try:\n", + " result = future.result()\n", + " all_daily_data.append(result)\n", + " print(f\"任务 {trade_date} 完成\")\n", + " except Exception as e:\n", + " print(f\"获取 {trade_date} 数据时出错: {e}\")\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "907f732d3c397bf", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-12T15:31:10.381348500Z", + "start_time": "2025-03-12T15:23:41.345460Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "所有每日基础数据获取并保存完毕!\n" + ] + } + ], + "source": [ + "\n", + "# 将所有数据合并为一个 DataFrame\n", + "all_daily_data_df = pd.concat(all_daily_data, ignore_index=True)\n", + "\n", + "# 将数据保存为 HDF5 文件(table 格式)\n", + "all_daily_data_df.to_hdf(h5_filename, key=key, mode='w', format='table', data_columns=True)\n", + "\n", + "print(\"所有每日基础数据获取并保存完毕!\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "73e829ac-ff3d-408e-beb3-0b87f5b00b19", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ts_code con_code\n", + "0 882002.TI 000008.SZ\n", + "1 882002.TI 000065.SZ\n", + "2 882002.TI 000151.SZ\n", + "3 882002.TI 000402.SZ\n", + "4 882002.TI 000603.SZ\n", + "... ... ...\n", + "326393 886103.TI 688103.SH\n", + "326394 886103.TI 688117.SH\n", + "326395 886103.TI 688186.SH\n", + "326396 886103.TI 688252.SH\n", + "326397 886103.TI 688722.SH\n", + "\n", + "[326398 rows x 2 columns]\n" + ] + } + ], + "source": [ + "max_date = None\n", + "with pd.HDFStore(h5_filename, mode='r') as store:\n", + " df = store[key][['ts_code', 'con_code']]\n", + " print(df)\n" + ] + } + ], + "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 +} diff --git a/code/data/update/cyq-perf.ipynb b/main/data/update/cyq-perf.ipynb similarity index 100% rename from code/data/update/cyq-perf.ipynb rename to main/data/update/cyq-perf.ipynb diff --git a/code/data/update/index_data.ipynb b/main/data/update/index_data.ipynb similarity index 100% rename from code/data/update/index_data.ipynb rename to main/data/update/index_data.ipynb diff --git a/code/data/update/sw_daily.ipynb b/main/data/update/sw_daily.ipynb similarity index 100% rename from code/data/update/sw_daily.ipynb rename to main/data/update/sw_daily.ipynb diff --git a/code/data/update/update_daily_basic.ipynb b/main/data/update/update_daily_basic.ipynb similarity index 100% rename from code/data/update/update_daily_basic.ipynb rename to main/data/update/update_daily_basic.ipynb diff --git a/code/data/update/update_daily_data.ipynb b/main/data/update/update_daily_data.ipynb similarity index 100% rename from code/data/update/update_daily_data.ipynb rename to main/data/update/update_daily_data.ipynb diff --git a/code/data/update/update_is_st.ipynb b/main/data/update/update_is_st.ipynb similarity index 100% rename from code/data/update/update_is_st.ipynb rename to main/data/update/update_is_st.ipynb diff --git a/code/data/update/update_money_flow.ipynb b/main/data/update/update_money_flow.ipynb similarity index 100% rename from code/data/update/update_money_flow.ipynb rename to main/data/update/update_money_flow.ipynb diff --git a/code/data/update/update_name_change.ipynb b/main/data/update/update_name_change.ipynb similarity index 100% rename from code/data/update/update_name_change.ipynb rename to main/data/update/update_name_change.ipynb diff --git a/code/data/update/update_stk_limit.ipynb b/main/data/update/update_stk_limit.ipynb similarity index 100% rename from code/data/update/update_stk_limit.ipynb rename to main/data/update/update_stk_limit.ipynb diff --git a/main/factor/__init__.py b/main/factor/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/main/factor/__pycache__/__init__.cpython-310.pyc b/main/factor/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..af061ff Binary files /dev/null and b/main/factor/__pycache__/__init__.cpython-310.pyc differ diff --git a/main/factor/__pycache__/__init__.cpython-311.pyc b/main/factor/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..1a5d30c Binary files /dev/null and b/main/factor/__pycache__/__init__.cpython-311.pyc differ diff --git a/main/factor/__pycache__/factor.cpython-310.pyc b/main/factor/__pycache__/factor.cpython-310.pyc new file mode 100644 index 0000000..a893bed Binary files /dev/null and b/main/factor/__pycache__/factor.cpython-310.pyc differ diff --git a/main/factor/__pycache__/factor.cpython-311.pyc b/main/factor/__pycache__/factor.cpython-311.pyc new file mode 100644 index 0000000..b4b8b8f Binary files /dev/null and b/main/factor/__pycache__/factor.cpython-311.pyc differ diff --git a/main/factor/__pycache__/operator.cpython-311.pyc b/main/factor/__pycache__/operator.cpython-311.pyc new file mode 100644 index 0000000..7670ed8 Binary files /dev/null and b/main/factor/__pycache__/operator.cpython-311.pyc differ diff --git a/main/factor/factor.py b/main/factor/factor.py new file mode 100644 index 0000000..c8eefb5 --- /dev/null +++ b/main/factor/factor.py @@ -0,0 +1,1532 @@ +import numpy as np +import pandas as pd +import talib + + +def get_rolling_factor(df): + old_columns = df.columns.tolist()[:] + + # 按股票和日期排序(如果尚未排序) + df = df.sort_values(by=['ts_code', 'trade_date']) + + grouped = df.groupby('ts_code', group_keys=False) + + epsilon = 1e-8 + df['lg_elg_net_buy_vol'] = df['buy_lg_vol'] + df['buy_elg_vol'] - df['sell_lg_vol'] - df['sell_elg_vol'] + # 检查 'volume' 列是否存在且有效 + df['flow_lg_elg_intensity'] = df['lg_elg_net_buy_vol'] / (df['vol'] + epsilon) + + # 2. 散户与主力背离度 (Retail vs Institutional Divergence) + # 衡量小单净流入与(大单+超大单)净流入的差异或比率 + df['sm_net_buy_vol'] = df['buy_sm_vol'] - df['sell_sm_vol'] + df['flow_divergence_diff'] = df['sm_net_buy_vol'] - df['lg_elg_net_buy_vol'] + # 比率形式可能更稳定 + df['flow_divergence_ratio'] = df['sm_net_buy_vol'] / ( + df['lg_elg_net_buy_vol'] + np.sign(df['lg_elg_net_buy_vol']) * epsilon + epsilon) # 复杂处理避免0/0 + + # 3. 资金流结构变动 (Flow Structure Change - Relative Strength of Large Flow) + # 大单+超大单买入额占总买入额的比例的变化 + df['total_buy_vol'] = df['buy_sm_vol'] + df['buy_lg_vol'] + df['buy_elg_vol'] + df['lg_elg_buy_prop'] = (df['buy_lg_vol'] + df['buy_elg_vol']) / (df['total_buy_vol'] + epsilon) + df['flow_struct_buy_change'] = grouped['lg_elg_buy_prop'].diff(1) # 1日变化 + + # 4. 资金流加速度 (Flow Acceleration) + # 净主力资金流的变化率(二阶导) + df['lg_elg_net_buy_vol_change'] = grouped['lg_elg_net_buy_vol'].diff(1) + df['flow_lg_elg_accel'] = grouped['lg_elg_net_buy_vol_change'].diff(1) + + # # 5. 极端资金流事件 (Categorical: Extreme Flow Event) + # # 定义主力资金流强度是否处于其历史极端水平(例如,过去N天的90分位数以上或10分位数以下) + # rolling_window = 20 # 可调整窗口期 + + # # Step 1: Calculate the rolling quantiles separately + # rolling_high = grouped['flow_lg_elg_intensity'].rolling(rolling_window, min_periods=1).quantile(0.9) # min_periods=1 保证窗口未满时也有输出 + # rolling_low = grouped['flow_lg_elg_intensity'].rolling(rolling_window, min_periods=1).quantile(0.1) + + # # Step 2: Assign the results to the DataFrame + # # 确保 df 和 rolling_high/low 的索引是一致的 + # # 如果 df 的索引在此期间没有被修改过,这通常是安全的 + # df['flow_lg_elg_intensity_rolling_high'] = rolling_high + # df['flow_lg_elg_intensity_rolling_low'] = rolling_low + + # # Step 3: Continue with the logic using the new columns + # conditions_flow = [ + # df['flow_lg_elg_intensity'] > df['flow_lg_elg_intensity_rolling_high'], + # df['flow_lg_elg_intensity'] < df['flow_lg_elg_intensity_rolling_low'] + # ] + # choices_flow = [1, -1] # 1: 极端流入, -1: 极端流出 + # df['cat_extreme_flow'] = np.select(conditions_flow, choices_flow, default=0) + + # --- 筹码分布因子 --- + + # 6. 筹码集中度 (Chip Concentration) + # 衡量筹码分布的紧密程度,例如 95% 与 5% 成本价的差距,相对于当前价格进行标准化 + # 检查 'close' 列是否存在且有效 + df['chip_concentration_range'] = (df['cost_95pct'] - df['cost_5pct']) / (df['close'] + epsilon) + + # 7. 筹码分布偏度 (Chip Distribution Skewness Proxy) + # 比较中位数成本 (cost_50pct) 和加权平均成本 (weight_avg) + # weight_avg > cost_50pct 暗示高成本区有较多筹码(右偏) + df['chip_skewness'] = (df['weight_avg'] - df['cost_50pct']) / (df['cost_50pct'] + epsilon) + + # 8. 浮筹比例 (Floating Chips Proxy) + # 衡量短期内(例如15%成本线以下)的筹码比例与总获利盘比例的关系 + # winner_rate 高但 cost_15pct 接近当前价,可能意味着大部分获利盘成本不高,易浮动 + # 这里简化为:获利盘比例 与 (当前价-15%成本价)/当前价 的乘积 + price_dist_cost15 = (df['close'] - df['cost_15pct']) / (df['close'] + epsilon) + df['floating_chip_proxy'] = df['winner_rate'] * np.maximum(0, price_dist_cost15) # 只考虑价格高于15%成本线的情况 + + # 9. 成本支撑强度变化 (Cost Support Strength Change) + # 观察低位筹码成本(如 5% 或 15% 分位点)的变化率,看支撑位是上移还是下移 + df['cost_support_15pct_change'] = grouped['cost_15pct'].pct_change(1) * 100 # 百分比变化 + + # 10. 获利盘压力/支撑区 (Categorical: Winner Rate Zone & Price Position) + # 结合获利盘比例和当前价格相对于筹码成本的位置 + # 例如: 价格在 85% 成本线之上 & 获利盘 > 0.8 -> 高位派发风险区? + # 价格在 15% 成本线之下 & 获利盘 < 0.2 -> 低位吸筹潜力区? + conditions_winner = [ + (df['close'] > df['cost_85pct']) & (df['winner_rate'] > 0.8), # 高位 & 高获利盘 + (df['close'] < df['cost_15pct']) & (df['winner_rate'] < 0.2), # 低位 & 低获利盘 + (df['close'] > df['cost_50pct']) & (df['winner_rate'] > 0.5), # 中高位 & 多数获利 + (df['close'] < df['cost_50pct']) & (df['winner_rate'] < 0.5), # 中低位 & 多数亏损 + ] + choices_winner = [1, 2, 3, 4] # 1:高风险区, 2:低潜力区, 3:中上获利区, 4:中下亏损区 + df['cat_winner_price_zone'] = np.select(conditions_winner, choices_winner, default=0) # 0: 其他 + + # --- 结合因子 --- + + # 11. 主力行为与筹码结构一致性 (Flow-Chip Consistency) + # 例如:主力净买入发生在价格接近下方筹码密集区(如 cost_15pct 到 cost_50pct)时 + price_near_low_support = (df['close'] > df['cost_15pct']) & (df['close'] < df['cost_50pct']) + df['flow_chip_consistency'] = df['lg_elg_net_buy_vol'] * price_near_low_support.astype(int) + # 可以进一步标准化或做成 categorical + + # 12. 获利了结压力/承接盘强度 (Profit-Taking Pressure vs Absorption) + # 在高获利盘(winner_rate > 0.7)的情况下,观察主力资金是净流出(了结)还是净流入(高位换手/承接) + high_winner_rate_flag = (df['winner_rate'] > 0.7).astype(int) + df['profit_taking_vs_absorb'] = df['lg_elg_net_buy_vol'] * high_winner_rate_flag + # 正值表示高获利盘下主力仍在买入(承接),负值表示主力在卖出(了结) + + # 清理临时列和可能产生的 NaN (可选,根据需要处理) + cols_to_drop = ['lg_elg_net_buy_vol', 'sm_net_buy_vol', 'total_buy_vol', 'lg_elg_buy_prop', + 'lg_elg_net_buy_vol_change', 'flow_lg_elg_intensity_rolling_high', + 'flow_lg_elg_intensity_rolling_low'] + # df = df.drop(columns=cols_to_drop) + + window = 20 + df['_is_positive'] = (df['pct_chg'] > 0).astype(int) + df['_is_negative'] = (df['pct_chg'] < 0).astype(int) + df['cat_is_positive'] = (df['pct_chg'] > 0).astype(int) + + # 分离正负收益率 (用于计算各自的均值和平方均值) + # 注意:这里我们保留原始收益率用于计算,而不是 clip 到 0 + df['_pos_returns'] = df['pct_chg'].where(df['pct_chg'] > 0, 0) # 非正设为0,便于求和 + df['_neg_returns'] = df['pct_chg'].where(df['pct_chg'] < 0, 0) # 非负设为0,便于求和 + + # 计算收益率的平方 (用于计算 E[X^2]) + df['_pos_returns_sq'] = np.square(df['_pos_returns']) + df['_neg_returns_sq'] = np.square(df['_neg_returns']) # 平方后负数变正 + + # 4. 计算滚动统计量 (使用内置函数,速度较快) + # 计算正收益日的统计量 + rolling_pos_count = grouped['_is_positive'].rolling(window, min_periods=max(1, window // 2)).sum() + rolling_pos_sum = grouped['_pos_returns'].rolling(window, min_periods=max(1, window // 2)).sum() + rolling_pos_sum_sq = grouped['_pos_returns_sq'].rolling(window, min_periods=max(1, window // 2)).sum() + + # 计算负收益日的统计量 + rolling_neg_count = grouped['_is_negative'].rolling(window, min_periods=max(1, window // 2)).sum() + rolling_neg_sum = grouped['_neg_returns'].rolling(window, min_periods=max(1, window // 2)).sum() + rolling_neg_sum_sq = grouped['_neg_returns_sq'].rolling(window, min_periods=max(1, window // 2)).sum() + + # 5. 计算方差和标准差 + pos_mean_sq = rolling_pos_sum_sq / rolling_pos_count + pos_mean = rolling_pos_sum / rolling_pos_count + pos_var = pos_mean_sq - np.square(pos_mean) + pos_var = pos_var.where(rolling_pos_count >= 2, np.nan).clip(lower=0) + upside_vol = np.sqrt(pos_var) + + neg_mean_sq = rolling_neg_sum_sq / rolling_neg_count + neg_mean = rolling_neg_sum / rolling_neg_count # 注意 neg_mean 是负数 + neg_var = neg_mean_sq - np.square(neg_mean) + neg_var = neg_var.where(rolling_neg_count >= 2, np.nan).clip(lower=0) + downside_vol = np.sqrt(neg_var) + + # rolling 操作后结果带有 MultiIndex,需要去除股票代码层级以便合并 + df['upside_vol'] = upside_vol.reset_index(level=0, drop=True) + df['downside_vol'] = downside_vol.reset_index(level=0, drop=True) + + df['vol_ratio'] = df['upside_vol'] / df['downside_vol'] + df['vol_ratio'] = df['vol_ratio'].replace([np.inf, -np.inf], np.nan).fillna(0) # 或 fillna(np.nan) + + df['return_skew'] = grouped['pct_chg'].rolling(window=5).skew().reset_index(0, drop=True) + df['return_kurtosis'] = grouped['pct_chg'].rolling(window=5).kurt().reset_index(0, drop=True) + + # 因子 1:短期成交量变化率 + df['volume_change_rate'] = ( + grouped['vol'].rolling(window=2).mean() / + grouped['vol'].rolling(window=10).mean() - 1 + ).reset_index(level=0, drop=True) # 确保索引对齐 + + # 因子 2:成交量突破信号 + max_volume = grouped['vol'].rolling(window=5).max().reset_index(level=0, drop=True) # 确保索引对齐 + df['cat_volume_breakout'] = (df['vol'] > max_volume) + + # 因子 3:换手率均线偏离度 + mean_turnover = grouped['turnover_rate'].rolling(window=3).mean().reset_index(level=0, drop=True) + std_turnover = grouped['turnover_rate'].rolling(window=3).std().reset_index(level=0, drop=True) + df['turnover_deviation'] = (df['turnover_rate'] - mean_turnover) / std_turnover + + # 因子 4:换手率激增信号 + df['cat_turnover_spike'] = (df['turnover_rate'] > mean_turnover + 2 * std_turnover) + + # 因子 5:量比均值 + df['avg_volume_ratio'] = grouped['volume_ratio'].rolling(window=3).mean().reset_index(level=0, drop=True) + + # 因子 6:量比突破信号 + max_volume_ratio = grouped['volume_ratio'].rolling(window=5).max().reset_index(level=0, drop=True) + df['cat_volume_ratio_breakout'] = (df['volume_ratio'] > max_volume_ratio) + + df['vol_spike'] = grouped.apply( + lambda x: pd.Series(x['vol'].rolling(20).mean(), index=x.index) + ) + df['vol_std_5'] = grouped['vol'].pct_change().rolling(window=5).std() + + # 计算 ATR + df['atr_14'] = grouped.apply( + lambda x: pd.Series(talib.ATR(x['high'].values, x['low'].values, x['close'].values, timeperiod=14), + index=x.index) + ) + df['atr_6'] = grouped.apply( + lambda x: pd.Series(talib.ATR(x['high'].values, x['low'].values, x['close'].values, timeperiod=6), + index=x.index) + ) + + # 计算 OBV 及其均线 + df['obv'] = grouped.apply( + lambda x: pd.Series(talib.OBV(x['close'].values, x['vol'].values), index=x.index) + ) + print(df.columns) + df['maobv_6'] = grouped.apply( + lambda x: pd.Series(talib.SMA(x['obv'].values, timeperiod=6), index=x.index) + ) + + df['rsi_3'] = grouped.apply( + lambda x: pd.Series(talib.RSI(x['close'].values, timeperiod=3), index=x.index) + ) + # df['rsi_6'] = grouped.apply( + # lambda x: pd.Series(talib.RSI(x['close'].values, timeperiod=6), index=x.index) + # ) + # df['rsi_9'] = grouped.apply( + # lambda x: pd.Series(talib.RSI(x['close'].values, timeperiod=9), index=x.index) + # ) + + # 计算 return_10 和 return_20 + df['return_5'] = grouped['close'].apply(lambda x: x / x.shift(5) - 1) + # df['return_10'] = grouped['close'].apply(lambda x: x / x.shift(10) - 1) + df['return_20'] = grouped['close'].apply(lambda x: x / x.shift(20) - 1) + + # df['avg_close_5'] = grouped['close'].apply(lambda x: x.rolling(window=5).mean() / x) + + # 计算标准差指标 + df['std_return_5'] = grouped['close'].apply(lambda x: x.pct_change().rolling(window=5).std()) + # df['std_return_15'] = grouped['close'].apply(lambda x: x.pct_change().rolling(window=15).std()) + # df['std_return_25'] = grouped['close'].apply(lambda x: x.pct_change().rolling(window=25).std()) + df['std_return_90'] = grouped['close'].apply(lambda x: x.pct_change().rolling(window=90).std()) + df['std_return_90_2'] = grouped['close'].apply(lambda x: x.shift(10).pct_change().rolling(window=90).std()) + + # 计算 EMA 指标 + df['_ema_5'] = grouped['close'].apply( + lambda x: pd.Series(talib.EMA(x.values, timeperiod=5), index=x.index) + ) + df['_ema_13'] = grouped['close'].apply( + lambda x: pd.Series(talib.EMA(x.values, timeperiod=13), index=x.index) + ) + df['_ema_20'] = grouped['close'].apply( + lambda x: pd.Series(talib.EMA(x.values, timeperiod=20), index=x.index) + ) + df['_ema_60'] = grouped['close'].apply( + lambda x: pd.Series(talib.EMA(x.values, timeperiod=60), index=x.index) + ) + + # 计算 act_factor1, act_factor2, act_factor3, act_factor4 + df['act_factor1'] = grouped['_ema_5'].apply( + lambda x: np.arctan((x / x.shift(1) - 1) * 100) * 57.3 / 50 + ) + df['act_factor2'] = grouped['_ema_13'].apply( + lambda x: np.arctan((x / x.shift(1) - 1) * 100) * 57.3 / 40 + ) + df['act_factor3'] = grouped['_ema_20'].apply( + lambda x: np.arctan((x / x.shift(1) - 1) * 100) * 57.3 / 21 + ) + df['act_factor4'] = grouped['_ema_60'].apply( + lambda x: np.arctan((x / x.shift(1) - 1) * 100) * 57.3 / 10 + ) + + # 根据 trade_date 截面计算排名 + df['rank_act_factor1'] = df.groupby('trade_date', group_keys=False)['act_factor1'].rank(ascending=False, pct=True) + df['rank_act_factor2'] = df.groupby('trade_date', group_keys=False)['act_factor2'].rank(ascending=False, pct=True) + df['rank_act_factor3'] = df.groupby('trade_date', group_keys=False)['act_factor3'].rank(ascending=False, pct=True) + + df['log_circ_mv'] = np.log(df['circ_mv']) + + window_high_volume = 5 + window_close_stddev = 20 + period_delta = 5 + + # 计算每只股票的滚动协方差 + def calculate_rolling_cov(group): + return group['high'].rolling(window_high_volume).cov(group['vol']) + + df['cov'] = grouped.apply(calculate_rolling_cov) + + # 计算每只股票的协方差差分 + def calculate_delta_cov(group): + return group['cov'].diff(period_delta) + + df['delta_cov'] = grouped.apply(calculate_delta_cov) + + # 计算每只股票的滚动标准差 + def calculate_stddev_close(group): + return group['close'].rolling(window_close_stddev).std() + + df['_stddev_close'] = grouped.apply(calculate_stddev_close) + df['_rank_stddev'] = df.groupby('trade_date')['_stddev_close'].rank(pct=True) + df['alpha_22_improved'] = -1 * df['delta_cov'] * df['_rank_stddev'] + + df['alpha_003'] = np.where(df['high'] != df['low'], + (df['close'] - df['open']) / (df['high'] - df['low']), + 0) + + df['alpha_007'] = grouped.apply(lambda x: x['close'].rolling(5).corr(x['vol'])) + df['alpha_007'] = df.groupby('trade_date', group_keys=False)['alpha_007'].rank(ascending=True, pct=True) + + df['alpha_013'] = grouped['close'].transform(lambda x: x.rolling(5).sum() - x.rolling(20).sum()) + df['alpha_013'] = df.groupby('trade_date', group_keys=False)['alpha_013'].rank(ascending=True, pct=True) + + df['cat_up_limit'] = (df['close'] == df['up_limit']) # 是否涨停(1表示涨停,0表示未涨停) + df['cat_down_limit'] = (df['close'] == df['down_limit']) # 是否跌停(1表示跌停,0表示未跌停) + df['up_limit_count_10d'] = grouped['cat_up_limit'].rolling(window=10, min_periods=1).sum().reset_index(level=0, + drop=True) + df['down_limit_count_10d'] = grouped['cat_down_limit'].rolling(window=10, min_periods=1).sum().reset_index(level=0, + drop=True) + + # 3. 最近连续涨跌停天数 + def calculate_consecutive_limits(series): + """ + 计算连续涨停/跌停天数。 + """ + consecutive_up = series * (series.groupby((series != series.shift()).cumsum()).cumcount() + 1) + consecutive_down = series * (series.groupby((series != series.shift()).cumsum()).cumcount() + 1) + return consecutive_up, consecutive_down + + # 连续涨停天数 + df['consecutive_up_limit'] = grouped['cat_up_limit'].apply( + lambda x: calculate_consecutive_limits(x)[0] + ) + + df['vol_break'] = np.where((df['close'] > df['cost_85pct']) & (df['volume_ratio'] > 2), 1, 0) + + df['weight_roc5'] = grouped['weight_avg'].apply(lambda x: x.pct_change(5)) + + def rolling_corr(group): + roc_close = group['close'].pct_change() + roc_weight = group['weight_avg'].pct_change() + return roc_close.rolling(10).corr(roc_weight) + + df['price_cost_divergence'] = grouped.apply(rolling_corr) + + df['smallcap_concentration'] = (1 / df['log_circ_mv']) * (df['cost_85pct'] - df['cost_15pct']) + + # 16. 筹码稳定性指数 (20日波动率) + df['weight_std20'] = grouped['weight_avg'].apply(lambda x: x.rolling(20).std()) + df['cost_stability'] = df['weight_std20'] / grouped['weight_avg'].transform(lambda x: x.rolling(20).mean()) + + # 17. 成本区间突破标记 + df['high_cost_break_days'] = grouped.apply(lambda g: g['close'].gt(g['cost_95pct']).rolling(5).sum()) + + # 20. 筹码-流动性风险 + df['liquidity_risk'] = (df['cost_95pct'] - df['cost_5pct']) * ( + 1 / grouped['vol'].transform(lambda x: x.rolling(10).mean())) + + # 7. 市值波动率因子 (使用 grouped) + df['turnover_std'] = grouped['turnover_rate'].transform(lambda x: x.rolling(window=20).std()) + df['mv_volatility'] = grouped.apply(lambda x: x['turnover_std'] / x['log_circ_mv']) + + # 8. 市值成长性因子 + df['volume_growth'] = grouped['vol'].pct_change(periods=20) + df['mv_growth'] = df['volume_growth'] / df['log_circ_mv'] + + # AR 指标 + df["ar"] = grouped.apply( + lambda x: (x["high"].div(x["open"]).rolling(3).sum()) / (x["open"].div(x["low"]).rolling(3).sum()) * 100) + + # BR 指标 + df["pre_close"] = grouped["close"].shift(1) + df["br_up"] = (df["high"] - df["pre_close"]).clip(lower=0) + df["br_down"] = (df["pre_close"] - df["low"]).clip(lower=0) + df["br"] = grouped.apply(lambda x: (x["br_up"].rolling(3).sum()) / (x["br_down"].rolling(3).sum()) * 100) + + # ARBR + df['arbr'] = df['ar'] - df['br'] + df.drop(columns=["pre_close", "br_up", "br_down", 'ar', 'br'], inplace=True) + + df.drop(columns=['weight_std20'], inplace=True, errors='ignore') + df.drop( + columns=['_is_positive', '_is_negative', '_pos_returns', '_neg_returns', '_pos_returns_sq', '_neg_returns_sq'], + inplace=True, errors='ignore') + new_columns = [col for col in df.columns.tolist()[:] if col not in old_columns] + + return df, new_columns + + +def get_simple_factor(df): + old_columns = df.columns.tolist()[:] + df = df.sort_values(by=['ts_code', 'trade_date']) + + alpha = 0.5 + df['momentum_factor'] = df['volume_change_rate'] + alpha * df['turnover_deviation'] + df['resonance_factor'] = df['volume_ratio'] * df['pct_chg'] + df['log_close'] = np.log(df['close']) + + df['cat_vol_spike'] = df['vol'] > 2 * df['vol_spike'] + + df['up'] = (df['high'] - df[['close', 'open']].max(axis=1)) / df['close'] + df['down'] = (df[['close', 'open']].min(axis=1) - df['low']) / df['close'] + + df['obv_maobv_6'] = df['obv'] - df['maobv_6'] + + # 计算比值指标 + df['std_return_5_over_std_return_90'] = df['std_return_5'] / df['std_return_90'] + # df['std_return_5 / std_return_25'] = df['std_return_5'] / df['std_return_25'] + + # 计算标准差差值 + df['std_return_90_minus_std_return_90_2'] = df['std_return_90'] - df['std_return_90_2'] + + # df['cat_af1'] = df['act_factor1'] > 0 + df['cat_af2'] = df['act_factor2'] > df['act_factor1'] + df['cat_af3'] = df['act_factor3'] > df['act_factor2'] + df['cat_af4'] = df['act_factor4'] > df['act_factor3'] + + # 计算 act_factor5 和 act_factor6 + df['act_factor5'] = df['act_factor1'] + df['act_factor2'] + df['act_factor3'] + df['act_factor4'] + df['act_factor6'] = (df['act_factor1'] - df['act_factor2']) / np.sqrt( + df['act_factor1'] ** 2 + df['act_factor2'] ** 2) + + df['active_buy_volume_large'] = df['buy_lg_vol'] / df['net_mf_vol'] + df['active_buy_volume_big'] = df['buy_elg_vol'] / df['net_mf_vol'] + df['active_buy_volume_small'] = df['buy_sm_vol'] / df['net_mf_vol'] + + df['buy_lg_vol_minus_sell_lg_vol'] = (df['buy_lg_vol'] - df['sell_lg_vol']) / df['net_mf_vol'] + df['buy_elg_vol_minus_sell_elg_vol'] = (df['buy_elg_vol'] - df['sell_elg_vol']) / df['net_mf_vol'] + + df['log_circ_mv'] = np.log(df['circ_mv']) + + df['ctrl_strength'] = (df['cost_85pct'] - df['cost_15pct']) / (df['his_high'] - df['his_low']) + + df['low_cost_dev'] = (df['close'] - df['cost_5pct']) / (df['cost_50pct'] - df['cost_5pct']) + + df['asymmetry'] = (df['cost_95pct'] - df['cost_50pct']) / (df['cost_50pct'] - df['cost_5pct']) + + df['lock_factor'] = df['turnover_rate'] * ( + 1 - (df['cost_95pct'] - df['cost_5pct']) / (df['his_high'] - df['his_low'])) + + df['cat_vol_break'] = (df['close'] > df['cost_85pct']) & (df['volume_ratio'] > 2) + + df['cost_atr_adj'] = (df['cost_95pct'] - df['cost_5pct']) / df['atr_14'] + + # 12. 小盘股筹码集中度 + df['smallcap_concentration'] = (1 / df['log_circ_mv']) * (df['cost_85pct'] - df['cost_15pct']) + + df['cat_golden_resonance'] = ((df['close'] > df['weight_avg']) & + (df['volume_ratio'] > 1.5) & + (df['winner_rate'] > 0.7)) + + df['mv_turnover_ratio'] = df['turnover_rate'] / df['log_circ_mv'] + + df['mv_adjusted_volume'] = df['vol'] / df['log_circ_mv'] + + df['mv_weighted_turnover'] = df['turnover_rate'] * (1 / df['log_circ_mv']) + + df['nonlinear_mv_volume'] = df['vol'] / df['log_circ_mv'] + + df['mv_volume_ratio'] = df['volume_ratio'] / df['log_circ_mv'] + + df['mv_momentum'] = df['turnover_rate'] * df['volume_ratio'] / df['log_circ_mv'] + + drop_columns = [col for col in df.columns if col.startswith('_')] + df.drop(columns=drop_columns, inplace=True, errors='ignore') + + new_columns = [col for col in df.columns.tolist()[:] if col not in old_columns] + return df, new_columns + + +import pandas as pd +import numpy as np +from scipy.stats import linregress # For factor 4 (if implementing slope directly) +# from hurst import compute_Hc # For factor 18, needs pip install hurst +# import statsmodels.api as sm # For factor 16, needs pip install statsmodels + +# --- Constants --- +epsilon = 1e-10 # Prevent division by zero + +# --- Helper Functions --- +def _safe_divide(a, b, default_val=0): + """Safe division, returns default_val for division by zero or NaN/inf results.""" + with np.errstate(divide='ignore', invalid='ignore'): + result = a / b + # Replace NaN, Inf, -Inf resulting from division or invalid ops + result[~np.isfinite(result)] = default_val + return result + +# --- Factor Calculation Functions (In-Place Modification) --- + +# Category 1: Large Player Intent & Behavior +def lg_flow_mom_corr(df: pd.DataFrame, N: int = 20, M: int = 60, factor_name: str = None): + """ + Calculates Factor 1: Large Flow & Price Momentum Concordance (In-place). + WARNING: Modifies df in-place. + """ + if factor_name is None: + factor_name = f'lg_flow_mom_corr_{N}_{M}' + print(f"Calculating {factor_name}...") + _temp_cols = ['_net_lg_flow_val', '_rolling_net_lg_flow', '_price_mom'] + try: + df['_net_lg_flow_val'] = (df['buy_lg_vol'] + df['buy_elg_vol'] - df['sell_lg_vol'] - df['sell_elg_vol']) * df['close'] + df['_rolling_net_lg_flow'] = df.groupby('ts_code')['_net_lg_flow_val'].rolling(N, min_periods=max(1, N // 2)).sum().reset_index(level=0, drop=True) + df['_price_mom'] = df.groupby('ts_code')['close'].pct_change(N) + # Calculate correlation on the temporary Series to handle alignment + factor_series = df['_rolling_net_lg_flow'].rolling(M, min_periods=max(1, M // 2)).corr(df['_price_mom']) + df[factor_name] = factor_series + except Exception as e: + print(f"Error calculating {factor_name}: {e}") + df[factor_name] = np.nan # Assign NaN on error + finally: + # Cleanup intermediate columns + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def lg_buy_consolidation(df: pd.DataFrame, N: int = 20, vol_quantile: float = 0.2, factor_name: str = None): + """ + Calculates Factor 2: Large Buying during Consolidation (In-place). + WARNING: Modifies df in-place. + """ + if factor_name is None: + factor_name = f'lg_buy_consolidation_{N}' + print(f"Calculating {factor_name}...") + _temp_cols = ['_rolling_std', '_net_lg_flow_ratio', '_rolling_net_lg_flow_ratio_mean', '_std_threshold'] + try: + df['_rolling_std'] = df.groupby('ts_code')['close'].rolling(N, min_periods=max(1, N // 2)).std().reset_index(level=0, drop=True) + df['_net_lg_flow_ratio'] = _safe_divide( + (df['buy_lg_vol'] + df['buy_elg_vol'] - df['sell_lg_vol'] - df['sell_elg_vol']), + df['vol'] + ) + df['_rolling_net_lg_flow_ratio_mean'] = df.groupby('ts_code')['_net_lg_flow_ratio'].rolling(N, min_periods=max(1, N // 2)).mean().reset_index(level=0, drop=True) + df['_std_threshold'] = df.groupby('trade_date')['_rolling_std'].transform(lambda x: x.quantile(vol_quantile)) + df[factor_name] = df['_rolling_net_lg_flow_ratio_mean'].where(df['_rolling_std'] < df['_std_threshold']) + except Exception as e: + print(f"Error calculating {factor_name}: {e}") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def lg_flow_accel(df: pd.DataFrame, factor_name: str = 'lg_flow_accel'): + """ + Calculates Factor 3: Large Flow Acceleration (In-place). + WARNING: Modifies df in-place. + """ + print(f"Calculating {factor_name}...") + _temp_cols = ['_net_lg_flow_vol'] + try: + df['_net_lg_flow_vol'] = df['buy_lg_vol'] + df['buy_elg_vol'] - df['sell_lg_vol'] - df['sell_elg_vol'] + df[factor_name] = df.groupby('ts_code')['_net_lg_flow_vol'].diff(1).diff(1) + except Exception as e: + print(f"Error calculating {factor_name}: {e}") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def intraday_lg_flow_corr(df: pd.DataFrame, N: int = 20, factor_name: str = None): + """ + Calculates Factor 4: (Approx) Intraday Trend & Large Flow Correlation (In-place). + NOTE: Direct rolling correlation between two rolling series is complex/slow in pandas. + This provides a placeholder or requires significant optimization/pre-calculation. + WARNING: Modifies df in-place. Placeholder implementation returns NaN. + """ + if factor_name is None: + factor_name = f'intraday_lg_flow_corr_{N}' + print(f"Calculating {factor_name} (Placeholder - complex implementation)...") + df[factor_name] = np.nan # Placeholder, see previous thought process for detailed logic needed + print(f"Finished {factor_name} (Placeholder).") + + +# Category 2: Cost Basis & PnL Status +def profit_pressure(df: pd.DataFrame, factor_name: str = 'profit_pressure'): + """ + Calculates Factor 5: Profit Pressure Index (In-place). + WARNING: Modifies df in-place. + """ + print(f"Calculating {factor_name}...") + _temp_cols = ['_profit_margin_85', '_profit_margin_95'] + try: + df['_profit_margin_85'] = _safe_divide(df['close'], df['cost_85pct']) - 1 + df['_profit_margin_95'] = _safe_divide(df['close'], df['cost_95pct']) - 1 + df[factor_name] = df['winner_rate'] * 0.5 * (df['_profit_margin_85'] + df['_profit_margin_95']) + except Exception as e: + print(f"Error calculating {factor_name}: {e}") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def underwater_resistance(df: pd.DataFrame, factor_name: str = 'underwater_resistance'): + """ + Calculates Factor 6: Resistance from Underwater Positions (In-place). + WARNING: Modifies df in-place. + """ + print(f"Calculating {factor_name}...") + _temp_cols = ['_underwater_ratio', '_dist_to_cost_15'] + try: + df['_underwater_ratio'] = 1.0 - df['winner_rate'] + df['_dist_to_cost_15'] = np.maximum(0, df['cost_15pct'] - df['close']) / (df['close'] + epsilon) + df[factor_name] = df['_underwater_ratio'] * df['_dist_to_cost_15'] + except Exception as e: + print(f"Error calculating {factor_name}: {e}") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def cost_conc_std(df: pd.DataFrame, N: int = 20, factor_name: str = None): + """ + Calculates Factor 7: Cost Concentration Change (Std Dev) (In-place). + WARNING: Modifies df in-place. + """ + if factor_name is None: + factor_name = f'cost_conc_std_{N}' + print(f"Calculating {factor_name}...") + _temp_cols = ['_cost_range_norm'] + try: + df['_cost_range_norm'] = _safe_divide( + (df['cost_85pct'] - df['cost_15pct']), + (df['weight_avg'] + epsilon) + ) + # Need to calculate rolling std on the temp col before dropping it + factor_series = df.groupby('ts_code')['_cost_range_norm'].rolling(N, min_periods=max(1, N//2)).std().reset_index(level=0, drop=True) + df[factor_name] = factor_series + except Exception as e: + print(f"Error calculating {factor_name}: {e}") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def profit_decay(df: pd.DataFrame, N: int = 20, factor_name: str = None): + """ + Calculates Factor 8: Profit Expectation Decay (In-place). + WARNING: Modifies df in-place. + """ + if factor_name is None: + factor_name = f'profit_decay_{N}' + print(f"Calculating {factor_name}...") + _temp_cols = ['_ret_N', '_winner_rate_change_N'] + try: + df['_ret_N'] = _safe_divide(df['close'], df.groupby('ts_code')['close'].shift(N)) - 1 + df['_winner_rate_change_N'] = df.groupby('ts_code')['winner_rate'].diff(N) + df[factor_name] = _safe_divide(df['_ret_N'], df['_winner_rate_change_N']) + except Exception as e: + print(f"Error calculating {factor_name}: {e}") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + + +# Category 3: Volatility Source & Market State +def vol_amp_loss(df: pd.DataFrame, N: int = 20, factor_name: str = None): + """ + Calculates Factor 9: Volatility Amplification when Underwater (In-place). + WARNING: Modifies df in-place. + """ + if factor_name is None: + factor_name = f'vol_amp_loss_{N}' + print(f"Calculating {factor_name}...") + _temp_cols = ['_vol_N', '_loss_degree'] + try: + df['_vol_N'] = df.groupby('ts_code')['pct_chg'].rolling(N, min_periods=max(1, N // 2)).std().reset_index(level=0, drop=True) + df['_loss_degree'] = np.maximum(0, df['weight_avg'] - df['close']) / (df['close'] + epsilon) + df[factor_name] = df['_vol_N'] * df['_loss_degree'] + except Exception as e: + print(f"Error calculating {factor_name}: {e}") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def vol_drop_profit_cnt(df: pd.DataFrame, N: int = 20, M: int = 5, profit_thresh: float = 0.1, drop_thresh: float = -0.03, vol_multiple: float = 2.0, factor_name: str = None): + """ + Calculates Factor 10: High Volume Drop when Profitable (Count over M days) (In-place). + WARNING: Modifies df in-place. + """ + if factor_name is None: + factor_name = f'vol_drop_profit_cnt_{M}' + print(f"Calculating {factor_name}...") + _temp_cols = ['_is_profitable', '_is_dropping', '_rolling_mean_vol', '_rolling_std_vol', '_is_high_vol', '_event'] + try: + df['_is_profitable'] = df['close'] > df['weight_avg'] * (1 + profit_thresh) + df['_is_dropping'] = df['pct_chg'] < drop_thresh + df['_rolling_mean_vol'] = df.groupby('ts_code')['vol'].rolling(N, min_periods=1).mean().reset_index(level=0, drop=True) + df['_rolling_std_vol'] = df.groupby('ts_code')['vol'].rolling(N, min_periods=2).std().reset_index(level=0, drop=True).fillna(0) + df['_is_high_vol'] = df['vol'] > (df['_rolling_mean_vol'] + vol_multiple * df['_rolling_std_vol']) + df['_event'] = (df['_is_profitable'] & df['_is_dropping'] & df['_is_high_vol']).astype(int) + factor_series = df.groupby('ts_code')['_event'].rolling(M, min_periods=1).sum().reset_index(level=0, drop=True) + df[factor_name] = factor_series + except Exception as e: + print(f"Error calculating {factor_name}: {e}") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def lg_flow_vol_interact(df: pd.DataFrame, N: int = 20, factor_name: str = None): + """ + Calculates Factor 11: Large Flow Driven Volatility (Interaction Term) (In-place). + WARNING: Modifies df in-place. + """ + if factor_name is None: + factor_name = f'lg_flow_vol_interact_{N}' + print(f"Calculating {factor_name}...") + _temp_cols = ['_vol_N', '_net_lg_flow_val', '_total_val', '_abs_net_lg_flow_ratio', '_abs_net_lg_flow_ratio_N'] + try: + df['_vol_N'] = df.groupby('ts_code')['pct_chg'].rolling(N, min_periods=max(1, N // 2)).std().reset_index(level=0, drop=True) + df['_net_lg_flow_val'] = (df['buy_lg_vol'] + df['buy_elg_vol'] - df['sell_lg_vol'] - df['sell_elg_vol']) * df['close'] + df['_total_val'] = df['vol'] * df['close'] + df['_abs_net_lg_flow_ratio'] = abs(df['_net_lg_flow_val']) / (df['_total_val'] + epsilon) + df['_abs_net_lg_flow_ratio_N'] = df.groupby('ts_code')['_abs_net_lg_flow_ratio'].rolling(N, min_periods=max(1, N // 2)).mean().reset_index(level=0, drop=True) + df[factor_name] = df['_vol_N'] * df['_abs_net_lg_flow_ratio_N'] + except Exception as e: + print(f"Error calculating {factor_name}: {e}") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def cost_break_confirm_cnt(df: pd.DataFrame, M: int = 5, factor_name: str = None): + """ + Calculates Factor 12: Cost Breakout Confirmation Count (In-place). + WARNING: Modifies df in-place. + """ + if factor_name is None: + factor_name = f'cost_break_confirm_cnt_{M}' + print(f"Calculating {factor_name}...") + _temp_cols = ['_prev_cost_85', '_prev_cost_15', '_break_up', '_break_down', '_net_lg_flow_vol', '_confirm_up', '_confirm_down', '_net_confirm'] + try: + df['_prev_cost_85'] = df.groupby('ts_code')['cost_85pct'].shift(1) + df['_prev_cost_15'] = df.groupby('ts_code')['cost_15pct'].shift(1) + df['_break_up'] = df['close'] > df['_prev_cost_85'] + df['_break_down'] = df['close'] < df['_prev_cost_15'] + df['_net_lg_flow_vol'] = df['buy_lg_vol'] + df['buy_elg_vol'] - df['sell_lg_vol'] - df['sell_elg_vol'] + df['_confirm_up'] = (df['_break_up'] & (df['_net_lg_flow_vol'] > 0)).astype(int) + df['_confirm_down'] = (df['_break_down'] & (df['_net_lg_flow_vol'] < 0)).astype(int) + df['_net_confirm'] = df['_confirm_up'] - df['_confirm_down'] + factor_series = df.groupby('ts_code')['_net_confirm'].rolling(M, min_periods=1).sum().reset_index(level=0, drop=True) + df[factor_name] = factor_series + except Exception as e: + print(f"Error calculating {factor_name}: {e}") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + + +# Category 4: Technical Indicators & Market Behavior +def atr_norm_channel_pos(df: pd.DataFrame, N: int = 14, factor_name: str = None): + """ + Calculates Factor 13: ATR Normalized Channel Position (In-place). + WARNING: Modifies df in-place. + """ + if factor_name is None: + factor_name = f'atr_norm_channel_pos_{N}' + print(f"Calculating {factor_name}...") + _temp_cols = ['_prev_close', '_h_l', '_h_pc', '_l_pc', '_tr', '_atr_N', '_roll_low_N'] + try: + df['_prev_close'] = df.groupby('ts_code')['close'].shift(1) + df['_h_l'] = df['high'] - df['low'] + df['_h_pc'] = abs(df['high'] - df['_prev_close']) + df['_l_pc'] = abs(df['low'] - df['_prev_close']) + df['_tr'] = df[['_h_l', '_h_pc', '_l_pc']].max(axis=1) + df['_atr_N'] = df.groupby('ts_code')['_tr'].rolling(N, min_periods=max(1, N//2)).mean().reset_index(level=0, drop=True) + df['_roll_low_N'] = df.groupby('ts_code')['low'].rolling(N, min_periods=max(1, N//2)).min().reset_index(level=0, drop=True) + df[factor_name] = _safe_divide((df['close'] - df['_roll_low_N']), df['_atr_N']) + except Exception as e: + print(f"Error calculating {factor_name}: {e}") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def turnover_diff_skew(df: pd.DataFrame, N: int = 20, factor_name: str = None): + """ + Calculates Factor 14: Skewness of Turnover Rate Change (In-place). + WARNING: Modifies df in-place. + """ + if factor_name is None: + factor_name = f'turnover_diff_skew_{N}' + print(f"Calculating {factor_name}...") + _temp_cols = ['_turnover_diff'] + try: + # Assuming turnover_rate is in percentage points, diff is fine + df['_turnover_diff'] = df.groupby('ts_code')['turnover_rate'].diff(1) + factor_series = df.groupby('ts_code')['_turnover_diff'].rolling(N, min_periods=max(3, N//2)).skew().reset_index(level=0, drop=True) + df[factor_name] = factor_series + except Exception as e: + print(f"Error calculating {factor_name}: {e}") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def lg_sm_flow_diverge(df: pd.DataFrame, N: int = 20, factor_name: str = None): + """ + Calculates Factor 15: Divergence between Large and Small Flow (In-place). + WARNING: Modifies df in-place. + """ + if factor_name is None: + factor_name = f'lg_sm_flow_diverge_{N}' + print(f"Calculating {factor_name}...") + _temp_cols = ['_lg_flow_ratio', '_sm_flow_ratio', '_lg_flow_ratio_N', '_sm_flow_ratio_N'] + try: + df['_lg_flow_ratio'] = _safe_divide( + (df['buy_lg_vol'] + df['buy_elg_vol'] - df['sell_lg_vol'] - df['sell_elg_vol']), + df['vol'] + ) + df['_sm_flow_ratio'] = _safe_divide( + (df['buy_sm_vol'] - df['sell_sm_vol']), + df['vol'] + ) + df['_lg_flow_ratio_N'] = df.groupby('ts_code')['_lg_flow_ratio'].rolling(N, min_periods=max(1, N // 2)).mean().reset_index(level=0, drop=True) + df['_sm_flow_ratio_N'] = df.groupby('ts_code')['_sm_flow_ratio'].rolling(N, min_periods=max(1, N // 2)).mean().reset_index(level=0, drop=True) + df[factor_name] = df['_lg_flow_ratio_N'] - df['_sm_flow_ratio_N'] + except Exception as e: + print(f"Error calculating {factor_name}: {e}") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + + +def cap_neutral_cost_metric(df: pd.DataFrame, factor_name: str = 'cap_neutral_cost_metric'): + """ + Calculates Factor 16: Market Cap Neutralized Cost Metric (Placeholder). + Requires statsmodels and complex implementation. + WARNING: Modifies df in-place. Placeholder implementation returns NaN. + """ + print(f"Calculating {factor_name} (Placeholder - requires statsmodels)...") + df[factor_name] = np.nan + print(f"Finished {factor_name} (Placeholder).") + + +def pullback_strong(df: pd.DataFrame, N: int = 20, M: int = 20, gain_thresh: float = 0.2, factor_name: str = None): + """ + Calculates Factor 17: Pullback Depth from Recent High for Strong Stocks (In-place). + WARNING: Modifies df in-place. + """ + if factor_name is None: + factor_name = f'pullback_strong_{N}_{M}' + print(f"Calculating {factor_name}...") + _temp_cols = ['_high_N', '_pullback_depth', '_recent_gain_M'] + try: + df['_high_N'] = df.groupby('ts_code')['high'].rolling(N, min_periods=max(1, N // 2)).max().reset_index(level=0, drop=True) + df['_pullback_depth'] = _safe_divide((df['_high_N'] - df['close']), df['_high_N']) + df['_recent_gain_M'] = _safe_divide(df['close'], df.groupby('ts_code')['close'].shift(M)) - 1 + df[factor_name] = _safe_divide(df['_pullback_depth'], df['_recent_gain_M']) + except Exception as e: + print(f"Error calculating {factor_name}: {e}") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def hurst_exponent_flow(df: pd.DataFrame, N: int = 60, flow_col: str = 'net_mf_vol', factor_name: str = None): + """ + Calculates Factor 18: Hurst Exponent of Money Flow (Placeholder). + Requires 'hurst' library and potentially slow rolling apply. + WARNING: Modifies df in-place. Placeholder implementation returns NaN. + """ + if factor_name is None: + factor_name = f'hurst_{flow_col}_{N}' + print(f"Calculating {factor_name} (Placeholder - requires hurst library)...") + try: + from hurst import compute_Hc + # Logic would go here, likely using rolling().apply() which is slow + # factor_series = df.groupby('ts_code')[flow_col]....apply(hurst_calc_func...) + df[factor_name] = np.nan # Placeholder + except ImportError: + print("Error: 'hurst' library not installed. Cannot calculate factor.") + df[factor_name] = np.nan + except Exception as e: + print(f"Error calculating {factor_name}: {e}") + df[factor_name] = np.nan + print(f"Finished {factor_name} (Placeholder).") + + +def vol_wgt_hist_pos(df: pd.DataFrame, N: int = 20, factor_name: str = None): + """ + Calculates Factor 19: Volume Weighting at Historical Price Level (In-place). + WARNING: Modifies df in-place. + """ + if factor_name is None: + factor_name = f'vol_wgt_hist_pos_{N}' + print(f"Calculating {factor_name}...") + _temp_cols = ['_hist_pos', '_rolling_mean_vol', '_vol_rel_strength'] + try: + df['_hist_pos'] = _safe_divide((df['close'] - df['his_low']), (df['his_high'] - df['his_low'])).clip(0, 1) + df['_rolling_mean_vol'] = df.groupby('ts_code')['vol'].rolling(N, min_periods=max(1, N // 2)).mean().reset_index(level=0, drop=True) + df['_vol_rel_strength'] = _safe_divide(df['vol'], df['_rolling_mean_vol']) + df[factor_name] = df['_hist_pos'] * df['_vol_rel_strength'] + except Exception as e: + print(f"Error calculating {factor_name}: {e}") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def vol_adj_roc(df: pd.DataFrame, N: int = 20, factor_name: str = None): + """ + Calculates Factor 20: Volatility-Adjusted ROC (In-place). + WARNING: Modifies df in-place. + """ + if factor_name is None: + factor_name = f'vol_adj_roc_{N}' + print(f"Calculating {factor_name}...") + _temp_cols = ['_roc_N', '_vol_N'] + try: + df['_roc_N'] = _safe_divide(df['close'], df.groupby('ts_code')['close'].shift(N)) - 1 + df['_vol_N'] = df.groupby('ts_code')['pct_chg'].rolling(N, min_periods=max(2, N // 2)).std().reset_index(level=0, drop=True).fillna(0) + df[factor_name] = _safe_divide(df['_roc_N'], df['_vol_N']) + except Exception as e: + print(f"Error calculating {factor_name}: {e}") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def calculate_complex_factor(df: pd.DataFrame, factor_name: str = "complex_factor_deap_1"): + """ + 表达式: sub(protected_div_torch(A, B), C) + 其中 A, B, C 及内部组件依赖于多个预计算因子列。 + + Args: + df (pd.DataFrame): 包含所有必需基础因子列的 DataFrame。 + factor_name (str): 要在 df 中创建的新因子列的名称。 + + WARNING: 此函数会原地修改输入的 DataFrame 'df'。 + 如果在计算过程中缺少任何必需的列,将打印错误并填充 NaN。 + """ + print(f"开始计算因子: {factor_name} (原地修改)...") + _temp_cols_list = [] # 用于记录中间计算列的名称 + + try: + # --- 分解计算表达式的各个部分 --- + + # 计算组件 D + # D = sub(mul(pullback_strong_20_20, div(log_close, industry_return_5)), div(add(vol_adj_roc_20, vol_drop_profit_cnt_5), sub(nonlinear_mv_volume, alpha_007))) + _temp_d_term1_div = _safe_divide(df['log_close'], df['industry_return_5']) + _temp_d_term1 = df['pullback_strong_20_20'] * _temp_d_term1_div + _temp_d_term2_sub = df['nonlinear_mv_volume'] - df['alpha_007'] + _temp_d_term2_add = df['vol_adj_roc_20'] + df['vol_drop_profit_cnt_5'] + _temp_d_term2 = _safe_divide(_temp_d_term2_add, _temp_d_term2_sub) + df['_temp_D'] = _temp_d_term1 - _temp_d_term2 + _temp_cols_list.extend(['_temp_D', '_temp_d_term1_div', '_temp_d_term1', '_temp_d_term2_sub', '_temp_d_term2_add', '_temp_d_term2']) + + # 计算组件 A + # A = add(add(mul(D, lg_buy_consolidation_20), lg_buy_consolidation_20), pullback_strong_20_20) + _temp_a_term1 = df['_temp_D'] * df['lg_buy_consolidation_20'] + _temp_a_term2 = _temp_a_term1 + df['lg_buy_consolidation_20'] + df['_temp_A'] = _temp_a_term2 + df['pullback_strong_20_20'] + _temp_cols_list.extend(['_temp_A', '_temp_a_term1', '_temp_a_term2']) + + # 计算组件 F + # F = mul(add(net_mf_vol, std_return_5), sub(arbr, industry_act_factor5)) + _temp_f_term1 = df['net_mf_vol'] + df['std_return_5'] + _temp_f_term2 = df['arbr'] - df['industry_act_factor5'] + df['_temp_F'] = _temp_f_term1 * _temp_f_term2 + _temp_cols_list.extend(['_temp_F', '_temp_f_term1', '_temp_f_term2']) + + # 计算组件 H + # H = add(add(industry_act_factor1, low_cost_dev), mul(mv_weighted_turnover, act_factor4)) + _temp_h_term1 = df['industry_act_factor1'] + df['low_cost_dev'] + _temp_h_term2 = df['mv_weighted_turnover'] * df['act_factor4'] + df['_temp_H'] = _temp_h_term1 + _temp_h_term2 + _temp_cols_list.extend(['_temp_H', '_temp_h_term1', '_temp_h_term2']) + + # 计算组件 B + # B = div(add(add(F, vol), H), lg_elg_buy_prop) + _temp_b_term1 = df['_temp_F'] + df['vol'] + _temp_b_term2 = _temp_b_term1 + df['_temp_H'] + df['_temp_B'] = _safe_divide(_temp_b_term2, df['lg_elg_buy_prop']) + _temp_cols_list.extend(['_temp_B', '_temp_b_term1', '_temp_b_term2']) + + # 计算组件 C + # C = div(div(intraday_lg_flow_corr_20, lg_elg_buy_prop), lg_elg_buy_prop) + # 注意: intraday_lg_flow_corr_20 可能本身就是 NaN 或需要特殊处理 + _temp_c_term1 = _safe_divide(df.get('intraday_lg_flow_corr_20', np.nan), df['lg_elg_buy_prop']) # 使用 .get 处理可能不存在的列 + df['_temp_C'] = _safe_divide(_temp_c_term1, df['lg_elg_buy_prop']) + _temp_cols_list.extend(['_temp_C', '_temp_c_term1']) + + # --- 计算最终表达式 --- + # final = sub(div(A, B), C) + _temp_final_term1 = _safe_divide(df['_temp_A'], df['_temp_B']) + final_factor_series = _temp_final_term1 - df['_temp_C'] + + # --- 将最终结果赋值给 df 的新列 (原地修改) --- + df[factor_name] = final_factor_series + + print(f"因子 {factor_name} 计算成功。") + + except KeyError as e: + # 捕获因为缺少列而产生的错误 + print(f"错误: 计算 {factor_name} 时缺少必需的列: {e}") + print("请确保输入的 DataFrame 包含所有表达式中引用的因子列。") + print("将为因子 {factor_name} 填充 NaN。") + df[factor_name] = np.nan # 出错时填充 NaN + except Exception as e: + # 捕获其他可能的计算错误 + print(f"错误: 计算 {factor_name} 时发生意外错误: {e}") + print(f"将为因子 {factor_name} 填充 NaN。") + df[factor_name] = np.nan # 出错时填充 NaN + finally: + # --- 清理所有中间计算列 --- + cols_to_drop = [col for col in _temp_cols_list if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + # print(f"已清理 {len(cols_to_drop)} 个临时列 for {factor_name}.") + print(f"因子 {factor_name} 计算流程结束。") + # 函数不返回任何值,因为 df 是原地修改的 + +import pandas as pd +import numpy as np +# from scipy.stats import rankdata # rankdata is not needed if using pandas rank +# import statsmodels.api as sm # Needed for factor 19 + +# --- Constants --- +epsilon = 1e-10 # Prevent division by zero + +# --- Helper Functions --- +def _safe_divide(numerator, denominator, default_val=0): + """ + 安全的除法函数,处理分母为零或接近零,以及NaN/Inf的情况。 + + Args: + numerator (pd.Series): 分子. + denominator (pd.Series): 分母. + default_val (float): 当分母为零或结果无效时返回的默认值. + + Returns: + pd.Series: 除法结果. + """ + with np.errstate(divide='ignore', invalid='ignore'): + # Convert inputs to numeric, coercing errors to NaN before division + num = pd.to_numeric(numerator, errors='coerce') + den = pd.to_numeric(denominator, errors='coerce') + # Perform division where denominator is not close to zero and inputs are valid numbers + result = np.where(np.abs(den) > epsilon, num / den, default_val) + # Ensure result is float, handle potential NaNs from coercion or division + result = pd.to_numeric(result, errors='coerce') + # Fill remaining NaNs if necessary + result = np.nan_to_num(result, nan=default_val, posinf=default_val, neginf=default_val) + # Ensure result index matches numerator's index if numerator is a Series + if isinstance(numerator, pd.Series): + return pd.Series(result, index=numerator.index) + else: + return pd.Series(result) # Fallback if numerator is not a Series (less likely) + +# --- Cross-Sectional Factor Calculation Functions (In-Place Modification) --- + +# Category 1: Cross-Sectional Flow & Behavior Strength +def cs_rank_net_lg_flow_val(df: pd.DataFrame, factor_name: str = 'cs_rank_net_lg_flow_val'): + """ + Factor 1: 大单净额截面排序 (In-place). + WARNING: Modifies df in-place. + """ + print(f"Calculating {factor_name}...") + _temp_cols = ['_net_lg_flow_val'] + try: + df['_net_lg_flow_val'] = (df['buy_lg_vol'] + df['buy_elg_vol'] - df['sell_lg_vol'] - df['sell_elg_vol']) * df['close'] + df[factor_name] = df.groupby('trade_date')['_net_lg_flow_val'].rank(pct=True) + except KeyError as e: + print(f"Error calculating {factor_name}: Missing column {e}. Assigning NaN.") + df[factor_name] = np.nan + except Exception as e: + print(f"An unexpected error occurred calculating {factor_name}: {e}. Assigning NaN.") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def cs_rank_flow_divergence(df: pd.DataFrame, factor_name: str = 'cs_rank_flow_divergence'): + """ + Factor 2: 大小单流向背离度截面排序 (In-place). + WARNING: Modifies df in-place. + """ + print(f"Calculating {factor_name}...") + _temp_cols = ['_lg_ratio', '_sm_ratio', '_divergence'] + try: + df['_lg_ratio'] = _safe_divide( + (df['buy_lg_vol'] + df['buy_elg_vol'] - df['sell_lg_vol'] - df['sell_elg_vol']), + df['vol'] + ) + df['_sm_ratio'] = _safe_divide( + (df['buy_sm_vol'] - df['sell_sm_vol']), + df['vol'] + ) + df['_divergence'] = df['_lg_ratio'] - df['_sm_ratio'] + df[factor_name] = df.groupby('trade_date')['_divergence'].rank(pct=True) + except KeyError as e: + print(f"Error calculating {factor_name}: Missing column {e}. Assigning NaN.") + df[factor_name] = np.nan + except Exception as e: + print(f"An unexpected error occurred calculating {factor_name}: {e}. Assigning NaN.") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def cs_rank_industry_adj_lg_flow(df: pd.DataFrame, factor_name: str = 'cs_rank_ind_adj_lg_flow'): + """ + Factor 3: 行业内大单流强度排序 (In-place). Requires 'cat_l2_code'. + WARNING: Modifies df in-place. + """ + print(f"Calculating {factor_name}...") + _temp_cols = ['_net_lg_flow_vol', '_industry_avg_flow', '_deviation'] + if 'cat_l2_code' not in df.columns: + print(f"Error calculating {factor_name}: Missing 'cat_l2_code' column. Assigning NaN.") + df[factor_name] = np.nan + return + try: + df['_net_lg_flow_vol'] = (df['buy_lg_vol'] + df['buy_elg_vol'] - df['sell_lg_vol'] - df['sell_elg_vol']) * df['close'] # Or use vol + df['_industry_avg_flow'] = df.groupby(['trade_date', 'cat_l2_code'])['_net_lg_flow_vol'].transform('mean') + df['_deviation'] = df['_net_lg_flow_vol'] - df['_industry_avg_flow'] + df[factor_name] = df.groupby('trade_date')['_deviation'].rank(pct=True) + except KeyError as e: + print(f"Error calculating {factor_name}: Missing column {e}. Assigning NaN.") + df[factor_name] = np.nan + except Exception as e: + print(f"An unexpected error occurred calculating {factor_name}: {e}. Assigning NaN.") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def cs_rank_elg_buy_ratio(df: pd.DataFrame, factor_name: str = 'cs_rank_elg_buy_ratio'): + """ + Factor 4: 超大单买入占比排序 (In-place). + WARNING: Modifies df in-place. + """ + print(f"Calculating {factor_name}...") + _temp_cols = ['_elg_buy_ratio'] + try: + df['_elg_buy_ratio'] = _safe_divide(df['buy_elg_vol'], df['vol']) + df[factor_name] = df.groupby('trade_date')['_elg_buy_ratio'].rank(pct=True) + except KeyError as e: + print(f"Error calculating {factor_name}: Missing column {e}. Assigning NaN.") + df[factor_name] = np.nan + except Exception as e: + print(f"An unexpected error occurred calculating {factor_name}: {e}. Assigning NaN.") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +# Category 2: Cross-Sectional Cost Basis & PnL Status +def cs_rank_rel_profit_margin(df: pd.DataFrame, factor_name: str = 'cs_rank_rel_profit_margin'): + """ + Factor 5: 相对盈利幅度排序 (In-place). + WARNING: Modifies df in-place. + """ + print(f"Calculating {factor_name}...") + _temp_cols = ['_profit_margin'] + try: + df['_profit_margin'] = _safe_divide((df['close'] - df['weight_avg']), df['close']) + df[factor_name] = df.groupby('trade_date')['_profit_margin'].rank(pct=True) + except KeyError as e: + print(f"Error calculating {factor_name}: Missing column {e}. Assigning NaN.") + df[factor_name] = np.nan + except Exception as e: + print(f"An unexpected error occurred calculating {factor_name}: {e}. Assigning NaN.") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def cs_rank_cost_breadth(df: pd.DataFrame, factor_name: str = 'cs_rank_cost_breadth'): + """ + Factor 6: 成本分布宽度截面排序 (In-place). + WARNING: Modifies df in-place. + """ + print(f"Calculating {factor_name}...") + _temp_cols = ['_cost_breadth'] + try: + df['_cost_breadth'] = _safe_divide((df['cost_85pct'] - df['cost_15pct']), df['weight_avg']) + df[factor_name] = df.groupby('trade_date')['_cost_breadth'].rank(pct=True) + except KeyError as e: + print(f"Error calculating {factor_name}: Missing column {e}. Assigning NaN.") + df[factor_name] = np.nan + except Exception as e: + print(f"An unexpected error occurred calculating {factor_name}: {e}. Assigning NaN.") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def cs_rank_dist_to_upper_cost(df: pd.DataFrame, factor_name: str = 'cs_rank_dist_to_upper_cost'): + """ + Factor 7: 股价相对高成本位距离排序 (In-place). + WARNING: Modifies df in-place. + """ + print(f"Calculating {factor_name}...") + _temp_cols = ['_dist_to_95'] + try: + df['_dist_to_95'] = _safe_divide(df['close'], df['cost_95pct']) + df[factor_name] = df.groupby('trade_date')['_dist_to_95'].rank(pct=True) + except KeyError as e: + print(f"Error calculating {factor_name}: Missing column {e}. Assigning NaN.") + df[factor_name] = np.nan + except Exception as e: + print(f"An unexpected error occurred calculating {factor_name}: {e}. Assigning NaN.") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def cs_rank_winner_rate(df: pd.DataFrame, factor_name: str = 'cs_rank_winner_rate'): + """ + Factor 8: 获利盘比例截面排序 (In-place). + WARNING: Modifies df in-place. + """ + print(f"Calculating {factor_name}...") + try: + df[factor_name] = df.groupby('trade_date')['winner_rate'].rank(pct=True) + except KeyError as e: + print(f"Error calculating {factor_name}: Missing column {e}. Assigning NaN.") + df[factor_name] = np.nan + except Exception as e: + print(f"An unexpected error occurred calculating {factor_name}: {e}. Assigning NaN.") + df[factor_name] = np.nan + finally: + print(f"Finished {factor_name}.") + + +# Category 3: Cross-Sectional Price Action & Volatility +def cs_rank_intraday_range(df: pd.DataFrame, factor_name: str = 'cs_rank_intraday_range'): + """ + Factor 9: 日内相对振幅排序 (In-place). + WARNING: Modifies df in-place. + """ + print(f"Calculating {factor_name}...") + _temp_cols = ['_norm_range'] + try: + df['_norm_range'] = _safe_divide((df['high'] - df['low']), df['close']) + df[factor_name] = df.groupby('trade_date')['_norm_range'].rank(pct=True) + except KeyError as e: + print(f"Error calculating {factor_name}: Missing column {e}. Assigning NaN.") + df[factor_name] = np.nan + except Exception as e: + print(f"An unexpected error occurred calculating {factor_name}: {e}. Assigning NaN.") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def cs_rank_close_pos_in_range(df: pd.DataFrame, factor_name: str = 'cs_rank_close_pos_in_range'): + """ + Factor 10: 收盘价在日内位置排序 (In-place). + WARNING: Modifies df in-place. + """ + print(f"Calculating {factor_name}...") + _temp_cols = ['_close_pos'] + try: + df['_close_pos'] = _safe_divide((df['close'] - df['low']), (df['high'] - df['low']), default_val=0.5) # Assign 0.5 if high==low + df[factor_name] = df.groupby('trade_date')['_close_pos'].rank(pct=True) + except KeyError as e: + print(f"Error calculating {factor_name}: Missing column {e}. Assigning NaN.") + df[factor_name] = np.nan + except Exception as e: + print(f"An unexpected error occurred calculating {factor_name}: {e}. Assigning NaN.") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def cs_rank_opening_gap(df: pd.DataFrame, factor_name: str = 'cs_rank_opening_gap'): + """ + Factor 11: 开盘相对跳空幅度排序 (In-place). Needs pre_close. + WARNING: Modifies df in-place. Assumes 'pre_close' exists. + """ + print(f"Calculating {factor_name}...") + _temp_cols = ['_gap'] + if 'pre_close' not in df.columns: + print(f"Error calculating {factor_name}: Missing 'pre_close' column. Assigning NaN.") + df[factor_name] = np.nan + return + try: + df['_gap'] = _safe_divide(df['open'], df['pre_close']) - 1 + df[factor_name] = df.groupby('trade_date')['_gap'].rank(pct=True) + except KeyError as e: + print(f"Error calculating {factor_name}: Missing column {e} (likely 'open'). Assigning NaN.") + df[factor_name] = np.nan + except Exception as e: + print(f"An unexpected error occurred calculating {factor_name}: {e}. Assigning NaN.") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def cs_rank_pos_in_hist_range(df: pd.DataFrame, factor_name: str = 'cs_rank_pos_in_hist_range'): + """ + Factor 12: 相对历史波动位置排序 (In-place). + WARNING: Modifies df in-place. + """ + print(f"Calculating {factor_name}...") + _temp_cols = ['_hist_pos'] + try: + df['_hist_pos'] = _safe_divide((df['close'] - df['his_low']), (df['his_high'] - df['his_low'])).clip(0, 1) # Clip to 0-1 range + df[factor_name] = df.groupby('trade_date')['_hist_pos'].rank(pct=True) + except KeyError as e: + print(f"Error calculating {factor_name}: Missing column {e}. Assigning NaN.") + df[factor_name] = np.nan + except Exception as e: + print(f"An unexpected error occurred calculating {factor_name}: {e}. Assigning NaN.") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + + +# Category 4: Cross-Sectional Interaction & Composite Indicators +def cs_rank_vol_x_profit_margin(df: pd.DataFrame, factor_name: str = 'cs_rank_vol_x_profit_margin'): + """ + Factor 13: 波动率与盈亏状态交互排序 (In-place). + WARNING: Modifies df in-place. + """ + print(f"Calculating {factor_name}...") + _temp_cols = ['_daily_vol', '_profit_margin', '_interaction'] + try: + df['_daily_vol'] = abs(df['pct_chg']) + df['_profit_margin'] = _safe_divide((df['close'] - df['weight_avg']), df['close']) + df['_interaction'] = df['_daily_vol'] * df['_profit_margin'] + df[factor_name] = df.groupby('trade_date')['_interaction'].rank(pct=True) + except KeyError as e: + print(f"Error calculating {factor_name}: Missing column {e}. Assigning NaN.") + df[factor_name] = np.nan + except Exception as e: + print(f"An unexpected error occurred calculating {factor_name}: {e}. Assigning NaN.") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def cs_rank_lg_flow_price_concordance(df: pd.DataFrame, factor_name: str = 'cs_rank_lg_flow_price_concordance'): + """ + Factor 14: 大单流向与价格变动一致性排序 (In-place). + WARNING: Modifies df in-place. + """ + print(f"Calculating {factor_name}...") + _temp_cols = ['_net_lg_flow_vol', '_concordance'] + try: + df['_net_lg_flow_vol'] = df['buy_lg_vol'] + df['buy_elg_vol'] - df['sell_lg_vol'] - df['sell_elg_vol'] + df['_concordance'] = df['_net_lg_flow_vol'] * df['pct_chg'] + df[factor_name] = df.groupby('trade_date')['_concordance'].rank(pct=True) + except KeyError as e: + print(f"Error calculating {factor_name}: Missing column {e}. Assigning NaN.") + df[factor_name] = np.nan + except Exception as e: + print(f"An unexpected error occurred calculating {factor_name}: {e}. Assigning NaN.") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def cs_rank_turnover_per_winner(df: pd.DataFrame, factor_name: str = 'cs_rank_turnover_per_winner'): + """ + Factor 15: 高换手获利盘占比排序 (In-place). + WARNING: Modifies df in-place. + """ + print(f"Calculating {factor_name}...") + _temp_cols = ['_turnover_per_winner'] + try: + df['_turnover_per_winner'] = _safe_divide(df['turnover_rate'], df['winner_rate']) + df[factor_name] = df.groupby('trade_date')['_turnover_per_winner'].rank(pct=True) + except KeyError as e: + print(f"Error calculating {factor_name}: Missing column {e}. Assigning NaN.") + df[factor_name] = np.nan + except Exception as e: + print(f"An unexpected error occurred calculating {factor_name}: {e}. Assigning NaN.") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def cs_rank_ind_cap_neutral_pe(df: pd.DataFrame, factor_name: str = 'cs_rank_ind_cap_neutral_pe'): + """ + Factor 16: 行业市值中性化PE排序 (Placeholder). + Requires statsmodels and complex cross-sectional regression implementation. + WARNING: Modifies df in-place. Placeholder implementation returns NaN. + """ + print(f"Calculating {factor_name} (Placeholder - requires statsmodels)...") + df[factor_name] = np.nan + print(f"Finished {factor_name} (Placeholder).") + +def cs_rank_volume_ratio(df: pd.DataFrame, factor_name: str = 'cs_rank_volume_ratio'): + """ + Factor 17: 成交量相对强度排序 (In-place). + WARNING: Modifies df in-place. + """ + print(f"Calculating {factor_name}...") + try: + # Assumes 'volume_ratio' (量比) column already exists + df[factor_name] = df.groupby('trade_date')['volume_ratio'].rank(pct=True) + except KeyError as e: + print(f"Error calculating {factor_name}: Missing column {e}. Assigning NaN.") + df[factor_name] = np.nan + except Exception as e: + print(f"An unexpected error occurred calculating {factor_name}: {e}. Assigning NaN.") + df[factor_name] = np.nan + finally: + print(f"Finished {factor_name}.") + +def cs_rank_elg_buy_sell_sm_ratio(df: pd.DataFrame, factor_name: str = 'cs_rank_elg_buy_sell_sm_ratio'): + """ + Factor 18: 超大单买入与小单卖出比排序 (In-place). + WARNING: Modifies df in-place. + """ + print(f"Calculating {factor_name}...") + _temp_cols = ['_ratio'] + try: + df['_ratio'] = _safe_divide(df['buy_elg_vol'], df['sell_sm_vol']) + df[factor_name] = df.groupby('trade_date')['_ratio'].rank(pct=True) + except KeyError as e: + print(f"Error calculating {factor_name}: Missing column {e}. Assigning NaN.") + df[factor_name] = np.nan + except Exception as e: + print(f"An unexpected error occurred calculating {factor_name}: {e}. Assigning NaN.") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def cs_rank_cost_dist_vol_ratio(df: pd.DataFrame, factor_name: str = 'cs_rank_cost_dist_vol_ratio'): + """ + Factor 19: 价格偏离成本程度与成交量放大交互排序 (In-place). + WARNING: Modifies df in-place. + """ + print(f"Calculating {factor_name}...") + _temp_cols = ['_dist', '_interaction'] + if 'volume_ratio' not in df.columns: + print(f"Error calculating {factor_name}: Missing 'volume_ratio' column. Assigning NaN.") + df[factor_name] = np.nan + return + try: + df['_dist'] = abs(df['close'] - df['weight_avg']) / (df['close'] + epsilon) + df['_interaction'] = df['_dist'] * df['volume_ratio'] + df[factor_name] = df.groupby('trade_date')['_interaction'].rank(pct=True) + except KeyError as e: + print(f"Error calculating {factor_name}: Missing column {e}. Assigning NaN.") + df[factor_name] = np.nan + except Exception as e: + print(f"An unexpected error occurred calculating {factor_name}: {e}. Assigning NaN.") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + +def cs_rank_size(df: pd.DataFrame, factor_name: str = 'cs_rank_size'): + """ + Factor 20: 市值因子暴露度排序 (Log of circ_mv) (In-place). + WARNING: Modifies df in-place. + """ + print(f"Calculating {factor_name}...") + _temp_cols = ['_log_circ_mv'] + try: + # Use log1p for stability if circ_mv can be zero or very small + df['_log_circ_mv'] = np.log1p(df['circ_mv']) + df[factor_name] = df.groupby('trade_date')['_log_circ_mv'].rank(pct=True) + except KeyError as e: + print(f"Error calculating {factor_name}: Missing column {e}. Assigning NaN.") + df[factor_name] = np.nan + except Exception as e: + print(f"An unexpected error occurred calculating {factor_name}: {e}. Assigning NaN.") + df[factor_name] = np.nan + finally: + cols_to_drop = [col for col in _temp_cols if col in df.columns] + if cols_to_drop: + df.drop(columns=cols_to_drop, inplace=True) + print(f"Finished {factor_name}.") + diff --git a/main/factor/generate_factor.ipynb b/main/factor/generate_factor.ipynb new file mode 100644 index 0000000..8b942f0 --- /dev/null +++ b/main/factor/generate_factor.ipynb @@ -0,0 +1,1028 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "initial_id", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-13T07:26:19.000054Z", + "start_time": "2025-04-13T07:26:18.895713Z" + }, + "collapsed": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "e:\\PyProject\\NewStock\\main\\factor\n" + ] + } + ], + "source": [ + "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": 2, + "id": "f1623b04c7a366af", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-13T07:30:48.534271Z", + "start_time": "2025-04-13T07:26:19.005576Z" + } + }, + "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", + "\n", + "RangeIndex: 5123740 entries, 0 to 5123739\n", + "Data columns (total 31 columns):\n", + " # Column Dtype \n", + "--- ------ ----- \n", + " 0 ts_code object \n", + " 1 trade_date datetime64[ns]\n", + " 2 open float64 \n", + " 3 close float64 \n", + " 4 high float64 \n", + " 5 low float64 \n", + " 6 vol float64 \n", + " 7 pct_chg float64 \n", + " 8 turnover_rate float64 \n", + " 9 pe_ttm float64 \n", + " 10 circ_mv float64 \n", + " 11 volume_ratio float64 \n", + " 12 is_st bool \n", + " 13 up_limit float64 \n", + " 14 down_limit float64 \n", + " 15 buy_sm_vol float64 \n", + " 16 sell_sm_vol float64 \n", + " 17 buy_lg_vol float64 \n", + " 18 sell_lg_vol float64 \n", + " 19 buy_elg_vol float64 \n", + " 20 sell_elg_vol float64 \n", + " 21 net_mf_vol float64 \n", + " 22 his_low float64 \n", + " 23 his_high float64 \n", + " 24 cost_5pct float64 \n", + " 25 cost_15pct float64 \n", + " 26 cost_50pct float64 \n", + " 27 cost_85pct float64 \n", + " 28 cost_95pct float64 \n", + " 29 weight_avg float64 \n", + " 30 winner_rate float64 \n", + "dtypes: bool(1), datetime64[ns](1), float64(28), object(1)\n", + "memory usage: 1.2+ GB\n", + "None\n", + "['ts_code', 'trade_date', 'open', 'close', 'high', 'low', 'vol', 'pct_chg', 'turnover_rate', 'pe_ttm', 'circ_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']\n", + "Index(['ts_code', 'trade_date', 'open', 'close', 'high', 'low', 'vol',\n", + " 'pct_chg', 'turnover_rate', 'pe_ttm', 'circ_mv', 'volume_ratio',\n", + " 'is_st', 'up_limit', 'down_limit', 'buy_sm_vol', 'sell_sm_vol',\n", + " 'buy_lg_vol', 'sell_lg_vol', 'buy_elg_vol', 'sell_elg_vol',\n", + " 'net_mf_vol', 'his_low', 'his_high', 'cost_5pct', 'cost_15pct',\n", + " 'cost_50pct', 'cost_85pct', 'cost_95pct', 'weight_avg', 'winner_rate',\n", + " 'lg_elg_net_buy_vol', 'flow_lg_elg_intensity', 'sm_net_buy_vol',\n", + " 'flow_divergence_diff', 'flow_divergence_ratio', 'total_buy_vol',\n", + " '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_buy_consolidation_20...\n", + "Finished lg_buy_consolidation_20.\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 intraday_lg_flow_corr_20 (Placeholder - complex implementation)...\n", + "Finished intraday_lg_flow_corr_20 (Placeholder).\n", + "Calculating cap_neutral_cost_metric (Placeholder - requires statsmodels)...\n", + "Finished cap_neutral_cost_metric (Placeholder).\n" + ] + } + ], + "source": [ + "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'],\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', 'volume_ratio',\n", + " 'is_st'], df=df, join='inner')\n", + "df = df[df['trade_date'] >= '2021-01-01']\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',\n", + " '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())\n", + "\n", + "origin_columns = df.columns.tolist()\n", + "origin_columns = [col for col in origin_columns if 'cyq' not in col]\n", + "print(origin_columns)\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'] >= '2022-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, _ = get_rolling_factor(df)\n", + "df, _ = get_simple_factor(df)\n", + "from main.factor.factor import *\n", + "lg_flow_mom_corr(df, N=20, M=60)\n", + "lg_buy_consolidation(df, N=20)\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", + "intraday_lg_flow_corr(df, N=20) # Placeholder\n", + "cap_neutral_cost_metric(df) # Placeholder\n", + "# hurst_exponent_flow(df, N=60) # Placeholder\n", + "# df['test'] = 1\n", + "# df['test2'] = 2\n", + "# df = df.merge(industry_df, on=['l2_code', 'trade_date'], how='left')\n", + "l2_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", + "df = merge_with_industry_data(df, l2_df)\n", + "df = df.rename(columns={'l2_code': 'cat_l2_code'})\n", + "# df = df.merge(index_data, on='trade_date', how='left')\n", + "\n", + "days = 2\n", + "df = df.sort_values(by=['ts_code', 'trade_date'])\n", + "# df['future_return'] = df.groupby('ts_code', group_keys=False)['close'].apply(lambda x: x.shift(-days) / x - 1)\n", + "df['future_return'] = (df.groupby('ts_code')['close'].shift(-days) - df.groupby('ts_code')['open'].shift(-1)) / \\\n", + " df.groupby('ts_code')['open'].shift(-1)\n", + "# df['future_return'] = df.groupby('ts_code')['pct_chg'].shift(-1)\n", + "df['future_return2'] = (df.groupby('ts_code')['close'].shift(-1) - df.groupby('ts_code')['open'].shift(-1)) / \\\n", + " df.groupby('ts_code')['open'].shift(-1)\n", + "\n", + "df['future_volatility'] = (\n", + " df.groupby('ts_code')['pct_chg']\n", + " .transform(lambda x: x.rolling(days).std().shift(-days))\n", + ")\n", + "df['future_score'] = calculate_score(df, days=days, lambda_param=0.3)\n", + "\n", + "\n", + "def select_pre_zt_stocks_dynamic(stock_df):\n", + " def select_stocks(group):\n", + " return group.nlargest(1000, 'return_5') # 如果循环结束仍未找到足够标签,则返回最大数量的股票\n", + "\n", + " stock_df = stock_df.groupby('trade_date', group_keys=False).apply(select_stocks)\n", + " return stock_df\n", + "\n", + "\n", + "gc.collect()\n", + "\n", + "# df = select_pre_zt_stocks_dynamic(df[(df['trade_date'] >= '2022-01-01') & (df['trade_date'] <= '2029-04-07')])\n", + "\n", + "industry_df = read_industry_data('../../data/sw_daily.h5')\n", + "df = df.merge(industry_df, on=['cat_l2_code', 'trade_date'], how='left')\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "1c1dd3d6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['open', 'close', 'high', 'low', 'vol', 'pct_chg', 'turnover_rate', 'circ_mv', 'volume_ratio', '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', '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', 'log_circ_mv', 'cov', 'delta_cov', 'alpha_22_improved', 'alpha_003', 'alpha_007', 'alpha_013', 'cat_up_limit', 'cat_down_limit', 'up_limit_count_10d', 'down_limit_count_10d', 'consecutive_up_limit', 'vol_break', 'weight_roc5', 'smallcap_concentration', 'cost_stability', 'high_cost_break_days', 'liquidity_risk', 'turnover_std', 'mv_volatility', 'volume_growth', 'mv_growth', 'arbr', '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_buy_consolidation_20', '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', 'intraday_lg_flow_corr_20', 'cap_neutral_cost_metric', 'in_date', 'industry_obv', 'industry_return_5', 'industry_return_20', 'industry__ema_5', 'industry__ema_13', 'industry__ema_20', 'industry__ema_60', 'industry_act_factor1', 'industry_act_factor2', 'industry_act_factor3', 'industry_act_factor4', 'industry_act_factor5', 'industry_act_factor6', 'industry_rank_act_factor1', 'industry_rank_act_factor2', 'industry_rank_act_factor3', 'industry_return_5_percentile', 'industry_return_20_percentile']\n" + ] + } + ], + "source": [ + "feature_columns = [col for col in df.columns if col in df.columns]\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 'cat_l2_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", + "\n", + "print(feature_columns)\n", + "numeric_columns = df.select_dtypes(include=['float64', 'int64']).columns\n", + "numeric_columns = [col for col in numeric_columns if col in feature_columns]" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "2c60c1ea", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "每个特征列中的 NaN 值数量(字典形式):\n", + "ts_code: 0\n", + "trade_date: 0\n", + "open: 0\n", + "close: 0\n", + "high: 0\n", + "low: 0\n", + "vol: 0\n", + "pct_chg: 0\n", + "turnover_rate: 0\n", + "pe_ttm: 499616\n", + "circ_mv: 0\n", + "volume_ratio: 791\n", + "is_st: 0\n", + "up_limit: 0\n", + "down_limit: 0\n", + "buy_sm_vol: 7\n", + "sell_sm_vol: 7\n", + "buy_lg_vol: 7\n", + "sell_lg_vol: 7\n", + "buy_elg_vol: 7\n", + "sell_elg_vol: 7\n", + "net_mf_vol: 7\n", + "his_low: 24695\n", + "his_high: 24695\n", + "cost_5pct: 24695\n", + "cost_15pct: 24695\n", + "cost_50pct: 24695\n", + "cost_85pct: 24695\n", + "cost_95pct: 24695\n", + "weight_avg: 24695\n", + "winner_rate: 24695\n", + "lg_elg_net_buy_vol: 7\n", + "flow_lg_elg_intensity: 7\n", + "sm_net_buy_vol: 7\n", + "flow_divergence_diff: 7\n", + "flow_divergence_ratio: 7\n", + "total_buy_vol: 7\n", + "lg_elg_buy_prop: 7\n", + "flow_struct_buy_change: 3287\n", + "lg_elg_net_buy_vol_change: 3287\n", + "flow_lg_elg_accel: 6567\n", + "chip_concentration_range: 24695\n", + "chip_skewness: 24695\n", + "floating_chip_proxy: 24695\n", + "cost_support_15pct_change: 27855\n", + "cat_winner_price_zone: 0\n", + "flow_chip_consistency: 7\n", + "profit_taking_vs_absorb: 7\n", + "cat_is_positive: 0\n", + "upside_vol: 29581\n", + "downside_vol: 29655\n", + "vol_ratio: 0\n", + "return_skew: 13096\n", + "return_kurtosis: 13096\n", + "volume_change_rate: 29466\n", + "cat_volume_breakout: 0\n", + "turnover_deviation: 6548\n", + "cat_turnover_spike: 0\n", + "avg_volume_ratio: 7341\n", + "cat_volume_ratio_breakout: 0\n", + "vol_spike: 62074\n", + "vol_std_5: 16370\n", + "atr_14: 45836\n", + "atr_6: 19644\n", + "obv: 0\n", + "maobv_6: 16370\n", + "rsi_3: 9822\n", + "return_5: 16370\n", + "return_20: 65315\n", + "std_return_5: 16370\n", + "std_return_90: 291770\n", + "std_return_90_2: 323906\n", + "act_factor1: 16370\n", + "act_factor2: 42562\n", + "act_factor3: 65315\n", + "act_factor4: 194886\n", + "rank_act_factor1: 16370\n", + "rank_act_factor2: 42562\n", + "rank_act_factor3: 65315\n", + "log_circ_mv: 0\n", + "cov: 13096\n", + "delta_cov: 29466\n", + "alpha_22_improved: 62074\n", + "alpha_003: 0\n", + "alpha_007: 13120\n", + "alpha_013: 62074\n", + "cat_up_limit: 0\n", + "cat_down_limit: 0\n", + "up_limit_count_10d: 0\n", + "down_limit_count_10d: 0\n", + "consecutive_up_limit: 0\n", + "vol_break: 0\n", + "weight_roc5: 40531\n", + "price_cost_divergence: 93280\n", + "smallcap_concentration: 24695\n", + "cost_stability: 85077\n", + "high_cost_break_days: 13096\n", + "liquidity_risk: 53215\n", + "turnover_std: 62074\n", + "mv_volatility: 62074\n", + "volume_growth: 65315\n", + "mv_growth: 65315\n", + "arbr: 9822\n", + "momentum_factor: 29466\n", + "resonance_factor: 791\n", + "log_close: 0\n", + "cat_vol_spike: 0\n", + "up: 0\n", + "down: 0\n", + "obv_maobv_6: 16370\n", + "std_return_5_over_std_return_90: 291770\n", + "std_return_90_minus_std_return_90_2: 323906\n", + "cat_af2: 0\n", + "cat_af3: 0\n", + "cat_af4: 0\n", + "act_factor5: 194886\n", + "act_factor6: 42562\n", + "active_buy_volume_large: 13\n", + "active_buy_volume_big: 79\n", + "active_buy_volume_small: 7\n", + "buy_lg_vol_minus_sell_lg_vol: 8\n", + "buy_elg_vol_minus_sell_elg_vol: 69\n", + "ctrl_strength: 24695\n", + "low_cost_dev: 24695\n", + "asymmetry: 24701\n", + "lock_factor: 24695\n", + "cat_vol_break: 0\n", + "cost_atr_adj: 69060\n", + "cat_golden_resonance: 0\n", + "mv_turnover_ratio: 0\n", + "mv_adjusted_volume: 0\n", + "mv_weighted_turnover: 0\n", + "nonlinear_mv_volume: 0\n", + "mv_volume_ratio: 791\n", + "mv_momentum: 791\n", + "lg_flow_mom_corr_20_60: 1186\n", + "lg_buy_consolidation_20: 1950902\n", + "lg_flow_accel: 6567\n", + "profit_pressure: 24695\n", + "underwater_resistance: 24695\n", + "cost_conc_std_20: 29466\n", + "profit_decay_20: 0\n", + "vol_amp_loss_20: 53215\n", + "vol_drop_profit_cnt_5: 0\n", + "lg_flow_vol_interact_20: 29466\n", + "cost_break_confirm_cnt_5: 0\n", + "atr_norm_channel_pos_14: 0\n", + "turnover_diff_skew_20: 32740\n", + "lg_sm_flow_diverge_20: 29466\n", + "pullback_strong_20_20: 0\n", + "vol_wgt_hist_pos_20: 0\n", + "vol_adj_roc_20: 0\n", + "intraday_lg_flow_corr_20: 2431461\n", + "cap_neutral_cost_metric: 2431461\n", + "cat_l2_code: 290\n", + "in_date: 65486\n", + "future_return: 6548\n", + "future_return2: 3274\n", + "future_volatility: 6548\n", + "score: 6548\n", + "future_score: 6548\n", + "industry_obv: 11272\n", + "industry_return_5: 11272\n", + "industry_return_20: 11272\n", + "industry__ema_5: 11272\n", + "industry__ema_13: 11272\n", + "industry__ema_20: 11272\n", + "industry__ema_60: 11272\n", + "industry_act_factor1: 11272\n", + "industry_act_factor2: 11272\n", + "industry_act_factor3: 11272\n", + "industry_act_factor4: 11272\n", + "industry_act_factor5: 11272\n", + "industry_act_factor6: 11272\n", + "industry_rank_act_factor1: 11272\n", + "industry_rank_act_factor2: 11272\n", + "industry_rank_act_factor3: 11272\n", + "industry_return_5_percentile: 11272\n", + "industry_return_20_percentile: 11272\n", + "['open', 'close', 'high', 'low', 'vol', 'pct_chg', 'turnover_rate', 'circ_mv', 'volume_ratio', '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', '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_std_5', 'atr_6', 'obv', 'maobv_6', 'rsi_3', 'return_5', 'std_return_5', 'act_factor1', 'rank_act_factor1', 'log_circ_mv', 'cov', 'delta_cov', 'alpha_003', 'alpha_007', 'cat_up_limit', 'cat_down_limit', 'up_limit_count_10d', 'down_limit_count_10d', 'consecutive_up_limit', 'vol_break', 'smallcap_concentration', 'high_cost_break_days', 'arbr', 'momentum_factor', 'resonance_factor', 'log_close', 'cat_vol_spike', 'up', 'down', 'obv_maobv_6', 'cat_af2', 'cat_af3', 'cat_af4', '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', '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_drop_profit_cnt_5', 'lg_flow_vol_interact_20', 'cost_break_confirm_cnt_5', 'atr_norm_channel_pos_14', 'lg_sm_flow_diverge_20', 'pullback_strong_20_20', 'vol_wgt_hist_pos_20', 'vol_adj_roc_20', 'industry_obv', 'industry_return_5', 'industry_return_20', 'industry__ema_5', 'industry__ema_13', 'industry__ema_20', 'industry__ema_60', 'industry_act_factor1', 'industry_act_factor2', 'industry_act_factor3', 'industry_act_factor4', 'industry_act_factor5', 'industry_act_factor6', 'industry_rank_act_factor1', 'industry_rank_act_factor2', 'industry_rank_act_factor3', 'industry_return_5_percentile', 'industry_return_20_percentile']\n" + ] + } + ], + "source": [ + "def count_nan_and_inf_per_feature(df: pd.DataFrame):\n", + " \"\"\"\n", + " 计算 DataFrame 中每个特征列的 NaN 和 Inf 值数量。\n", + "\n", + " Args:\n", + " df: 要分析的 pandas DataFrame。\n", + "\n", + " Returns:\n", + " 一个字典,包含两个 pandas Series:\n", + " - 'NaN_Count': 索引是列名,值是该列中 NaN 的数量。\n", + " - 'Inf_Count': 索引是列名,值是该列中 Inf 的数量。\n", + " \"\"\"\n", + " nan_counts = df.isna().sum()\n", + " # inf_counts = np.isinf(df).sum()\n", + " return nan_counts\n", + "\n", + "\n", + "nan_counts_series = count_nan_and_inf_per_feature(df)\n", + "\n", + "# 或者,如果您想以字典的形式获取结果:\n", + "nan_counts_dict = nan_counts_series.to_dict()\n", + "print(\"\\n每个特征列中的 NaN 值数量(字典形式):\")\n", + "for k, v in nan_counts_dict.items():\n", + " print(f'{k}: {v}')\n", + " if v > 30000 and k in feature_columns:\n", + " feature_columns.remove(k)\n", + "print(feature_columns)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "e088bd8a357e815a", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-13T15:39:47.461434Z", + "start_time": "2025-04-13T15:39:44.369664Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "gen\tnevals\tavg \tstd \tmin\tmax \n", + "0 \t64 \t-0.387605\t0.492269\t-1 \t0.84339\n", + "1 \t52 \t-0.0280574\t0.328787\t-1 \t0.84339\n", + "2 \t56 \t-0.0442643\t0.498012\t-1 \t0.84339\n", + "3 \t50 \t0.0843881 \t0.506873\t-1 \t0.84339\n", + "4 \t56 \t0.128797 \t0.586781\t-1 \t0.84339\n", + "5 \t52 \t0.107366 \t0.586957\t-1 \t0.918103\n", + "6 \t52 \t0.0602483 \t0.666345\t-1 \t0.918103\n", + "7 \t54 \t0.177717 \t0.561644\t-1 \t0.918103\n", + "8 \t57 \t0.206183 \t0.620791\t-1 \t0.957887\n", + "9 \t51 \t0.253306 \t0.667259\t-1 \t1.07875 \n", + "10 \t53 \t0.19914 \t0.681541\t-1 \t1.14356 \n", + "11 \t54 \t0.173093 \t0.752007\t-1 \t1.24408 \n", + "12 \t53 \t0.429303 \t0.636249\t-1.07606\t1.24408 \n", + "13 \t46 \t0.443469 \t0.754764\t-1.17052\t1.24408 \n", + "14 \t57 \t0.412168 \t0.719715\t-1.2066 \t1.24408 \n", + "15 \t47 \t0.420095 \t0.833547\t-1.20899\t1.25608 \n", + "16 \t46 \t0.516075 \t0.916347\t-1.16556\t1.25765 \n", + "17 \t48 \t0.52129 \t0.872883\t-1 \t1.30663 \n", + "18 \t53 \t0.530992 \t0.923366\t-1 \t1.3677 \n", + "19 \t54 \t0.569299 \t0.861833\t-1.39138\t1.3677 \n", + "20 \t51 \t0.538589 \t0.883032\t-1.12472\t1.3677 \n", + "21 \t49 \t0.684813 \t0.874059\t-1 \t1.3677 \n", + "22 \t46 \t0.659823 \t0.86879 \t-1.17051\t1.3677 \n", + "23 \t42 \t0.678971 \t0.886044\t-1.39138\t1.3677 \n", + "24 \t55 \t0.639381 \t0.905808\t-1.39138\t1.37645 \n", + "25 \t42 \t0.721136 \t0.915513\t-1.30205\t1.39372 \n", + "26 \t56 \t0.695918 \t0.849837\t-1.0437 \t1.39372 \n", + "27 \t56 \t0.465007 \t0.934313\t-1 \t1.39372 \n", + "28 \t51 \t0.714563 \t0.88635 \t-1.13547\t1.43745 \n", + "29 \t49 \t0.687478 \t0.84568 \t-1 \t1.43745 \n", + "30 \t50 \t0.646657 \t0.835957\t-1 \t1.43745 \n", + "31 \t49 \t0.615978 \t0.939622\t-1.04846\t1.43745 \n", + "32 \t49 \t0.654171 \t0.973861\t-1.12771\t1.43745 \n", + "\n", + "Best Factors Found:\n", + "Fitness: 1.4375, Factor 1: protected_div_torch(mul(protected_div_torch(add(return_kurtosis, profit_pressure), mul(cost_85pct, buy_elg_vol_minus_sell_elg_vol)), protected_div_torch(cost_break_confirm_cnt_5, pow(cos(lg_flow_vol_interact_20), cos(chip_concentration_range)))), sub(add(obv, protected_div_torch(cost_break_confirm_cnt_5, cos(chip_skewness))), add(obv, protected_div_torch(add(return_kurtosis, profit_pressure), pow(alpha_007, active_buy_volume_big)))))\n", + "Fitness: 1.3937, Factor 2: protected_div_torch(mul(protected_div_torch(protected_div_torch(add(return_kurtosis, profit_pressure), pow(alpha_007, active_buy_volume_big)), delta_cov), protected_div_torch(protected_div_torch(add(return_kurtosis, profit_pressure), pow(alpha_007, active_buy_volume_big)), rank_act_factor2)), sub(add(obv, protected_div_torch(cost_break_confirm_cnt_5, cos(chip_skewness))), add(obv, protected_div_torch(add(return_kurtosis, profit_pressure), pow(alpha_007, active_buy_volume_big)))))\n", + "Fitness: 1.3843, Factor 3: protected_div_torch(mul(protected_div_torch(protected_div_torch(profit_pressure, pow(alpha_007, active_buy_volume_big)), delta_cov), protected_div_torch(protected_div_torch(add(return_kurtosis, profit_pressure), pow(alpha_007, active_buy_volume_big)), rank_act_factor2)), sub(add(obv, protected_div_torch(cost_break_confirm_cnt_5, cos(chip_skewness))), add(obv, protected_div_torch(add(return_kurtosis, profit_pressure), pow(alpha_007, active_buy_volume_big)))))\n" + ] + } + ], + "source": [ + "from deap import creator, gp, tools, base, algorithms\n", + "import numpy as np\n", + "import pandas as pd\n", + "import torch\n", + "from scipy.stats import spearmanr\n", + "import operator\n", + "\n", + "# 保护性除法函数 (PyTorch 版本)\n", + "def protected_div_torch(left, right):\n", + " return torch.where(right != 0, left / right, torch.ones_like(left))\n", + "\n", + "def generate_deap_factors_pytorch_v2(df: pd.DataFrame, numeric_columns: list, target_column: str = 'future_return', date_column: str = 'trade_date', params: dict = None, random_state: int = 42):\n", + " \"\"\"\n", + " 使用 deap 库通过遗传编程生成新的因子,并使用 PyTorch 算子和计算,过滤 NaN 值。\n", + "\n", + " Args:\n", + " df (pd.DataFrame): 包含因子和目标变量的数据框。\n", + " numeric_columns (list): 数值型因子列名的列表。\n", + " target_column (str): 目标变量的列名,默认为 'future_return'。\n", + " params (dict): deap 进化算法的参数字典。\n", + " random_state (int): 随机种子,用于保证结果的可重复性。\n", + "\n", + " Returns:\n", + " list: 包含最佳因子表达式的列表。\n", + " \"\"\"\n", + " if params is None:\n", + " params = {}\n", + "\n", + " # 设置随机种子\n", + " np.random.seed(random_state)\n", + " torch.manual_seed(random_state)\n", + "\n", + " # 1. 定义原始集 (Primitive Set) - 使用 PyTorch 算子\n", + " pset_torch = gp.PrimitiveSet(\"PYTORCH\", arity=len(numeric_columns))\n", + " pset_torch.addPrimitive(torch.add, 2)\n", + " pset_torch.addPrimitive(torch.sub, 2)\n", + " pset_torch.addPrimitive(torch.mul, 2)\n", + " pset_torch.addPrimitive(protected_div_torch, 2) # 使用 PyTorch 保护性除法\n", + " # 新增的复杂算子\n", + " pset_torch.addPrimitive(torch.sin, 1) # 正弦函数 (一元算子)\n", + " pset_torch.addPrimitive(torch.cos, 1) # 余弦函数 (一元算子)\n", + " # pset_torch.addPrimitive(torch.abs, 1) # 绝对值 (一元算子)\n", + " # pset_torch.addPrimitive(torch.sqrt, 1) # 平方根 (一元算子)\n", + " pset_torch.addPrimitive(torch.pow, 2) # 指数运算 (二元算子,例如 x 的 y 次方)\n", + " # pset_torch.addPrimitive(torch.tanh, 1) # 双曲正切函数 (一元算子)\n", + "\n", + " # def rate_of_change_torch(x, y): # 计算 y 相对于 x 的变化率\n", + " # return (y - x) / (x + 1e-8)\n", + " # pset_torch.addPrimitive(rate_of_change_torch, 2)\n", + "\n", + " # def covariance_like_torch(x, y):\n", + " # mean_x = torch.mean(x, dim=0, keepdim=True) # 保持维度以便广播\n", + " # mean_y = torch.mean(y, dim=0, keepdim=True)\n", + " # return (x - mean_x) * (y - mean_y)\n", + "\n", + " # pset_torch.addPrimitive(covariance_like_torch, 2)\n", + "\n", + " # 将 numeric_columns 作为终端添加到原始集\n", + " pset_torch.renameArguments(**{f\"ARG{i}\": col for i, col in enumerate(numeric_columns)})\n", + "\n", + " # 2. 定义适应度和个体\n", + " # 目标是最大化 IC 夏普比率\n", + " creator.create(\"FitnessMax\", base.Fitness, weights=(1.0,))\n", + " creator.create(\"Individual\", gp.PrimitiveTree, fitness=creator.FitnessMax)\n", + "\n", + " # 3. 创建工具箱 (Toolbox)\n", + " toolbox = base.Toolbox()\n", + " toolbox.register(\"expr_torch\", gp.genHalfAndHalf, pset=pset_torch, min_=1, max_=3) # 调整 min_/max_ 以控制表达式复杂性\n", + " toolbox.register(\"individual\", tools.initIterate, creator.Individual, toolbox.expr_torch)\n", + " toolbox.register(\"population\", tools.initRepeat, list, toolbox.individual)\n", + " toolbox.register(\"compile_torch\", gp.compile, pset=pset_torch) # 编译为 PyTorch 函数\n", + "\n", + " # 准备 PyTorch 张量数据 (所有日期所有股票)\n", + " device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", + " data_tensor_all = torch.from_numpy(df[numeric_columns].values).float().to(device)\n", + " target_tensor_all = torch.from_numpy(df[target_column].values).float().to(device)\n", + " dates_all = df[date_column].values # 获取日期 numpy 数组\n", + "\n", + " # 4. 定义基于 PyTorch + IC 夏普比率的适应度函数\n", + " def evaluate_torch_cuda_ic_sharpe(individual, data_tensor_all, target_tensor_all, dates_all):\n", + " # 将个体(表达式树)编译成可执行的 PyTorch 函数\n", + " func_torch = toolbox.compile_torch(expr=individual)\n", + "\n", + " try:\n", + " # 应用该函数到 PyTorch 张量 (一次性计算所有日期所有股票的因子值)\n", + " # 处理可能的维度不一致,确保输出是一维或二维 (N, 1) 的张量\n", + " factor_values_tensor = func_torch(*torch.split(data_tensor_all, 1, dim=1))\n", + " if factor_values_tensor.ndim > 1 and factor_values_tensor.shape[1] != 1:\n", + " # 如果输出是 (N, M) 其中 M > 1,可能需要一个聚合操作,这里暂时返回负适应度\n", + " print(f\"警告: 因子表达式输出张量维度为 {factor_values_tensor.shape},期望 (N, 1)。\")\n", + " return (-1.0,)\n", + " factor_values_tensor = factor_values_tensor.flatten() # 确保是展平的一维张量\n", + "\n", + " # 将 PyTorch 张量移回 CPU 并转换为 NumPy 数组\n", + " factor_values_np = factor_values_tensor.cpu().numpy()\n", + " target_np = target_tensor_all.cpu().numpy().flatten() # 目标也展平\n", + " dates_np = dates_all # 日期已经是 numpy 数组\n", + "\n", + " # 创建一个临时 Pandas DataFrame 以便按日期分组计算每日 IC\n", + " temp_df = pd.DataFrame({\n", + " 'date': dates_np,\n", + " 'factor_value': factor_values_np,\n", + " 'target_value': target_np\n", + " })\n", + "\n", + " # 计算每日 Rank IC\n", + " # 在分组应用 spearmanr 时处理 NaN 和数据点不足的问题\n", + " daily_ics = temp_df.groupby('date').apply(\n", + " lambda x: spearmanr(x['factor_value'], x['target_value'])[0]\n", + " if len(x) >= 2 and x['factor_value'].notna().sum() >= 2 and x['target_value'].notna().sum() >= 2 # 确保分组内有效数据点 >= 2\n", + " else np.nan # 数据点不足或计算失败时返回 NaN\n", + " ).dropna() # 移除 NaN 的每日 IC\n", + "\n", + " # 计算 IC 夏普比率\n", + " if len(daily_ics) < 5: # 需要至少几个有效日 IC 才能计算夏普比率\n", + " # print(f\"警告: 有效日 IC 数量不足 ({len(daily_ics)}),无法计算夏普比率。\")\n", + " return (-1.0,) # 有效日 IC 太少,返回负适应度\n", + "\n", + " ic_mean = daily_ics.mean()\n", + " ic_std = daily_ics.std()\n", + "\n", + " # 处理标准差为零的情况 (非常罕见,可能意味着每日 IC 是常数)\n", + " if ic_std == 0:\n", + " ic_sharpe = ic_mean * 1e6 if ic_mean > 0 else -1.0 # 如果均值>0且标差为0,给一个很大的正值\n", + " else:\n", + " ic_sharpe = ic_mean / ic_std\n", + "\n", + " # 返回 IC 夏普比率作为适应度 (需要最大化)\n", + " # 如果计算结果是 NaN (例如,mean/std 导致 NaN),返回负值\n", + " return (ic_sharpe if not np.isnan(ic_sharpe) else -1.0,)\n", + "\n", + " except (ValueError, TypeError, ZeroDivisionError, RuntimeError) as e:\n", + " # 打印错误信息和导致错误的个体,以便调试\n", + " print(f\"Error during evaluation for individual {individual}: {e}\")\n", + " return (-1.0,) # 如果计算过程中出现错误,返回一个很小的负值\n", + "\n", + " # 修改 toolbox.register 调用,将 target_tensor 传递给 evaluate_torch_cuda\n", + " toolbox.register(\"evaluate\", evaluate_torch_cuda_ic_sharpe, data_tensor_all=data_tensor_all, target_tensor_all=target_tensor_all, dates_all=dates_all)\n", + " toolbox.register(\"select\", tools.selTournament, tournsize=params.get('tournament_size', 3))\n", + " toolbox.register(\"mate\", gp.cxOnePointLeafBiased, termpb=0.2) # 移除 pset=pset\n", + " toolbox.register(\"mutate\", gp.mutUniform, expr=toolbox.expr_torch, pset=pset_torch) # 使用 PyTorch 原始集\n", + "\n", + " MAX_TREE_DEPTH = 5\n", + "\n", + " toolbox.decorate(\"mate\", gp.staticLimit(key=operator.attrgetter('height'), max_value=MAX_TREE_DEPTH))\n", + " toolbox.decorate(\"mutate\", gp.staticLimit(key=operator.attrgetter('height'), max_value=MAX_TREE_DEPTH))\n", + "\n", + " # 5. 设置进化参数\n", + " population_size = params.get('population_size', 100)\n", + " generations = params.get('generations', 10)\n", + " crossover_probability = params.get('crossover_probability', 0.7) # 调整参数以增加探索\n", + " mutation_probability = params.get('mutation_probability', 0.3) # 调整参数以增加探索\n", + "\n", + " # 6. 初始化种群\n", + " pop = toolbox.population(n=population_size)\n", + " hof = tools.HallOfFame(params.get('hall_of_fame_size', 5)) # 保留最佳的几个个体\n", + " stats = tools.Statistics(lambda ind: ind.fitness.values)\n", + " stats.register(\"avg\", np.mean)\n", + " stats.register(\"std\", np.std)\n", + " stats.register(\"min\", np.min)\n", + " stats.register(\"max\", np.max)\n", + "\n", + " # 7. 运行进化算法\n", + " algorithms.eaSimple(pop, toolbox, cxpb=crossover_probability, mutpb=mutation_probability, ngen=generations,\n", + " stats=stats, halloffame=hof, verbose=True)\n", + "\n", + " # 8. 返回最佳因子表达式\n", + " return hof, stats\n", + "\n", + "params = {\n", + " 'population_size': 64,\n", + " 'generations': 32,\n", + " 'crossover_probability': 0.7,\n", + " 'mutation_probability': 0.3,\n", + " 'tournament_size': 4,\n", + " 'hall_of_fame_size': 3\n", + "}\n", + "\n", + "best_factors_hof, stats = generate_deap_factors_pytorch_v2(df.copy(), numeric_columns, params=params)\n", + "\n", + "print(\"\\nBest Factors Found:\")\n", + "for i, ind in enumerate(best_factors_hof):\n", + " fitness_value = ind.fitness.values[0] # 获取适应度值\n", + " print(f\"Fitness: {fitness_value:.4f}, Factor {i+1}: {ind}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "a0b3d7551ef0c81f", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-13T15:39:47.502867Z", + "start_time": "2025-04-13T15:39:47.461434Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "全面因子分析报告 - 特征因子: 'generated_factor'\n", + "------------------------------------------------------------\n", + "整体 Rank IC: 0.0817\n", + "整体 P-value: 0.0000\n", + "------------------------------------------------------------\n", + "计算滚动 Rank IC (按 'D' 窗口)...\n", + "滚动 Rank IC 统计量 (D):\n", + " 均值: 0.0124\n", + " 标准差: 0.2330\n", + " 夏普比率 (IC Mean / IC Std): 0.0531\n", + " T-statistic: 1.4577\n", + " T-statistic P-value: 0.1453\n", + "------------------------------------------------------------\n", + "Hit Ratio (正向 Rank IC 比例): 0.5060\n", + "------------------------------------------------------------\n", + "因子 10 分位数分析 (按因子值从小到大排序):\n", + " 第 1 分位数: 平均 'future_return' = -0.0004\n", + " 第 2 分位数: 平均 'future_return' = -0.0008\n", + " 第 3 分位数: 平均 'future_return' = -0.0004\n", + " 第 4 分位数: 平均 'future_return' = 0.0005\n", + " 第 5 分位数: 平均 'future_return' = 0.0007\n", + " 第 6 分位数: 平均 'future_return' = 0.0015\n", + " 第 7 分位数: 平均 'future_return' = 0.0021\n", + " 第 8 分位数: 平均 'future_return' = 0.0033\n", + " 第 9 分位数: 平均 'future_return' = 0.0054\n", + " 第 10 分位数: 平均 'future_return' = 0.0135\n", + "\n", + "因子值的分位数范围:\n", + " 第 1 分位数: [-1.0490, 0.0581]\n", + " 第 2 分位数: [0.0581, 0.1051]\n", + " 第 3 分位数: [0.1051, 0.1458]\n", + " 第 4 分位数: [0.1458, 0.1881]\n", + " 第 5 分位数: [0.1881, 0.2354]\n", + " 第 6 分位数: [0.2354, 0.2909]\n", + " 第 7 分位数: [0.2909, 0.3594]\n", + " 第 8 分位数: [0.3594, 0.4505]\n", + " 第 9 分位数: [0.4505, 0.5880]\n", + " 第 10 分位数: [0.5880, 1.9782]\n", + "------------------------------------------------------------\n", + "分析完成。\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "import torch\n", + "\n", + "target_column = 'future_return'\n", + "# 假设您已经定义了 protected_div_torch 函数\n", + "def protected_div_torch(left, right):\n", + " return torch.where(right != 0, left / right, torch.ones_like(left))\n", + "\n", + "def protected_div_np(left, right):\n", + " \"\"\"安全除法,避免除以零的错误\"\"\"\n", + " return np.where(right != 0, left / right, np.ones_like(left) * np.nan) # 除以零时返回 NaN\n", + "\n", + "def calculate_factor_4(df: pd.DataFrame) -> pd.Series:\n", + " \"\"\"\n", + " 计算因子: sub(add(add(protected_div_torch(pow(pct_chg, std_return_90), cost_95pct), protected_div_torch(industry_act_factor6, cost_95pct)), pow(protected_div_torch(protected_div_torch(act_factor6, cost_95pct), cost_95pct), protected_div_torch(protected_div_torch(act_factor6, cost_95pct), cost_95pct))), cos(industry_act_factor1)).\n", + "\n", + " Args:\n", + " df (pd.DataFrame): 包含必要列的数据框。\n", + "\n", + " Returns:\n", + " pd.Series: 计算得到的因子值。\n", + " \"\"\"\n", + " pct_chg = df['pct_chg']\n", + " std_return_90 = df['std_return_90']\n", + " cost_95pct = df['cost_95pct']\n", + " industry_act_factor6 = df['industry_act_factor6']\n", + " act_factor6 = df['act_factor6']\n", + " industry_act_factor1 = df['industry_act_factor1']\n", + "\n", + " # Term 1: protected_div_torch(pow(pct_chg, std_return_90), cost_95pct)\n", + " term1_num = np.power(pct_chg, std_return_90)\n", + " term1 = protected_div_np(term1_num, cost_95pct)\n", + "\n", + " # Term 2: protected_div_torch(industry_act_factor6, cost_95pct)\n", + " term2 = protected_div_np(industry_act_factor6, cost_95pct)\n", + "\n", + " # Term 3: pow(protected_div_torch(protected_div_torch(act_factor6, cost_95pct), cost_95pct), protected_div_torch(protected_div_torch(act_factor6, cost_95pct), cost_95pct))\n", + " term3_base_inner = protected_div_np(act_factor6, cost_95pct)\n", + " term3_base = protected_div_np(term3_base_inner, cost_95pct)\n", + " term3_exponent_inner = protected_div_np(act_factor6, cost_95pct)\n", + " term3_exponent = protected_div_np(term3_exponent_inner, cost_95pct)\n", + " term3 = np.power(term3_base, term3_exponent)\n", + "\n", + "\n", + " # Sum of the first three terms\n", + " add_terms = term1 + term2 + term3\n", + "\n", + " # Term 4: cos(industry_act_factor1)\n", + " term4 = np.cos(industry_act_factor1)\n", + "\n", + " # Final factor\n", + " factor4 = add_terms - term4\n", + "\n", + " return factor4\n", + "\n", + "df['generated_factor'] = calculate_factor_4(df)\n", + "\n", + "import pandas as pd\n", + "import numpy as np\n", + "from scipy.stats import spearmanr, ttest_1samp\n", + "\n", + "def comprehensive_factor_analysis(df: pd.DataFrame, factor_column: str, target_column: str = 'future_return', date_column: str = 'trade_date', rolling_window: str = 'D', n_deciles: int = 10):\n", + " \"\"\"\n", + " 对 DataFrame 中的一个特征因子进行全面分析。\n", + "\n", + " Args:\n", + " df (pd.DataFrame): 包含因子和目标变量的数据框。\n", + " factor_column (str): 要分析的特征因子的列名。\n", + " target_column (str): 目标变量的列名,默认为 'future_return'。\n", + " date_column (str): 包含日期信息的列名,默认为 'trade_date'。\n", + " rolling_window (str): 滚动 Rank IC 的时间窗口(例如 'D' 表示按天,'W' 表示按周)。\n", + " n_deciles (int): 进行分位数分析时使用的分位数数量,默认为 10。\n", + " \"\"\"\n", + " if factor_column not in df.columns:\n", + " print(f\"错误: 特征因子列 '{factor_column}' 不存在于 DataFrame 中。\")\n", + " return\n", + " if target_column not in df.columns:\n", + " print(f\"错误: 目标列 '{target_column}' 不存在于 DataFrame 中。\")\n", + " return\n", + " if date_column not in df.columns:\n", + " print(f\"错误: 日期列 '{date_column}' 不存在于 DataFrame 中。\")\n", + " return\n", + "\n", + " # 确保日期列是 datetime 类型并设置为索引\n", + " df_analy = df.copy()\n", + " df_analy[date_column] = pd.to_datetime(df_analy[date_column])\n", + " df_analy = df_analy.set_index(date_column)\n", + "\n", + " # 移除因子或目标变量为 NaN 的行\n", + " df_analy = df_analy.dropna(subset=[factor_column, target_column])\n", + "\n", + " if len(df_analy) < 2:\n", + " print(\"警告: 有效数据点太少,无法进行分析。\")\n", + " return\n", + "\n", + " print(f\"全面因子分析报告 - 特征因子: '{factor_column}'\")\n", + " print(\"-\" * 60)\n", + "\n", + " # 1. 计算整体 Rank IC\n", + " overall_rank_ic, overall_p_value = spearmanr(df_analy[factor_column], df_analy[target_column])\n", + " print(f\"整体 Rank IC: {overall_rank_ic:.4f}\")\n", + " print(f\"整体 P-value: {overall_p_value:.4f}\")\n", + " print(\"-\" * 60)\n", + "\n", + " # 2. 计算滚动 Rank IC (按指定时间窗口)\n", + " print(f\"计算滚动 Rank IC (按 '{rolling_window}' 窗口)...\")\n", + " rolling_ics = df_analy.groupby(df_analy.index.to_period(rolling_window)).apply(\n", + " lambda x: spearmanr(x[factor_column], x[target_column])[0] if len(x) >= 2 else np.nan\n", + " ).dropna()\n", + "\n", + " if len(rolling_ics) < 2:\n", + " print(\"警告: 滚动 Rank IC 有效周期太少,无法计算统计量。\")\n", + " else:\n", + " # 3. 滚动 IC 统计量\n", + " ic_mean = rolling_ics.mean()\n", + " ic_std = rolling_ics.std()\n", + " ic_sharpe = ic_mean / ic_std if ic_std != 0 else np.nan\n", + " t_statistic, p_value_t = ttest_1samp(rolling_ics, 0) # 检验均值是否显著不为零\n", + "\n", + " print(f\"滚动 Rank IC 统计量 ({rolling_window}):\")\n", + " print(f\" 均值: {ic_mean:.4f}\")\n", + " print(f\" 标准差: {ic_std:.4f}\")\n", + " print(f\" 夏普比率 (IC Mean / IC Std): {ic_sharpe:.4f}\")\n", + " print(f\" T-statistic: {t_statistic:.4f}\")\n", + " print(f\" T-statistic P-value: {p_value_t:.4f}\")\n", + " print(\"-\" * 60)\n", + "\n", + " # 4. Hit Ratio (正向 Rank IC 的比例)\n", + " hit_ratio = (rolling_ics > 0).sum() / len(rolling_ics)\n", + " print(f\"Hit Ratio (正向 Rank IC 比例): {hit_ratio:.4f}\")\n", + " print(\"-\" * 60)\n", + "\n", + " # 5. 分位数分析 (在整个数据集上进行)\n", + " print(f\"因子 {n_deciles} 分位数分析 (按因子值从小到大排序):\")\n", + " df_analy['decile'] = pd.qcut(df_analy[factor_column], q=n_deciles, labels=False, duplicates='drop')\n", + " decile_analysis = df_analy.groupby('decile')[target_column].mean().sort_index()\n", + "\n", + " if len(decile_analysis) > 0:\n", + " for decile, avg_return in decile_analysis.items():\n", + " print(f\" 第 {decile + 1} 分位数: 平均 '{target_column}' = {avg_return:.4f}\")\n", + "\n", + " # 打印每个分位数的因子值范围\n", + " percentiles = np.linspace(0, 100, n_deciles + 1)\n", + " factor_percentiles = df_analy[factor_column].quantile(percentiles / 100)\n", + " print(\"\\n因子值的分位数范围:\")\n", + " # 修复分位数范围打印的 KeyError\n", + " for i in range(len(decile_analysis)): # 确保只打印实际存在的分位数\n", + " lower_bound = factor_percentiles[percentiles[i] / 100]\n", + " upper_bound = factor_percentiles[percentiles[i+1] / 100]\n", + " print(f\" 第 {i + 1} 分位数: [{lower_bound:.4f}, {upper_bound:.4f}]\")\n", + " else:\n", + " print(\"警告: 分位数分析无法执行,可能是因为数据点不足或因子值分布问题。\")\n", + "\n", + "\n", + " print(\"-\" * 60)\n", + " print(\"分析完成。\")\n", + "\n", + "comprehensive_factor_analysis(df, factor_column='generated_factor', target_column='future_return', date_column='trade_date', rolling_window='D', n_deciles=10)\n" + ] + } + ], + "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 +} diff --git a/main/factor/operators.py b/main/factor/operators.py new file mode 100644 index 0000000..6ca0451 --- /dev/null +++ b/main/factor/operators.py @@ -0,0 +1,7 @@ + + +from main.utils.utils import read_and_merge_h5_data, merge_with_industry_data + + +import sys +print(sys.path) \ No newline at end of file diff --git a/main/factor/save_factor.py b/main/factor/save_factor.py new file mode 100644 index 0000000..fe43ca6 --- /dev/null +++ b/main/factor/save_factor.py @@ -0,0 +1,222 @@ +from tqdm import tqdm + +from main.factor.factor import get_rolling_factor, get_simple_factor +from main.utils.utils import read_and_merge_h5_data +import pandas as pd + + +def create_factor_table_clickhouse(clickhouse_host: str, clickhouse_port: int, + clickhouse_user: str, clickhouse_password: str, + clickhouse_database: str, table_name: str = 'factor_data'): + """ + 在 ClickHouse 中创建 factor_data 表,考虑读取速度。 + """ + try: + print('create factor table') + client = Client(host=clickhouse_host, port=clickhouse_port, user=clickhouse_user, + password=clickhouse_password, database=clickhouse_database) + + create_table_query = f""" + CREATE TABLE IF NOT EXISTS {table_name} + ( + date Date, + asset_id String, + factor_name String, + factor_value Float64 + ) + ENGINE = MergeTree() + PARTITION BY toYYYYMM(date) + ORDER BY (date, asset_id, factor_name) + """ + + client.execute(create_table_query) + print(f"成功在 ClickHouse 数据库 '{clickhouse_database}' 中创建表 '{table_name}'!") + + except Exception as e: + print(f"创建 ClickHouse 表发生错误: {e}") + finally: + if 'client' in locals() and client.connection: + client.disconnect() + + +def write_features_to_clickhouse(df: pd.DataFrame, feature_columns: list, + clickhouse_host: str, clickhouse_port: int, + clickhouse_user: str, clickhouse_password: str, + clickhouse_database: str, table_name: str = 'stock_factor', + batch_size: int = 5000): # 设置批次大小 + """ + 将 DataFrame 中指定的特征列分批写入 ClickHouse 的宽表,动态添加列。 + """ + try: + client = Client(host=clickhouse_host, port=clickhouse_port, user=clickhouse_user, + password=clickhouse_password, database=clickhouse_database) + + if 'ts_code' not in df.columns or 'trade_date' not in df.columns: + raise ValueError("DataFrame 必须包含 'ts_code' 和 'trade_date' 列。") + + existing_columns = set() + columns_query = f"DESCRIBE TABLE {table_name}" + columns_result = client.execute(columns_query) + for col in columns_result: + existing_columns.add(col[0]) + + for factor_name in feature_columns: + if factor_name not in existing_columns: + if factor_name not in df.columns: + print(f"警告: 特征 '{factor_name}' 不存在于 DataFrame 中,将跳过添加列。") + continue + + factor_series = df[factor_name] + factor_dtype = factor_series.dtype + + clickhouse_dtype = None + if pd.api.types.is_float_dtype(factor_dtype): + clickhouse_dtype = 'Float64' + elif pd.api.types.is_integer_dtype(factor_dtype): + clickhouse_dtype = 'Int64' + elif factor_dtype == 'object': + print(f"警告: 特征 '{factor_name}' 的数据类型为 object,将跳过添加列。") + continue + else: + clickhouse_dtype = 'Float64' + + if clickhouse_dtype: + add_column_query = f"ALTER TABLE {table_name} ADD COLUMN IF NOT EXISTS {factor_name} {clickhouse_dtype}" + client.execute(add_column_query) + print(f"在表 '{table_name}' 中添加了新列: {factor_name} ({clickhouse_dtype})") + existing_columns.add(factor_name) + + insert_columns_order = ['date', 'asset_id'] + [col for col in feature_columns if + col in existing_columns and col in df.columns] + + # 分批处理 DataFrame + num_rows = len(df) + for i in tqdm(range(0, num_rows, batch_size), desc="写入批次"): + batch_df = df[i:i + batch_size] + data_to_insert_batch = [] + for row in batch_df.itertuples(index=False): + insert_row = [getattr(row, 'trade_date'), getattr(row, 'ts_code')] + for factor in feature_columns: + if factor in existing_columns and factor in df.columns: + try: + insert_row.append(getattr(row, factor)) + except AttributeError: + insert_row.append(None) + data_to_insert_batch.append(tuple(insert_row)) + write_batch_to_clickhouse(client, table_name, data_to_insert_batch, insert_columns_order) + + except Exception as e: + print(f"写入 ClickHouse 发生错误: {e}") + finally: + if 'client' in locals() and client.connection: + client.disconnect() + + +def write_batch_to_clickhouse(client, table_name, data_to_insert, columns_order): + """将一个批次的数据写入 ClickHouse""" + if data_to_insert: + insert_query_final = f"INSERT INTO {table_name} ({', '.join(columns_order)}) VALUES" + try: + client.execute(insert_query_final, data_to_insert) + print(f"成功写入 {len(data_to_insert)} 条数据到 ClickHouse 表 '{table_name}'!") + except Exception as e: + print(f"写入 ClickHouse 批次数据发生错误: {e}") + + +# -------------------- 使用示例 -------------------- +if __name__ == "__main__": + # 示例 DataFrame + + print('daily data') + df = read_and_merge_h5_data('../../data/daily_data.h5', key='daily_data', + columns=['ts_code', 'trade_date', 'open', 'close', 'high', 'low', 'vol', 'pct_chg'], + df=None) + + print('daily basic') + df = read_and_merge_h5_data('../../data/daily_basic.h5', key='daily_basic', + columns=['ts_code', 'trade_date', 'turnover_rate', 'pe_ttm', 'circ_mv', 'volume_ratio', + 'is_st'], df=df, join='inner') + df = df[df['trade_date'] >= '2021-01-01'] + + print('stk limit') + df = read_and_merge_h5_data('../../data/stk_limit.h5', key='stk_limit', + columns=['ts_code', 'trade_date', 'pre_close', 'up_limit', 'down_limit'], + df=df) + print('money flow') + df = read_and_merge_h5_data('../../data/money_flow.h5', key='money_flow', + columns=['ts_code', 'trade_date', 'buy_sm_vol', 'sell_sm_vol', 'buy_lg_vol', + 'sell_lg_vol', + 'buy_elg_vol', 'sell_elg_vol', 'net_mf_vol'], + df=df) + print('cyq perf') + df = read_and_merge_h5_data('../../data/cyq_perf.h5', key='cyq_perf', + columns=['ts_code', 'trade_date', 'his_low', 'his_high', 'cost_5pct', 'cost_15pct', + 'cost_50pct', + 'cost_85pct', 'cost_95pct', 'weight_avg', 'winner_rate'], + df=df) + print(df.info()) + + origin_columns = df.columns.tolist() + origin_columns = [col for col in origin_columns if 'cyq' not in col] + print(origin_columns) + + + def filter_data(df): + # df = df.groupby('trade_date').apply(lambda x: x.nlargest(1000, 'act_factor1')) + df = df[~df['is_st']] + df = df[~df['ts_code'].str.endswith('BJ')] + df = df[~df['ts_code'].str.startswith('30')] + df = df[~df['ts_code'].str.startswith('68')] + df = df[~df['ts_code'].str.startswith('8')] + df = df[df['trade_date'] >= '20180101'] + if 'in_date' in df.columns: + df = df.drop(columns=['in_date']) + df = df.reset_index(drop=True) + return df + + + df = filter_data(df) + df, _ = get_rolling_factor(df) + df, _ = get_simple_factor(df) + # df['test'] = 1 + # df['test2'] = 2 + # df = df.merge(industry_df, on=['l2_code', 'trade_date'], how='left') + df = df.rename(columns={'l2_code': 'cat_l2_code'}) + # df = df.merge(index_data, on='trade_date', how='left') + + print(df.info()) + + feature_columns = [col for col in df.columns if col in df.columns] + feature_columns = [col for col in feature_columns if col not in ['trade_date', + 'ts_code', + 'label']] + feature_columns = [col for col in feature_columns if 'future' not in col] + feature_columns = [col for col in feature_columns if 'label' not in col] + feature_columns = [col for col in feature_columns if 'score' not in col] + feature_columns = [col for col in feature_columns if 'gen' not in col] + feature_columns = [col for col in feature_columns if 'is_st' not in col] + # feature_columns = [col for col in feature_columns if 'pe_ttm' not in col] + # feature_columns = [col for col in feature_columns if 'volatility' not in col] + # feature_columns = [col for col in feature_columns if 'circ_mv' not in col] + feature_columns = [col for col in feature_columns if 'cat_l2_code' not in col] + feature_columns = [col for col in feature_columns if col not in origin_columns] + feature_columns = [col for col in feature_columns if not col.startswith('_')] + + print(feature_columns) + + # 替换为您的 ClickHouse 连接信息 + clickhouse_host = '127.0.0.1' + clickhouse_port = 9000 + clickhouse_user = 'default' + clickhouse_password = 'clickhouse520102' + clickhouse_database = 'stock_data' + + # create_factor_table_clickhouse(clickhouse_host, clickhouse_port, + # clickhouse_user, clickhouse_password, + # clickhouse_database) + + write_features_to_clickhouse( + df[[col for col in df.columns if col in ['ts_code', 'trade_date'] or col in feature_columns]], feature_columns, + clickhouse_host, clickhouse_port, + clickhouse_user, clickhouse_password, + clickhouse_database) diff --git a/code/model/lightgbm_model_UpdateRegression_2025-2-25.pkl b/main/model/lightgbm_model_UpdateRegression_2025-2-25.pkl similarity index 100% rename from code/model/lightgbm_model_UpdateRegression_2025-2-25.pkl rename to main/model/lightgbm_model_UpdateRegression_2025-2-25.pkl diff --git a/main/test.py b/main/test.py new file mode 100644 index 0000000..f84565e --- /dev/null +++ b/main/test.py @@ -0,0 +1,4 @@ +import sys +print(sys.path) + +from main.utils.utils import read_and_merge_h5_data, merge_with_industry_data diff --git a/main/train/AnalyzeData.ipynb b/main/train/AnalyzeData.ipynb new file mode 100644 index 0000000..13aab3f --- /dev/null +++ b/main/train/AnalyzeData.ipynb @@ -0,0 +1,2179 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "79a7758178bafdd3", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-09T16:39:30.609224Z", + "start_time": "2025-04-09T16:39:29.929606Z" + }, + "jupyter": { + "source_hidden": true + } + }, + "outputs": [], + "source": [ + "# %load_ext autoreload\n", + "# %autoreload 2\n", + "\n", + "import pandas as pd\n", + "import warnings\n", + "\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "pd.set_option('display.max_columns', None)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "2c66084a979c42dd", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-09T16:39:30.914968Z", + "start_time": "2025-04-09T16:39:30.858395Z" + }, + "jupyter": { + "source_hidden": true + } + }, + "outputs": [], + "source": [ + "\n", + "import talib\n", + "\n", + "\n", + "def get_rolling_factor(df):\n", + " old_columns = df.columns.tolist()[:]\n", + "\n", + " # 按股票和日期排序(如果尚未排序)\n", + " df = df.sort_values(by=['ts_code', 'trade_date'])\n", + "\n", + " grouped = df.groupby('ts_code', group_keys=False)\n", + "\n", + " window = 20\n", + " df['_is_positive'] = (df['pct_chg'] > 0).astype(int)\n", + " df['_is_negative'] = (df['pct_chg'] < 0).astype(int)\n", + " df['cat_is_positive'] = (df['pct_chg'] > 0).astype(int)\n", + "\n", + " # 分离正负收益率 (用于计算各自的均值和平方均值)\n", + " # 注意:这里我们保留原始收益率用于计算,而不是 clip 到 0\n", + " df['_pos_returns'] = df['pct_chg'].where(df['pct_chg'] > 0, 0) # 非正设为0,便于求和\n", + " df['_neg_returns'] = df['pct_chg'].where(df['pct_chg'] < 0, 0) # 非负设为0,便于求和\n", + "\n", + " # 计算收益率的平方 (用于计算 E[X^2])\n", + " df['_pos_returns_sq'] = np.square(df['_pos_returns'])\n", + " df['_neg_returns_sq'] = np.square(df['_neg_returns']) # 平方后负数变正\n", + "\n", + " # 4. 计算滚动统计量 (使用内置函数,速度较快)\n", + " # 计算正收益日的统计量\n", + " rolling_pos_count = grouped['_is_positive'].rolling(window, min_periods=max(1, window // 2)).sum()\n", + " rolling_pos_sum = grouped['_pos_returns'].rolling(window, min_periods=max(1, window // 2)).sum()\n", + " rolling_pos_sum_sq = grouped['_pos_returns_sq'].rolling(window, min_periods=max(1, window // 2)).sum()\n", + "\n", + " # 计算负收益日的统计量\n", + " rolling_neg_count = grouped['_is_negative'].rolling(window, min_periods=max(1, window // 2)).sum()\n", + " rolling_neg_sum = grouped['_neg_returns'].rolling(window, min_periods=max(1, window // 2)).sum()\n", + " rolling_neg_sum_sq = grouped['_neg_returns_sq'].rolling(window, min_periods=max(1, window // 2)).sum()\n", + "\n", + " # 5. 计算方差和标准差\n", + " pos_mean_sq = rolling_pos_sum_sq / rolling_pos_count\n", + " pos_mean = rolling_pos_sum / rolling_pos_count\n", + " pos_var = pos_mean_sq - np.square(pos_mean)\n", + " pos_var = pos_var.where(rolling_pos_count >= 2, np.nan).clip(lower=0)\n", + " upside_vol = np.sqrt(pos_var)\n", + "\n", + " neg_mean_sq = rolling_neg_sum_sq / rolling_neg_count\n", + " neg_mean = rolling_neg_sum / rolling_neg_count # 注意 neg_mean 是负数\n", + " neg_var = neg_mean_sq - np.square(neg_mean)\n", + " neg_var = neg_var.where(rolling_neg_count >= 2, np.nan).clip(lower=0)\n", + " downside_vol = np.sqrt(neg_var)\n", + "\n", + " # rolling 操作后结果带有 MultiIndex,需要去除股票代码层级以便合并\n", + " df['upside_vol'] = upside_vol.reset_index(level=0, drop=True)\n", + " df['downside_vol'] = downside_vol.reset_index(level=0, drop=True)\n", + "\n", + " df['vol_ratio'] = df['upside_vol'] / df['downside_vol']\n", + " df['vol_ratio'] = df['vol_ratio'].replace([np.inf, -np.inf], np.nan).fillna(0) # 或 fillna(np.nan)\n", + "\n", + " df['return_skew'] = grouped['pct_chg'].rolling(window=5).skew().reset_index(0, drop=True)\n", + " df['return_kurtosis'] = grouped['pct_chg'].rolling(window=5).kurt().reset_index(0, drop=True)\n", + "\n", + " # 因子 1:短期成交量变化率\n", + " df['volume_change_rate'] = (\n", + " grouped['vol'].rolling(window=2).mean() /\n", + " grouped['vol'].rolling(window=10).mean() - 1\n", + " ).reset_index(level=0, drop=True) # 确保索引对齐\n", + "\n", + " # 因子 2:成交量突破信号\n", + " max_volume = grouped['vol'].rolling(window=5).max().reset_index(level=0, drop=True) # 确保索引对齐\n", + " df['cat_volume_breakout'] = (df['vol'] > max_volume)\n", + "\n", + " # 因子 3:换手率均线偏离度\n", + " mean_turnover = grouped['turnover_rate'].rolling(window=3).mean().reset_index(level=0, drop=True)\n", + " std_turnover = grouped['turnover_rate'].rolling(window=3).std().reset_index(level=0, drop=True)\n", + " df['turnover_deviation'] = (df['turnover_rate'] - mean_turnover) / std_turnover\n", + "\n", + " # 因子 4:换手率激增信号\n", + " df['cat_turnover_spike'] = (df['turnover_rate'] > mean_turnover + 2 * std_turnover)\n", + "\n", + " # 因子 5:量比均值\n", + " df['avg_volume_ratio'] = grouped['volume_ratio'].rolling(window=3).mean().reset_index(level=0, drop=True)\n", + "\n", + " # 因子 6:量比突破信号\n", + " max_volume_ratio = grouped['volume_ratio'].rolling(window=5).max().reset_index(level=0, drop=True)\n", + " df['cat_volume_ratio_breakout'] = (df['volume_ratio'] > max_volume_ratio)\n", + "\n", + " df['vol_spike'] = grouped.apply(\n", + " lambda x: pd.Series(x['vol'].rolling(20).mean(), index=x.index)\n", + " )\n", + " df['vol_std_5'] = grouped['vol'].pct_change().rolling(window=5).std()\n", + "\n", + " # 计算 ATR\n", + " df['atr_14'] = grouped.apply(\n", + " lambda x: pd.Series(talib.ATR(x['high'].values, x['low'].values, x['close'].values, timeperiod=14),\n", + " index=x.index)\n", + " )\n", + " df['atr_6'] = grouped.apply(\n", + " lambda x: pd.Series(talib.ATR(x['high'].values, x['low'].values, x['close'].values, timeperiod=6),\n", + " index=x.index)\n", + " )\n", + "\n", + " # 计算 OBV 及其均线\n", + " df['obv'] = grouped.apply(\n", + " lambda x: pd.Series(talib.OBV(x['close'].values, x['vol'].values), index=x.index)\n", + " )\n", + " print(df.columns)\n", + " df['maobv_6'] = grouped.apply(\n", + " lambda x: pd.Series(talib.SMA(x['obv'].values, timeperiod=6), index=x.index)\n", + " )\n", + "\n", + " df['rsi_3'] = grouped.apply(\n", + " lambda x: pd.Series(talib.RSI(x['close'].values, timeperiod=3), index=x.index)\n", + " )\n", + " # df['rsi_6'] = grouped.apply(\n", + " # lambda x: pd.Series(talib.RSI(x['close'].values, timeperiod=6), index=x.index)\n", + " # )\n", + " # df['rsi_9'] = grouped.apply(\n", + " # lambda x: pd.Series(talib.RSI(x['close'].values, timeperiod=9), index=x.index)\n", + " # )\n", + "\n", + " # 计算 return_10 和 return_20\n", + " df['return_5'] = grouped['close'].apply(lambda x: x / x.shift(5) - 1)\n", + " # df['return_10'] = grouped['close'].apply(lambda x: x / x.shift(10) - 1)\n", + " df['return_20'] = grouped['close'].apply(lambda x: x / x.shift(20) - 1)\n", + "\n", + " # df['avg_close_5'] = grouped['close'].apply(lambda x: x.rolling(window=5).mean() / x)\n", + "\n", + " # 计算标准差指标\n", + " df['std_return_5'] = grouped['close'].apply(lambda x: x.pct_change().rolling(window=5).std())\n", + " # df['std_return_15'] = grouped['close'].apply(lambda x: x.pct_change().rolling(window=15).std())\n", + " # df['std_return_25'] = grouped['close'].apply(lambda x: x.pct_change().rolling(window=25).std())\n", + " df['std_return_90'] = grouped['close'].apply(lambda x: x.pct_change().rolling(window=90).std())\n", + " df['std_return_90_2'] = grouped['close'].apply(lambda x: x.shift(10).pct_change().rolling(window=90).std())\n", + "\n", + " # 计算 EMA 指标\n", + " df['_ema_5'] = grouped['close'].apply(\n", + " lambda x: pd.Series(talib.EMA(x.values, timeperiod=5), index=x.index)\n", + " )\n", + " df['_ema_13'] = grouped['close'].apply(\n", + " lambda x: pd.Series(talib.EMA(x.values, timeperiod=13), index=x.index)\n", + " )\n", + " df['_ema_20'] = grouped['close'].apply(\n", + " lambda x: pd.Series(talib.EMA(x.values, timeperiod=20), index=x.index)\n", + " )\n", + " df['_ema_60'] = grouped['close'].apply(\n", + " lambda x: pd.Series(talib.EMA(x.values, timeperiod=60), index=x.index)\n", + " )\n", + "\n", + " # 计算 act_factor1, act_factor2, act_factor3, act_factor4\n", + " df['act_factor1'] = grouped['_ema_5'].apply(\n", + " lambda x: np.arctan((x / x.shift(1) - 1) * 100) * 57.3 / 50\n", + " )\n", + " df['act_factor2'] = grouped['_ema_13'].apply(\n", + " lambda x: np.arctan((x / x.shift(1) - 1) * 100) * 57.3 / 40\n", + " )\n", + " df['act_factor3'] = grouped['_ema_20'].apply(\n", + " lambda x: np.arctan((x / x.shift(1) - 1) * 100) * 57.3 / 21\n", + " )\n", + " df['act_factor4'] = grouped['_ema_60'].apply(\n", + " lambda x: np.arctan((x / x.shift(1) - 1) * 100) * 57.3 / 10\n", + " )\n", + "\n", + " # 根据 trade_date 截面计算排名\n", + " df['rank_act_factor1'] = df.groupby('trade_date', group_keys=False)['act_factor1'].rank(ascending=False, pct=True)\n", + " df['rank_act_factor2'] = df.groupby('trade_date', group_keys=False)['act_factor2'].rank(ascending=False, pct=True)\n", + " df['rank_act_factor3'] = df.groupby('trade_date', group_keys=False)['act_factor3'].rank(ascending=False, pct=True)\n", + "\n", + " df['log(circ_mv)'] = np.log(df['circ_mv'])\n", + "\n", + " window_high_volume = 5\n", + " window_close_stddev = 20\n", + " period_delta = 5\n", + "\n", + " # 计算每只股票的滚动协方差\n", + " def calculate_rolling_cov(group):\n", + " return group['high'].rolling(window_high_volume).cov(group['vol'])\n", + "\n", + " df['cov'] = grouped.apply(calculate_rolling_cov)\n", + "\n", + " # 计算每只股票的协方差差分\n", + " def calculate_delta_cov(group):\n", + " return group['cov'].diff(period_delta)\n", + "\n", + " df['delta_cov'] = grouped.apply(calculate_delta_cov)\n", + "\n", + " # 计算每只股票的滚动标准差\n", + " def calculate_stddev_close(group):\n", + " return group['close'].rolling(window_close_stddev).std()\n", + "\n", + " df['_stddev_close'] = grouped.apply(calculate_stddev_close)\n", + " df['_rank_stddev'] = df.groupby('trade_date')['_stddev_close'].rank(pct=True)\n", + " df['alpha_22_improved'] = -1 * df['delta_cov'] * df['_rank_stddev']\n", + "\n", + "\n", + " df['alpha_003'] = np.where(df['high'] != df['low'],\n", + " (df['close'] - df['open']) / (df['high'] - df['low']),\n", + " 0)\n", + "\n", + " df['alpha_007'] = grouped.apply(lambda x: x['close'].rolling(5).corr(x['vol']))\n", + " df['alpha_007'] = df.groupby('trade_date', group_keys=False)['alpha_007'].rank(ascending=True, pct=True)\n", + "\n", + " df['alpha_013'] = grouped['close'].transform(lambda x: x.rolling(5).sum() - x.rolling(20).sum())\n", + " df['alpha_013'] = df.groupby('trade_date', group_keys=False)['alpha_013'].rank(ascending=True, pct=True)\n", + "\n", + " df['cat_up_limit'] = (df['close'] == df['up_limit']) # 是否涨停(1表示涨停,0表示未涨停)\n", + " df['cat_down_limit'] = (df['close'] == df['down_limit']) # 是否跌停(1表示跌停,0表示未跌停)\n", + " df['up_limit_count_10d'] = grouped['cat_up_limit'].rolling(window=10, min_periods=1).sum().reset_index(level=0,\n", + " drop=True)\n", + " df['down_limit_count_10d'] = grouped['cat_down_limit'].rolling(window=10, min_periods=1).sum().reset_index(level=0,\n", + " drop=True)\n", + "\n", + " # 3. 最近连续涨跌停天数\n", + " def calculate_consecutive_limits(series):\n", + " \"\"\"\n", + " 计算连续涨停/跌停天数。\n", + " \"\"\"\n", + " consecutive_up = series * (series.groupby((series != series.shift()).cumsum()).cumcount() + 1)\n", + " consecutive_down = series * (series.groupby((series != series.shift()).cumsum()).cumcount() + 1)\n", + " return consecutive_up, consecutive_down\n", + "\n", + " # 连续涨停天数\n", + " df['consecutive_up_limit'] = grouped['cat_up_limit'].apply(\n", + " lambda x: calculate_consecutive_limits(x)[0]\n", + " )\n", + "\n", + " df['vol_break'] = np.where((df['close'] > df['cost_85pct']) & (df['volume_ratio'] > 2), 1, 0)\n", + "\n", + " df['weight_roc5'] = grouped['weight_avg'].apply(lambda x: x.pct_change(5))\n", + "\n", + " def rolling_corr(group):\n", + " roc_close = group['close'].pct_change()\n", + " roc_weight = group['weight_avg'].pct_change()\n", + " return roc_close.rolling(10).corr(roc_weight)\n", + "\n", + " df['price_cost_divergence'] = grouped.apply(rolling_corr)\n", + "\n", + " df['smallcap_concentration'] = (1 / df['log(circ_mv)']) * (df['cost_85pct'] - df['cost_15pct'])\n", + "\n", + " # 16. 筹码稳定性指数 (20日波动率)\n", + " df['weight_std20'] = grouped['weight_avg'].apply(lambda x: x.rolling(20).std())\n", + " df['cost_stability'] = df['weight_std20'] / grouped['weight_avg'].transform(lambda x: x.rolling(20).mean())\n", + "\n", + " # 17. 成本区间突破标记\n", + " df['high_cost_break_days'] = grouped.apply(lambda g: g['close'].gt(g['cost_95pct']).rolling(5).sum())\n", + "\n", + " # 20. 筹码-流动性风险\n", + " df['liquidity_risk'] = (df['cost_95pct'] - df['cost_5pct']) * (\n", + " 1 / grouped['vol'].transform(lambda x: x.rolling(10).mean()))\n", + "\n", + " # # 7. 市值波动率因子\n", + " # df['turnover_std'] = df.groupby('ts_code')['turnover_rate'].transform(lambda x: x.rolling(window=20).std())\n", + " # df['mv_volatility'] = grouped.apply(lambda x: x['turnover_std'] / x['log(circ_mv)'])\n", + " #\n", + " # # 8. 市值成长性因子\n", + " # df['volume_growth'] = df.groupby('ts_code')['vol'].pct_change(periods=20)\n", + " # df['mv_growth'] = df['volume_growth'] / df['log(circ_mv)']\n", + " #\n", + " # df[\"ar\"] = df.groupby('ts_code').apply(lambda x: (x[\"high\"].div(x[\"open\"]).rolling(3).sum()) / (x[\"open\"].div(x[\"low\"]).rolling(3).sum()) * 100).reset_index(level='ts_code', drop=True)\n", + " # df[\"pre_close\"] = df.groupby('ts_code')[\"close\"].shift(1)\n", + " # df[\"br_up\"] = (df[\"high\"] - df[\"pre_close\"]).clip(lower=0)\n", + " # df[\"br_down\"] = (df[\"pre_close\"] - df[\"low\"]).clip(lower=0)\n", + " # df[\"br\"] = df.groupby('ts_code').apply(lambda x: (x[\"br_up\"].rolling(3).sum()) / (x[\"br_down\"].rolling(3).sum()) * 100).reset_index(level='ts_code', drop=True)\n", + " # df['arbr'] = df['ar'] - df['br']\n", + " # df.drop(columns=[\"pre_close\", \"br_up\", \"br_down\", 'ar', 'br'], inplace=True)\n", + "\n", + " # 7. 市值波动率因子 (使用 grouped)\n", + " df['turnover_std'] = grouped['turnover_rate'].transform(lambda x: x.rolling(window=20).std())\n", + " df['mv_volatility'] = grouped.apply(lambda x: x['turnover_std'] / x['log(circ_mv)'])\n", + "\n", + " # 8. 市值成长性因子\n", + " df['volume_growth'] = grouped['vol'].pct_change(periods=20)\n", + " df['mv_growth'] = df['volume_growth'] / df['log(circ_mv)']\n", + "\n", + " # AR 指标\n", + " df[\"ar\"] = grouped.apply(lambda x: (x[\"high\"].div(x[\"open\"]).rolling(3).sum()) / (x[\"open\"].div(x[\"low\"]).rolling(3).sum()) * 100)\n", + "\n", + " # BR 指标\n", + " df[\"pre_close\"] = grouped[\"close\"].shift(1)\n", + " df[\"br_up\"] = (df[\"high\"] - df[\"pre_close\"]).clip(lower=0)\n", + " df[\"br_down\"] = (df[\"pre_close\"] - df[\"low\"]).clip(lower=0)\n", + " df[\"br\"] = grouped.apply(lambda x: (x[\"br_up\"].rolling(3).sum()) / (x[\"br_down\"].rolling(3).sum()) * 100)\n", + "\n", + " # ARBR\n", + " df['arbr'] = df['ar'] - df['br']\n", + " df.drop(columns=[\"pre_close\", \"br_up\", \"br_down\", 'ar', 'br'], inplace=True)\n", + "\n", + " df.drop(columns=['weight_std20'], inplace=True, errors='ignore')\n", + " df.drop(\n", + " columns=['_is_positive', '_is_negative', '_pos_returns', '_neg_returns', '_pos_returns_sq', '_neg_returns_sq'],\n", + " inplace=True, errors='ignore')\n", + " new_columns = [col for col in df.columns.tolist()[:] if col not in old_columns]\n", + "\n", + " return df, new_columns\n", + "\n", + "\n", + "def get_simple_factor(df):\n", + " old_columns = df.columns.tolist()[:]\n", + " df = df.sort_values(by=['ts_code', 'trade_date'])\n", + "\n", + " alpha = 0.5\n", + " df['momentum_factor'] = df['volume_change_rate'] + alpha * df['turnover_deviation']\n", + " df['resonance_factor'] = df['volume_ratio'] * df['pct_chg']\n", + " df['log_close'] = np.log(df['close'])\n", + "\n", + " df['cat_vol_spike'] = df['vol'] > 2 * df['vol_spike']\n", + "\n", + " df['up'] = (df['high'] - df[['close', 'open']].max(axis=1)) / df['close']\n", + " df['down'] = (df[['close', 'open']].min(axis=1) - df['low']) / df['close']\n", + "\n", + " df['obv-maobv_6'] = df['obv'] - df['maobv_6']\n", + "\n", + " # 计算比值指标\n", + " df['std_return_5 / std_return_90'] = df['std_return_5'] / df['std_return_90']\n", + " # df['std_return_5 / std_return_25'] = df['std_return_5'] / df['std_return_25']\n", + "\n", + " # 计算标准差差值\n", + " df['std_return_90 - std_return_90_2'] = df['std_return_90'] - df['std_return_90_2']\n", + "\n", + " # df['cat_af1'] = df['act_factor1'] > 0\n", + " df['cat_af2'] = df['act_factor2'] > df['act_factor1']\n", + " df['cat_af3'] = df['act_factor3'] > df['act_factor2']\n", + " df['cat_af4'] = df['act_factor4'] > df['act_factor3']\n", + "\n", + " # 计算 act_factor5 和 act_factor6\n", + " df['act_factor5'] = df['act_factor1'] + df['act_factor2'] + df['act_factor3'] + df['act_factor4']\n", + " df['act_factor6'] = (df['act_factor1'] - df['act_factor2']) / np.sqrt(\n", + " df['act_factor1'] ** 2 + df['act_factor2'] ** 2)\n", + "\n", + " df['active_buy_volume_large'] = df['buy_lg_vol'] / df['net_mf_vol']\n", + " df['active_buy_volume_big'] = df['buy_elg_vol'] / df['net_mf_vol']\n", + " df['active_buy_volume_small'] = df['buy_sm_vol'] / df['net_mf_vol']\n", + "\n", + " df['buy_lg_vol_minus_sell_lg_vol'] = (df['buy_lg_vol'] - df['sell_lg_vol']) / df['net_mf_vol']\n", + " df['buy_elg_vol_minus_sell_elg_vol'] = (df['buy_elg_vol'] - df['sell_elg_vol']) / df['net_mf_vol']\n", + "\n", + " df['log(circ_mv)'] = np.log(df['circ_mv'])\n", + "\n", + " df['ctrl_strength'] = (df['cost_85pct'] - df['cost_15pct']) / (df['his_high'] - df['his_low'])\n", + "\n", + " df['low_cost_dev'] = (df['close'] - df['cost_5pct']) / (df['cost_50pct'] - df['cost_5pct'])\n", + "\n", + " df['asymmetry'] = (df['cost_95pct'] - df['cost_50pct']) / (df['cost_50pct'] - df['cost_5pct'])\n", + "\n", + " df['lock_factor'] = df['turnover_rate'] * (\n", + " 1 - (df['cost_95pct'] - df['cost_5pct']) / (df['his_high'] - df['his_low']))\n", + "\n", + " df['cat_vol_break'] = (df['close'] > df['cost_85pct']) & (df['volume_ratio'] > 2)\n", + "\n", + " df['cost_atr_adj'] = (df['cost_95pct'] - df['cost_5pct']) / df['atr_14']\n", + "\n", + " # 12. 小盘股筹码集中度\n", + " df['smallcap_concentration'] = (1 / df['log(circ_mv)']) * (df['cost_85pct'] - df['cost_15pct'])\n", + "\n", + " df['cat_golden_resonance'] = ((df['close'] > df['weight_avg']) &\n", + " (df['volume_ratio'] > 1.5) &\n", + " (df['winner_rate'] > 0.7))\n", + "\n", + " df['mv_turnover_ratio'] = df['turnover_rate'] / df['log(circ_mv)']\n", + "\n", + " df['mv_adjusted_volume'] = df['vol'] / df['log(circ_mv)']\n", + "\n", + " df['mv_weighted_turnover'] = df['turnover_rate'] * (1 / df['log(circ_mv)'])\n", + "\n", + " df['nonlinear_mv_volume'] = df['vol'] / df['log(circ_mv)']\n", + "\n", + " df['mv_volume_ratio'] = df['volume_ratio'] / df['log(circ_mv)']\n", + "\n", + " df['mv_momentum'] = df['turnover_rate'] * df['volume_ratio'] / df['log(circ_mv)']\n", + "\n", + " drop_columns = [col for col in df.columns if col.startswith('_')]\n", + " df.drop(columns=drop_columns, inplace=True, errors='ignore')\n", + "\n", + " new_columns = [col for col in df.columns.tolist()[:] if col not in old_columns]\n", + " return df, new_columns\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "a79cafb06a7e0e43", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-09T16:40:19.471361Z", + "start_time": "2025-04-09T16:39:30.917824Z" + }, + "jupyter": { + "source_hidden": true + }, + "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", + "\n", + "RangeIndex: 4051406 entries, 0 to 4051405\n", + "Data columns (total 31 columns):\n", + " # Column Dtype \n", + "--- ------ ----- \n", + " 0 ts_code object \n", + " 1 trade_date datetime64[ns]\n", + " 2 open float64 \n", + " 3 close float64 \n", + " 4 high float64 \n", + " 5 low float64 \n", + " 6 vol float64 \n", + " 7 pct_chg float64 \n", + " 8 turnover_rate float64 \n", + " 9 pe_ttm float64 \n", + " 10 circ_mv float64 \n", + " 11 volume_ratio float64 \n", + " 12 is_st bool \n", + " 13 up_limit float64 \n", + " 14 down_limit float64 \n", + " 15 buy_sm_vol float64 \n", + " 16 sell_sm_vol float64 \n", + " 17 buy_lg_vol float64 \n", + " 18 sell_lg_vol float64 \n", + " 19 buy_elg_vol float64 \n", + " 20 sell_elg_vol float64 \n", + " 21 net_mf_vol float64 \n", + " 22 his_low float64 \n", + " 23 his_high float64 \n", + " 24 cost_5pct float64 \n", + " 25 cost_15pct float64 \n", + " 26 cost_50pct float64 \n", + " 27 cost_85pct float64 \n", + " 28 cost_95pct float64 \n", + " 29 weight_avg float64 \n", + " 30 winner_rate float64 \n", + "dtypes: bool(1), datetime64[ns](1), float64(28), object(1)\n", + "memory usage: 931.2+ MB\n", + "None\n" + ] + } + ], + "source": [ + "from code.utils.utils import read_and_merge_h5_data\n", + "\n", + "print('daily data')\n", + "df1 = read_and_merge_h5_data('../../data-copy/daily_data.h5', key='daily_data',\n", + " columns=['ts_code', 'trade_date', 'open', 'close', 'high', 'low', 'vol', 'pct_chg'],\n", + " df=None)\n", + "df1 = df1[df1['trade_date'] >= '2022-01-01']\n", + "\n", + "print('daily basic')\n", + "df1 = read_and_merge_h5_data('../../data-copy/daily_basic.h5', key='daily_basic',\n", + " columns=['ts_code', 'trade_date', 'turnover_rate', 'pe_ttm', 'circ_mv', 'volume_ratio',\n", + " 'is_st'], df=df1, join='inner')\n", + "\n", + "print('stk limit')\n", + "df1 = read_and_merge_h5_data('../../data-copy/stk_limit.h5', key='stk_limit',\n", + " columns=['ts_code', 'trade_date', 'pre_close', 'up_limit', 'down_limit'],\n", + " df=df1)\n", + "print('money flow')\n", + "df1 = read_and_merge_h5_data('../../data-copy/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=df1)\n", + "print('cyq perf')\n", + "df1 = read_and_merge_h5_data('../../data-copy/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=df1)\n", + "print(df1.info())" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cac01788dac10678", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-09T16:40:23.694912Z", + "start_time": "2025-04-09T16:40:19.488481Z" + }, + "jupyter": { + "source_hidden": true + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "industry\n" + ] + } + ], + "source": [ + "print('industry')\n", + "industry_df1 = read_and_merge_h5_data('../../data-copy/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", + "df1 = merge_with_industry_data(df1, industry_df1)\n", + "# print(mdf[mdf['ts_code'] == '600751.SH'][['ts_code', 'trade_date', 'l2_code']])" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "5f7a8b42681606f6", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-09T16:40:30.145830Z", + "start_time": "2025-04-09T16:40:23.712071Z" + }, + "jupyter": { + "source_hidden": true + } + }, + "outputs": [], + "source": [ + "from code.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", + " 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_df1 = read_industry_data('../../data-copy/sw_daily.h5')\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "85c3e3d0235ffffa", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-09T16:41:39.580305Z", + "start_time": "2025-04-09T16:40:30.170820Z" + }, + "jupyter": { + "source_hidden": true + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Index(['ts_code', 'trade_date', 'open', 'close', 'high', 'low', 'vol',\n", + " 'pct_chg', 'turnover_rate', 'pe_ttm', 'circ_mv', 'volume_ratio',\n", + " 'is_st', 'up_limit', 'down_limit', 'buy_sm_vol', 'sell_sm_vol',\n", + " 'buy_lg_vol', 'sell_lg_vol', 'buy_elg_vol', 'sell_elg_vol',\n", + " 'net_mf_vol', 'his_low', 'his_high', 'cost_5pct', 'cost_15pct',\n", + " 'cost_50pct', 'cost_85pct', 'cost_95pct', 'weight_avg', 'winner_rate',\n", + " 'l2_code', '_is_positive', '_is_negative', 'cat_is_positive',\n", + " '_pos_returns', '_neg_returns', '_pos_returns_sq', '_neg_returns_sq',\n", + " 'upside_vol', 'downside_vol', 'vol_ratio', 'return_skew',\n", + " 'return_kurtosis', 'volume_change_rate', 'cat_volume_breakout',\n", + " 'turnover_deviation', 'cat_turnover_spike', 'avg_volume_ratio',\n", + " 'cat_volume_ratio_breakout', 'vol_spike', 'vol_std_5', 'atr_14',\n", + " 'atr_6', 'obv'],\n", + " dtype='object')\n", + "\n", + "RangeIndex: 2425287 entries, 0 to 2425286\n", + "Columns: 137 entries, ts_code to industry_return_20_percentile\n", + "dtypes: bool(12), datetime64[ns](1), float64(119), int32(2), int64(1), object(2)\n", + "memory usage: 2.3+ GB\n", + "None\n" + ] + } + ], + "source": [ + "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", + " 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", + "df1 = filter_data(df1)\n", + "df1, _ = get_rolling_factor(df1)\n", + "df1, _ = get_simple_factor(df1)\n", + "df1 = df1.rename(columns={'l2_code': 'cat_l2_code'})\n", + "df1 = df1.merge(industry_df1, on=['cat_l2_code', 'trade_date'], how='left')\n", + "\n", + "\n", + "print(df1.info())" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "5dabff1e7bdd48c0", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-09T16:42:29.604069Z", + "start_time": "2025-04-09T16:41:39.621703Z" + }, + "jupyter": { + "source_hidden": 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", + "\n", + "RangeIndex: 4062142 entries, 0 to 4062141\n", + "Data columns (total 31 columns):\n", + " # Column Dtype \n", + "--- ------ ----- \n", + " 0 ts_code object \n", + " 1 trade_date datetime64[ns]\n", + " 2 open float64 \n", + " 3 close float64 \n", + " 4 high float64 \n", + " 5 low float64 \n", + " 6 vol float64 \n", + " 7 pct_chg float64 \n", + " 8 turnover_rate float64 \n", + " 9 pe_ttm float64 \n", + " 10 circ_mv float64 \n", + " 11 volume_ratio float64 \n", + " 12 is_st bool \n", + " 13 up_limit float64 \n", + " 14 down_limit float64 \n", + " 15 buy_sm_vol float64 \n", + " 16 sell_sm_vol float64 \n", + " 17 buy_lg_vol float64 \n", + " 18 sell_lg_vol float64 \n", + " 19 buy_elg_vol float64 \n", + " 20 sell_elg_vol float64 \n", + " 21 net_mf_vol float64 \n", + " 22 his_low float64 \n", + " 23 his_high float64 \n", + " 24 cost_5pct float64 \n", + " 25 cost_15pct float64 \n", + " 26 cost_50pct float64 \n", + " 27 cost_85pct float64 \n", + " 28 cost_95pct float64 \n", + " 29 weight_avg float64 \n", + " 30 winner_rate float64 \n", + "dtypes: bool(1), datetime64[ns](1), float64(28), object(1)\n", + "memory usage: 933.6+ MB\n", + "None\n" + ] + } + ], + "source": [ + "from code.utils.utils import read_and_merge_h5_data\n", + "\n", + "print('daily data')\n", + "df2 = 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'],\n", + " df=None)\n", + "df2 = df2[df2['trade_date'] >= '2022-01-01']\n", + "\n", + "print('daily basic')\n", + "df2 = read_and_merge_h5_data('../../data/daily_basic.h5', key='daily_basic',\n", + " columns=['ts_code', 'trade_date', 'turnover_rate', 'pe_ttm', 'circ_mv', 'volume_ratio',\n", + " 'is_st'], df=df2, join='inner')\n", + "\n", + "print('stk limit')\n", + "df2 = 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=df2)\n", + "print('money flow')\n", + "df2 = 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=df2)\n", + "print('cyq perf')\n", + "df2 = 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=df2)\n", + "print(df2.info())" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "7da9e79ee7f2eeb2", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-09T16:42:33.590224Z", + "start_time": "2025-04-09T16:42:29.605171Z" + }, + "jupyter": { + "source_hidden": true + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "industry\n" + ] + } + ], + "source": [ + "print('industry')\n", + "industry_df2 = 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", + "df2 = merge_with_industry_data(df2, industry_df2)\n", + "# print(mdf[mdf['ts_code'] == '600751.SH'][['ts_code', 'trade_date', 'l2_code']])" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "7f0830ced3ce1050", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-09T16:42:39.280494Z", + "start_time": "2025-04-09T16:42:33.613600Z" + } + }, + "outputs": [], + "source": [ + "industry_df2 = read_industry_data('../../data/sw_daily.h5')\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "ee9d7511597a312b", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-09T16:43:50.865104Z", + "start_time": "2025-04-09T16:42:39.340589Z" + }, + "jupyter": { + "source_hidden": true + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Index(['ts_code', 'trade_date', 'open', 'close', 'high', 'low', 'vol',\n", + " 'pct_chg', 'turnover_rate', 'pe_ttm', 'circ_mv', 'volume_ratio',\n", + " 'is_st', 'up_limit', 'down_limit', 'buy_sm_vol', 'sell_sm_vol',\n", + " 'buy_lg_vol', 'sell_lg_vol', 'buy_elg_vol', 'sell_elg_vol',\n", + " 'net_mf_vol', 'his_low', 'his_high', 'cost_5pct', 'cost_15pct',\n", + " 'cost_50pct', 'cost_85pct', 'cost_95pct', 'weight_avg', 'winner_rate',\n", + " 'l2_code', '_is_positive', '_is_negative', 'cat_is_positive',\n", + " '_pos_returns', '_neg_returns', '_pos_returns_sq', '_neg_returns_sq',\n", + " 'upside_vol', 'downside_vol', 'vol_ratio', 'return_skew',\n", + " 'return_kurtosis', 'volume_change_rate', 'cat_volume_breakout',\n", + " 'turnover_deviation', 'cat_turnover_spike', 'avg_volume_ratio',\n", + " 'cat_volume_ratio_breakout', 'vol_spike', 'vol_std_5', 'atr_14',\n", + " 'atr_6', 'obv'],\n", + " dtype='object')\n", + "\n", + "RangeIndex: 2431461 entries, 0 to 2431460\n", + "Columns: 137 entries, ts_code to industry_return_20_percentile\n", + "dtypes: bool(12), datetime64[ns](1), float64(119), int32(2), int64(1), object(2)\n", + "memory usage: 2.3+ GB\n", + "None\n" + ] + } + ], + "source": [ + "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", + " 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", + "df2 = filter_data(df2)\n", + "df2, _ = get_rolling_factor(df2)\n", + "df2, _ = get_simple_factor(df2)\n", + "df2 = df2.rename(columns={'l2_code': 'cat_l2_code'})\n", + "df2 = df2.merge(industry_df2, on=['cat_l2_code', 'trade_date'], how='left')\n", + "\n", + "print(df2.info())" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "4ae711775caefbe5", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-09T16:43:53.621695Z", + "start_time": "2025-04-09T16:43:50.925481Z" + } + }, + "outputs": [], + "source": [ + "# print(df1[df1['trade_date'] == '2025-04-07'][['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']].tail())\n", + "# print(df2[df2['trade_date'] == '2025-04-07'][['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']].tail())\n", + "# print(df1[df1['trade_date'] == '2025-04-07'].equals(df2[df2['trade_date'] == '2025-04-07']))\n", + "days = 2\n", + "df1 = df1.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", + "df1['future_return'] = (df1.groupby('ts_code')['close'].shift(-days) - df1.groupby('ts_code')['open'].shift(-1)) / \\\n", + " df1.groupby('ts_code')['open'].shift(-1)\n", + "df1['future_score'] = calculate_score(df1, days=2, lambda_param=0.3)\n", + "df1['label'] = df1.groupby('trade_date', group_keys=False)['future_score'].transform(\n", + " lambda x: pd.qcut(x, q=20, labels=False, duplicates='drop')\n", + ")\n", + "\n", + "df2 = df2.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", + "df2['future_return'] = (df2.groupby('ts_code')['close'].shift(-days) - df2.groupby('ts_code')['open'].shift(-1)) / \\\n", + " df2.groupby('ts_code')['open'].shift(-1)\n", + "df2['future_score'] = calculate_score(df2, days=2, lambda_param=0.3)\n", + "df2['label'] = df2.groupby('trade_date', group_keys=False)['future_score'].transform(\n", + " lambda x: pd.qcut(x, q=20, labels=False, duplicates='drop')\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "350bf91df8c3dfc2", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-09T16:43:53.723327Z", + "start_time": "2025-04-09T16:43:53.658090Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "日期: 2025-03-26\n", + "------------------------------\n", + "Slice 1 形状: (3086, 141)\n", + "Slice 2 形状: (3086, 141)\n", + "!!! 索引不同,尝试按 ts_code 对齐 !!!\n", + "------------------------------\n", + "使用 compare() 方法查找差异:\n", + "!!! 发现差异 (compare结果):\n", + "MultiIndex([('vol_std_5', 'self'),\n", + " ('vol_std_5', 'other')],\n", + " )\n", + " vol_std_5 \n", + " self other\n", + "ts_code \n", + "000004.SZ 1.076957 1.076957\n", + "000006.SZ 1.228637 1.228637\n", + "000007.SZ 0.533913 0.533913\n", + "000008.SZ 0.368086 0.368086\n", + "000009.SZ 0.393264 0.393264\n", + "... ... ...\n", + "605580.SH 1.164645 1.164645\n", + "605588.SH 0.314876 0.314876\n", + "605589.SH 0.562543 0.562543\n", + "605598.SH 1.057029 1.057029\n", + "605599.SH 0.193314 0.193314\n", + "\n", + "[3001 rows x 2 columns]\n", + "\n", + "存在差异的列: ['vol_std_5']\n" + ] + } + ], + "source": [ + "# 假设 slice1 和 slice2 已经获取,并且索引和列已对齐\n", + "# (如果索引或列不同,需要先用 .sort_index() 或 .sort_index(axis=1) 对齐)\n", + "# 假设 pdf1 和 pdf2 已经是处理到最后一步的结果\n", + "date_to_compare = '2025-03-26'\n", + "\n", + "# 1. 获取两个 DataFrame 在该日期的切片\n", + "slice1 = df1[df1['trade_date'] == date_to_compare]\n", + "slice2 = df2[df2['trade_date'] == date_to_compare]\n", + "\n", + "def get_diff(slice1, slice2):\n", + " print(f\"日期: {date_to_compare}\")\n", + " print(\"-\" * 30)\n", + " print(f\"Slice 1 形状: {slice1.shape}\")\n", + " print(f\"Slice 2 形状: {slice2.shape}\")\n", + " if slice1.shape != slice2.shape:\n", + " print(\"!!! 形状不同 !!!\")\n", + "\n", + " if not slice1.index.equals(slice2.index):\n", + " print(\"!!! 索引不同,尝试按 ts_code 对齐 !!!\")\n", + " try:\n", + " slice1 = slice1.set_index('ts_code').sort_index()\n", + " slice2 = slice2.set_index('ts_code').sort_index()\n", + " except KeyError:\n", + " print(\"错误:无法按 ts_code 设置索引,请确保该列存在。\")\n", + " # 或者尝试其他对齐方式,例如 reset_index\n", + " # slice1 = slice1.reset_index(drop=True)\n", + " # slice2 = slice2.reset_index(drop=True)\n", + "\n", + " if not slice1.columns.equals(slice2.columns):\n", + " print(\"!!! 列名或顺序不同,尝试按列名排序对齐 !!!\")\n", + " slice1 = slice1.sort_index(axis=1)\n", + " slice2 = slice2.sort_index(axis=1)\n", + "\n", + " # 再次检查对齐情况\n", + " if slice1.index.equals(slice2.index) and slice1.columns.equals(slice2.columns):\n", + " print(\"-\" * 30)\n", + " print(\"使用 compare() 方法查找差异:\")\n", + " try:\n", + " # compare 会返回一个显示差异的 DataFrame\n", + " # self 列显示 slice1 的值,other 列显示 slice2 的值\n", + " diff_compare = slice1.compare(slice2)\n", + "\n", + " if diff_compare.empty:\n", + " print(\"使用 compare() 未发现差异。\")\n", + " # 如果 compare 为空但 equals 仍为 False, 可能是非常细微的浮点差异或类型差异\n", + " # 可以再检查一下dtypes\n", + " if not slice1.dtypes.equals(slice2.dtypes):\n", + " print(\"!!! 发现数据类型 (dtypes) 不同 !!!\")\n", + " print(slice1.dtypes[slice1.dtypes != slice2.dtypes])\n", + " print(slice2.dtypes[slice1.dtypes != slice2.dtypes])\n", + "\n", + " else:\n", + " print(\"!!! 发现差异 (compare结果):\")\n", + " # 默认情况下,compare 的列是 MultiIndex ('列名', 'self'/'other')\n", + " # 为了更清晰地显示,可以调整一下格式\n", + " # diff_compare.columns = ['_'.join(col) for col in diff_compare.columns]\n", + " print(diff_compare.columns)\n", + " print(diff_compare[diff_compare[('vol_std_5', 'self')] != diff_compare[('vol_std_5', 'other')]]) # 打印差异的头部\n", + "\n", + " # 找出哪些列存在差异\n", + " differing_columns = diff_compare.columns.get_level_values(0).unique().tolist()\n", + " print(f\"\\n存在差异的列: {differing_columns}\")\n", + "\n", + " except Exception as e:\n", + " print(f\"使用 compare() 时出错: {e}\")\n", + " else:\n", + " print(\"-\" * 30)\n", + " print(\"索引或列在对齐后仍然不匹配,无法使用 compare()。请检查对齐逻辑。\")\n", + "\n", + "get_diff(slice1, slice2)\n", + "# print(df1['trade_date'].unique().tolist()[-5:])" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "9df2781fc6c7ae44", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-09T16:43:55.223316Z", + "start_time": "2025-04-09T16:43:53.868461Z" + }, + "jupyter": { + "source_hidden": true + } + }, + "outputs": [], + "source": [ + "import pandas as pd\n", + "\n", + "from scipy.stats import ks_2samp, wasserstein_distance\n", + "\n", + "\n", + "def remove_shifted_features(train_data, feature_columns, ks_threshold=0.05, wasserstein_threshold=0.1, size=0.8,\n", + " log=True):\n", + " dropped_features = []\n", + "\n", + " all_dates = sorted(train_data['trade_date'].unique().tolist()) # 获取所有唯一的 trade_date\n", + " split_date = all_dates[int(len(all_dates) * size)] # 划分点为倒数第 validation_days 天\n", + " train_data_split = train_data[train_data['trade_date'] < split_date] # 训练集\n", + " val_data_split = train_data[train_data['trade_date'] >= split_date] # 验证集\n", + "\n", + " # **统计数据漂移**\n", + " numeric_columns = train_data_split.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_split[feature], val_data_split[feature])\n", + " wasserstein_dist = wasserstein_distance(train_data_split[feature], val_data_split[feature])\n", + "\n", + " if p_value < ks_threshold or wasserstein_dist > wasserstein_threshold:\n", + " dropped_features.append(feature)\n", + " if log:\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", + "\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(df, features, industry_col, mkt_cap_col):\n", + " \"\"\" 手动实现简单回归以提升速度 \"\"\"\n", + "\n", + " for col in features:\n", + " residuals = []\n", + " for _, group in df.groupby(industry_col):\n", + " if len(group) > 1:\n", + " x = np.log(group[mkt_cap_col]) # 市值对数\n", + " y = group[col] # 因子值\n", + " beta = np.cov(y, x)[0, 1] / np.var(x) # 计算斜率\n", + " alpha = np.mean(y) - beta * np.mean(x) # 计算截距\n", + " resid = y - (alpha + beta * x) # 计算残差\n", + " residuals.extend(resid)\n", + " else:\n", + " residuals.extend(group[col]) # 样本不足时保留原值\n", + "\n", + " df[col] = residuals\n", + "\n", + " return 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(\n", + " lambda x: x.rolling(window=min(len(x), window)).quantile(lower_quantile))\n", + " rolling_upper = df.groupby('trade_date')[col].transform(\n", + " 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 time_series_quantile_filter(df, features, lower_quantile=0.01, upper_quantile=0.99, window=60):\n", + " df = df.copy()\n", + " # 确保按股票和时间排序\n", + " df = df.sort_values(['ts_code', 'trade_date'])\n", + " grouped = df.groupby('ts_code')\n", + " for col in features:\n", + " # 对每个股票的时间序列计算滚动分位数\n", + " rolling_lower = grouped[col].rolling(window=window, min_periods=window // 2).quantile(lower_quantile)\n", + " rolling_upper = grouped[col].rolling(window=window, min_periods=window // 2).quantile(upper_quantile)\n", + " # rolling结果带有多重索引,需要对齐\n", + " rolling_lower = rolling_lower.reset_index(level=0, drop=True)\n", + " rolling_upper = rolling_upper.reset_index(level=0, drop=True)\n", + " # 应用 clip\n", + " df[col] = np.clip(df[col], rolling_lower, rolling_upper)\n", + " return df\n", + "\n", + "def cross_sectional_quantile_filter(df, features, lower_quantile=0.01, upper_quantile=0.99):\n", + " df = df.copy()\n", + " grouped = df.groupby('trade_date')\n", + " for col in features:\n", + " # 计算每日截面的分位数边界\n", + " lower_bound = grouped[col].transform(lambda x: x.quantile(lower_quantile))\n", + " upper_bound = grouped[col].transform(lambda x: x.quantile(upper_quantile))\n", + " # 应用 clip\n", + " df[col] = np.clip(df[col], lower_bound, upper_bound)\n", + " return df" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "99f677aca6a286d0", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-09T16:54:07.250024Z", + "start_time": "2025-04-09T16:53:57.299050Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[Timestamp('2025-04-01 00:00:00')] 19 [19. 0. 5. 2. 1. 6. 10. 18. 4. 12. 17. 16. 11. 8. 15. 7. 14. 9.\n", + " 13.]\n", + "[Timestamp('2025-04-02 00:00:00')] 19 [18. 19. 1. 0. 3. 7. 17. 10. 16. 5. 9. 15. 8. 6. 4. 13. 2. 11.\n", + " 14.]\n", + "[Timestamp('2025-04-03 00:00:00')] 0 [nan]\n", + "[Timestamp('2025-04-07 00:00:00')] 0 [nan]\n", + "2025-04-07 00:00:00\n", + "[Timestamp('2025-04-01 00:00:00')] 19 [19. 0. 5. 2. 1. 6. 10. 18. 4. 12. 17. 16. 11. 8. 15. 7. 14. 9.\n", + " 13.]\n", + "[Timestamp('2025-04-02 00:00:00')] 19 [18. 19. 1. 0. 3. 7. 17. 10. 16. 5. 9. 15. 8. 6. 4. 13. 2. 11.\n", + " 14.]\n", + "[Timestamp('2025-04-03 00:00:00')] 19 [ 2. 15. 19. 0. 1. 5. 18. 17. 4. 6. 16. 8. 13. 14. 9. 7. 12. 11.\n", + " 3.]\n", + "[Timestamp('2025-04-07 00:00:00')] 19 [ 0. 18. 4. 17. 1. 19. 9. 13. 7. 5. 2. 16. 15. 6. 12. 11. 3. 14.\n", + " 8.]\n", + "[Timestamp('2025-04-08 00:00:00')] 0 [nan]\n", + "[Timestamp('2025-04-09 00:00:00')] 0 [nan]\n", + "2025-04-09 00:00:00\n", + "日期: 2025-04-07\n", + "------------------------------\n", + "Slice 1 形状: (100, 159)\n", + "Slice 2 形状: (110, 159)\n", + "!!! 形状不同 !!!\n", + "!!! 索引不同,尝试按 ts_code 对齐 !!!\n", + "------------------------------\n", + "索引或列在对齐后仍然不匹配,无法使用 compare()。请检查对齐逻辑。\n" + ] + } + ], + "source": [ + "def get_pdf(df, industry_df):\n", + " 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 'cyq' not in col]\n", + "\n", + " days = 2\n", + " # df = df.sort_values(by=['ts_code', 'trade_date'])\n", + " # # df['future_return'] = df.groupby('ts_code', group_keys=False)['close'].apply(lambda x: x.shift(-days) / x - 1)\n", + " # df['future_return'] = (df.groupby('ts_code')['close'].shift(-days) - df.groupby('ts_code')['open'].shift(-1)) / \\\n", + " # df.groupby('ts_code')['open'].shift(-1)\n", + " # df['future_score'] = calculate_score(df, days=2, lambda_param=0.3)\n", + " # df['label'] = df.groupby('trade_date', group_keys=False)['future_score'].transform(\n", + " # lambda x: pd.qcut(x, q=20, labels=False, duplicates='drop')\n", + " # )\n", + " # df['label'] = df.groupby('trade_date', group_keys=False)['future_score'].transform(\n", + " # lambda x: pd.qcut(x.rank(method='first'), q=20, labels=False, duplicates='raise')\n", + " # )\n", + " # df['future_score'] = (\n", + " # 0.7 * df['future_return']\n", + " # * 0.3 * df['future_volatility']\n", + " # )\n", + "\n", + " def select_pre_zt_stocks_dynamic(stock_df):\n", + " def select_stocks(group):\n", + " max_stocks = 150\n", + " initial_data = group.nlargest(100, 'return_20')\n", + " unique_labels = initial_data['label'].nunique()\n", + "\n", + " print(group['trade_date'].unique().tolist(), initial_data['label'].nunique(), initial_data['label'].unique())\n", + " if unique_labels >= 20 or unique_labels == 0: # 包含标签种类为0的情况\n", + " return initial_data\n", + "\n", + " for i in range(110, max_stocks + 1, 10):\n", + " data = group.nlargest(i, 'return_20')\n", + " unique_labels = data['label'].nunique()\n", + " if unique_labels >= 20:\n", + " return data\n", + "\n", + " return group.nlargest(max_stocks, 'return_20') # 如果循环结束仍未找到足够标签,则返回最大数量的股票\n", + "\n", + " stock_df = stock_df.groupby('trade_date', group_keys=False).apply(select_stocks)\n", + " return stock_df\n", + "\n", + "\n", + " pdf = select_pre_zt_stocks_dynamic(df[(df['trade_date'] >= '2022-01-01') & (df['trade_date'] <= '2029-04-07')])\n", + " print(pdf['trade_date'].max())\n", + "\n", + " pdf = pdf.merge(industry_df, on=['cat_l2_code', 'trade_date'], how='left')\n", + " pdf = pdf.replace([np.inf, -np.inf], np.nan)\n", + "\n", + " feature_columns = [col for col in pdf.columns if col in pdf.columns]\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 '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 'cat_l2_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", + "\n", + " numeric_columns = pdf.select_dtypes(include=['float64', 'int64']).columns\n", + " numeric_columns = [col for col in numeric_columns if col in feature_columns]\n", + "\n", + " pdf = cross_sectional_quantile_filter(pdf, numeric_columns)\n", + " # pdf = cross_sectional_standardization(pdf, numeric_columns)\n", + "\n", + " pdf = pdf.sort_values(by=['ts_code', 'trade_date'])\n", + "\n", + " filter_index = pdf['future_return'].between(pdf['future_return'].quantile(0.01), pdf['future_return'].quantile(0.99))\n", + "\n", + " feature_columns = remove_highly_correlated_features(pdf, feature_columns)\n", + "\n", + " return pdf, feature_columns, filter_index\n", + "\n", + "pdf1, feature_columns1, filter_index1 = get_pdf(df1[df1['trade_date'] >= '2025-04-01'], industry_df1)\n", + "pdf2, feature_columns2, filter_index2 = get_pdf(df2[df2['trade_date'] >= '2025-04-01'], industry_df2)\n", + "\n", + "# date_to_compare = '2025-04-07'\n", + "slice1 = pdf1[pdf1['trade_date'] == date_to_compare]\n", + "slice2 = pdf2[pdf2['trade_date'] == date_to_compare]\n", + "get_diff(slice1, slice2)" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "1b863e4115252d2d", + "metadata": { + "jupyter": { + "source_hidden": true + } + }, + "outputs": [], + "source": [ + "from sklearn.preprocessing import StandardScaler\n", + "import lightgbm as lgb\n", + "import matplotlib.pyplot as plt\n", + "\n", + "def train_light_model(train_data_df, params, feature_columns, callbacks, evals,\n", + " print_feature_importance=True, num_boost_round=100,\n", + " validation_days=180, use_pca=False, split_date=None): # 新增参数:validation_days\n", + " # 确保数据按时间排序\n", + " train_data_df = train_data_df.sort_values(by='trade_date')\n", + "\n", + " numeric_columns = train_data_df.select_dtypes(include=['float64', 'int64']).columns\n", + " numeric_columns = [col for col in numeric_columns if col in feature_columns]\n", + " # X_train.loc[:, numeric_columns] = scaler.fit_transform(X_train[numeric_columns])\n", + " # X_val.loc[:, numeric_columns] = scaler.transform(X_val[numeric_columns])\n", + " # train_data_df = cross_sectional_standardization(train_data_df, numeric_columns)\n", + "\n", + " # 去除标签为空的样本\n", + " train_data_df = train_data_df.dropna(subset=['label'])\n", + " # print('原始训练集大小: ', len(train_data_df))\n", + "\n", + " # 按时间顺序划分训练集和验证集\n", + " if split_date is None:\n", + " all_dates = train_data_df['trade_date'].unique() # 获取所有唯一的 trade_date\n", + " if validation_days == 0:\n", + " split_date = all_dates[-1]\n", + " else:\n", + " split_date = all_dates[-validation_days] # 划分点为倒数第 validation_days 天\n", + " if validation_days == 0:\n", + " train_data_split = train_data_df\n", + " else:\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", + " # 打印划分结果\n", + " print(f\"划分后的训练集大小: {len(train_data_split)}, 验证集大小: {len(val_data_split)}\")\n", + "\n", + " # 提取特征和标签\n", + " X_train = train_data_split[feature_columns]\n", + " y_train = train_data_split['label']\n", + "\n", + " # 标准化数值特征\n", + " scaler = StandardScaler()\n", + "\n", + " # 计算每个 trade_date 内的样本数(LTR 需要 group 信息)\n", + " train_groups = train_data_split.groupby('trade_date').size().tolist()\n", + " val_groups = val_data_split.groupby('trade_date').size().tolist()\n", + "\n", + " # 处理类别特征\n", + " categorical_feature = [col for col in feature_columns if 'cat' in col]\n", + "\n", + " # 计算权重(基于时间)\n", + " # trade_date = train_data_split['trade_date'] # 交易日期\n", + " # weights = (trade_date - trade_date.min()).dt.days / (trade_date.max() - trade_date.min()).days + 1\n", + " # weights = train_data_split.groupby('trade_date')['std_return_5'].transform(\n", + " # lambda x: x / x.mean()\n", + " # )\n", + " ud = sorted(train_data_split[\"trade_date\"].unique().tolist())\n", + " date_weights = {date: weight * weight for date, weight in zip(ud, np.linspace(1, 10, len(ud)))}\n", + " params['weight'] = train_data_split[\"trade_date\"].map(date_weights).tolist()\n", + "\n", + " train_dataset = lgb.Dataset(\n", + " X_train, label=y_train, group=train_groups,\n", + " categorical_feature=categorical_feature\n", + " )\n", + "\n", + " if validation_days > 0:\n", + " X_val = val_data_split[feature_columns]\n", + " y_val = val_data_split['label']\n", + " val_groups = val_data_split.groupby('trade_date').size().tolist()\n", + " val_dataset = lgb.Dataset(\n", + " X_val, label=y_val, group=val_groups,\n", + " categorical_feature=categorical_feature\n", + " )\n", + " # 训练模型\n", + " model = lgb.train(\n", + " params, train_dataset, num_boost_round=num_boost_round,\n", + " valid_sets=[train_dataset, val_dataset], valid_names=['train', 'valid'],\n", + " callbacks=callbacks\n", + " )\n", + " else:\n", + " model = lgb.train(\n", + " params, train_dataset, num_boost_round=num_boost_round, callbacks=callbacks\n", + " )\n", + "\n", + " # 打印特征重要性(如果需要)\n", + " if print_feature_importance:\n", + " lgb.plot_metric(evals)\n", + " lgb.plot_importance(model, importance_type='split', max_num_features=20)\n", + " plt.show()\n", + "\n", + " return model, scaler, None\n", + "\n", + "def rolling_train_predict(df, train_days, test_days, feature_columns_origin, days=5, use_pca=False, validation_days=60,\n", + " filter_index=None, params=None):\n", + " # 1. 按照交易日期排序\n", + " unique_dates = df[df['trade_date'] >= '2020-01-01']['trade_date'].unique().tolist()\n", + " unique_dates = sorted(unique_dates)\n", + " n = len(unique_dates)\n", + "\n", + " # 2. 计算需要跳过的天数,使后续窗口对齐\n", + " extra_days = (n - train_days) % test_days\n", + " start_index = extra_days # 从此索引开始滚动\n", + "\n", + " predictions_list = []\n", + "\n", + " for start in range(start_index, n - train_days - test_days + 1, test_days):\n", + "\n", + " train_dates = unique_dates[start: start + train_days]\n", + " test_dates = unique_dates[start + train_days: start + train_days + test_days]\n", + "\n", + " # 根据日期筛选数据\n", + " # train_data = df[df['trade_date'].isin(train_dates)]\n", + " train_data = df[filter_index & df['trade_date'].isin(train_dates)]\n", + " test_data = df[df['trade_date'].isin(test_dates)]\n", + "\n", + " train_data = train_data.sort_values('trade_date')\n", + " test_data = test_data.sort_values('trade_date')\n", + "\n", + " feature_columns, _ = remove_shifted_features(train_data, feature_columns_origin, size=0.8, log=False)\n", + "\n", + " train_data = train_data.dropna(subset=feature_columns)\n", + " train_data = train_data.dropna(subset=['label'])\n", + " train_data = train_data.reset_index(drop=True)\n", + "\n", + " # print(test_data.tail())\n", + " test_data = test_data.dropna(subset=feature_columns)\n", + " # test_data = test_data.dropna(subset=['label'])\n", + " test_data = test_data.reset_index(drop=True)\n", + "\n", + " # print(len(train_data))\n", + " # print(f\"最小日期: {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['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 df.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", + " label_gain = list(range(len(train_data['label'].unique())))\n", + " label_gain = [(gain + 1) * (gain + 1) for gain in label_gain]\n", + " params['label_gain'] = label_gain\n", + "\n", + " # ud = train_data[\"trade_date\"].unique()\n", + " # date_weights = {date: weight for date, weight in zip(ud, np.linspace(1, 2, len(unique_dates)))}\n", + " # params['weight'] = train_data[\"trade_date\"].map(date_weights).tolist()\n", + "\n", + " # print(f'feature_columns: {feature_columns}')\n", + " # feature_contri = [2 if feat.startswith('act_factor') else 1 for feat in feature_columns]\n", + " # params['feature_contri'] = feature_contri\n", + " evals = {}\n", + " model, _, _ = train_light_model(train_data.dropna(subset=['label']),\n", + " params, feature_columns,\n", + " [lgb.log_evaluation(period=100),\n", + " lgb.callback.record_evaluation(evals),\n", + " # lgb.early_stopping(100, first_metric_only=True)\n", + " ], evals,\n", + " num_boost_round=100, validation_days=validation_days,\n", + " print_feature_importance=False, use_pca=False)\n", + "\n", + " score_df = test_data.copy()\n", + " score_df['score'] = model.predict(score_df[feature_columns])\n", + " score_df = score_df.loc[score_df.groupby('trade_date')['score'].idxmax()]\n", + " score_df = score_df[['trade_date', 'score', 'ts_code']]\n", + " predictions_list.append(score_df)\n", + "\n", + " final_predictions = pd.concat(predictions_list, ignore_index=True)\n", + " return final_predictions" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "ddb5b67a9852e2", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "train_data最大日期: 2022-12-07\n", + "test_data最大日期: 2022-12-08\n", + "划分后的训练集大小: 525, 验证集大小: 106\n", + "train_data最大日期: 2022-12-08\n", + "test_data最大日期: 2022-12-09\n", + "划分后的训练集大小: 531, 验证集大小: 109\n", + "train_data最大日期: 2022-12-09\n", + "test_data最大日期: 2022-12-12\n", + "划分后的训练集大小: 516, 验证集大小: 100\n", + "train_data最大日期: 2022-12-12\n", + "test_data最大日期: 2022-12-13\n", + "划分后的训练集大小: 528, 验证集大小: 108\n", + "train_data最大日期: 2022-12-13\n", + "test_data最大日期: 2022-12-14\n", + "划分后的训练集大小: 571, 验证集大小: 148\n", + "train_data最大日期: 2022-12-14\n", + "test_data最大日期: 2022-12-15\n", + "划分后的训练集大小: 565, 验证集大小: 100\n", + "train_data最大日期: 2022-12-15\n", + "test_data最大日期: 2022-12-16\n", + "划分后的训练集大小: 600, 验证集大小: 144\n", + "train_data最大日期: 2022-12-16\n", + "test_data最大日期: 2022-12-19\n", + "划分后的训练集大小: 597, 验证集大小: 97\n", + "train_data最大日期: 2022-12-19\n", + "test_data最大日期: 2022-12-20\n", + "划分后的训练集大小: 633, 验证集大小: 144\n", + "train_data最大日期: 2022-12-20\n", + "test_data最大日期: 2022-12-21\n", + "划分后的训练集大小: 627, 验证集大小: 142\n", + "train_data最大日期: 2022-12-21\n", + "test_data最大日期: 2022-12-22\n", + "划分后的训练集大小: 624, 验证集大小: 97\n", + "train_data最大日期: 2022-12-22\n", + "test_data最大日期: 2022-12-23\n", + "划分后的训练集大小: 605, 验证集大小: 125\n", + "train_data最大日期: 2022-12-23\n", + "test_data最大日期: 2022-12-26\n", + "划分后的训练集大小: 603, 验证集大小: 95\n", + "train_data最大日期: 2022-12-26\n", + "test_data最大日期: 2022-12-27\n", + "划分后的训练集大小: 558, 验证集大小: 99\n", + "train_data最大日期: 2022-12-27\n", + "test_data最大日期: 2022-12-28\n", + "划分后的训练集大小: 510, 验证集大小: 94\n", + "train_data最大日期: 2022-12-28\n", + "test_data最大日期: 2022-12-29\n", + "划分后的训练集大小: 508, 验证集大小: 95\n", + "train_data最大日期: 2022-12-29\n", + "test_data最大日期: 2022-12-30\n", + "划分后的训练集大小: 528, 验证集大小: 145\n", + "train_data最大日期: 2022-12-30\n", + "test_data最大日期: 2023-01-03\n", + "划分后的训练集大小: 570, 验证集大小: 137\n", + "train_data最大日期: 2023-01-03\n", + "test_data最大日期: 2023-01-04\n", + "划分后的训练集大小: 618, 验证集大小: 147\n", + "train_data最大日期: 2023-01-04\n", + "test_data最大日期: 2023-01-05\n", + "划分后的训练集大小: 666, 验证集大小: 142\n", + "train_data最大日期: 2023-01-05\n", + "test_data最大日期: 2023-01-06\n", + "划分后的训练集大小: 717, 验证集大小: 146\n", + "train_data最大日期: 2023-01-06\n", + "test_data最大日期: 2023-01-09\n", + "划分后的训练集大小: 670, 验证集大小: 98\n", + "train_data最大日期: 2023-01-09\n", + "test_data最大日期: 2023-01-10\n", + "划分后的训练集大小: 630, 验证集大小: 97\n", + "train_data最大日期: 2023-01-10\n", + "test_data最大日期: 2023-01-11\n", + "划分后的训练集大小: 589, 验证集大小: 106\n", + "train_data最大日期: 2023-01-11\n", + "test_data最大日期: 2023-01-12\n", + "划分后的训练集大小: 543, 验证集大小: 96\n", + "train_data最大日期: 2023-01-12\n", + "test_data最大日期: 2023-01-13\n", + "划分后的训练集大小: 544, 验证集大小: 147\n", + "train_data最大日期: 2023-01-13\n", + "test_data最大日期: 2023-01-16\n", + "划分后的训练集大小: 553, 验证集大小: 107\n", + "train_data最大日期: 2023-01-16\n", + "test_data最大日期: 2023-01-17\n", + "划分后的训练集大小: 573, 验证集大小: 117\n", + "train_data最大日期: 2023-01-17\n", + "test_data最大日期: 2023-01-18\n", + "划分后的训练集大小: 604, 验证集大小: 137\n", + "train_data最大日期: 2023-01-18\n", + "test_data最大日期: 2023-01-19\n", + "划分后的训练集大小: 625, 验证集大小: 117\n", + "train_data最大日期: 2023-01-19\n", + "test_data最大日期: 2023-01-20\n", + "划分后的训练集大小: 616, 验证集大小: 138\n", + "train_data最大日期: 2023-01-20\n", + "test_data最大日期: 2023-01-30\n", + "划分后的训练集大小: 609, 验证集大小: 100\n", + "train_data最大日期: 2023-01-30\n", + "test_data最大日期: 2023-01-31\n", + "划分后的训练集大小: 621, 验证集大小: 129\n", + "train_data最大日期: 2023-01-31\n", + "test_data最大日期: 2023-02-01\n", + "划分后的训练集大小: 584, 验证集大小: 100\n", + "train_data最大日期: 2023-02-01\n", + "test_data最大日期: 2023-02-02\n", + "划分后的训练集大小: 583, 验证集大小: 116\n", + "train_data最大日期: 2023-02-02\n", + "test_data最大日期: 2023-02-03\n", + "划分后的训练集大小: 553, 验证集大小: 108\n", + "train_data最大日期: 2023-02-03\n", + "test_data最大日期: 2023-02-06\n", + "划分后的训练集大小: 581, 验证集大小: 128\n", + "train_data最大日期: 2023-02-06\n", + "test_data最大日期: 2023-02-07\n", + "划分后的训练集大小: 572, 验证集大小: 120\n", + "train_data最大日期: 2023-02-07\n", + "test_data最大日期: 2023-02-08\n", + "划分后的训练集大小: 622, 验证集大小: 150\n", + "train_data最大日期: 2023-02-08\n", + "test_data最大日期: 2023-02-09\n", + "划分后的训练集大小: 656, 验证集大小: 150\n", + "train_data最大日期: 2023-02-09\n", + "test_data最大日期: 2023-02-10\n", + "划分后的训练集大小: 697, 验证集大小: 149\n", + "train_data最大日期: 2023-02-10\n", + "test_data最大日期: 2023-02-13\n", + "划分后的训练集大小: 698, 验证集大小: 129\n", + "train_data最大日期: 2023-02-13\n", + "test_data最大日期: 2023-02-14\n", + "划分后的训练集大小: 717, 验证集大小: 139\n", + "train_data最大日期: 2023-02-14\n", + "test_data最大日期: 2023-02-15\n", + "划分后的训练集大小: 715, 验证集大小: 148\n", + "train_data最大日期: 2023-02-15\n", + "test_data最大日期: 2023-02-16\n", + "划分后的训练集大小: 714, 验证集大小: 149\n", + "train_data最大日期: 2023-02-16\n", + "test_data最大日期: 2023-02-17\n", + "划分后的训练集大小: 713, 验证集大小: 148\n", + "train_data最大日期: 2023-02-17\n", + "test_data最大日期: 2023-02-20\n", + "划分后的训练集大小: 682, 验证集大小: 98\n", + "train_data最大日期: 2023-02-20\n", + "test_data最大日期: 2023-02-21\n", + "划分后的训练集大小: 681, 验证集大小: 138\n", + "train_data最大日期: 2023-02-21\n", + "test_data最大日期: 2023-02-22\n", + "划分后的训练集大小: 632, 验证集大小: 99\n", + "train_data最大日期: 2023-02-22\n", + "test_data最大日期: 2023-02-23\n", + "划分后的训练集大小: 619, 验证集大小: 136\n", + "train_data最大日期: 2023-02-23\n", + "test_data最大日期: 2023-02-24\n", + "划分后的训练集大小: 571, 验证集大小: 100\n", + "train_data最大日期: 2023-02-24\n", + "test_data最大日期: 2023-02-27\n", + "划分后的训练集大小: 621, 验证集大小: 148\n", + "train_data最大日期: 2023-02-27\n", + "test_data最大日期: 2023-02-28\n", + "划分后的训练集大小: 632, 验证集大小: 149\n", + "train_data最大日期: 2023-02-28\n", + "test_data最大日期: 2023-03-01\n", + "划分后的训练集大小: 632, 验证集大小: 99\n", + "train_data最大日期: 2023-03-01\n", + "test_data最大日期: 2023-03-02\n", + "划分后的训练集大小: 596, 验证集大小: 100\n", + "train_data最大日期: 2023-03-02\n", + "test_data最大日期: 2023-03-03\n", + "划分后的训练集大小: 595, 验证集大小: 99\n", + "train_data最大日期: 2023-03-03\n", + "test_data最大日期: 2023-03-06\n", + "划分后的训练集大小: 596, 验证集大小: 149\n", + "train_data最大日期: 2023-03-06\n", + "test_data最大日期: 2023-03-07\n", + "划分后的训练集大小: 547, 验证集大小: 100\n", + "train_data最大日期: 2023-03-07\n", + "test_data最大日期: 2023-03-08\n", + "划分后的训练集大小: 567, 验证集大小: 119\n", + "train_data最大日期: 2023-03-08\n", + "test_data最大日期: 2023-03-09\n", + "划分后的训练集大小: 585, 验证集大小: 118\n", + "train_data最大日期: 2023-03-09\n", + "test_data最大日期: 2023-03-10\n", + "划分后的训练集大小: 634, 验证集大小: 148\n", + "train_data最大日期: 2023-03-10\n", + "test_data最大日期: 2023-03-13\n", + "划分后的训练集大小: 630, 验证集大小: 145\n", + "train_data最大日期: 2023-03-13\n", + "test_data最大日期: 2023-03-14\n", + "划分后的训练集大小: 638, 验证集大小: 108\n", + "train_data最大日期: 2023-03-14\n", + "test_data最大日期: 2023-03-15\n", + "划分后的训练集大小: 665, 验证集大小: 146\n", + "train_data最大日期: 2023-03-15\n", + "test_data最大日期: 2023-03-16\n", + "划分后的训练集大小: 677, 验证集大小: 130\n", + "train_data最大日期: 2023-03-16\n", + "test_data最大日期: 2023-03-17\n", + "划分后的训练集大小: 678, 验证集大小: 149\n", + "train_data最大日期: 2023-03-17\n", + "test_data最大日期: 2023-03-20\n", + "划分后的训练集大小: 642, 验证集大小: 109\n", + "train_data最大日期: 2023-03-20\n", + "test_data最大日期: 2023-03-21\n", + "划分后的训练集大小: 663, 验证集大小: 129\n", + "train_data最大日期: 2023-03-21\n", + "test_data最大日期: 2023-03-22\n", + "划分后的训练集大小: 615, 验证集大小: 98\n", + "train_data最大日期: 2023-03-22\n", + "test_data最大日期: 2023-03-23\n", + "划分后的训练集大小: 633, 验证集大小: 148\n", + "train_data最大日期: 2023-03-23\n", + "test_data最大日期: 2023-03-24\n", + "划分后的训练集大小: 627, 验证集大小: 143\n", + "train_data最大日期: 2023-03-24\n", + "test_data最大日期: 2023-03-27\n", + "划分后的训练集大小: 646, 验证集大小: 128\n", + "train_data最大日期: 2023-03-27\n", + "test_data最大日期: 2023-03-28\n", + "划分后的训练集大小: 615, 验证集大小: 98\n", + "train_data最大日期: 2023-03-28\n", + "test_data最大日期: 2023-03-29\n", + "划分后的训练集大小: 644, 验证集大小: 127\n", + "train_data最大日期: 2023-03-29\n", + "test_data最大日期: 2023-03-30\n", + "划分后的训练集大小: 623, 验证集大小: 127\n", + "train_data最大日期: 2023-03-30\n", + "test_data最大日期: 2023-03-31\n", + "划分后的训练集大小: 577, 验证集大小: 97\n", + "train_data最大日期: 2023-03-31\n", + "test_data最大日期: 2023-04-03\n", + "划分后的训练集大小: 595, 验证集大小: 146\n", + "train_data最大日期: 2023-04-03\n", + "test_data最大日期: 2023-04-04\n", + "划分后的训练集大小: 644, 验证集大小: 147\n", + "train_data最大日期: 2023-04-04\n", + "test_data最大日期: 2023-04-06\n", + "划分后的训练集大小: 632, 验证集大小: 115\n", + "train_data最大日期: 2023-04-06\n", + "test_data最大日期: 2023-04-07\n", + "划分后的训练集大小: 651, 验证集大小: 146\n", + "train_data最大日期: 2023-04-07\n", + "test_data最大日期: 2023-04-10\n", + "划分后的训练集大小: 702, 验证集大小: 148\n", + "train_data最大日期: 2023-04-10\n", + "test_data最大日期: 2023-04-11\n", + "划分后的训练集大小: 701, 验证集大小: 145\n", + "train_data最大日期: 2023-04-11\n", + "test_data最大日期: 2023-04-12\n", + "划分后的训练集大小: 672, 验证集大小: 118\n", + "train_data最大日期: 2023-04-12\n", + "test_data最大日期: 2023-04-13\n", + "划分后的训练集大小: 694, 验证集大小: 137\n", + "train_data最大日期: 2023-04-13\n", + "test_data最大日期: 2023-04-14\n", + "划分后的训练集大小: 695, 验证集大小: 147\n", + "train_data最大日期: 2023-04-14\n", + "test_data最大日期: 2023-04-17\n", + "划分后的训练集大小: 684, 验证集大小: 137\n", + "train_data最大日期: 2023-04-17\n", + "test_data最大日期: 2023-04-18\n", + "划分后的训练集大小: 638, 验证集大小: 99\n", + "train_data最大日期: 2023-04-18\n", + "test_data最大日期: 2023-04-19\n", + "划分后的训练集大小: 649, 验证集大小: 129\n", + "train_data最大日期: 2023-04-19\n", + "test_data最大日期: 2023-04-20\n", + "划分后的训练集大小: 610, 验证集大小: 98\n", + "train_data最大日期: 2023-04-20\n", + "test_data最大日期: 2023-04-21\n", + "划分后的训练集大小: 611, 验证集大小: 148\n", + "train_data最大日期: 2023-04-21\n", + "test_data最大日期: 2023-04-24\n", + "划分后的训练集大小: 610, 验证集大小: 136\n", + "train_data最大日期: 2023-04-24\n", + "test_data最大日期: 2023-04-25\n", + "划分后的训练集大小: 657, 验证集大小: 146\n", + "train_data最大日期: 2023-04-25\n", + "test_data最大日期: 2023-04-26\n", + "划分后的训练集大小: 675, 验证集大小: 147\n", + "train_data最大日期: 2023-04-26\n", + "test_data最大日期: 2023-04-27\n", + "划分后的训练集大小: 677, 验证集大小: 100\n", + "train_data最大日期: 2023-04-27\n", + "test_data最大日期: 2023-04-28\n", + "划分后的训练集大小: 653, 验证集大小: 124\n", + "train_data最大日期: 2023-04-28\n", + "test_data最大日期: 2023-05-04\n", + "划分后的训练集大小: 664, 验证集大小: 147\n", + "train_data最大日期: 2023-05-04\n", + "test_data最大日期: 2023-05-05\n", + "划分后的训练集大小: 636, 验证集大小: 118\n", + "train_data最大日期: 2023-05-05\n", + "test_data最大日期: 2023-05-08\n", + "划分后的训练集大小: 637, 验证集大小: 148\n", + "train_data最大日期: 2023-05-08\n", + "test_data最大日期: 2023-05-09\n", + "划分后的训练集大小: 685, 验证集大小: 148\n", + "train_data最大日期: 2023-05-09\n", + "test_data最大日期: 2023-05-10\n", + "划分后的训练集大小: 658, 验证集大小: 97\n", + "train_data最大日期: 2023-05-10\n", + "test_data最大日期: 2023-05-11\n", + "划分后的训练集大小: 638, 验证集大小: 127\n", + "train_data最大日期: 2023-05-11\n", + "test_data最大日期: 2023-05-12\n", + "划分后的训练集大小: 666, 验证集大小: 146\n", + "train_data最大日期: 2023-05-12\n", + "test_data最大日期: 2023-05-15\n", + "划分后的训练集大小: 664, 验证集大小: 146\n", + "train_data最大日期: 2023-05-15\n", + "test_data最大日期: 2023-05-16\n", + "划分后的训练集大小: 621, 验证集大小: 105\n", + "train_data最大日期: 2023-05-16\n", + "test_data最大日期: 2023-05-17\n", + "划分后的训练集大小: 623, 验证集大小: 99\n", + "train_data最大日期: 2023-05-17\n", + "test_data最大日期: 2023-05-18\n", + "划分后的训练集大小: 606, 验证集大小: 110\n", + "train_data最大日期: 2023-05-18\n", + "test_data最大日期: 2023-05-19\n", + "划分后的训练集大小: 578, 验证集大小: 118\n", + "train_data最大日期: 2023-05-19\n", + "test_data最大日期: 2023-05-22\n", + "划分后的训练集大小: 540, 验证集大小: 108\n", + "train_data最大日期: 2023-05-22\n", + "test_data最大日期: 2023-05-23\n", + "划分后的训练集大小: 532, 验证集大小: 97\n", + "train_data最大日期: 2023-05-23\n", + "test_data最大日期: 2023-05-24\n", + "划分后的训练集大小: 559, 验证集大小: 126\n", + "train_data最大日期: 2023-05-24\n", + "test_data最大日期: 2023-05-25\n", + "划分后的训练集大小: 548, 验证集大小: 99\n", + "train_data最大日期: 2023-05-25\n", + "test_data最大日期: 2023-05-26\n", + "划分后的训练集大小: 526, 验证集大小: 96\n", + "train_data最大日期: 2023-05-26\n", + "test_data最大日期: 2023-05-29\n", + "划分后的训练集大小: 516, 验证集大小: 98\n", + "train_data最大日期: 2023-05-29\n", + "test_data最大日期: 2023-05-30\n", + "划分后的训练集大小: 527, 验证集大小: 108\n", + "train_data最大日期: 2023-05-30\n", + "test_data最大日期: 2023-05-31\n", + "划分后的训练集大小: 546, 验证集大小: 145\n", + "train_data最大日期: 2023-05-31\n", + "test_data最大日期: 2023-06-01\n", + "划分后的训练集大小: 594, 验证集大小: 147\n", + "train_data最大日期: 2023-06-01\n", + "test_data最大日期: 2023-06-02\n", + "划分后的训练集大小: 616, 验证集大小: 118\n", + "train_data最大日期: 2023-06-02\n", + "test_data最大日期: 2023-06-05\n", + "划分后的训练集大小: 666, 验证集大小: 148\n", + "train_data最大日期: 2023-06-05\n", + "test_data最大日期: 2023-06-06\n", + "划分后的训练集大小: 676, 验证集大小: 118\n", + "train_data最大日期: 2023-06-06\n", + "test_data最大日期: 2023-06-07\n", + "划分后的训练集大小: 626, 验证集大小: 95\n", + "train_data最大日期: 2023-06-07\n", + "test_data最大日期: 2023-06-08\n", + "划分后的训练集大小: 626, 验证集大小: 147\n", + "train_data最大日期: 2023-06-08\n", + "test_data最大日期: 2023-06-09\n", + "划分后的训练集大小: 606, 验证集大小: 98\n", + "train_data最大日期: 2023-06-09\n", + "test_data最大日期: 2023-06-12\n", + "划分后的训练集大小: 558, 验证集大小: 100\n", + "train_data最大日期: 2023-06-12\n", + "test_data最大日期: 2023-06-13\n", + "划分后的训练集大小: 579, 验证集大小: 139\n", + "train_data最大日期: 2023-06-13\n", + "test_data最大日期: 2023-06-14\n", + "划分后的训练集大小: 630, 验证集大小: 146\n" + ] + }, + { + "ename": "LightGBMError", + "evalue": "Forced splits file includes feature index 0, but maximum feature index in dataset is -1", + "output_type": "error", + "traceback": [ + "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[1;31mLightGBMError\u001B[0m Traceback (most recent call last)", + "Cell \u001B[1;32mIn[36], line 38\u001B[0m\n\u001B[0;32m 34\u001B[0m final_predictions\u001B[38;5;241m.\u001B[39mto_csv(\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mpredictions_test.tsv\u001B[39m\u001B[38;5;124m'\u001B[39m, index\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mFalse\u001B[39;00m)\n\u001B[0;32m 36\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m final_predictions\n\u001B[1;32m---> 38\u001B[0m final_predictions1 \u001B[38;5;241m=\u001B[39m train(pdf1, feature_columns1, filter_index1)\n\u001B[0;32m 39\u001B[0m final_predictions2 \u001B[38;5;241m=\u001B[39m train(pdf2, feature_columns2, filter_index2)\n", + "Cell \u001B[1;32mIn[36], line 31\u001B[0m, in \u001B[0;36mtrain\u001B[1;34m(pdf, feature_columns, filter_index)\u001B[0m\n\u001B[0;32m 4\u001B[0m light_params \u001B[38;5;241m=\u001B[39m {\n\u001B[0;32m 5\u001B[0m \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mlabel_gain\u001B[39m\u001B[38;5;124m'\u001B[39m: label_gain,\n\u001B[0;32m 6\u001B[0m \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mobjective\u001B[39m\u001B[38;5;124m'\u001B[39m: \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mlambdarank\u001B[39m\u001B[38;5;124m'\u001B[39m,\n\u001B[1;32m (...)\u001B[0m\n\u001B[0;32m 26\u001B[0m \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mseed\u001B[39m\u001B[38;5;124m'\u001B[39m: \u001B[38;5;241m7\u001B[39m\n\u001B[0;32m 27\u001B[0m }\n\u001B[0;32m 29\u001B[0m gc\u001B[38;5;241m.\u001B[39mcollect()\n\u001B[1;32m---> 31\u001B[0m final_predictions \u001B[38;5;241m=\u001B[39m rolling_train_predict(\n\u001B[0;32m 32\u001B[0m pdf[(pdf[\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mtrade_date\u001B[39m\u001B[38;5;124m'\u001B[39m] \u001B[38;5;241m>\u001B[39m\u001B[38;5;241m=\u001B[39m \u001B[38;5;124m'\u001B[39m\u001B[38;5;124m2022-12-01\u001B[39m\u001B[38;5;124m'\u001B[39m) \u001B[38;5;241m&\u001B[39m (pdf[\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mtrade_date\u001B[39m\u001B[38;5;124m'\u001B[39m] \u001B[38;5;241m<\u001B[39m\u001B[38;5;241m=\u001B[39m \u001B[38;5;124m'\u001B[39m\u001B[38;5;124m2029-03-26\u001B[39m\u001B[38;5;124m'\u001B[39m)], \u001B[38;5;241m5\u001B[39m, \u001B[38;5;241m1\u001B[39m, feature_columns,\n\u001B[0;32m 33\u001B[0m days\u001B[38;5;241m=\u001B[39m\u001B[38;5;241m0\u001B[39m, validation_days\u001B[38;5;241m=\u001B[39m\u001B[38;5;241m0\u001B[39m, filter_index\u001B[38;5;241m=\u001B[39mfilter_index, params\u001B[38;5;241m=\u001B[39mlight_params)\n\u001B[0;32m 34\u001B[0m final_predictions\u001B[38;5;241m.\u001B[39mto_csv(\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mpredictions_test.tsv\u001B[39m\u001B[38;5;124m'\u001B[39m, index\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mFalse\u001B[39;00m)\n\u001B[0;32m 36\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m final_predictions\n", + "Cell \u001B[1;32mIn[33], line 154\u001B[0m, in \u001B[0;36mrolling_train_predict\u001B[1;34m(df, train_days, test_days, feature_columns_origin, days, use_pca, validation_days, filter_index, params)\u001B[0m\n\u001B[0;32m 146\u001B[0m \u001B[38;5;66;03m# ud = train_data[\"trade_date\"].unique()\u001B[39;00m\n\u001B[0;32m 147\u001B[0m \u001B[38;5;66;03m# date_weights = {date: weight for date, weight in zip(ud, np.linspace(1, 2, len(unique_dates)))}\u001B[39;00m\n\u001B[0;32m 148\u001B[0m \u001B[38;5;66;03m# params['weight'] = train_data[\"trade_date\"].map(date_weights).tolist()\u001B[39;00m\n\u001B[1;32m (...)\u001B[0m\n\u001B[0;32m 151\u001B[0m \u001B[38;5;66;03m# feature_contri = [2 if feat.startswith('act_factor') else 1 for feat in feature_columns]\u001B[39;00m\n\u001B[0;32m 152\u001B[0m \u001B[38;5;66;03m# params['feature_contri'] = feature_contri\u001B[39;00m\n\u001B[0;32m 153\u001B[0m evals \u001B[38;5;241m=\u001B[39m {}\n\u001B[1;32m--> 154\u001B[0m model, _, _ \u001B[38;5;241m=\u001B[39m train_light_model(train_data\u001B[38;5;241m.\u001B[39mdropna(subset\u001B[38;5;241m=\u001B[39m[\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mlabel\u001B[39m\u001B[38;5;124m'\u001B[39m]),\n\u001B[0;32m 155\u001B[0m params, feature_columns,\n\u001B[0;32m 156\u001B[0m [lgb\u001B[38;5;241m.\u001B[39mlog_evaluation(period\u001B[38;5;241m=\u001B[39m\u001B[38;5;241m100\u001B[39m),\n\u001B[0;32m 157\u001B[0m lgb\u001B[38;5;241m.\u001B[39mcallback\u001B[38;5;241m.\u001B[39mrecord_evaluation(evals),\n\u001B[0;32m 158\u001B[0m \u001B[38;5;66;03m# lgb.early_stopping(100, first_metric_only=True)\u001B[39;00m\n\u001B[0;32m 159\u001B[0m ], evals,\n\u001B[0;32m 160\u001B[0m num_boost_round\u001B[38;5;241m=\u001B[39m\u001B[38;5;241m100\u001B[39m, validation_days\u001B[38;5;241m=\u001B[39mvalidation_days,\n\u001B[0;32m 161\u001B[0m print_feature_importance\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mFalse\u001B[39;00m, use_pca\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mFalse\u001B[39;00m)\n\u001B[0;32m 163\u001B[0m score_df \u001B[38;5;241m=\u001B[39m test_data\u001B[38;5;241m.\u001B[39mcopy()\n\u001B[0;32m 164\u001B[0m score_df[\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mscore\u001B[39m\u001B[38;5;124m'\u001B[39m] \u001B[38;5;241m=\u001B[39m model\u001B[38;5;241m.\u001B[39mpredict(score_df[feature_columns])\n", + "Cell \u001B[1;32mIn[33], line 81\u001B[0m, in \u001B[0;36mtrain_light_model\u001B[1;34m(train_data_df, params, feature_columns, callbacks, evals, print_feature_importance, num_boost_round, validation_days, use_pca, split_date)\u001B[0m\n\u001B[0;32m 75\u001B[0m model \u001B[38;5;241m=\u001B[39m lgb\u001B[38;5;241m.\u001B[39mtrain(\n\u001B[0;32m 76\u001B[0m params, train_dataset, num_boost_round\u001B[38;5;241m=\u001B[39mnum_boost_round,\n\u001B[0;32m 77\u001B[0m valid_sets\u001B[38;5;241m=\u001B[39m[train_dataset, val_dataset], valid_names\u001B[38;5;241m=\u001B[39m[\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mtrain\u001B[39m\u001B[38;5;124m'\u001B[39m, \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mvalid\u001B[39m\u001B[38;5;124m'\u001B[39m],\n\u001B[0;32m 78\u001B[0m callbacks\u001B[38;5;241m=\u001B[39mcallbacks\n\u001B[0;32m 79\u001B[0m )\n\u001B[0;32m 80\u001B[0m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[1;32m---> 81\u001B[0m model \u001B[38;5;241m=\u001B[39m lgb\u001B[38;5;241m.\u001B[39mtrain(\n\u001B[0;32m 82\u001B[0m params, train_dataset, num_boost_round\u001B[38;5;241m=\u001B[39mnum_boost_round, callbacks\u001B[38;5;241m=\u001B[39mcallbacks\n\u001B[0;32m 83\u001B[0m )\n\u001B[0;32m 85\u001B[0m \u001B[38;5;66;03m# 打印特征重要性(如果需要)\u001B[39;00m\n\u001B[0;32m 86\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m print_feature_importance:\n", + "File \u001B[1;32mE:\\Python\\anaconda\\envs\\new_trader\\Lib\\site-packages\\lightgbm\\engine.py:297\u001B[0m, in \u001B[0;36mtrain\u001B[1;34m(params, train_set, num_boost_round, valid_sets, valid_names, feval, init_model, keep_training_booster, callbacks)\u001B[0m\n\u001B[0;32m 295\u001B[0m \u001B[38;5;66;03m# construct booster\u001B[39;00m\n\u001B[0;32m 296\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[1;32m--> 297\u001B[0m booster \u001B[38;5;241m=\u001B[39m Booster(params\u001B[38;5;241m=\u001B[39mparams, train_set\u001B[38;5;241m=\u001B[39mtrain_set)\n\u001B[0;32m 298\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m is_valid_contain_train:\n\u001B[0;32m 299\u001B[0m booster\u001B[38;5;241m.\u001B[39mset_train_data_name(train_data_name)\n", + "File \u001B[1;32mE:\\Python\\anaconda\\envs\\new_trader\\Lib\\site-packages\\lightgbm\\basic.py:3660\u001B[0m, in \u001B[0;36mBooster.__init__\u001B[1;34m(self, params, train_set, model_file, model_str)\u001B[0m\n\u001B[0;32m 3658\u001B[0m params\u001B[38;5;241m.\u001B[39mupdate(train_set\u001B[38;5;241m.\u001B[39mget_params())\n\u001B[0;32m 3659\u001B[0m params_str \u001B[38;5;241m=\u001B[39m _param_dict_to_str(params)\n\u001B[1;32m-> 3660\u001B[0m _safe_call(\n\u001B[0;32m 3661\u001B[0m _LIB\u001B[38;5;241m.\u001B[39mLGBM_BoosterCreate(\n\u001B[0;32m 3662\u001B[0m train_set\u001B[38;5;241m.\u001B[39m_handle,\n\u001B[0;32m 3663\u001B[0m _c_str(params_str),\n\u001B[0;32m 3664\u001B[0m ctypes\u001B[38;5;241m.\u001B[39mbyref(\u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_handle),\n\u001B[0;32m 3665\u001B[0m )\n\u001B[0;32m 3666\u001B[0m )\n\u001B[0;32m 3667\u001B[0m \u001B[38;5;66;03m# save reference to data\u001B[39;00m\n\u001B[0;32m 3668\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mtrain_set \u001B[38;5;241m=\u001B[39m train_set\n", + "File \u001B[1;32mE:\\Python\\anaconda\\envs\\new_trader\\Lib\\site-packages\\lightgbm\\basic.py:313\u001B[0m, in \u001B[0;36m_safe_call\u001B[1;34m(ret)\u001B[0m\n\u001B[0;32m 305\u001B[0m \u001B[38;5;250m\u001B[39m\u001B[38;5;124;03m\"\"\"Check the return value from C API call.\u001B[39;00m\n\u001B[0;32m 306\u001B[0m \n\u001B[0;32m 307\u001B[0m \u001B[38;5;124;03mParameters\u001B[39;00m\n\u001B[1;32m (...)\u001B[0m\n\u001B[0;32m 310\u001B[0m \u001B[38;5;124;03m The return value from C API calls.\u001B[39;00m\n\u001B[0;32m 311\u001B[0m \u001B[38;5;124;03m\"\"\"\u001B[39;00m\n\u001B[0;32m 312\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m ret \u001B[38;5;241m!=\u001B[39m \u001B[38;5;241m0\u001B[39m:\n\u001B[1;32m--> 313\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m LightGBMError(_LIB\u001B[38;5;241m.\u001B[39mLGBM_GetLastError()\u001B[38;5;241m.\u001B[39mdecode(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mutf-8\u001B[39m\u001B[38;5;124m\"\u001B[39m))\n", + "\u001B[1;31mLightGBMError\u001B[0m: Forced splits file includes feature index 0, but maximum feature index in dataset is -1" + ] + } + ], + "source": [ + "\n", + "\n", + "def train(pdf, feature_columns, filter_index):\n", + " label_gain = list(range(len(pdf['label'].unique())))\n", + " label_gain = [gain * gain for gain in label_gain]\n", + " light_params = {\n", + " 'label_gain': label_gain,\n", + " 'objective': 'lambdarank',\n", + " 'metric': 'ndcg',\n", + " 'learning_rate': 0.03,\n", + " 'num_leaves': 32,\n", + " # 'min_data_in_leaf': 128,\n", + " 'max_depth': 8,\n", + " 'max_bin': 32,\n", + " 'feature_fraction': 0.7,\n", + " # 'bagging_fraction': 0.7,\n", + " 'bagging_freq': 5,\n", + " 'lambda_l1': 0.1,\n", + " 'lambda_l2': 0.1,\n", + " 'boosting': 'gbdt',\n", + " 'verbosity': -1,\n", + " 'extra_trees': True,\n", + " 'max_position': 5,\n", + " 'ndcg_at': 1,\n", + " 'quant_train_renew_leaf': True,\n", + " 'lambdarank_truncation_level': 3,\n", + " # 'lambdarank_position_bias_regularization': 1,\n", + " 'seed': 7\n", + " }\n", + "\n", + " gc.collect()\n", + "\n", + " final_predictions = rolling_train_predict(\n", + " pdf[(pdf['trade_date'] >= '2022-12-01') & (pdf['trade_date'] <= '2029-03-26')], 5, 1, feature_columns,\n", + " days=0, validation_days=0, filter_index=filter_index, params=light_params)\n", + " final_predictions.to_csv('predictions_test.tsv', index=False)\n", + "\n", + " return final_predictions\n", + "\n", + "final_predictions1 = train(pdf1, feature_columns1, filter_index1)\n", + "final_predictions2 = train(pdf2, feature_columns2, filter_index2)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e7e470a2-e1e5-42e5-a2ee-a5fc80455d95", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "slice1 = final_predictions1[final_predictions1['trade_date'] == date_to_compare]\n", + "slice2 = final_predictions2[final_predictions2['trade_date'] == date_to_compare]\n", + "get_diff(slice1, slice2)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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 +} diff --git a/code/train/Classify.ipynb b/main/train/Classify.ipynb similarity index 99% rename from code/train/Classify.ipynb rename to main/train/Classify.ipynb index bbd3771..b789116 100644 --- a/code/train/Classify.ipynb +++ b/main/train/Classify.ipynb @@ -36,7 +36,7 @@ } }, "source": [ - "from utils.utils import read_and_merge_h5_data\n", + "from code.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", diff --git a/code/train/ClassifyLR.ipynb b/main/train/ClassifyLR.ipynb similarity index 99% rename from code/train/ClassifyLR.ipynb rename to main/train/ClassifyLR.ipynb index df5178e..1219056 100644 --- a/code/train/ClassifyLR.ipynb +++ b/main/train/ClassifyLR.ipynb @@ -75,7 +75,7 @@ } ], "source": [ - "from utils.utils import read_and_merge_h5_data\n", + "from code.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", diff --git a/code/train/DoubleQuntile.ipynb b/main/train/DoubleQuntile.ipynb similarity index 99% rename from code/train/DoubleQuntile.ipynb rename to main/train/DoubleQuntile.ipynb index fe08a2c..f5ef0b9 100644 --- a/code/train/DoubleQuntile.ipynb +++ b/main/train/DoubleQuntile.ipynb @@ -172,7 +172,7 @@ } }, "source": [ - "from utils.utils import read_and_merge_h5_data\n", + "from code.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", diff --git a/code/train/DoubleRank.ipynb b/main/train/DoubleRank.ipynb similarity index 99% rename from code/train/DoubleRank.ipynb rename to main/train/DoubleRank.ipynb index cff6b9c..3ce3697 100644 --- a/code/train/DoubleRank.ipynb +++ b/main/train/DoubleRank.ipynb @@ -26,7 +26,7 @@ "scrolled": true }, "source": [ - "from utils.utils import read_and_merge_h5_data\n", + "from code.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", @@ -510,7 +510,7 @@ "scrolled": true }, "source": [ - "from utils.factor import get_act_factor\n", + "from code.utils.factor import get_act_factor\n", "\n", "\n", "def read_industry_data(h5_filename):\n", @@ -620,9 +620,6 @@ }, "source": [ "from scipy.stats import ks_2samp, wasserstein_distance\n", - "from sklearn.metrics import roc_auc_score\n", - "from sklearn.model_selection import train_test_split\n", - "from sklearn.preprocessing import StandardScaler\n", "\n", "\n", "def remove_shifted_features(train_data, test_data, feature_columns, ks_threshold=0.1, wasserstein_threshold=0.15,\n", @@ -786,7 +783,6 @@ "\n", "\n", "import pandas as pd\n", - "from sklearn.preprocessing import StandardScaler\n", "\n", "\n", "def cross_sectional_standardization(df, features):\n", diff --git a/code/train/MultiClassify.ipynb b/main/train/MultiClassify.ipynb similarity index 99% rename from code/train/MultiClassify.ipynb rename to main/train/MultiClassify.ipynb index 0ab875a..e4953fa 100644 --- a/code/train/MultiClassify.ipynb +++ b/main/train/MultiClassify.ipynb @@ -36,7 +36,7 @@ } }, "source": [ - "from utils.utils import read_and_merge_h5_data\n", + "from code.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", diff --git a/code/train/PlUpdateClassify.ipynb b/main/train/PlUpdateClassify.ipynb similarity index 99% rename from code/train/PlUpdateClassify.ipynb rename to main/train/PlUpdateClassify.ipynb index 3111572..93153dc 100644 --- a/code/train/PlUpdateClassify.ipynb +++ b/main/train/PlUpdateClassify.ipynb @@ -35,7 +35,7 @@ } }, "source": [ - "from utils.utils import read_and_merge_h5_data_polars\n", + "from code.utils.utils import read_and_merge_h5_data_polars\n", "\n", "print('daily data')\n", "df = read_and_merge_h5_data_polars('../../data/daily_data.h5', key='daily_data',\n", @@ -721,13 +721,7 @@ "import lightgbm as lgb\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", - "import optuna\n", - "from sklearn.model_selection import KFold\n", - "from sklearn.metrics import mean_absolute_error\n", - "import os\n", - "import json\n", - "import pickle\n", - "import hashlib\n", + "\n", "\n", "def train_light_model(train_data_df, test_data_df, params, feature_columns, callbacks, evals,\n", " print_feature_importance=True, num_boost_round=100,\n", diff --git a/main/train/Rank.ipynb b/main/train/Rank.ipynb new file mode 100644 index 0000000..0331554 --- /dev/null +++ b/main/train/Rank.ipynb @@ -0,0 +1,1984 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "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": [ + "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": 2, + "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", + "\n", + "RangeIndex: 8514978 entries, 0 to 8514977\n", + "Data columns (total 31 columns):\n", + " # Column Dtype \n", + "--- ------ ----- \n", + " 0 ts_code object \n", + " 1 trade_date datetime64[ns]\n", + " 2 open float64 \n", + " 3 close float64 \n", + " 4 high float64 \n", + " 5 low float64 \n", + " 6 vol float64 \n", + " 7 pct_chg float64 \n", + " 8 turnover_rate float64 \n", + " 9 pe_ttm float64 \n", + " 10 circ_mv float64 \n", + " 11 volume_ratio float64 \n", + " 12 is_st bool \n", + " 13 up_limit float64 \n", + " 14 down_limit float64 \n", + " 15 buy_sm_vol float64 \n", + " 16 sell_sm_vol float64 \n", + " 17 buy_lg_vol float64 \n", + " 18 sell_lg_vol float64 \n", + " 19 buy_elg_vol float64 \n", + " 20 sell_elg_vol float64 \n", + " 21 net_mf_vol float64 \n", + " 22 his_low float64 \n", + " 23 his_high float64 \n", + " 24 cost_5pct float64 \n", + " 25 cost_15pct float64 \n", + " 26 cost_50pct float64 \n", + " 27 cost_85pct float64 \n", + " 28 cost_95pct float64 \n", + " 29 weight_avg float64 \n", + " 30 winner_rate float64 \n", + "dtypes: bool(1), datetime64[ns](1), float64(28), object(1)\n", + "memory usage: 1.9+ 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'],\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', '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": 3, + "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": 4, + "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": [ + "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", + " 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', 'RSI', 'MACD', 'Signal_line',\n", + " 'MACD_hist', '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": 5, + "id": "a735bc02ceb4d872", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-03T12:47:10.821169Z", + "start_time": "2025-04-03T12:47:10.751831Z" + } + }, + "outputs": [], + "source": [ + "\n", + "import talib\n", + "\n", + "\n", + "def get_rolling_factor(df):\n", + " old_columns = df.columns.tolist()[:]\n", + "\n", + " # 按股票和日期排序(如果尚未排序)\n", + " df = df.sort_values(by=['ts_code', 'trade_date'])\n", + "\n", + " grouped = df.groupby('ts_code', group_keys=False)\n", + "\n", + " epsilon = 1e-8\n", + " df['lg_elg_net_buy_vol'] = df['buy_lg_vol'] + df['buy_elg_vol'] - df['sell_lg_vol'] - df['sell_elg_vol']\n", + " # 检查 'volume' 列是否存在且有效\n", + " df['flow_lg_elg_intensity'] = df['lg_elg_net_buy_vol'] / (df['vol'] + epsilon)\n", + " \n", + " \n", + " # 2. 散户与主力背离度 (Retail vs Institutional Divergence)\n", + " # 衡量小单净流入与(大单+超大单)净流入的差异或比率\n", + " df['sm_net_buy_vol'] = df['buy_sm_vol'] - df['sell_sm_vol']\n", + " df['flow_divergence_diff'] = df['sm_net_buy_vol'] - df['lg_elg_net_buy_vol']\n", + " # 比率形式可能更稳定\n", + " df['flow_divergence_ratio'] = df['sm_net_buy_vol'] / (df['lg_elg_net_buy_vol'] + np.sign(df['lg_elg_net_buy_vol']) * epsilon + epsilon) # 复杂处理避免0/0\n", + " \n", + " # 3. 资金流结构变动 (Flow Structure Change - Relative Strength of Large Flow)\n", + " # 大单+超大单买入额占总买入额的比例的变化\n", + " df['total_buy_vol'] = df['buy_sm_vol'] + df['buy_lg_vol'] + df['buy_elg_vol']\n", + " df['lg_elg_buy_prop'] = (df['buy_lg_vol'] + df['buy_elg_vol']) / (df['total_buy_vol'] + epsilon)\n", + " df['flow_struct_buy_change'] = grouped['lg_elg_buy_prop'].diff(1) # 1日变化\n", + " \n", + " # 4. 资金流加速度 (Flow Acceleration)\n", + " # 净主力资金流的变化率(二阶导)\n", + " df['lg_elg_net_buy_vol_change'] = grouped['lg_elg_net_buy_vol'].diff(1)\n", + " df['flow_lg_elg_accel'] = grouped['lg_elg_net_buy_vol_change'].diff(1)\n", + " \n", + " # # 5. 极端资金流事件 (Categorical: Extreme Flow Event)\n", + " # # 定义主力资金流强度是否处于其历史极端水平(例如,过去N天的90分位数以上或10分位数以下)\n", + " # rolling_window = 20 # 可调整窗口期\n", + " \n", + " # # Step 1: Calculate the rolling quantiles separately\n", + " # rolling_high = grouped['flow_lg_elg_intensity'].rolling(rolling_window, min_periods=1).quantile(0.9) # min_periods=1 保证窗口未满时也有输出\n", + " # rolling_low = grouped['flow_lg_elg_intensity'].rolling(rolling_window, min_periods=1).quantile(0.1)\n", + " \n", + " # # Step 2: Assign the results to the DataFrame\n", + " # # 确保 df 和 rolling_high/low 的索引是一致的\n", + " # # 如果 df 的索引在此期间没有被修改过,这通常是安全的\n", + " # df['flow_lg_elg_intensity_rolling_high'] = rolling_high\n", + " # df['flow_lg_elg_intensity_rolling_low'] = rolling_low\n", + " \n", + " # # Step 3: Continue with the logic using the new columns\n", + " # conditions_flow = [\n", + " # df['flow_lg_elg_intensity'] > df['flow_lg_elg_intensity_rolling_high'],\n", + " # df['flow_lg_elg_intensity'] < df['flow_lg_elg_intensity_rolling_low']\n", + " # ]\n", + " # choices_flow = [1, -1] # 1: 极端流入, -1: 极端流出\n", + " # df['cat_extreme_flow'] = np.select(conditions_flow, choices_flow, default=0)\n", + " \n", + " # --- 筹码分布因子 ---\n", + " \n", + " # 6. 筹码集中度 (Chip Concentration)\n", + " # 衡量筹码分布的紧密程度,例如 95% 与 5% 成本价的差距,相对于当前价格进行标准化\n", + " # 检查 'close' 列是否存在且有效\n", + " df['chip_concentration_range'] = (df['cost_95pct'] - df['cost_5pct']) / (df['close'] + epsilon)\n", + " \n", + " \n", + " # 7. 筹码分布偏度 (Chip Distribution Skewness Proxy)\n", + " # 比较中位数成本 (cost_50pct) 和加权平均成本 (weight_avg)\n", + " # weight_avg > cost_50pct 暗示高成本区有较多筹码(右偏)\n", + " df['chip_skewness'] = (df['weight_avg'] - df['cost_50pct']) / (df['cost_50pct'] + epsilon)\n", + " \n", + " # 8. 浮筹比例 (Floating Chips Proxy)\n", + " # 衡量短期内(例如15%成本线以下)的筹码比例与总获利盘比例的关系\n", + " # winner_rate 高但 cost_15pct 接近当前价,可能意味着大部分获利盘成本不高,易浮动\n", + " # 这里简化为:获利盘比例 与 (当前价-15%成本价)/当前价 的乘积\n", + " price_dist_cost15 = (df['close'] - df['cost_15pct']) / (df['close'] + epsilon)\n", + " df['floating_chip_proxy'] = df['winner_rate'] * np.maximum(0, price_dist_cost15) # 只考虑价格高于15%成本线的情况\n", + " \n", + " # 9. 成本支撑强度变化 (Cost Support Strength Change)\n", + " # 观察低位筹码成本(如 5% 或 15% 分位点)的变化率,看支撑位是上移还是下移\n", + " df['cost_support_15pct_change'] = grouped['cost_15pct'].pct_change(1) * 100 # 百分比变化\n", + " \n", + " # 10. 获利盘压力/支撑区 (Categorical: Winner Rate Zone & Price Position)\n", + " # 结合获利盘比例和当前价格相对于筹码成本的位置\n", + " # 例如: 价格在 85% 成本线之上 & 获利盘 > 0.8 -> 高位派发风险区?\n", + " # 价格在 15% 成本线之下 & 获利盘 < 0.2 -> 低位吸筹潜力区?\n", + " conditions_winner = [\n", + " (df['close'] > df['cost_85pct']) & (df['winner_rate'] > 0.8), # 高位 & 高获利盘\n", + " (df['close'] < df['cost_15pct']) & (df['winner_rate'] < 0.2), # 低位 & 低获利盘\n", + " (df['close'] > df['cost_50pct']) & (df['winner_rate'] > 0.5), # 中高位 & 多数获利\n", + " (df['close'] < df['cost_50pct']) & (df['winner_rate'] < 0.5), # 中低位 & 多数亏损\n", + " ]\n", + " choices_winner = [1, 2, 3, 4] # 1:高风险区, 2:低潜力区, 3:中上获利区, 4:中下亏损区\n", + " df['cat_winner_price_zone'] = np.select(conditions_winner, choices_winner, default=0) # 0: 其他\n", + " \n", + " \n", + " # --- 结合因子 ---\n", + " \n", + " # 11. 主力行为与筹码结构一致性 (Flow-Chip Consistency)\n", + " # 例如:主力净买入发生在价格接近下方筹码密集区(如 cost_15pct 到 cost_50pct)时\n", + " price_near_low_support = (df['close'] > df['cost_15pct']) & (df['close'] < df['cost_50pct'])\n", + " df['flow_chip_consistency'] = df['lg_elg_net_buy_vol'] * price_near_low_support.astype(int)\n", + " # 可以进一步标准化或做成 categorical\n", + " \n", + " # 12. 获利了结压力/承接盘强度 (Profit-Taking Pressure vs Absorption)\n", + " # 在高获利盘(winner_rate > 0.7)的情况下,观察主力资金是净流出(了结)还是净流入(高位换手/承接)\n", + " high_winner_rate_flag = (df['winner_rate'] > 0.7).astype(int)\n", + " df['profit_taking_vs_absorb'] = df['lg_elg_net_buy_vol'] * high_winner_rate_flag\n", + " # 正值表示高获利盘下主力仍在买入(承接),负值表示主力在卖出(了结)\n", + " \n", + " \n", + " # 清理临时列和可能产生的 NaN (可选,根据需要处理)\n", + " cols_to_drop = ['lg_elg_net_buy_vol', 'sm_net_buy_vol', 'total_buy_vol', 'lg_elg_buy_prop',\n", + " 'lg_elg_net_buy_vol_change', 'flow_lg_elg_intensity_rolling_high',\n", + " 'flow_lg_elg_intensity_rolling_low']\n", + " # df = df.drop(columns=cols_to_drop)\n", + "\n", + "\n", + " window = 20\n", + " df['_is_positive'] = (df['pct_chg'] > 0).astype(int)\n", + " df['_is_negative'] = (df['pct_chg'] < 0).astype(int)\n", + " df['cat_is_positive'] = (df['pct_chg'] > 0).astype(int)\n", + "\n", + " # 分离正负收益率 (用于计算各自的均值和平方均值)\n", + " # 注意:这里我们保留原始收益率用于计算,而不是 clip 到 0\n", + " df['_pos_returns'] = df['pct_chg'].where(df['pct_chg'] > 0, 0) # 非正设为0,便于求和\n", + " df['_neg_returns'] = df['pct_chg'].where(df['pct_chg'] < 0, 0) # 非负设为0,便于求和\n", + "\n", + " # 计算收益率的平方 (用于计算 E[X^2])\n", + " df['_pos_returns_sq'] = np.square(df['_pos_returns'])\n", + " df['_neg_returns_sq'] = np.square(df['_neg_returns']) # 平方后负数变正\n", + "\n", + " # 4. 计算滚动统计量 (使用内置函数,速度较快)\n", + " # 计算正收益日的统计量\n", + " rolling_pos_count = grouped['_is_positive'].rolling(window, min_periods=max(1, window // 2)).sum()\n", + " rolling_pos_sum = grouped['_pos_returns'].rolling(window, min_periods=max(1, window // 2)).sum()\n", + " rolling_pos_sum_sq = grouped['_pos_returns_sq'].rolling(window, min_periods=max(1, window // 2)).sum()\n", + "\n", + " # 计算负收益日的统计量\n", + " rolling_neg_count = grouped['_is_negative'].rolling(window, min_periods=max(1, window // 2)).sum()\n", + " rolling_neg_sum = grouped['_neg_returns'].rolling(window, min_periods=max(1, window // 2)).sum()\n", + " rolling_neg_sum_sq = grouped['_neg_returns_sq'].rolling(window, min_periods=max(1, window // 2)).sum()\n", + "\n", + " # 5. 计算方差和标准差\n", + " pos_mean_sq = rolling_pos_sum_sq / rolling_pos_count\n", + " pos_mean = rolling_pos_sum / rolling_pos_count\n", + " pos_var = pos_mean_sq - np.square(pos_mean)\n", + " pos_var = pos_var.where(rolling_pos_count >= 2, np.nan).clip(lower=0)\n", + " upside_vol = np.sqrt(pos_var)\n", + "\n", + " neg_mean_sq = rolling_neg_sum_sq / rolling_neg_count\n", + " neg_mean = rolling_neg_sum / rolling_neg_count # 注意 neg_mean 是负数\n", + " neg_var = neg_mean_sq - np.square(neg_mean)\n", + " neg_var = neg_var.where(rolling_neg_count >= 2, np.nan).clip(lower=0)\n", + " downside_vol = np.sqrt(neg_var)\n", + "\n", + " # rolling 操作后结果带有 MultiIndex,需要去除股票代码层级以便合并\n", + " df['upside_vol'] = upside_vol.reset_index(level=0, drop=True)\n", + " df['downside_vol'] = downside_vol.reset_index(level=0, drop=True)\n", + "\n", + " df['vol_ratio'] = df['upside_vol'] / df['downside_vol']\n", + " df['vol_ratio'] = df['vol_ratio'].replace([np.inf, -np.inf], np.nan).fillna(0) # 或 fillna(np.nan)\n", + "\n", + " df['return_skew'] = grouped['pct_chg'].rolling(window=5).skew().reset_index(0, drop=True)\n", + " df['return_kurtosis'] = grouped['pct_chg'].rolling(window=5).kurt().reset_index(0, drop=True)\n", + "\n", + " # 因子 1:短期成交量变化率\n", + " df['volume_change_rate'] = (\n", + " grouped['vol'].rolling(window=2).mean() /\n", + " grouped['vol'].rolling(window=10).mean() - 1\n", + " ).reset_index(level=0, drop=True) # 确保索引对齐\n", + "\n", + " # 因子 2:成交量突破信号\n", + " max_volume = grouped['vol'].rolling(window=5).max().reset_index(level=0, drop=True) # 确保索引对齐\n", + " df['cat_volume_breakout'] = (df['vol'] > max_volume)\n", + "\n", + " # 因子 3:换手率均线偏离度\n", + " mean_turnover = grouped['turnover_rate'].rolling(window=3).mean().reset_index(level=0, drop=True)\n", + " std_turnover = grouped['turnover_rate'].rolling(window=3).std().reset_index(level=0, drop=True)\n", + " df['turnover_deviation'] = (df['turnover_rate'] - mean_turnover) / std_turnover\n", + "\n", + " # 因子 4:换手率激增信号\n", + " df['cat_turnover_spike'] = (df['turnover_rate'] > mean_turnover + 2 * std_turnover)\n", + "\n", + " # 因子 5:量比均值\n", + " df['avg_volume_ratio'] = grouped['volume_ratio'].rolling(window=3).mean().reset_index(level=0, drop=True)\n", + "\n", + " # 因子 6:量比突破信号\n", + " max_volume_ratio = grouped['volume_ratio'].rolling(window=5).max().reset_index(level=0, drop=True)\n", + " df['cat_volume_ratio_breakout'] = (df['volume_ratio'] > max_volume_ratio)\n", + "\n", + " df['vol_spike'] = grouped.apply(\n", + " lambda x: pd.Series(x['vol'].rolling(20).mean(), index=x.index)\n", + " )\n", + " df['vol_std_5'] = grouped['vol'].pct_change().rolling(window=5).std()\n", + "\n", + " # 计算 ATR\n", + " df['atr_14'] = grouped.apply(\n", + " lambda x: pd.Series(talib.ATR(x['high'].values, x['low'].values, x['close'].values, timeperiod=14),\n", + " index=x.index)\n", + " )\n", + " df['atr_6'] = grouped.apply(\n", + " lambda x: pd.Series(talib.ATR(x['high'].values, x['low'].values, x['close'].values, timeperiod=6),\n", + " index=x.index)\n", + " )\n", + "\n", + " # 计算 OBV 及其均线\n", + " df['obv'] = grouped.apply(\n", + " lambda x: pd.Series(talib.OBV(x['close'].values, x['vol'].values), index=x.index)\n", + " )\n", + " print(df.columns)\n", + " df['maobv_6'] = grouped.apply(\n", + " lambda x: pd.Series(talib.SMA(x['obv'].values, timeperiod=6), index=x.index)\n", + " )\n", + "\n", + " df['rsi_3'] = grouped.apply(\n", + " lambda x: pd.Series(talib.RSI(x['close'].values, timeperiod=3), index=x.index)\n", + " )\n", + " # df['rsi_6'] = grouped.apply(\n", + " # lambda x: pd.Series(talib.RSI(x['close'].values, timeperiod=6), index=x.index)\n", + " # )\n", + " # df['rsi_9'] = grouped.apply(\n", + " # lambda x: pd.Series(talib.RSI(x['close'].values, timeperiod=9), index=x.index)\n", + " # )\n", + "\n", + " # 计算 return_10 和 return_20\n", + " df['return_5'] = grouped['close'].apply(lambda x: x / x.shift(5) - 1)\n", + " # df['return_10'] = grouped['close'].apply(lambda x: x / x.shift(10) - 1)\n", + " df['return_20'] = grouped['close'].apply(lambda x: x / x.shift(20) - 1)\n", + "\n", + " # df['avg_close_5'] = grouped['close'].apply(lambda x: x.rolling(window=5).mean() / x)\n", + "\n", + " # 计算标准差指标\n", + " df['std_return_5'] = grouped['close'].apply(lambda x: x.pct_change().rolling(window=5).std())\n", + " # df['std_return_15'] = grouped['close'].apply(lambda x: x.pct_change().rolling(window=15).std())\n", + " # df['std_return_25'] = grouped['close'].apply(lambda x: x.pct_change().rolling(window=25).std())\n", + " df['std_return_90'] = grouped['close'].apply(lambda x: x.pct_change().rolling(window=90).std())\n", + " df['std_return_90_2'] = grouped['close'].apply(lambda x: x.shift(10).pct_change().rolling(window=90).std())\n", + "\n", + " # 计算 EMA 指标\n", + " df['_ema_5'] = grouped['close'].apply(\n", + " lambda x: pd.Series(talib.EMA(x.values, timeperiod=5), index=x.index)\n", + " )\n", + " df['_ema_13'] = grouped['close'].apply(\n", + " lambda x: pd.Series(talib.EMA(x.values, timeperiod=13), index=x.index)\n", + " )\n", + " df['_ema_20'] = grouped['close'].apply(\n", + " lambda x: pd.Series(talib.EMA(x.values, timeperiod=20), index=x.index)\n", + " )\n", + " df['_ema_60'] = grouped['close'].apply(\n", + " lambda x: pd.Series(talib.EMA(x.values, timeperiod=60), index=x.index)\n", + " )\n", + "\n", + " # 计算 act_factor1, act_factor2, act_factor3, act_factor4\n", + " df['act_factor1'] = grouped['_ema_5'].apply(\n", + " lambda x: np.arctan((x / x.shift(1) - 1) * 100) * 57.3 / 50\n", + " )\n", + " df['act_factor2'] = grouped['_ema_13'].apply(\n", + " lambda x: np.arctan((x / x.shift(1) - 1) * 100) * 57.3 / 40\n", + " )\n", + " df['act_factor3'] = grouped['_ema_20'].apply(\n", + " lambda x: np.arctan((x / x.shift(1) - 1) * 100) * 57.3 / 21\n", + " )\n", + " df['act_factor4'] = grouped['_ema_60'].apply(\n", + " lambda x: np.arctan((x / x.shift(1) - 1) * 100) * 57.3 / 10\n", + " )\n", + "\n", + " # 根据 trade_date 截面计算排名\n", + " df['rank_act_factor1'] = df.groupby('trade_date', group_keys=False)['act_factor1'].rank(ascending=False, pct=True)\n", + " df['rank_act_factor2'] = df.groupby('trade_date', group_keys=False)['act_factor2'].rank(ascending=False, pct=True)\n", + " df['rank_act_factor3'] = df.groupby('trade_date', group_keys=False)['act_factor3'].rank(ascending=False, pct=True)\n", + "\n", + " df['log(circ_mv)'] = np.log(df['circ_mv'])\n", + "\n", + " window_high_volume = 5\n", + " window_close_stddev = 20\n", + " period_delta = 5\n", + "\n", + " # 计算每只股票的滚动协方差\n", + " def calculate_rolling_cov(group):\n", + " return group['high'].rolling(window_high_volume).cov(group['vol'])\n", + "\n", + " df['cov'] = grouped.apply(calculate_rolling_cov)\n", + "\n", + " # 计算每只股票的协方差差分\n", + " def calculate_delta_cov(group):\n", + " return group['cov'].diff(period_delta)\n", + "\n", + " df['delta_cov'] = grouped.apply(calculate_delta_cov)\n", + "\n", + " # 计算每只股票的滚动标准差\n", + " def calculate_stddev_close(group):\n", + " return group['close'].rolling(window_close_stddev).std()\n", + "\n", + " df['_stddev_close'] = grouped.apply(calculate_stddev_close)\n", + " df['_rank_stddev'] = df.groupby('trade_date')['_stddev_close'].rank(pct=True)\n", + " df['alpha_22_improved'] = -1 * df['delta_cov'] * df['_rank_stddev']\n", + "\n", + " df['alpha_003'] = np.where(df['high'] != df['low'],\n", + " (df['close'] - df['open']) / (df['high'] - df['low']),\n", + " 0)\n", + "\n", + " df['alpha_007'] = grouped.apply(lambda x: x['close'].rolling(5).corr(x['vol']))\n", + " df['alpha_007'] = df.groupby('trade_date', group_keys=False)['alpha_007'].rank(ascending=True, pct=True)\n", + "\n", + " df['alpha_013'] = grouped['close'].transform(lambda x: x.rolling(5).sum() - x.rolling(20).sum())\n", + " df['alpha_013'] = df.groupby('trade_date', group_keys=False)['alpha_013'].rank(ascending=True, pct=True)\n", + "\n", + " df['cat_up_limit'] = (df['close'] == df['up_limit']) # 是否涨停(1表示涨停,0表示未涨停)\n", + " df['cat_down_limit'] = (df['close'] == df['down_limit']) # 是否跌停(1表示跌停,0表示未跌停)\n", + " df['up_limit_count_10d'] = grouped['cat_up_limit'].rolling(window=10, min_periods=1).sum().reset_index(level=0,\n", + " drop=True)\n", + " df['down_limit_count_10d'] = grouped['cat_down_limit'].rolling(window=10, min_periods=1).sum().reset_index(level=0,\n", + " drop=True)\n", + "\n", + " # 3. 最近连续涨跌停天数\n", + " def calculate_consecutive_limits(series):\n", + " \"\"\"\n", + " 计算连续涨停/跌停天数。\n", + " \"\"\"\n", + " consecutive_up = series * (series.groupby((series != series.shift()).cumsum()).cumcount() + 1)\n", + " consecutive_down = series * (series.groupby((series != series.shift()).cumsum()).cumcount() + 1)\n", + " return consecutive_up, consecutive_down\n", + "\n", + " # 连续涨停天数\n", + " df['consecutive_up_limit'] = grouped['cat_up_limit'].apply(\n", + " lambda x: calculate_consecutive_limits(x)[0]\n", + " )\n", + "\n", + " df['vol_break'] = np.where((df['close'] > df['cost_85pct']) & (df['volume_ratio'] > 2), 1, 0)\n", + "\n", + " df['weight_roc5'] = grouped['weight_avg'].apply(lambda x: x.pct_change(5))\n", + "\n", + " def rolling_corr(group):\n", + " roc_close = group['close'].pct_change()\n", + " roc_weight = group['weight_avg'].pct_change()\n", + " return roc_close.rolling(10).corr(roc_weight)\n", + "\n", + " df['price_cost_divergence'] = grouped.apply(rolling_corr)\n", + "\n", + " df['smallcap_concentration'] = (1 / df['log(circ_mv)']) * (df['cost_85pct'] - df['cost_15pct'])\n", + "\n", + " # 16. 筹码稳定性指数 (20日波动率)\n", + " df['weight_std20'] = grouped['weight_avg'].apply(lambda x: x.rolling(20).std())\n", + " df['cost_stability'] = df['weight_std20'] / grouped['weight_avg'].transform(lambda x: x.rolling(20).mean())\n", + "\n", + " # 17. 成本区间突破标记\n", + " df['high_cost_break_days'] = grouped.apply(lambda g: g['close'].gt(g['cost_95pct']).rolling(5).sum())\n", + "\n", + " # 20. 筹码-流动性风险\n", + " df['liquidity_risk'] = (df['cost_95pct'] - df['cost_5pct']) * (\n", + " 1 / grouped['vol'].transform(lambda x: x.rolling(10).mean()))\n", + "\n", + " # 7. 市值波动率因子 (使用 grouped)\n", + " df['turnover_std'] = grouped['turnover_rate'].transform(lambda x: x.rolling(window=20).std())\n", + " df['mv_volatility'] = grouped.apply(lambda x: x['turnover_std'] / x['log(circ_mv)'])\n", + "\n", + " # 8. 市值成长性因子\n", + " df['volume_growth'] = grouped['vol'].pct_change(periods=20)\n", + " df['mv_growth'] = df['volume_growth'] / df['log(circ_mv)']\n", + "\n", + " # AR 指标\n", + " df[\"ar\"] = grouped.apply(\n", + " lambda x: (x[\"high\"].div(x[\"open\"]).rolling(3).sum()) / (x[\"open\"].div(x[\"low\"]).rolling(3).sum()) * 100)\n", + "\n", + " # BR 指标\n", + " df[\"pre_close\"] = grouped[\"close\"].shift(1)\n", + " df[\"br_up\"] = (df[\"high\"] - df[\"pre_close\"]).clip(lower=0)\n", + " df[\"br_down\"] = (df[\"pre_close\"] - df[\"low\"]).clip(lower=0)\n", + " df[\"br\"] = grouped.apply(lambda x: (x[\"br_up\"].rolling(3).sum()) / (x[\"br_down\"].rolling(3).sum()) * 100)\n", + "\n", + " # ARBR\n", + " df['arbr'] = df['ar'] - df['br']\n", + " df.drop(columns=[\"pre_close\", \"br_up\", \"br_down\", 'ar', 'br'], inplace=True)\n", + "\n", + " df.drop(columns=['weight_std20'], inplace=True, errors='ignore')\n", + " df.drop(\n", + " columns=['_is_positive', '_is_negative', '_pos_returns', '_neg_returns', '_pos_returns_sq', '_neg_returns_sq'],\n", + " inplace=True, errors='ignore')\n", + " new_columns = [col for col in df.columns.tolist()[:] if col not in old_columns]\n", + "\n", + " return df, new_columns\n", + "\n", + "\n", + "def get_simple_factor(df):\n", + " old_columns = df.columns.tolist()[:]\n", + " df = df.sort_values(by=['ts_code', 'trade_date'])\n", + "\n", + " alpha = 0.5\n", + " df['momentum_factor'] = df['volume_change_rate'] + alpha * df['turnover_deviation']\n", + " df['resonance_factor'] = df['volume_ratio'] * df['pct_chg']\n", + " df['log_close'] = np.log(df['close'])\n", + "\n", + " df['cat_vol_spike'] = df['vol'] > 2 * df['vol_spike']\n", + "\n", + " df['up'] = (df['high'] - df[['close', 'open']].max(axis=1)) / df['close']\n", + " df['down'] = (df[['close', 'open']].min(axis=1) - df['low']) / df['close']\n", + "\n", + " df['obv-maobv_6'] = df['obv'] - df['maobv_6']\n", + "\n", + " # 计算比值指标\n", + " df['std_return_5 / std_return_90'] = df['std_return_5'] / df['std_return_90']\n", + " # df['std_return_5 / std_return_25'] = df['std_return_5'] / df['std_return_25']\n", + "\n", + " # 计算标准差差值\n", + " df['std_return_90 - std_return_90_2'] = df['std_return_90'] - df['std_return_90_2']\n", + "\n", + " # df['cat_af1'] = df['act_factor1'] > 0\n", + " df['cat_af2'] = df['act_factor2'] > df['act_factor1']\n", + " df['cat_af3'] = df['act_factor3'] > df['act_factor2']\n", + " df['cat_af4'] = df['act_factor4'] > df['act_factor3']\n", + "\n", + " # 计算 act_factor5 和 act_factor6\n", + " df['act_factor5'] = df['act_factor1'] + df['act_factor2'] + df['act_factor3'] + df['act_factor4']\n", + " df['act_factor6'] = (df['act_factor1'] - df['act_factor2']) / np.sqrt(\n", + " df['act_factor1'] ** 2 + df['act_factor2'] ** 2)\n", + "\n", + " df['active_buy_volume_large'] = df['buy_lg_vol'] / df['net_mf_vol']\n", + " df['active_buy_volume_big'] = df['buy_elg_vol'] / df['net_mf_vol']\n", + " df['active_buy_volume_small'] = df['buy_sm_vol'] / df['net_mf_vol']\n", + "\n", + " df['buy_lg_vol_minus_sell_lg_vol'] = (df['buy_lg_vol'] - df['sell_lg_vol']) / df['net_mf_vol']\n", + " df['buy_elg_vol_minus_sell_elg_vol'] = (df['buy_elg_vol'] - df['sell_elg_vol']) / df['net_mf_vol']\n", + "\n", + " df['log(circ_mv)'] = np.log(df['circ_mv'])\n", + "\n", + " df['ctrl_strength'] = (df['cost_85pct'] - df['cost_15pct']) / (df['his_high'] - df['his_low'])\n", + "\n", + " df['low_cost_dev'] = (df['close'] - df['cost_5pct']) / (df['cost_50pct'] - df['cost_5pct'])\n", + "\n", + " df['asymmetry'] = (df['cost_95pct'] - df['cost_50pct']) / (df['cost_50pct'] - df['cost_5pct'])\n", + "\n", + " df['lock_factor'] = df['turnover_rate'] * (\n", + " 1 - (df['cost_95pct'] - df['cost_5pct']) / (df['his_high'] - df['his_low']))\n", + "\n", + " df['cat_vol_break'] = (df['close'] > df['cost_85pct']) & (df['volume_ratio'] > 2)\n", + "\n", + " df['cost_atr_adj'] = (df['cost_95pct'] - df['cost_5pct']) / df['atr_14']\n", + "\n", + " # 12. 小盘股筹码集中度\n", + " df['smallcap_concentration'] = (1 / df['log(circ_mv)']) * (df['cost_85pct'] - df['cost_15pct'])\n", + "\n", + " df['cat_golden_resonance'] = ((df['close'] > df['weight_avg']) &\n", + " (df['volume_ratio'] > 1.5) &\n", + " (df['winner_rate'] > 0.7))\n", + "\n", + " df['mv_turnover_ratio'] = df['turnover_rate'] / df['log(circ_mv)']\n", + "\n", + " df['mv_adjusted_volume'] = df['vol'] / df['log(circ_mv)']\n", + "\n", + " df['mv_weighted_turnover'] = df['turnover_rate'] * (1 / df['log(circ_mv)'])\n", + "\n", + " df['nonlinear_mv_volume'] = df['vol'] / df['log(circ_mv)']\n", + "\n", + " df['mv_volume_ratio'] = df['volume_ratio'] / df['log(circ_mv)']\n", + "\n", + " df['mv_momentum'] = df['turnover_rate'] * df['volume_ratio'] / df['log(circ_mv)']\n", + "\n", + " drop_columns = [col for col in df.columns if col.startswith('_')]\n", + " df.drop(columns=drop_columns, inplace=True, errors='ignore')\n", + "\n", + " new_columns = [col for col in df.columns.tolist()[:] if col not in old_columns]\n", + " return df, new_columns\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "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", + " 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": 7, + "id": "dbe2fd8021b9417f", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-03T12:47:15.969344Z", + "start_time": "2025-04-03T12:47:15.963327Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['ts_code', 'open', 'close', 'high', 'low', 'circ_mv', '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": 8, + "id": "85c3e3d0235ffffa", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-03T12:47:16.089879Z", + "start_time": "2025-04-03T12:47:15.990101Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ts_code trade_date is_st\n", + "29 000037.SZ 2017-01-03 True\n", + "72 000408.SZ 2017-01-03 True\n", + "95 000504.SZ 2017-01-03 True\n", + "96 000505.SZ 2017-01-03 True\n", + "101 000511.SZ 2017-01-03 True\n", + "... ... ... ...\n", + "8513971 603869.SH 2025-04-09 True\n", + "8513976 603879.SH 2025-04-09 True\n", + "8514023 603959.SH 2025-04-09 True\n", + "8514406 688282.SH 2025-04-09 True\n", + "8514410 688287.SH 2025-04-09 True\n", + "\n", + "[193456 rows x 3 columns]\n" + ] + } + ], + "source": [ + "print(df[df['is_st']][['ts_code', 'trade_date', 'is_st']])" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "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": [ + "Index(['ts_code', 'trade_date', 'open', 'close', 'high', 'low', 'vol',\n", + " 'pct_chg', 'turnover_rate', 'pe_ttm', 'circ_mv', 'volume_ratio',\n", + " 'is_st', 'up_limit', 'down_limit', 'buy_sm_vol', 'sell_sm_vol',\n", + " 'buy_lg_vol', 'sell_lg_vol', 'buy_elg_vol', 'sell_elg_vol',\n", + " 'net_mf_vol', 'his_low', 'his_high', 'cost_5pct', 'cost_15pct',\n", + " 'cost_50pct', 'cost_85pct', 'cost_95pct', 'weight_avg', 'winner_rate',\n", + " 'l2_code', '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_buy_consolidation_20...\n", + "Finished lg_buy_consolidation_20.\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 intraday_lg_flow_corr_20 (Placeholder - complex implementation)...\n", + "Finished intraday_lg_flow_corr_20 (Placeholder).\n", + "Calculating cap_neutral_cost_metric (Placeholder - requires statsmodels)...\n", + "Finished cap_neutral_cost_metric (Placeholder).\n", + "Calculating hurst_net_mf_vol_60 (Placeholder - requires hurst library)...\n", + "Error: 'hurst' library not installed. Cannot calculate factor.\n", + "Finished hurst_net_mf_vol_60 (Placeholder).\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", + "Error calculating cs_rank_ind_adj_lg_flow: Missing 'cat_l2_code' column. Assigning NaN.\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", + "\n", + "Index: 3830570 entries, 0 to 3830569\n", + "Columns: 176 entries, ts_code to cs_rank_size\n", + "dtypes: bool(12), datetime64[ns](1), float64(157), int32(3), int64(1), object(2)\n", + "memory usage: 4.7+ GB\n", + "None\n" + ] + } + ], + "source": [ + "import numpy as np\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'] >= '2020-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", + "df = filter_data(df)\n", + "# df = get_technical_factor(df)\n", + "# df = get_act_factor(df)\n", + "# df = get_money_flow_factor(df)\n", + "# df = get_alpha_factor(df)\n", + "# df = get_limit_factor(df)\n", + "# df = get_cyp_perf_factor(df)\n", + "# df = get_mv_factors(df)\n", + "df, _ = get_rolling_factor(df)\n", + "df, _ = get_simple_factor(df)\n", + "from main.factor.factor import *\n", + "lg_flow_mom_corr(df, N=20, M=60)\n", + "lg_buy_consolidation(df, N=20)\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", + "intraday_lg_flow_corr(df, N=20) # Placeholder\n", + "cap_neutral_cost_metric(df) # Placeholder\n", + "hurst_exponent_flow(df, N=60) # Placeholder\n", + "# calculate_complex_factor(df)\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", + "# df = df.merge(industry_df, on=['l1_code', 'trade_date'], how='left')\n", + "df = df.rename(columns={'l1_code': 'cat_l1_code'})\n", + "df = df.rename(columns={'l2_code': 'cat_l2_code'})\n", + "\n", + "# df = df.merge(index_data, on='trade_date', how='left')\n", + "\n", + "print(df.info())" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "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": 11, + "id": "f4f16d63ad18d1bc", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-03T13:08:03.670700Z", + "start_time": "2025-04-03T13:08:03.665739Z" + } + }, + "outputs": [], + "source": [ + "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": 12, + "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(df, features, industry_col, mkt_cap_col):\n", + " \"\"\" 手动实现简单回归以提升速度 \"\"\"\n", + "\n", + " for col in features:\n", + " residuals = []\n", + " for _, group in df.groupby(industry_col):\n", + " if len(group) > 1:\n", + " x = np.log(group[mkt_cap_col]) # 市值对数\n", + " y = group[col] # 因子值\n", + " beta = np.cov(y, x)[0, 1] / np.var(x) # 计算斜率\n", + " alpha = np.mean(y) - beta * np.mean(x) # 计算截距\n", + " resid = y - (alpha + beta * x) # 计算残差\n", + " residuals.extend(resid)\n", + " else:\n", + " residuals.extend(group[col]) # 样本不足时保留原值\n", + "\n", + " df[col] = residuals\n", + "\n", + " return 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" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "47c12bb34062ae7a", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-03T14:57:50.841165Z", + "start_time": "2025-04-03T14:49:25.889057Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['vol', 'pct_chg', 'turnover_rate', 'volume_ratio', 'winner_rate', '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', 'cat_up_limit', 'cat_down_limit', 'up_limit_count_10d', 'down_limit_count_10d', 'consecutive_up_limit', 'vol_break', 'weight_roc5', 'smallcap_concentration', 'cost_stability', 'high_cost_break_days', 'liquidity_risk', 'turnover_std', 'mv_volatility', 'volume_growth', 'mv_growth', 'arbr', 'momentum_factor', 'resonance_factor', 'log_close', 'cat_vol_spike', 'up', 'down', 'obv-maobv_6', 'std_return_5 / std_return_90', 'std_return_90 - 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']\n", + "去极值\n", + "去极值\n", + "检测到 36 个可能漂移的特征: ['vol', 'pct_chg', 'turnover_rate', 'volume_ratio', 'vol_ratio', 'obv', 'alpha_003', 'resonance_factor', 'log_close', 'up', 'down', 'mv_turnover_ratio', 'mv_adjusted_volume', 'mv_weighted_turnover', 'nonlinear_mv_volume', 'mv_volume_ratio', 'mv_momentum', 'profit_decay_20', 'vol_drop_profit_cnt_5', 'cost_break_confirm_cnt_5', 'atr_norm_channel_pos_14', 'pullback_strong_20_20', 'vol_wgt_hist_pos_20', 'vol_adj_roc_20', 'cs_rank_elg_buy_ratio', 'cs_rank_rel_profit_margin', 'cs_rank_cost_breadth', 'cs_rank_dist_to_upper_cost', 'cs_rank_intraday_range', 'cs_rank_close_pos_in_range', 'cs_rank_pos_in_hist_range', 'cs_rank_vol_x_profit_margin', 'cs_rank_turnover_per_winner', 'cs_rank_volume_ratio', 'cs_rank_elg_buy_sell_sm_ratio', 'cs_rank_size']\n", + "feature_columns: ['winner_rate', 'lg_elg_net_buy_vol', 'flow_lg_elg_intensity', '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', 'cat_is_positive', 'upside_vol', 'downside_vol', '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', 'maobv_6', 'rsi_3', 'return_5', 'return_20', 'std_return_5', 'std_return_90', 'act_factor1', 'act_factor2', 'act_factor3', 'act_factor4', 'rank_act_factor1', 'rank_act_factor2', 'rank_act_factor3', 'cov', 'delta_cov', 'alpha_007', 'alpha_013', 'cat_up_limit', 'cat_down_limit', 'up_limit_count_10d', 'down_limit_count_10d', 'consecutive_up_limit', 'vol_break', 'weight_roc5', 'smallcap_concentration', 'cost_stability', 'high_cost_break_days', 'liquidity_risk', 'turnover_std', 'volume_growth', 'arbr', 'momentum_factor', 'cat_vol_spike', 'obv-maobv_6', 'std_return_5 / std_return_90', 'std_return_90 - 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', 'lg_flow_mom_corr_20_60', 'profit_pressure', 'underwater_resistance', 'cost_conc_std_20', 'vol_amp_loss_20', 'lg_flow_vol_interact_20', 'turnover_diff_skew_20', 'lg_sm_flow_diverge_20', 'cs_rank_net_lg_flow_val', 'cs_rank_winner_rate', 'cs_rank_lg_flow_price_concordance', 'cs_rank_cost_dist_vol_ratio']\n", + "739366\n", + "最小日期: 2020-06-04\n", + "最大日期: 2023-08-01\n", + "402667\n", + "最小日期: 2023-08-01\n", + "最大日期: 2025-04-09\n" + ] + } + ], + "source": [ + "days = 1\n", + "validation_days = 120\n", + "\n", + "import gc\n", + "\n", + "gc.collect()\n", + "\n", + "df = df.sort_values(by=['ts_code', 'trade_date'])\n", + "# df['future_return'] = df.groupby('ts_code', group_keys=False)['close'].apply(lambda x: x.shift(-days) / x - 1)\n", + "df['future_return'] = (df.groupby('ts_code')['close'].shift(-days) - df.groupby('ts_code')['open'].shift(-1)) / \\\n", + " df.groupby('ts_code')['open'].shift(-1)\n", + "df['future_volatility'] = (\n", + " df.groupby('ts_code')['pct_chg']\n", + " .transform(lambda x: x.rolling(days).std().shift(-days))\n", + ")\n", + "\n", + "df['future_score'] = (\n", + " 0.7 * df['future_return']\n", + " + 0.3 * df['future_volatility']\n", + ")\n", + "# df['future_score'] = calculate_score(df, days=2, lambda_param=0.3)\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_volatility'].between(df['future_volatility'].quantile(0.01),\n", + " df['future_volatility'].quantile(0.99)) | filter_index\n", + "\n", + "# df['label'] = df.groupby('trade_date', group_keys=False)['future_volatility'].transform(\n", + "# lambda x: pd.qcut(x, q=30, labels=False, duplicates='drop')\n", + "# )\n", + "\n", + "df['label'] = df.groupby('trade_date', group_keys=False)['future_return'].transform(\n", + " lambda x: pd.qcut(x, q=20, labels=False, duplicates='drop')\n", + ")\n", + "\n", + "\n", + "# df['1_score'] = df.groupby('ts_code', group_keys=False)['future_score'].shift(days)\n", + "# df['2_score'] = df.groupby('ts_code', group_keys=False)['future_score'].shift(1 + days)\n", + "# df['3_score'] = df.groupby('ts_code', group_keys=False)['future_score'].shift(3 + days - 1)\n", + "\n", + "def symmetric_log_transform(values):\n", + " return np.sign(values) * np.log1p(np.abs(values))\n", + "\n", + "\n", + "train_data = df[filter_index & (df['trade_date'] <= '2023-08-01') & (df['trade_date'] >= '2000-01-01')]\n", + "test_data = df[(df['trade_date'] >= '2023-08-01')]\n", + "\n", + "\n", + "def select_pre_zt_stocks_dynamic(stock_df):\n", + " # 排序数据\n", + " stock_df = stock_df.sort_values(by=['trade_date', 'ts_code'])\n", + " stock_df = stock_df.groupby('trade_date', group_keys=False).apply(\n", + " lambda x: x.nlargest(1000, 'return_20')\n", + " )\n", + "\n", + " return stock_df\n", + "\n", + "\n", + "train_data = select_pre_zt_stocks_dynamic(train_data)\n", + "test_data = select_pre_zt_stocks_dynamic(test_data)\n", + "\n", + "# train_data['label'] = train_data.groupby('trade_date', group_keys=False)['future_score'].transform(\n", + "# lambda x: pd.qcut(x, q=50, labels=False, duplicates='drop')\n", + "# )\n", + "# test_data['label'] = test_data.groupby('trade_date', group_keys=False)['future_score'].transform(\n", + "# lambda x: pd.qcut(x, q=50, labels=False, duplicates='drop')\n", + "# )\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, feature_columns)\n", + "# test_data, _ = create_deviation_within_dates(test_data, feature_columns)\n", + "\n", + "feature_columns = [col for col in df.columns if col in df.columns]\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 'cat_l2_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", + "print(feature_columns)\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", + "print('去极值')\n", + "train_data = quantile_filter(train_data, numeric_columns) # 去极值\n", + "# print('中性化')\n", + "# train_data = neutralize_manual(train_data, numeric_columns, industry_col='cat_l2_code', mkt_cap_col='log(circ_mv)') # 中性化\n", + "print('去极值')\n", + "test_data = quantile_filter(test_data, numeric_columns) # 去极值\n", + "# print('中性化')\n", + "# test_data = neutralize_manual(test_data, numeric_columns, industry_col='cat_l2_code', mkt_cap_col='log(circ_mv)')\n", + "all_dates = train_data['trade_date'].unique() # 获取所有唯一的 trade_date\n", + "split_date = all_dates[-validation_days] # 划分点为倒数第 validation_days 天\n", + "train_data_split = train_data[train_data['trade_date'] < split_date] # 训练集\n", + "val_data_split = train_data[train_data['trade_date'] >= split_date] # 验证集\n", + "\n", + "feature_columns, _ = remove_shifted_features(\n", + " train_data_split,\n", + " val_data_split,\n", + " feature_columns)\n", + "\n", + "feature_columns = remove_highly_correlated_features(train_data,\n", + " feature_columns)\n", + "keep_columns = [col for col in train_data.columns if\n", + " col in feature_columns or col in ['ts_code', 'trade_date', 'label', 'future_return',\n", + " 'future_score', 'future_volatility']]\n", + "# train_data = train_data[keep_columns]\n", + "print(f'feature_columns: {feature_columns}')\n", + "\n", + "train_data = train_data.dropna(subset=feature_columns)\n", + "train_data = train_data.dropna(subset=['label'])\n", + "train_data = train_data.reset_index(drop=True)\n", + "\n", + "# print(test_data.tail())\n", + "test_data = test_data.dropna(subset=feature_columns)\n", + "# test_data = test_data.dropna(subset=['label'])\n", + "test_data = test_data.reset_index(drop=True)\n", + "\n", + "print(len(train_data))\n", + "print(f\"最小日期: {train_data['trade_date'].min().strftime('%Y-%m-%d')}\")\n", + "print(f\"最大日期: {train_data['trade_date'].max().strftime('%Y-%m-%d')}\")\n", + "print(len(test_data))\n", + "print(f\"最小日期: {test_data['trade_date'].min().strftime('%Y-%m-%d')}\")\n", + "print(f\"最大日期: {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", + "\n", + "\n", + "# feature_columns_new.remove('cat_l2_code')" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "8f134d435f71e9e2", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-03T14:57:51.050696Z", + "start_time": "2025-04-03T14:57:51.034030Z" + }, + "jupyter": { + "source_hidden": true + } + }, + "outputs": [], + "source": [ + "from sklearn.preprocessing import StandardScaler\n", + "import lightgbm as lgb\n", + "import matplotlib.pyplot as plt\n", + "from sklearn.decomposition import PCA\n", + "\n", + "\n", + "def train_light_model(train_data_df, params, feature_columns, callbacks, evals,\n", + " print_feature_importance=True, num_boost_round=100,\n", + " validation_days=180, use_pca=False, split_date=None): # 新增参数:validation_days\n", + " # 确保数据按时间排序\n", + " train_data_df = train_data_df.sort_values(by='trade_date')\n", + "\n", + " numeric_columns = train_data_df.select_dtypes(include=['float64', 'int64']).columns\n", + " numeric_columns = [col for col in numeric_columns if col in feature_columns]\n", + " # X_train.loc[:, numeric_columns] = scaler.fit_transform(X_train[numeric_columns])\n", + " # X_val.loc[:, numeric_columns] = scaler.transform(X_val[numeric_columns])\n", + " train_data_df = cross_sectional_standardization(train_data_df, numeric_columns)\n", + "\n", + " # 去除标签为空的样本\n", + " train_data_df = train_data_df.dropna(subset=['label'])\n", + " print('原始训练集大小: ', len(train_data_df))\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", + " # 打印划分结果\n", + " print(f\"划分后的训练集大小: {len(train_data_split)}, 验证集大小: {len(val_data_split)}\")\n", + "\n", + " # 提取特征和标签\n", + " X_train = train_data_split[feature_columns]\n", + " y_train = train_data_split['label']\n", + "\n", + " X_val = val_data_split[feature_columns]\n", + " y_val = val_data_split['label']\n", + "\n", + " # 标准化数值特征\n", + " scaler = StandardScaler()\n", + "\n", + " # 计算每个 trade_date 内的样本数(LTR 需要 group 信息)\n", + " train_groups = train_data_split.groupby('trade_date').size().tolist()\n", + " val_groups = val_data_split.groupby('trade_date').size().tolist()\n", + "\n", + " # 处理类别特征\n", + " categorical_feature = [col for col in feature_columns if 'cat' in col]\n", + "\n", + " pca = None\n", + " if use_pca:\n", + " pca = PCA(n_components=0.95) # 或指定 n_components=固定值(如 10)\n", + " numeric_features = [col for col in feature_columns if col not in categorical_feature]\n", + " numeric_pca = pca.fit_transform(X_train[numeric_features])\n", + " X_train = pd.concat([pd.DataFrame(numeric_pca, index=X_train.index), X_train[categorical_feature]], axis=1)\n", + "\n", + " numeric_pca = pca.transform(X_val[numeric_features])\n", + " X_val = pd.concat([pd.DataFrame(numeric_pca, index=X_val.index), X_val[categorical_feature]], axis=1)\n", + "\n", + " # 计算权重(基于时间)\n", + " # trade_date = train_data_split['trade_date'] # 交易日期\n", + " # weights = (trade_date - trade_date.min()).dt.days / (trade_date.max() - trade_date.min()).days + 1\n", + " # weights = train_data_split.groupby('trade_date')['std_return_5'].transform(\n", + " # lambda x: x / x.mean()\n", + " # )\n", + " ud = sorted(train_data_split[\"trade_date\"].unique().tolist())\n", + " date_weights = {date: weight * weight for date, weight in zip(ud, np.linspace(1, 10, len(ud)))}\n", + " params['weight'] = train_data_split[\"trade_date\"].map(date_weights).tolist()\n", + "\n", + " print('feature_columns size: ', len(X_train.columns.tolist()))\n", + "\n", + " train_dataset = lgb.Dataset(\n", + " X_train, label=y_train, group=train_groups,\n", + " categorical_feature=categorical_feature\n", + " )\n", + "\n", + " # weights = val_data_split.groupby('trade_date')['std_return_5'].transform(\n", + " # lambda x: x / x.mean()\n", + " # )\n", + " val_dataset = lgb.Dataset(\n", + " X_val, label=y_val, group=val_groups,\n", + " categorical_feature=categorical_feature\n", + " )\n", + "\n", + " # 训练模型\n", + " model = lgb.train(\n", + " params, train_dataset, num_boost_round=num_boost_round,\n", + " valid_sets=[train_dataset, val_dataset], valid_names=['train', 'valid'],\n", + " callbacks=callbacks\n", + " )\n", + "\n", + " # 打印特征重要性(如果需要)\n", + " if print_feature_importance:\n", + " lgb.plot_metric(evals)\n", + " lgb.plot_importance(model, importance_type='split', max_num_features=20)\n", + " plt.show()\n", + "\n", + " return model, scaler, pca\n", + "\n", + "\n", + "from catboost import CatBoostRanker, Pool\n", + "import numpy as np\n", + "\n", + "\n", + "def train_catboost(train_data_df, test_data_df, feature_columns, params=None, plot=False):\n", + " X_train = train_data_df[feature_columns]\n", + " y_train = train_data_df['label']\n", + "\n", + " X_val = test_data_df[feature_columns]\n", + " y_val = test_data_df['label']\n", + "\n", + " scaler = StandardScaler()\n", + " numeric_columns = X_train.select_dtypes(include=['float64', 'int64']).columns\n", + " X_train.loc[:, numeric_columns] = scaler.fit_transform(X_train[numeric_columns])\n", + " X_val.loc[:, numeric_columns] = scaler.transform(X_val[numeric_columns])\n", + "\n", + " group_train = train_data_df['trade_date'].factorize()[0]\n", + " group_val = test_data_df['trade_date'].factorize()[0]\n", + "\n", + " cat_features = [i for i, col in enumerate(feature_columns) if col.startswith('cat')]\n", + " print(f'cat_features: {cat_features}')\n", + "\n", + " train_pool = Pool(\n", + " data=X_train,\n", + " label=y_train,\n", + " group_id=group_train,\n", + " cat_features=cat_features\n", + " )\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", + " # CatBoost 排序学习模型\n", + " model = CatBoostRanker(**params)\n", + " model.fit(train_pool, eval_set=val_pool, plot=plot, use_best_model=True)\n", + "\n", + " return model, scaler\n" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "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: 739366\n", + "原始训练集大小: 739366\n", + "划分后的训练集大小: 621433, 验证集大小: 117933\n", + "feature_columns size: 93\n", + "Training until validation scores don't improve for 100 rounds\n", + "[100]\ttrain's ndcg@1: 0.790171\tvalid's ndcg@1: 0.396168\n", + "Early stopping, best iteration is:\n", + "[22]\ttrain's ndcg@1: 0.653157\tvalid's ndcg@1: 0.479455\n", + "Evaluated only: ndcg@1\n" + ] + }, + { + "data": { + "image/png": "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5MgCrSotW7a0tLpV1sSJE3F0dCywzd7eHr1ebylDfHw8Li4utGnThn379pXputfXw80332x5XRU5Nz4+nuTkZABLt9/UqVMLHPfvf/+7TNcXoqaSLi0hrMTb25shQ4awbNky0tPTMZlM3H///UUee+rUKZKSkvDx8Slyf2xsbJnv26xZs1KPOXPmDAAdO3Ys83UBrly5QkZGBq1atSq0r02bNvz111/lut71ylL2PNcGXHk8PDwsOU+xsbFkZGTQsmXLQscVta0iiiqv2Wzm/fff55NPPuHcuXMFcm/yutpK4uDgUKjL7drXVZrr68XDwwOAq1ev4ubmRkREBHq9vlDZq6pOhLAWCXiEsKKxY8cyZcoUoqOjueOOO3B3dy/yOLPZjI+PD99//32R+8uSc5Ln+hYHaymupej65NtrlafsxbViaaXk/lSlosr7xhtv8PLLL/PII4/w6quv0rBhQ/R6Pc8880yZRnFVdNRWaeffyHoRwhok4BHCiu69914ef/xxdu7cyfLly4s9rkWLFqxfv55+/fqV+qFfFUPL87qCjhw5Uq5v9t7e3jg6OnLq1KlC+8LDwws8z2tZSExMLLA9IiKinKWtGB8fHxwcHDh9+nShfUVtK0pF6nrFihUMHjyYr776qsD2xMREvLy8yn29qta0aVPMZjPnzp0r0FJX1joRoqaSHB4hrMjFxYVPP/2UuXPnMmLEiGKPe/DBBzGZTLz66quF9uXk5BQIGpydnQsFEeV122234erqyvz588nMzCywr6SWAIPBwLBhw1i1ahWRkZGW7cePH2fdunUFjnVzc8PLy4stW7YU2P7JJ59UquxlZTAYGDJkCKtWreLy5cuW7adPn2bNmjVluoazszNJSUnlvu/1dfjzzz9z6dKlcl2nuuTlWl3/7/Dhhx9aozhCVBlp4RHCyiZOnFjqMQMHDuTxxx9n/vz5HDhwgNtuuw1bW1tOnTrFzz//zPvvv2/J/wkODubTTz/ltddeo2XLlvj4+HDLLbeUq0xubm689957PProo/Ts2dMy983BgwdJT0/nm2++KfbcefPmsXbtWm6++WamTp1KTk4OH374IR06dODQoUMFjn300Ud58803efTRR+nRowdbtmzh5MmT5SprZcydO5e///6bfv368eSTT2Iymfjoo4/o2LEjBw4cKPX84OBgli9fzowZM+jZsycuLi4lBq4Ad911F6+88gqTJ0+mb9++HD58mO+//57mzZtX0auqnODgYO677z4WLlxIfHy8ZVh63r9LbZqcUohrScAjRC2xaNEigoOD+eyzz3jhhRewsbEhKCiIhx9+mH79+lmOmz17NhEREfzvf/8jJSWFgQMHljvgAfjXv/6Fj48Pb775Jq+++iq2tra0bduW//znPyWe17lzZ9atW8eMGTOYPXs2jRs3Zt68eURFRRUKeGbPns2VK1dYsWIFP/30E3fccQdr1qwpNjm7qgUHB7NmzRpmzpzJyy+/TGBgIK+88grHjx/nxIkTpZ4/depUDhw4wNdff817771H06ZNSw14XnjhBdLS0li2bBnLly+ne/furF69mueff76qXlalffvtt/j5+fHDDz/w66+/MmTIEJYvX06bNm1qxazWQhRFp0mmmhBCFDBy5EiOHj1aZC5SfXXgwAG6devGd999x7hx46xdHCHKTXJ4hBD12vULj546dYq//vrLsiRHfVTUYqwLFy5Er9czYMAAK5RIiMqTLi0hRL3WvHlzJk2aRPPmzYmIiODTTz/Fzs6OZ5991tpFs5r//e9/hIWFMXjwYGxsbFizZg1r1qzhscceIzAw0NrFE6JCpEtLCFGvTZ48mY0bNxIdHY29vT19+vThjTfeoHv37tYumtWEhIQwb948jh07RmpqKk2aNGH8+PG8+OKLsgK7qLUk4BFCCCFEnSc5PEIIIYSo8yTgEUIIIUSdV+86Y81mM5cvX8bV1VUm0BJCCCFqCU3TSElJwd/fH72+Au01mpV99NFHWtOmTTV7e3utV69e2q5du4o9Njs7W5s3b57WvHlzzd7eXuvcubO2Zs2act3vwoULGiAPechDHvKQhzxq4ePChQsVijes2sKTNyX7okWL6N27NwsXLmTYsGGEh4cXOdPqSy+9xHfffccXX3xB27ZtWbduHffeey/bt2+nW7duZbqnq6srAOfOnaNhw4ZV+nrqE6PRyN9//21Z4kCUn9Rh5UkdVg2px8qTOqy80uowOTmZwMBAy+d4eVk14FmwYAFTpkxh8uTJgJo6f/Xq1SxevLjIadaXLl3Kiy++yPDhwwF48sknWb9+Pe+++y7fffddme6Z143l6uqKm5tbFb2S+sdoNOLk5ISbm5v8564gqcPKkzqsGlKPlSd1WHllrcOKpqNYLeDJ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Tk5O2r333qtFRUVZr9C1wPUBj9Rh6f744w+tY8eOmr29vda2bVvt888/L7DfbDZrL7/8subr66vZ29trt956qxYeHm6l0tZMycnJ2tNPP601adJEc3Bw0Jo3b669+OKLBT5UpB4L2rhxY5F/AydOnKhpWtnqKz4+XhszZozm4uKiubm5aZMnT9ZSUlKs8Gqsp6R6PHfuXLGfNRs3brRcoyrqUadp10yzKYQQQghRB9WbHB4hhBBC1F8S8AghhBCizpOARwghhBB1ngQ8QgghhKjzJOARQgghRJ0nAY8QQggh6jwJeIQQQghR50nAI4SoF4KCgli4cKG1iyGEsBIJeIQQVW7SpEmMHDkSgEGDBvHMM8/csHsvWbIEd3f3Qtv37NnDY489dsPKIYSoWWysXQAhhCiL7Oxs7OzsKny+t7d3FZZGCFHbSAuPEKLaTJo0ic2bN/P++++j0+nQ6XScP38egCNHjnDHHXfg4uKCr68v48ePJy4uznLuoEGDmD59Os888wxeXl4MGzYMgAULFtCpUyecnZ0JDAxk6tSppKamArBp0yYmT55MUlKS5X5z584FCndpRUZGcs899+Di4oKbmxsPPvggMTExlv1z586la9euLF26lKCgIBo0aMDo0aNJSUmxHLNixQo6deqEo6Mjnp6eDBkyhLS0tGqqTSFEZUjAI4SoNu+//z59+vRhypQpREVFERUVRWBgIImJidxyyy1069aNvXv3snbtWmJiYnjwwQcLnP/NN99gZ2fHtm3bWLRoEQB6vZ4PPviAo0eP8s0337BhwwaeffZZAPr27cvChQtxc3Oz3G/mzJmFymU2m7nnnntISEhg8+bNhISEcPbsWR566KECx505c4ZVq1bx559/8ueff7J582befPNNAKKiohgzZgyPPPIIx48fZ9OmTYwaNQpZnlCImkm6tIQQ1aZBgwbY2dnh5OSEn5+fZftHH31Et27deOONNyzbFi9eTGBgICdPnqR169YAtGrViv/9738FrnltPlBQUBCvvfYaTzzxBJ988gl2dnY0aNAAnU5X4H7XCw0N5fDhw5w7d47AwEAAvv32Wzp06MCePXvo2bMnoAKjJUuW4OrqCsD48eMJDQ3l9ddfJyoqipycHEaNGkXTpk0B6NSpUyVqSwhRnaSFRwhxwx08eJCNGzfi4uJiebRt2xZQrSp5goODC527fv16br31VgICAnB1dWX8+PHEx8eTnp5e5vsfP36cwMBAS7AD0L59e9zd3Tl+/LhlW1BQkCXYAWjUqBGxsbEAdOnShVtvvZVOnTrxwAMP8MUXX3D16tWyV4IQ4oaSgEcIccOlpqYyYsQIDhw4UOBx6tQpBgwYYDnO2dm5wHnnz5/nrrvuonPnzvzyyy+EhYXx8ccfAyqpuarZ2toWeK7T6TCbzQAYDAZCQkJYs2YN7du358MPP6RNmzacO3euysshhKg8CXiEENXKzs4Ok8lUYFv37t05evQoQUFBtGzZssDj+iDnWmFhYZjNZt59911uuukmWrduzeXLl0u93/XatWvHhQsXuHDhgmXbsWPHSExMpH379mV+bTqdjn79+jFv3jz279+PnZ0dv/76a5nPF0LcOBLwCCGqVVBQELt27eL8+fPExcVhNpuZNm0aCQkJjBkzhj179nDmzBnWrVvH5P9v325VFIjiMIy/q100TDGKCIqDxWTQMgh6AwZBsBkNxskOE7R4CWKwCF6ARTAo4iUYpmq0ieyGhWVlP3DDGv48v3xgOCcMD+ej2/01VrLZrK7XqyaTiY7Ho6bT6cdl5s/fu1wuWq1WOp/P3x51eZ4n13XVbrd1OBy02+3U6XRUq9VULpcfmtd2u9VwONR+v1cURVosFjqdTsrn839bIABPQfAA+FeDwUDxeFyFQkGO4yiKIqXTaW02G91uN9Xrdbmuq36/r2QyqVjs599SqVTSeDxWGIYqFouazWYKguBuTKVSUa/XU6vVkuM4Xy49S+87M8vlUqlUStVqVZ7nKZPJaD6fPzyvRCKh9XqtZrOpXC4n3/c1Go3UaDQeXxwAT/PyyhtKAABgHDs8AADAPIIHAACYR/AAAADzCB4AAGAewQMAAMwjeAAAgHkEDwAAMI/gAQAA5hE8AADAPIIHAACYR/AAAADzCB4AAGDeG+z1HD9lXy3tAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "train data size: 739366\n" + ] + } + ], + "source": [ + "print('train data size: ', len(train_data))\n", + "\n", + "label_gain = list(range(len(train_data['label'].unique())))\n", + "label_gain = [gain * gain for gain in label_gain]\n", + "light_params = {\n", + " 'label_gain': label_gain,\n", + " 'objective': 'lambdarank',\n", + " 'metric': 'ndcg',\n", + " 'learning_rate': 0.03,\n", + " 'num_leaves': 32,\n", + " # 'min_data_in_leaf': 128,\n", + " 'max_depth': 8,\n", + " 'max_bin': 32,\n", + " 'feature_fraction': 0.7,\n", + " 'bagging_fraction': 0.7,\n", + " 'bagging_freq': 5,\n", + " 'lambda_l1': 0.1,\n", + " 'lambda_l2': 0.1,\n", + " 'boosting': 'gbdt',\n", + " 'verbosity': -1,\n", + " 'extra_trees': True,\n", + " 'max_position': 5,\n", + " 'ndcg_at': 1,\n", + " 'quant_train_renew_leaf': True,\n", + " 'lambdarank_truncation_level': 1,\n", + " 'lambdarank_position_bias_regularization': 1,\n", + " 'seed': 7\n", + "}\n", + "evals = {}\n", + "\n", + "gc.collect()\n", + "\n", + "use_pca = False\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_light_model(train_data.dropna(subset=['label']),\n", + " light_params, feature_columns,\n", + " [lgb.log_evaluation(period=100),\n", + " lgb.callback.record_evaluation(evals),\n", + " lgb.early_stopping(100, first_metric_only=True)\n", + " ], evals,\n", + " num_boost_round=1000, validation_days=120,\n", + " print_feature_importance=True, use_pca=use_pca)\n", + "\n", + "print('train data size: ', len(train_data))" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "5d1522a7538db91b", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-03T15:04:39.656944Z", + "start_time": "2025-04-03T15:04:39.298483Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "9.663390663390663\n", + " trade_date ts_code future_return future_score label\n", + "29 2023-08-01 605188.SH 0.046809 NaN 19.0\n", + "1017 2023-08-02 000014.SZ 0.062638 NaN 19.0\n", + "1988 2023-08-03 000890.SZ 0.132200 NaN 19.0\n", + "2958 2023-08-04 603536.SH -0.062907 NaN 0.0\n", + "3921 2023-08-07 603828.SH -0.016221 NaN 2.0\n", + "4948 2023-08-08 002336.SZ -0.032209 NaN 0.0\n", + "5902 2023-08-09 600155.SH 0.027813 NaN 19.0\n", + "6964 2023-08-10 002787.SZ 0.085946 NaN 19.0\n", + "7842 2023-08-11 601136.SH 0.103558 NaN 19.0\n", + "8841 2023-08-14 603536.SH -0.028928 NaN 0.0\n" + ] + } + ], + "source": [ + "# train_data = train_data.sort_values(by='trade_date')\n", + "# all_dates = train_data['trade_date'].unique() # 获取所有唯一的 trade_date\n", + "# split_date = all_dates[-120] # 划分点为倒数第 validation_days 天\n", + "# print(split_date)\n", + "# print(all_dates)\n", + "# val_data_split = train_data[train_data['trade_date'] >= split_date] # 验证集\n", + "\n", + "score_df = test_data\n", + "numeric_columns = score_df.select_dtypes(include=['float64', 'int64']).columns\n", + "numeric_columns = [col for col in numeric_columns if col in feature_columns]\n", + "# score_df.loc[:, numeric_columns] = scaler.transform(score_df[numeric_columns])\n", + "score_df = cross_sectional_standardization(score_df, numeric_columns)\n", + "\n", + "if use_pca and pca is not None:\n", + " categorical_feature = [col for col in feature_columns if 'cat' in col]\n", + " numeric_features = [col for col in feature_columns if col not in categorical_feature]\n", + " numeric_pca = pca.transform(score_df[numeric_features])\n", + " score_df = pd.concat([pd.DataFrame(numeric_pca), score_df[categorical_feature],\n", + " score_df[['trade_date', 'ts_code', 'future_return', 'future_score', 'label']]], axis=1)\n", + " score_df['score'] = model.predict(score_df[[col for col in score_df.columns if\n", + " col not in ['trade_date', 'ts_code', 'future_return', 'future_score',\n", + " 'label']]])\n", + "else:\n", + " score_df['score'] = model.predict(score_df[feature_columns])\n", + "# train_data['score'] = catboost_model.predict(train_data[feature_columns])\n", + "score_df = score_df.loc[score_df.groupby('trade_date')['score'].idxmax()]\n", + "# score_df = score_df[score_df['score'] > 0]\n", + "score_df[['trade_date', 'score', 'ts_code']].to_csv('predictions_test.tsv', index=False)\n", + "print(score_df['label'].mean())\n", + "print(score_df[['trade_date', 'ts_code', 'future_return', 'future_score', 'label']].head(10))" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "d86af99d15cb3bdd", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-03T15:03:25.791021Z", + "start_time": "2025-04-03T15:03:25.537833Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " trade_date ts_code close open future_return\n", + "2563 2020-01-02 603577.SH 16.43 16.34 0.001819\n", + "5354 2020-01-03 603577.SH 16.52 16.49 -0.007251\n", + "8146 2020-01-06 603577.SH 16.43 16.55 0.007299\n", + "10936 2020-01-07 603577.SH 16.56 16.44 -0.012689\n", + "13725 2020-01-08 603577.SH 16.34 16.55 0.003636\n", + "... ... ... ... ... ...\n", + "3817882 2025-04-02 603577.SH 19.74 19.78 0.003579\n", + "3820967 2025-04-03 603577.SH 19.63 19.56 -0.066596\n", + "3824052 2025-04-07 603577.SH 17.66 18.92 -0.078398\n", + "3827137 2025-04-08 603577.SH 16.34 17.73 0.047170\n", + "3830225 2025-04-09 603577.SH 16.65 15.90 NaN\n", + "\n", + "[1275 rows x 5 columns]\n" + ] + } + ], + "source": [ + "print(df[(df['ts_code'] == '603577.SH') & (df['trade_date'] >= '2018-06-04')][\n", + " ['trade_date', 'ts_code', 'close', 'open', 'future_return']])" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "ef9d068e-67f7-412c-bbd8-cdee7492dbc9", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-03T15:03:25.893508Z", + "start_time": "2025-04-03T15:03:25.878525Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "nan\n", + "nan\n" + ] + } + ], + "source": [ + "print(train_data[\"future_score\"].corr(train_data[\"label\"]))\n", + "print(test_data[\"future_score\"].corr(test_data[\"label\"]))\n" + ] + } + ], + "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 +} diff --git a/code/train/Regression.ipynb b/main/train/Regression.ipynb similarity index 99% rename from code/train/Regression.ipynb rename to main/train/Regression.ipynb index dbb6b53..86e99f2 100644 --- a/code/train/Regression.ipynb +++ b/main/train/Regression.ipynb @@ -27,7 +27,7 @@ } }, "source": [ - "from utils.utils import read_and_merge_h5_data\n", + "from code.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", @@ -502,7 +502,7 @@ "id": "a735bc02ceb4d872", "metadata": {}, "source": [ - "from utils.factor import get_act_factor\n", + "from code.utils.factor import get_act_factor\n", "\n", "\n", "def read_industry_data(h5_filename):\n", @@ -612,9 +612,6 @@ "metadata": {}, "source": [ "from scipy.stats import ks_2samp, wasserstein_distance\n", - "from sklearn.metrics import roc_auc_score\n", - "from sklearn.model_selection import train_test_split\n", - "from sklearn.preprocessing import StandardScaler\n", "\n", "\n", "def remove_shifted_features(train_data, test_data, feature_columns, ks_threshold=0.05, wasserstein_threshold=0.1,\n", @@ -769,8 +766,6 @@ " or 'act' in col or 'af' in col]\n", " return remaining_features\n", "\n", - "import pandas as pd\n", - "from sklearn.preprocessing import StandardScaler\n", "\n", "def cross_sectional_standardization(df, features):\n", " df_sorted = df.sort_values(by='trade_date') # 按时间排序\n", diff --git a/main/train/RollingRank.ipynb b/main/train/RollingRank.ipynb new file mode 100644 index 0000000..7a15122 --- /dev/null +++ b/main/train/RollingRank.ipynb @@ -0,0 +1,4641 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "79a7758178bafdd3", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T11:40:18.436038Z", + "start_time": "2025-04-11T11:40:17.948261Z" + }, + "jupyter": { + "source_hidden": true + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "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": 2, + "id": "a79cafb06a7e0e43", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T11:41:04.815882Z", + "start_time": "2025-04-11T11:40:18.445044Z" + }, + "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", + "\n", + "RangeIndex: 5123740 entries, 0 to 5123739\n", + "Data columns (total 31 columns):\n", + " # Column Dtype \n", + "--- ------ ----- \n", + " 0 ts_code object \n", + " 1 trade_date datetime64[ns]\n", + " 2 open float64 \n", + " 3 close float64 \n", + " 4 high float64 \n", + " 5 low float64 \n", + " 6 vol float64 \n", + " 7 pct_chg float64 \n", + " 8 turnover_rate float64 \n", + " 9 pe_ttm float64 \n", + " 10 circ_mv float64 \n", + " 11 volume_ratio float64 \n", + " 12 is_st bool \n", + " 13 up_limit float64 \n", + " 14 down_limit float64 \n", + " 15 buy_sm_vol float64 \n", + " 16 sell_sm_vol float64 \n", + " 17 buy_lg_vol float64 \n", + " 18 sell_lg_vol float64 \n", + " 19 buy_elg_vol float64 \n", + " 20 sell_elg_vol float64 \n", + " 21 net_mf_vol float64 \n", + " 22 his_low float64 \n", + " 23 his_high float64 \n", + " 24 cost_5pct float64 \n", + " 25 cost_15pct float64 \n", + " 26 cost_50pct float64 \n", + " 27 cost_85pct float64 \n", + " 28 cost_95pct float64 \n", + " 29 weight_avg float64 \n", + " 30 winner_rate float64 \n", + "dtypes: bool(1), datetime64[ns](1), float64(28), object(1)\n", + "memory usage: 1.2+ GB\n", + "None\n" + ] + } + ], + "source": [ + "from main.utils.utils import read_and_merge_h5_data\n", + "\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'],\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', 'volume_ratio',\n", + " 'is_st'], df=df, join='inner')\n", + "df = df[df['trade_date'] >= '2021-01-01']\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": 3, + "id": "cac01788dac10678", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T11:41:09.893735Z", + "start_time": "2025-04-11T11:41:05.012569Z" + }, + "jupyter": { + "source_hidden": true + } + }, + "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', 'l1_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', 'l1_code']])\n", + "\n", + " # 填充未匹配到的记录(trade_date 早于所有 in_date 的情况)\n", + " merged['l1_code'] = merged['l1_code'].fillna(\n", + " merged['ts_code'].map(min_in_date_per_ts.set_index('ts_code')['l1_code'])\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', 'l1_code']])" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "c4e9e1d31da6dba6", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T11:41:10.010631Z", + "start_time": "2025-04-11T11:41:09.918750Z" + }, + "jupyter": { + "source_hidden": true + } + }, + "outputs": [], + "source": [ + "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", + " 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', 'RSI', 'MACD', 'Signal_line',\n", + " 'MACD_hist', '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": 5, + "id": "a735bc02ceb4d872", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T11:41:10.069433Z", + "start_time": "2025-04-11T11:41:10.018146Z" + } + }, + "outputs": [], + "source": [ + "\n", + "import talib\n", + "\n", + "\n", + "def get_rolling_factor(df):\n", + " old_columns = df.columns.tolist()[:]\n", + "\n", + " # 按股票和日期排序(如果尚未排序)\n", + " df = df.sort_values(by=['ts_code', 'trade_date'])\n", + "\n", + " grouped = df.groupby('ts_code', group_keys=False)\n", + "\n", + " epsilon = 1e-8\n", + " df['lg_elg_net_buy_vol'] = df['buy_lg_vol'] + df['buy_elg_vol'] - df['sell_lg_vol'] - df['sell_elg_vol']\n", + " # 检查 'volume' 列是否存在且有效\n", + " df['flow_lg_elg_intensity'] = df['lg_elg_net_buy_vol'] / (df['vol'] + epsilon)\n", + " \n", + " \n", + " # 2. 散户与主力背离度 (Retail vs Institutional Divergence)\n", + " # 衡量小单净流入与(大单+超大单)净流入的差异或比率\n", + " df['sm_net_buy_vol'] = df['buy_sm_vol'] - df['sell_sm_vol']\n", + " df['flow_divergence_diff'] = df['sm_net_buy_vol'] - df['lg_elg_net_buy_vol']\n", + " # 比率形式可能更稳定\n", + " df['flow_divergence_ratio'] = df['sm_net_buy_vol'] / (df['lg_elg_net_buy_vol'] + np.sign(df['lg_elg_net_buy_vol']) * epsilon + epsilon) # 复杂处理避免0/0\n", + " \n", + " # 3. 资金流结构变动 (Flow Structure Change - Relative Strength of Large Flow)\n", + " # 大单+超大单买入额占总买入额的比例的变化\n", + " df['total_buy_vol'] = df['buy_sm_vol'] + df['buy_lg_vol'] + df['buy_elg_vol']\n", + " df['lg_elg_buy_prop'] = (df['buy_lg_vol'] + df['buy_elg_vol']) / (df['total_buy_vol'] + epsilon)\n", + " df['flow_struct_buy_change'] = grouped['lg_elg_buy_prop'].diff(1) # 1日变化\n", + " \n", + " # 4. 资金流加速度 (Flow Acceleration)\n", + " # 净主力资金流的变化率(二阶导)\n", + " df['lg_elg_net_buy_vol_change'] = grouped['lg_elg_net_buy_vol'].diff(1)\n", + " df['flow_lg_elg_accel'] = grouped['lg_elg_net_buy_vol_change'].diff(1)\n", + " \n", + " # # 5. 极端资金流事件 (Categorical: Extreme Flow Event)\n", + " # # 定义主力资金流强度是否处于其历史极端水平(例如,过去N天的90分位数以上或10分位数以下)\n", + " # rolling_window = 20 # 可调整窗口期\n", + " \n", + " # # Step 1: Calculate the rolling quantiles separately\n", + " # rolling_high = grouped['flow_lg_elg_intensity'].rolling(rolling_window, min_periods=1).quantile(0.9) # min_periods=1 保证窗口未满时也有输出\n", + " # rolling_low = grouped['flow_lg_elg_intensity'].rolling(rolling_window, min_periods=1).quantile(0.1)\n", + " \n", + " # # Step 2: Assign the results to the DataFrame\n", + " # # 确保 df 和 rolling_high/low 的索引是一致的\n", + " # # 如果 df 的索引在此期间没有被修改过,这通常是安全的\n", + " # df['flow_lg_elg_intensity_rolling_high'] = rolling_high\n", + " # df['flow_lg_elg_intensity_rolling_low'] = rolling_low\n", + " \n", + " # # Step 3: Continue with the logic using the new columns\n", + " # conditions_flow = [\n", + " # df['flow_lg_elg_intensity'] > df['flow_lg_elg_intensity_rolling_high'],\n", + " # df['flow_lg_elg_intensity'] < df['flow_lg_elg_intensity_rolling_low']\n", + " # ]\n", + " # choices_flow = [1, -1] # 1: 极端流入, -1: 极端流出\n", + " # df['cat_extreme_flow'] = np.select(conditions_flow, choices_flow, default=0)\n", + " \n", + " # --- 筹码分布因子 ---\n", + " \n", + " # 6. 筹码集中度 (Chip Concentration)\n", + " # 衡量筹码分布的紧密程度,例如 95% 与 5% 成本价的差距,相对于当前价格进行标准化\n", + " # 检查 'close' 列是否存在且有效\n", + " df['chip_concentration_range'] = (df['cost_95pct'] - df['cost_5pct']) / (df['close'] + epsilon)\n", + " \n", + " \n", + " # 7. 筹码分布偏度 (Chip Distribution Skewness Proxy)\n", + " # 比较中位数成本 (cost_50pct) 和加权平均成本 (weight_avg)\n", + " # weight_avg > cost_50pct 暗示高成本区有较多筹码(右偏)\n", + " df['chip_skewness'] = (df['weight_avg'] - df['cost_50pct']) / (df['cost_50pct'] + epsilon)\n", + " \n", + " # 8. 浮筹比例 (Floating Chips Proxy)\n", + " # 衡量短期内(例如15%成本线以下)的筹码比例与总获利盘比例的关系\n", + " # winner_rate 高但 cost_15pct 接近当前价,可能意味着大部分获利盘成本不高,易浮动\n", + " # 这里简化为:获利盘比例 与 (当前价-15%成本价)/当前价 的乘积\n", + " price_dist_cost15 = (df['close'] - df['cost_15pct']) / (df['close'] + epsilon)\n", + " df['floating_chip_proxy'] = df['winner_rate'] * np.maximum(0, price_dist_cost15) # 只考虑价格高于15%成本线的情况\n", + " \n", + " # 9. 成本支撑强度变化 (Cost Support Strength Change)\n", + " # 观察低位筹码成本(如 5% 或 15% 分位点)的变化率,看支撑位是上移还是下移\n", + " df['cost_support_15pct_change'] = grouped['cost_15pct'].pct_change(1) * 100 # 百分比变化\n", + " \n", + " # 10. 获利盘压力/支撑区 (Categorical: Winner Rate Zone & Price Position)\n", + " # 结合获利盘比例和当前价格相对于筹码成本的位置\n", + " # 例如: 价格在 85% 成本线之上 & 获利盘 > 0.8 -> 高位派发风险区?\n", + " # 价格在 15% 成本线之下 & 获利盘 < 0.2 -> 低位吸筹潜力区?\n", + " conditions_winner = [\n", + " (df['close'] > df['cost_85pct']) & (df['winner_rate'] > 0.8), # 高位 & 高获利盘\n", + " (df['close'] < df['cost_15pct']) & (df['winner_rate'] < 0.2), # 低位 & 低获利盘\n", + " (df['close'] > df['cost_50pct']) & (df['winner_rate'] > 0.5), # 中高位 & 多数获利\n", + " (df['close'] < df['cost_50pct']) & (df['winner_rate'] < 0.5), # 中低位 & 多数亏损\n", + " ]\n", + " choices_winner = [1, 2, 3, 4] # 1:高风险区, 2:低潜力区, 3:中上获利区, 4:中下亏损区\n", + " df['cat_winner_price_zone'] = np.select(conditions_winner, choices_winner, default=0) # 0: 其他\n", + " \n", + " \n", + " # --- 结合因子 ---\n", + " \n", + " # 11. 主力行为与筹码结构一致性 (Flow-Chip Consistency)\n", + " # 例如:主力净买入发生在价格接近下方筹码密集区(如 cost_15pct 到 cost_50pct)时\n", + " price_near_low_support = (df['close'] > df['cost_15pct']) & (df['close'] < df['cost_50pct'])\n", + " df['flow_chip_consistency'] = df['lg_elg_net_buy_vol'] * price_near_low_support.astype(int)\n", + " # 可以进一步标准化或做成 categorical\n", + " \n", + " # 12. 获利了结压力/承接盘强度 (Profit-Taking Pressure vs Absorption)\n", + " # 在高获利盘(winner_rate > 0.7)的情况下,观察主力资金是净流出(了结)还是净流入(高位换手/承接)\n", + " high_winner_rate_flag = (df['winner_rate'] > 0.7).astype(int)\n", + " df['profit_taking_vs_absorb'] = df['lg_elg_net_buy_vol'] * high_winner_rate_flag\n", + " # 正值表示高获利盘下主力仍在买入(承接),负值表示主力在卖出(了结)\n", + " \n", + " \n", + " # 清理临时列和可能产生的 NaN (可选,根据需要处理)\n", + " cols_to_drop = ['lg_elg_net_buy_vol', 'sm_net_buy_vol', 'total_buy_vol', 'lg_elg_buy_prop',\n", + " 'lg_elg_net_buy_vol_change', 'flow_lg_elg_intensity_rolling_high',\n", + " 'flow_lg_elg_intensity_rolling_low']\n", + " # df = df.drop(columns=cols_to_drop)\n", + "\n", + "\n", + " window = 20\n", + " df['_is_positive'] = (df['pct_chg'] > 0).astype(int)\n", + " df['_is_negative'] = (df['pct_chg'] < 0).astype(int)\n", + " df['cat_is_positive'] = (df['pct_chg'] > 0).astype(int)\n", + "\n", + " # 分离正负收益率 (用于计算各自的均值和平方均值)\n", + " # 注意:这里我们保留原始收益率用于计算,而不是 clip 到 0\n", + " df['_pos_returns'] = df['pct_chg'].where(df['pct_chg'] > 0, 0) # 非正设为0,便于求和\n", + " df['_neg_returns'] = df['pct_chg'].where(df['pct_chg'] < 0, 0) # 非负设为0,便于求和\n", + "\n", + " # 计算收益率的平方 (用于计算 E[X^2])\n", + " df['_pos_returns_sq'] = np.square(df['_pos_returns'])\n", + " df['_neg_returns_sq'] = np.square(df['_neg_returns']) # 平方后负数变正\n", + "\n", + " # 4. 计算滚动统计量 (使用内置函数,速度较快)\n", + " # 计算正收益日的统计量\n", + " rolling_pos_count = grouped['_is_positive'].rolling(window, min_periods=max(1, window // 2)).sum()\n", + " rolling_pos_sum = grouped['_pos_returns'].rolling(window, min_periods=max(1, window // 2)).sum()\n", + " rolling_pos_sum_sq = grouped['_pos_returns_sq'].rolling(window, min_periods=max(1, window // 2)).sum()\n", + "\n", + " # 计算负收益日的统计量\n", + " rolling_neg_count = grouped['_is_negative'].rolling(window, min_periods=max(1, window // 2)).sum()\n", + " rolling_neg_sum = grouped['_neg_returns'].rolling(window, min_periods=max(1, window // 2)).sum()\n", + " rolling_neg_sum_sq = grouped['_neg_returns_sq'].rolling(window, min_periods=max(1, window // 2)).sum()\n", + "\n", + " # 5. 计算方差和标准差\n", + " pos_mean_sq = rolling_pos_sum_sq / rolling_pos_count\n", + " pos_mean = rolling_pos_sum / rolling_pos_count\n", + " pos_var = pos_mean_sq - np.square(pos_mean)\n", + " pos_var = pos_var.where(rolling_pos_count >= 2, np.nan).clip(lower=0)\n", + " upside_vol = np.sqrt(pos_var)\n", + "\n", + " neg_mean_sq = rolling_neg_sum_sq / rolling_neg_count\n", + " neg_mean = rolling_neg_sum / rolling_neg_count # 注意 neg_mean 是负数\n", + " neg_var = neg_mean_sq - np.square(neg_mean)\n", + " neg_var = neg_var.where(rolling_neg_count >= 2, np.nan).clip(lower=0)\n", + " downside_vol = np.sqrt(neg_var)\n", + "\n", + " # rolling 操作后结果带有 MultiIndex,需要去除股票代码层级以便合并\n", + " df['upside_vol'] = upside_vol.reset_index(level=0, drop=True)\n", + " df['downside_vol'] = downside_vol.reset_index(level=0, drop=True)\n", + "\n", + " df['vol_ratio'] = df['upside_vol'] / df['downside_vol']\n", + " df['vol_ratio'] = df['vol_ratio'].replace([np.inf, -np.inf], np.nan).fillna(0) # 或 fillna(np.nan)\n", + "\n", + " df['return_skew'] = grouped['pct_chg'].rolling(window=5).skew().reset_index(0, drop=True)\n", + " df['return_kurtosis'] = grouped['pct_chg'].rolling(window=5).kurt().reset_index(0, drop=True)\n", + "\n", + " # 因子 1:短期成交量变化率\n", + " df['volume_change_rate'] = (\n", + " grouped['vol'].rolling(window=2).mean() /\n", + " grouped['vol'].rolling(window=10).mean() - 1\n", + " ).reset_index(level=0, drop=True) # 确保索引对齐\n", + "\n", + " # 因子 2:成交量突破信号\n", + " max_volume = grouped['vol'].rolling(window=5).max().reset_index(level=0, drop=True) # 确保索引对齐\n", + " df['cat_volume_breakout'] = (df['vol'] > max_volume)\n", + "\n", + " # 因子 3:换手率均线偏离度\n", + " mean_turnover = grouped['turnover_rate'].rolling(window=3).mean().reset_index(level=0, drop=True)\n", + " std_turnover = grouped['turnover_rate'].rolling(window=3).std().reset_index(level=0, drop=True)\n", + " df['turnover_deviation'] = (df['turnover_rate'] - mean_turnover) / std_turnover\n", + "\n", + " # 因子 4:换手率激增信号\n", + " df['cat_turnover_spike'] = (df['turnover_rate'] > mean_turnover + 2 * std_turnover)\n", + "\n", + " # 因子 5:量比均值\n", + " df['avg_volume_ratio'] = grouped['volume_ratio'].rolling(window=3).mean().reset_index(level=0, drop=True)\n", + "\n", + " # 因子 6:量比突破信号\n", + " max_volume_ratio = grouped['volume_ratio'].rolling(window=5).max().reset_index(level=0, drop=True)\n", + " df['cat_volume_ratio_breakout'] = (df['volume_ratio'] > max_volume_ratio)\n", + "\n", + " df['vol_spike'] = grouped.apply(\n", + " lambda x: pd.Series(x['vol'].rolling(20).mean(), index=x.index)\n", + " )\n", + " df['vol_std_5'] = grouped['vol'].pct_change().rolling(window=5).std()\n", + "\n", + " # 计算 ATR\n", + " df['atr_14'] = grouped.apply(\n", + " lambda x: pd.Series(talib.ATR(x['high'].values, x['low'].values, x['close'].values, timeperiod=14),\n", + " index=x.index)\n", + " )\n", + " df['atr_6'] = grouped.apply(\n", + " lambda x: pd.Series(talib.ATR(x['high'].values, x['low'].values, x['close'].values, timeperiod=6),\n", + " index=x.index)\n", + " )\n", + "\n", + " # 计算 OBV 及其均线\n", + " df['obv'] = grouped.apply(\n", + " lambda x: pd.Series(talib.OBV(x['close'].values, x['vol'].values), index=x.index)\n", + " )\n", + " print(df.columns)\n", + " df['maobv_6'] = grouped.apply(\n", + " lambda x: pd.Series(talib.SMA(x['obv'].values, timeperiod=6), index=x.index)\n", + " )\n", + "\n", + " df['rsi_3'] = grouped.apply(\n", + " lambda x: pd.Series(talib.RSI(x['close'].values, timeperiod=3), index=x.index)\n", + " )\n", + " # df['rsi_6'] = grouped.apply(\n", + " # lambda x: pd.Series(talib.RSI(x['close'].values, timeperiod=6), index=x.index)\n", + " # )\n", + " # df['rsi_9'] = grouped.apply(\n", + " # lambda x: pd.Series(talib.RSI(x['close'].values, timeperiod=9), index=x.index)\n", + " # )\n", + "\n", + " # 计算 return_10 和 return_20\n", + " df['return_5'] = grouped['close'].apply(lambda x: x / x.shift(5) - 1)\n", + " # df['return_10'] = grouped['close'].apply(lambda x: x / x.shift(10) - 1)\n", + " df['return_20'] = grouped['close'].apply(lambda x: x / x.shift(20) - 1)\n", + "\n", + " # df['avg_close_5'] = grouped['close'].apply(lambda x: x.rolling(window=5).mean() / x)\n", + "\n", + " # 计算标准差指标\n", + " df['std_return_5'] = grouped['close'].apply(lambda x: x.pct_change().rolling(window=5).std())\n", + " # df['std_return_15'] = grouped['close'].apply(lambda x: x.pct_change().rolling(window=15).std())\n", + " # df['std_return_25'] = grouped['close'].apply(lambda x: x.pct_change().rolling(window=25).std())\n", + " df['std_return_90'] = grouped['close'].apply(lambda x: x.pct_change().rolling(window=90).std())\n", + " df['std_return_90_2'] = grouped['close'].apply(lambda x: x.shift(10).pct_change().rolling(window=90).std())\n", + "\n", + " # 计算 EMA 指标\n", + " df['_ema_5'] = grouped['close'].apply(\n", + " lambda x: pd.Series(talib.EMA(x.values, timeperiod=5), index=x.index)\n", + " )\n", + " df['_ema_13'] = grouped['close'].apply(\n", + " lambda x: pd.Series(talib.EMA(x.values, timeperiod=13), index=x.index)\n", + " )\n", + " df['_ema_20'] = grouped['close'].apply(\n", + " lambda x: pd.Series(talib.EMA(x.values, timeperiod=20), index=x.index)\n", + " )\n", + " df['_ema_60'] = grouped['close'].apply(\n", + " lambda x: pd.Series(talib.EMA(x.values, timeperiod=60), index=x.index)\n", + " )\n", + "\n", + " # 计算 act_factor1, act_factor2, act_factor3, act_factor4\n", + " df['act_factor1'] = grouped['_ema_5'].apply(\n", + " lambda x: np.arctan((x / x.shift(1) - 1) * 100) * 57.3 / 50\n", + " )\n", + " df['act_factor2'] = grouped['_ema_13'].apply(\n", + " lambda x: np.arctan((x / x.shift(1) - 1) * 100) * 57.3 / 40\n", + " )\n", + " df['act_factor3'] = grouped['_ema_20'].apply(\n", + " lambda x: np.arctan((x / x.shift(1) - 1) * 100) * 57.3 / 21\n", + " )\n", + " df['act_factor4'] = grouped['_ema_60'].apply(\n", + " lambda x: np.arctan((x / x.shift(1) - 1) * 100) * 57.3 / 10\n", + " )\n", + "\n", + " # 根据 trade_date 截面计算排名\n", + " df['rank_act_factor1'] = df.groupby('trade_date', group_keys=False)['act_factor1'].rank(ascending=False, pct=True)\n", + " df['rank_act_factor2'] = df.groupby('trade_date', group_keys=False)['act_factor2'].rank(ascending=False, pct=True)\n", + " df['rank_act_factor3'] = df.groupby('trade_date', group_keys=False)['act_factor3'].rank(ascending=False, pct=True)\n", + "\n", + " df['log(circ_mv)'] = np.log(df['circ_mv'])\n", + "\n", + " window_high_volume = 5\n", + " window_close_stddev = 20\n", + " period_delta = 5\n", + "\n", + " # 计算每只股票的滚动协方差\n", + " def calculate_rolling_cov(group):\n", + " return group['high'].rolling(window_high_volume).cov(group['vol'])\n", + "\n", + " df['cov'] = grouped.apply(calculate_rolling_cov)\n", + "\n", + " # 计算每只股票的协方差差分\n", + " def calculate_delta_cov(group):\n", + " return group['cov'].diff(period_delta)\n", + "\n", + " df['delta_cov'] = grouped.apply(calculate_delta_cov)\n", + "\n", + " # 计算每只股票的滚动标准差\n", + " def calculate_stddev_close(group):\n", + " return group['close'].rolling(window_close_stddev).std()\n", + "\n", + " df['_stddev_close'] = grouped.apply(calculate_stddev_close)\n", + " df['_rank_stddev'] = df.groupby('trade_date')['_stddev_close'].rank(pct=True)\n", + " df['alpha_22_improved'] = -1 * df['delta_cov'] * df['_rank_stddev']\n", + "\n", + " df['alpha_003'] = np.where(df['high'] != df['low'],\n", + " (df['close'] - df['open']) / (df['high'] - df['low']),\n", + " 0)\n", + "\n", + " df['alpha_007'] = grouped.apply(lambda x: x['close'].rolling(5).corr(x['vol']))\n", + " df['alpha_007'] = df.groupby('trade_date', group_keys=False)['alpha_007'].rank(ascending=True, pct=True)\n", + "\n", + " df['alpha_013'] = grouped['close'].transform(lambda x: x.rolling(5).sum() - x.rolling(20).sum())\n", + " df['alpha_013'] = df.groupby('trade_date', group_keys=False)['alpha_013'].rank(ascending=True, pct=True)\n", + "\n", + " df['cat_up_limit'] = (df['close'] == df['up_limit']) # 是否涨停(1表示涨停,0表示未涨停)\n", + " df['cat_down_limit'] = (df['close'] == df['down_limit']) # 是否跌停(1表示跌停,0表示未跌停)\n", + " df['up_limit_count_10d'] = grouped['cat_up_limit'].rolling(window=10, min_periods=1).sum().reset_index(level=0,\n", + " drop=True)\n", + " df['down_limit_count_10d'] = grouped['cat_down_limit'].rolling(window=10, min_periods=1).sum().reset_index(level=0,\n", + " drop=True)\n", + "\n", + " # 3. 最近连续涨跌停天数\n", + " def calculate_consecutive_limits(series):\n", + " \"\"\"\n", + " 计算连续涨停/跌停天数。\n", + " \"\"\"\n", + " consecutive_up = series * (series.groupby((series != series.shift()).cumsum()).cumcount() + 1)\n", + " consecutive_down = series * (series.groupby((series != series.shift()).cumsum()).cumcount() + 1)\n", + " return consecutive_up, consecutive_down\n", + "\n", + " # 连续涨停天数\n", + " df['consecutive_up_limit'] = grouped['cat_up_limit'].apply(\n", + " lambda x: calculate_consecutive_limits(x)[0]\n", + " )\n", + "\n", + " df['vol_break'] = np.where((df['close'] > df['cost_85pct']) & (df['volume_ratio'] > 2), 1, 0)\n", + "\n", + " df['weight_roc5'] = grouped['weight_avg'].apply(lambda x: x.pct_change(5))\n", + "\n", + " def rolling_corr(group):\n", + " roc_close = group['close'].pct_change()\n", + " roc_weight = group['weight_avg'].pct_change()\n", + " return roc_close.rolling(10).corr(roc_weight)\n", + "\n", + " df['price_cost_divergence'] = grouped.apply(rolling_corr)\n", + "\n", + " df['smallcap_concentration'] = (1 / df['log(circ_mv)']) * (df['cost_85pct'] - df['cost_15pct'])\n", + "\n", + " # 16. 筹码稳定性指数 (20日波动率)\n", + " df['weight_std20'] = grouped['weight_avg'].apply(lambda x: x.rolling(20).std())\n", + " df['cost_stability'] = df['weight_std20'] / grouped['weight_avg'].transform(lambda x: x.rolling(20).mean())\n", + "\n", + " # 17. 成本区间突破标记\n", + " df['high_cost_break_days'] = grouped.apply(lambda g: g['close'].gt(g['cost_95pct']).rolling(5).sum())\n", + "\n", + " # 20. 筹码-流动性风险\n", + " df['liquidity_risk'] = (df['cost_95pct'] - df['cost_5pct']) * (\n", + " 1 / grouped['vol'].transform(lambda x: x.rolling(10).mean()))\n", + "\n", + " # 7. 市值波动率因子 (使用 grouped)\n", + " df['turnover_std'] = grouped['turnover_rate'].transform(lambda x: x.rolling(window=20).std())\n", + " df['mv_volatility'] = grouped.apply(lambda x: x['turnover_std'] / x['log(circ_mv)'])\n", + "\n", + " # 8. 市值成长性因子\n", + " df['volume_growth'] = grouped['vol'].pct_change(periods=20)\n", + " df['mv_growth'] = df['volume_growth'] / df['log(circ_mv)']\n", + "\n", + " # AR 指标\n", + " df[\"ar\"] = grouped.apply(\n", + " lambda x: (x[\"high\"].div(x[\"open\"]).rolling(3).sum()) / (x[\"open\"].div(x[\"low\"]).rolling(3).sum()) * 100)\n", + "\n", + " # BR 指标\n", + " df[\"pre_close\"] = grouped[\"close\"].shift(1)\n", + " df[\"br_up\"] = (df[\"high\"] - df[\"pre_close\"]).clip(lower=0)\n", + " df[\"br_down\"] = (df[\"pre_close\"] - df[\"low\"]).clip(lower=0)\n", + " df[\"br\"] = grouped.apply(lambda x: (x[\"br_up\"].rolling(3).sum()) / (x[\"br_down\"].rolling(3).sum()) * 100)\n", + "\n", + " # ARBR\n", + " df['arbr'] = df['ar'] - df['br']\n", + " df.drop(columns=[\"pre_close\", \"br_up\", \"br_down\", 'ar', 'br'], inplace=True)\n", + "\n", + " df.drop(columns=['weight_std20'], inplace=True, errors='ignore')\n", + " df.drop(\n", + " columns=['_is_positive', '_is_negative', '_pos_returns', '_neg_returns', '_pos_returns_sq', '_neg_returns_sq'],\n", + " inplace=True, errors='ignore')\n", + " new_columns = [col for col in df.columns.tolist()[:] if col not in old_columns]\n", + "\n", + " return df, new_columns\n", + "\n", + "\n", + "def get_simple_factor(df):\n", + " old_columns = df.columns.tolist()[:]\n", + " df = df.sort_values(by=['ts_code', 'trade_date'])\n", + "\n", + " alpha = 0.5\n", + " df['momentum_factor'] = df['volume_change_rate'] + alpha * df['turnover_deviation']\n", + " df['resonance_factor'] = df['volume_ratio'] * df['pct_chg']\n", + " df['log_close'] = np.log(df['close'])\n", + "\n", + " df['cat_vol_spike'] = df['vol'] > 2 * df['vol_spike']\n", + "\n", + " df['up'] = (df['high'] - df[['close', 'open']].max(axis=1)) / df['close']\n", + " df['down'] = (df[['close', 'open']].min(axis=1) - df['low']) / df['close']\n", + "\n", + " df['obv-maobv_6'] = df['obv'] - df['maobv_6']\n", + "\n", + " # 计算比值指标\n", + " df['std_return_5 / std_return_90'] = df['std_return_5'] / df['std_return_90']\n", + " # df['std_return_5 / std_return_25'] = df['std_return_5'] / df['std_return_25']\n", + "\n", + " # 计算标准差差值\n", + " df['std_return_90 - std_return_90_2'] = df['std_return_90'] - df['std_return_90_2']\n", + "\n", + " # df['cat_af1'] = df['act_factor1'] > 0\n", + " df['cat_af2'] = df['act_factor2'] > df['act_factor1']\n", + " df['cat_af3'] = df['act_factor3'] > df['act_factor2']\n", + " df['cat_af4'] = df['act_factor4'] > df['act_factor3']\n", + "\n", + " # 计算 act_factor5 和 act_factor6\n", + " df['act_factor5'] = df['act_factor1'] + df['act_factor2'] + df['act_factor3'] + df['act_factor4']\n", + " df['act_factor6'] = (df['act_factor1'] - df['act_factor2']) / np.sqrt(\n", + " df['act_factor1'] ** 2 + df['act_factor2'] ** 2)\n", + "\n", + " df['active_buy_volume_large'] = df['buy_lg_vol'] / df['net_mf_vol']\n", + " df['active_buy_volume_big'] = df['buy_elg_vol'] / df['net_mf_vol']\n", + " df['active_buy_volume_small'] = df['buy_sm_vol'] / df['net_mf_vol']\n", + "\n", + " df['buy_lg_vol_minus_sell_lg_vol'] = (df['buy_lg_vol'] - df['sell_lg_vol']) / df['net_mf_vol']\n", + " df['buy_elg_vol_minus_sell_elg_vol'] = (df['buy_elg_vol'] - df['sell_elg_vol']) / df['net_mf_vol']\n", + "\n", + " df['log(circ_mv)'] = np.log(df['circ_mv'])\n", + "\n", + " df['ctrl_strength'] = (df['cost_85pct'] - df['cost_15pct']) / (df['his_high'] - df['his_low'])\n", + "\n", + " df['low_cost_dev'] = (df['close'] - df['cost_5pct']) / (df['cost_50pct'] - df['cost_5pct'])\n", + "\n", + " df['asymmetry'] = (df['cost_95pct'] - df['cost_50pct']) / (df['cost_50pct'] - df['cost_5pct'])\n", + "\n", + " df['lock_factor'] = df['turnover_rate'] * (\n", + " 1 - (df['cost_95pct'] - df['cost_5pct']) / (df['his_high'] - df['his_low']))\n", + "\n", + " df['cat_vol_break'] = (df['close'] > df['cost_85pct']) & (df['volume_ratio'] > 2)\n", + "\n", + " df['cost_atr_adj'] = (df['cost_95pct'] - df['cost_5pct']) / df['atr_14']\n", + "\n", + " # 12. 小盘股筹码集中度\n", + " df['smallcap_concentration'] = (1 / df['log(circ_mv)']) * (df['cost_85pct'] - df['cost_15pct'])\n", + "\n", + " df['cat_golden_resonance'] = ((df['close'] > df['weight_avg']) &\n", + " (df['volume_ratio'] > 1.5) &\n", + " (df['winner_rate'] > 0.7))\n", + "\n", + " df['mv_turnover_ratio'] = df['turnover_rate'] / df['log(circ_mv)']\n", + "\n", + " df['mv_adjusted_volume'] = df['vol'] / df['log(circ_mv)']\n", + "\n", + " df['mv_weighted_turnover'] = df['turnover_rate'] * (1 / df['log(circ_mv)'])\n", + "\n", + " df['nonlinear_mv_volume'] = df['vol'] / df['log(circ_mv)']\n", + "\n", + " df['mv_volume_ratio'] = df['volume_ratio'] / df['log(circ_mv)']\n", + "\n", + " df['mv_momentum'] = df['turnover_rate'] * df['volume_ratio'] / df['log(circ_mv)']\n", + "\n", + " drop_columns = [col for col in df.columns if col.startswith('_')]\n", + " df.drop(columns=drop_columns, inplace=True, errors='ignore')\n", + "\n", + " new_columns = [col for col in df.columns.tolist()[:] if col not in old_columns]\n", + " return df, new_columns\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "53f86ddc0677a6d7", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T11:41:15.152455Z", + "start_time": "2025-04-11T11:41:10.084099Z" + }, + "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", + " 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", + "industry_df = read_industry_data('../../data/sw_daily.h5')" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "dbe2fd8021b9417f", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T11:41:15.172103Z", + "start_time": "2025-04-11T11:41:15.167533Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['ts_code', 'open', 'close', 'high', 'low', 'circ_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', 'l2_code', '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', 'l1_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": 8, + "id": "85c3e3d0235ffffa", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T11:42:31.593231Z", + "start_time": "2025-04-11T11:41:15.188575Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Index(['ts_code', 'trade_date', 'open', 'close', 'high', 'low', 'vol',\n", + " 'pct_chg', 'turnover_rate', 'pe_ttm', 'circ_mv', 'volume_ratio',\n", + " 'is_st', 'up_limit', 'down_limit', 'buy_sm_vol', 'sell_sm_vol',\n", + " 'buy_lg_vol', 'sell_lg_vol', 'buy_elg_vol', 'sell_elg_vol',\n", + " 'net_mf_vol', 'his_low', 'his_high', 'cost_5pct', 'cost_15pct',\n", + " 'cost_50pct', 'cost_85pct', 'cost_95pct', 'weight_avg', 'winner_rate',\n", + " 'l1_code', 'l2_code', '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_buy_consolidation_20...\n", + "Finished lg_buy_consolidation_20.\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 intraday_lg_flow_corr_20 (Placeholder - complex implementation)...\n", + "Finished intraday_lg_flow_corr_20 (Placeholder).\n", + "Calculating cap_neutral_cost_metric (Placeholder - requires statsmodels)...\n", + "Finished cap_neutral_cost_metric (Placeholder).\n", + "Calculating hurst_net_mf_vol_60 (Placeholder - requires hurst library)...\n", + "Error: 'hurst' library not installed. Cannot calculate factor.\n", + "Finished hurst_net_mf_vol_60 (Placeholder).\n", + "\n", + "Index: 3148690 entries, 0 to 3148689\n", + "Columns: 157 entries, ts_code to hurst_net_mf_vol_60\n", + "dtypes: bool(12), datetime64[ns](1), float64(137), int32(3), int64(1), object(3)\n", + "memory usage: 3.4+ GB\n", + "None\n" + ] + } + ], + "source": [ + "import numpy as np\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'] >= '2021-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", + "df = filter_data(df)\n", + "# df = get_technical_factor(df)\n", + "# df = get_act_factor(df)\n", + "# df = get_money_flow_factor(df)\n", + "# df = get_alpha_factor(df)\n", + "# df = get_limit_factor(df)\n", + "# df = get_cyp_perf_factor(df)\n", + "# df = get_mv_factors(df)\n", + "df, _ = get_rolling_factor(df)\n", + "df, _ = get_simple_factor(df)\n", + "from main.factor.factor import *\n", + "lg_flow_mom_corr(df, N=20, M=60)\n", + "lg_buy_consolidation(df, N=20)\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", + "intraday_lg_flow_corr(df, N=20) # Placeholder\n", + "cap_neutral_cost_metric(df) # Placeholder\n", + "hurst_exponent_flow(df, N=60) # Placeholder\n", + "# calculate_complex_factor(df)\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", + "# df = df.merge(industry_df, on=['l1_code', 'trade_date'], how='left')\n", + "df = df.rename(columns={'l1_code': 'cat_l1_code'})\n", + "df = df.rename(columns={'l2_code': 'cat_l2_code'})\n", + "\n", + "# df = df.merge(index_data, on='trade_date', how='left')\n", + "\n", + "print(df.info())" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "f4f16d63ad18d1bc", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T11:42:31.775937Z", + "start_time": "2025-04-11T11:42:31.765571Z" + }, + "jupyter": { + "source_hidden": true + } + }, + "outputs": [], + "source": [ + "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": 10, + "id": "40e6b68a91b30c79", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T11:42:33.375897Z", + "start_time": "2025-04-11T11:42:31.854118Z" + }, + "jupyter": { + "source_hidden": true + } + }, + "outputs": [], + "source": [ + "import pandas as pd\n", + "\n", + "from scipy.stats import ks_2samp\n", + "from sklearn.discriminant_analysis import StandardScaler\n", + "\n", + "\n", + "def remove_shifted_features(train_data, feature_columns, ks_threshold=0.05, wasserstein_threshold=0.1, size=0.8,\n", + " log=True, val_data=None):\n", + " dropped_features = []\n", + "\n", + " if val_data is None:\n", + " all_dates = sorted(train_data['trade_date'].unique().tolist()) # 获取所有唯一的 trade_date\n", + " split_date = all_dates[int(len(all_dates) * size)] # 划分点为倒数第 validation_days 天\n", + " train_data_split = train_data[train_data['trade_date'] < split_date] # 训练集\n", + " val_data_split = train_data[train_data['trade_date'] >= split_date] # 验证集\n", + " else:\n", + " train_data_split = train_data\n", + " val_data_split = val_data\n", + "\n", + " # **统计数据漂移**\n", + " numeric_columns = train_data_split.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_split[feature], val_data_split[feature])\n", + " # wasserstein_dist = wasserstein_distance(train_data_split[feature], val_data_split[feature])\n", + "\n", + " # if p_value < ks_threshold or wasserstein_dist > wasserstein_threshold:\n", + " if p_value < ks_threshold:\n", + " dropped_features.append(feature)\n", + " if log:\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", + "\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(df, features, industry_col, mkt_cap_col):\n", + " \"\"\" 手动实现简单回归以提升速度 \"\"\"\n", + "\n", + " for col in features:\n", + " residuals = []\n", + " for _, group in df.groupby(industry_col):\n", + " if len(group) > 1:\n", + " x = np.log(group[mkt_cap_col]) # 市值对数\n", + " y = group[col] # 因子值\n", + " beta = np.cov(y, x)[0, 1] / np.var(x) # 计算斜率\n", + " alpha = np.mean(y) - beta * np.mean(x) # 计算截距\n", + " resid = y - (alpha + beta * x) # 计算残差\n", + " residuals.extend(resid)\n", + " else:\n", + " residuals.extend(group[col]) # 样本不足时保留原值\n", + "\n", + " df[col] = residuals\n", + "\n", + " return 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(\n", + " lambda x: x.rolling(window=min(len(x), window)).quantile(lower_quantile))\n", + " rolling_upper = df.groupby('trade_date')[col].transform(\n", + " 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", + "\n", + "def time_series_quantile_filter(df, features, lower_quantile=0.01, upper_quantile=0.99, window=60):\n", + " df = df.copy()\n", + " # 确保按股票和时间排序\n", + " df = df.sort_values(['ts_code', 'trade_date'])\n", + " grouped = df.groupby('ts_code')\n", + " for col in features:\n", + " # 对每个股票的时间序列计算滚动分位数\n", + " rolling_lower = grouped[col].rolling(window=window, min_periods=window // 2).quantile(lower_quantile)\n", + " rolling_upper = grouped[col].rolling(window=window, min_periods=window // 2).quantile(upper_quantile)\n", + " # rolling结果带有多重索引,需要对齐\n", + " rolling_lower = rolling_lower.reset_index(level=0, drop=True)\n", + " rolling_upper = rolling_upper.reset_index(level=0, drop=True)\n", + " # 应用 clip\n", + " df[col] = np.clip(df[col], rolling_lower, rolling_upper)\n", + " return df\n", + "\n", + "\n", + "def cross_sectional_quantile_filter(df, features, lower_quantile=0.01, upper_quantile=0.99):\n", + " df = df.copy()\n", + " grouped = df.groupby('trade_date')\n", + " for col in features:\n", + " # 计算每日截面的分位数边界\n", + " lower_bound = grouped[col].transform(lambda x: x.quantile(lower_quantile))\n", + " upper_bound = grouped[col].transform(lambda x: x.quantile(upper_quantile))\n", + " # 应用 clip\n", + " df[col] = np.clip(df[col], lower_bound, upper_bound)\n", + " return df\n", + "\n", + "from scipy.stats import spearmanr\n", + "\n", + "def select_top_rankic_features(train_data: pd.DataFrame, feature_columns: list, target_column: str = 'future_return', n: int = 10):\n", + " rankic_values = {}\n", + " numeric_columns = train_data.select_dtypes(include=['float64', 'int64']).columns\n", + " feature_columns = [col for col in numeric_columns if col in feature_columns]\n", + " if target_column not in train_data.columns:\n", + " print(f\"警告: 目标列 '{target_column}' 不存在于 train_data 中。\")\n", + " return []\n", + "\n", + " for feature in feature_columns:\n", + " if feature in train_data.columns:\n", + " factor_values = train_data[feature].values\n", + " target_values = train_data[target_column].values\n", + "\n", + " # 处理 NaN 值\n", + " valid_indices = ~np.isnan(factor_values) & ~np.isnan(target_values)\n", + " factor_values_valid = factor_values[valid_indices]\n", + " target_values_valid = target_values[valid_indices]\n", + "\n", + " if len(factor_values_valid) >= 2:\n", + " correlation, p_value = spearmanr(factor_values_valid, target_values_valid)\n", + " rankic_values[feature] = correlation\n", + " else:\n", + " rankic_values[feature] = np.nan\n", + " print(f\"警告: 特征 '{feature}' 和目标列 '{target_column}' 共同有效的非 NaN 数据点少于 2 个,无法计算 Rank IC。\")\n", + " else:\n", + " print(f\"警告: 特征列 '{feature}' 不存在于 train_data 中。\")\n", + "\n", + " # 根据 Rank IC 的绝对值进行排序\n", + " sorted_rankic = sorted(rankic_values.items(), key=lambda item: abs(item[1]), reverse=True)\n", + "\n", + " # 保留前 n 个特征\n", + " top_n_features = [item[0] for item in sorted_rankic[:n] if not np.isnan(item[1])]\n", + "\n", + " return top_n_features" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "da2bb202843d9275", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T11:42:33.974040Z", + "start_time": "2025-04-11T11:42:33.430374Z" + }, + "jupyter": { + "source_hidden": true + } + }, + "outputs": [], + "source": [ + "from sklearn.preprocessing import StandardScaler\n", + "import lightgbm as lgb\n", + "import matplotlib.pyplot as plt\n", + "\n", + "\n", + "def train_light_model(train_data_df, params, feature_columns, callbacks, evals,\n", + " print_feature_importance=True, num_boost_round=100,\n", + " validation_days=180, use_pca=False, split_date=None): # 新增参数:validation_days\n", + " # 确保数据按时间排序\n", + " train_data_df = train_data_df.sort_values(by='trade_date')\n", + "\n", + " numeric_columns = train_data_df.select_dtypes(include=['float64', 'int64']).columns\n", + " numeric_columns = [col for col in numeric_columns if col in feature_columns]\n", + " # X_train.loc[:, numeric_columns] = scaler.fit_transform(X_train[numeric_columns])\n", + " # X_val.loc[:, numeric_columns] = scaler.transform(X_val[numeric_columns])\n", + " # train_data_df = cross_sectional_standardization(train_data_df, numeric_columns)\n", + "\n", + " # 去除标签为空的样本\n", + " train_data_df = train_data_df.dropna(subset=['label'])\n", + " # print('原始训练集大小: ', len(train_data_df))\n", + "\n", + " # 按时间顺序划分训练集和验证集\n", + " if split_date is None:\n", + " all_dates = train_data_df['trade_date'].unique() # 获取所有唯一的 trade_date\n", + " if validation_days == 0:\n", + " split_date = all_dates[-1]\n", + " else:\n", + " split_date = all_dates[-validation_days] # 划分点为倒数第 validation_days 天\n", + " if validation_days == 0:\n", + " train_data_split = train_data_df\n", + " else:\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", + " # 打印划分结果\n", + " print(f\"划分后的训练集大小: {len(train_data_split)}, 验证集大小: {len(val_data_split)}\")\n", + "\n", + " # 提取特征和标签\n", + " X_train = train_data_split[feature_columns]\n", + " y_train = train_data_split['label']\n", + "\n", + " # 标准化数值特征\n", + " scaler = StandardScaler()\n", + "\n", + " # 计算每个 trade_date 内的样本数(LTR 需要 group 信息)\n", + " train_groups = train_data_split.groupby('trade_date').size().tolist()\n", + " val_groups = val_data_split.groupby('trade_date').size().tolist()\n", + "\n", + " # 处理类别特征\n", + " categorical_feature = [col for col in feature_columns if 'cat' in col]\n", + "\n", + " # 计算权重(基于时间)\n", + " # trade_date = train_data_split['trade_date'] # 交易日期\n", + " # weights = (trade_date - trade_date.min()).dt.days / (trade_date.max() - trade_date.min()).days + 1\n", + " # weights = train_data_split.groupby('trade_date')['std_return_5'].transform(\n", + " # lambda x: x / x.mean()\n", + " # )\n", + " ud = sorted(train_data_split[\"trade_date\"].unique().tolist())\n", + " date_weights = {date: weight * weight for date, weight in zip(ud, np.linspace(1, 10, len(ud)))}\n", + " params['weight'] = train_data_split[\"trade_date\"].map(date_weights).tolist()\n", + "\n", + " train_dataset = lgb.Dataset(\n", + " X_train, label=y_train, group=train_groups,\n", + " categorical_feature=categorical_feature\n", + " )\n", + "\n", + " if validation_days > 0:\n", + " X_val = val_data_split[feature_columns]\n", + " y_val = val_data_split['label']\n", + " val_groups = val_data_split.groupby('trade_date').size().tolist()\n", + " val_dataset = lgb.Dataset(\n", + " X_val, label=y_val, group=val_groups,\n", + " categorical_feature=categorical_feature\n", + " )\n", + " # 训练模型\n", + " model = lgb.train(\n", + " params, train_dataset, num_boost_round=num_boost_round,\n", + " valid_sets=[train_dataset, val_dataset], valid_names=['train', 'valid'],\n", + " callbacks=callbacks\n", + " )\n", + " else:\n", + " model = lgb.train(\n", + " params, train_dataset, num_boost_round=num_boost_round, callbacks=callbacks\n", + " )\n", + "\n", + " # 打印特征重要性(如果需要)\n", + " if print_feature_importance:\n", + " lgb.plot_metric(evals)\n", + " lgb.plot_importance(model, importance_type='split', max_num_features=20)\n", + " plt.show()\n", + "\n", + " return model, scaler, None" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "ff19e3f1e051a489", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T17:50:59.056134Z", + "start_time": "2025-04-11T17:49:54.975048Z" + } + }, + "outputs": [], + "source": [ + "\n", + "days = 1\n", + "df = df.sort_values(by=['ts_code', 'trade_date'])\n", + "# df['future_return'] = df.groupby('ts_code', group_keys=False)['close'].apply(lambda x: x.shift(-days) / x - 1)\n", + "df['future_return'] = (df.groupby('ts_code')['close'].shift(-days) - df.groupby('ts_code')['open'].shift(-1)) / \\\n", + " df.groupby('ts_code')['open'].shift(-1)\n", + "# df['future_return'] = df.groupby('ts_code')['pct_chg'].shift(-1)\n", + "df['future_return2'] = (df.groupby('ts_code')['close'].shift(-1) - df.groupby('ts_code')['open'].shift(-1)) / \\\n", + " df.groupby('ts_code')['open'].shift(-1)\n", + "\n", + "df['future_volatility'] = (\n", + " df.groupby('ts_code')['pct_chg']\n", + " .transform(lambda x: x.rolling(days).std().shift(-days))\n", + ")\n", + "# df['future_score'] = calculate_score(df, days=days, lambda_param=0.3)\n", + "# df['future_score'] = df['future_return'] + 0.3 * df['future_volatility']\n", + "df['label'] = df.groupby('trade_date', group_keys=False)['future_return'].transform(\n", + " lambda x: pd.qcut(x, q=20, labels=False, duplicates='drop')\n", + ")\n", + "df['label2'] = df.groupby('trade_date', group_keys=False)['future_return2'].transform(\n", + " lambda x: pd.qcut(x, q=20, labels=False, duplicates='drop')\n", + ")\n", + "\n", + "# df['label'] = df.groupby('trade_date', group_keys=False)['future_score'].transform(\n", + "# lambda x: pd.qcut(x.rank(method='first'), q=20, labels=False, duplicates='raise')\n", + "# )\n", + "# df['future_score'] = (\n", + "# 0.7 * df['future_return']\n", + "# * 0.3 * df['future_volatility']\n", + "# )" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "id": "27dba27b2e108316", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T17:51:05.124540Z", + "start_time": "2025-04-11T17:50:59.121885Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2025-04-09 00:00:00\n" + ] + } + ], + "source": [ + "# def select_stocks(stock_df: pd.DataFrame) -> pd.DataFrame:\n", + "# \"\"\"\n", + "# 筛选出当日所属行业5日平均涨幅排名前5的行业的股票。\n", + "# 假设输入的 stock_df 包含多日数据,并有 'trade_date', 'return_5', 'cat_l2_code' 列。\n", + "# 筛选时会过滤掉行业中股票数量小于等于5的行业。\n", + "# \"\"\"\n", + "\n", + "# def select_func(day_df: pd.DataFrame):\n", + "# # day_df 是某一个交易日的股票数据\n", + "\n", + "# # 检查必需列是否存在\n", + "# required_cols = ['return_5', 'cat_l2_code']\n", + "# if not all(col in day_df.columns for col in required_cols):\n", + "# # 如果必需列缺失,返回一个列名与原始输入一致的空DataFrame\n", + "# return pd.DataFrame(columns=day_df.columns)\n", + "\n", + "# # --- 新增逻辑:计算每个行业的股票数量并过滤小行业 ---\n", + "# stock_count_by_industry = day_df.groupby('cat_l2_code').size()\n", + "\n", + "# # 找出股票数量大于5的行业代码\n", + "# industries_to_consider = stock_count_by_industry[stock_count_by_industry >= 5].index.tolist()\n", + "\n", + "# # 过滤掉股票数量不足的行业,只保留要考虑的行业数据\n", + "# filtered_day_df = day_df[day_df['cat_l2_code'].isin(industries_to_consider)]\n", + "# # 如果过滤后没有数据,直接返回空DataFrame\n", + "# if filtered_day_df.empty:\n", + "# return pd.DataFrame(columns=day_df.columns)\n", + "\n", + "# industry_avg_return = filtered_day_df.groupby('cat_l2_code')['return_5'].mean()\n", + "\n", + "# industry_avg_return = industry_avg_return.dropna()\n", + "\n", + "# if industry_avg_return.empty:\n", + "# return pd.DataFrame(columns=day_df.columns)\n", + "# top_industries = industry_avg_return.nlargest(5).index.tolist()\n", + "# return filtered_day_df[filtered_day_df['cat_l2_code'].isin(top_industries)].copy()\n", + "\n", + "# stock_df['trade_date'] = pd.to_datetime(stock_df['trade_date'])\n", + "# selected_stocks_df = stock_df.groupby('trade_date', group_keys=False).apply(select_func)\n", + "# return selected_stocks_df\n", + "\n", + "def select_stocks(stock_df):\n", + " def select(group):\n", + " max_stocks = 150\n", + " initial_data = group.nlargest(150, 'return_5')\n", + " unique_labels = initial_data['label'].nunique()\n", + " if unique_labels >= 20 or unique_labels == 0: # 包含标签种类为0的情况\n", + " return initial_data\n", + " for i in range(110, max_stocks + 1, 10):\n", + " data = group.nlargest(i, 'return_5')\n", + " unique_labels = data['label'].nunique()\n", + " if unique_labels >= 20:\n", + " return data\n", + " return group.nlargest(max_stocks, 'return_5') # 如果循环结束仍未找到足够标签,则返回最大数量的股票\n", + " stock_df = stock_df.groupby('trade_date', group_keys=False).apply(select)\n", + " return stock_df\n", + "\n", + "gc.collect()\n", + "\n", + "pdf = select_stocks(df[(df['trade_date'] >= '2022-01-01') & (df['trade_date'] <= '2029-04-07')])\n", + "print(pdf['trade_date'].max())\n", + "\n", + "# pdf['label'] = pdf.groupby('trade_date', group_keys=False)['future_score'].transform(\n", + "# lambda x: pd.qcut(x, q=20, labels=False, duplicates='drop')\n", + "# )" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "id": "ca96fb81e17c4a90", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T17:51:06.005791Z", + "start_time": "2025-04-11T17:51:05.133551Z" + }, + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['vol', 'pct_chg', 'turnover_rate', 'volume_ratio', 'winner_rate', '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', 'cat_up_limit', 'cat_down_limit', 'up_limit_count_10d', 'down_limit_count_10d', 'consecutive_up_limit', 'vol_break', 'weight_roc5', 'smallcap_concentration', 'cost_stability', 'high_cost_break_days', 'liquidity_risk', 'turnover_std', 'mv_volatility', 'volume_growth', 'mv_growth', 'arbr', 'momentum_factor', 'resonance_factor', 'log_close', 'cat_vol_spike', 'up', 'down', 'obv-maobv_6', 'std_return_5 / std_return_90', 'std_return_90 - 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', 'industry_obv', 'industry_return_5', 'industry_return_20', 'industry__ema_5', 'industry__ema_13', 'industry__ema_20', 'industry__ema_60', 'industry_act_factor1', 'industry_act_factor2', 'industry_act_factor3', 'industry_act_factor4', 'industry_act_factor5', 'industry_act_factor6', 'industry_rank_act_factor1', 'industry_rank_act_factor2', 'industry_rank_act_factor3', 'industry_return_5_percentile', 'industry_return_20_percentile']\n" + ] + } + ], + "source": [ + "pdf = pdf.merge(industry_df, on=['cat_l2_code', 'trade_date'], how='left')\n", + "pdf = pdf.replace([np.inf, -np.inf], np.nan)\n", + "\n", + "feature_columns = [col for col in pdf.columns if col in pdf.columns]\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", + "print(feature_columns)\n", + "numeric_columns = pdf.select_dtypes(include=['float64', 'int64']).columns\n", + "numeric_columns = [col for col in numeric_columns if col in feature_columns]\n", + "\n", + "\n", + "# filter_index = pdf['future_volatility'].between(pdf['future_volatility'].quantile(0.01),\n", + "# pdf['future_volatility'].quantile(0.99)) | filter_index" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "id": "6746b3d1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "特征列分析:\n", + "特征: vol 最大值: 56161348.41 最小值: 412.4 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: pct_chg 最大值: 11.94 最小值: -10.26 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: turnover_rate 最大值: 86.2547 最小值: 0.0238 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: volume_ratio 最大值: 147.72 最小值: 0.01 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: winner_rate 最大值: 100.59 最小值: 0.0 NaN 数量: 1279 NaN 占比: 0.010806928601605408\n", + "特征: lg_elg_net_buy_vol 最大值: 6033873.0 最小值: -9918925.0 NaN 数量: 4 NaN 占比: 3.3798056611744823e-05\n", + "特征: flow_lg_elg_intensity 最大值: 1.0001389979829125 最小值: -0.9999999999973489 NaN 数量: 4 NaN 占比: 3.3798056611744823e-05\n", + "特征: sm_net_buy_vol 最大值: 6829932.0 最小值: -3215196.0 NaN 数量: 4 NaN 占比: 3.3798056611744823e-05\n", + "特征: total_buy_vol 最大值: 39152290.0 最小值: 1.0 NaN 数量: 4 NaN 占比: 3.3798056611744823e-05\n", + "特征: lg_elg_buy_prop 最大值: 0.999999999999989 最小值: 0.0 NaN 数量: 4 NaN 占比: 3.3798056611744823e-05\n", + "特征: flow_struct_buy_change 最大值: 0.9999999999996841 最小值: -0.9999999999988995 NaN 数量: 8 NaN 占比: 6.759611322348965e-05\n", + "特征: lg_elg_net_buy_vol_change 最大值: 8937693.0 最小值: -15389140.0 NaN 数量: 8 NaN 占比: 6.759611322348965e-05\n", + "特征: flow_lg_elg_accel 最大值: 23486299.0 最小值: -20413545.0 NaN 数量: 12 NaN 占比: 0.00010139416983523447\n", + "特征: chip_concentration_range 最大值: 3.999999946666667 最小值: 1.8163778247505023e-05 NaN 数量: 1279 NaN 占比: 0.010806928601605408\n", + "特征: chip_skewness 最大值: 2.549999872500006 最小值: -0.24642857134856214 NaN 数量: 1279 NaN 占比: 0.010806928601605408\n", + "特征: floating_chip_proxy 最大值: 99.9998754669836 最小值: 0.0 NaN 数量: 1279 NaN 占比: 0.010806928601605408\n", + "特征: cost_support_15pct_change 最大值: 414.28571428571433 最小值: -34.48275862068966 NaN 数量: 1279 NaN 占比: 0.010806928601605408\n", + "特征: flow_chip_consistency 最大值: 202833.0 最小值: -366952.0 NaN 数量: 4 NaN 占比: 3.3798056611744823e-05\n", + "特征: profit_taking_vs_absorb 最大值: 6033873.0 最小值: -9918925.0 NaN 数量: 4 NaN 占比: 3.3798056611744823e-05\n", + "特征: upside_vol 最大值: 764.4778008549366 最小值: 0.0165334933205142 NaN 数量: 205 NaN 占比: 0.0017321504013519222\n", + "特征: downside_vol 最大值: 33.318906704752486 最小值: 0.0 NaN 数量: 344 NaN 占比: 0.002906632868610055\n", + "特征: vol_ratio 最大值: 2247.2826795128544 最小值: 0.0 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: return_skew 最大值: 2.236072323764041 最小值: -2.2360835630243616 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: return_kurtosis 最大值: 5.176755179819847 最小值: -3.3969791567236354 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: volume_change_rate 最大值: 3.6858958043807935 最小值: -0.9853275631981724 NaN 数量: 205 NaN 占比: 0.0017321504013519222\n", + "特征: turnover_deviation 最大值: 1.1547005383792526 最小值: -1.1547005383793087 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: avg_volume_ratio 最大值: 51.17666666666667 最小值: 0.016666666666666077 NaN 数量: 117 NaN 占比: 0.0009885931558935362\n", + "特征: vol_spike 最大值: 17304365.832 最小值: 2045.5939999999996 NaN 数量: 452 NaN 占比: 0.003819180397127165\n", + "特征: vol_std_5 最大值: 195.41495999867846 最小值: 0.02236335411041056 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: atr_14 最大值: 2724.9261989170695 最小值: 0.03221843258883346 NaN 数量: 354 NaN 占比: 0.002991128010139417\n", + "特征: atr_6 最大值: 3297.064666161383 最小值: 0.047258943138373116 NaN 数量: 67 NaN 占比: 0.0005661174482467259\n", + "特征: obv 最大值: 331434005.2600001 最小值: -65316063.65999998 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: maobv_6 最大值: 301175240.9416665 最小值: -63824730.77333332 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: rsi_3 最大值: 100.0 最小值: 3.3647855304150056 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: return_5 最大值: 5.2441983122362865 最小值: -0.02432244614315504 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: return_20 最大值: 5.826058201058201 最小值: -0.9763608087091757 NaN 数量: 470 NaN 占比: 0.003971271651880017\n", + "特征: std_return_5 最大值: 2.0453098693768377 最小值: 2.3779477130116365e-05 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: std_return_90 最大值: 0.48241224100991925 最小值: 0.006789942003203817 NaN 数量: 1722 NaN 占比: 0.014550063371356147\n", + "特征: std_return_90_2 最大值: 0.4824625874734999 最小值: 0.005959840082236695 NaN 数量: 1892 NaN 占比: 0.015986480777355302\n", + "特征: act_factor1 最大值: 1.7923765799991518 最小值: -1.7515748399531699 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: act_factor2 最大值: 2.22641899181911 最小值: -2.1386570901417015 NaN 数量: 329 NaN 占比: 0.002779890156316012\n", + "特征: act_factor3 最大值: 4.216399393047003 最小值: -3.977208595014822 NaN 数量: 470 NaN 占比: 0.003971271651880017\n", + "特征: act_factor4 最大值: 8.557135781567926 最小值: -7.2482094185227 NaN 数量: 1075 NaN 占比: 0.009083227714406422\n", + "特征: rank_act_factor1 最大值: 1.0 最小值: 0.00031959092361776926 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: rank_act_factor2 最大值: 1.0 最小值: 0.00031969309462915604 NaN 数量: 329 NaN 占比: 0.002779890156316012\n", + "特征: rank_act_factor3 最大值: 1.0 最小值: 0.0003197953309881676 NaN 数量: 470 NaN 占比: 0.003971271651880017\n", + "特征: cov 最大值: 9211640500.850372 最小值: -1025351031.6684246 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: delta_cov 最大值: 9207544083.643394 最小值: -4494222099.3107605 NaN 数量: 205 NaN 占比: 0.0017321504013519222\n", + "特征: alpha_22_improved 最大值: 4494222099.3107605 最小值: -9207544083.643394 NaN 数量: 452 NaN 占比: 0.003819180397127165\n", + "特征: alpha_003 最大值: 1.0 最小值: -1.0 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: alpha_007 最大值: 1.0 最小值: 0.0003197953309881676 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: alpha_013 最大值: 1.0 最小值: 0.00032 NaN 数量: 452 NaN 占比: 0.003819180397127165\n", + "特征: up_limit_count_10d 最大值: 10.0 最小值: 0.0 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: down_limit_count_10d 最大值: 3.0 最小值: 0.0 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: consecutive_up_limit 最大值: 17 最小值: 0 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: weight_roc5 最大值: 4.360439560439561 最小值: -0.4019607843137255 NaN 数量: 1279 NaN 占比: 0.010806928601605408\n", + "特征: smallcap_concentration 最大值: 31.905964365688405 最小值: 0.0 NaN 数量: 1279 NaN 占比: 0.010806928601605408\n", + "特征: cost_stability 最大值: 0.9735383316685713 最小值: 0.0 NaN 数量: 1727 NaN 占比: 0.014592310942120828\n", + "特征: high_cost_break_days 最大值: 5.0 最小值: 0.0 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: liquidity_risk 最大值: 0.02727568902683877 最小值: 1.5428810759666794e-08 NaN 数量: 1482 NaN 占比: 0.012522179974651458\n", + "特征: turnover_std 最大值: 29.89517932088497 最小值: 0.014570369174964438 NaN 数量: 452 NaN 占比: 0.003819180397127165\n", + "特征: mv_volatility 最大值: 2.394008567880757 最小值: 0.0008012062079915182 NaN 数量: 452 NaN 占比: 0.003819180397127165\n", + "特征: volume_growth 最大值: 293.7911250760605 最小值: -0.9934612815303644 NaN 数量: 470 NaN 占比: 0.003971271651880017\n", + "特征: mv_growth 最大值: 24.5789647232902 最小值: -0.08405345257185749 NaN 数量: 470 NaN 占比: 0.003971271651880017\n", + "特征: arbr 最大值: 100.95433748749065 最小值: -168097.89086977823 NaN 数量: 3225 NaN 占比: 0.027249683143219267\n", + "特征: momentum_factor 最大值: 4.193633393631664 最小值: -1.50529286642075 NaN 数量: 205 NaN 占比: 0.0017321504013519222\n", + "特征: resonance_factor 最大值: 932.1131999999999 最小值: -700.3989 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: log_close 最大值: 10.555153146092515 最小值: -0.916290731874155 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: up 最大值: 0.20949720670391064 最小值: 0.0 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: down 最大值: 0.1827803785874212 最小值: 0.0 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: obv-maobv_6 最大值: 91437345.39833334 最小值: -27497457.346666686 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: std_return_5 / std_return_90 最大值: 4.303704383077534 最小值: 0.00044776171350482643 NaN 数量: 1722 NaN 占比: 0.014550063371356147\n", + "特征: std_return_90 - std_return_90_2 最大值: 0.20102002437102318 最小值: -0.12445401518813239 NaN 数量: 1892 NaN 占比: 0.015986480777355302\n", + "特征: act_factor5 最大值: 16.79229294033777 最小值: -15.078581389549012 NaN 数量: 1075 NaN 占比: 0.009083227714406422\n", + "特征: act_factor6 最大值: 1.4142127937678 最小值: -1.414213562373081 NaN 数量: 329 NaN 占比: 0.002779890156316012\n", + "特征: active_buy_volume_large 最大值: 46116.0 最小值: -39192.25 NaN 数量: 7 NaN 占比: 5.9146599070553444e-05\n", + "特征: active_buy_volume_big 最大值: 24021.0 最小值: -16309.69642857143 NaN 数量: 7 NaN 占比: 5.9146599070553444e-05\n", + "特征: active_buy_volume_small 最大值: 101231.0 最小值: -60896.75 NaN 数量: 7 NaN 占比: 5.9146599070553444e-05\n", + "特征: buy_lg_vol_minus_sell_lg_vol 最大值: 6315.5 最小值: -10406.0 NaN 数量: 7 NaN 占比: 5.9146599070553444e-05\n", + "特征: buy_elg_vol_minus_sell_elg_vol 最大值: 15169.0 最小值: -4364.428571428572 NaN 数量: 7 NaN 占比: 5.9146599070553444e-05\n", + "特征: ctrl_strength 最大值: 0.8142857142857142 最小值: 0.0 NaN 数量: 1279 NaN 占比: 0.010806928601605408\n", + "特征: low_cost_dev 最大值: 159371.40000000023 最小值: -2.2444444444444476 NaN 数量: 1345 NaN 占比: 0.011364596535699198\n", + "特征: asymmetry 最大值: 44.00000000000096 最小值: 0.0 NaN 数量: 1345 NaN 占比: 0.011364596535699198\n", + "特征: lock_factor 最大值: 76.68641720430107 最小值: 0.008677083333333335 NaN 数量: 1279 NaN 占比: 0.010806928601605408\n", + "特征: cost_atr_adj 最大值: 88.61860174093074 最小值: 0.0002894966414380616 NaN 数量: 1621 NaN 占比: 0.01369666244190959\n", + "特征: mv_turnover_ratio 最大值: 7.519401708236303 最小值: 0.001323022901502634 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: mv_adjusted_volume 最大值: 3675084.7886439813 最小值: 33.57880521115875 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: mv_weighted_turnover 最大值: 7.519401708236303 最小值: 0.001323022901502634 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: nonlinear_mv_volume 最大值: 3675084.7886439813 最小值: 33.57880521115875 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: mv_volume_ratio 最大值: 11.5095150992187 最小值: 0.0007549215390555348 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: mv_momentum 最大值: 829.2617138502171 最小值: 5.7669216057474964e-05 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: lg_flow_mom_corr_20_60 最大值: 0.9968522209091177 最小值: -0.9776689270624704 NaN 数量: 6 NaN 占比: 5.0697084917617235e-05\n", + "特征: lg_flow_accel 最大值: 23486299.0 最小值: -20413545.0 NaN 数量: 12 NaN 占比: 0.00010139416983523447\n", + "特征: profit_pressure 最大值: 1219604.64459375 最小值: -32.29264392059554 NaN 数量: 1279 NaN 占比: 0.010806928601605408\n", + "特征: underwater_resistance 最大值: 0.09745390693548084 最小值: -0.4039719240263924 NaN 数量: 1279 NaN 占比: 0.010806928601605408\n", + "特征: cost_conc_std_20 最大值: 0.7414146004910032 最小值: 0.0 NaN 数量: 205 NaN 占比: 0.0017321504013519222\n", + "特征: profit_decay_20 最大值: 63.63523419055014 最小值: -201.87032418942292 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: vol_amp_loss_20 最大值: 5.1986896779851355 最小值: 0.0 NaN 数量: 1482 NaN 占比: 0.012522179974651458\n", + "特征: vol_drop_profit_cnt_5 最大值: 3.0 最小值: 0.0 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: lg_flow_vol_interact_20 最大值: 42.845555877280596 最小值: 0.018505969076852875 NaN 数量: 205 NaN 占比: 0.0017321504013519222\n", + "特征: cost_break_confirm_cnt_5 最大值: 5.0 最小值: -4.0 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: atr_norm_channel_pos_14 最大值: 14.000000000000002 最小值: 0.0 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: turnover_diff_skew_20 最大值: 4.459639535682866 最小值: -3.4969687384726353 NaN 数量: 235 NaN 占比: 0.0019856358259400086\n", + "特征: lg_sm_flow_diverge_20 最大值: 1.999775245257143 最小值: -0.41700592094659733 NaN 数量: 205 NaN 占比: 0.0017321504013519222\n", + "特征: pullback_strong_20_20 最大值: 253.95565410196681 最小值: -675.6615306894897 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: vol_wgt_hist_pos_20 最大值: 13.234987955447918 最小值: 0.0 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: vol_adj_roc_20 最大值: 1.538154444208913 最小值: -1.2754286421125485 NaN 数量: 0 NaN 占比: 0.0\n", + "特征: industry_obv 最大值: 33357180.0 最小值: -17512962.0 NaN 数量: 474 NaN 占比: 0.004005069708491762\n", + "特征: industry_return_5 最大值: 0.524069457577581 最小值: -0.2267495964295888 NaN 数量: 474 NaN 占比: 0.004005069708491762\n", + "特征: industry_return_20 最大值: 0.8652496476530473 最小值: -0.3099103385178408 NaN 数量: 474 NaN 占比: 0.004005069708491762\n", + "特征: industry__ema_5 最大值: 73493.7215643315 最小值: 378.62741122827816 NaN 数量: 474 NaN 占比: 0.004005069708491762\n", + "特征: industry__ema_13 最大值: 73322.71553688897 最小值: 379.5203678371781 NaN 数量: 474 NaN 占比: 0.004005069708491762\n", + "特征: industry__ema_20 最大值: 74222.1571225974 最小值: 381.359643019974 NaN 数量: 474 NaN 占比: 0.004005069708491762\n", + "特征: industry__ema_60 最大值: 74769.33183235777 最小值: 401.1538611414743 NaN 数量: 474 NaN 占比: 0.004005069708491762\n", + "特征: industry_act_factor1 最大值: 1.6986610274753051 最小值: -1.5933252482654896 NaN 数量: 474 NaN 占比: 0.004005069708491762\n", + "特征: industry_act_factor2 最大值: 2.0328341745548633 最小值: -1.7615460391418363 NaN 数量: 474 NaN 占比: 0.004005069708491762\n", + "特征: industry_act_factor3 最大值: 3.7292424474002375 最小值: -3.0674619011216553 NaN 数量: 474 NaN 占比: 0.004005069708491762\n", + "特征: industry_act_factor4 最大值: 6.509680946056449 最小值: -4.337297946027389 NaN 数量: 474 NaN 占比: 0.004005069708491762\n", + "特征: industry_act_factor5 最大值: 13.628812636988545 最小值: -10.685144264799936 NaN 数量: 474 NaN 占比: 0.004005069708491762\n", + "特征: industry_act_factor6 最大值: 1.4142134956701664 最小值: -1.4142135617846938 NaN 数量: 474 NaN 占比: 0.004005069708491762\n", + "特征: industry_rank_act_factor1 最大值: 1.0 最小值: 0.002277904328018223 NaN 数量: 474 NaN 占比: 0.004005069708491762\n", + "特征: industry_rank_act_factor2 最大值: 1.0 最小值: 0.002277904328018223 NaN 数量: 474 NaN 占比: 0.004005069708491762\n", + "特征: industry_rank_act_factor3 最大值: 1.0 最小值: 0.002277904328018223 NaN 数量: 474 NaN 占比: 0.004005069708491762\n", + "特征: industry_return_5_percentile 最大值: 1.0 最小值: 0.002277904328018223 NaN 数量: 474 NaN 占比: 0.004005069708491762\n", + "特征: industry_return_20_percentile 最大值: 1.0 最小值: 0.002277904328018223 NaN 数量: 474 NaN 占比: 0.004005069708491762\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "\n", + "def analyze_features(df: pd.DataFrame, feature_columns: list):\n", + " \"\"\"\n", + " 分析 DataFrame 中指定特征列的基本信息,包括最大值、最小值和 NaN 数量。\n", + "\n", + " Args:\n", + " df (pd.DataFrame): 要分析的数据框。\n", + " feature_columns (list): 需要分析的特征列名列表。\n", + " \"\"\"\n", + " print(\"特征列分析:\")\n", + " for col in feature_columns:\n", + " if col in df.columns:\n", + " max_val = df[col].max()\n", + " min_val = df[col].min()\n", + " nan_count = df[col].isnull().sum()\n", + " df_count = len(df)\n", + " print(f\"特征: {col} 最大值: {max_val} 最小值: {min_val} NaN 数量: {nan_count} NaN 占比: {nan_count / df_count}\")\n", + " else:\n", + " print(f\"警告: 特征列 '{col}' 不存在于 DataFrame 中。\")\n", + "\n", + "\n", + "analyze_features(pdf, numeric_columns)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "id": "81d4570663ae21d7", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T17:52:34.201657Z", + "start_time": "2025-04-11T17:51:34.426871Z" + } + }, + "outputs": [], + "source": [ + "\n", + "# pdf = time_series_quantile_filter(pdf, numeric_columns)\n", + "pdf = cross_sectional_quantile_filter(pdf, numeric_columns)\n", + "pdf = cross_sectional_standardization(pdf, numeric_columns)\n", + "\n", + "pdf = pdf.sort_values(by=['ts_code', 'trade_date'])\n", + "\n", + "filter_index = pdf['future_return'].between(pdf['future_return'].quantile(0.01), pdf['future_return'].quantile(0.99))\n", + "\n", + "feature_columns = remove_highly_correlated_features(pdf, feature_columns)" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "id": "92428d543f4727ad", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T17:54:48.103331Z", + "start_time": "2025-04-11T17:54:47.906668Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 92, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# print('train data size: ', len(train_data))\n", + "\n", + "label_gain = list(range(len(df['label'].unique())))\n", + "label_gain = [gain * gain for gain in label_gain]\n", + "light_params = {\n", + " 'label_gain': label_gain,\n", + " 'objective': 'lambdarank',\n", + " 'metric': 'ndcg',\n", + " 'learning_rate': 0.03,\n", + " 'num_leaves': 32,\n", + " # 'min_data_in_leaf': 128,\n", + " 'max_depth': 8,\n", + " 'max_bin': 32,\n", + " 'feature_fraction': 0.7,\n", + " 'bagging_fraction': 0.7,\n", + " 'bagging_freq': 1,\n", + " 'lambda_l1': 0.1,\n", + " 'lambda_l2': 0.1,\n", + " 'boosting': 'gbdt',\n", + " 'verbosity': -1,\n", + " 'extra_trees': True,\n", + " 'max_position': 20,\n", + " 'ndcg_at': 1,\n", + " 'quant_train_renew_leaf': True,\n", + " 'lambdarank_truncation_level': 20,\n", + " # 'lambdarank_position_bias_regularization': 1,\n", + " 'seed': 7\n", + "}\n", + "evals = {}\n", + "\n", + "gc.collect()" + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "id": "8f134d435f71e9e2", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T17:54:48.336088Z", + "start_time": "2025-04-11T17:54:48.146675Z" + } + }, + "outputs": [], + "source": [ + "gc.collect()\n", + "\n", + "\n", + "def rolling_train_predict(df, train_days, test_days, feature_columns, days=5, use_pca=False, validation_days=60,\n", + " filter_index=None, params=None):\n", + " # 1. 按照交易日期排序\n", + " unique_dates = df[df['trade_date'] >= '2020-01-01']['trade_date'].unique().tolist()\n", + " unique_dates = sorted(unique_dates)\n", + " n = len(unique_dates)\n", + "\n", + " # 2. 计算需要跳过的天数,使后续窗口对齐\n", + " extra_days = (n - train_days) % test_days\n", + " start_index = extra_days # 从此索引开始滚动\n", + "\n", + " predictions_list = []\n", + "\n", + " for start in range(start_index, n - train_days - test_days + 1, test_days):\n", + " try:\n", + " # train_dates = unique_dates[start: start + train_days]\n", + " train_dates = unique_dates[max(0, start - days + 1): start + train_days - days + 1]\n", + " test_dates = unique_dates[start + train_days: start + train_days + test_days]\n", + "\n", + " # 根据日期筛选数据\n", + " # train_data = df[df['trade_date'].isin(train_dates)]\n", + " train_data = df[filter_index & df['trade_date'].isin(train_dates)]\n", + " val_data = df[(df['trade_date'] == unique_dates[start + train_days - days + 1])]\n", + " val_data['label'] = val_data['label2']\n", + " # train_data = pd.concat([train_data, val_data], axis=0)\n", + " test_data = df[df['trade_date'].isin(test_dates)]\n", + "\n", + " train_data = train_data.sort_values('trade_date')\n", + " test_data = test_data.sort_values('trade_date')\n", + "\n", + " final_feature_columns = [col for col in feature_columns]\n", + " # final_feature_columns = select_top_rankic_features(train_data, final_feature_columns, n=50)\n", + " # final_feature_columns, _ = remove_shifted_features(train_data, final_feature_columns, size=0.8, log=False,\n", + " # val_data=val_data)\n", + "\n", + " train_data = train_data.dropna(subset=final_feature_columns)\n", + " train_data = train_data.dropna(subset=['label'])\n", + " train_data = train_data.reset_index(drop=True)\n", + "\n", + "\n", + " # print(test_data.tail())\n", + " test_data = test_data.dropna(subset=final_feature_columns)\n", + " # test_data = test_data.dropna(subset=['label'])\n", + " test_data = test_data.reset_index(drop=True)\n", + "\n", + " # print(len(train_data))\n", + " # print(f\"最小日期: {train_data['trade_date'].min().strftime('%Y-%m-%d')}\")\n", + " print(\n", + " f\"train_data最大日期: {train_data['trade_date'].max().strftime('%Y-%m-%d')}, 训练天数:{train_data['trade_date'].nunique()}, feat size:{len(final_feature_columns)}\")\n", + " # # print(len(test_data))\n", + " # print(f\"最小日期: {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 df.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", + " label_gain = list(range(len(train_data['label'].unique())))\n", + " label_gain = [(gain + 1) * (gain + 1) for gain in label_gain]\n", + " params['label_gain'] = label_gain\n", + "\n", + " # ud = train_data[\"trade_date\"].unique()\n", + " # date_weights = {date: weight for date, weight in zip(ud, np.linspace(1, 2, len(unique_dates)))}\n", + " # params['weight'] = train_data[\"trade_date\"].map(date_weights).tolist()\n", + "\n", + " # print(f'feature_columns: {feature_columns}')\n", + " # feature_contri = [2 if feat.startswith('act_factor') else 1 for feat in feature_columns]\n", + " # params['feature_contri'] = feature_contri\n", + " try:\n", + " model, _, _ = train_light_model(train_data.dropna(subset=['label']),\n", + " params, final_feature_columns,\n", + " [lgb.log_evaluation(period=100),\n", + " lgb.callback.record_evaluation(evals),\n", + " # lgb.early_stopping(100, first_metric_only=True)\n", + " ], evals,\n", + " num_boost_round=100, validation_days=validation_days,\n", + " print_feature_importance=False, use_pca=False)\n", + "\n", + " score_df = test_data.copy()\n", + " score_df['score'] = model.predict(score_df[final_feature_columns])\n", + " # score_df = score_df.loc[score_df.groupby('trade_date')['score'].idxmax()]\n", + " score_df = score_df[['trade_date', 'score', 'ts_code']]\n", + " predictions_list.append(score_df)\n", + " except Exception as e:\n", + " print(e)\n", + " print(train_data['label'].unique().tolist())\n", + " except Exception as e:\n", + " print(df[df['trade_date'].isin(test_dates)])\n", + " print(train_dates, test_dates)\n", + " raise e \n", + "\n", + "\n", + " final_predictions = pd.concat(predictions_list, ignore_index=True)\n", + " return final_predictions\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "id": "777822bd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Index: 118350 entries, 2240 to 82199\n", + "Columns: 180 entries, ts_code to industry_return_20_percentile\n", + "dtypes: bool(12), datetime64[ns](1), float64(161), int32(3), object(3)\n", + "memory usage: 152.6+ MB\n", + "None\n" + ] + } + ], + "source": [ + "print(pdf.info())" + ] + }, + { + "cell_type": "code", + "execution_count": 95, + "id": "63235069-dc59-48fb-961a-e80373e41a61", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T18:03:45.666269Z", + "start_time": "2025-04-11T18:02:13.319322Z" + }, + "editable": true, + "scrolled": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "finish\n", + "train_data最大日期: 2022-01-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-01-11\n", + "划分后的训练集大小: 649, 验证集大小: 132\n", + "train_data最大日期: 2022-01-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-01-12\n", + "划分后的训练集大小: 661, 验证集大小: 136\n", + "train_data最大日期: 2022-01-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-01-13\n", + "划分后的训练集大小: 676, 验证集大小: 140\n", + "train_data最大日期: 2022-01-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-01-14\n", + "划分后的训练集大小: 680, 验证集大小: 136\n", + "train_data最大日期: 2022-01-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-01-17\n", + "划分后的训练集大小: 677, 验证集大小: 133\n", + "train_data最大日期: 2022-01-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-01-18\n", + "划分后的训练集大小: 674, 验证集大小: 129\n", + "train_data最大日期: 2022-01-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-01-19\n", + "划分后的训练集大小: 675, 验证集大小: 137\n", + "train_data最大日期: 2022-01-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-01-20\n", + "划分后的训练集大小: 674, 验证集大小: 139\n", + "train_data最大日期: 2022-01-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-01-21\n", + "划分后的训练集大小: 682, 验证集大小: 144\n", + "train_data最大日期: 2022-01-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-01-24\n", + "划分后的训练集大小: 692, 验证集大小: 143\n", + "train_data最大日期: 2022-01-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-01-25\n", + "划分后的训练集大小: 701, 验证集大小: 138\n", + "train_data最大日期: 2022-01-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-01-26\n", + "划分后的训练集大小: 702, 验证集大小: 138\n", + "train_data最大日期: 2022-01-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-01-27\n", + "划分后的训练集大小: 705, 验证集大小: 142\n", + "train_data最大日期: 2022-01-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-01-28\n", + "划分后的训练集大小: 698, 验证集大小: 137\n", + "train_data最大日期: 2022-01-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-02-07\n", + "划分后的训练集大小: 699, 验证集大小: 144\n", + "train_data最大日期: 2022-02-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-02-08\n", + "划分后的训练集大小: 705, 验证集大小: 144\n", + "train_data最大日期: 2022-02-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-02-09\n", + "划分后的训练集大小: 701, 验证集大小: 134\n", + "train_data最大日期: 2022-02-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-02-10\n", + "划分后的训练集大小: 688, 验证集大小: 129\n", + "train_data最大日期: 2022-02-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-02-11\n", + "划分后的训练集大小: 683, 验证集大小: 132\n", + "train_data最大日期: 2022-02-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-02-14\n", + "划分后的训练集大小: 673, 验证集大小: 134\n", + "train_data最大日期: 2022-02-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-02-15\n", + "划分后的训练集大小: 668, 验证集大小: 139\n", + "train_data最大日期: 2022-02-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-02-16\n", + "划分后的训练集大小: 678, 验证集大小: 144\n", + "train_data最大日期: 2022-02-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-02-17\n", + "划分后的训练集大小: 689, 验证集大小: 140\n", + "train_data最大日期: 2022-02-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-02-18\n", + "划分后的训练集大小: 698, 验证集大小: 141\n", + "train_data最大日期: 2022-02-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-02-21\n", + "划分后的训练集大小: 701, 验证集大小: 137\n", + "train_data最大日期: 2022-02-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-02-22\n", + "划分后的训练集大小: 701, 验证集大小: 139\n", + "train_data最大日期: 2022-02-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-02-23\n", + "划分后的训练集大小: 681, 验证集大小: 124\n", + "train_data最大日期: 2022-02-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-02-24\n", + "划分后的训练集大小: 675, 验证集大小: 134\n", + "train_data最大日期: 2022-02-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-02-25\n", + "划分后的训练集大小: 665, 验证集大小: 131\n", + "train_data最大日期: 2022-02-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-02-28\n", + "划分后的训练集大小: 659, 验证集大小: 131\n", + "train_data最大日期: 2022-02-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-01\n", + "划分后的训练集大小: 661, 验证集大小: 141\n", + "train_data最大日期: 2022-03-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-02\n", + "划分后的训练集大小: 677, 验证集大小: 140\n", + "train_data最大日期: 2022-03-02, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-03\n", + "划分后的训练集大小: 677, 验证集大小: 134\n", + "train_data最大日期: 2022-03-03, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-04\n", + "划分后的训练集大小: 679, 验证集大小: 133\n", + "train_data最大日期: 2022-03-04, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-07\n", + "划分后的训练集大小: 682, 验证集大小: 134\n", + "train_data最大日期: 2022-03-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-08\n", + "划分后的训练集大小: 666, 验证集大小: 125\n", + "train_data最大日期: 2022-03-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-09\n", + "划分后的训练集大小: 649, 验证集大小: 123\n", + "train_data最大日期: 2022-03-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-10\n", + "划分后的训练集大小: 643, 验证集大小: 128\n", + "train_data最大日期: 2022-03-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-11\n", + "划分后的训练集大小: 638, 验证集大小: 128\n", + "train_data最大日期: 2022-03-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-14\n", + "划分后的训练集大小: 642, 验证集大小: 138\n", + "train_data最大日期: 2022-03-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-15\n", + "划分后的训练集大小: 653, 验证集大小: 136\n", + "train_data最大日期: 2022-03-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-16\n", + "划分后的训练集大小: 669, 验证集大小: 139\n", + "train_data最大日期: 2022-03-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-17\n", + "划分后的训练集大小: 683, 验证集大小: 142\n", + "train_data最大日期: 2022-03-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-18\n", + "划分后的训练集大小: 695, 验证集大小: 140\n", + "train_data最大日期: 2022-03-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-21\n", + "划分后的训练集大小: 688, 验证集大小: 131\n", + "train_data最大日期: 2022-03-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-22\n", + "划分后的训练集大小: 680, 验证集大小: 128\n", + "train_data最大日期: 2022-03-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-23\n", + "划分后的训练集大小: 674, 验证集大小: 133\n", + "train_data最大日期: 2022-03-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-24\n", + "划分后的训练集大小: 658, 验证集大小: 126\n", + "train_data最大日期: 2022-03-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-25\n", + "划分后的训练集大小: 645, 验证集大小: 127\n", + "train_data最大日期: 2022-03-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-28\n", + "划分后的训练集大小: 643, 验证集大小: 129\n", + "train_data最大日期: 2022-03-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-29\n", + "划分后的训练集大小: 646, 验证集大小: 131\n", + "train_data最大日期: 2022-03-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-30\n", + "划分后的训练集大小: 647, 验证集大小: 134\n", + "train_data最大日期: 2022-03-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-03-31\n", + "划分后的训练集大小: 655, 验证集大小: 134\n", + "train_data最大日期: 2022-03-31, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-04-01\n", + "划分后的训练集大小: 656, 验证集大小: 128\n", + "train_data最大日期: 2022-04-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-04-06\n", + "划分后的训练集大小: 659, 验证集大小: 132\n", + "train_data最大日期: 2022-04-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-04-07\n", + "划分后的训练集大小: 659, 验证集大小: 131\n", + "train_data最大日期: 2022-04-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-04-08\n", + "划分后的训练集大小: 658, 验证集大小: 133\n", + "train_data最大日期: 2022-04-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-04-11\n", + "划分后的训练集大小: 656, 验证集大小: 132\n", + "train_data最大日期: 2022-04-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-04-12\n", + "划分后的训练集大小: 654, 验证集大小: 126\n", + "train_data最大日期: 2022-04-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-04-13\n", + "划分后的训练集大小: 657, 验证集大小: 135\n", + "train_data最大日期: 2022-04-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-04-14\n", + "划分后的训练集大小: 663, 验证集大小: 137\n", + "train_data最大日期: 2022-04-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-04-15\n", + "划分后的训练集大小: 663, 验证集大小: 133\n", + "train_data最大日期: 2022-04-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-04-18\n", + "划分后的训练集大小: 665, 验证集大小: 134\n", + "train_data最大日期: 2022-04-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-04-19\n", + "划分后的训练集大小: 679, 验证集大小: 140\n", + "train_data最大日期: 2022-04-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-04-20\n", + "划分后的训练集大小: 679, 验证集大小: 135\n", + "train_data最大日期: 2022-04-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-04-21\n", + "划分后的训练集大小: 679, 验证集大小: 137\n", + "train_data最大日期: 2022-04-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-04-22\n", + "划分后的训练集大小: 676, 验证集大小: 130\n", + "train_data最大日期: 2022-04-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-04-25\n", + "划分后的训练集大小: 674, 验证集大小: 132\n", + "train_data最大日期: 2022-04-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-04-26\n", + "划分后的训练集大小: 663, 验证集大小: 129\n", + "train_data最大日期: 2022-04-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-04-27\n", + "划分后的训练集大小: 667, 验证集大小: 139\n", + "train_data最大日期: 2022-04-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-04-28\n", + "划分后的训练集大小: 668, 验证集大小: 138\n", + "train_data最大日期: 2022-04-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-04-29\n", + "划分后的训练集大小: 679, 验证集大小: 141\n", + "train_data最大日期: 2022-04-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-05-05\n", + "划分后的训练集大小: 688, 验证集大小: 141\n", + "train_data最大日期: 2022-05-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-05-06\n", + "划分后的训练集大小: 700, 验证集大小: 141\n", + "train_data最大日期: 2022-05-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-05-09\n", + "划分后的训练集大小: 692, 验证集大小: 131\n", + "train_data最大日期: 2022-05-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-05-10\n", + "划分后的训练集大小: 683, 验证集大小: 129\n", + "train_data最大日期: 2022-05-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-05-11\n", + "划分后的训练集大小: 667, 验证集大小: 125\n", + "train_data最大日期: 2022-05-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-05-12\n", + "划分后的训练集大小: 650, 验证集大小: 124\n", + "train_data最大日期: 2022-05-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-05-13\n", + "划分后的训练集大小: 642, 验证集大小: 133\n", + "train_data最大日期: 2022-05-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-05-16\n", + "划分后的训练集大小: 646, 验证集大小: 135\n", + "train_data最大日期: 2022-05-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-05-17\n", + "划分后的训练集大小: 652, 验证集大小: 135\n", + "train_data最大日期: 2022-05-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-05-18\n", + "划分后的训练集大小: 667, 验证集大小: 140\n", + "train_data最大日期: 2022-05-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-05-19\n", + "划分后的训练集大小: 678, 验证集大小: 135\n", + "train_data最大日期: 2022-05-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-05-20\n", + "划分后的训练集大小: 676, 验证集大小: 131\n", + "train_data最大日期: 2022-05-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-05-23\n", + "划分后的训练集大小: 669, 验证集大小: 128\n", + "train_data最大日期: 2022-05-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-05-24\n", + "划分后的训练集大小: 658, 验证集大小: 124\n", + "train_data最大日期: 2022-05-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-05-25\n", + "划分后的训练集大小: 644, 验证集大小: 126\n", + "train_data最大日期: 2022-05-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-05-26\n", + "划分后的训练集大小: 642, 验证集大小: 133\n", + "train_data最大日期: 2022-05-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-05-27\n", + "划分后的训练集大小: 647, 验证集大小: 136\n", + "train_data最大日期: 2022-05-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-05-30\n", + "划分后的训练集大小: 649, 验证集大小: 130\n", + "train_data最大日期: 2022-05-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-05-31\n", + "划分后的训练集大小: 655, 验证集大小: 130\n", + "train_data最大日期: 2022-05-31, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-06-01\n", + "划分后的训练集大小: 658, 验证集大小: 129\n", + "train_data最大日期: 2022-06-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-06-02\n", + "划分后的训练集大小: 655, 验证集大小: 130\n", + "train_data最大日期: 2022-06-02, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-06-06\n", + "划分后的训练集大小: 650, 验证集大小: 131\n", + "train_data最大日期: 2022-06-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-06-07\n", + "划分后的训练集大小: 657, 验证集大小: 137\n", + "train_data最大日期: 2022-06-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-06-08\n", + "划分后的训练集大小: 667, 验证集大小: 140\n", + "train_data最大日期: 2022-06-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-06-09\n", + "划分后的训练集大小: 675, 验证集大小: 137\n", + "train_data最大日期: 2022-06-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-06-10\n", + "划分后的训练集大小: 686, 验证集大小: 141\n", + "train_data最大日期: 2022-06-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-06-13\n", + "划分后的训练集大小: 693, 验证集大小: 138\n", + "train_data最大日期: 2022-06-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-06-14\n", + "划分后的训练集大小: 688, 验证集大小: 132\n", + "train_data最大日期: 2022-06-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-06-15\n", + "划分后的训练集大小: 684, 验证集大小: 136\n", + "train_data最大日期: 2022-06-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-06-16\n", + "划分后的训练集大小: 678, 验证集大小: 131\n", + "train_data最大日期: 2022-06-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-06-17\n", + "划分后的训练集大小: 671, 验证集大小: 134\n", + "train_data最大日期: 2022-06-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-06-20\n", + "划分后的训练集大小: 667, 验证集大小: 134\n", + "train_data最大日期: 2022-06-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-06-21\n", + "划分后的训练集大小: 669, 验证集大小: 134\n", + "train_data最大日期: 2022-06-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-06-22\n", + "划分后的训练集大小: 670, 验证集大小: 137\n", + "train_data最大日期: 2022-06-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-06-23\n", + "划分后的训练集大小: 669, 验证集大小: 130\n", + "train_data最大日期: 2022-06-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-06-24\n", + "划分后的训练集大小: 669, 验证集大小: 134\n", + "train_data最大日期: 2022-06-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-06-27\n", + "划分后的训练集大小: 667, 验证集大小: 132\n", + "train_data最大日期: 2022-06-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-06-28\n", + "划分后的训练集大小: 657, 验证集大小: 124\n", + "train_data最大日期: 2022-06-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-06-29\n", + "划分后的训练集大小: 635, 验证集大小: 115\n", + "train_data最大日期: 2022-06-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-06-30\n", + "划分后的训练集大小: 634, 验证集大小: 129\n", + "train_data最大日期: 2022-06-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-07-01\n", + "划分后的训练集大小: 640, 验证集大小: 140\n", + "train_data最大日期: 2022-07-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-07-04\n", + "划分后的训练集大小: 649, 验证集大小: 141\n", + "train_data最大日期: 2022-07-04, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-07-05\n", + "划分后的训练集大小: 663, 验证集大小: 138\n", + "train_data最大日期: 2022-07-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-07-06\n", + "划分后的训练集大小: 688, 验证集大小: 140\n", + "train_data最大日期: 2022-07-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-07-07\n", + "划分后的训练集大小: 698, 验证集大小: 139\n", + "train_data最大日期: 2022-07-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-07-08\n", + "划分后的训练集大小: 701, 验证集大小: 143\n", + "train_data最大日期: 2022-07-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-07-11\n", + "划分后的训练集大小: 696, 验证集大小: 136\n", + "train_data最大日期: 2022-07-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-07-12\n", + "划分后的训练集大小: 695, 验证集大小: 137\n", + "train_data最大日期: 2022-07-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-07-13\n", + "划分后的训练集大小: 692, 验证集大小: 137\n", + "train_data最大日期: 2022-07-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-07-14\n", + "划分后的训练集大小: 683, 验证集大小: 130\n", + "train_data最大日期: 2022-07-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-07-15\n", + "划分后的训练集大小: 675, 验证集大小: 135\n", + "train_data最大日期: 2022-07-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-07-18\n", + "划分后的训练集大小: 678, 验证集大小: 139\n", + "train_data最大日期: 2022-07-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-07-19\n", + "划分后的训练集大小: 675, 验证集大小: 134\n", + "train_data最大日期: 2022-07-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-07-20\n", + "划分后的训练集大小: 667, 验证集大小: 129\n", + "train_data最大日期: 2022-07-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-07-21\n", + "划分后的训练集大小: 670, 验证集大小: 133\n", + "train_data最大日期: 2022-07-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-07-22\n", + "划分后的训练集大小: 673, 验证集大小: 138\n", + "train_data最大日期: 2022-07-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-07-25\n", + "划分后的训练集大小: 672, 验证集大小: 138\n", + "train_data最大日期: 2022-07-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-07-26\n", + "划分后的训练集大小: 680, 验证集大小: 142\n", + "train_data最大日期: 2022-07-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-07-27\n", + "划分后的训练集大小: 689, 验证集大小: 138\n", + "train_data最大日期: 2022-07-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-07-28\n", + "划分后的训练集大小: 696, 验证集大小: 140\n", + "train_data最大日期: 2022-07-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-07-29\n", + "划分后的训练集大小: 693, 验证集大小: 135\n", + "train_data最大日期: 2022-07-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-01\n", + "划分后的训练集大小: 692, 验证集大小: 137\n", + "train_data最大日期: 2022-08-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-02\n", + "划分后的训练集大小: 684, 验证集大小: 134\n", + "train_data最大日期: 2022-08-02, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-03\n", + "划分后的训练集大小: 679, 验证集大小: 133\n", + "train_data最大日期: 2022-08-03, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-04\n", + "划分后的训练集大小: 673, 验证集大小: 134\n", + "train_data最大日期: 2022-08-04, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-05\n", + "划分后的训练集大小: 670, 验证集大小: 132\n", + "train_data最大日期: 2022-08-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-08\n", + "划分后的训练集大小: 664, 验证集大小: 131\n", + "train_data最大日期: 2022-08-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-09\n", + "划分后的训练集大小: 655, 验证集大小: 125\n", + "train_data最大日期: 2022-08-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-10\n", + "划分后的训练集大小: 660, 验证集大小: 138\n", + "train_data最大日期: 2022-08-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-11\n", + "划分后的训练集大小: 668, 验证集大小: 142\n", + "train_data最大日期: 2022-08-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-12\n", + "划分后的训练集大小: 677, 验证集大小: 141\n", + "train_data最大日期: 2022-08-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-15\n", + "划分后的训练集大小: 680, 验证集大小: 134\n", + "train_data最大日期: 2022-08-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-16\n", + "划分后的训练集大小: 690, 验证集大小: 135\n", + "train_data最大日期: 2022-08-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-17\n", + "划分后的训练集大小: 685, 验证集大小: 133\n", + "train_data最大日期: 2022-08-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-18\n", + "划分后的训练集大小: 677, 验证集大小: 134\n", + "train_data最大日期: 2022-08-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-19\n", + "划分后的训练集大小: 668, 验证集大小: 132\n", + "train_data最大日期: 2022-08-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-22\n", + "划分后的训练集大小: 672, 验证集大小: 138\n", + "train_data最大日期: 2022-08-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-23\n", + "划分后的训练集大小: 680, 验证集大小: 143\n", + "train_data最大日期: 2022-08-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-24\n", + "划分后的训练集大小: 685, 验证集大小: 138\n", + "train_data最大日期: 2022-08-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-25\n", + "划分后的训练集大小: 687, 验证集大小: 136\n", + "train_data最大日期: 2022-08-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-26\n", + "划分后的训练集大小: 688, 验证集大小: 133\n", + "train_data最大日期: 2022-08-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-29\n", + "划分后的训练集大小: 683, 验证集大小: 133\n", + "train_data最大日期: 2022-08-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-30\n", + "划分后的训练集大小: 672, 验证集大小: 132\n", + "train_data最大日期: 2022-08-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-08-31\n", + "划分后的训练集大小: 668, 验证集大小: 134\n", + "train_data最大日期: 2022-08-31, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-09-01\n", + "划分后的训练集大小: 666, 验证集大小: 134\n", + "train_data最大日期: 2022-09-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-09-02\n", + "划分后的训练集大小: 672, 验证集大小: 139\n", + "train_data最大日期: 2022-09-02, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-09-05\n", + "划分后的训练集大小: 678, 验证集大小: 139\n", + "train_data最大日期: 2022-09-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-09-06\n", + "划分后的训练集大小: 689, 验证集大小: 143\n", + "train_data最大日期: 2022-09-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-09-07\n", + "划分后的训练集大小: 690, 验证集大小: 135\n", + "train_data最大日期: 2022-09-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-09-08\n", + "划分后的训练集大小: 696, 验证集大小: 140\n", + "train_data最大日期: 2022-09-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-09-09\n", + "划分后的训练集大小: 699, 验证集大小: 142\n", + "train_data最大日期: 2022-09-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-09-13\n", + "划分后的训练集大小: 704, 验证集大小: 144\n", + "train_data最大日期: 2022-09-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-09-14\n", + "划分后的训练集大小: 702, 验证集大小: 141\n", + "train_data最大日期: 2022-09-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-09-15\n", + "划分后的训练集大小: 703, 验证集大小: 136\n", + "train_data最大日期: 2022-09-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-09-16\n", + "划分后的训练集大小: 703, 验证集大小: 140\n", + "train_data最大日期: 2022-09-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-09-19\n", + "划分后的训练集大小: 700, 验证集大小: 139\n", + "train_data最大日期: 2022-09-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-09-20\n", + "划分后的训练集大小: 697, 验证集大小: 141\n", + "train_data最大日期: 2022-09-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-09-21\n", + "划分后的训练集大小: 700, 验证集大小: 144\n", + "train_data最大日期: 2022-09-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-09-22\n", + "划分后的训练集大小: 703, 验证集大小: 139\n", + "train_data最大日期: 2022-09-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-09-23\n", + "划分后的训练集大小: 697, 验证集大小: 134\n", + "train_data最大日期: 2022-09-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-09-26\n", + "划分后的训练集大小: 695, 验证集大小: 137\n", + "train_data最大日期: 2022-09-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-09-27\n", + "划分后的训练集大小: 690, 验证集大小: 136\n", + "train_data最大日期: 2022-09-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-09-28\n", + "划分后的训练集大小: 686, 验证集大小: 140\n", + "train_data最大日期: 2022-09-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-09-29\n", + "划分后的训练集大小: 685, 验证集大小: 138\n", + "train_data最大日期: 2022-09-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-09-30\n", + "划分后的训练集大小: 692, 验证集大小: 141\n", + "train_data最大日期: 2022-09-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-10-10\n", + "划分后的训练集大小: 697, 验证集大小: 142\n", + "train_data最大日期: 2022-10-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-10-11\n", + "划分后的训练集大小: 696, 验证集大小: 135\n", + "train_data最大日期: 2022-10-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-10-12\n", + "划分后的训练集大小: 696, 验证集大小: 140\n", + "train_data最大日期: 2022-10-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-10-13\n", + "划分后的训练集大小: 698, 验证集大小: 140\n", + "train_data最大日期: 2022-10-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-10-14\n", + "划分后的训练集大小: 699, 验证集大小: 142\n", + "train_data最大日期: 2022-10-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-10-17\n", + "划分后的训练集大小: 694, 验证集大小: 137\n", + "train_data最大日期: 2022-10-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-10-18\n", + "划分后的训练集大小: 699, 验证集大小: 140\n", + "train_data最大日期: 2022-10-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-10-19\n", + "划分后的训练集大小: 696, 验证集大小: 137\n", + "train_data最大日期: 2022-10-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-10-20\n", + "划分后的训练集大小: 688, 验证集大小: 132\n", + "train_data最大日期: 2022-10-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-10-21\n", + "划分后的训练集大小: 681, 验证集大小: 135\n", + "train_data最大日期: 2022-10-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-10-24\n", + "划分后的训练集大小: 677, 验证集大小: 133\n", + "train_data最大日期: 2022-10-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-10-25\n", + "划分后的训练集大小: 677, 验证集大小: 140\n", + "train_data最大日期: 2022-10-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-10-26\n", + "划分后的训练集大小: 676, 验证集大小: 136\n", + "train_data最大日期: 2022-10-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-10-27\n", + "划分后的训练集大小: 678, 验证集大小: 134\n", + "train_data最大日期: 2022-10-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-10-28\n", + "划分后的训练集大小: 685, 验证集大小: 142\n", + "train_data最大日期: 2022-10-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-10-31\n", + "划分后的训练集大小: 692, 验证集大小: 140\n", + "train_data最大日期: 2022-10-31, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-01\n", + "划分后的训练集大小: 697, 验证集大小: 145\n", + "train_data最大日期: 2022-11-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-02\n", + "划分后的训练集大小: 705, 验证集大小: 144\n", + "train_data最大日期: 2022-11-02, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-03\n", + "划分后的训练集大小: 712, 验证集大小: 141\n", + "train_data最大日期: 2022-11-03, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-04\n", + "划分后的训练集大小: 712, 验证集大小: 142\n", + "train_data最大日期: 2022-11-04, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-07\n", + "划分后的训练集大小: 713, 验证集大小: 141\n", + "train_data最大日期: 2022-11-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-08\n", + "划分后的训练集大小: 705, 验证集大小: 137\n", + "train_data最大日期: 2022-11-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-09\n", + "划分后的训练集大小: 695, 验证集大小: 134\n", + "train_data最大日期: 2022-11-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-10\n", + "划分后的训练集大小: 688, 验证集大小: 134\n", + "train_data最大日期: 2022-11-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-11\n", + "划分后的训练集大小: 673, 验证集大小: 127\n", + "train_data最大日期: 2022-11-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-14\n", + "划分后的训练集大小: 663, 验证集大小: 131\n", + "train_data最大日期: 2022-11-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-15\n", + "划分后的训练集大小: 667, 验证集大小: 141\n", + "train_data最大日期: 2022-11-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-16\n", + "划分后的训练集大小: 664, 验证集大小: 131\n", + "train_data最大日期: 2022-11-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-17\n", + "划分后的训练集大小: 667, 验证集大小: 137\n", + "train_data最大日期: 2022-11-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-18\n", + "划分后的训练集大小: 678, 验证集大小: 138\n", + "train_data最大日期: 2022-11-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-21\n", + "划分后的训练集大小: 687, 验证集大小: 140\n", + "train_data最大日期: 2022-11-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-22\n", + "划分后的训练集大小: 687, 验证集大小: 141\n", + "train_data最大日期: 2022-11-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-23\n", + "划分后的训练集大小: 699, 验证集大小: 143\n", + "train_data最大日期: 2022-11-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-24\n", + "划分后的训练集大小: 704, 验证集大小: 142\n", + "train_data最大日期: 2022-11-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-25\n", + "划分后的训练集大小: 710, 验证集大小: 144\n", + "train_data最大日期: 2022-11-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-28\n", + "划分后的训练集大小: 703, 验证集大小: 133\n", + "train_data最大日期: 2022-11-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-29\n", + "划分后的训练集大小: 691, 验证集大小: 129\n", + "train_data最大日期: 2022-11-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-11-30\n", + "划分后的训练集大小: 676, 验证集大小: 128\n", + "train_data最大日期: 2022-11-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-01\n", + "划分后的训练集大小: 662, 验证集大小: 128\n", + "train_data最大日期: 2022-12-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-02\n", + "划分后的训练集大小: 648, 验证集大小: 130\n", + "train_data最大日期: 2022-12-02, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-05\n", + "划分后的训练集大小: 654, 验证集大小: 139\n", + "train_data最大日期: 2022-12-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-06\n", + "划分后的训练集大小: 656, 验证集大小: 131\n", + "train_data最大日期: 2022-12-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-07\n", + "划分后的训练集大小: 668, 验证集大小: 140\n", + "train_data最大日期: 2022-12-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-08\n", + "划分后的训练集大小: 675, 验证集大小: 135\n", + "train_data最大日期: 2022-12-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-09\n", + "划分后的训练集大小: 680, 验证集大小: 135\n", + "train_data最大日期: 2022-12-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-12\n", + "划分后的训练集大小: 680, 验证集大小: 139\n", + "train_data最大日期: 2022-12-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-13\n", + "划分后的训练集大小: 689, 验证集大小: 140\n", + "train_data最大日期: 2022-12-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-14\n", + "划分后的训练集大小: 689, 验证集大小: 140\n", + "train_data最大日期: 2022-12-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-15\n", + "划分后的训练集大小: 700, 验证集大小: 146\n", + "train_data最大日期: 2022-12-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-16\n", + "划分后的训练集大小: 704, 验证集大小: 139\n", + "train_data最大日期: 2022-12-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-19\n", + "划分后的训练集大小: 706, 验证集大小: 141\n", + "train_data最大日期: 2022-12-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-20\n", + "划分后的训练集大小: 706, 验证集大小: 140\n", + "train_data最大日期: 2022-12-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-21\n", + "划分后的训练集大小: 708, 验证集大小: 142\n", + "train_data最大日期: 2022-12-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-22\n", + "划分后的训练集大小: 705, 验证集大小: 143\n", + "train_data最大日期: 2022-12-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-23\n", + "划分后的训练集大小: 706, 验证集大小: 140\n", + "train_data最大日期: 2022-12-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-26\n", + "划分后的训练集大小: 707, 验证集大小: 142\n", + "train_data最大日期: 2022-12-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-27\n", + "划分后的训练集大小: 710, 验证集大小: 143\n", + "train_data最大日期: 2022-12-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-28\n", + "划分后的训练集大小: 704, 验证集大小: 136\n", + "train_data最大日期: 2022-12-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-29\n", + "划分后的训练集大小: 702, 验证集大小: 141\n", + "train_data最大日期: 2022-12-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2022-12-30\n", + "划分后的训练集大小: 702, 验证集大小: 140\n", + "train_data最大日期: 2022-12-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-01-03\n", + "划分后的训练集大小: 701, 验证集大小: 141\n", + "train_data最大日期: 2023-01-03, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-01-04\n", + "划分后的训练集大小: 698, 验证集大小: 140\n", + "train_data最大日期: 2023-01-04, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-01-05\n", + "划分后的训练集大小: 701, 验证集大小: 139\n", + "train_data最大日期: 2023-01-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-01-06\n", + "划分后的训练集大小: 699, 验证集大小: 139\n", + "train_data最大日期: 2023-01-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-01-09\n", + "划分后的训练集大小: 699, 验证集大小: 140\n", + "train_data最大日期: 2023-01-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-01-10\n", + "划分后的训练集大小: 701, 验证集大小: 143\n", + "train_data最大日期: 2023-01-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-01-11\n", + "划分后的训练集大小: 705, 验证集大小: 144\n", + "train_data最大日期: 2023-01-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-01-12\n", + "划分后的训练集大小: 705, 验证集大小: 139\n", + "train_data最大日期: 2023-01-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-01-13\n", + "划分后的训练集大小: 707, 验证集大小: 141\n", + "train_data最大日期: 2023-01-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-01-16\n", + "划分后的训练集大小: 703, 验证集大小: 136\n", + "train_data最大日期: 2023-01-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-01-17\n", + "划分后的训练集大小: 695, 验证集大小: 135\n", + "train_data最大日期: 2023-01-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-01-18\n", + "划分后的训练集大小: 684, 验证集大小: 133\n", + "train_data最大日期: 2023-01-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-01-19\n", + "划分后的训练集大小: 678, 验证集大小: 133\n", + "train_data最大日期: 2023-01-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-01-20\n", + "划分后的训练集大小: 676, 验证集大小: 139\n", + "train_data最大日期: 2023-01-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-01-30\n", + "划分后的训练集大小: 687, 验证集大小: 147\n", + "train_data最大日期: 2023-01-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-01-31\n", + "划分后的训练集大小: 698, 验证集大小: 146\n", + "train_data最大日期: 2023-01-31, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-02-01\n", + "划分后的训练集大小: 703, 验证集大小: 138\n", + "train_data最大日期: 2023-02-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-02-02\n", + "划分后的训练集大小: 707, 验证集大小: 137\n", + "train_data最大日期: 2023-02-02, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-02-03\n", + "划分后的训练集大小: 709, 验证集大小: 141\n", + "train_data最大日期: 2023-02-03, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-02-06\n", + "划分后的训练集大小: 704, 验证集大小: 142\n", + "train_data最大日期: 2023-02-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-02-07\n", + "划分后的训练集大小: 703, 验证集大小: 145\n", + "train_data最大日期: 2023-02-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-02-08\n", + "划分后的训练集大小: 709, 验证集大小: 144\n", + "train_data最大日期: 2023-02-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-02-09\n", + "划分后的训练集大小: 712, 验证集大小: 140\n", + "train_data最大日期: 2023-02-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-02-10\n", + "划分后的训练集大小: 714, 验证集大小: 143\n", + "train_data最大日期: 2023-02-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-02-13\n", + "划分后的训练集大小: 712, 验证集大小: 140\n", + "train_data最大日期: 2023-02-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-02-14\n", + "划分后的训练集大小: 703, 验证集大小: 136\n", + "train_data最大日期: 2023-02-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-02-15\n", + "划分后的训练集大小: 700, 验证集大小: 141\n", + "train_data最大日期: 2023-02-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-02-16\n", + "划分后的训练集大小: 700, 验证集大小: 140\n", + "train_data最大日期: 2023-02-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-02-17\n", + "划分后的训练集大小: 702, 验证集大小: 145\n", + "train_data最大日期: 2023-02-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-02-20\n", + "划分后的训练集大小: 706, 验证集大小: 144\n", + "train_data最大日期: 2023-02-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-02-21\n", + "划分后的训练集大小: 716, 验证集大小: 146\n", + "train_data最大日期: 2023-02-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-02-22\n", + "划分后的训练集大小: 713, 验证集大小: 138\n", + "train_data最大日期: 2023-02-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-02-23\n", + "划分后的训练集大小: 717, 验证集大小: 144\n", + "train_data最大日期: 2023-02-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-02-24\n", + "划分后的训练集大小: 712, 验证集大小: 140\n", + "train_data最大日期: 2023-02-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-02-27\n", + "划分后的训练集大小: 709, 验证集大小: 141\n", + "train_data最大日期: 2023-02-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-02-28\n", + "划分后的训练集大小: 704, 验证集大小: 141\n", + "train_data最大日期: 2023-02-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-01\n", + "划分后的训练集大小: 709, 验证集大小: 143\n", + "train_data最大日期: 2023-03-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-02\n", + "划分后的训练集大小: 708, 验证集大小: 143\n", + "train_data最大日期: 2023-03-02, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-03\n", + "划分后的训练集大小: 705, 验证集大小: 137\n", + "train_data最大日期: 2023-03-03, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-06\n", + "划分后的训练集大小: 709, 验证集大小: 145\n", + "train_data最大日期: 2023-03-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-07\n", + "划分后的训练集大小: 713, 验证集大小: 145\n", + "train_data最大日期: 2023-03-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-08\n", + "划分后的训练集大小: 712, 验证集大小: 142\n", + "train_data最大日期: 2023-03-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-09\n", + "划分后的训练集大小: 710, 验证集大小: 141\n", + "train_data最大日期: 2023-03-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-10\n", + "划分后的训练集大小: 714, 验证集大小: 141\n", + "train_data最大日期: 2023-03-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-13\n", + "划分后的训练集大小: 707, 验证集大小: 138\n", + "train_data最大日期: 2023-03-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-14\n", + "划分后的训练集大小: 702, 验证集大小: 140\n", + "train_data最大日期: 2023-03-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-15\n", + "划分后的训练集大小: 703, 验证集大小: 143\n", + "train_data最大日期: 2023-03-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-16\n", + "划分后的训练集大小: 705, 验证集大小: 143\n", + "train_data最大日期: 2023-03-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-17\n", + "划分后的训练集大小: 708, 验证集大小: 144\n", + "train_data最大日期: 2023-03-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-20\n", + "划分后的训练集大小: 711, 验证集大小: 141\n", + "train_data最大日期: 2023-03-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-21\n", + "划分后的训练集大小: 709, 验证集大小: 138\n", + "train_data最大日期: 2023-03-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-22\n", + "划分后的训练集大小: 702, 验证集大小: 136\n", + "train_data最大日期: 2023-03-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-23\n", + "划分后的训练集大小: 699, 验证集大小: 140\n", + "train_data最大日期: 2023-03-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-24\n", + "划分后的训练集大小: 698, 验证集大小: 143\n", + "train_data最大日期: 2023-03-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-27\n", + "划分后的训练集大小: 700, 验证集大小: 143\n", + "train_data最大日期: 2023-03-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-28\n", + "划分后的训练集大小: 706, 验证集大小: 144\n", + "train_data最大日期: 2023-03-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-29\n", + "划分后的训练集大小: 713, 验证集大小: 143\n", + "train_data最大日期: 2023-03-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-30\n", + "划分后的训练集大小: 716, 验证集大小: 143\n", + "train_data最大日期: 2023-03-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-03-31\n", + "划分后的训练集大小: 718, 验证集大小: 145\n", + "train_data最大日期: 2023-03-31, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-04-03\n", + "划分后的训练集大小: 719, 验证集大小: 144\n", + "train_data最大日期: 2023-04-03, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-04-04\n", + "划分后的训练集大小: 720, 验证集大小: 145\n", + "train_data最大日期: 2023-04-04, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-04-06\n", + "划分后的训练集大小: 722, 验证集大小: 145\n", + "train_data最大日期: 2023-04-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-04-07\n", + "划分后的训练集大小: 725, 验证集大小: 146\n", + "train_data最大日期: 2023-04-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-04-10\n", + "划分后的训练集大小: 728, 验证集大小: 148\n", + "train_data最大日期: 2023-04-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-04-11\n", + "划分后的训练集大小: 729, 验证集大小: 145\n", + "train_data最大日期: 2023-04-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-04-12\n", + "划分后的训练集大小: 726, 验证集大小: 142\n", + "train_data最大日期: 2023-04-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-04-13\n", + "划分后的训练集大小: 727, 验证集大小: 146\n", + "train_data最大日期: 2023-04-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-04-14\n", + "划分后的训练集大小: 725, 验证集大小: 144\n", + "train_data最大日期: 2023-04-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-04-17\n", + "划分后的训练集大小: 719, 验证集大小: 142\n", + "train_data最大日期: 2023-04-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-04-18\n", + "划分后的训练集大小: 717, 验证集大小: 143\n", + "train_data最大日期: 2023-04-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-04-19\n", + "划分后的训练集大小: 719, 验证集大小: 144\n", + "train_data最大日期: 2023-04-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-04-20\n", + "划分后的训练集大小: 714, 验证集大小: 141\n", + "train_data最大日期: 2023-04-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-04-21\n", + "划分后的训练集大小: 714, 验证集大小: 144\n", + "train_data最大日期: 2023-04-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-04-24\n", + "划分后的训练集大小: 714, 验证集大小: 142\n", + "train_data最大日期: 2023-04-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-04-25\n", + "划分后的训练集大小: 719, 验证集大小: 148\n", + "train_data最大日期: 2023-04-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-04-26\n", + "划分后的训练集大小: 721, 验证集大小: 146\n", + "train_data最大日期: 2023-04-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-04-27\n", + "划分后的训练集大小: 729, 验证集大小: 149\n", + "train_data最大日期: 2023-04-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-04-28\n", + "划分后的训练集大小: 727, 验证集大小: 142\n", + "train_data最大日期: 2023-04-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-05-04\n", + "划分后的训练集大小: 731, 验证集大小: 146\n", + "train_data最大日期: 2023-05-04, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-05-05\n", + "划分后的训练集大小: 728, 验证集大小: 145\n", + "train_data最大日期: 2023-05-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-05-08\n", + "划分后的训练集大小: 720, 验证集大小: 138\n", + "train_data最大日期: 2023-05-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-05-09\n", + "划分后的训练集大小: 711, 验证集大小: 140\n", + "train_data最大日期: 2023-05-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-05-10\n", + "划分后的训练集大小: 707, 验证集大小: 138\n", + "train_data最大日期: 2023-05-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-05-11\n", + "划分后的训练集大小: 700, 验证集大小: 139\n", + "train_data最大日期: 2023-05-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-05-12\n", + "划分后的训练集大小: 693, 验证集大小: 138\n", + "train_data最大日期: 2023-05-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-05-15\n", + "划分后的训练集大小: 690, 验证集大小: 135\n", + "train_data最大日期: 2023-05-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-05-16\n", + "划分后的训练集大小: 691, 验证集大小: 141\n", + "train_data最大日期: 2023-05-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-05-17\n", + "划分后的训练集大小: 699, 验证集大小: 146\n", + "train_data最大日期: 2023-05-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-05-18\n", + "划分后的训练集大小: 703, 验证集大小: 143\n", + "train_data最大日期: 2023-05-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-05-19\n", + "划分后的训练集大小: 708, 验证集大小: 143\n", + "train_data最大日期: 2023-05-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-05-22\n", + "划分后的训练集大小: 716, 验证集大小: 143\n", + "train_data最大日期: 2023-05-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-05-23\n", + "划分后的训练集大小: 716, 验证集大小: 141\n", + "train_data最大日期: 2023-05-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-05-24\n", + "划分后的训练集大小: 708, 验证集大小: 138\n", + "train_data最大日期: 2023-05-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-05-25\n", + "划分后的训练集大小: 703, 验证集大小: 138\n", + "train_data最大日期: 2023-05-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-05-26\n", + "划分后的训练集大小: 698, 验证集大小: 138\n", + "train_data最大日期: 2023-05-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-05-29\n", + "划分后的训练集大小: 694, 验证集大小: 139\n", + "train_data最大日期: 2023-05-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-05-30\n", + "划分后的训练集大小: 690, 验证集大小: 137\n", + "train_data最大日期: 2023-05-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-05-31\n", + "划分后的训练集大小: 691, 验证集大小: 139\n", + "train_data最大日期: 2023-05-31, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-06-01\n", + "划分后的训练集大小: 692, 验证集大小: 139\n", + "train_data最大日期: 2023-06-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-06-02\n", + "划分后的训练集大小: 694, 验证集大小: 140\n", + "train_data最大日期: 2023-06-02, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-06-05\n", + "划分后的训练集大小: 699, 验证集大小: 144\n", + "train_data最大日期: 2023-06-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-06-06\n", + "划分后的训练集大小: 704, 验证集大小: 142\n", + "train_data最大日期: 2023-06-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-06-07\n", + "划分后的训练集大小: 698, 验证集大小: 133\n", + "train_data最大日期: 2023-06-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-06-08\n", + "划分后的训练集大小: 699, 验证集大小: 140\n", + "train_data最大日期: 2023-06-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-06-09\n", + "划分后的训练集大小: 698, 验证集大小: 139\n", + "train_data最大日期: 2023-06-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-06-12\n", + "划分后的训练集大小: 696, 验证集大小: 142\n", + "train_data最大日期: 2023-06-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-06-13\n", + "划分后的训练集大小: 696, 验证集大小: 142\n", + "train_data最大日期: 2023-06-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-06-14\n", + "划分后的训练集大小: 700, 验证集大小: 137\n", + "train_data最大日期: 2023-06-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-06-15\n", + "划分后的训练集大小: 700, 验证集大小: 140\n", + "train_data最大日期: 2023-06-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-06-16\n", + "划分后的训练集大小: 702, 验证集大小: 141\n", + "train_data最大日期: 2023-06-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-06-19\n", + "划分后的训练集大小: 707, 验证集大小: 147\n", + "train_data最大日期: 2023-06-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-06-20\n", + "划分后的训练集大小: 708, 验证集大小: 143\n", + "train_data最大日期: 2023-06-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-06-21\n", + "划分后的训练集大小: 713, 验证集大小: 142\n", + "train_data最大日期: 2023-06-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-06-26\n", + "划分后的训练集大小: 715, 验证集大小: 142\n", + "train_data最大日期: 2023-06-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-06-27\n", + "划分后的训练集大小: 715, 验证集大小: 141\n", + "train_data最大日期: 2023-06-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-06-28\n", + "划分后的训练集大小: 709, 验证集大小: 141\n", + "train_data最大日期: 2023-06-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-06-29\n", + "划分后的训练集大小: 709, 验证集大小: 143\n", + "train_data最大日期: 2023-06-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-06-30\n", + "划分后的训练集大小: 706, 验证集大小: 139\n", + "train_data最大日期: 2023-06-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-07-03\n", + "划分后的训练集大小: 707, 验证集大小: 143\n", + "train_data最大日期: 2023-07-03, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-07-04\n", + "划分后的训练集大小: 711, 验证集大小: 145\n", + "train_data最大日期: 2023-07-04, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-07-05\n", + "划分后的训练集大小: 715, 验证集大小: 145\n", + "train_data最大日期: 2023-07-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-07-06\n", + "划分后的训练集大小: 713, 验证集大小: 141\n", + "train_data最大日期: 2023-07-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-07-07\n", + "划分后的训练集大小: 720, 验证集大小: 146\n", + "train_data最大日期: 2023-07-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-07-10\n", + "划分后的训练集大小: 723, 验证集大小: 146\n", + "train_data最大日期: 2023-07-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-07-11\n", + "划分后的训练集大小: 720, 验证集大小: 142\n", + "train_data最大日期: 2023-07-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-07-12\n", + "划分后的训练集大小: 717, 验证集大小: 142\n", + "train_data最大日期: 2023-07-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-07-13\n", + "划分后的训练集大小: 716, 验证集大小: 140\n", + "train_data最大日期: 2023-07-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-07-14\n", + "划分后的训练集大小: 712, 验证集大小: 142\n", + "train_data最大日期: 2023-07-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-07-17\n", + "划分后的训练集大小: 710, 验证集大小: 144\n", + "train_data最大日期: 2023-07-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-07-18\n", + "划分后的训练集大小: 711, 验证集大小: 143\n", + "train_data最大日期: 2023-07-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-07-19\n", + "划分后的训练集大小: 712, 验证集大小: 143\n", + "train_data最大日期: 2023-07-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-07-20\n", + "划分后的训练集大小: 714, 验证集大小: 142\n", + "train_data最大日期: 2023-07-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-07-21\n", + "划分后的训练集大小: 712, 验证集大小: 140\n", + "train_data最大日期: 2023-07-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-07-24\n", + "划分后的训练集大小: 704, 验证集大小: 136\n", + "train_data最大日期: 2023-07-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-07-25\n", + "划分后的训练集大小: 701, 验证集大小: 140\n", + "train_data最大日期: 2023-07-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-07-26\n", + "划分后的训练集大小: 698, 验证集大小: 140\n", + "train_data最大日期: 2023-07-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-07-27\n", + "划分后的训练集大小: 694, 验证集大小: 138\n", + "train_data最大日期: 2023-07-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-07-28\n", + "划分后的训练集大小: 696, 验证集大小: 142\n", + "train_data最大日期: 2023-07-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-07-31\n", + "划分后的训练集大小: 705, 验证集大小: 145\n", + "train_data最大日期: 2023-07-31, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-01\n", + "划分后的训练集大小: 706, 验证集大小: 141\n", + "train_data最大日期: 2023-08-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-02\n", + "划分后的训练集大小: 708, 验证集大小: 142\n", + "train_data最大日期: 2023-08-02, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-03\n", + "划分后的训练集大小: 715, 验证集大小: 145\n", + "train_data最大日期: 2023-08-03, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-04\n", + "划分后的训练集大小: 716, 验证集大小: 143\n", + "train_data最大日期: 2023-08-04, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-07\n", + "划分后的训练集大小: 716, 验证集大小: 145\n", + "train_data最大日期: 2023-08-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-08\n", + "划分后的训练集大小: 719, 验证集大小: 144\n", + "train_data最大日期: 2023-08-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-09\n", + "划分后的训练集大小: 723, 验证集大小: 146\n", + "train_data最大日期: 2023-08-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-10\n", + "划分后的训练集大小: 722, 验证集大小: 144\n", + "train_data最大日期: 2023-08-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-11\n", + "划分后的训练集大小: 719, 验证集大小: 140\n", + "train_data最大日期: 2023-08-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-14\n", + "划分后的训练集大小: 711, 验证集大小: 137\n", + "train_data最大日期: 2023-08-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-15\n", + "划分后的训练集大小: 705, 验证集大小: 138\n", + "train_data最大日期: 2023-08-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-16\n", + "划分后的训练集大小: 695, 验证集大小: 136\n", + "train_data最大日期: 2023-08-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-17\n", + "划分后的训练集大小: 688, 验证集大小: 137\n", + "train_data最大日期: 2023-08-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-18\n", + "划分后的训练集大小: 686, 验证集大小: 138\n", + "train_data最大日期: 2023-08-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-21\n", + "划分后的训练集大小: 684, 验证集大小: 135\n", + "train_data最大日期: 2023-08-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-22\n", + "划分后的训练集大小: 678, 验证集大小: 132\n", + "train_data最大日期: 2023-08-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-23\n", + "划分后的训练集大小: 682, 验证集大小: 140\n", + "train_data最大日期: 2023-08-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-24\n", + "划分后的训练集大小: 678, 验证集大小: 133\n", + "train_data最大日期: 2023-08-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-25\n", + "划分后的训练集大小: 681, 验证集大小: 141\n", + "train_data最大日期: 2023-08-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-28\n", + "划分后的训练集大小: 691, 验证集大小: 145\n", + "train_data最大日期: 2023-08-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-29\n", + "划分后的训练集大小: 704, 验证集大小: 145\n", + "train_data最大日期: 2023-08-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-30\n", + "划分后的训练集大小: 705, 验证集大小: 141\n", + "train_data最大日期: 2023-08-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-08-31\n", + "划分后的训练集大小: 713, 验证集大小: 141\n", + "train_data最大日期: 2023-08-31, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-09-01\n", + "划分后的训练集大小: 719, 验证集大小: 147\n", + "train_data最大日期: 2023-09-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-09-04\n", + "划分后的训练集大小: 716, 验证集大小: 142\n", + "train_data最大日期: 2023-09-04, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-09-05\n", + "划分后的训练集大小: 717, 验证集大小: 146\n", + "train_data最大日期: 2023-09-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-09-06\n", + "划分后的训练集大小: 721, 验证集大小: 145\n", + "train_data最大日期: 2023-09-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-09-07\n", + "划分后的训练集大小: 724, 验证集大小: 144\n", + "train_data最大日期: 2023-09-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-09-08\n", + "划分后的训练集大小: 722, 验证集大小: 145\n", + "train_data最大日期: 2023-09-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-09-11\n", + "划分后的训练集大小: 724, 验证集大小: 144\n", + "train_data最大日期: 2023-09-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-09-12\n", + "划分后的训练集大小: 722, 验证集大小: 144\n", + "train_data最大日期: 2023-09-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-09-13\n", + "划分后的训练集大小: 723, 验证集大小: 146\n", + "train_data最大日期: 2023-09-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-09-14\n", + "划分后的训练集大小: 726, 验证集大小: 147\n", + "train_data最大日期: 2023-09-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-09-15\n", + "划分后的训练集大小: 727, 验证集大小: 146\n", + "train_data最大日期: 2023-09-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-09-18\n", + "划分后的训练集大小: 729, 验证集大小: 146\n", + "train_data最大日期: 2023-09-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-09-19\n", + "划分后的训练集大小: 728, 验证集大小: 143\n", + "train_data最大日期: 2023-09-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-09-20\n", + "划分后的训练集大小: 725, 验证集大小: 143\n", + "train_data最大日期: 2023-09-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-09-21\n", + "划分后的训练集大小: 722, 验证集大小: 144\n", + "train_data最大日期: 2023-09-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-09-22\n", + "划分后的训练集大小: 718, 验证集大小: 142\n", + "train_data最大日期: 2023-09-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-09-25\n", + "划分后的训练集大小: 717, 验证集大小: 145\n", + "train_data最大日期: 2023-09-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-09-26\n", + "划分后的训练集大小: 715, 验证集大小: 141\n", + "train_data最大日期: 2023-09-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-09-27\n", + "划分后的训练集大小: 715, 验证集大小: 143\n", + "train_data最大日期: 2023-09-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-09-28\n", + "划分后的训练集大小: 714, 验证集大小: 143\n", + "train_data最大日期: 2023-09-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-10-09\n", + "划分后的训练集大小: 717, 验证集大小: 145\n", + "train_data最大日期: 2023-10-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-10-10\n", + "划分后的训练集大小: 716, 验证集大小: 144\n", + "train_data最大日期: 2023-10-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-10-11\n", + "划分后的训练集大小: 716, 验证集大小: 141\n", + "train_data最大日期: 2023-10-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-10-12\n", + "划分后的训练集大小: 713, 验证集大小: 140\n", + "train_data最大日期: 2023-10-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-10-13\n", + "划分后的训练集大小: 715, 验证集大小: 145\n", + "train_data最大日期: 2023-10-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-10-16\n", + "划分后的训练集大小: 713, 验证集大小: 143\n", + "train_data最大日期: 2023-10-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-10-17\n", + "划分后的训练集大小: 708, 验证集大小: 139\n", + "train_data最大日期: 2023-10-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-10-18\n", + "划分后的训练集大小: 708, 验证集大小: 141\n", + "train_data最大日期: 2023-10-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-10-19\n", + "划分后的训练集大小: 708, 验证集大小: 140\n", + "train_data最大日期: 2023-10-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-10-20\n", + "划分后的训练集大小: 705, 验证集大小: 142\n", + "train_data最大日期: 2023-10-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-10-23\n", + "划分后的训练集大小: 705, 验证集大小: 143\n", + "train_data最大日期: 2023-10-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-10-24\n", + "划分后的训练集大小: 711, 验证集大小: 145\n", + "train_data最大日期: 2023-10-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-10-25\n", + "划分后的训练集大小: 709, 验证集大小: 139\n", + "train_data最大日期: 2023-10-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-10-26\n", + "划分后的训练集大小: 707, 验证集大小: 138\n", + "train_data最大日期: 2023-10-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-10-27\n", + "划分后的训练集大小: 701, 验证集大小: 136\n", + "train_data最大日期: 2023-10-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-10-30\n", + "划分后的训练集大小: 698, 验证集大小: 140\n", + "train_data最大日期: 2023-10-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-10-31\n", + "划分后的训练集大小: 696, 验证集大小: 143\n", + "train_data最大日期: 2023-10-31, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-01\n", + "划分后的训练集大小: 695, 验证集大小: 138\n", + "train_data最大日期: 2023-11-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-02\n", + "划分后的训练集大小: 700, 验证集大小: 143\n", + "train_data最大日期: 2023-11-02, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-03\n", + "划分后的训练集大小: 708, 验证集大小: 144\n", + "train_data最大日期: 2023-11-03, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-06\n", + "划分后的训练集大小: 713, 验证集大小: 145\n", + "train_data最大日期: 2023-11-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-07\n", + "划分后的训练集大小: 715, 验证集大小: 145\n", + "train_data最大日期: 2023-11-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-08\n", + "划分后的训练集大小: 711, 验证集大小: 134\n", + "train_data最大日期: 2023-11-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-09\n", + "划分后的训练集大小: 705, 验证集大小: 137\n", + "train_data最大日期: 2023-11-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-10\n", + "划分后的训练集大小: 703, 验证集大小: 142\n", + "train_data最大日期: 2023-11-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-13\n", + "划分后的训练集大小: 700, 验证集大小: 142\n", + "train_data最大日期: 2023-11-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-14\n", + "划分后的训练集大小: 701, 验证集大小: 146\n", + "train_data最大日期: 2023-11-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-15\n", + "划分后的训练集大小: 710, 验证集大小: 143\n", + "train_data最大日期: 2023-11-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-16\n", + "划分后的训练集大小: 710, 验证集大小: 137\n", + "train_data最大日期: 2023-11-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-17\n", + "划分后的训练集大小: 709, 验证集大小: 141\n", + "train_data最大日期: 2023-11-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-20\n", + "划分后的训练集大小: 712, 验证集大小: 145\n", + "train_data最大日期: 2023-11-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-21\n", + "划分后的训练集大小: 710, 验证集大小: 144\n", + "train_data最大日期: 2023-11-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-22\n", + "划分后的训练集大小: 705, 验证集大小: 138\n", + "train_data最大日期: 2023-11-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-23\n", + "划分后的训练集大小: 704, 验证集大小: 136\n", + "train_data最大日期: 2023-11-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-24\n", + "划分后的训练集大小: 702, 验证集大小: 139\n", + "train_data最大日期: 2023-11-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-27\n", + "划分后的训练集大小: 700, 验证集大小: 143\n", + "train_data最大日期: 2023-11-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-28\n", + "划分后的训练集大小: 700, 验证集大小: 144\n", + "train_data最大日期: 2023-11-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-29\n", + "划分后的训练集大小: 708, 验证集大小: 146\n", + "train_data最大日期: 2023-11-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-11-30\n", + "划分后的训练集大小: 716, 验证集大小: 144\n", + "train_data最大日期: 2023-11-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-12-01\n", + "划分后的训练集大小: 725, 验证集大小: 148\n", + "train_data最大日期: 2023-12-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-12-04\n", + "划分后的训练集大小: 728, 验证集大小: 146\n", + "train_data最大日期: 2023-12-04, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-12-05\n", + "划分后的训练集大小: 726, 验证集大小: 142\n", + "train_data最大日期: 2023-12-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-12-06\n", + "划分后的训练集大小: 719, 验证集大小: 139\n", + "train_data最大日期: 2023-12-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-12-07\n", + "划分后的训练集大小: 711, 验证集大小: 136\n", + "train_data最大日期: 2023-12-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-12-08\n", + "划分后的训练集大小: 703, 验证集大小: 140\n", + "train_data最大日期: 2023-12-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-12-11\n", + "划分后的训练集大小: 700, 验证集大小: 143\n", + "train_data最大日期: 2023-12-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-12-12\n", + "划分后的训练集大小: 701, 验证集大小: 143\n", + "train_data最大日期: 2023-12-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-12-13\n", + "划分后的训练集大小: 706, 验证集大小: 144\n", + "train_data最大日期: 2023-12-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-12-14\n", + "划分后的训练集大小: 714, 验证集大小: 144\n", + "train_data最大日期: 2023-12-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-12-15\n", + "划分后的训练集大小: 716, 验证集大小: 142\n", + "train_data最大日期: 2023-12-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-12-18\n", + "划分后的训练集大小: 714, 验证集大小: 141\n", + "train_data最大日期: 2023-12-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-12-19\n", + "划分后的训练集大小: 714, 验证集大小: 143\n", + "train_data最大日期: 2023-12-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-12-20\n", + "划分后的训练集大小: 717, 验证集大小: 147\n", + "train_data最大日期: 2023-12-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-12-21\n", + "划分后的训练集大小: 715, 验证集大小: 142\n", + "train_data最大日期: 2023-12-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-12-22\n", + "划分后的训练集大小: 710, 验证集大小: 137\n", + "train_data最大日期: 2023-12-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-12-25\n", + "划分后的训练集大小: 712, 验证集大小: 143\n", + "train_data最大日期: 2023-12-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-12-26\n", + "划分后的训练集大小: 713, 验证集大小: 144\n", + "train_data最大日期: 2023-12-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-12-27\n", + "划分后的训练集大小: 707, 验证集大小: 141\n", + "train_data最大日期: 2023-12-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-12-28\n", + "划分后的训练集大小: 706, 验证集大小: 141\n", + "train_data最大日期: 2023-12-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2023-12-29\n", + "划分后的训练集大小: 712, 验证集大小: 143\n", + "train_data最大日期: 2023-12-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-02\n", + "划分后的训练集大小: 713, 验证集大小: 144\n", + "train_data最大日期: 2024-01-02, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-03\n", + "划分后的训练集大小: 712, 验证集大小: 143\n", + "train_data最大日期: 2024-01-03, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-04\n", + "划分后的训练集大小: 708, 验证集大小: 137\n", + "train_data最大日期: 2024-01-04, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-05\n", + "划分后的训练集大小: 706, 验证集大小: 139\n", + "train_data最大日期: 2024-01-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-08\n", + "划分后的训练集大小: 704, 验证集大小: 141\n", + "train_data最大日期: 2024-01-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-09\n", + "划分后的训练集大小: 695, 验证集大小: 135\n", + "train_data最大日期: 2024-01-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-10\n", + "划分后的训练集大小: 691, 验证集大小: 139\n", + "train_data最大日期: 2024-01-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-11\n", + "划分后的训练集大小: 689, 验证集大小: 135\n", + "train_data最大日期: 2024-01-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-12\n", + "划分后的训练集大小: 686, 验证集大小: 136\n", + "train_data最大日期: 2024-01-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-15\n", + "划分后的训练集大小: 685, 验证集大小: 140\n", + "train_data最大日期: 2024-01-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-16\n", + "划分后的训练集大小: 681, 验证集大小: 131\n", + "train_data最大日期: 2024-01-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-17\n", + "划分后的训练集大小: 680, 验证集大小: 138\n", + "train_data最大日期: 2024-01-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-18\n", + "划分后的训练集大小: 685, 验证集大小: 140\n", + "train_data最大日期: 2024-01-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-19\n", + "划分后的训练集大小: 689, 验证集大小: 140\n", + "train_data最大日期: 2024-01-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-22\n", + "划分后的训练集大小: 691, 验证集大小: 142\n", + "train_data最大日期: 2024-01-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-23\n", + "划分后的训练集大小: 698, 验证集大小: 138\n", + "train_data最大日期: 2024-01-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-24\n", + "划分后的训练集大小: 701, 验证集大小: 141\n", + "train_data最大日期: 2024-01-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-25\n", + "划分后的训练集大小: 700, 验证集大小: 139\n", + "train_data最大日期: 2024-01-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-26\n", + "划分后的训练集大小: 698, 验证集大小: 138\n", + "train_data最大日期: 2024-01-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-29\n", + "划分后的训练集大小: 682, 验证集大小: 126\n", + "train_data最大日期: 2024-01-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-30\n", + "划分后的训练集大小: 676, 验证集大小: 132\n", + "train_data最大日期: 2024-01-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-01-31\n", + "划分后的训练集大小: 676, 验证集大小: 141\n", + "train_data最大日期: 2024-01-31, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-02-01\n", + "划分后的训练集大小: 680, 验证集大小: 143\n", + "train_data最大日期: 2024-02-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-02-02\n", + "划分后的训练集大小: 686, 验证集大小: 144\n", + "train_data最大日期: 2024-02-02, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-02-05\n", + "划分后的训练集大小: 706, 验证集大小: 146\n", + "train_data最大日期: 2024-02-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-02-06\n", + "划分后的训练集大小: 721, 验证集大小: 147\n", + "train_data最大日期: 2024-02-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-02-07\n", + "划分后的训练集大小: 727, 验证集大小: 147\n", + "train_data最大日期: 2024-02-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-02-08\n", + "划分后的训练集大小: 731, 验证集大小: 147\n", + "train_data最大日期: 2024-02-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-02-19\n", + "划分后的训练集大小: 731, 验证集大小: 144\n", + "train_data最大日期: 2024-02-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-02-20\n", + "划分后的训练集大小: 726, 验证集大小: 141\n", + "train_data最大日期: 2024-02-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-02-21\n", + "划分后的训练集大小: 721, 验证集大小: 142\n", + "train_data最大日期: 2024-02-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-02-22\n", + "划分后的训练集大小: 711, 验证集大小: 137\n", + "train_data最大日期: 2024-02-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-02-23\n", + "划分后的训练集大小: 699, 验证集大小: 135\n", + "train_data最大日期: 2024-02-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-02-26\n", + "划分后的训练集大小: 685, 验证集大小: 130\n", + "train_data最大日期: 2024-02-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-02-27\n", + "划分后的训练集大小: 667, 验证集大小: 123\n", + "train_data最大日期: 2024-02-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-02-28\n", + "划分后的训练集大小: 629, 验证集大小: 104\n", + "train_data最大日期: 2024-02-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-02-29\n", + "划分后的训练集大小: 617, 验证集大小: 125\n", + "train_data最大日期: 2024-02-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-03-01\n", + "划分后的训练集大小: 615, 验证集大小: 133\n", + "train_data最大日期: 2024-03-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-03-04\n", + "划分后的训练集大小: 624, 验证集大小: 139\n", + "train_data最大日期: 2024-03-04, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-03-05\n", + "划分后的训练集大小: 641, 验证集大小: 140\n", + "train_data最大日期: 2024-03-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-03-06\n", + "划分后的训练集大小: 673, 验证集大小: 136\n", + "train_data最大日期: 2024-03-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-03-07\n", + "划分后的训练集大小: 688, 验证集大小: 140\n", + "train_data最大日期: 2024-03-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-03-08\n", + "划分后的训练集大小: 695, 验证集大小: 140\n", + "train_data最大日期: 2024-03-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-03-11\n", + "划分后的训练集大小: 693, 验证集大小: 137\n", + "train_data最大日期: 2024-03-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-03-12\n", + "划分后的训练集大小: 693, 验证集大小: 140\n", + "train_data最大日期: 2024-03-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-03-13\n", + "划分后的训练集大小: 700, 验证集大小: 143\n", + "train_data最大日期: 2024-03-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-03-14\n", + "划分后的训练集大小: 698, 验证集大小: 138\n", + "train_data最大日期: 2024-03-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-03-15\n", + "划分后的训练集大小: 702, 验证集大小: 144\n", + "train_data最大日期: 2024-03-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-03-18\n", + "划分后的训练集大小: 712, 验证集大小: 147\n", + "train_data最大日期: 2024-03-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-03-19\n", + "划分后的训练集大小: 713, 验证集大小: 141\n", + "train_data最大日期: 2024-03-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-03-20\n", + "划分后的训练集大小: 708, 验证集大小: 138\n", + "train_data最大日期: 2024-03-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-03-21\n", + "划分后的训练集大小: 705, 验证集大小: 135\n", + "train_data最大日期: 2024-03-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-03-22\n", + "划分后的训练集大小: 692, 验证集大小: 131\n", + "train_data最大日期: 2024-03-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-03-25\n", + "划分后的训练集大小: 677, 验证集大小: 132\n", + "train_data最大日期: 2024-03-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-03-26\n", + "划分后的训练集大小: 675, 验证集大小: 139\n", + "train_data最大日期: 2024-03-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-03-27\n", + "划分后的训练集大小: 676, 验证集大小: 139\n", + "train_data最大日期: 2024-03-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-03-28\n", + "划分后的训练集大小: 683, 验证集大小: 142\n", + "train_data最大日期: 2024-03-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-03-29\n", + "划分后的训练集大小: 696, 验证集大小: 144\n", + "train_data最大日期: 2024-03-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-04-01\n", + "划分后的训练集大小: 706, 验证集大小: 142\n", + "train_data最大日期: 2024-04-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-04-02\n", + "划分后的训练集大小: 702, 验证集大小: 135\n", + "train_data最大日期: 2024-04-02, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-04-03\n", + "划分后的训练集大小: 695, 验证集大小: 132\n", + "train_data最大日期: 2024-04-03, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-04-08\n", + "划分后的训练集大小: 691, 验证集大小: 138\n", + "train_data最大日期: 2024-04-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-04-09\n", + "划分后的训练集大小: 689, 验证集大小: 142\n", + "train_data最大日期: 2024-04-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-04-10\n", + "划分后的训练集大小: 693, 验证集大小: 146\n", + "train_data最大日期: 2024-04-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-04-11\n", + "划分后的训练集大小: 703, 验证集大小: 145\n", + "train_data最大日期: 2024-04-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-04-12\n", + "划分后的训练集大小: 711, 验证集大小: 140\n", + "train_data最大日期: 2024-04-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-04-15\n", + "划分后的训练集大小: 712, 验证集大小: 139\n", + "train_data最大日期: 2024-04-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-04-16\n", + "划分后的训练集大小: 708, 验证集大小: 138\n", + "train_data最大日期: 2024-04-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-04-17\n", + "划分后的训练集大小: 705, 验证集大小: 143\n", + "train_data最大日期: 2024-04-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-04-18\n", + "划分后的训练集大小: 699, 验证集大小: 139\n", + "train_data最大日期: 2024-04-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-04-19\n", + "划分后的训练集大小: 698, 验证集大小: 139\n", + "train_data最大日期: 2024-04-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-04-22\n", + "划分后的训练集大小: 702, 验证集大小: 143\n", + "train_data最大日期: 2024-04-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-04-23\n", + "划分后的训练集大小: 711, 验证集大小: 147\n", + "train_data最大日期: 2024-04-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-04-24\n", + "划分后的训练集大小: 710, 验证集大小: 142\n", + "train_data最大日期: 2024-04-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-04-25\n", + "划分后的训练集大小: 711, 验证集大小: 140\n", + "train_data最大日期: 2024-04-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-04-26\n", + "划分后的训练集大小: 718, 验证集大小: 146\n", + "train_data最大日期: 2024-04-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-04-29\n", + "划分后的训练集大小: 720, 验证集大小: 145\n", + "train_data最大日期: 2024-04-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-04-30\n", + "划分后的训练集大小: 716, 验证集大小: 143\n", + "train_data最大日期: 2024-04-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-05-06\n", + "划分后的训练集大小: 718, 验证集大小: 144\n", + "train_data最大日期: 2024-05-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-05-07\n", + "划分后的训练集大小: 713, 验证集大小: 135\n", + "train_data最大日期: 2024-05-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-05-08\n", + "划分后的训练集大小: 706, 验证集大小: 139\n", + "train_data最大日期: 2024-05-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-05-09\n", + "划分后的训练集大小: 700, 验证集大小: 139\n", + "train_data最大日期: 2024-05-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-05-10\n", + "划分后的训练集大小: 698, 验证集大小: 141\n", + "train_data最大日期: 2024-05-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-05-13\n", + "划分后的训练集大小: 697, 验证集大小: 143\n", + "train_data最大日期: 2024-05-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-05-14\n", + "划分后的训练集大小: 706, 验证集大小: 144\n", + "train_data最大日期: 2024-05-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-05-15\n", + "划分后的训练集大小: 713, 验证集大小: 146\n", + "train_data最大日期: 2024-05-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-05-16\n", + "划分后的训练集大小: 724, 验证集大小: 150\n", + "train_data最大日期: 2024-05-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-05-17\n", + "划分后的训练集大小: 729, 验证集大小: 146\n", + "train_data最大日期: 2024-05-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-05-20\n", + "划分后的训练集大小: 730, 验证集大小: 144\n", + "train_data最大日期: 2024-05-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-05-21\n", + "划分后的训练集大小: 726, 验证集大小: 140\n", + "train_data最大日期: 2024-05-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-05-22\n", + "划分后的训练集大小: 725, 验证集大小: 145\n", + "train_data最大日期: 2024-05-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-05-23\n", + "划分后的训练集大小: 719, 验证集大小: 144\n", + "train_data最大日期: 2024-05-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-05-24\n", + "划分后的训练集大小: 717, 验证集大小: 144\n", + "train_data最大日期: 2024-05-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-05-27\n", + "划分后的训练集大小: 721, 验证集大小: 148\n", + "train_data最大日期: 2024-05-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-05-28\n", + "划分后的训练集大小: 729, 验证集大小: 148\n", + "train_data最大日期: 2024-05-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-05-29\n", + "划分后的训练集大小: 726, 验证集大小: 142\n", + "train_data最大日期: 2024-05-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-05-30\n", + "划分后的训练集大小: 723, 验证集大小: 141\n", + "train_data最大日期: 2024-05-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-05-31\n", + "划分后的训练集大小: 726, 验证集大小: 147\n", + "train_data最大日期: 2024-05-31, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-06-03\n", + "划分后的训练集大小: 726, 验证集大小: 148\n", + "train_data最大日期: 2024-06-03, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-06-04\n", + "划分后的训练集大小: 725, 验证集大小: 147\n", + "train_data最大日期: 2024-06-04, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-06-05\n", + "划分后的训练集大小: 729, 验证集大小: 146\n", + "train_data最大日期: 2024-06-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-06-06\n", + "划分后的训练集大小: 734, 验证集大小: 146\n", + "train_data最大日期: 2024-06-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-06-07\n", + "划分后的训练集大小: 736, 验证集大小: 149\n", + "train_data最大日期: 2024-06-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-06-11\n", + "划分后的训练集大小: 736, 验证集大小: 148\n", + "train_data最大日期: 2024-06-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-06-12\n", + "划分后的训练集大小: 736, 验证集大小: 147\n", + "train_data最大日期: 2024-06-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-06-13\n", + "划分后的训练集大小: 732, 验证集大小: 142\n", + "train_data最大日期: 2024-06-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-06-14\n", + "划分后的训练集大小: 728, 验证集大小: 142\n", + "train_data最大日期: 2024-06-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-06-17\n", + "划分后的训练集大小: 725, 验证集大小: 146\n", + "train_data最大日期: 2024-06-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-06-18\n", + "划分后的训练集大小: 722, 验证集大小: 145\n", + "train_data最大日期: 2024-06-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-06-19\n", + "划分后的训练集大小: 720, 验证集大小: 145\n", + "train_data最大日期: 2024-06-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-06-20\n", + "划分后的训练集大小: 721, 验证集大小: 143\n", + "train_data最大日期: 2024-06-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-06-21\n", + "划分后的训练集大小: 722, 验证集大小: 143\n", + "train_data最大日期: 2024-06-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-06-24\n", + "划分后的训练集大小: 721, 验证集大小: 145\n", + "train_data最大日期: 2024-06-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-06-25\n", + "划分后的训练集大小: 721, 验证集大小: 145\n", + "train_data最大日期: 2024-06-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-06-26\n", + "划分后的训练集大小: 721, 验证集大小: 145\n", + "train_data最大日期: 2024-06-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-06-27\n", + "划分后的训练集大小: 719, 验证集大小: 141\n", + "train_data最大日期: 2024-06-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-06-28\n", + "划分后的训练集大小: 720, 验证集大小: 144\n", + "train_data最大日期: 2024-06-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-01\n", + "划分后的训练集大小: 718, 验证集大小: 143\n", + "train_data最大日期: 2024-07-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-02\n", + "划分后的训练集大小: 719, 验证集大小: 146\n", + "train_data最大日期: 2024-07-02, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-03\n", + "划分后的训练集大小: 716, 验证集大小: 142\n", + "train_data最大日期: 2024-07-03, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-04\n", + "划分后的训练集大小: 717, 验证集大小: 142\n", + "train_data最大日期: 2024-07-04, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-05\n", + "划分后的训练集大小: 717, 验证集大小: 144\n", + "train_data最大日期: 2024-07-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-08\n", + "划分后的训练集大小: 717, 验证集大小: 143\n", + "train_data最大日期: 2024-07-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-09\n", + "划分后的训练集大小: 716, 验证集大小: 145\n", + "train_data最大日期: 2024-07-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-10\n", + "划分后的训练集大小: 722, 验证集大小: 148\n", + "train_data最大日期: 2024-07-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-11\n", + "划分后的训练集大小: 728, 验证集大小: 148\n", + "train_data最大日期: 2024-07-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-12\n", + "划分后的训练集大小: 728, 验证集大小: 144\n", + "train_data最大日期: 2024-07-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-15\n", + "划分后的训练集大小: 727, 验证集大小: 142\n", + "train_data最大日期: 2024-07-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-16\n", + "划分后的训练集大小: 725, 验证集大小: 143\n", + "train_data最大日期: 2024-07-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-17\n", + "划分后的训练集大小: 724, 验证集大小: 147\n", + "train_data最大日期: 2024-07-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-18\n", + "划分后的训练集大小: 718, 验证集大小: 142\n", + "train_data最大日期: 2024-07-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-19\n", + "划分后的训练集大小: 721, 验证集大小: 147\n", + "train_data最大日期: 2024-07-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-22\n", + "划分后的训练集大小: 727, 验证集大小: 148\n", + "train_data最大日期: 2024-07-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-23\n", + "划分后的训练集大小: 731, 验证集大小: 147\n", + "train_data最大日期: 2024-07-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-24\n", + "划分后的训练集大小: 728, 验证集大小: 144\n", + "train_data最大日期: 2024-07-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-25\n", + "划分后的训练集大小: 730, 验证集大小: 144\n", + "train_data最大日期: 2024-07-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-26\n", + "划分后的训练集大小: 731, 验证集大小: 148\n", + "train_data最大日期: 2024-07-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-29\n", + "划分后的训练集大小: 730, 验证集大小: 147\n", + "train_data最大日期: 2024-07-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-30\n", + "划分后的训练集大小: 732, 验证集大小: 149\n", + "train_data最大日期: 2024-07-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-07-31\n", + "划分后的训练集大小: 725, 验证集大小: 137\n", + "train_data最大日期: 2024-07-31, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-01\n", + "划分后的训练集大小: 726, 验证集大小: 145\n", + "train_data最大日期: 2024-08-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-02\n", + "划分后的训练集大小: 720, 验证集大小: 142\n", + "train_data最大日期: 2024-08-02, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-05\n", + "划分后的训练集大小: 712, 验证集大小: 139\n", + "train_data最大日期: 2024-08-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-06\n", + "划分后的训练集大小: 706, 验证集大小: 143\n", + "train_data最大日期: 2024-08-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-07\n", + "划分后的训练集大小: 715, 验证集大小: 146\n", + "train_data最大日期: 2024-08-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-08\n", + "划分后的训练集大小: 713, 验证集大小: 143\n", + "train_data最大日期: 2024-08-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-09\n", + "划分后的训练集大小: 709, 验证集大小: 138\n", + "train_data最大日期: 2024-08-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-12\n", + "划分后的训练集大小: 712, 验证集大小: 142\n", + "train_data最大日期: 2024-08-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-13\n", + "划分后的训练集大小: 715, 验证集大小: 146\n", + "train_data最大日期: 2024-08-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-14\n", + "划分后的训练集大小: 714, 验证集大小: 145\n", + "train_data最大日期: 2024-08-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-15\n", + "划分后的训练集大小: 720, 验证集大小: 149\n", + "train_data最大日期: 2024-08-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-16\n", + "划分后的训练集大小: 730, 验证集大小: 148\n", + "train_data最大日期: 2024-08-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-19\n", + "划分后的训练集大小: 735, 验证集大小: 147\n", + "train_data最大日期: 2024-08-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-20\n", + "划分后的训练集大小: 738, 验证集大小: 149\n", + "train_data最大日期: 2024-08-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-21\n", + "划分后的训练集大小: 741, 验证集大小: 148\n", + "train_data最大日期: 2024-08-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-22\n", + "划分后的训练集大小: 738, 验证集大小: 146\n", + "train_data最大日期: 2024-08-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-23\n", + "划分后的训练集大小: 736, 验证集大小: 146\n", + "train_data最大日期: 2024-08-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-26\n", + "划分后的训练集大小: 736, 验证集大小: 147\n", + "train_data最大日期: 2024-08-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-27\n", + "划分后的训练集大小: 733, 验证集大小: 146\n", + "train_data最大日期: 2024-08-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-28\n", + "划分后的训练集大小: 733, 验证集大小: 148\n", + "train_data最大日期: 2024-08-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-29\n", + "划分后的训练集大小: 730, 验证集大小: 143\n", + "train_data最大日期: 2024-08-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-08-30\n", + "划分后的训练集大小: 729, 验证集大小: 145\n", + "train_data最大日期: 2024-08-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-09-02\n", + "划分后的训练集大小: 728, 验证集大小: 146\n", + "train_data最大日期: 2024-09-02, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-09-03\n", + "划分后的训练集大小: 727, 验证集大小: 145\n", + "train_data最大日期: 2024-09-03, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-09-04\n", + "划分后的训练集大小: 722, 验证集大小: 143\n", + "train_data最大日期: 2024-09-04, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-09-05\n", + "划分后的训练集大小: 724, 验证集大小: 145\n", + "train_data最大日期: 2024-09-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-09-06\n", + "划分后的训练集大小: 724, 验证集大小: 145\n", + "train_data最大日期: 2024-09-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-09-09\n", + "划分后的训练集大小: 716, 验证集大小: 138\n", + "train_data最大日期: 2024-09-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-09-10\n", + "划分后的训练集大小: 713, 验证集大小: 142\n", + "train_data最大日期: 2024-09-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-09-11\n", + "划分后的训练集大小: 716, 验证集大小: 146\n", + "train_data最大日期: 2024-09-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-09-12\n", + "划分后的训练集大小: 718, 验证集大小: 147\n", + "train_data最大日期: 2024-09-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-09-13\n", + "划分后的训练集大小: 719, 验证集大小: 146\n", + "train_data最大日期: 2024-09-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-09-18\n", + "划分后的训练集大小: 726, 验证集大小: 145\n", + "train_data最大日期: 2024-09-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-09-19\n", + "划分后的训练集大小: 729, 验证集大小: 145\n", + "train_data最大日期: 2024-09-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-09-20\n", + "划分后的训练集大小: 727, 验证集大小: 144\n", + "train_data最大日期: 2024-09-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-09-23\n", + "划分后的训练集大小: 724, 验证集大小: 144\n", + "train_data最大日期: 2024-09-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-09-24\n", + "划分后的训练集大小: 714, 验证集大小: 136\n", + "train_data最大日期: 2024-09-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-09-25\n", + "划分后的训练集大小: 710, 验证集大小: 141\n", + "train_data最大日期: 2024-09-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-09-26\n", + "划分后的训练集大小: 710, 验证集大小: 145\n", + "train_data最大日期: 2024-09-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-09-27\n", + "划分后的训练集大小: 701, 验证集大小: 135\n", + "train_data最大日期: 2024-09-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-09-30\n", + "划分后的训练集大小: 684, 验证集大小: 127\n", + "train_data最大日期: 2024-09-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-10-08\n", + "划分后的训练集大小: 668, 验证集大小: 120\n", + "train_data最大日期: 2024-10-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-10-09\n", + "划分后的训练集大小: 569, 验证集大小: 42\n", + "train_data最大日期: 2024-10-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-10-10\n", + "划分后的训练集大小: 545, 验证集大小: 121\n", + "train_data最大日期: 2024-10-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-10-11\n", + "划分后的训练集大小: 553, 验证集大小: 143\n", + "train_data最大日期: 2024-10-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-10-14\n", + "划分后的训练集大小: 571, 验证集大小: 145\n", + "train_data最大日期: 2024-10-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-10-15\n", + "划分后的训练集大小: 596, 验证集大小: 145\n", + "train_data最大日期: 2024-10-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-10-16\n", + "划分后的训练集大小: 699, 验证集大小: 145\n", + "train_data最大日期: 2024-10-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-10-17\n", + "划分后的训练集大小: 717, 验证集大小: 139\n", + "train_data最大日期: 2024-10-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-10-18\n", + "划分后的训练集大小: 713, 验证集大小: 139\n", + "train_data最大日期: 2024-10-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-10-21\n", + "划分后的训练集大小: 710, 验证集大小: 142\n", + "train_data最大日期: 2024-10-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-10-22\n", + "划分后的训练集大小: 699, 验证集大小: 134\n", + "train_data最大日期: 2024-10-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-10-23\n", + "划分后的训练集大小: 680, 验证集大小: 126\n", + "train_data最大日期: 2024-10-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-10-24\n", + "划分后的训练集大小: 668, 验证集大小: 127\n", + "train_data最大日期: 2024-10-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-10-25\n", + "划分后的训练集大小: 667, 验证集大小: 138\n", + "train_data最大日期: 2024-10-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-10-28\n", + "划分后的训练集大小: 659, 验证集大小: 134\n", + "train_data最大日期: 2024-10-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-10-29\n", + "划分后的训练集大小: 650, 验证集大小: 125\n", + "train_data最大日期: 2024-10-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-10-30\n", + "划分后的训练集大小: 610, 验证集大小: 86\n", + "train_data最大日期: 2024-10-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-10-31\n", + "划分后的训练集大小: 606, 验证集大小: 123\n", + "train_data最大日期: 2024-10-31, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-11-01\n", + "划分后的训练集大小: 562, 验证集大小: 94\n", + "train_data最大日期: 2024-11-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-11-04\n", + "划分后的训练集大小: 557, 验证集大小: 129\n", + "train_data最大日期: 2024-11-04, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-11-05\n", + "划分后的训练集大小: 565, 验证集大小: 133\n", + "train_data最大日期: 2024-11-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-11-06\n", + "划分后的训练集大小: 618, 验证集大小: 139\n", + "train_data最大日期: 2024-11-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-11-07\n", + "划分后的训练集大小: 613, 验证集大小: 118\n", + "train_data最大日期: 2024-11-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-11-08\n", + "划分后的训练集大小: 648, 验证集大小: 129\n", + "train_data最大日期: 2024-11-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-11-11\n", + "划分后的训练集大小: 653, 验证集大小: 134\n", + "train_data最大日期: 2024-11-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-11-12\n", + "划分后的训练集大小: 654, 验证集大小: 134\n", + "train_data最大日期: 2024-11-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-11-13\n", + "划分后的训练集大小: 646, 验证集大小: 131\n", + "train_data最大日期: 2024-11-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-11-14\n", + "划分后的训练集大小: 667, 验证集大小: 139\n", + "train_data最大日期: 2024-11-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-11-15\n", + "划分后的训练集大小: 676, 验证集大小: 138\n", + "train_data最大日期: 2024-11-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-11-18\n", + "划分后的训练集大小: 677, 验证集大小: 135\n", + "train_data最大日期: 2024-11-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-11-19\n", + "划分后的训练集大小: 680, 验证集大小: 137\n", + "train_data最大日期: 2024-11-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-11-20\n", + "划分后的训练集大小: 687, 验证集大小: 138\n", + "train_data最大日期: 2024-11-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-11-21\n", + "划分后的训练集大小: 690, 验证集大小: 142\n", + "train_data最大日期: 2024-11-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-11-22\n", + "划分后的训练集大小: 681, 验证集大小: 129\n", + "train_data最大日期: 2024-11-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-11-25\n", + "划分后的训练集大小: 676, 验证集大小: 130\n", + "train_data最大日期: 2024-11-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-11-26\n", + "划分后的训练集大小: 656, 验证集大小: 117\n", + "train_data最大日期: 2024-11-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-11-27\n", + "划分后的训练集大小: 652, 验证集大小: 134\n", + "train_data最大日期: 2024-11-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-11-28\n", + "划分后的训练集大小: 647, 验证集大小: 137\n", + "train_data最大日期: 2024-11-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-11-29\n", + "划分后的训练集大小: 653, 验证集大小: 135\n", + "train_data最大日期: 2024-11-29, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-02\n", + "划分后的训练集大小: 657, 验证集大小: 134\n", + "train_data最大日期: 2024-12-02, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-03\n", + "划分后的训练集大小: 673, 验证集大小: 133\n", + "train_data最大日期: 2024-12-03, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-04\n", + "划分后的训练集大小: 663, 验证集大小: 124\n", + "train_data最大日期: 2024-12-04, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-05\n", + "划分后的训练集大小: 653, 验证集大小: 127\n", + "train_data最大日期: 2024-12-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-06\n", + "划分后的训练集大小: 651, 验证集大小: 133\n", + "train_data最大日期: 2024-12-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-09\n", + "划分后的训练集大小: 652, 验证集大小: 135\n", + "train_data最大日期: 2024-12-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-10\n", + "划分后的训练集大小: 655, 验证集大小: 136\n", + "train_data最大日期: 2024-12-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-11\n", + "划分后的训练集大小: 669, 验证集大小: 138\n", + "train_data最大日期: 2024-12-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-12\n", + "划分后的训练集大小: 686, 验证集大小: 144\n", + "train_data最大日期: 2024-12-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-13\n", + "划分后的训练集大小: 677, 验证集大小: 124\n", + "train_data最大日期: 2024-12-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-16\n", + "划分后的训练集大小: 668, 验证集大小: 126\n", + "train_data最大日期: 2024-12-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-17\n", + "划分后的训练集大小: 655, 验证集大小: 123\n", + "train_data最大日期: 2024-12-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-18\n", + "划分后的训练集大小: 651, 验证集大小: 134\n", + "train_data最大日期: 2024-12-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-19\n", + "划分后的训练集大小: 645, 验证集大小: 138\n", + "train_data最大日期: 2024-12-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-20\n", + "划分后的训练集大小: 664, 验证集大小: 143\n", + "train_data最大日期: 2024-12-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-23\n", + "划分后的训练集大小: 681, 验证集大小: 143\n", + "train_data最大日期: 2024-12-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-24\n", + "划分后的训练集大小: 700, 验证集大小: 142\n", + "train_data最大日期: 2024-12-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-25\n", + "划分后的训练集大小: 708, 验证集大小: 142\n", + "train_data最大日期: 2024-12-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-26\n", + "划分后的训练集大小: 710, 验证集大小: 140\n", + "train_data最大日期: 2024-12-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-27\n", + "划分后的训练集大小: 707, 验证集大小: 140\n", + "train_data最大日期: 2024-12-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-30\n", + "划分后的训练集大小: 710, 验证集大小: 146\n", + "train_data最大日期: 2024-12-30, 训练天数:5, feat size:116\n", + "test_data最大日期: 2024-12-31\n", + "划分后的训练集大小: 711, 验证集大小: 143\n", + "train_data最大日期: 2024-12-31, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-01-02\n", + "划分后的训练集大小: 706, 验证集大小: 137\n", + "train_data最大日期: 2025-01-02, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-01-03\n", + "划分后的训练集大小: 703, 验证集大小: 137\n", + "train_data最大日期: 2025-01-03, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-01-06\n", + "划分后的训练集大小: 707, 验证集大小: 144\n", + "train_data最大日期: 2025-01-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-01-07\n", + "划分后的训练集大小: 700, 验证集大小: 139\n", + "train_data最大日期: 2025-01-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-01-08\n", + "划分后的训练集大小: 700, 验证集大小: 143\n", + "train_data最大日期: 2025-01-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-01-09\n", + "划分后的训练集大小: 708, 验证集大小: 145\n", + "train_data最大日期: 2025-01-09, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-01-10\n", + "划分后的训练集大小: 709, 验证集大小: 138\n", + "train_data最大日期: 2025-01-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-01-13\n", + "划分后的训练集大小: 710, 验证集大小: 145\n", + "train_data最大日期: 2025-01-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-01-14\n", + "划分后的训练集大小: 717, 验证集大小: 146\n", + "train_data最大日期: 2025-01-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-01-15\n", + "划分后的训练集大小: 714, 验证集大小: 140\n", + "train_data最大日期: 2025-01-15, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-01-16\n", + "划分后的训练集大小: 715, 验证集大小: 146\n", + "train_data最大日期: 2025-01-16, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-01-17\n", + "划分后的训练集大小: 709, 验证集大小: 132\n", + "train_data最大日期: 2025-01-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-01-20\n", + "划分后的训练集大小: 704, 验证集大小: 140\n", + "train_data最大日期: 2025-01-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-01-21\n", + "划分后的训练集大小: 704, 验证集大小: 146\n", + "train_data最大日期: 2025-01-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-01-22\n", + "划分后的训练集大小: 706, 验证集大小: 142\n", + "train_data最大日期: 2025-01-22, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-01-23\n", + "划分后的训练集大小: 698, 验证集大小: 138\n", + "train_data最大日期: 2025-01-23, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-01-24\n", + "划分后的训练集大小: 710, 验证集大小: 144\n", + "train_data最大日期: 2025-01-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-01-27\n", + "划分后的训练集大小: 717, 验证集大小: 147\n", + "train_data最大日期: 2025-01-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-02-05\n", + "划分后的训练集大小: 714, 验证集大小: 143\n", + "train_data最大日期: 2025-02-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-02-06\n", + "划分后的训练集大小: 710, 验证集大小: 138\n", + "train_data最大日期: 2025-02-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-02-07\n", + "划分后的训练集大小: 712, 验证集大小: 140\n", + "train_data最大日期: 2025-02-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-02-10\n", + "划分后的训练集大小: 704, 验证集大小: 136\n", + "train_data最大日期: 2025-02-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-02-11\n", + "划分后的训练集大小: 698, 验证集大小: 141\n", + "train_data最大日期: 2025-02-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-02-12\n", + "划分后的训练集大小: 690, 验证集大小: 135\n", + "train_data最大日期: 2025-02-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-02-13\n", + "划分后的训练集大小: 691, 验证集大小: 139\n", + "train_data最大日期: 2025-02-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-02-14\n", + "划分后的训练集大小: 691, 验证集大小: 140\n", + "train_data最大日期: 2025-02-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-02-17\n", + "划分后的训练集大小: 696, 验证集大小: 141\n", + "train_data最大日期: 2025-02-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-02-18\n", + "划分后的训练集大小: 695, 验证集大小: 140\n", + "train_data最大日期: 2025-02-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-02-19\n", + "划分后的训练集大小: 704, 验证集大小: 144\n", + "train_data最大日期: 2025-02-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-02-20\n", + "划分后的训练集大小: 708, 验证集大小: 143\n", + "train_data最大日期: 2025-02-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-02-21\n", + "划分后的训练集大小: 711, 验证集大小: 143\n", + "train_data最大日期: 2025-02-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-02-24\n", + "划分后的训练集大小: 710, 验证集大小: 140\n", + "train_data最大日期: 2025-02-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-02-25\n", + "划分后的训练集大小: 708, 验证集大小: 138\n", + "train_data最大日期: 2025-02-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-02-26\n", + "划分后的训练集大小: 711, 验证集大小: 147\n", + "train_data最大日期: 2025-02-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-02-27\n", + "划分后的训练集大小: 711, 验证集大小: 143\n", + "train_data最大日期: 2025-02-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-02-28\n", + "划分后的训练集大小: 708, 验证集大小: 140\n", + "train_data最大日期: 2025-02-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-03-03\n", + "划分后的训练集大小: 707, 验证集大小: 139\n", + "train_data最大日期: 2025-03-03, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-03-04\n", + "划分后的训练集大小: 710, 验证集大小: 141\n", + "train_data最大日期: 2025-03-04, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-03-05\n", + "划分后的训练集大小: 707, 验证集大小: 144\n", + "train_data最大日期: 2025-03-05, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-03-06\n", + "划分后的训练集大小: 705, 验证集大小: 141\n", + "train_data最大日期: 2025-03-06, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-03-07\n", + "划分后的训练集大小: 711, 验证集大小: 146\n", + "train_data最大日期: 2025-03-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-03-10\n", + "划分后的训练集大小: 717, 验证集大小: 145\n", + "train_data最大日期: 2025-03-10, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-03-11\n", + "划分后的训练集大小: 715, 验证集大小: 139\n", + "train_data最大日期: 2025-03-11, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-03-12\n", + "划分后的训练集大小: 717, 验证集大小: 146\n", + "train_data最大日期: 2025-03-12, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-03-13\n", + "划分后的训练集大小: 719, 验证集大小: 143\n", + "train_data最大日期: 2025-03-13, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-03-14\n", + "划分后的训练集大小: 719, 验证集大小: 146\n", + "train_data最大日期: 2025-03-14, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-03-17\n", + "划分后的训练集大小: 716, 验证集大小: 142\n", + "train_data最大日期: 2025-03-17, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-03-18\n", + "划分后的训练集大小: 721, 验证集大小: 144\n", + "train_data最大日期: 2025-03-18, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-03-19\n", + "划分后的训练集大小: 714, 验证集大小: 139\n", + "train_data最大日期: 2025-03-19, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-03-20\n", + "划分后的训练集大小: 709, 验证集大小: 138\n", + "train_data最大日期: 2025-03-20, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-03-21\n", + "划分后的训练集大小: 706, 验证集大小: 143\n", + "train_data最大日期: 2025-03-21, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-03-24\n", + "划分后的训练集大小: 702, 验证集大小: 138\n", + "train_data最大日期: 2025-03-24, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-03-25\n", + "划分后的训练集大小: 701, 验证集大小: 143\n", + "train_data最大日期: 2025-03-25, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-03-26\n", + "划分后的训练集大小: 702, 验证集大小: 140\n", + "train_data最大日期: 2025-03-26, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-03-27\n", + "划分后的训练集大小: 704, 验证集大小: 140\n", + "train_data最大日期: 2025-03-27, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-03-28\n", + "划分后的训练集大小: 702, 验证集大小: 141\n", + "train_data最大日期: 2025-03-28, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-03-31\n", + "划分后的训练集大小: 711, 验证集大小: 147\n", + "train_data最大日期: 2025-03-31, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-04-01\n", + "划分后的训练集大小: 709, 验证集大小: 141\n", + "train_data最大日期: 2025-04-01, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-04-02\n", + "划分后的训练集大小: 713, 验证集大小: 144\n", + "train_data最大日期: 2025-04-02, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-04-03\n", + "划分后的训练集大小: 716, 验证集大小: 143\n", + "train_data最大日期: 2025-04-03, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-04-07\n", + "划分后的训练集大小: 716, 验证集大小: 141\n", + "train_data最大日期: 2025-04-07, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-04-08\n", + "划分后的训练集大小: 704, 验证集大小: 135\n", + "train_data最大日期: 2025-04-08, 训练天数:5, feat size:116\n", + "test_data最大日期: 2025-04-09\n", + "划分后的训练集大小: 703, 验证集大小: 140\n" + ] + } + ], + "source": [ + "\n", + "gc.collect()\n", + "\n", + "print('finish')\n", + "# qdf = qdf[qdf['trade_date'] >= '2022-01-01']\n", + "\n", + "final_predictions = rolling_train_predict(\n", + " pdf[(pdf['trade_date'] >= '2022-01-01') & (pdf['trade_date'] <= '2029-03-26')], 5, 1, feature_columns,\n", + " days=days, validation_days=0, filter_index=filter_index, params=light_params)\n", + "# final_predictions2 = rolling_train_predict(\n", + "# pdf[(pdf['trade_date'] >= '2022-01-01') & (pdf['trade_date'] <= '2029-03-26')], 20, 1, feature_columns,\n", + "# days=days, validation_days=0, filter_index=filter_index, params=light_params)\n", + "# final_predictions['score'] = final_predictions.groupby('trade_date')['score'].rank(ascending=False)\n", + "# final_predictions2['score'] = final_predictions2.groupby('trade_date')['score'].rank(ascending=False)\n", + "# final_predictions = pd.merge(final_predictions, final_predictions2, on=['trade_date', 'ts_code'], suffixes=['_1', '_2'])\n", + "# final_predictions['score'] = final_predictions['score_1'] + final_predictions['score_2']\n", + "final_predictions = final_predictions.loc[final_predictions.groupby('trade_date')['score'].idxmax()]\n", + "# final_predictions = final_predictions[final_predictions['score'] <= 6]\n", + "final_predictions[['trade_date', 'score', 'ts_code']].to_csv('predictions_test.tsv', index=False)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 96, + "id": "e01fe33b-e30d-4bc6-bf40-de91e61862b4", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T18:00:15.486598Z", + "start_time": "2025-04-11T18:00:15.482042Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Empty DataFrame\n", + "Columns: [ts_code, trade_date, open, close, high, low, vol, pct_chg, turnover_rate, pe_ttm, circ_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_l1_code, cat_l2_code, 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, log(circ_mv), cov, delta_cov, alpha_22_improved, alpha_003, alpha_007, alpha_013, cat_up_limit, cat_down_limit, up_limit_count_10d, down_limit_count_10d, consecutive_up_limit, vol_break, weight_roc5, price_cost_divergence, smallcap_concentration, cost_stability, high_cost_break_days, liquidity_risk, ...]\n", + "Index: []\n", + "\n", + "[0 rows x 180 columns]\n" + ] + } + ], + "source": [ + "print(pdf[pdf['trade_date'] == '2025-03-08'])" + ] + }, + { + "cell_type": "code", + "execution_count": 97, + "id": "0dc75517-c857-4f1d-8815-e807400a6d33", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T18:00:15.508026Z", + "start_time": "2025-04-11T18:00:15.503740Z" + } + }, + "outputs": [], + "source": [ + "# pdf1 = select_pre_zt_stocks_dynamic(df[(df['trade_date'] >= '2022-03-26') & (df['trade_date'] <= '2029-03-26')])\n", + "# pdf1 = pdf1.merge(industry_df, on=['cat_l1_code', 'trade_date'], how='left')\n", + "# pdf1 = pdf1.replace([np.inf, -np.inf], np.nan)\n", + "#\n", + "# feature_columns = [col for col in pdf1.columns if col in pdf.columns]\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 '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 'cat_l1_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", + "#\n", + "# print(feature_columns)\n", + "# numeric_columns = pdf.select_dtypes(include=['float64', 'int64']).columns\n", + "# numeric_columns = [col for col in numeric_columns if col in feature_columns]\n", + "#\n", + "# pdf1 = cross_sectional_quantile_filter(pdf1, numeric_columns)\n", + "# pdf1 = cross_sectional_standardization(pdf1, numeric_columns)\n", + "#\n", + "# # pdf1 = cross_sectional_standardization(pdf1, numeric_columns)\n", + "# # pdf1 = pdf1[pdf1['trade_date'] <= '2025-03-26']\n", + "# pdf1 = pdf1.sort_values(by=['ts_code', 'trade_date'])\n", + "# filter_index1 = pdf1['future_return'].between(pdf1['future_return'].quantile(0.01), pdf1['future_return'].quantile(0.99))\n", + "#\n", + "# print('-----------------------------------------')\n", + "# pdf2 = select_pre_zt_stocks_dynamic(df[(df['trade_date'] >= '2022-03-26') & (df['trade_date'] <= '2025-03-26')])\n", + "# pdf2 = pdf2.merge(industry_df, on=['cat_l2_code', 'trade_date'], how='left')\n", + "# pdf2 = pdf2.replace([np.inf, -np.inf], np.nan)\n", + "#\n", + "# pdf2 = cross_sectional_quantile_filter(pdf2, numeric_columns)\n", + "# pdf2 = cross_sectional_standardization(pdf2, numeric_columns)\n", + "#\n", + "# # pdf2 = cross_sectional_standardization(pdf2, numeric_columns)\n", + "#\n", + "# # pdf2 = pdf2[pdf2['trade_date'] <= '2025-03-26']\n", + "# pdf2 = pdf2.sort_values(by=['ts_code', 'trade_date'])\n", + "# filter_index2 = pdf2['future_return'].between(pdf2['future_return'].quantile(0.01), pdf2['future_return'].quantile(0.99))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "id": "8299a6f461097f14", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T18:00:15.552767Z", + "start_time": "2025-04-11T18:00:15.549412Z" + } + }, + "outputs": [], + "source": [ + "# are_equal = pdf1[filter_index1].equals(pdf2[filter_index2])\n", + "# print(are_equal) # 输出 True 或 False\n", + "#\n", + "# are_equal = pdf1.equals(pdf2)\n", + "# print(are_equal) # 输出 True 或 False\n", + "#\n", + "# are_equal = filter_index1.equals(filter_index2)\n", + "# print(are_equal) # 输出 True 或 False" + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "id": "3f5079aa2c937c22", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T18:00:15.630669Z", + "start_time": "2025-04-11T18:00:15.626474Z" + } + }, + "outputs": [], + "source": [ + "# final_predictions1 = rolling_train_predict(\n", + "# pdf1[(pdf1['trade_date'] >= '2024-12-01')], 5, 1, feature_columns,\n", + "# days=days, validation_days=0, filter_index=filter_index1, params=light_params)\n", + "# final_predictions.to_csv('test1.tsv', index=False)\n", + "#\n", + "# final_predictions2 = rolling_train_predict(\n", + "# pdf2[(pdf2['trade_date'] >= '2024-12-01')], 5, 1, feature_columns,\n", + "# days=days, validation_days=0, filter_index=filter_index2, params=light_params)\n", + "# final_predictions2.to_csv('test2.tsv', index=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 100, + "id": "199b12e7e20e4e6a", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-11T18:00:15.643137Z", + "start_time": "2025-04-11T18:00:15.640776Z" + } + }, + "outputs": [], + "source": [ + "# print(final_predictions1['trade_date'].max())\n", + "# print(final_predictions2['trade_date'].max())\n", + "#\n", + "# are_equal = final_predictions1[(final_predictions1['trade_date'] >= '2022-12-01') & (final_predictions1['trade_date'] <= '2025-03-26')].equals(final_predictions2[(final_predictions2['trade_date'] >= '2022-12-01') & (final_predictions2['trade_date'] <= '2025-03-26')])\n", + "# print(are_equal) # 输出 True 或 False" + ] + } + ], + "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 +} diff --git a/main/train/RollingRank.txt b/main/train/RollingRank.txt new file mode 100644 index 0000000..d992c05 --- /dev/null +++ b/main/train/RollingRank.txt @@ -0,0 +1,918 @@ +#!/usr/bin/env python +# coding: utf-8 + +# In[1]: + + +# %load_ext autoreload +# %autoreload 2 + +import pandas as pd +import warnings + +warnings.filterwarnings("ignore") + +pd.set_option('display.max_columns', None) + + +# In[2]: + + +from utils.utils import read_and_merge_h5_data + +print('daily data') +df = read_and_merge_h5_data('../../data/daily_data.h5', key='daily_data', + columns=['ts_code', 'trade_date', 'open', 'close', 'high', 'low', 'vol', 'pct_chg'], + df=None) + +print('daily basic') +df = read_and_merge_h5_data('../../data/daily_basic.h5', key='daily_basic', + columns=['ts_code', 'trade_date', 'turnover_rate', 'pe_ttm', 'circ_mv', 'volume_ratio', + 'is_st'], df=df, join='inner') + +print('stk limit') +df = read_and_merge_h5_data('../../data/stk_limit.h5', key='stk_limit', + columns=['ts_code', 'trade_date', 'pre_close', 'up_limit', 'down_limit'], + df=df) +print('money flow') +df = read_and_merge_h5_data('../../data/money_flow.h5', key='money_flow', + columns=['ts_code', 'trade_date', 'buy_sm_vol', 'sell_sm_vol', 'buy_lg_vol', 'sell_lg_vol', + 'buy_elg_vol', 'sell_elg_vol', 'net_mf_vol'], + df=df) +print('cyq perf') +df = read_and_merge_h5_data('../../data/cyq_perf.h5', key='cyq_perf', + columns=['ts_code', 'trade_date', 'his_low', 'his_high', 'cost_5pct', 'cost_15pct', + 'cost_50pct', + 'cost_85pct', 'cost_95pct', 'weight_avg', 'winner_rate'], + df=df) +print(df.info()) + + +# In[3]: + + +print('industry') +industry_df = read_and_merge_h5_data('../../data/industry_data.h5', key='industry_data', + columns=['ts_code', 'l2_code', 'in_date'], + df=None, on=['ts_code'], join='left') + + +def merge_with_industry_data(df, industry_df): + # 确保日期字段是 datetime 类型 + df['trade_date'] = pd.to_datetime(df['trade_date']) + industry_df['in_date'] = pd.to_datetime(industry_df['in_date']) + + # 对 industry_df 按 ts_code 和 in_date 排序 + industry_df_sorted = industry_df.sort_values(['in_date', 'ts_code']) + + # 对原始 df 按 ts_code 和 trade_date 排序 + df_sorted = df.sort_values(['trade_date', 'ts_code']) + + # 使用 merge_asof 进行向后合并 + merged = pd.merge_asof( + df_sorted, + industry_df_sorted, + by='ts_code', # 按 ts_code 分组 + left_on='trade_date', + right_on='in_date', + direction='backward' + ) + + # 获取每个 ts_code 的最早 in_date 记录 + min_in_date_per_ts = (industry_df_sorted + .groupby('ts_code') + .first() + .reset_index()[['ts_code', 'l2_code']]) + + # 填充未匹配到的记录(trade_date 早于所有 in_date 的情况) + merged['l2_code'] = merged['l2_code'].fillna( + merged['ts_code'].map(min_in_date_per_ts.set_index('ts_code')['l2_code']) + ) + + # 保留需要的列并重置索引 + result = merged.reset_index(drop=True) + return result + + +# 使用示例 +df = merge_with_industry_data(df, industry_df) +# print(mdf[mdf['ts_code'] == '600751.SH'][['ts_code', 'trade_date', 'l2_code']]) + + +# In[4]: + + +def calculate_indicators(df): + """ + 计算四个指标:当日涨跌幅、5日移动平均、RSI、MACD。 + """ + df = df.sort_values('trade_date') + df['daily_return'] = (df['close'] - df['pre_close']) / df['pre_close'] * 100 + # df['5_day_ma'] = df['close'].rolling(window=5).mean() + delta = df['close'].diff() + gain = delta.where(delta > 0, 0) + loss = -delta.where(delta < 0, 0) + avg_gain = gain.rolling(window=14).mean() + avg_loss = loss.rolling(window=14).mean() + rs = avg_gain / avg_loss + df['RSI'] = 100 - (100 / (1 + rs)) + + # 计算MACD + ema12 = df['close'].ewm(span=12, adjust=False).mean() + ema26 = df['close'].ewm(span=26, adjust=False).mean() + df['MACD'] = ema12 - ema26 + df['Signal_line'] = df['MACD'].ewm(span=9, adjust=False).mean() + df['MACD_hist'] = df['MACD'] - df['Signal_line'] + + # 4. 情绪因子1:市场上涨比例(Up Ratio) + df['up_ratio'] = df['daily_return'].apply(lambda x: 1 if x > 0 else 0) + df['up_ratio_20d'] = df['up_ratio'].rolling(window=20).mean() # 过去20天上涨比例 + + # 5. 情绪因子2:成交量变化率(Volume Change Rate) + df['volume_mean'] = df['vol'].rolling(window=20).mean() # 过去20天的平均成交量 + df['volume_change_rate'] = (df['vol'] - df['volume_mean']) / df['volume_mean'] * 100 # 成交量变化率 + + # 6. 情绪因子3:波动率(Volatility) + df['volatility'] = df['daily_return'].rolling(window=20).std() # 过去20天的日收益率标准差 + + # 7. 情绪因子4:成交额变化率(Amount Change Rate) + df['amount_mean'] = df['amount'].rolling(window=20).mean() # 过去20天的平均成交额 + df['amount_change_rate'] = (df['amount'] - df['amount_mean']) / df['amount_mean'] * 100 # 成交额变化率 + + return df + + +def generate_index_indicators(h5_filename): + df = pd.read_hdf(h5_filename, key='index_data') + df['trade_date'] = pd.to_datetime(df['trade_date'], format='%Y%m%d') + df = df.sort_values('trade_date') + + # 计算每个ts_code的相关指标 + df_indicators = [] + for ts_code in df['ts_code'].unique(): + df_index = df[df['ts_code'] == ts_code].copy() + df_index = calculate_indicators(df_index) + df_indicators.append(df_index) + + # 合并所有指数的结果 + df_all_indicators = pd.concat(df_indicators, ignore_index=True) + + # 保留trade_date列,并将同一天的数据按ts_code合并成一行 + df_final = df_all_indicators.pivot_table( + index='trade_date', + columns='ts_code', + values=['daily_return', 'RSI', 'MACD', 'Signal_line', + 'MACD_hist', 'up_ratio_20d', 'volume_change_rate', 'volatility', + 'amount_change_rate', 'amount_mean'], + aggfunc='last' + ) + + df_final.columns = [f"{col[1]}_{col[0]}" for col in df_final.columns] + df_final = df_final.reset_index() + + return df_final + + +# 使用函数 +h5_filename = '../../data/index_data.h5' +index_data = generate_index_indicators(h5_filename) +index_data = index_data.dropna() + + + +# In[6]: + + +from utils.factor import get_act_factor + + +def read_industry_data(h5_filename): + # 读取 H5 文件中所有的行业数据 + industry_data = pd.read_hdf(h5_filename, key='sw_daily', columns=[ + 'ts_code', 'trade_date', 'open', 'close', 'high', 'low', 'pe', 'pb', 'vol' + ]) # 假设 H5 文件的键是 'industry_data' + industry_data = industry_data.sort_values(by=['ts_code', 'trade_date']) + industry_data = industry_data.reindex() + industry_data['trade_date'] = pd.to_datetime(industry_data['trade_date'], format='%Y%m%d') + + grouped = industry_data.groupby('ts_code', group_keys=False) + industry_data['obv'] = grouped.apply( + lambda x: pd.Series(talib.OBV(x['close'].values, x['vol'].values), index=x.index) + ) + industry_data['return_5'] = grouped['close'].apply(lambda x: x / x.shift(5) - 1) + industry_data['return_20'] = grouped['close'].apply(lambda x: x / x.shift(20) - 1) + + industry_data = get_act_factor(industry_data, cat=False) + industry_data = industry_data.sort_values(by=['trade_date', 'ts_code']) + + # # 计算每天每个 ts_code 的因子和当天所有 ts_code 的中位数的偏差 + # factor_columns = ['obv', 'return_5', 'return_20', 'act_factor1', 'act_factor2', 'act_factor3', 'act_factor4'] # 因子列 + # + # for factor in factor_columns: + # if factor in industry_data.columns: + # # 计算每天每个 ts_code 的因子值与当天所有 ts_code 的中位数的偏差 + # industry_data[f'{factor}_deviation'] = industry_data.groupby('trade_date')[factor].transform( + # lambda x: x - x.mean()) + + industry_data['return_5_percentile'] = industry_data.groupby('trade_date')['return_5'].transform( + lambda x: x.rank(pct=True)) + industry_data['return_20_percentile'] = industry_data.groupby('trade_date')['return_20'].transform( + lambda x: x.rank(pct=True)) + industry_data = industry_data.drop(columns=['open', 'close', 'high', 'low', 'pe', 'pb', 'vol']) + + industry_data = industry_data.rename( + columns={col: f'industry_{col}' for col in industry_data.columns if col not in ['ts_code', 'trade_date']}) + + industry_data = industry_data.rename(columns={'ts_code': 'cat_l2_code'}) + return industry_data + + +industry_df = read_industry_data('../../data/sw_daily.h5') + + +# In[7]: + + +origin_columns = df.columns.tolist() +origin_columns = [col for col in origin_columns if + col not in ['turnover_rate', 'pe_ttm', 'volume_ratio', 'vol', 'pct_chg', 'l2_code', 'winner_rate']] +origin_columns = [col for col in origin_columns if col not in index_data.columns] +origin_columns = [col for col in origin_columns if 'cyq' not in col] +print(origin_columns) + + +# In[8]: + + +def filter_data(df): + # df = df.groupby('trade_date').apply(lambda x: x.nlargest(1000, 'act_factor1')) + df = df[~df['is_st']] + df = df[~df['ts_code'].str.endswith('BJ')] + df = df[~df['ts_code'].str.startswith('30')] + df = df[~df['ts_code'].str.startswith('68')] + df = df[~df['ts_code'].str.startswith('8')] + df = df[df['trade_date'] >= '20180101'] + df = df.drop(columns=['in_date']) + df = df.reset_index(drop=True) + return df + + +df = filter_data(df) +# df = get_technical_factor(df) +# df = get_act_factor(df) +# df = get_money_flow_factor(df) +# df = get_alpha_factor(df) +# df = get_limit_factor(df) +# df = get_cyp_perf_factor(df) +# df = get_mv_factors(df) +df, _ = get_rolling_factor(df) +df, _ = get_simple_factor(df) +# df = df.merge(industry_df, on=['l2_code', 'trade_date'], how='left') +df = df.rename(columns={'l2_code': 'cat_l2_code'}) +# df = df.merge(index_data, on='trade_date', how='left') + +print(df.info()) + + +# In[9]: + + +def create_deviation_within_dates(df, feature_columns): + groupby_col = 'cat_l2_code' # 使用 trade_date 进行分组 + new_columns = {} + ret_feature_columns = feature_columns[:] + + # 自动选择所有数值型特征 + num_features = [col for col in feature_columns if 'cat' not in col and 'index' not in col] + + # 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'] + num_features = [col for col in num_features if 'cat' not in col and 'industry' not in col] + num_features = [col for col in num_features if 'limit' not in col] + num_features = [col for col in num_features if 'cyq' not in col] + + # 遍历所有数值型特征 + for feature in num_features: + if feature == 'trade_date': # 不需要对 'trade_date' 计算偏差 + continue + + # grouped_mean = df.groupby(['trade_date'])[feature].transform('mean') + # deviation_col_name = f'deviation_mean_{feature}' + # new_columns[deviation_col_name] = df[feature] - grouped_mean + # ret_feature_columns.append(deviation_col_name) + + grouped_mean = df.groupby(['trade_date', groupby_col])[feature].transform('mean') + deviation_col_name = f'deviation_mean_{feature}' + new_columns[deviation_col_name] = df[feature] - grouped_mean + ret_feature_columns.append(deviation_col_name) + + # 将新计算的偏差特征与原始 DataFrame 合并 + df = pd.concat([df, pd.DataFrame(new_columns)], axis=1) + + # for feature in ['obv', 'return_20', 'act_factor1', 'act_factor2', 'act_factor3', 'act_factor4']: + # df[f'deviation_industry_{feature}'] = df[feature] - df[f'industry_{feature}'] + + return df, ret_feature_columns + + +# In[10]: + + +import pandas as pd + +from scipy.stats import ks_2samp, wasserstein_distance +from sklearn.metrics import roc_auc_score +from sklearn.model_selection import train_test_split +from sklearn.preprocessing import StandardScaler + + +def remove_shifted_features(train_data, feature_columns, ks_threshold=0.05, wasserstein_threshold=0.1, size=0.8): + dropped_features = [] + + all_dates = train_data['trade_date'].unique() # 获取所有唯一的 trade_date + split_date = all_dates[int(len(all_dates) * size)] # 划分点为倒数第 validation_days 天 + train_data_split = train_data[train_data['trade_date'] < split_date] # 训练集 + val_data_split = train_data[train_data['trade_date'] >= split_date] # 验证集 + + # **统计数据漂移** + numeric_columns = train_data_split.select_dtypes(include=['float64', 'int64']).columns + numeric_columns = [col for col in numeric_columns if col in feature_columns] + for feature in numeric_columns: + ks_stat, p_value = ks_2samp(train_data_split[feature], val_data_split[feature]) + wasserstein_dist = wasserstein_distance(train_data_split[feature], val_data_split[feature]) + + if p_value < ks_threshold or wasserstein_dist > wasserstein_threshold: + dropped_features.append(feature) + + print(f"检测到 {len(dropped_features)} 个可能漂移的特征: {dropped_features}") + + # **应用阈值进行最终筛选** + filtered_features = [f for f in feature_columns if f not in dropped_features] + + return filtered_features, dropped_features + + +def remove_outliers_label_percentile(label: pd.Series, lower_percentile: float = 0.01, upper_percentile: float = 0.99, + log=True): + if not (0 <= lower_percentile < upper_percentile <= 1): + raise ValueError("Percentile values must satisfy 0 <= lower_percentile < upper_percentile <= 1.") + + # Calculate lower and upper bounds based on percentiles + lower_bound = label.quantile(lower_percentile) + upper_bound = label.quantile(upper_percentile) + + # Filter out values outside the bounds + filtered_label = label[(label >= lower_bound) & (label <= upper_bound)] + + # Print the number of removed outliers + if log: + print(f"Removed {len(label) - len(filtered_label)} outliers.") + return filtered_label + + +def calculate_risk_adjusted_target(df, days=5): + df = df.sort_values(by=['ts_code', 'trade_date']) + + df['future_close'] = df.groupby('ts_code')['close'].shift(-days) + df['future_open'] = df.groupby('ts_code')['open'].shift(-1) + df['future_return'] = (df['future_close'] - df['future_open']) / df['future_open'] + + df['future_volatility'] = df.groupby('ts_code')['future_return'].rolling(days, min_periods=1).std().reset_index( + level=0, drop=True) + sharpe_ratio = df['future_return'] * df['future_volatility'] + sharpe_ratio.replace([np.inf, -np.inf], np.nan, inplace=True) + + return sharpe_ratio + + +def calculate_score(df, days=5, lambda_param=1.0): + def calculate_max_drawdown(prices): + peak = prices.iloc[0] # 初始化峰值 + max_drawdown = 0 # 初始化最大回撤 + + for price in prices: + if price > peak: + peak = price # 更新峰值 + else: + drawdown = (peak - price) / peak # 计算当前回撤 + max_drawdown = max(max_drawdown, drawdown) # 更新最大回撤 + + return max_drawdown + + def compute_stock_score(stock_df): + stock_df = stock_df.sort_values(by=['trade_date']) + future_return = stock_df['future_return'] + # 使用已有的 pct_chg 字段计算波动率 + volatility = stock_df['pct_chg'].rolling(days).std().shift(-days) + max_drawdown = stock_df['close'].rolling(days).apply(calculate_max_drawdown, raw=False).shift(-days) + score = future_return - lambda_param * max_drawdown + return score + + # # 确保 DataFrame 按照股票代码和交易日期排序 + # df = df.sort_values(by=['ts_code', 'trade_date']) + + # 对每个股票分别计算 score + df['score'] = df.groupby('ts_code').apply(compute_stock_score).reset_index(level=0, drop=True) + + return df['score'] + + +def remove_highly_correlated_features(df, feature_columns, threshold=0.9): + numeric_features = df[feature_columns].select_dtypes(include=[np.number]).columns.tolist() + if not numeric_features: + raise ValueError("No numeric features found in the provided data.") + + corr_matrix = df[numeric_features].corr().abs() + upper = corr_matrix.where(np.triu(np.ones(corr_matrix.shape), k=1).astype(bool)) + to_drop = [column for column in upper.columns if any(upper[column] > threshold)] + remaining_features = [col for col in feature_columns if col not in to_drop + or 'act' in col or 'af' in col] + return remaining_features + + +import pandas as pd +from sklearn.preprocessing import StandardScaler + + +def cross_sectional_standardization(df, features): + df_sorted = df.sort_values(by='trade_date') # 按时间排序 + df_standardized = df_sorted.copy() + + for date in df_sorted['trade_date'].unique(): + # 获取当前时间点的数据 + current_data = df_standardized[df_standardized['trade_date'] == date] + + # 只对指定特征进行标准化 + scaler = StandardScaler() + standardized_values = scaler.fit_transform(current_data[features]) + + # 将标准化结果重新赋值回去 + df_standardized.loc[df_standardized['trade_date'] == date, features] = standardized_values + + return df_standardized + + +import numpy as np +import pandas as pd +import statsmodels.api as sm + +from concurrent.futures import ProcessPoolExecutor + + +def neutralize_manual(df, features, industry_col, mkt_cap_col): + """ 手动实现简单回归以提升速度 """ + + for col in features: + residuals = [] + for _, group in df.groupby(industry_col): + if len(group) > 1: + x = np.log(group[mkt_cap_col]) # 市值对数 + y = group[col] # 因子值 + beta = np.cov(y, x)[0, 1] / np.var(x) # 计算斜率 + alpha = np.mean(y) - beta * np.mean(x) # 计算截距 + resid = y - (alpha + beta * x) # 计算残差 + residuals.extend(resid) + else: + residuals.extend(group[col]) # 样本不足时保留原值 + + df[col] = residuals + + return df + + +import gc + +gc.collect() + + +def mad_filter(df, features, n=3): + for col in features: + median = df[col].median() + mad = np.median(np.abs(df[col] - median)) + upper = median + n * mad + lower = median - n * mad + df[col] = np.clip(df[col], lower, upper) # 截断极值 + return df + + +def percentile_filter(df, features, lower_percentile=0.01, upper_percentile=0.99): + for col in features: + # 按日期分组计算上下百分位数 + lower_bound = df.groupby('trade_date')[col].transform( + lambda x: x.quantile(lower_percentile) + ) + upper_bound = df.groupby('trade_date')[col].transform( + lambda x: x.quantile(upper_percentile) + ) + # 截断超出范围的值 + df[col] = np.clip(df[col], lower_bound, upper_bound) + return df + + +from scipy.stats import iqr + + +def iqr_filter(df, features): + for col in features: + df[col] = df.groupby('trade_date')[col].transform( + lambda x: (x - x.median()) / iqr(x) if iqr(x) != 0 else x + ) + return df + + +def quantile_filter(df, features, lower_quantile=0.01, upper_quantile=0.99, window=60): + df = df.copy() + for col in features: + # 计算 rolling 统计量,需要按日期进行 groupby + rolling_lower = df.groupby('trade_date')[col].transform( + lambda x: x.rolling(window=min(len(x), window)).quantile(lower_quantile)) + rolling_upper = df.groupby('trade_date')[col].transform( + lambda x: x.rolling(window=min(len(x), window)).quantile(upper_quantile)) + + # 对数据进行裁剪 + df[col] = np.clip(df[col], rolling_lower, rolling_upper) + + return df + + +# In[11]: + + +# print(test_data.head()[['act_factor1', 'act_factor2', 'ts_code', 'trade_date']]) + + +# In[12]: + + +from sklearn.preprocessing import StandardScaler +import lightgbm as lgb +import matplotlib.pyplot as plt +from sklearn.decomposition import PCA + + +def train_light_model(train_data_df, params, feature_columns, callbacks, evals, + print_feature_importance=True, num_boost_round=100, + validation_days=180, use_pca=False, split_date=None): # 新增参数:validation_days + # 确保数据按时间排序 + train_data_df = train_data_df.sort_values(by='trade_date') + + numeric_columns = train_data_df.select_dtypes(include=['float64', 'int64']).columns + numeric_columns = [col for col in numeric_columns if col in feature_columns] + # X_train.loc[:, numeric_columns] = scaler.fit_transform(X_train[numeric_columns]) + # X_val.loc[:, numeric_columns] = scaler.transform(X_val[numeric_columns]) + # train_data_df = cross_sectional_standardization(train_data_df, numeric_columns) + + # 去除标签为空的样本 + train_data_df = train_data_df.dropna(subset=['label']) + print('原始训练集大小: ', len(train_data_df)) + + # 按时间顺序划分训练集和验证集 + if split_date is None: + all_dates = train_data_df['trade_date'].unique() # 获取所有唯一的 trade_date + split_date = all_dates[-validation_days] # 划分点为倒数第 validation_days 天 + train_data_split = train_data_df[train_data_df['trade_date'] < split_date] # 训练集 + val_data_split = train_data_df[train_data_df['trade_date'] >= split_date] # 验证集 + + # 打印划分结果 + print(f"划分后的训练集大小: {len(train_data_split)}, 验证集大小: {len(val_data_split)}") + + # 提取特征和标签 + X_train = train_data_split[feature_columns] + y_train = train_data_split['label'] + + X_val = val_data_split[feature_columns] + y_val = val_data_split['label'] + + # 标准化数值特征 + scaler = StandardScaler() + + # 计算每个 trade_date 内的样本数(LTR 需要 group 信息) + train_groups = train_data_split.groupby('trade_date').size().tolist() + val_groups = val_data_split.groupby('trade_date').size().tolist() + + # 处理类别特征 + categorical_feature = [col for col in feature_columns if 'cat' in col] + + pca = None + if use_pca: + pca = PCA(n_components=0.95) # 或指定 n_components=固定值(如 10) + numeric_features = [col for col in feature_columns if col not in categorical_feature] + numeric_pca = pca.fit_transform(X_train[numeric_features]) + X_train = pd.concat([pd.DataFrame(numeric_pca, index=X_train.index), X_train[categorical_feature]], axis=1) + + numeric_pca = pca.transform(X_val[numeric_features]) + X_val = pd.concat([pd.DataFrame(numeric_pca, index=X_val.index), X_val[categorical_feature]], axis=1) + + # 计算权重(基于时间) + # trade_date = train_data_split['trade_date'] # 交易日期 + # weights = (trade_date - trade_date.min()).dt.days / (trade_date.max() - trade_date.min()).days + 1 + # weights = train_data_split.groupby('trade_date')['std_return_5'].transform( + # lambda x: x / x.mean() + # ) + ud = sorted(train_data_split["trade_date"].unique().tolist()) + date_weights = {date: weight * weight for date, weight in zip(ud, np.linspace(1, 10, len(ud)))} + params['weight'] = train_data_split["trade_date"].map(date_weights).tolist() + + train_dataset = lgb.Dataset( + X_train, label=y_train, group=train_groups, + categorical_feature=categorical_feature + ) + + # weights = val_data_split.groupby('trade_date')['std_return_5'].transform( + # lambda x: x / x.mean() + # ) + val_dataset = lgb.Dataset( + X_val, label=y_val, group=val_groups, + categorical_feature=categorical_feature + ) + + # 训练模型 + model = lgb.train( + params, train_dataset, num_boost_round=num_boost_round, + valid_sets=[train_dataset, val_dataset], valid_names=['train', 'valid'], + callbacks=callbacks + ) + + # 打印特征重要性(如果需要) + if print_feature_importance: + lgb.plot_metric(evals) + lgb.plot_importance(model, importance_type='split', max_num_features=20) + plt.show() + + return model, scaler, pca + + +# In[13]: + + +days = 2 +df = df.sort_values(by=['ts_code', 'trade_date']) +# df['future_return'] = df.groupby('ts_code', group_keys=False)['close'].apply(lambda x: x.shift(-days) / x - 1) +df['future_return'] = (df.groupby('ts_code')['close'].shift(-days) - df.groupby('ts_code')['open'].shift(-1)) / \ + df.groupby('ts_code')['open'].shift(-1) +df['future_volatility'] = ( + df.groupby('ts_code')['pct_chg'] + .transform(lambda x: x.rolling(days).std().shift(-days)) +) +df['future_score'] = calculate_score(df, days=2, lambda_param=0.3) +df['label'] = df.groupby('trade_date', group_keys=False)['future_score'].transform( + lambda x: pd.qcut(x, q=20, labels=False, duplicates='drop') +) +# df['future_score'] = ( +# 0.7 * df['future_return'] +# * 0.3 * df['future_volatility'] +# ) + + +# In[30]: + + +def select_pre_zt_stocks_dynamic( + stock_df, +): + stock_df = stock_df.groupby('trade_date', group_keys=False).apply( + lambda x: x.nlargest(1000, 'return_20') + ) + return stock_df + + +pdf = select_pre_zt_stocks_dynamic(df) +filter_index = pdf['future_return'].between(pdf['future_return'].quantile(0.01), pdf['future_return'].quantile(0.99)) + +# filter_index = pdf['future_volatility'].between(pdf['future_volatility'].quantile(0.01), +# pdf['future_volatility'].quantile(0.99)) | filter_index + + +# In[ ]: + + +pdf = pdf.merge(industry_df, on=['cat_l2_code', 'trade_date'], how='left') +pdf = pdf.sort_values(['trade_date']) +pdf = pdf.replace([np.inf, -np.inf], np.nan) + + +feature_columns = [col for col in pdf.columns if col in pdf.columns] +feature_columns = [col for col in feature_columns if col not in ['trade_date', + 'ts_code', + 'label']] +feature_columns = [col for col in feature_columns if 'future' not in col] +feature_columns = [col for col in feature_columns if 'label' not in col] +feature_columns = [col for col in feature_columns if 'score' not in col] +feature_columns = [col for col in feature_columns if 'gen' not in col] +feature_columns = [col for col in feature_columns if 'cat_l2_code' not in col] +feature_columns = [col for col in feature_columns if col not in origin_columns] +feature_columns = [col for col in feature_columns if not col.startswith('_')] + +numeric_columns = pdf.select_dtypes(include=['float64', 'int64']).columns +numeric_columns = [col for col in numeric_columns if col in feature_columns] + +# feature_columns, _ = remove_shifted_features(pdf, feature_columns, size=0.8) + +pdf = quantile_filter(pdf, numeric_columns) + +pdf = cross_sectional_standardization(pdf, numeric_columns) + + +# print('去极值') +# train_data = quantile_filter(train_data, numeric_columns) # 去极值 +# # print('中性化') +# # train_data = neutralize_manual(train_data, numeric_columns, industry_col='cat_l2_code', mkt_cap_col='log(circ_mv)') # 中性化 +# print('去极值') +# test_data = quantile_filter(test_data, numeric_columns) # 去极值 + +feature_columns = remove_highly_correlated_features(pdf, + feature_columns) +print(len(pdf)) + + +# In[123]: + + +# print('train data size: ', len(train_data)) + +label_gain = list(range(len(df['label'].unique()))) +label_gain = [gain * gain for gain in label_gain] +light_params = { + 'label_gain': label_gain, + 'objective': 'lambdarank', + 'metric': 'ndcg', + 'learning_rate': 0.03, + 'num_leaves': 32, + # 'min_data_in_leaf': 128, + 'max_depth': 8, + 'max_bin': 32, + 'feature_fraction': 0.7, + # 'bagging_fraction': 0.7, + 'bagging_freq': 5, + 'lambda_l1': 0.1, + 'lambda_l2': 0.1, + 'boosting': 'gbdt', + 'verbosity': -1, + 'extra_trees': True, + 'max_position': 5, + 'ndcg_at': 1, + 'quant_train_renew_leaf': True, + 'lambdarank_truncation_level': 3, + # 'lambdarank_position_bias_regularization': 1, + 'seed': 7 +} +evals = {} + +gc.collect() + + +# In[128]: + + +gc.collect() + + +def rolling_train_predict(df, train_days, test_days, feature_columns_origin, days=5, use_pca=False, validation_days=60, + filter_index=None): + # 1. 按照交易日期排序 + unique_dates = df[df['trade_date'] >= '2020-01-01']['trade_date'].unique().tolist() + unique_dates = sorted(unique_dates) + n = len(unique_dates) + + # 2. 计算需要跳过的天数,使后续窗口对齐 + extra_days = (n - train_days) % test_days + start_index = extra_days # 从此索引开始滚动 + + predictions_list = [] + + for start in range(start_index, n - train_days - test_days + 1, test_days): + + train_dates = unique_dates[start: start + train_days] + test_dates = unique_dates[start + train_days: start + train_days + test_days] + + # 根据日期筛选数据 + train_data = df[filter_index & df['trade_date'].isin(train_dates)] + test_data = df[df['trade_date'].isin(test_dates)] + + train_data = train_data.sort_values('trade_date') + test_data = test_data.sort_values('trade_date') + + # feature_columns, _ = remove_shifted_features(train_data, feature_columns_origin, size=0.8) + + train_data = train_data.dropna(subset=feature_columns) + train_data = train_data.dropna(subset=['label']) + train_data = train_data.reset_index(drop=True) + + # print(test_data.tail()) + test_data = test_data.dropna(subset=feature_columns) + # test_data = test_data.dropna(subset=['label']) + test_data = test_data.reset_index(drop=True) + + # print(len(train_data)) + print(f"最小日期: {train_data['trade_date'].min().strftime('%Y-%m-%d')}") + print(f"最大日期: {train_data['trade_date'].max().strftime('%Y-%m-%d')}") + # print(len(test_data)) + print(f"最小日期: {test_data['trade_date'].min().strftime('%Y-%m-%d')}") + print(f"最大日期: {test_data['trade_date'].max().strftime('%Y-%m-%d')}") + + cat_columns = [col for col in df.columns if col.startswith('cat')] + for col in cat_columns: + train_data[col] = train_data[col].astype('category') + test_data[col] = test_data[col].astype('category') + + label_gain = list(range(len(train_data['label'].unique()))) + label_gain = [(gain + 1) * (gain + 1) for gain in label_gain] + light_params['label_gain'] = label_gain + + # ud = train_data["trade_date"].unique() + # date_weights = {date: weight for date, weight in zip(ud, np.linspace(1, 2, len(unique_dates)))} + # light_params['weight'] = train_data["trade_date"].map(date_weights).tolist() + + # print(f'feature_columns: {feature_columns}') + # feature_contri = [2 if feat.startswith('act_factor') else 1 for feat in feature_columns] + # light_params['feature_contri'] = feature_contri + model, _, _ = train_light_model(train_data.dropna(subset=['label']), + light_params, feature_columns, + [lgb.log_evaluation(period=100), + lgb.callback.record_evaluation(evals), + lgb.early_stopping(100, first_metric_only=True) + ], evals, + num_boost_round=3000, validation_days=validation_days, + print_feature_importance=False, use_pca=False) + + score_df = test_data.copy() + score_df['score'] = model.predict(score_df[feature_columns]) + score_df = score_df.loc[score_df.groupby('trade_date')['score'].idxmax()] + score_df = score_df[['trade_date', 'score', 'ts_code']] + predictions_list.append(score_df) + + # m = 5 + # all_data = [] + # for i, trade_date in enumerate(sorted(score_df['trade_date'].unique().tolist())): + # # 提取当前日期的数据 + # current_data = score_df[score_df['trade_date'] == trade_date] + # all_data.append(current_data) + # + # numeric_columns = [col for col in feature_columns if col in current_data.select_dtypes(include=['float64', 'int64']).columns] + # current_data = cross_sectional_standardization(current_data, numeric_columns) + # current_data['score'] = model.predict(current_data[feature_columns]) + # daily_top_score = current_data.loc[[current_data['score'].idxmax()]] + # predictions_list.append(daily_top_score[['trade_date', 'score', 'ts_code']]) + # + # if i % m == 0: + # train_data_split = pd.concat(all_data) + # train_data_split = train_data_split.dropna(subset=['label']) + # + # X_train = train_data_split[feature_columns] + # y_train = train_data_split['label'] + # + # train_groups = train_data_split.groupby('trade_date').size().tolist() + # categorical_feature = [col for col in feature_columns if 'cat' in col] + # + # train_dataset = lgb.Dataset( + # X_train, label=y_train, group=train_groups, + # categorical_feature=categorical_feature + # ) + # + # model = lgb.train( + # light_params, train_dataset, num_boost_round=36, + # init_model=model + # ) + # all_data = [] + + final_predictions = pd.concat(predictions_list, ignore_index=True) + return final_predictions + + +# In[129]: + + +gc.collect() + +print(df[df['ts_code'] == '000001.SZ'].tail(1)[['act_factor1', 'act_factor2']]) +print('finish') +# qdf = qdf[qdf['trade_date'] >= '2022-01-01'] + +final_predictions = rolling_train_predict(pdf[pdf['trade_date'] >= '2020-01-01'], 500, 20, feature_columns, + days=days, validation_days=60, filter_index=filter_index) +final_predictions.to_csv('predictions_test.tsv', index=False) + + +# In[126]: + + +print(df[df['ts_code'] == '000001.SZ'].tail(1)[['act_factor1', 'act_factor2']]) +print('finish') + + +# In[29]: + + +train_data = pdf[filter_index & (pdf['trade_date'] == '2023-01-03')] +train_data = train_data.dropna(subset=['label']) +train_data = train_data.reset_index(drop=True) +print(len(train_data)) + + +# In[34]: + + +# filter_index = pdf['future_return'].between(pdf['future_return'].quantile(0.01), pdf['future_return'].quantile(0.99)) + +train_data = pdf[filter_index & (pdf['trade_date'] == '2023-01-03')] +print(len(train_data)) + diff --git a/code/train/RollingRank.ipynb b/main/train/RollingRankCopy.ipynb similarity index 54% rename from code/train/RollingRank.ipynb rename to main/train/RollingRankCopy.ipynb index a197c70..2d520fd 100644 --- a/code/train/RollingRank.ipynb +++ b/main/train/RollingRankCopy.ipynb @@ -2,71 +2,57 @@ "cells": [ { "cell_type": "code", + "execution_count": 113, "id": "79a7758178bafdd3", "metadata": { - "jupyter": { - "source_hidden": true - }, "ExecuteTime": { "end_time": "2025-04-10T13:32:04.258788Z", "start_time": "2025-04-10T13:32:03.855779Z" + }, + "jupyter": { + "source_hidden": true } }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "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\")\n", - "\n", - "pd.set_option('display.max_columns', None)\n" - ], - "outputs": [], - "execution_count": 1 + "warnings.filterwarnings(\"ignore\")" + ] }, { "cell_type": "code", + "execution_count": 114, "id": "a79cafb06a7e0e43", "metadata": { - "scrolled": true, "ExecuteTime": { "end_time": "2025-04-10T13:32:50.139059Z", "start_time": "2025-04-10T13:32:04.267804Z" - } + }, + "scrolled": true }, - "source": [ - "from 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'],\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', 'volume_ratio',\n", - " 'is_st'], df=df, join='inner')\n", - "df = df[df['trade_date'] >= '2021-01-01']\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())" - ], "outputs": [ { "name": "stdout", @@ -123,10 +109,41 @@ ] } ], - "execution_count": 2 + "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'],\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', 'volume_ratio',\n", + " 'is_st'], df=df, join='inner')\n", + "df = df[df['trade_date'] >= '2021-01-01']\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": 115, "id": "cac01788dac10678", "metadata": { "ExecuteTime": { @@ -134,6 +151,15 @@ "start_time": "2025-04-10T13:32:50.335162Z" } }, + "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", @@ -181,20 +207,11 @@ "# 使用示例\n", "df = merge_with_industry_data(df, industry_df)\n", "# print(mdf[mdf['ts_code'] == '600751.SH'][['ts_code', 'trade_date', 'l2_code']])" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "industry\n" - ] - } - ], - "execution_count": 3 + ] }, { "cell_type": "code", + "execution_count": 116, "id": "c4e9e1d31da6dba6", "metadata": { "ExecuteTime": { @@ -202,6 +219,7 @@ "start_time": "2025-04-10T13:32:55.203565Z" } }, + "outputs": [], "source": [ "def calculate_indicators(df):\n", " \"\"\"\n", @@ -278,27 +296,103 @@ "h5_filename = '../../data/index_data.h5'\n", "index_data = generate_index_indicators(h5_filename)\n", "index_data = index_data.dropna()\n" - ], - "outputs": [], - "execution_count": 4 + ] }, { "cell_type": "code", - "id": "a735bc02ceb4d872", + "execution_count": 117, + "id": "53f86ddc0677a6d7", "metadata": { - "jupyter": { - "source_hidden": true - }, "ExecuteTime": { - "end_time": "2025-04-10T13:32:55.359628Z", - "start_time": "2025-04-10T13:32:55.317246Z" + "end_time": "2025-04-10T13:33:00.552850Z", + "start_time": "2025-04-10T13:32:55.363645Z" + }, + "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", + " 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": 118, + "id": "dbe2fd8021b9417f", + "metadata": { + "ExecuteTime": { + "end_time": "2025-04-10T13:33:00.573680Z", + "start_time": "2025-04-10T13:33:00.569106Z" } }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['ts_code', 'open', 'close', 'high', 'low', 'circ_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": 119, + "id": "da6e43bd", + "metadata": {}, + "outputs": [], "source": [ - "import numpy as np\n", - "import talib\n", - "\n", - "\n", "def get_rolling_factor(df):\n", " old_columns = df.columns.tolist()[:]\n", "\n", @@ -307,6 +401,108 @@ "\n", " grouped = df.groupby('ts_code', group_keys=False)\n", "\n", + " epsilon = 1e-8\n", + " df['lg_elg_net_buy_vol'] = df['buy_lg_vol'] + df['buy_elg_vol'] - df['sell_lg_vol'] - df['sell_elg_vol']\n", + " # 检查 'volume' 列是否存在且有效\n", + " df['flow_lg_elg_intensity'] = df['lg_elg_net_buy_vol'] / (df['vol'] + epsilon)\n", + "\n", + " # 2. 散户与主力背离度 (Retail vs Institutional Divergence)\n", + " # 衡量小单净流入与(大单+超大单)净流入的差异或比率\n", + " df['sm_net_buy_vol'] = df['buy_sm_vol'] - df['sell_sm_vol']\n", + " df['flow_divergence_diff'] = df['sm_net_buy_vol'] - df['lg_elg_net_buy_vol']\n", + " # 比率形式可能更稳定\n", + " df['flow_divergence_ratio'] = df['sm_net_buy_vol'] / (\n", + " df['lg_elg_net_buy_vol'] + np.sign(df['lg_elg_net_buy_vol']) * epsilon + epsilon) # 复杂处理避免0/0\n", + "\n", + " # 3. 资金流结构变动 (Flow Structure Change - Relative Strength of Large Flow)\n", + " # 大单+超大单买入额占总买入额的比例的变化\n", + " df['total_buy_vol'] = df['buy_sm_vol'] + df['buy_lg_vol'] + df['buy_elg_vol']\n", + " df['lg_elg_buy_prop'] = (df['buy_lg_vol'] + df['buy_elg_vol']) / (df['total_buy_vol'] + epsilon)\n", + " df['flow_struct_buy_change'] = grouped['lg_elg_buy_prop'].diff(1) # 1日变化\n", + "\n", + " # 4. 资金流加速度 (Flow Acceleration)\n", + " # 净主力资金流的变化率(二阶导)\n", + " df['lg_elg_net_buy_vol_change'] = grouped['lg_elg_net_buy_vol'].diff(1)\n", + " df['flow_lg_elg_accel'] = grouped['lg_elg_net_buy_vol_change'].diff(1)\n", + "\n", + " # # 5. 极端资金流事件 (Categorical: Extreme Flow Event)\n", + " # # 定义主力资金流强度是否处于其历史极端水平(例如,过去N天的90分位数以上或10分位数以下)\n", + " # rolling_window = 20 # 可调整窗口期\n", + "\n", + " # # Step 1: Calculate the rolling quantiles separately\n", + " # rolling_high = grouped['flow_lg_elg_intensity'].rolling(rolling_window, min_periods=1).quantile(0.9) # min_periods=1 保证窗口未满时也有输出\n", + " # rolling_low = grouped['flow_lg_elg_intensity'].rolling(rolling_window, min_periods=1).quantile(0.1)\n", + "\n", + " # # Step 2: Assign the results to the DataFrame\n", + " # # 确保 df 和 rolling_high/low 的索引是一致的\n", + " # # 如果 df 的索引在此期间没有被修改过,这通常是安全的\n", + " # df['flow_lg_elg_intensity_rolling_high'] = rolling_high\n", + " # df['flow_lg_elg_intensity_rolling_low'] = rolling_low\n", + "\n", + " # # Step 3: Continue with the logic using the new columns\n", + " # conditions_flow = [\n", + " # df['flow_lg_elg_intensity'] > df['flow_lg_elg_intensity_rolling_high'],\n", + " # df['flow_lg_elg_intensity'] < df['flow_lg_elg_intensity_rolling_low']\n", + " # ]\n", + " # choices_flow = [1, -1] # 1: 极端流入, -1: 极端流出\n", + " # df['cat_extreme_flow'] = np.select(conditions_flow, choices_flow, default=0)\n", + "\n", + " # --- 筹码分布因子 ---\n", + "\n", + " # 6. 筹码集中度 (Chip Concentration)\n", + " # 衡量筹码分布的紧密程度,例如 95% 与 5% 成本价的差距,相对于当前价格进行标准化\n", + " # 检查 'close' 列是否存在且有效\n", + " df['chip_concentration_range'] = (df['cost_95pct'] - df['cost_5pct']) / (df['close'] + epsilon)\n", + "\n", + " # 7. 筹码分布偏度 (Chip Distribution Skewness Proxy)\n", + " # 比较中位数成本 (cost_50pct) 和加权平均成本 (weight_avg)\n", + " # weight_avg > cost_50pct 暗示高成本区有较多筹码(右偏)\n", + " df['chip_skewness'] = (df['weight_avg'] - df['cost_50pct']) / (df['cost_50pct'] + epsilon)\n", + "\n", + " # 8. 浮筹比例 (Floating Chips Proxy)\n", + " # 衡量短期内(例如15%成本线以下)的筹码比例与总获利盘比例的关系\n", + " # winner_rate 高但 cost_15pct 接近当前价,可能意味着大部分获利盘成本不高,易浮动\n", + " # 这里简化为:获利盘比例 与 (当前价-15%成本价)/当前价 的乘积\n", + " price_dist_cost15 = (df['close'] - df['cost_15pct']) / (df['close'] + epsilon)\n", + " df['floating_chip_proxy'] = df['winner_rate'] * np.maximum(0, price_dist_cost15) # 只考虑价格高于15%成本线的情况\n", + "\n", + " # 9. 成本支撑强度变化 (Cost Support Strength Change)\n", + " # 观察低位筹码成本(如 5% 或 15% 分位点)的变化率,看支撑位是上移还是下移\n", + " df['cost_support_15pct_change'] = grouped['cost_15pct'].pct_change(1) * 100 # 百分比变化\n", + "\n", + " # 10. 获利盘压力/支撑区 (Categorical: Winner Rate Zone & Price Position)\n", + " # 结合获利盘比例和当前价格相对于筹码成本的位置\n", + " # 例如: 价格在 85% 成本线之上 & 获利盘 > 0.8 -> 高位派发风险区?\n", + " # 价格在 15% 成本线之下 & 获利盘 < 0.2 -> 低位吸筹潜力区?\n", + " conditions_winner = [\n", + " (df['close'] > df['cost_85pct']) & (df['winner_rate'] > 0.8), # 高位 & 高获利盘\n", + " (df['close'] < df['cost_15pct']) & (df['winner_rate'] < 0.2), # 低位 & 低获利盘\n", + " (df['close'] > df['cost_50pct']) & (df['winner_rate'] > 0.5), # 中高位 & 多数获利\n", + " (df['close'] < df['cost_50pct']) & (df['winner_rate'] < 0.5), # 中低位 & 多数亏损\n", + " ]\n", + " choices_winner = [1, 2, 3, 4] # 1:高风险区, 2:低潜力区, 3:中上获利区, 4:中下亏损区\n", + " df['cat_winner_price_zone'] = np.select(conditions_winner, choices_winner, default=0) # 0: 其他\n", + "\n", + " # --- 结合因子 ---\n", + "\n", + " # 11. 主力行为与筹码结构一致性 (Flow-Chip Consistency)\n", + " # 例如:主力净买入发生在价格接近下方筹码密集区(如 cost_15pct 到 cost_50pct)时\n", + " price_near_low_support = (df['close'] > df['cost_15pct']) & (df['close'] < df['cost_50pct'])\n", + " df['flow_chip_consistency'] = df['lg_elg_net_buy_vol'] * price_near_low_support.astype(int)\n", + " # 可以进一步标准化或做成 categorical\n", + "\n", + " # 12. 获利了结压力/承接盘强度 (Profit-Taking Pressure vs Absorption)\n", + " # 在高获利盘(winner_rate > 0.7)的情况下,观察主力资金是净流出(了结)还是净流入(高位换手/承接)\n", + " high_winner_rate_flag = (df['winner_rate'] > 0.7).astype(int)\n", + " df['profit_taking_vs_absorb'] = df['lg_elg_net_buy_vol'] * high_winner_rate_flag\n", + " # 正值表示高获利盘下主力仍在买入(承接),负值表示主力在卖出(了结)\n", + "\n", + " # 清理临时列和可能产生的 NaN (可选,根据需要处理)\n", + " cols_to_drop = ['lg_elg_net_buy_vol', 'sm_net_buy_vol', 'total_buy_vol', 'lg_elg_buy_prop',\n", + " 'lg_elg_net_buy_vol_change', 'flow_lg_elg_intensity_rolling_high',\n", + " 'flow_lg_elg_intensity_rolling_low']\n", + " # df = df.drop(columns=cols_to_drop)\n", + "\n", " window = 20\n", " df['_is_positive'] = (df['pct_chg'] > 0).astype(int)\n", " df['_is_negative'] = (df['pct_chg'] < 0).astype(int)\n", @@ -461,7 +657,7 @@ " df['rank_act_factor2'] = df.groupby('trade_date', group_keys=False)['act_factor2'].rank(ascending=False, pct=True)\n", " df['rank_act_factor3'] = df.groupby('trade_date', group_keys=False)['act_factor3'].rank(ascending=False, pct=True)\n", "\n", - " df['log(circ_mv)'] = np.log(df['circ_mv'])\n", + " df['log_circ_mv'] = np.log(df['circ_mv'])\n", "\n", " window_high_volume = 5\n", " window_close_stddev = 20\n", @@ -529,7 +725,7 @@ "\n", " df['price_cost_divergence'] = grouped.apply(rolling_corr)\n", "\n", - " df['smallcap_concentration'] = (1 / df['log(circ_mv)']) * (df['cost_85pct'] - df['cost_15pct'])\n", + " df['smallcap_concentration'] = (1 / df['log_circ_mv']) * (df['cost_85pct'] - df['cost_15pct'])\n", "\n", " # 16. 筹码稳定性指数 (20日波动率)\n", " df['weight_std20'] = grouped['weight_avg'].apply(lambda x: x.rolling(20).std())\n", @@ -544,11 +740,11 @@ "\n", " # 7. 市值波动率因子 (使用 grouped)\n", " df['turnover_std'] = grouped['turnover_rate'].transform(lambda x: x.rolling(window=20).std())\n", - " df['mv_volatility'] = grouped.apply(lambda x: x['turnover_std'] / x['log(circ_mv)'])\n", + " df['mv_volatility'] = grouped.apply(lambda x: x['turnover_std'] / x['log_circ_mv'])\n", "\n", " # 8. 市值成长性因子\n", " df['volume_growth'] = grouped['vol'].pct_change(periods=20)\n", - " df['mv_growth'] = df['volume_growth'] / df['log(circ_mv)']\n", + " df['mv_growth'] = df['volume_growth'] / df['log_circ_mv']\n", "\n", " # AR 指标\n", " df[\"ar\"] = grouped.apply(\n", @@ -587,14 +783,14 @@ " df['up'] = (df['high'] - df[['close', 'open']].max(axis=1)) / df['close']\n", " df['down'] = (df[['close', 'open']].min(axis=1) - df['low']) / df['close']\n", "\n", - " df['obv-maobv_6'] = df['obv'] - df['maobv_6']\n", + " df['obv_maobv_6'] = df['obv'] - df['maobv_6']\n", "\n", " # 计算比值指标\n", - " df['std_return_5 / std_return_90'] = df['std_return_5'] / df['std_return_90']\n", + " df['std_return_5_over_std_return_90'] = df['std_return_5'] / df['std_return_90']\n", " # df['std_return_5 / std_return_25'] = df['std_return_5'] / df['std_return_25']\n", "\n", " # 计算标准差差值\n", - " df['std_return_90 - std_return_90_2'] = df['std_return_90'] - df['std_return_90_2']\n", + " df['std_return_90_minus_std_return_90_2'] = df['std_return_90'] - df['std_return_90_2']\n", "\n", " # df['cat_af1'] = df['act_factor1'] > 0\n", " df['cat_af2'] = df['act_factor2'] > df['act_factor1']\n", @@ -613,7 +809,7 @@ " df['buy_lg_vol_minus_sell_lg_vol'] = (df['buy_lg_vol'] - df['sell_lg_vol']) / df['net_mf_vol']\n", " df['buy_elg_vol_minus_sell_elg_vol'] = (df['buy_elg_vol'] - df['sell_elg_vol']) / df['net_mf_vol']\n", "\n", - " df['log(circ_mv)'] = np.log(df['circ_mv'])\n", + " df['log_circ_mv'] = np.log(df['circ_mv'])\n", "\n", " df['ctrl_strength'] = (df['cost_85pct'] - df['cost_15pct']) / (df['his_high'] - df['his_low'])\n", "\n", @@ -629,123 +825,34 @@ " df['cost_atr_adj'] = (df['cost_95pct'] - df['cost_5pct']) / df['atr_14']\n", "\n", " # 12. 小盘股筹码集中度\n", - " df['smallcap_concentration'] = (1 / df['log(circ_mv)']) * (df['cost_85pct'] - df['cost_15pct'])\n", + " df['smallcap_concentration'] = (1 / df['log_circ_mv']) * (df['cost_85pct'] - df['cost_15pct'])\n", "\n", " df['cat_golden_resonance'] = ((df['close'] > df['weight_avg']) &\n", " (df['volume_ratio'] > 1.5) &\n", " (df['winner_rate'] > 0.7))\n", "\n", - " df['mv_turnover_ratio'] = df['turnover_rate'] / df['log(circ_mv)']\n", + " df['mv_turnover_ratio'] = df['turnover_rate'] / df['log_circ_mv']\n", "\n", - " df['mv_adjusted_volume'] = df['vol'] / df['log(circ_mv)']\n", + " df['mv_adjusted_volume'] = df['vol'] / df['log_circ_mv']\n", "\n", - " df['mv_weighted_turnover'] = df['turnover_rate'] * (1 / df['log(circ_mv)'])\n", + " df['mv_weighted_turnover'] = df['turnover_rate'] * (1 / df['log_circ_mv'])\n", "\n", - " df['nonlinear_mv_volume'] = df['vol'] / df['log(circ_mv)']\n", + " df['nonlinear_mv_volume'] = df['vol'] / df['log_circ_mv']\n", "\n", - " df['mv_volume_ratio'] = df['volume_ratio'] / df['log(circ_mv)']\n", + " df['mv_volume_ratio'] = df['volume_ratio'] / df['log_circ_mv']\n", "\n", - " df['mv_momentum'] = df['turnover_rate'] * df['volume_ratio'] / df['log(circ_mv)']\n", + " df['mv_momentum'] = df['turnover_rate'] * df['volume_ratio'] / df['log_circ_mv']\n", "\n", " drop_columns = [col for col in df.columns if col.startswith('_')]\n", " df.drop(columns=drop_columns, inplace=True, errors='ignore')\n", "\n", " new_columns = [col for col in df.columns.tolist()[:] if col not in old_columns]\n", " return df, new_columns\n" - ], - "outputs": [], - "execution_count": 5 - }, - { - "cell_type": "code", - "id": "53f86ddc0677a6d7", - "metadata": { - "scrolled": true, - "ExecuteTime": { - "end_time": "2025-04-10T13:33:00.552850Z", - "start_time": "2025-04-10T13:32:55.363645Z" - } - }, - "source": [ - "from 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", - " 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" - ], - "outputs": [], - "execution_count": 6 - }, - { - "cell_type": "code", - "id": "dbe2fd8021b9417f", - "metadata": { - "ExecuteTime": { - "end_time": "2025-04-10T13:33:00.573680Z", - "start_time": "2025-04-10T13:33:00.569106Z" - } - }, - "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)" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "['ts_code', 'open', 'close', 'high', 'low', 'circ_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" - ] - } - ], - "execution_count": 7 + ] }, { "cell_type": "code", + "execution_count": 120, "id": "85c3e3d0235ffffa", "metadata": { "ExecuteTime": { @@ -753,6 +860,40 @@ "start_time": "2025-04-10T13:33:00.592221Z" } }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Index(['ts_code', 'trade_date', 'open', 'close', 'high', 'low', 'vol',\n", + " 'pct_chg', 'turnover_rate', 'pe_ttm', 'circ_mv', 'volume_ratio',\n", + " 'is_st', 'up_limit', 'down_limit', 'buy_sm_vol', 'sell_sm_vol',\n", + " 'buy_lg_vol', 'sell_lg_vol', 'buy_elg_vol', 'sell_elg_vol',\n", + " 'net_mf_vol', 'his_low', 'his_high', 'cost_5pct', 'cost_15pct',\n", + " 'cost_50pct', 'cost_85pct', 'cost_95pct', 'weight_avg', 'winner_rate',\n", + " 'l2_code', '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", + "\n", + "Index: 3148690 entries, 0 to 3148689\n", + "Columns: 136 entries, ts_code to mv_momentum\n", + "dtypes: bool(12), datetime64[ns](1), float64(117), int32(3), int64(1), object(2)\n", + "memory usage: 2.9+ GB\n", + "None\n" + ] + } + ], "source": [ "def filter_data(df):\n", " # df = df.groupby('trade_date').apply(lambda x: x.nlargest(1000, 'act_factor1'))\n", @@ -783,49 +924,22 @@ "# df = df.merge(index_data, on='trade_date', how='left')\n", "\n", "print(df.info())" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Index(['ts_code', 'trade_date', 'open', 'close', 'high', 'low', 'vol',\n", - " 'pct_chg', 'turnover_rate', 'pe_ttm', 'circ_mv', 'volume_ratio',\n", - " 'is_st', 'up_limit', 'down_limit', 'buy_sm_vol', 'sell_sm_vol',\n", - " 'buy_lg_vol', 'sell_lg_vol', 'buy_elg_vol', 'sell_elg_vol',\n", - " 'net_mf_vol', 'his_low', 'his_high', 'cost_5pct', 'cost_15pct',\n", - " 'cost_50pct', 'cost_85pct', 'cost_95pct', 'weight_avg', 'winner_rate',\n", - " 'l2_code', '_is_positive', '_is_negative', 'cat_is_positive',\n", - " '_pos_returns', '_neg_returns', '_pos_returns_sq', '_neg_returns_sq',\n", - " 'upside_vol', 'downside_vol', 'vol_ratio', 'return_skew',\n", - " 'return_kurtosis', 'volume_change_rate', 'cat_volume_breakout',\n", - " 'turnover_deviation', 'cat_turnover_spike', 'avg_volume_ratio',\n", - " 'cat_volume_ratio_breakout', 'vol_spike', 'vol_std_5', 'atr_14',\n", - " 'atr_6', 'obv'],\n", - " dtype='object')\n", - "\n", - "Index: 3148690 entries, 0 to 3148689\n", - "Columns: 119 entries, ts_code to mv_momentum\n", - "dtypes: bool(12), datetime64[ns](1), float64(101), int32(2), int64(1), object(2)\n", - "memory usage: 2.5+ GB\n", - "None\n" - ] - } - ], - "execution_count": 8 + ] }, { "cell_type": "code", + "execution_count": 121, "id": "f4f16d63ad18d1bc", "metadata": { - "jupyter": { - "source_hidden": true - }, "ExecuteTime": { "end_time": "2025-04-10T13:34:15.976910Z", "start_time": "2025-04-10T13:34:15.967986Z" + }, + "jupyter": { + "source_hidden": true } }, + "outputs": [], "source": [ "def create_deviation_within_dates(df, feature_columns):\n", " groupby_col = 'cat_l2_code' # 使用 trade_date 进行分组\n", @@ -862,12 +976,11 @@ " # df[f'deviation_industry_{feature}'] = df[feature] - df[f'industry_{feature}']\n", "\n", " return df, ret_feature_columns\n" - ], - "outputs": [], - "execution_count": 9 + ] }, { "cell_type": "code", + "execution_count": 122, "id": "40e6b68a91b30c79", "metadata": { "ExecuteTime": { @@ -875,6 +988,7 @@ "start_time": "2025-04-10T14:45:38.545898Z" } }, + "outputs": [], "source": [ "import pandas as pd\n", "\n", @@ -1126,12 +1240,11 @@ " # 应用 clip\n", " df[col] = np.clip(df[col], lower_bound, upper_bound)\n", " return df" - ], - "outputs": [], - "execution_count": 120 + ] }, { "cell_type": "code", + "execution_count": 123, "id": "1c46817a-b5dd-4bec-8bb4-e6e80bfd9d66", "metadata": { "ExecuteTime": { @@ -1139,24 +1252,25 @@ "start_time": "2025-04-10T14:38:09.381447Z" } }, + "outputs": [], "source": [ "# print(test_data.head()[['act_factor1', 'act_factor2', 'ts_code', 'trade_date']])" - ], - "outputs": [], - "execution_count": 105 + ] }, { "cell_type": "code", + "execution_count": 124, "id": "da2bb202843d9275", "metadata": { - "jupyter": { - "source_hidden": true - }, "ExecuteTime": { "end_time": "2025-04-10T14:38:09.436214Z", "start_time": "2025-04-10T14:38:09.426468Z" + }, + "jupyter": { + "source_hidden": true } }, + "outputs": [], "source": [ "from sklearn.preprocessing import StandardScaler\n", "import lightgbm as lgb\n", @@ -1251,12 +1365,11 @@ " plt.show()\n", "\n", " return model, scaler, None" - ], - "outputs": [], - "execution_count": 106 + ] }, { "cell_type": "code", + "execution_count": 125, "id": "ff19e3f1e051a489", "metadata": { "ExecuteTime": { @@ -1264,9 +1377,10 @@ "start_time": "2025-04-10T14:48:29.721868Z" } }, + "outputs": [], "source": [ "\n", - "days = 2\n", + "days = 5\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", @@ -1287,12 +1401,11 @@ "# 0.7 * df['future_return']\n", "# * 0.3 * df['future_volatility']\n", "# )" - ], - "outputs": [], - "execution_count": 128 + ] }, { "cell_type": "code", + "execution_count": 126, "id": "27dba27b2e108316", "metadata": { "ExecuteTime": { @@ -1300,23 +1413,20 @@ "start_time": "2025-04-10T14:50:15.128276Z" } }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2025-04-09 00:00:00\n" + ] + } + ], "source": [ "def select_pre_zt_stocks_dynamic(stock_df):\n", " def select_stocks(group):\n", - " max_stocks = 300\n", - " initial_data = group.nlargest(300, 'return_5')\n", - " unique_labels = initial_data['label'].nunique()\n", - "\n", - " if unique_labels >= 20 or unique_labels == 0: # 包含标签种类为0的情况\n", - " return initial_data\n", - "\n", - " for i in range(110, max_stocks + 1, 10):\n", - " data = group.nlargest(i, 'return_5')\n", - " unique_labels = data['label'].nunique()\n", - " if unique_labels >= 20:\n", - " return data\n", - "\n", - " return group.nlargest(max_stocks, 'return_20') # 如果循环结束仍未找到足够标签,则返回最大数量的股票\n", + " max_stocks = 500\n", + " return group.nsmallest(max_stocks, 'log_circ_mv')\n", "\n", " stock_df = stock_df.groupby('trade_date', group_keys=False).apply(select_stocks)\n", " return stock_df\n", @@ -1328,20 +1438,11 @@ "# pdf['label'] = pdf.groupby('trade_date', group_keys=False)['future_score'].transform(\n", "# lambda x: pd.qcut(x, q=20, labels=False, duplicates='drop')\n", "# )" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2025-04-09 00:00:00\n" - ] - } - ], - "execution_count": 129 + ] }, { "cell_type": "code", + "execution_count": 127, "id": "ca96fb81e17c4a90", "metadata": { "ExecuteTime": { @@ -1349,6 +1450,15 @@ "start_time": "2025-04-10T14:50:24.111269Z" } }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['vol', 'pct_chg', 'turnover_rate', 'volume_ratio', 'winner_rate', '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', 'log_circ_mv', 'cov', 'delta_cov', 'alpha_22_improved', 'alpha_003', 'alpha_007', 'alpha_013', 'cat_up_limit', 'cat_down_limit', 'up_limit_count_10d', 'down_limit_count_10d', 'consecutive_up_limit', 'vol_break', 'weight_roc5', 'smallcap_concentration', 'cost_stability', 'high_cost_break_days', 'liquidity_risk', 'turnover_std', 'volume_growth', 'mv_growth', 'arbr', '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', 'industry_obv', 'industry_return_5', 'industry_return_20', 'industry__ema_5', 'industry__ema_13', 'industry__ema_20', 'industry__ema_60', 'industry_act_factor1', 'industry_act_factor2', 'industry_act_factor3', 'industry_act_factor4', 'industry_act_factor5', 'industry_act_factor6', 'industry_rank_act_factor1', 'industry_rank_act_factor2', 'industry_rank_act_factor3', 'industry_return_5_percentile', 'industry_return_20_percentile']\n" + ] + } + ], "source": [ "pdf = pdf.merge(industry_df, on=['cat_l2_code', 'trade_date'], how='left')\n", "pdf = pdf.replace([np.inf, -np.inf], np.nan)\n", @@ -1375,20 +1485,11 @@ "\n", "# filter_index = pdf['future_volatility'].between(pdf['future_volatility'].quantile(0.01),\n", "# pdf['future_volatility'].quantile(0.99)) | filter_index" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "['vol', 'pct_chg', 'turnover_rate', 'volume_ratio', 'winner_rate', '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', 'log(circ_mv)', 'cov', 'delta_cov', 'alpha_22_improved', 'alpha_003', 'alpha_007', 'alpha_013', 'cat_up_limit', 'cat_down_limit', 'up_limit_count_10d', 'down_limit_count_10d', 'consecutive_up_limit', 'vol_break', 'weight_roc5', 'smallcap_concentration', 'cost_stability', 'high_cost_break_days', 'liquidity_risk', 'turnover_std', 'volume_growth', 'mv_growth', 'arbr', 'momentum_factor', 'resonance_factor', 'log_close', 'cat_vol_spike', 'up', 'down', 'obv-maobv_6', 'std_return_5 / std_return_90', 'std_return_90 - 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', 'industry_obv', 'industry_return_5', 'industry_return_20', 'industry__ema_5', 'industry__ema_13', 'industry__ema_20', 'industry__ema_60', 'industry_act_factor1', 'industry_act_factor2', 'industry_act_factor3', 'industry_act_factor4', 'industry_act_factor5', 'industry_act_factor6', 'industry_rank_act_factor1', 'industry_rank_act_factor2', 'industry_rank_act_factor3', 'industry_return_5_percentile', 'industry_return_20_percentile']\n" - ] - } - ], - "execution_count": 130 + ] }, { "cell_type": "code", + "execution_count": 128, "id": "81d4570663ae21d7", "metadata": { "ExecuteTime": { @@ -1396,6 +1497,7 @@ "start_time": "2025-04-10T14:50:25.514971Z" } }, + "outputs": [], "source": [ "\n", "# pdf = time_series_quantile_filter(pdf, numeric_columns)\n", @@ -1405,12 +1507,11 @@ "pdf = pdf.sort_values(by=['ts_code', 'trade_date'])\n", "filter_index = pdf['future_return'].between(pdf['future_return'].quantile(0.01),\n", " pdf['future_return'].quantile(0.99))" - ], - "outputs": [], - "execution_count": 131 + ] }, { "cell_type": "code", + "execution_count": 129, "id": "92428d543f4727ad", "metadata": { "ExecuteTime": { @@ -1418,6 +1519,18 @@ "start_time": "2025-04-10T14:52:09.941361Z" } }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 129, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# print('train data size: ', len(train_data))\n", "\n", @@ -1450,23 +1563,11 @@ "evals = {}\n", "\n", "gc.collect()" - ], - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 132, - "metadata": {}, - "output_type": "execute_result" - } - ], - "execution_count": 132 + ] }, { "cell_type": "code", + "execution_count": 142, "id": "8f134d435f71e9e2", "metadata": { "ExecuteTime": { @@ -1474,6 +1575,7 @@ "start_time": "2025-04-10T15:08:28.217183Z" } }, + "outputs": [], "source": [ "gc.collect()\n", "\n", @@ -1493,37 +1595,29 @@ "\n", " for start in range(start_index, n - train_days - test_days + 1, test_days):\n", "\n", - " # train_dates = unique_dates[start: start + train_days]\n", " train_dates = unique_dates[max(0, start - days + 1): start + train_days - days + 1]\n", " test_dates = unique_dates[start + train_days: start + train_days + test_days]\n", "\n", - " # 根据日期筛选数据\n", - " # train_data = df[df['trade_date'].isin(train_dates)]\n", + " # # 根据日期筛选数据\n", + " # # train_data = df[df['trade_date'].isin(train_dates)]\n", " train_data = df[filter_index & df['trade_date'].isin(train_dates)]\n", " test_data = df[df['trade_date'].isin(test_dates)]\n", "\n", " train_data = train_data.sort_values('trade_date')\n", " test_data = test_data.sort_values('trade_date')\n", "\n", - " feature_columns, _ = remove_shifted_features(train_data, feature_columns_origin, size=0.8, log=False,\n", - " val_data=df[filter_index & (df['trade_date'] ==\n", - " unique_dates[start + train_days - days + 1])])\n", + " # feature_columns, _ = remove_shifted_features(train_data, feature_columns_origin, size=0.8, log=False,\n", + " # val_data=df[filter_index & (df['trade_date'] ==\n", + " # unique_dates[start + train_days - days + 1])])\n", "\n", " train_data = train_data.dropna(subset=feature_columns)\n", " train_data = train_data.dropna(subset=['label'])\n", " train_data = train_data.reset_index(drop=True)\n", "\n", - " # print(test_data.tail())\n", " test_data = test_data.dropna(subset=feature_columns)\n", - " # test_data = test_data.dropna(subset=['label'])\n", " test_data = test_data.reset_index(drop=True)\n", - "\n", - " # print(len(train_data))\n", - " # print(f\"最小日期: {train_data['trade_date'].min().strftime('%Y-%m-%d')}\")\n", " print(\n", " f\"train_data最大日期: {train_data['trade_date'].max().strftime('%Y-%m-%d')}, 训练天数{train_data['trade_date'].nunique()}\")\n", - " # # print(len(test_data))\n", - " # print(f\"最小日期: {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 df.columns if col.startswith('cat')]\n", @@ -1535,6 +1629,7 @@ " label_gain = [(gain + 1) * (gain + 1) for gain in label_gain]\n", " params['label_gain'] = label_gain\n", "\n", + " ##############################################\n", " # ud = train_data[\"trade_date\"].unique()\n", " # date_weights = {date: weight for date, weight in zip(ud, np.linspace(1, 2, len(unique_dates)))}\n", " # params['weight'] = train_data[\"trade_date\"].map(date_weights).tolist()\n", @@ -1556,32 +1651,1738 @@ "\n", " score_df = test_data.copy()\n", " score_df['score'] = model.predict(score_df[feature_columns])\n", - " score_df = score_df.loc[score_df.groupby('trade_date')['score'].idxmax()]\n", - " score_df = score_df[['trade_date', 'score', 'ts_code']]\n", - " predictions_list.append(score_df)\n", + " # score_df = score_df.loc[score_df.groupby('trade_date')['score'].idxmax()]\n", + " # score_df = score_df[['trade_date', 'score', 'ts_code']]\n", + " # predictions_list.append(score_df)\n", + " # score_df['score'] = score_df['log_circ_mv']\n", + " top_stock_ = score_df.nlargest(1, columns='score').reset_index(level=0)\n", + " final_selection = top_stock_[['trade_date', 'score', 'ts_code']]\n", + " predictions_list.append(final_selection)\n", "\n", " final_predictions = pd.concat(predictions_list, ignore_index=True)\n", " return final_predictions\n", "\n" - ], - "outputs": [], - "execution_count": 147 + ] }, { "cell_type": "code", + "execution_count": 143, "id": "63235069-dc59-48fb-961a-e80373e41a61", "metadata": { + "ExecuteTime": { + "end_time": "2025-04-10T15:15:47.062878Z", + "start_time": "2025-04-10T15:11:48.632695Z" + }, "editable": true, "scrolled": true, "slideshow": { "slide_type": "" }, - "tags": [], - "ExecuteTime": { - "end_time": "2025-04-10T15:15:47.062878Z", - "start_time": "2025-04-10T15:11:48.632695Z" - } + "tags": [] }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "finish\n", + "train_data最大日期: 2022-12-01, 训练天数1\n", + "test_data最大日期: 2022-12-08\n", + "划分后的训练集大小: 451, 验证集大小: 451\n", + "train_data最大日期: 2022-12-02, 训练天数2\n", + "test_data最大日期: 2022-12-09\n", + "划分后的训练集大小: 901, 验证集大小: 450\n", + "train_data最大日期: 2022-12-05, 训练天数3\n", + "test_data最大日期: 2022-12-12\n", + "划分后的训练集大小: 1348, 验证集大小: 447\n", + "train_data最大日期: 2022-12-06, 训练天数4\n", + "test_data最大日期: 2022-12-13\n", + "划分后的训练集大小: 1798, 验证集大小: 450\n", + "train_data最大日期: 2022-12-07, 训练天数5\n", + "test_data最大日期: 2022-12-14\n", + "划分后的训练集大小: 2246, 验证集大小: 448\n", + "train_data最大日期: 2022-12-08, 训练天数5\n", + "test_data最大日期: 2022-12-15\n", + "划分后的训练集大小: 2241, 验证集大小: 446\n", + "train_data最大日期: 2022-12-09, 训练天数5\n", + "test_data最大日期: 2022-12-16\n", + "划分后的训练集大小: 2241, 验证集大小: 450\n", + "train_data最大日期: 2022-12-12, 训练天数5\n", + "test_data最大日期: 2022-12-19\n", + "划分后的训练集大小: 2243, 验证集大小: 449\n", + "train_data最大日期: 2022-12-13, 训练天数5\n", + "test_data最大日期: 2022-12-20\n", + "划分后的训练集大小: 2244, 验证集大小: 451\n", + "train_data最大日期: 2022-12-14, 训练天数5\n", + "test_data最大日期: 2022-12-21\n", + "划分后的训练集大小: 2245, 验证集大小: 449\n", + "train_data最大日期: 2022-12-15, 训练天数5\n", + "test_data最大日期: 2022-12-22\n", + "划分后的训练集大小: 2247, 验证集大小: 448\n", + "train_data最大日期: 2022-12-16, 训练天数5\n", + "test_data最大日期: 2022-12-23\n", + "划分后的训练集大小: 2247, 验证集大小: 450\n", + "train_data最大日期: 2022-12-19, 训练天数5\n", + "test_data最大日期: 2022-12-26\n", + "划分后的训练集大小: 2248, 验证集大小: 450\n", + "train_data最大日期: 2022-12-20, 训练天数5\n", + "test_data最大日期: 2022-12-27\n", + "划分后的训练集大小: 2247, 验证集大小: 450\n", + "train_data最大日期: 2022-12-21, 训练天数5\n", + "test_data最大日期: 2022-12-28\n", + "划分后的训练集大小: 2246, 验证集大小: 448\n", + "train_data最大日期: 2022-12-22, 训练天数5\n", + "test_data最大日期: 2022-12-29\n", + "划分后的训练集大小: 2249, 验证集大小: 451\n", + "train_data最大日期: 2022-12-23, 训练天数5\n", + "test_data最大日期: 2022-12-30\n", + "划分后的训练集大小: 2251, 验证集大小: 452\n", + "train_data最大日期: 2022-12-26, 训练天数5\n", + "test_data最大日期: 2023-01-03\n", + "划分后的训练集大小: 2254, 验证集大小: 453\n", + "train_data最大日期: 2022-12-27, 训练天数5\n", + "test_data最大日期: 2023-01-04\n", + "划分后的训练集大小: 2259, 验证集大小: 455\n", + "train_data最大日期: 2022-12-28, 训练天数5\n", + "test_data最大日期: 2023-01-05\n", + "划分后的训练集大小: 2264, 验证集大小: 453\n", + "train_data最大日期: 2022-12-29, 训练天数5\n", + "test_data最大日期: 2023-01-06\n", + "划分后的训练集大小: 2267, 验证集大小: 454\n", + "train_data最大日期: 2022-12-30, 训练天数5\n", + "test_data最大日期: 2023-01-09\n", + "划分后的训练集大小: 2270, 验证集大小: 455\n", + "train_data最大日期: 2023-01-03, 训练天数5\n", + "test_data最大日期: 2023-01-10\n", + "划分后的训练集大小: 2271, 验证集大小: 454\n", + "train_data最大日期: 2023-01-04, 训练天数5\n", + "test_data最大日期: 2023-01-11\n", + "划分后的训练集大小: 2268, 验证集大小: 452\n", + "train_data最大日期: 2023-01-05, 训练天数5\n", + "test_data最大日期: 2023-01-12\n", + "划分后的训练集大小: 2270, 验证集大小: 455\n", + "train_data最大日期: 2023-01-06, 训练天数5\n", + "test_data最大日期: 2023-01-13\n", + "划分后的训练集大小: 2272, 验证集大小: 456\n", + "train_data最大日期: 2023-01-09, 训练天数5\n", + "test_data最大日期: 2023-01-16\n", + "划分后的训练集大小: 2274, 验证集大小: 457\n", + "train_data最大日期: 2023-01-10, 训练天数5\n", + "test_data最大日期: 2023-01-17\n", + "划分后的训练集大小: 2274, 验证集大小: 454\n", + "train_data最大日期: 2023-01-11, 训练天数5\n", + "test_data最大日期: 2023-01-18\n", + "划分后的训练集大小: 2277, 验证集大小: 455\n", + "train_data最大日期: 2023-01-12, 训练天数5\n", + "test_data最大日期: 2023-01-19\n", + "划分后的训练集大小: 2279, 验证集大小: 457\n", + "train_data最大日期: 2023-01-13, 训练天数5\n", + "test_data最大日期: 2023-01-20\n", + "划分后的训练集大小: 2279, 验证集大小: 456\n", + "train_data最大日期: 2023-01-16, 训练天数5\n", + "test_data最大日期: 2023-01-30\n", + "划分后的训练集大小: 2277, 验证集大小: 455\n", + "train_data最大日期: 2023-01-17, 训练天数5\n", + "test_data最大日期: 2023-01-31\n", + "划分后的训练集大小: 2277, 验证集大小: 454\n", + "train_data最大日期: 2023-01-18, 训练天数5\n", + "test_data最大日期: 2023-02-01\n", + "划分后的训练集大小: 2275, 验证集大小: 453\n", + "train_data最大日期: 2023-01-19, 训练天数5\n", + "test_data最大日期: 2023-02-02\n", + "划分后的训练集大小: 2268, 验证集大小: 450\n", + "train_data最大日期: 2023-01-20, 训练天数5\n", + "test_data最大日期: 2023-02-03\n", + "划分后的训练集大小: 2268, 验证集大小: 456\n", + "train_data最大日期: 2023-01-30, 训练天数5\n", + "test_data最大日期: 2023-02-06\n", + "划分后的训练集大小: 2266, 验证集大小: 453\n", + "train_data最大日期: 2023-01-31, 训练天数5\n", + "test_data最大日期: 2023-02-07\n", + "划分后的训练集大小: 2263, 验证集大小: 451\n", + "train_data最大日期: 2023-02-01, 训练天数5\n", + "test_data最大日期: 2023-02-08\n", + "划分后的训练集大小: 2265, 验证集大小: 455\n", + "train_data最大日期: 2023-02-02, 训练天数5\n", + "test_data最大日期: 2023-02-09\n", + "划分后的训练集大小: 2269, 验证集大小: 454\n", + "train_data最大日期: 2023-02-03, 训练天数5\n", + "test_data最大日期: 2023-02-10\n", + "划分后的训练集大小: 2275, 验证集大小: 462\n", + "train_data最大日期: 2023-02-06, 训练天数5\n", + "test_data最大日期: 2023-02-13\n", + "划分后的训练集大小: 2284, 验证集大小: 462\n", + "train_data最大日期: 2023-02-07, 训练天数5\n", + "test_data最大日期: 2023-02-14\n", + "划分后的训练集大小: 2294, 验证集大小: 461\n", + "train_data最大日期: 2023-02-08, 训练天数5\n", + "test_data最大日期: 2023-02-15\n", + "划分后的训练集大小: 2300, 验证集大小: 461\n", + "train_data最大日期: 2023-02-09, 训练天数5\n", + "test_data最大日期: 2023-02-16\n", + "划分后的训练集大小: 2308, 验证集大小: 462\n", + "train_data最大日期: 2023-02-10, 训练天数5\n", + "test_data最大日期: 2023-02-17\n", + "划分后的训练集大小: 2307, 验证集大小: 461\n", + "train_data最大日期: 2023-02-13, 训练天数5\n", + "test_data最大日期: 2023-02-20\n", + "划分后的训练集大小: 2309, 验证集大小: 464\n", + "train_data最大日期: 2023-02-14, 训练天数5\n", + "test_data最大日期: 2023-02-21\n", + "划分后的训练集大小: 2312, 验证集大小: 464\n", + "train_data最大日期: 2023-02-15, 训练天数5\n", + "test_data最大日期: 2023-02-22\n", + "划分后的训练集大小: 2314, 验证集大小: 463\n", + "train_data最大日期: 2023-02-16, 训练天数5\n", + "test_data最大日期: 2023-02-23\n", + "划分后的训练集大小: 2311, 验证集大小: 459\n", + "train_data最大日期: 2023-02-17, 训练天数5\n", + "test_data最大日期: 2023-02-24\n", + "划分后的训练集大小: 2307, 验证集大小: 457\n", + "train_data最大日期: 2023-02-20, 训练天数5\n", + "test_data最大日期: 2023-02-27\n", + "划分后的训练集大小: 2301, 验证集大小: 458\n", + "train_data最大日期: 2023-02-21, 训练天数5\n", + "test_data最大日期: 2023-02-28\n", + "划分后的训练集大小: 2298, 验证集大小: 461\n", + "train_data最大日期: 2023-02-22, 训练天数5\n", + "test_data最大日期: 2023-03-01\n", + "划分后的训练集大小: 2294, 验证集大小: 459\n", + "train_data最大日期: 2023-02-23, 训练天数5\n", + "test_data最大日期: 2023-03-02\n", + "划分后的训练集大小: 2292, 验证集大小: 457\n", + "train_data最大日期: 2023-02-24, 训练天数5\n", + "test_data最大日期: 2023-03-03\n", + "划分后的训练集大小: 2292, 验证集大小: 457\n", + "train_data最大日期: 2023-02-27, 训练天数5\n", + "test_data最大日期: 2023-03-06\n", + "划分后的训练集大小: 2294, 验证集大小: 460\n", + "train_data最大日期: 2023-02-28, 训练天数5\n", + "test_data最大日期: 2023-03-07\n", + "划分后的训练集大小: 2293, 验证集大小: 460\n", + "train_data最大日期: 2023-03-01, 训练天数5\n", + "test_data最大日期: 2023-03-08\n", + "划分后的训练集大小: 2292, 验证集大小: 458\n", + "train_data最大日期: 2023-03-02, 训练天数5\n", + "test_data最大日期: 2023-03-09\n", + "划分后的训练集大小: 2291, 验证集大小: 456\n", + "train_data最大日期: 2023-03-03, 训练天数5\n", + "test_data最大日期: 2023-03-10\n", + "划分后的训练集大小: 2292, 验证集大小: 458\n", + "train_data最大日期: 2023-03-06, 训练天数5\n", + "test_data最大日期: 2023-03-13\n", + "划分后的训练集大小: 2290, 验证集大小: 458\n", + "train_data最大日期: 2023-03-07, 训练天数5\n", + "test_data最大日期: 2023-03-14\n", + "划分后的训练集大小: 2287, 验证集大小: 457\n", + "train_data最大日期: 2023-03-08, 训练天数5\n", + "test_data最大日期: 2023-03-15\n", + "划分后的训练集大小: 2284, 验证集大小: 455\n", + "train_data最大日期: 2023-03-09, 训练天数5\n", + "test_data最大日期: 2023-03-16\n", + "划分后的训练集大小: 2287, 验证集大小: 459\n", + "train_data最大日期: 2023-03-10, 训练天数5\n", + "test_data最大日期: 2023-03-17\n", + "划分后的训练集大小: 2286, 验证集大小: 457\n", + "train_data最大日期: 2023-03-13, 训练天数5\n", + "test_data最大日期: 2023-03-20\n", + "划分后的训练集大小: 2286, 验证集大小: 458\n", + "train_data最大日期: 2023-03-14, 训练天数5\n", + "test_data最大日期: 2023-03-21\n", + "划分后的训练集大小: 2288, 验证集大小: 459\n", + "train_data最大日期: 2023-03-15, 训练天数5\n", + "test_data最大日期: 2023-03-22\n", + "划分后的训练集大小: 2289, 验证集大小: 456\n", + "train_data最大日期: 2023-03-16, 训练天数5\n", + "test_data最大日期: 2023-03-23\n", + "划分后的训练集大小: 2286, 验证集大小: 456\n", + "train_data最大日期: 2023-03-17, 训练天数5\n", + "test_data最大日期: 2023-03-24\n", + "划分后的训练集大小: 2287, 验证集大小: 458\n", + "train_data最大日期: 2023-03-20, 训练天数5\n", + "test_data最大日期: 2023-03-27\n", + "划分后的训练集大小: 2288, 验证集大小: 459\n", + "train_data最大日期: 2023-03-21, 训练天数5\n", + "test_data最大日期: 2023-03-28\n", + "划分后的训练集大小: 2285, 验证集大小: 456\n", + "train_data最大日期: 2023-03-22, 训练天数5\n", + "test_data最大日期: 2023-03-29\n", + "划分后的训练集大小: 2289, 验证集大小: 460\n", + "train_data最大日期: 2023-03-23, 训练天数5\n", + "test_data最大日期: 2023-03-30\n", + "划分后的训练集大小: 2293, 验证集大小: 460\n", + "train_data最大日期: 2023-03-24, 训练天数5\n", + "test_data最大日期: 2023-03-31\n", + "划分后的训练集大小: 2295, 验证集大小: 460\n", + "train_data最大日期: 2023-03-27, 训练天数5\n", + "test_data最大日期: 2023-04-03\n", + "划分后的训练集大小: 2294, 验证集大小: 458\n", + "train_data最大日期: 2023-03-28, 训练天数5\n", + "test_data最大日期: 2023-04-04\n", + "划分后的训练集大小: 2297, 验证集大小: 459\n", + "train_data最大日期: 2023-03-29, 训练天数5\n", + "test_data最大日期: 2023-04-06\n", + "划分后的训练集大小: 2294, 验证集大小: 457\n", + "train_data最大日期: 2023-03-30, 训练天数5\n", + "test_data最大日期: 2023-04-07\n", + "划分后的训练集大小: 2294, 验证集大小: 460\n", + "train_data最大日期: 2023-03-31, 训练天数5\n", + "test_data最大日期: 2023-04-10\n", + "划分后的训练集大小: 2294, 验证集大小: 460\n", + "train_data最大日期: 2023-04-03, 训练天数5\n", + "test_data最大日期: 2023-04-11\n", + "划分后的训练集大小: 2296, 验证集大小: 460\n", + "train_data最大日期: 2023-04-04, 训练天数5\n", + "test_data最大日期: 2023-04-12\n", + "划分后的训练集大小: 2296, 验证集大小: 459\n", + "train_data最大日期: 2023-04-06, 训练天数5\n", + "test_data最大日期: 2023-04-13\n", + "划分后的训练集大小: 2301, 验证集大小: 462\n", + "train_data最大日期: 2023-04-07, 训练天数5\n", + "test_data最大日期: 2023-04-14\n", + "划分后的训练集大小: 2303, 验证集大小: 462\n", + "train_data最大日期: 2023-04-10, 训练天数5\n", + "test_data最大日期: 2023-04-17\n", + "划分后的训练集大小: 2302, 验证集大小: 459\n", + "train_data最大日期: 2023-04-11, 训练天数5\n", + "test_data最大日期: 2023-04-18\n", + "划分后的训练集大小: 2297, 验证集大小: 455\n", + "train_data最大日期: 2023-04-12, 训练天数5\n", + "test_data最大日期: 2023-04-19\n", + "划分后的训练集大小: 2294, 验证集大小: 456\n", + "train_data最大日期: 2023-04-13, 训练天数5\n", + "test_data最大日期: 2023-04-20\n", + "划分后的训练集大小: 2287, 验证集大小: 455\n", + "train_data最大日期: 2023-04-14, 训练天数5\n", + "test_data最大日期: 2023-04-21\n", + "划分后的训练集大小: 2279, 验证集大小: 454\n", + "train_data最大日期: 2023-04-17, 训练天数5\n", + "test_data最大日期: 2023-04-24\n", + "划分后的训练集大小: 2275, 验证集大小: 455\n", + "train_data最大日期: 2023-04-18, 训练天数5\n", + "test_data最大日期: 2023-04-25\n", + "划分后的训练集大小: 2273, 验证集大小: 453\n", + "train_data最大日期: 2023-04-19, 训练天数5\n", + "test_data最大日期: 2023-04-26\n", + "划分后的训练集大小: 2270, 验证集大小: 453\n", + "train_data最大日期: 2023-04-20, 训练天数5\n", + "test_data最大日期: 2023-04-27\n", + "划分后的训练集大小: 2263, 验证集大小: 448\n", + "train_data最大日期: 2023-04-21, 训练天数5\n", + "test_data最大日期: 2023-04-28\n", + "划分后的训练集大小: 2259, 验证集大小: 450\n", + "train_data最大日期: 2023-04-24, 训练天数5\n", + "test_data最大日期: 2023-05-04\n", + "划分后的训练集大小: 2252, 验证集大小: 448\n", + "train_data最大日期: 2023-04-25, 训练天数5\n", + "test_data最大日期: 2023-05-05\n", + "划分后的训练集大小: 2247, 验证集大小: 448\n", + "train_data最大日期: 2023-04-26, 训练天数5\n", + "test_data最大日期: 2023-05-08\n", + "划分后的训练集大小: 2241, 验证集大小: 447\n", + "train_data最大日期: 2023-04-27, 训练天数5\n", + "test_data最大日期: 2023-05-09\n", + "划分后的训练集大小: 2244, 验证集大小: 451\n", + "train_data最大日期: 2023-04-28, 训练天数5\n", + "test_data最大日期: 2023-05-10\n", + "划分后的训练集大小: 2248, 验证集大小: 454\n", + "train_data最大日期: 2023-05-04, 训练天数5\n", + "test_data最大日期: 2023-05-11\n", + "划分后的训练集大小: 2264, 验证集大小: 464\n", + "train_data最大日期: 2023-05-05, 训练天数5\n", + "test_data最大日期: 2023-05-12\n", + "划分后的训练集大小: 2280, 验证集大小: 464\n", + "train_data最大日期: 2023-05-08, 训练天数5\n", + "test_data最大日期: 2023-05-15\n", + "划分后的训练集大小: 2299, 验证集大小: 466\n", + "train_data最大日期: 2023-05-09, 训练天数5\n", + "test_data最大日期: 2023-05-16\n", + "划分后的训练集大小: 2314, 验证集大小: 466\n", + "train_data最大日期: 2023-05-10, 训练天数5\n", + "test_data最大日期: 2023-05-17\n", + "划分后的训练集大小: 2326, 验证集大小: 466\n", + "train_data最大日期: 2023-05-11, 训练天数5\n", + "test_data最大日期: 2023-05-18\n", + "划分后的训练集大小: 2328, 验证集大小: 466\n", + "train_data最大日期: 2023-05-12, 训练天数5\n", + "test_data最大日期: 2023-05-19\n", + "划分后的训练集大小: 2330, 验证集大小: 466\n", + "train_data最大日期: 2023-05-15, 训练天数5\n", + "test_data最大日期: 2023-05-22\n", + "划分后的训练集大小: 2331, 验证集大小: 467\n", + "train_data最大日期: 2023-05-16, 训练天数5\n", + "test_data最大日期: 2023-05-23\n", + "划分后的训练集大小: 2332, 验证集大小: 467\n", + "train_data最大日期: 2023-05-17, 训练天数5\n", + "test_data最大日期: 2023-05-24\n", + "划分后的训练集大小: 2332, 验证集大小: 466\n", + "train_data最大日期: 2023-05-18, 训练天数5\n", + "test_data最大日期: 2023-05-25\n", + "划分后的训练集大小: 2331, 验证集大小: 465\n", + "train_data最大日期: 2023-05-19, 训练天数5\n", + "test_data最大日期: 2023-05-26\n", + "划分后的训练集大小: 2330, 验证集大小: 465\n", + "train_data最大日期: 2023-05-22, 训练天数5\n", + "test_data最大日期: 2023-05-29\n", + "划分后的训练集大小: 2329, 验证集大小: 466\n", + "train_data最大日期: 2023-05-23, 训练天数5\n", + "test_data最大日期: 2023-05-30\n", + "划分后的训练集大小: 2327, 验证集大小: 465\n", + "train_data最大日期: 2023-05-24, 训练天数5\n", + "test_data最大日期: 2023-05-31\n", + "划分后的训练集大小: 2326, 验证集大小: 465\n", + "train_data最大日期: 2023-05-25, 训练天数5\n", + "test_data最大日期: 2023-06-01\n", + "划分后的训练集大小: 2323, 验证集大小: 462\n", + "train_data最大日期: 2023-05-26, 训练天数5\n", + "test_data最大日期: 2023-06-02\n", + "划分后的训练集大小: 2319, 验证集大小: 461\n", + "train_data最大日期: 2023-05-29, 训练天数5\n", + "test_data最大日期: 2023-06-05\n", + "划分后的训练集大小: 2314, 验证集大小: 461\n", + "train_data最大日期: 2023-05-30, 训练天数5\n", + "test_data最大日期: 2023-06-06\n", + "划分后的训练集大小: 2310, 验证集大小: 461\n", + "train_data最大日期: 2023-05-31, 训练天数5\n", + "test_data最大日期: 2023-06-07\n", + "划分后的训练集大小: 2302, 验证集大小: 457\n", + "train_data最大日期: 2023-06-01, 训练天数5\n", + "test_data最大日期: 2023-06-08\n", + "划分后的训练集大小: 2296, 验证集大小: 456\n", + "train_data最大日期: 2023-06-02, 训练天数5\n", + "test_data最大日期: 2023-06-09\n", + "划分后的训练集大小: 2293, 验证集大小: 458\n", + "train_data最大日期: 2023-06-05, 训练天数5\n", + "test_data最大日期: 2023-06-12\n", + "划分后的训练集大小: 2287, 验证集大小: 455\n", + "train_data最大日期: 2023-06-06, 训练天数5\n", + "test_data最大日期: 2023-06-13\n", + "划分后的训练集大小: 2285, 验证集大小: 459\n", + "train_data最大日期: 2023-06-07, 训练天数5\n", + "test_data最大日期: 2023-06-14\n", + "划分后的训练集大小: 2284, 验证集大小: 456\n", + "train_data最大日期: 2023-06-08, 训练天数5\n", + "test_data最大日期: 2023-06-15\n", + "划分后的训练集大小: 2287, 验证集大小: 459\n", + "train_data最大日期: 2023-06-09, 训练天数5\n", + "test_data最大日期: 2023-06-16\n", + "划分后的训练集大小: 2290, 验证集大小: 461\n", + "train_data最大日期: 2023-06-12, 训练天数5\n", + "test_data最大日期: 2023-06-19\n", + "划分后的训练集大小: 2294, 验证集大小: 459\n", + "train_data最大日期: 2023-06-13, 训练天数5\n", + "test_data最大日期: 2023-06-20\n", + "划分后的训练集大小: 2289, 验证集大小: 454\n", + "train_data最大日期: 2023-06-14, 训练天数5\n", + "test_data最大日期: 2023-06-21\n", + "划分后的训练集大小: 2286, 验证集大小: 453\n", + "train_data最大日期: 2023-06-15, 训练天数5\n", + "test_data最大日期: 2023-06-26\n", + "划分后的训练集大小: 2279, 验证集大小: 452\n", + "train_data最大日期: 2023-06-16, 训练天数5\n", + "test_data最大日期: 2023-06-27\n", + "划分后的训练集大小: 2269, 验证集大小: 451\n", + "train_data最大日期: 2023-06-19, 训练天数5\n", + "test_data最大日期: 2023-06-28\n", + "划分后的训练集大小: 2262, 验证集大小: 452\n", + "train_data最大日期: 2023-06-20, 训练天数5\n", + "test_data最大日期: 2023-06-29\n", + "划分后的训练集大小: 2260, 验证集大小: 452\n", + "train_data最大日期: 2023-06-21, 训练天数5\n", + "test_data最大日期: 2023-06-30\n", + "划分后的训练集大小: 2262, 验证集大小: 455\n", + "train_data最大日期: 2023-06-26, 训练天数5\n", + "test_data最大日期: 2023-07-03\n", + "划分后的训练集大小: 2266, 验证集大小: 456\n", + "train_data最大日期: 2023-06-27, 训练天数5\n", + "test_data最大日期: 2023-07-04\n", + "划分后的训练集大小: 2272, 验证集大小: 457\n", + "train_data最大日期: 2023-06-28, 训练天数5\n", + "test_data最大日期: 2023-07-05\n", + "划分后的训练集大小: 2276, 验证集大小: 456\n", + "train_data最大日期: 2023-06-29, 训练天数5\n", + "test_data最大日期: 2023-07-06\n", + "划分后的训练集大小: 2279, 验证集大小: 455\n", + "train_data最大日期: 2023-06-30, 训练天数5\n", + "test_data最大日期: 2023-07-07\n", + "划分后的训练集大小: 2277, 验证集大小: 453\n", + "train_data最大日期: 2023-07-03, 训练天数5\n", + "test_data最大日期: 2023-07-10\n", + "划分后的训练集大小: 2275, 验证集大小: 454\n", + "train_data最大日期: 2023-07-04, 训练天数5\n", + "test_data最大日期: 2023-07-11\n", + "划分后的训练集大小: 2271, 验证集大小: 453\n", + "train_data最大日期: 2023-07-05, 训练天数5\n", + "test_data最大日期: 2023-07-12\n", + "划分后的训练集大小: 2270, 验证集大小: 455\n", + "train_data最大日期: 2023-07-06, 训练天数5\n", + "test_data最大日期: 2023-07-13\n", + "划分后的训练集大小: 2274, 验证集大小: 459\n", + "train_data最大日期: 2023-07-07, 训练天数5\n", + "test_data最大日期: 2023-07-14\n", + "划分后的训练集大小: 2279, 验证集大小: 458\n", + "train_data最大日期: 2023-07-10, 训练天数5\n", + "test_data最大日期: 2023-07-17\n", + "划分后的训练集大小: 2284, 验证集大小: 459\n", + "train_data最大日期: 2023-07-11, 训练天数5\n", + "test_data最大日期: 2023-07-18\n", + "划分后的训练集大小: 2291, 验证集大小: 460\n", + "train_data最大日期: 2023-07-12, 训练天数5\n", + "test_data最大日期: 2023-07-19\n", + "划分后的训练集大小: 2299, 验证集大小: 463\n", + "train_data最大日期: 2023-07-13, 训练天数5\n", + "test_data最大日期: 2023-07-20\n", + "划分后的训练集大小: 2304, 验证集大小: 464\n", + "train_data最大日期: 2023-07-14, 训练天数5\n", + "test_data最大日期: 2023-07-21\n", + "划分后的训练集大小: 2311, 验证集大小: 465\n", + "train_data最大日期: 2023-07-17, 训练天数5\n", + "test_data最大日期: 2023-07-24\n", + "划分后的训练集大小: 2318, 验证集大小: 466\n", + "train_data最大日期: 2023-07-18, 训练天数5\n", + "test_data最大日期: 2023-07-25\n", + "划分后的训练集大小: 2320, 验证集大小: 462\n", + "train_data最大日期: 2023-07-19, 训练天数5\n", + "test_data最大日期: 2023-07-26\n", + "划分后的训练集大小: 2319, 验证集大小: 462\n", + "train_data最大日期: 2023-07-20, 训练天数5\n", + "test_data最大日期: 2023-07-27\n", + "划分后的训练集大小: 2321, 验证集大小: 466\n", + "train_data最大日期: 2023-07-21, 训练天数5\n", + "test_data最大日期: 2023-07-28\n", + "划分后的训练集大小: 2324, 验证集大小: 468\n", + "train_data最大日期: 2023-07-24, 训练天数5\n", + "test_data最大日期: 2023-07-31\n", + "划分后的训练集大小: 2324, 验证集大小: 466\n", + "train_data最大日期: 2023-07-25, 训练天数5\n", + "test_data最大日期: 2023-08-01\n", + "划分后的训练集大小: 2328, 验证集大小: 466\n", + "train_data最大日期: 2023-07-26, 训练天数5\n", + "test_data最大日期: 2023-08-02\n", + "划分后的训练集大小: 2333, 验证集大小: 467\n", + "train_data最大日期: 2023-07-27, 训练天数5\n", + "test_data最大日期: 2023-08-03\n", + "划分后的训练集大小: 2336, 验证集大小: 469\n", + "train_data最大日期: 2023-07-28, 训练天数5\n", + "test_data最大日期: 2023-08-04\n", + "划分后的训练集大小: 2336, 验证集大小: 468\n", + "train_data最大日期: 2023-07-31, 训练天数5\n", + "test_data最大日期: 2023-08-07\n", + "划分后的训练集大小: 2342, 验证集大小: 472\n", + "train_data最大日期: 2023-08-01, 训练天数5\n", + "test_data最大日期: 2023-08-08\n", + "划分后的训练集大小: 2345, 验证集大小: 469\n", + "train_data最大日期: 2023-08-02, 训练天数5\n", + "test_data最大日期: 2023-08-09\n", + "划分后的训练集大小: 2350, 验证集大小: 472\n", + "train_data最大日期: 2023-08-03, 训练天数5\n", + "test_data最大日期: 2023-08-10\n", + "划分后的训练集大小: 2354, 验证集大小: 473\n", + "train_data最大日期: 2023-08-04, 训练天数5\n", + "test_data最大日期: 2023-08-11\n", + "划分后的训练集大小: 2357, 验证集大小: 471\n", + "train_data最大日期: 2023-08-07, 训练天数5\n", + "test_data最大日期: 2023-08-14\n", + "划分后的训练集大小: 2356, 验证集大小: 471\n", + "train_data最大日期: 2023-08-08, 训练天数5\n", + "test_data最大日期: 2023-08-15\n", + "划分后的训练集大小: 2357, 验证集大小: 470\n", + "train_data最大日期: 2023-08-09, 训练天数5\n", + "test_data最大日期: 2023-08-16\n", + "划分后的训练集大小: 2359, 验证集大小: 474\n", + "train_data最大日期: 2023-08-10, 训练天数5\n", + "test_data最大日期: 2023-08-17\n", + "划分后的训练集大小: 2361, 验证集大小: 475\n", + "train_data最大日期: 2023-08-11, 训练天数5\n", + "test_data最大日期: 2023-08-18\n", + "划分后的训练集大小: 2365, 验证集大小: 475\n", + "train_data最大日期: 2023-08-14, 训练天数5\n", + "test_data最大日期: 2023-08-21\n", + "划分后的训练集大小: 2370, 验证集大小: 476\n", + "train_data最大日期: 2023-08-15, 训练天数5\n", + "test_data最大日期: 2023-08-22\n", + "划分后的训练集大小: 2374, 验证集大小: 474\n", + "train_data最大日期: 2023-08-16, 训练天数5\n", + "test_data最大日期: 2023-08-23\n", + "划分后的训练集大小: 2374, 验证集大小: 474\n", + "train_data最大日期: 2023-08-17, 训练天数5\n", + "test_data最大日期: 2023-08-24\n", + "划分后的训练集大小: 2372, 验证集大小: 473\n", + "train_data最大日期: 2023-08-18, 训练天数5\n", + "test_data最大日期: 2023-08-25\n", + "划分后的训练集大小: 2369, 验证集大小: 472\n", + "train_data最大日期: 2023-08-21, 训练天数5\n", + "test_data最大日期: 2023-08-28\n", + "划分后的训练集大小: 2367, 验证集大小: 474\n", + "train_data最大日期: 2023-08-22, 训练天数5\n", + "test_data最大日期: 2023-08-29\n", + "划分后的训练集大小: 2370, 验证集大小: 477\n", + "train_data最大日期: 2023-08-23, 训练天数5\n", + "test_data最大日期: 2023-08-30\n", + "划分后的训练集大小: 2374, 验证集大小: 478\n", + "train_data最大日期: 2023-08-24, 训练天数5\n", + "test_data最大日期: 2023-08-31\n", + "划分后的训练集大小: 2378, 验证集大小: 477\n", + "train_data最大日期: 2023-08-25, 训练天数5\n", + "test_data最大日期: 2023-09-01\n", + "划分后的训练集大小: 2384, 验证集大小: 478\n", + "train_data最大日期: 2023-08-28, 训练天数5\n", + "test_data最大日期: 2023-09-04\n", + "划分后的训练集大小: 2386, 验证集大小: 476\n", + "train_data最大日期: 2023-08-29, 训练天数5\n", + "test_data最大日期: 2023-09-05\n", + "划分后的训练集大小: 2386, 验证集大小: 477\n", + "train_data最大日期: 2023-08-30, 训练天数5\n", + "test_data最大日期: 2023-09-06\n", + "划分后的训练集大小: 2378, 验证集大小: 470\n", + "train_data最大日期: 2023-08-31, 训练天数5\n", + "test_data最大日期: 2023-09-07\n", + "划分后的训练集大小: 2379, 验证集大小: 478\n", + "train_data最大日期: 2023-09-01, 训练天数5\n", + "test_data最大日期: 2023-09-08\n", + "划分后的训练集大小: 2376, 验证集大小: 475\n", + "train_data最大日期: 2023-09-04, 训练天数5\n", + "test_data最大日期: 2023-09-11\n", + "划分后的训练集大小: 2384, 验证集大小: 484\n", + "train_data最大日期: 2023-09-05, 训练天数5\n", + "test_data最大日期: 2023-09-12\n", + "划分后的训练集大小: 2389, 验证集大小: 482\n", + "train_data最大日期: 2023-09-06, 训练天数5\n", + "test_data最大日期: 2023-09-13\n", + "划分后的训练集大小: 2401, 验证集大小: 482\n", + "train_data最大日期: 2023-09-07, 训练天数5\n", + "test_data最大日期: 2023-09-14\n", + "划分后的训练集大小: 2406, 验证集大小: 483\n", + "train_data最大日期: 2023-09-08, 训练天数5\n", + "test_data最大日期: 2023-09-15\n", + "划分后的训练集大小: 2413, 验证集大小: 482\n", + "train_data最大日期: 2023-09-11, 训练天数5\n", + "test_data最大日期: 2023-09-18\n", + "划分后的训练集大小: 2411, 验证集大小: 482\n", + "train_data最大日期: 2023-09-12, 训练天数5\n", + "test_data最大日期: 2023-09-19\n", + "划分后的训练集大小: 2412, 验证集大小: 483\n", + "train_data最大日期: 2023-09-13, 训练天数5\n", + "test_data最大日期: 2023-09-20\n", + "划分后的训练集大小: 2412, 验证集大小: 482\n", + "train_data最大日期: 2023-09-14, 训练天数5\n", + "test_data最大日期: 2023-09-21\n", + "划分后的训练集大小: 2413, 验证集大小: 484\n", + "train_data最大日期: 2023-09-15, 训练天数5\n", + "test_data最大日期: 2023-09-22\n", + "划分后的训练集大小: 2413, 验证集大小: 482\n", + "train_data最大日期: 2023-09-18, 训练天数5\n", + "test_data最大日期: 2023-09-25\n", + "划分后的训练集大小: 2414, 验证集大小: 483\n", + "train_data最大日期: 2023-09-19, 训练天数5\n", + "test_data最大日期: 2023-09-26\n", + "划分后的训练集大小: 2415, 验证集大小: 484\n", + "train_data最大日期: 2023-09-20, 训练天数5\n", + "test_data最大日期: 2023-09-27\n", + "划分后的训练集大小: 2417, 验证集大小: 484\n", + "train_data最大日期: 2023-09-21, 训练天数5\n", + "test_data最大日期: 2023-09-28\n", + "划分后的训练集大小: 2415, 验证集大小: 482\n", + "train_data最大日期: 2023-09-22, 训练天数5\n", + "test_data最大日期: 2023-10-09\n", + "划分后的训练集大小: 2413, 验证集大小: 480\n", + "train_data最大日期: 2023-09-25, 训练天数5\n", + "test_data最大日期: 2023-10-10\n", + "划分后的训练集大小: 2410, 验证集大小: 480\n", + "train_data最大日期: 2023-09-26, 训练天数5\n", + "test_data最大日期: 2023-10-11\n", + "划分后的训练集大小: 2409, 验证集大小: 483\n", + "train_data最大日期: 2023-09-27, 训练天数5\n", + "test_data最大日期: 2023-10-12\n", + "划分后的训练集大小: 2409, 验证集大小: 484\n", + "train_data最大日期: 2023-09-28, 训练天数5\n", + "test_data最大日期: 2023-10-13\n", + "划分后的训练集大小: 2411, 验证集大小: 484\n", + "train_data最大日期: 2023-10-09, 训练天数5\n", + "test_data最大日期: 2023-10-16\n", + "划分后的训练集大小: 2415, 验证集大小: 484\n", + "train_data最大日期: 2023-10-10, 训练天数5\n", + "test_data最大日期: 2023-10-17\n", + "划分后的训练集大小: 2417, 验证集大小: 482\n", + "train_data最大日期: 2023-10-11, 训练天数5\n", + "test_data最大日期: 2023-10-18\n", + "划分后的训练集大小: 2417, 验证集大小: 483\n", + "train_data最大日期: 2023-10-12, 训练天数5\n", + "test_data最大日期: 2023-10-19\n", + "划分后的训练集大小: 2414, 验证集大小: 481\n", + "train_data最大日期: 2023-10-13, 训练天数5\n", + "test_data最大日期: 2023-10-20\n", + "划分后的训练集大小: 2412, 验证集大小: 482\n", + "train_data最大日期: 2023-10-16, 训练天数5\n", + "test_data最大日期: 2023-10-23\n", + "划分后的训练集大小: 2408, 验证集大小: 480\n", + "train_data最大日期: 2023-10-17, 训练天数5\n", + "test_data最大日期: 2023-10-24\n", + "划分后的训练集大小: 2407, 验证集大小: 481\n", + "train_data最大日期: 2023-10-18, 训练天数5\n", + "test_data最大日期: 2023-10-25\n", + "划分后的训练集大小: 2407, 验证集大小: 483\n", + "train_data最大日期: 2023-10-19, 训练天数5\n", + "test_data最大日期: 2023-10-26\n", + "划分后的训练集大小: 2409, 验证集大小: 483\n", + "train_data最大日期: 2023-10-20, 训练天数5\n", + "test_data最大日期: 2023-10-27\n", + "划分后的训练集大小: 2409, 验证集大小: 482\n", + "train_data最大日期: 2023-10-23, 训练天数5\n", + "test_data最大日期: 2023-10-30\n", + "划分后的训练集大小: 2415, 验证集大小: 486\n", + "train_data最大日期: 2023-10-24, 训练天数5\n", + "test_data最大日期: 2023-10-31\n", + "划分后的训练集大小: 2420, 验证集大小: 486\n", + "train_data最大日期: 2023-10-25, 训练天数5\n", + "test_data最大日期: 2023-11-01\n", + "划分后的训练集大小: 2419, 验证集大小: 482\n", + "train_data最大日期: 2023-10-26, 训练天数5\n", + "test_data最大日期: 2023-11-02\n", + "划分后的训练集大小: 2417, 验证集大小: 481\n", + "train_data最大日期: 2023-10-27, 训练天数5\n", + "test_data最大日期: 2023-11-03\n", + "划分后的训练集大小: 2420, 验证集大小: 485\n", + "train_data最大日期: 2023-10-30, 训练天数5\n", + "test_data最大日期: 2023-11-06\n", + "划分后的训练集大小: 2418, 验证集大小: 484\n", + "train_data最大日期: 2023-10-31, 训练天数5\n", + "test_data最大日期: 2023-11-07\n", + "划分后的训练集大小: 2415, 验证集大小: 483\n", + "train_data最大日期: 2023-11-01, 训练天数5\n", + "test_data最大日期: 2023-11-08\n", + "划分后的训练集大小: 2417, 验证集大小: 484\n", + "train_data最大日期: 2023-11-02, 训练天数5\n", + "test_data最大日期: 2023-11-09\n", + "划分后的训练集大小: 2418, 验证集大小: 482\n", + "train_data最大日期: 2023-11-03, 训练天数5\n", + "test_data最大日期: 2023-11-10\n", + "划分后的训练集大小: 2419, 验证集大小: 486\n", + "train_data最大日期: 2023-11-06, 训练天数5\n", + "test_data最大日期: 2023-11-13\n", + "划分后的训练集大小: 2419, 验证集大小: 484\n", + "train_data最大日期: 2023-11-07, 训练天数5\n", + "test_data最大日期: 2023-11-14\n", + "划分后的训练集大小: 2416, 验证集大小: 480\n", + "train_data最大日期: 2023-11-08, 训练天数5\n", + "test_data最大日期: 2023-11-15\n", + "划分后的训练集大小: 2412, 验证集大小: 480\n", + "train_data最大日期: 2023-11-09, 训练天数5\n", + "test_data最大日期: 2023-11-16\n", + "划分后的训练集大小: 2410, 验证集大小: 480\n", + "train_data最大日期: 2023-11-10, 训练天数5\n", + "test_data最大日期: 2023-11-17\n", + "划分后的训练集大小: 2404, 验证集大小: 480\n", + "train_data最大日期: 2023-11-13, 训练天数5\n", + "test_data最大日期: 2023-11-20\n", + "划分后的训练集大小: 2398, 验证集大小: 478\n", + "train_data最大日期: 2023-11-14, 训练天数5\n", + "test_data最大日期: 2023-11-21\n", + "划分后的训练集大小: 2393, 验证集大小: 475\n", + "train_data最大日期: 2023-11-15, 训练天数5\n", + "test_data最大日期: 2023-11-22\n", + "划分后的训练集大小: 2384, 验证集大小: 471\n", + "train_data最大日期: 2023-11-16, 训练天数5\n", + "test_data最大日期: 2023-11-23\n", + "划分后的训练集大小: 2380, 验证集大小: 476\n", + "train_data最大日期: 2023-11-17, 训练天数5\n", + "test_data最大日期: 2023-11-24\n", + "划分后的训练集大小: 2378, 验证集大小: 478\n", + "train_data最大日期: 2023-11-20, 训练天数5\n", + "test_data最大日期: 2023-11-27\n", + "划分后的训练集大小: 2378, 验证集大小: 478\n", + "train_data最大日期: 2023-11-21, 训练天数5\n", + "test_data最大日期: 2023-11-28\n", + "划分后的训练集大小: 2378, 验证集大小: 475\n", + "train_data最大日期: 2023-11-22, 训练天数5\n", + "test_data最大日期: 2023-11-29\n", + "划分后的训练集大小: 2380, 验证集大小: 473\n", + "train_data最大日期: 2023-11-23, 训练天数5\n", + "test_data最大日期: 2023-11-30\n", + "划分后的训练集大小: 2379, 验证集大小: 475\n", + "train_data最大日期: 2023-11-24, 训练天数5\n", + "test_data最大日期: 2023-12-01\n", + "划分后的训练集大小: 2378, 验证集大小: 477\n", + "train_data最大日期: 2023-11-27, 训练天数5\n", + "test_data最大日期: 2023-12-04\n", + "划分后的训练集大小: 2375, 验证集大小: 475\n", + "train_data最大日期: 2023-11-28, 训练天数5\n", + "test_data最大日期: 2023-12-05\n", + "划分后的训练集大小: 2378, 验证集大小: 478\n", + "train_data最大日期: 2023-11-29, 训练天数5\n", + "test_data最大日期: 2023-12-06\n", + "划分后的训练集大小: 2380, 验证集大小: 475\n", + "train_data最大日期: 2023-11-30, 训练天数5\n", + "test_data最大日期: 2023-12-07\n", + "划分后的训练集大小: 2380, 验证集大小: 475\n", + "train_data最大日期: 2023-12-01, 训练天数5\n", + "test_data最大日期: 2023-12-08\n", + "划分后的训练集大小: 2379, 验证集大小: 476\n", + "train_data最大日期: 2023-12-04, 训练天数5\n", + "test_data最大日期: 2023-12-11\n", + "划分后的训练集大小: 2382, 验证集大小: 478\n", + "train_data最大日期: 2023-12-05, 训练天数5\n", + "test_data最大日期: 2023-12-12\n", + "划分后的训练集大小: 2380, 验证集大小: 476\n", + "train_data最大日期: 2023-12-06, 训练天数5\n", + "test_data最大日期: 2023-12-13\n", + "划分后的训练集大小: 2383, 验证集大小: 478\n", + "train_data最大日期: 2023-12-07, 训练天数5\n", + "test_data最大日期: 2023-12-14\n", + "划分后的训练集大小: 2385, 验证集大小: 477\n", + "train_data最大日期: 2023-12-08, 训练天数5\n", + "test_data最大日期: 2023-12-15\n", + "划分后的训练集大小: 2382, 验证集大小: 473\n", + "train_data最大日期: 2023-12-11, 训练天数5\n", + "test_data最大日期: 2023-12-18\n", + "划分后的训练集大小: 2376, 验证集大小: 472\n", + "train_data最大日期: 2023-12-12, 训练天数5\n", + "test_data最大日期: 2023-12-19\n", + "划分后的训练集大小: 2375, 验证集大小: 475\n", + "train_data最大日期: 2023-12-13, 训练天数5\n", + "test_data最大日期: 2023-12-20\n", + "划分后的训练集大小: 2372, 验证集大小: 475\n", + "train_data最大日期: 2023-12-14, 训练天数5\n", + "test_data最大日期: 2023-12-21\n", + "划分后的训练集大小: 2371, 验证集大小: 476\n", + "train_data最大日期: 2023-12-15, 训练天数5\n", + "test_data最大日期: 2023-12-22\n", + "划分后的训练集大小: 2370, 验证集大小: 472\n", + "train_data最大日期: 2023-12-18, 训练天数5\n", + "test_data最大日期: 2023-12-25\n", + "划分后的训练集大小: 2369, 验证集大小: 471\n", + "train_data最大日期: 2023-12-19, 训练天数5\n", + "test_data最大日期: 2023-12-26\n", + "划分后的训练集大小: 2368, 验证集大小: 474\n", + "train_data最大日期: 2023-12-20, 训练天数5\n", + "test_data最大日期: 2023-12-27\n", + "划分后的训练集大小: 2362, 验证集大小: 469\n", + "train_data最大日期: 2023-12-21, 训练天数5\n", + "test_data最大日期: 2023-12-28\n", + "划分后的训练集大小: 2359, 验证集大小: 473\n", + "train_data最大日期: 2023-12-22, 训练天数5\n", + "test_data最大日期: 2023-12-29\n", + "划分后的训练集大小: 2360, 验证集大小: 473\n", + "train_data最大日期: 2023-12-25, 训练天数5\n", + "test_data最大日期: 2024-01-02\n", + "划分后的训练集大小: 2359, 验证集大小: 470\n", + "train_data最大日期: 2023-12-26, 训练天数5\n", + "test_data最大日期: 2024-01-03\n", + "划分后的训练集大小: 2351, 验证集大小: 466\n", + "train_data最大日期: 2023-12-27, 训练天数5\n", + "test_data最大日期: 2024-01-04\n", + "划分后的训练集大小: 2349, 验证集大小: 467\n", + "train_data最大日期: 2023-12-28, 训练天数5\n", + "test_data最大日期: 2024-01-05\n", + "划分后的训练集大小: 2349, 验证集大小: 473\n", + "train_data最大日期: 2023-12-29, 训练天数5\n", + "test_data最大日期: 2024-01-08\n", + "划分后的训练集大小: 2345, 验证集大小: 469\n", + "train_data最大日期: 2024-01-02, 训练天数5\n", + "test_data最大日期: 2024-01-09\n", + "划分后的训练集大小: 2348, 验证集大小: 473\n", + "train_data最大日期: 2024-01-03, 训练天数5\n", + "test_data最大日期: 2024-01-10\n", + "划分后的训练集大小: 2358, 验证集大小: 476\n", + "train_data最大日期: 2024-01-04, 训练天数5\n", + "test_data最大日期: 2024-01-11\n", + "划分后的训练集大小: 2363, 验证集大小: 472\n", + "train_data最大日期: 2024-01-05, 训练天数5\n", + "test_data最大日期: 2024-01-12\n", + "划分后的训练集大小: 2364, 验证集大小: 474\n", + "train_data最大日期: 2024-01-08, 训练天数5\n", + "test_data最大日期: 2024-01-15\n", + "划分后的训练集大小: 2365, 验证集大小: 470\n", + "train_data最大日期: 2024-01-09, 训练天数5\n", + "test_data最大日期: 2024-01-16\n", + "划分后的训练集大小: 2362, 验证集大小: 470\n", + "train_data最大日期: 2024-01-10, 训练天数5\n", + "test_data最大日期: 2024-01-17\n", + "划分后的训练集大小: 2357, 验证集大小: 471\n", + "train_data最大日期: 2024-01-11, 训练天数5\n", + "test_data最大日期: 2024-01-18\n", + "划分后的训练集大小: 2352, 验证集大小: 467\n", + "train_data最大日期: 2024-01-12, 训练天数5\n", + "test_data最大日期: 2024-01-19\n", + "划分后的训练集大小: 2345, 验证集大小: 467\n", + "train_data最大日期: 2024-01-15, 训练天数5\n", + "test_data最大日期: 2024-01-22\n", + "划分后的训练集大小: 2325, 验证集大小: 450\n", + "train_data最大日期: 2024-01-16, 训练天数5\n", + "test_data最大日期: 2024-01-23\n", + "划分后的训练集大小: 2304, 验证集大小: 449\n", + "train_data最大日期: 2024-01-17, 训练天数5\n", + "test_data最大日期: 2024-01-24\n", + "划分后的训练集大小: 2294, 验证集大小: 461\n", + "train_data最大日期: 2024-01-18, 训练天数5\n", + "test_data最大日期: 2024-01-25\n", + "划分后的训练集大小: 2296, 验证集大小: 469\n", + "train_data最大日期: 2024-01-19, 训练天数5\n", + "test_data最大日期: 2024-01-26\n", + "划分后的训练集大小: 2298, 验证集大小: 469\n", + "train_data最大日期: 2024-01-22, 训练天数5\n", + "test_data最大日期: 2024-01-29\n", + "划分后的训练集大小: 2312, 验证集大小: 464\n", + "train_data最大日期: 2024-01-23, 训练天数5\n", + "test_data最大日期: 2024-01-30\n", + "划分后的训练集大小: 2329, 验证集大小: 466\n", + "train_data最大日期: 2024-01-24, 训练天数5\n", + "test_data最大日期: 2024-01-31\n", + "划分后的训练集大小: 2321, 验证集大小: 453\n", + "train_data最大日期: 2024-01-25, 训练天数5\n", + "test_data最大日期: 2024-02-01\n", + "划分后的训练集大小: 2247, 验证集大小: 395\n", + "train_data最大日期: 2024-01-26, 训练天数5\n", + "test_data最大日期: 2024-02-02\n", + "划分后的训练集大小: 1931, 验证集大小: 153\n", + "train_data最大日期: 2024-01-29, 训练天数5\n", + "test_data最大日期: 2024-02-05\n", + "划分后的训练集大小: 1522, 验证集大小: 55\n", + "train_data最大日期: 2024-01-30, 训练天数5\n", + "test_data最大日期: 2024-02-06\n", + "划分后的训练集大小: 1142, 验证集大小: 86\n", + "train_data最大日期: 2024-01-31, 训练天数5\n", + "test_data最大日期: 2024-02-07\n", + "划分后的训练集大小: 792, 验证集大小: 103\n", + "[2.0, 6.0, 9.0, 4.0, 7.0, 3.0, 5.0, 1.0, 19.0, 8.0, 12.0, 17.0, 10.0, 14.0, 13.0, 18.0, 11.0, 16.0, 15.0]\n", + "train_data最大日期: 2024-02-01, 训练天数5\n", + "test_data最大日期: 2024-02-08\n", + "划分后的训练集大小: 637, 验证集大小: 240\n", + "[6.0, 10.0, 19.0, 8.0, 9.0, 11.0, 16.0, 7.0, 13.0, 12.0, 5.0, 15.0, 17.0, 14.0, 18.0, 4.0, 3.0, 2.0]\n", + "train_data最大日期: 2024-02-02, 训练天数5\n", + "test_data最大日期: 2024-02-19\n", + "划分后的训练集大小: 914, 验证集大小: 430\n", + "train_data最大日期: 2024-02-05, 训练天数5\n", + "test_data最大日期: 2024-02-20\n", + "划分后的训练集大小: 1319, 验证集大小: 460\n", + "train_data最大日期: 2024-02-06, 训练天数5\n", + "test_data最大日期: 2024-02-21\n", + "划分后的训练集大小: 1694, 验证集大小: 461\n", + "train_data最大日期: 2024-02-07, 训练天数5\n", + "test_data最大日期: 2024-02-22\n", + "划分后的训练集大小: 1807, 验证集大小: 216\n", + "train_data最大日期: 2024-02-08, 训练天数5\n", + "test_data最大日期: 2024-02-23\n", + "划分后的训练集大小: 1925, 验证集大小: 358\n", + "train_data最大日期: 2024-02-19, 训练天数5\n", + "test_data最大日期: 2024-02-26\n", + "划分后的训练集大小: 1912, 验证集大小: 417\n", + "train_data最大日期: 2024-02-20, 训练天数5\n", + "test_data最大日期: 2024-02-27\n", + "划分后的训练集大小: 1843, 验证集大小: 391\n", + "train_data最大日期: 2024-02-21, 训练天数5\n", + "test_data最大日期: 2024-02-28\n", + "划分后的训练集大小: 1841, 验证集大小: 459\n", + "train_data最大日期: 2024-02-22, 训练天数5\n", + "test_data最大日期: 2024-02-29\n", + "划分后的训练集大小: 2087, 验证集大小: 462\n", + "train_data最大日期: 2024-02-23, 训练天数5\n", + "test_data最大日期: 2024-03-01\n", + "划分后的训练集大小: 2192, 验证集大小: 463\n", + "train_data最大日期: 2024-02-26, 训练天数5\n", + "test_data最大日期: 2024-03-04\n", + "划分后的训练集大小: 2236, 验证集大小: 461\n", + "train_data最大日期: 2024-02-27, 训练天数5\n", + "test_data最大日期: 2024-03-05\n", + "划分后的训练集大小: 2312, 验证集大小: 467\n", + "train_data最大日期: 2024-02-28, 训练天数5\n", + "test_data最大日期: 2024-03-06\n", + "划分后的训练集大小: 2312, 验证集大小: 459\n", + "train_data最大日期: 2024-02-29, 训练天数5\n", + "test_data最大日期: 2024-03-07\n", + "划分后的训练集大小: 2317, 验证集大小: 467\n", + "train_data最大日期: 2024-03-01, 训练天数5\n", + "test_data最大日期: 2024-03-08\n", + "划分后的训练集大小: 2317, 验证集大小: 463\n", + "train_data最大日期: 2024-03-04, 训练天数5\n", + "test_data最大日期: 2024-03-11\n", + "划分后的训练集大小: 2320, 验证集大小: 464\n", + "train_data最大日期: 2024-03-05, 训练天数5\n", + "test_data最大日期: 2024-03-12\n", + "划分后的训练集大小: 2314, 验证集大小: 461\n", + "train_data最大日期: 2024-03-06, 训练天数5\n", + "test_data最大日期: 2024-03-13\n", + "划分后的训练集大小: 2321, 验证集大小: 466\n", + "train_data最大日期: 2024-03-07, 训练天数5\n", + "test_data最大日期: 2024-03-14\n", + "划分后的训练集大小: 2319, 验证集大小: 465\n", + "train_data最大日期: 2024-03-08, 训练天数5\n", + "test_data最大日期: 2024-03-15\n", + "划分后的训练集大小: 2313, 验证集大小: 457\n", + "train_data最大日期: 2024-03-11, 训练天数5\n", + "test_data最大日期: 2024-03-18\n", + "划分后的训练集大小: 2308, 验证集大小: 459\n", + "train_data最大日期: 2024-03-12, 训练天数5\n", + "test_data最大日期: 2024-03-19\n", + "划分后的训练集大小: 2308, 验证集大小: 461\n", + "train_data最大日期: 2024-03-13, 训练天数5\n", + "test_data最大日期: 2024-03-20\n", + "划分后的训练集大小: 2299, 验证集大小: 457\n", + "train_data最大日期: 2024-03-14, 训练天数5\n", + "test_data最大日期: 2024-03-21\n", + "划分后的训练集大小: 2292, 验证集大小: 458\n", + "train_data最大日期: 2024-03-15, 训练天数5\n", + "test_data最大日期: 2024-03-22\n", + "划分后的训练集大小: 2295, 验证集大小: 460\n", + "train_data最大日期: 2024-03-18, 训练天数5\n", + "test_data最大日期: 2024-03-25\n", + "划分后的训练集大小: 2299, 验证集大小: 463\n", + "train_data最大日期: 2024-03-19, 训练天数5\n", + "test_data最大日期: 2024-03-26\n", + "划分后的训练集大小: 2298, 验证集大小: 460\n", + "train_data最大日期: 2024-03-20, 训练天数5\n", + "test_data最大日期: 2024-03-27\n", + "划分后的训练集大小: 2295, 验证集大小: 454\n", + "train_data最大日期: 2024-03-21, 训练天数5\n", + "test_data最大日期: 2024-03-28\n", + "划分后的训练集大小: 2300, 验证集大小: 463\n", + "train_data最大日期: 2024-03-22, 训练天数5\n", + "test_data最大日期: 2024-03-29\n", + "划分后的训练集大小: 2303, 验证集大小: 463\n", + "train_data最大日期: 2024-03-25, 训练天数5\n", + "test_data最大日期: 2024-04-01\n", + "划分后的训练集大小: 2302, 验证集大小: 462\n", + "train_data最大日期: 2024-03-26, 训练天数5\n", + "test_data最大日期: 2024-04-02\n", + "划分后的训练集大小: 2303, 验证集大小: 461\n", + "train_data最大日期: 2024-03-27, 训练天数5\n", + "test_data最大日期: 2024-04-03\n", + "划分后的训练集大小: 2303, 验证集大小: 454\n", + "train_data最大日期: 2024-03-28, 训练天数5\n", + "test_data最大日期: 2024-04-08\n", + "划分后的训练集大小: 2297, 验证集大小: 457\n", + "train_data最大日期: 2024-03-29, 训练天数5\n", + "test_data最大日期: 2024-04-09\n", + "划分后的训练集大小: 2299, 验证集大小: 465\n", + "train_data最大日期: 2024-04-01, 训练天数5\n", + "test_data最大日期: 2024-04-10\n", + "划分后的训练集大小: 2290, 验证集大小: 453\n", + "train_data最大日期: 2024-04-02, 训练天数5\n", + "test_data最大日期: 2024-04-11\n", + "划分后的训练集大小: 2279, 验证集大小: 450\n", + "train_data最大日期: 2024-04-03, 训练天数5\n", + "test_data最大日期: 2024-04-12\n", + "划分后的训练集大小: 2282, 验证集大小: 457\n", + "train_data最大日期: 2024-04-08, 训练天数5\n", + "test_data最大日期: 2024-04-15\n", + "划分后的训练集大小: 2282, 验证集大小: 457\n", + "train_data最大日期: 2024-04-09, 训练天数5\n", + "test_data最大日期: 2024-04-16\n", + "划分后的训练集大小: 2076, 验证集大小: 259\n", + "train_data最大日期: 2024-04-10, 训练天数5\n", + "test_data最大日期: 2024-04-17\n", + "划分后的训练集大小: 2070, 验证集大小: 447\n", + "train_data最大日期: 2024-04-11, 训练天数5\n", + "test_data最大日期: 2024-04-18\n", + "划分后的训练集大小: 2054, 验证集大小: 434\n", + "train_data最大日期: 2024-04-12, 训练天数5\n", + "test_data最大日期: 2024-04-19\n", + "划分后的训练集大小: 2044, 验证集大小: 447\n", + "train_data最大日期: 2024-04-15, 训练天数5\n", + "test_data最大日期: 2024-04-22\n", + "划分后的训练集大小: 2053, 验证集大小: 466\n", + "train_data最大日期: 2024-04-16, 训练天数5\n", + "test_data最大日期: 2024-04-23\n", + "划分后的训练集大小: 2255, 验证集大小: 461\n", + "train_data最大日期: 2024-04-17, 训练天数5\n", + "test_data最大日期: 2024-04-24\n", + "划分后的训练集大小: 2276, 验证集大小: 468\n", + "train_data最大日期: 2024-04-18, 训练天数5\n", + "test_data最大日期: 2024-04-25\n", + "划分后的训练集大小: 2310, 验证集大小: 468\n", + "train_data最大日期: 2024-04-19, 训练天数5\n", + "test_data最大日期: 2024-04-26\n", + "划分后的训练集大小: 2326, 验证集大小: 463\n", + "train_data最大日期: 2024-04-22, 训练天数5\n", + "test_data最大日期: 2024-04-29\n", + "划分后的训练集大小: 2319, 验证集大小: 459\n", + "train_data最大日期: 2024-04-23, 训练天数5\n", + "test_data最大日期: 2024-04-30\n", + "划分后的训练集大小: 2309, 验证集大小: 451\n", + "train_data最大日期: 2024-04-24, 训练天数5\n", + "test_data最大日期: 2024-05-06\n", + "划分后的训练集大小: 2289, 验证集大小: 448\n", + "train_data最大日期: 2024-04-25, 训练天数5\n", + "test_data最大日期: 2024-05-07\n", + "划分后的训练集大小: 2265, 验证集大小: 444\n", + "train_data最大日期: 2024-04-26, 训练天数5\n", + "test_data最大日期: 2024-05-08\n", + "划分后的训练集大小: 2258, 验证集大小: 456\n", + "train_data最大日期: 2024-04-29, 训练天数5\n", + "test_data最大日期: 2024-05-09\n", + "划分后的训练集大小: 2258, 验证集大小: 459\n", + "train_data最大日期: 2024-04-30, 训练天数5\n", + "test_data最大日期: 2024-05-10\n", + "划分后的训练集大小: 2285, 验证集大小: 478\n", + "train_data最大日期: 2024-05-06, 训练天数5\n", + "test_data最大日期: 2024-05-13\n", + "划分后的训练集大小: 2309, 验证集大小: 472\n", + "train_data最大日期: 2024-05-07, 训练天数5\n", + "test_data最大日期: 2024-05-14\n", + "划分后的训练集大小: 2342, 验证集大小: 477\n", + "train_data最大日期: 2024-05-08, 训练天数5\n", + "test_data最大日期: 2024-05-15\n", + "划分后的训练集大小: 2364, 验证集大小: 478\n", + "train_data最大日期: 2024-05-09, 训练天数5\n", + "test_data最大日期: 2024-05-16\n", + "划分后的训练集大小: 2379, 验证集大小: 474\n", + "train_data最大日期: 2024-05-10, 训练天数5\n", + "test_data最大日期: 2024-05-17\n", + "划分后的训练集大小: 2376, 验证集大小: 475\n", + "train_data最大日期: 2024-05-13, 训练天数5\n", + "test_data最大日期: 2024-05-20\n", + "划分后的训练集大小: 2377, 验证集大小: 473\n", + "train_data最大日期: 2024-05-14, 训练天数5\n", + "test_data最大日期: 2024-05-21\n", + "划分后的训练集大小: 2376, 验证集大小: 476\n", + "train_data最大日期: 2024-05-15, 训练天数5\n", + "test_data最大日期: 2024-05-22\n", + "划分后的训练集大小: 2375, 验证集大小: 477\n", + "train_data最大日期: 2024-05-16, 训练天数5\n", + "test_data最大日期: 2024-05-23\n", + "划分后的训练集大小: 2375, 验证集大小: 474\n", + "train_data最大日期: 2024-05-17, 训练天数5\n", + "test_data最大日期: 2024-05-24\n", + "划分后的训练集大小: 2374, 验证集大小: 474\n", + "train_data最大日期: 2024-05-20, 训练天数5\n", + "test_data最大日期: 2024-05-27\n", + "划分后的训练集大小: 2373, 验证集大小: 472\n", + "train_data最大日期: 2024-05-21, 训练天数5\n", + "test_data最大日期: 2024-05-28\n", + "划分后的训练集大小: 2376, 验证集大小: 479\n", + "train_data最大日期: 2024-05-22, 训练天数5\n", + "test_data最大日期: 2024-05-29\n", + "划分后的训练集大小: 2377, 验证集大小: 478\n", + "train_data最大日期: 2024-05-23, 训练天数5\n", + "test_data最大日期: 2024-05-30\n", + "划分后的训练集大小: 2385, 验证集大小: 482\n", + "train_data最大日期: 2024-05-24, 训练天数5\n", + "test_data最大日期: 2024-05-31\n", + "划分后的训练集大小: 2392, 验证集大小: 481\n", + "train_data最大日期: 2024-05-27, 训练天数5\n", + "test_data最大日期: 2024-06-03\n", + "划分后的训练集大小: 2400, 验证集大小: 480\n", + "train_data最大日期: 2024-05-28, 训练天数5\n", + "test_data最大日期: 2024-06-04\n", + "划分后的训练集大小: 2401, 验证集大小: 480\n", + "train_data最大日期: 2024-05-29, 训练天数5\n", + "test_data最大日期: 2024-06-05\n", + "划分后的训练集大小: 2397, 验证集大小: 474\n", + "train_data最大日期: 2024-05-30, 训练天数5\n", + "test_data最大日期: 2024-06-06\n", + "划分后的训练集大小: 2349, 验证集大小: 434\n", + "train_data最大日期: 2024-05-31, 训练天数5\n", + "test_data最大日期: 2024-06-07\n", + "划分后的训练集大小: 2342, 验证集大小: 474\n", + "train_data最大日期: 2024-06-03, 训练天数5\n", + "test_data最大日期: 2024-06-11\n", + "划分后的训练集大小: 2342, 验证集大小: 480\n", + "train_data最大日期: 2024-06-04, 训练天数5\n", + "test_data最大日期: 2024-06-12\n", + "划分后的训练集大小: 2344, 验证集大小: 482\n", + "train_data最大日期: 2024-06-05, 训练天数5\n", + "test_data最大日期: 2024-06-13\n", + "划分后的训练集大小: 2349, 验证集大小: 479\n", + "train_data最大日期: 2024-06-06, 训练天数5\n", + "test_data最大日期: 2024-06-14\n", + "划分后的训练集大小: 2389, 验证集大小: 474\n", + "train_data最大日期: 2024-06-07, 训练天数5\n", + "test_data最大日期: 2024-06-17\n", + "划分后的训练集大小: 2389, 验证集大小: 474\n", + "train_data最大日期: 2024-06-11, 训练天数5\n", + "test_data最大日期: 2024-06-18\n", + "划分后的训练集大小: 2386, 验证集大小: 477\n", + "train_data最大日期: 2024-06-12, 训练天数5\n", + "test_data最大日期: 2024-06-19\n", + "划分后的训练集大小: 2381, 验证集大小: 477\n", + "train_data最大日期: 2024-06-13, 训练天数5\n", + "test_data最大日期: 2024-06-20\n", + "划分后的训练集大小: 2378, 验证集大小: 476\n", + "train_data最大日期: 2024-06-14, 训练天数5\n", + "test_data最大日期: 2024-06-21\n", + "划分后的训练集大小: 2378, 验证集大小: 474\n", + "train_data最大日期: 2024-06-17, 训练天数5\n", + "test_data最大日期: 2024-06-24\n", + "划分后的训练集大小: 2380, 验证集大小: 476\n", + "train_data最大日期: 2024-06-18, 训练天数5\n", + "test_data最大日期: 2024-06-25\n", + "划分后的训练集大小: 2380, 验证集大小: 477\n", + "train_data最大日期: 2024-06-19, 训练天数5\n", + "test_data最大日期: 2024-06-26\n", + "划分后的训练集大小: 2381, 验证集大小: 478\n", + "train_data最大日期: 2024-06-20, 训练天数5\n", + "test_data最大日期: 2024-06-27\n", + "划分后的训练集大小: 2381, 验证集大小: 476\n", + "train_data最大日期: 2024-06-21, 训练天数5\n", + "test_data最大日期: 2024-06-28\n", + "划分后的训练集大小: 2383, 验证集大小: 476\n", + "train_data最大日期: 2024-06-24, 训练天数5\n", + "test_data最大日期: 2024-07-01\n", + "划分后的训练集大小: 2385, 验证集大小: 478\n", + "train_data最大日期: 2024-06-25, 训练天数5\n", + "test_data最大日期: 2024-07-02\n", + "划分后的训练集大小: 2387, 验证集大小: 479\n", + "train_data最大日期: 2024-06-26, 训练天数5\n", + "test_data最大日期: 2024-07-03\n", + "划分后的训练集大小: 2386, 验证集大小: 477\n", + "train_data最大日期: 2024-06-27, 训练天数5\n", + "test_data最大日期: 2024-07-04\n", + "划分后的训练集大小: 2390, 验证集大小: 480\n", + "train_data最大日期: 2024-06-28, 训练天数5\n", + "test_data最大日期: 2024-07-05\n", + "划分后的训练集大小: 2394, 验证集大小: 480\n", + "train_data最大日期: 2024-07-01, 训练天数5\n", + "test_data最大日期: 2024-07-08\n", + "划分后的训练集大小: 2397, 验证集大小: 481\n", + "train_data最大日期: 2024-07-02, 训练天数5\n", + "test_data最大日期: 2024-07-09\n", + "划分后的训练集大小: 2402, 验证集大小: 484\n", + "train_data最大日期: 2024-07-03, 训练天数5\n", + "test_data最大日期: 2024-07-10\n", + "划分后的训练集大小: 2407, 验证集大小: 482\n", + "train_data最大日期: 2024-07-04, 训练天数5\n", + "test_data最大日期: 2024-07-11\n", + "划分后的训练集大小: 2410, 验证集大小: 483\n", + "train_data最大日期: 2024-07-05, 训练天数5\n", + "test_data最大日期: 2024-07-12\n", + "划分后的训练集大小: 2413, 验证集大小: 483\n", + "train_data最大日期: 2024-07-08, 训练天数5\n", + "test_data最大日期: 2024-07-15\n", + "划分后的训练集大小: 2414, 验证集大小: 482\n", + "train_data最大日期: 2024-07-09, 训练天数5\n", + "test_data最大日期: 2024-07-16\n", + "划分后的训练集大小: 2416, 验证集大小: 486\n", + "train_data最大日期: 2024-07-10, 训练天数5\n", + "test_data最大日期: 2024-07-17\n", + "划分后的训练集大小: 2417, 验证集大小: 483\n", + "train_data最大日期: 2024-07-11, 训练天数5\n", + "test_data最大日期: 2024-07-18\n", + "划分后的训练集大小: 2420, 验证集大小: 486\n", + "train_data最大日期: 2024-07-12, 训练天数5\n", + "test_data最大日期: 2024-07-19\n", + "划分后的训练集大小: 2421, 验证集大小: 484\n", + "train_data最大日期: 2024-07-15, 训练天数5\n", + "test_data最大日期: 2024-07-22\n", + "划分后的训练集大小: 2423, 验证集大小: 484\n", + "train_data最大日期: 2024-07-16, 训练天数5\n", + "test_data最大日期: 2024-07-23\n", + "划分后的训练集大小: 2421, 验证集大小: 484\n", + "train_data最大日期: 2024-07-17, 训练天数5\n", + "test_data最大日期: 2024-07-24\n", + "划分后的训练集大小: 2420, 验证集大小: 482\n", + "train_data最大日期: 2024-07-18, 训练天数5\n", + "test_data最大日期: 2024-07-25\n", + "划分后的训练集大小: 2418, 验证集大小: 484\n", + "train_data最大日期: 2024-07-19, 训练天数5\n", + "test_data最大日期: 2024-07-26\n", + "划分后的训练集大小: 2418, 验证集大小: 484\n", + "train_data最大日期: 2024-07-22, 训练天数5\n", + "test_data最大日期: 2024-07-29\n", + "划分后的训练集大小: 2414, 验证集大小: 480\n", + "train_data最大日期: 2024-07-23, 训练天数5\n", + "test_data最大日期: 2024-07-30\n", + "划分后的训练集大小: 2410, 验证集大小: 480\n", + "train_data最大日期: 2024-07-24, 训练天数5\n", + "test_data最大日期: 2024-07-31\n", + "划分后的训练集大小: 2410, 验证集大小: 482\n", + "train_data最大日期: 2024-07-25, 训练天数5\n", + "test_data最大日期: 2024-08-01\n", + "划分后的训练集大小: 2405, 验证集大小: 479\n", + "train_data最大日期: 2024-07-26, 训练天数5\n", + "test_data最大日期: 2024-08-02\n", + "划分后的训练集大小: 2401, 验证集大小: 480\n", + "train_data最大日期: 2024-07-29, 训练天数5\n", + "test_data最大日期: 2024-08-05\n", + "划分后的训练集大小: 2399, 验证集大小: 478\n", + "train_data最大日期: 2024-07-30, 训练天数5\n", + "test_data最大日期: 2024-08-06\n", + "划分后的训练集大小: 2398, 验证集大小: 479\n", + "train_data最大日期: 2024-07-31, 训练天数5\n", + "test_data最大日期: 2024-08-07\n", + "划分后的训练集大小: 2396, 验证集大小: 480\n", + "train_data最大日期: 2024-08-01, 训练天数5\n", + "test_data最大日期: 2024-08-08\n", + "划分后的训练集大小: 2396, 验证集大小: 479\n", + "train_data最大日期: 2024-08-02, 训练天数5\n", + "test_data最大日期: 2024-08-09\n", + "划分后的训练集大小: 2395, 验证集大小: 479\n", + "train_data最大日期: 2024-08-05, 训练天数5\n", + "test_data最大日期: 2024-08-12\n", + "划分后的训练集大小: 2394, 验证集大小: 477\n", + "train_data最大日期: 2024-08-06, 训练天数5\n", + "test_data最大日期: 2024-08-13\n", + "划分后的训练集大小: 2396, 验证集大小: 481\n", + "train_data最大日期: 2024-08-07, 训练天数5\n", + "test_data最大日期: 2024-08-14\n", + "划分后的训练集大小: 2398, 验证集大小: 482\n", + "train_data最大日期: 2024-08-08, 训练天数5\n", + "test_data最大日期: 2024-08-15\n", + "划分后的训练集大小: 2401, 验证集大小: 482\n", + "train_data最大日期: 2024-08-09, 训练天数5\n", + "test_data最大日期: 2024-08-16\n", + "划分后的训练集大小: 2405, 验证集大小: 483\n", + "train_data最大日期: 2024-08-12, 训练天数5\n", + "test_data最大日期: 2024-08-19\n", + "划分后的训练集大小: 2407, 验证集大小: 479\n", + "train_data最大日期: 2024-08-13, 训练天数5\n", + "test_data最大日期: 2024-08-20\n", + "划分后的训练集大小: 2408, 验证集大小: 482\n", + "train_data最大日期: 2024-08-14, 训练天数5\n", + "test_data最大日期: 2024-08-21\n", + "划分后的训练集大小: 2408, 验证集大小: 482\n", + "train_data最大日期: 2024-08-15, 训练天数5\n", + "test_data最大日期: 2024-08-22\n", + "划分后的训练集大小: 2408, 验证集大小: 482\n", + "train_data最大日期: 2024-08-16, 训练天数5\n", + "test_data最大日期: 2024-08-23\n", + "划分后的训练集大小: 2407, 验证集大小: 482\n", + "train_data最大日期: 2024-08-19, 训练天数5\n", + "test_data最大日期: 2024-08-26\n", + "划分后的训练集大小: 2409, 验证集大小: 481\n", + "train_data最大日期: 2024-08-20, 训练天数5\n", + "test_data最大日期: 2024-08-27\n", + "划分后的训练集大小: 2410, 验证集大小: 483\n", + "train_data最大日期: 2024-08-21, 训练天数5\n", + "test_data最大日期: 2024-08-28\n", + "划分后的训练集大小: 2412, 验证集大小: 484\n", + "train_data最大日期: 2024-08-22, 训练天数5\n", + "test_data最大日期: 2024-08-29\n", + "划分后的训练集大小: 2416, 验证集大小: 486\n", + "train_data最大日期: 2024-08-23, 训练天数5\n", + "test_data最大日期: 2024-08-30\n", + "划分后的训练集大小: 2420, 验证集大小: 486\n", + "train_data最大日期: 2024-08-26, 训练天数5\n", + "test_data最大日期: 2024-09-02\n", + "划分后的训练集大小: 2424, 验证集大小: 485\n", + "train_data最大日期: 2024-08-27, 训练天数5\n", + "test_data最大日期: 2024-09-03\n", + "划分后的训练集大小: 2424, 验证集大小: 483\n", + "train_data最大日期: 2024-08-28, 训练天数5\n", + "test_data最大日期: 2024-09-04\n", + "划分后的训练集大小: 2425, 验证集大小: 485\n", + "train_data最大日期: 2024-08-29, 训练天数5\n", + "test_data最大日期: 2024-09-05\n", + "划分后的训练集大小: 2421, 验证集大小: 482\n", + "train_data最大日期: 2024-08-30, 训练天数5\n", + "test_data最大日期: 2024-09-06\n", + "划分后的训练集大小: 2417, 验证集大小: 482\n", + "train_data最大日期: 2024-09-02, 训练天数5\n", + "test_data最大日期: 2024-09-09\n", + "划分后的训练集大小: 2413, 验证集大小: 481\n", + "train_data最大日期: 2024-09-03, 训练天数5\n", + "test_data最大日期: 2024-09-10\n", + "划分后的训练集大小: 2408, 验证集大小: 478\n", + "train_data最大日期: 2024-09-04, 训练天数5\n", + "test_data最大日期: 2024-09-11\n", + "划分后的训练集大小: 2400, 验证集大小: 477\n", + "train_data最大日期: 2024-09-05, 训练天数5\n", + "test_data最大日期: 2024-09-12\n", + "划分后的训练集大小: 2397, 验证集大小: 479\n", + "train_data最大日期: 2024-09-06, 训练天数5\n", + "test_data最大日期: 2024-09-13\n", + "划分后的训练集大小: 2393, 验证集大小: 478\n", + "train_data最大日期: 2024-09-09, 训练天数5\n", + "test_data最大日期: 2024-09-18\n", + "划分后的训练集大小: 2388, 验证集大小: 476\n", + "train_data最大日期: 2024-09-10, 训练天数5\n", + "test_data最大日期: 2024-09-19\n", + "划分后的训练集大小: 2389, 验证集大小: 479\n", + "train_data最大日期: 2024-09-11, 训练天数5\n", + "test_data最大日期: 2024-09-20\n", + "划分后的训练集大小: 2386, 验证集大小: 474\n", + "train_data最大日期: 2024-09-12, 训练天数5\n", + "test_data最大日期: 2024-09-23\n", + "划分后的训练集大小: 2384, 验证集大小: 477\n", + "train_data最大日期: 2024-09-13, 训练天数5\n", + "test_data最大日期: 2024-09-24\n", + "划分后的训练集大小: 2381, 验证集大小: 475\n", + "train_data最大日期: 2024-09-18, 训练天数5\n", + "test_data最大日期: 2024-09-25\n", + "划分后的训练集大小: 2381, 验证集大小: 476\n", + "train_data最大日期: 2024-09-19, 训练天数5\n", + "test_data最大日期: 2024-09-26\n", + "划分后的训练集大小: 2380, 验证集大小: 478\n", + "train_data最大日期: 2024-09-20, 训练天数5\n", + "test_data最大日期: 2024-09-27\n", + "划分后的训练集大小: 2375, 验证集大小: 469\n", + "train_data最大日期: 2024-09-23, 训练天数5\n", + "test_data最大日期: 2024-09-30\n", + "划分后的训练集大小: 2310, 验证集大小: 412\n", + "train_data最大日期: 2024-09-24, 训练天数5\n", + "test_data最大日期: 2024-10-08\n", + "划分后的训练集大小: 2197, 验证集大小: 362\n", + "train_data最大日期: 2024-09-25, 训练天数5\n", + "test_data最大日期: 2024-10-09\n", + "划分后的训练集大小: 2173, 验证集大小: 452\n", + "train_data最大日期: 2024-09-26, 训练天数5\n", + "test_data最大日期: 2024-10-10\n", + "划分后的训练集大小: 2150, 验证集大小: 455\n", + "train_data最大日期: 2024-09-27, 训练天数5\n", + "test_data最大日期: 2024-10-11\n", + "划分后的训练集大小: 2144, 验证集大小: 463\n", + "train_data最大日期: 2024-09-30, 训练天数5\n", + "test_data最大日期: 2024-10-14\n", + "划分后的训练集大小: 2190, 验证集大小: 458\n", + "train_data最大日期: 2024-10-08, 训练天数5\n", + "test_data最大日期: 2024-10-15\n", + "划分后的训练集大小: 2219, 验证集大小: 391\n", + "train_data最大日期: 2024-10-09, 训练天数5\n", + "test_data最大日期: 2024-10-16\n", + "划分后的训练集大小: 2246, 验证集大小: 479\n", + "train_data最大日期: 2024-10-10, 训练天数5\n", + "test_data最大日期: 2024-10-17\n", + "划分后的训练集大小: 2268, 验证集大小: 477\n", + "train_data最大日期: 2024-10-11, 训练天数5\n", + "test_data最大日期: 2024-10-18\n", + "划分后的训练集大小: 2280, 验证集大小: 475\n", + "train_data最大日期: 2024-10-14, 训练天数5\n", + "test_data最大日期: 2024-10-21\n", + "划分后的训练集大小: 2298, 验证集大小: 476\n", + "train_data最大日期: 2024-10-15, 训练天数5\n", + "test_data最大日期: 2024-10-22\n", + "划分后的训练集大小: 2381, 验证集大小: 474\n", + "train_data最大日期: 2024-10-16, 训练天数5\n", + "test_data最大日期: 2024-10-23\n", + "划分后的训练集大小: 2382, 验证集大小: 480\n", + "train_data最大日期: 2024-10-17, 训练天数5\n", + "test_data最大日期: 2024-10-24\n", + "划分后的训练集大小: 2377, 验证集大小: 472\n", + "train_data最大日期: 2024-10-18, 训练天数5\n", + "test_data最大日期: 2024-10-25\n", + "划分后的训练集大小: 2370, 验证集大小: 468\n", + "train_data最大日期: 2024-10-21, 训练天数5\n", + "test_data最大日期: 2024-10-28\n", + "划分后的训练集大小: 2351, 验证集大小: 457\n", + "train_data最大日期: 2024-10-22, 训练天数5\n", + "test_data最大日期: 2024-10-29\n", + "划分后的训练集大小: 2347, 验证集大小: 470\n", + "train_data最大日期: 2024-10-23, 训练天数5\n", + "test_data最大日期: 2024-10-30\n", + "划分后的训练集大小: 2336, 验证集大小: 469\n", + "train_data最大日期: 2024-10-24, 训练天数5\n", + "test_data最大日期: 2024-10-31\n", + "划分后的训练集大小: 2334, 验证集大小: 470\n", + "train_data最大日期: 2024-10-25, 训练天数5\n", + "test_data最大日期: 2024-11-01\n", + "划分后的训练集大小: 2341, 验证集大小: 475\n", + "train_data最大日期: 2024-10-28, 训练天数5\n", + "test_data最大日期: 2024-11-04\n", + "划分后的训练集大小: 2360, 验证集大小: 476\n", + "train_data最大日期: 2024-10-29, 训练天数5\n", + "test_data最大日期: 2024-11-05\n", + "划分后的训练集大小: 2357, 验证集大小: 467\n", + "train_data最大日期: 2024-10-30, 训练天数5\n", + "test_data最大日期: 2024-11-06\n", + "划分后的训练集大小: 2362, 验证集大小: 474\n", + "train_data最大日期: 2024-10-31, 训练天数5\n", + "test_data最大日期: 2024-11-07\n", + "划分后的训练集大小: 2368, 验证集大小: 476\n", + "train_data最大日期: 2024-11-01, 训练天数5\n", + "test_data最大日期: 2024-11-08\n", + "划分后的训练集大小: 2366, 验证集大小: 473\n", + "train_data最大日期: 2024-11-04, 训练天数5\n", + "test_data最大日期: 2024-11-11\n", + "划分后的训练集大小: 2366, 验证集大小: 476\n", + "train_data最大日期: 2024-11-05, 训练天数5\n", + "test_data最大日期: 2024-11-12\n", + "划分后的训练集大小: 2378, 验证集大小: 479\n", + "train_data最大日期: 2024-11-06, 训练天数5\n", + "test_data最大日期: 2024-11-13\n", + "划分后的训练集大小: 2380, 验证集大小: 476\n", + "train_data最大日期: 2024-11-07, 训练天数5\n", + "test_data最大日期: 2024-11-14\n", + "划分后的训练集大小: 2382, 验证集大小: 478\n", + "train_data最大日期: 2024-11-08, 训练天数5\n", + "test_data最大日期: 2024-11-15\n", + "划分后的训练集大小: 2386, 验证集大小: 477\n", + "train_data最大日期: 2024-11-11, 训练天数5\n", + "test_data最大日期: 2024-11-18\n", + "划分后的训练集大小: 2387, 验证集大小: 477\n", + "train_data最大日期: 2024-11-12, 训练天数5\n", + "test_data最大日期: 2024-11-19\n", + "划分后的训练集大小: 2389, 验证集大小: 481\n", + "train_data最大日期: 2024-11-13, 训练天数5\n", + "test_data最大日期: 2024-11-20\n", + "划分后的训练集大小: 2393, 验证集大小: 480\n", + "train_data最大日期: 2024-11-14, 训练天数5\n", + "test_data最大日期: 2024-11-21\n", + "划分后的训练集大小: 2394, 验证集大小: 479\n", + "train_data最大日期: 2024-11-15, 训练天数5\n", + "test_data最大日期: 2024-11-22\n", + "划分后的训练集大小: 2395, 验证集大小: 478\n", + "train_data最大日期: 2024-11-18, 训练天数5\n", + "test_data最大日期: 2024-11-25\n", + "划分后的训练集大小: 2382, 验证集大小: 464\n", + "train_data最大日期: 2024-11-19, 训练天数5\n", + "test_data最大日期: 2024-11-26\n", + "划分后的训练集大小: 2375, 验证集大小: 474\n", + "train_data最大日期: 2024-11-20, 训练天数5\n", + "test_data最大日期: 2024-11-27\n", + "划分后的训练集大小: 2370, 验证集大小: 475\n", + "train_data最大日期: 2024-11-21, 训练天数5\n", + "test_data最大日期: 2024-11-28\n", + "划分后的训练集大小: 2367, 验证集大小: 476\n", + "train_data最大日期: 2024-11-22, 训练天数5\n", + "test_data最大日期: 2024-11-29\n", + "划分后的训练集大小: 2361, 验证集大小: 472\n", + "train_data最大日期: 2024-11-25, 训练天数5\n", + "test_data最大日期: 2024-12-02\n", + "划分后的训练集大小: 2374, 验证集大小: 477\n", + "train_data最大日期: 2024-11-26, 训练天数5\n", + "test_data最大日期: 2024-12-03\n", + "划分后的训练集大小: 2374, 验证集大小: 474\n", + "train_data最大日期: 2024-11-27, 训练天数5\n", + "test_data最大日期: 2024-12-04\n", + "划分后的训练集大小: 2373, 验证集大小: 474\n", + "train_data最大日期: 2024-11-28, 训练天数5\n", + "test_data最大日期: 2024-12-05\n", + "划分后的训练集大小: 2371, 验证集大小: 474\n", + "train_data最大日期: 2024-11-29, 训练天数5\n", + "test_data最大日期: 2024-12-06\n", + "划分后的训练集大小: 2373, 验证集大小: 474\n", + "train_data最大日期: 2024-12-02, 训练天数5\n", + "test_data最大日期: 2024-12-09\n", + "划分后的训练集大小: 2375, 验证集大小: 479\n", + "train_data最大日期: 2024-12-03, 训练天数5\n", + "test_data最大日期: 2024-12-10\n", + "划分后的训练集大小: 2382, 验证集大小: 481\n", + "train_data最大日期: 2024-12-04, 训练天数5\n", + "test_data最大日期: 2024-12-11\n", + "划分后的训练集大小: 2381, 验证集大小: 473\n", + "train_data最大日期: 2024-12-05, 训练天数5\n", + "test_data最大日期: 2024-12-12\n", + "划分后的训练集大小: 2383, 验证集大小: 476\n", + "train_data最大日期: 2024-12-06, 训练天数5\n", + "test_data最大日期: 2024-12-13\n", + "划分后的训练集大小: 2380, 验证集大小: 471\n", + "train_data最大日期: 2024-12-09, 训练天数5\n", + "test_data最大日期: 2024-12-16\n", + "划分后的训练集大小: 2375, 验证集大小: 474\n", + "train_data最大日期: 2024-12-10, 训练天数5\n", + "test_data最大日期: 2024-12-17\n", + "划分后的训练集大小: 2375, 验证集大小: 481\n", + "train_data最大日期: 2024-12-11, 训练天数5\n", + "test_data最大日期: 2024-12-18\n", + "划分后的训练集大小: 2385, 验证集大小: 483\n", + "train_data最大日期: 2024-12-12, 训练天数5\n", + "test_data最大日期: 2024-12-19\n", + "划分后的训练集大小: 2394, 验证集大小: 485\n", + "train_data最大日期: 2024-12-13, 训练天数5\n", + "test_data最大日期: 2024-12-20\n", + "划分后的训练集大小: 2406, 验证集大小: 483\n", + "train_data最大日期: 2024-12-16, 训练天数5\n", + "test_data最大日期: 2024-12-23\n", + "划分后的训练集大小: 2399, 验证集大小: 467\n", + "train_data最大日期: 2024-12-17, 训练天数5\n", + "test_data最大日期: 2024-12-24\n", + "划分后的训练集大小: 2400, 验证集大小: 482\n", + "train_data最大日期: 2024-12-18, 训练天数5\n", + "test_data最大日期: 2024-12-25\n", + "划分后的训练集大小: 2386, 验证集大小: 469\n", + "train_data最大日期: 2024-12-19, 训练天数5\n", + "test_data最大日期: 2024-12-26\n", + "划分后的训练集大小: 2376, 验证集大小: 475\n", + "train_data最大日期: 2024-12-20, 训练天数5\n", + "test_data最大日期: 2024-12-27\n", + "划分后的训练集大小: 2374, 验证集大小: 481\n", + "train_data最大日期: 2024-12-23, 训练天数5\n", + "test_data最大日期: 2024-12-30\n", + "划分后的训练集大小: 2394, 验证集大小: 487\n", + "train_data最大日期: 2024-12-24, 训练天数5\n", + "test_data最大日期: 2024-12-31\n", + "划分后的训练集大小: 2396, 验证集大小: 484\n", + "train_data最大日期: 2024-12-25, 训练天数5\n", + "test_data最大日期: 2025-01-02\n", + "划分后的训练集大小: 2412, 验证集大小: 485\n", + "train_data最大日期: 2024-12-26, 训练天数5\n", + "test_data最大日期: 2025-01-03\n", + "划分后的训练集大小: 2418, 验证集大小: 481\n", + "train_data最大日期: 2024-12-27, 训练天数5\n", + "test_data最大日期: 2025-01-06\n", + "划分后的训练集大小: 2414, 验证集大小: 477\n", + "train_data最大日期: 2024-12-30, 训练天数5\n", + "test_data最大日期: 2025-01-07\n", + "划分后的训练集大小: 2406, 验证集大小: 479\n", + "train_data最大日期: 2024-12-31, 训练天数5\n", + "test_data最大日期: 2025-01-08\n", + "划分后的训练集大小: 2400, 验证集大小: 478\n", + "train_data最大日期: 2025-01-02, 训练天数5\n", + "test_data最大日期: 2025-01-09\n", + "划分后的训练集大小: 2398, 验证集大小: 483\n", + "train_data最大日期: 2025-01-03, 训练天数5\n", + "test_data最大日期: 2025-01-10\n", + "划分后的训练集大小: 2398, 验证集大小: 481\n", + "train_data最大日期: 2025-01-06, 训练天数5\n", + "test_data最大日期: 2025-01-13\n", + "划分后的训练集大小: 2400, 验证集大小: 479\n", + "train_data最大日期: 2025-01-07, 训练天数5\n", + "test_data最大日期: 2025-01-14\n", + "划分后的训练集大小: 2400, 验证集大小: 479\n", + "train_data最大日期: 2025-01-08, 训练天数5\n", + "test_data最大日期: 2025-01-15\n", + "划分后的训练集大小: 2406, 验证集大小: 484\n", + "train_data最大日期: 2025-01-09, 训练天数5\n", + "test_data最大日期: 2025-01-16\n", + "划分后的训练集大小: 2404, 验证集大小: 481\n", + "train_data最大日期: 2025-01-10, 训练天数5\n", + "test_data最大日期: 2025-01-17\n", + "划分后的训练集大小: 2400, 验证集大小: 477\n", + "train_data最大日期: 2025-01-13, 训练天数5\n", + "test_data最大日期: 2025-01-20\n", + "划分后的训练集大小: 2399, 验证集大小: 478\n", + "train_data最大日期: 2025-01-14, 训练天数5\n", + "test_data最大日期: 2025-01-21\n", + "划分后的训练集大小: 2400, 验证集大小: 480\n", + "train_data最大日期: 2025-01-15, 训练天数5\n", + "test_data最大日期: 2025-01-22\n", + "划分后的训练集大小: 2390, 验证集大小: 474\n", + "train_data最大日期: 2025-01-16, 训练天数5\n", + "test_data最大日期: 2025-01-23\n", + "划分后的训练集大小: 2377, 验证集大小: 468\n", + "train_data最大日期: 2025-01-17, 训练天数5\n", + "test_data最大日期: 2025-01-24\n", + "划分后的训练集大小: 2373, 验证集大小: 473\n", + "train_data最大日期: 2025-01-20, 训练天数5\n", + "test_data最大日期: 2025-01-27\n", + "划分后的训练集大小: 2371, 验证集大小: 476\n", + "train_data最大日期: 2025-01-21, 训练天数5\n", + "test_data最大日期: 2025-02-05\n", + "划分后的训练集大小: 2374, 验证集大小: 483\n", + "train_data最大日期: 2025-01-22, 训练天数5\n", + "test_data最大日期: 2025-02-06\n", + "划分后的训练集大小: 2385, 验证集大小: 485\n", + "train_data最大日期: 2025-01-23, 训练天数5\n", + "test_data最大日期: 2025-02-07\n", + "划分后的训练集大小: 2399, 验证集大小: 482\n", + "train_data最大日期: 2025-01-24, 训练天数5\n", + "test_data最大日期: 2025-02-10\n", + "划分后的训练集大小: 2404, 验证集大小: 478\n", + "train_data最大日期: 2025-01-27, 训练天数5\n", + "test_data最大日期: 2025-02-11\n", + "划分后的训练集大小: 2407, 验证集大小: 479\n", + "train_data最大日期: 2025-02-05, 训练天数5\n", + "test_data最大日期: 2025-02-12\n", + "划分后的训练集大小: 2407, 验证集大小: 483\n", + "train_data最大日期: 2025-02-06, 训练天数5\n", + "test_data最大日期: 2025-02-13\n", + "划分后的训练集大小: 2407, 验证集大小: 485\n", + "train_data最大日期: 2025-02-07, 训练天数5\n", + "test_data最大日期: 2025-02-14\n", + "划分后的训练集大小: 2412, 验证集大小: 487\n", + "train_data最大日期: 2025-02-10, 训练天数5\n", + "test_data最大日期: 2025-02-17\n", + "划分后的训练集大小: 2421, 验证集大小: 487\n", + "train_data最大日期: 2025-02-11, 训练天数5\n", + "test_data最大日期: 2025-02-18\n", + "划分后的训练集大小: 2426, 验证集大小: 484\n", + "train_data最大日期: 2025-02-12, 训练天数5\n", + "test_data最大日期: 2025-02-19\n", + "划分后的训练集大小: 2423, 验证集大小: 480\n", + "train_data最大日期: 2025-02-13, 训练天数5\n", + "test_data最大日期: 2025-02-20\n", + "划分后的训练集大小: 2421, 验证集大小: 483\n", + "train_data最大日期: 2025-02-14, 训练天数5\n", + "test_data最大日期: 2025-02-21\n", + "划分后的训练集大小: 2416, 验证集大小: 482\n", + "train_data最大日期: 2025-02-17, 训练天数5\n", + "test_data最大日期: 2025-02-24\n", + "划分后的训练集大小: 2413, 验证集大小: 484\n", + "train_data最大日期: 2025-02-18, 训练天数5\n", + "test_data最大日期: 2025-02-25\n", + "划分后的训练集大小: 2410, 验证集大小: 481\n", + "train_data最大日期: 2025-02-19, 训练天数5\n", + "test_data最大日期: 2025-02-26\n", + "划分后的训练集大小: 2411, 验证集大小: 481\n", + "train_data最大日期: 2025-02-20, 训练天数5\n", + "test_data最大日期: 2025-02-27\n", + "划分后的训练集大小: 2404, 验证集大小: 476\n", + "train_data最大日期: 2025-02-21, 训练天数5\n", + "test_data最大日期: 2025-02-28\n", + "划分后的训练集大小: 2406, 验证集大小: 484\n", + "train_data最大日期: 2025-02-24, 训练天数5\n", + "test_data最大日期: 2025-03-03\n", + "划分后的训练集大小: 2406, 验证集大小: 484\n", + "train_data最大日期: 2025-02-25, 训练天数5\n", + "test_data最大日期: 2025-03-04\n", + "划分后的训练集大小: 2407, 验证集大小: 482\n", + "train_data最大日期: 2025-02-26, 训练天数5\n", + "test_data最大日期: 2025-03-05\n", + "划分后的训练集大小: 2407, 验证集大小: 481\n", + "train_data最大日期: 2025-02-27, 训练天数5\n", + "test_data最大日期: 2025-03-06\n", + "划分后的训练集大小: 2412, 验证集大小: 481\n", + "train_data最大日期: 2025-02-28, 训练天数5\n", + "test_data最大日期: 2025-03-07\n", + "划分后的训练集大小: 2410, 验证集大小: 482\n", + "train_data最大日期: 2025-03-03, 训练天数5\n", + "test_data最大日期: 2025-03-10\n", + "划分后的训练集大小: 2408, 验证集大小: 482\n", + "train_data最大日期: 2025-03-04, 训练天数5\n", + "test_data最大日期: 2025-03-11\n", + "划分后的训练集大小: 2405, 验证集大小: 479\n", + "train_data最大日期: 2025-03-05, 训练天数5\n", + "test_data最大日期: 2025-03-12\n", + "划分后的训练集大小: 2403, 验证集大小: 479\n", + "train_data最大日期: 2025-03-06, 训练天数5\n", + "test_data最大日期: 2025-03-13\n", + "划分后的训练集大小: 2404, 验证集大小: 482\n", + "train_data最大日期: 2025-03-07, 训练天数5\n", + "test_data最大日期: 2025-03-14\n", + "划分后的训练集大小: 2405, 验证集大小: 483\n", + "train_data最大日期: 2025-03-10, 训练天数5\n", + "test_data最大日期: 2025-03-17\n", + "划分后的训练集大小: 2404, 验证集大小: 481\n", + "train_data最大日期: 2025-03-11, 训练天数5\n", + "test_data最大日期: 2025-03-18\n", + "划分后的训练集大小: 2406, 验证集大小: 481\n", + "train_data最大日期: 2025-03-12, 训练天数5\n", + "test_data最大日期: 2025-03-19\n", + "划分后的训练集大小: 2404, 验证集大小: 477\n", + "train_data最大日期: 2025-03-13, 训练天数5\n", + "test_data最大日期: 2025-03-20\n", + "划分后的训练集大小: 2401, 验证集大小: 479\n", + "train_data最大日期: 2025-03-14, 训练天数5\n", + "test_data最大日期: 2025-03-21\n", + "划分后的训练集大小: 2396, 验证集大小: 478\n", + "train_data最大日期: 2025-03-17, 训练天数5\n", + "test_data最大日期: 2025-03-24\n", + "划分后的训练集大小: 2397, 验证集大小: 482\n", + "train_data最大日期: 2025-03-18, 训练天数5\n", + "test_data最大日期: 2025-03-25\n", + "划分后的训练集大小: 2397, 验证集大小: 481\n", + "train_data最大日期: 2025-03-19, 训练天数5\n", + "test_data最大日期: 2025-03-26\n", + "划分后的训练集大小: 2402, 验证集大小: 482\n", + "train_data最大日期: 2025-03-20, 训练天数5\n", + "test_data最大日期: 2025-03-27\n", + "划分后的训练集大小: 2403, 验证集大小: 480\n", + "train_data最大日期: 2025-03-21, 训练天数5\n", + "test_data最大日期: 2025-03-28\n", + "划分后的训练集大小: 2403, 验证集大小: 478\n", + "train_data最大日期: 2025-03-24, 训练天数5\n", + "test_data最大日期: 2025-03-31\n", + "划分后的训练集大小: 2403, 验证集大小: 482\n", + "train_data最大日期: 2025-03-25, 训练天数5\n", + "test_data最大日期: 2025-04-01\n", + "划分后的训练集大小: 2404, 验证集大小: 482\n", + "train_data最大日期: 2025-03-26, 训练天数5\n", + "test_data最大日期: 2025-04-02\n", + "划分后的训练集大小: 2407, 验证集大小: 485\n", + "train_data最大日期: 2025-03-27, 训练天数5\n", + "test_data最大日期: 2025-04-03\n", + "划分后的训练集大小: 2413, 验证集大小: 486\n", + "train_data最大日期: 2025-03-28, 训练天数5\n", + "test_data最大日期: 2025-04-07\n", + "划分后的训练集大小: 2417, 验证集大小: 482\n", + "train_data最大日期: 2025-03-31, 训练天数5\n", + "test_data最大日期: 2025-04-08\n", + "划分后的训练集大小: 2373, 验证集大小: 438\n", + "train_data最大日期: 2025-04-01, 训练天数5\n", + "test_data最大日期: 2025-04-09\n", + "划分后的训练集大小: 2349, 验证集大小: 458\n" + ] + } + ], "source": [ "\n", "gc.collect()\n", @@ -1590,1698 +3391,14 @@ "# qdf = qdf[qdf['trade_date'] >= '2022-01-01']\n", "\n", "final_predictions = rolling_train_predict(\n", - " pdf[(pdf['trade_date'] >= '2022-12-01') & (pdf['trade_date'] <= '2029-03-26')], 3, 1, feature_columns,\n", + " pdf[(pdf['trade_date'] >= '2022-12-01') & (pdf['trade_date'] <= '2029-03-26')], 5, 1, feature_columns,\n", " days=days, validation_days=0, filter_index=filter_index, params=light_params)\n", "final_predictions.to_csv('predictions_test.tsv', index=False)\n" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "finish\n", - "train_data最大日期: 2022-12-27, 训练天数19\n", - "test_data最大日期: 2022-12-29\n", - "划分后的训练集大小: 5351, 验证集大小: 282\n", - "train_data最大日期: 2022-12-28, 训练天数20\n", - "test_data最大日期: 2022-12-30\n", - "划分后的训练集大小: 5631, 验证集大小: 280\n", - "train_data最大日期: 2022-12-29, 训练天数20\n", - "test_data最大日期: 2023-01-03\n", - "划分后的训练集大小: 5641, 验证集大小: 282\n", - "train_data最大日期: 2022-12-30, 训练天数20\n", - "test_data最大日期: 2023-01-04\n", - "划分后的训练集大小: 5650, 验证集大小: 287\n", - "train_data最大日期: 2023-01-03, 训练天数20\n", - "test_data最大日期: 2023-01-05\n", - "划分后的训练集大小: 5663, 验证集大小: 284\n", - "train_data最大日期: 2023-01-04, 训练天数20\n", - "test_data最大日期: 2023-01-06\n", - "划分后的训练集大小: 5663, 验证集大小: 280\n", - "train_data最大日期: 2023-01-05, 训练天数20\n", - "test_data最大日期: 2023-01-09\n", - "划分后的训练集大小: 5669, 验证集大小: 282\n", - "train_data最大日期: 2023-01-06, 训练天数20\n", - "test_data最大日期: 2023-01-10\n", - "划分后的训练集大小: 5671, 验证集大小: 284\n", - "train_data最大日期: 2023-01-09, 训练天数20\n", - "test_data最大日期: 2023-01-11\n", - "划分后的训练集大小: 5671, 验证集大小: 283\n", - "train_data最大日期: 2023-01-10, 训练天数20\n", - "test_data最大日期: 2023-01-12\n", - "划分后的训练集大小: 5685, 验证集大小: 292\n", - "train_data最大日期: 2023-01-11, 训练天数20\n", - "test_data最大日期: 2023-01-13\n", - "划分后的训练集大小: 5686, 验证集大小: 285\n", - "train_data最大日期: 2023-01-12, 训练天数20\n", - "test_data最大日期: 2023-01-16\n", - "划分后的训练集大小: 5680, 验证集大小: 284\n", - "train_data最大日期: 2023-01-13, 训练天数20\n", - "test_data最大日期: 2023-01-17\n", - "划分后的训练集大小: 5674, 验证集大小: 279\n", - "train_data最大日期: 2023-01-16, 训练天数20\n", - "test_data最大日期: 2023-01-18\n", - "划分后的训练集大小: 5669, 验证集大小: 277\n", - "train_data最大日期: 2023-01-17, 训练天数20\n", - "test_data最大日期: 2023-01-19\n", - "划分后的训练集大小: 5655, 验证集大小: 275\n", - "train_data最大日期: 2023-01-18, 训练天数20\n", - "test_data最大日期: 2023-01-20\n", - "划分后的训练集大小: 5649, 验证集大小: 280\n", - "train_data最大日期: 2023-01-19, 训练天数20\n", - "test_data最大日期: 2023-01-30\n", - "划分后的训练集大小: 5648, 验证集大小: 284\n", - "train_data最大日期: 2023-01-20, 训练天数20\n", - "test_data最大日期: 2023-01-31\n", - "划分后的训练集大小: 5656, 验证集大小: 292\n", - "train_data最大日期: 2023-01-30, 训练天数20\n", - "test_data最大日期: 2023-02-01\n", - "划分后的训练集大小: 5665, 验证集大小: 291\n", - "train_data最大日期: 2023-01-31, 训练天数20\n", - "test_data最大日期: 2023-02-02\n", - "划分后的训练集大小: 5662, 验证集大小: 279\n", - "train_data最大日期: 2023-02-01, 训练天数20\n", - "test_data最大日期: 2023-02-03\n", - "划分后的训练集大小: 5661, 验证集大小: 281\n", - "train_data最大日期: 2023-02-02, 训练天数20\n", - "test_data最大日期: 2023-02-06\n", - "划分后的训练集大小: 5667, 验证集大小: 286\n", - "train_data最大日期: 2023-02-03, 训练天数20\n", - "test_data最大日期: 2023-02-07\n", - "划分后的训练集大小: 5674, 验证集大小: 289\n", - "train_data最大日期: 2023-02-06, 训练天数20\n", - "test_data最大日期: 2023-02-08\n", - "划分后的训练集大小: 5680, 验证集大小: 293\n", - "train_data最大日期: 2023-02-07, 训练天数20\n", - "test_data最大日期: 2023-02-09\n", - "划分后的训练集大小: 5688, 验证集大小: 292\n", - "train_data最大日期: 2023-02-08, 训练天数20\n", - "test_data最大日期: 2023-02-10\n", - "划分后的训练集大小: 5696, 验证集大小: 288\n", - "train_data最大日期: 2023-02-09, 训练天数20\n", - "test_data最大日期: 2023-02-13\n", - "划分后的训练集大小: 5703, 验证集大小: 289\n", - "train_data最大日期: 2023-02-10, 训练天数20\n", - "test_data最大日期: 2023-02-14\n", - "划分后的训练集大小: 5707, 验证集大小: 288\n", - "train_data最大日期: 2023-02-13, 训练天数20\n", - "test_data最大日期: 2023-02-15\n", - "划分后的训练集大小: 5708, 验证集大小: 284\n", - "train_data最大日期: 2023-02-14, 训练天数20\n", - "test_data最大日期: 2023-02-16\n", - "划分后的训练集大小: 5701, 验证集大小: 285\n", - "train_data最大日期: 2023-02-15, 训练天数20\n", - "test_data最大日期: 2023-02-17\n", - "划分后的训练集大小: 5697, 验证集大小: 281\n", - "train_data最大日期: 2023-02-16, 训练天数20\n", - "test_data最大日期: 2023-02-20\n", - "划分后的训练集大小: 5701, 验证集大小: 288\n", - "train_data最大日期: 2023-02-17, 训练天数20\n", - "test_data最大日期: 2023-02-21\n", - "划分后的训练集大小: 5709, 验证集大小: 287\n", - "train_data最大日期: 2023-02-20, 训练天数20\n", - "test_data最大日期: 2023-02-22\n", - "划分后的训练集大小: 5720, 验证集大小: 288\n", - "train_data最大日期: 2023-02-21, 训练天数20\n", - "test_data最大日期: 2023-02-23\n", - "划分后的训练集大小: 5724, 验证集大小: 279\n", - "train_data最大日期: 2023-02-22, 训练天数20\n", - "test_data最大日期: 2023-02-24\n", - "划分后的训练集大小: 5733, 验证集大小: 289\n", - "train_data最大日期: 2023-02-23, 训练天数20\n", - "test_data最大日期: 2023-02-27\n", - "划分后的训练集大小: 5737, 验证集大小: 288\n", - "train_data最大日期: 2023-02-24, 训练天数20\n", - "test_data最大日期: 2023-02-28\n", - "划分后的训练集大小: 5734, 验证集大小: 289\n", - "train_data最大日期: 2023-02-27, 训练天数20\n", - "test_data最大日期: 2023-03-01\n", - "划分后的训练集大小: 5730, 验证集大小: 287\n", - "train_data最大日期: 2023-02-28, 训练天数20\n", - "test_data最大日期: 2023-03-02\n", - "划分后的训练集大小: 5736, 验证集大小: 285\n", - "train_data最大日期: 2023-03-01, 训练天数20\n", - "test_data最大日期: 2023-03-03\n", - "划分后的训练集大小: 5742, 验证集大小: 287\n", - "train_data最大日期: 2023-03-02, 训练天数20\n", - "test_data最大日期: 2023-03-06\n", - "划分后的训练集大小: 5740, 验证集大小: 284\n", - "train_data最大日期: 2023-03-03, 训练天数20\n", - "test_data最大日期: 2023-03-07\n", - "划分后的训练集大小: 5741, 验证集大小: 290\n", - "train_data最大日期: 2023-03-06, 训练天数20\n", - "test_data最大日期: 2023-03-08\n", - "划分后的训练集大小: 5739, 验证集大小: 291\n", - "train_data最大日期: 2023-03-07, 训练天数20\n", - "test_data最大日期: 2023-03-09\n", - "划分后的训练集大小: 5734, 验证集大小: 287\n", - "train_data最大日期: 2023-03-08, 训练天数20\n", - "test_data最大日期: 2023-03-10\n", - "划分后的训练集大小: 5730, 验证集大小: 284\n", - "train_data最大日期: 2023-03-09, 训练天数20\n", - "test_data最大日期: 2023-03-13\n", - "划分后的训练集大小: 5731, 验证集大小: 290\n", - "train_data最大日期: 2023-03-10, 训练天数20\n", - "test_data最大日期: 2023-03-14\n", - "划分后的训练集大小: 5723, 验证集大小: 280\n", - "train_data最大日期: 2023-03-13, 训练天数20\n", - "test_data最大日期: 2023-03-15\n", - "划分后的训练集大小: 5723, 验证集大小: 284\n", - "train_data最大日期: 2023-03-14, 训练天数20\n", - "test_data最大日期: 2023-03-16\n", - "划分后的训练集大小: 5724, 验证集大小: 286\n", - "train_data最大日期: 2023-03-15, 训练天数20\n", - "test_data最大日期: 2023-03-17\n", - "划分后的训练集大小: 5730, 验证集大小: 287\n", - "train_data最大日期: 2023-03-16, 训练天数20\n", - "test_data最大日期: 2023-03-20\n", - "划分后的训练集大小: 5729, 验证集大小: 287\n", - "train_data最大日期: 2023-03-17, 训练天数20\n", - "test_data最大日期: 2023-03-21\n", - "划分后的训练集大小: 5727, 验证集大小: 285\n", - "train_data最大日期: 2023-03-20, 训练天数20\n", - "test_data最大日期: 2023-03-22\n", - "划分后的训练集大小: 5723, 验证集大小: 284\n", - "train_data最大日期: 2023-03-21, 训练天数20\n", - "test_data最大日期: 2023-03-23\n", - "划分后的训练集大小: 5724, 验证集大小: 280\n", - "train_data最大日期: 2023-03-22, 训练天数20\n", - "test_data最大日期: 2023-03-24\n", - "划分后的训练集大小: 5718, 验证集大小: 283\n", - "train_data最大日期: 2023-03-23, 训练天数20\n", - "test_data最大日期: 2023-03-27\n", - "划分后的训练集大小: 5710, 验证集大小: 280\n", - "train_data最大日期: 2023-03-24, 训练天数20\n", - "test_data最大日期: 2023-03-28\n", - "划分后的训练集大小: 5706, 验证集大小: 285\n", - "train_data最大日期: 2023-03-27, 训练天数20\n", - "test_data最大日期: 2023-03-29\n", - "划分后的训练集大小: 5707, 验证集大小: 288\n", - "train_data最大日期: 2023-03-28, 训练天数20\n", - "test_data最大日期: 2023-03-30\n", - "划分后的训练集大小: 5710, 验证集大小: 288\n", - "train_data最大日期: 2023-03-29, 训练天数20\n", - "test_data最大日期: 2023-03-31\n", - "划分后的训练集大小: 5713, 验证集大小: 290\n", - "train_data最大日期: 2023-03-30, 训练天数20\n", - "test_data最大日期: 2023-04-03\n", - "划分后的训练集大小: 5716, 验证集大小: 287\n", - "train_data最大日期: 2023-03-31, 训练天数20\n", - "test_data最大日期: 2023-04-04\n", - "划分后的训练集大小: 5716, 验证集大小: 290\n", - "train_data最大日期: 2023-04-03, 训练天数20\n", - "test_data最大日期: 2023-04-06\n", - "划分后的训练集大小: 5714, 验证集大小: 289\n", - "train_data最大日期: 2023-04-04, 训练天数20\n", - "test_data最大日期: 2023-04-07\n", - "划分后的训练集大小: 5715, 验证集大小: 288\n", - "train_data最大日期: 2023-04-06, 训练天数20\n", - "test_data最大日期: 2023-04-10\n", - "划分后的训练集大小: 5722, 验证集大小: 291\n", - "train_data最大日期: 2023-04-07, 训练天数20\n", - "test_data最大日期: 2023-04-11\n", - "划分后的训练集大小: 5719, 验证集大小: 287\n", - "train_data最大日期: 2023-04-10, 训练天数20\n", - "test_data最大日期: 2023-04-12\n", - "划分后的训练集大小: 5725, 验证集大小: 286\n", - "train_data最大日期: 2023-04-11, 训练天数20\n", - "test_data最大日期: 2023-04-13\n", - "划分后的训练集大小: 5729, 验证集大小: 288\n", - "train_data最大日期: 2023-04-12, 训练天数20\n", - "test_data最大日期: 2023-04-14\n", - "划分后的训练集大小: 5737, 验证集大小: 294\n", - "train_data最大日期: 2023-04-13, 训练天数20\n", - "test_data最大日期: 2023-04-17\n", - "划分后的训练集大小: 5735, 验证集大小: 285\n", - "train_data最大日期: 2023-04-14, 训练天数20\n", - "test_data最大日期: 2023-04-18\n", - "划分后的训练集大小: 5738, 验证集大小: 290\n", - "train_data最大日期: 2023-04-17, 训练天数20\n", - "test_data最大日期: 2023-04-19\n", - "划分后的训练集大小: 5740, 验证集大小: 287\n", - "train_data最大日期: 2023-04-18, 训练天数20\n", - "test_data最大日期: 2023-04-20\n", - "划分后的训练集大小: 5745, 验证集大小: 289\n", - "train_data最大日期: 2023-04-19, 训练天数20\n", - "test_data最大日期: 2023-04-21\n", - "划分后的训练集大小: 5754, 验证集大小: 289\n", - "train_data最大日期: 2023-04-20, 训练天数20\n", - "test_data最大日期: 2023-04-24\n", - "划分后的训练集大小: 5759, 验证集大小: 288\n", - "train_data最大日期: 2023-04-21, 训练天数20\n", - "test_data最大日期: 2023-04-25\n", - "划分后的训练集大小: 5767, 验证集大小: 288\n", - "train_data最大日期: 2023-04-24, 训练天数20\n", - "test_data最大日期: 2023-04-26\n", - "划分后的训练集大小: 5768, 验证集大小: 286\n", - "train_data最大日期: 2023-04-25, 训练天数20\n", - "test_data最大日期: 2023-04-27\n", - "划分后的训练集大小: 5769, 验证集大小: 289\n", - "train_data最大日期: 2023-04-26, 训练天数20\n", - "test_data最大日期: 2023-04-28\n", - "划分后的训练集大小: 5775, 验证集大小: 294\n", - "train_data最大日期: 2023-04-27, 训练天数20\n", - "test_data最大日期: 2023-05-04\n", - "划分后的训练集大小: 5763, 验证集大小: 278\n", - "train_data最大日期: 2023-04-28, 训练天数20\n", - "test_data最大日期: 2023-05-05\n", - "划分后的训练集大小: 5767, 验证集大小: 291\n", - "train_data最大日期: 2023-05-04, 训练天数20\n", - "test_data最大日期: 2023-05-08\n", - "划分后的训练集大小: 5765, 验证集大小: 288\n", - "train_data最大日期: 2023-05-05, 训练天数20\n", - "test_data最大日期: 2023-05-09\n", - "划分后的训练集大小: 5755, 验证集大小: 279\n", - "train_data最大日期: 2023-05-08, 训练天数20\n", - "test_data最大日期: 2023-05-10\n", - "划分后的训练集大小: 5752, 验证集大小: 285\n", - "train_data最大日期: 2023-05-09, 训练天数20\n", - "test_data最大日期: 2023-05-11\n", - "划分后的训练集大小: 5737, 验证集大小: 276\n", - "train_data最大日期: 2023-05-10, 训练天数20\n", - "test_data最大日期: 2023-05-12\n", - "划分后的训练集大小: 5733, 验证集大小: 283\n", - "train_data最大日期: 2023-05-11, 训练天数20\n", - "test_data最大日期: 2023-05-15\n", - "划分后的训练集大小: 5730, 验证集大小: 283\n", - "train_data最大日期: 2023-05-12, 训练天数20\n", - "test_data最大日期: 2023-05-16\n", - "划分后的训练集大小: 5718, 验证集大小: 276\n", - "train_data最大日期: 2023-05-15, 训练天数20\n", - "test_data最大日期: 2023-05-17\n", - "划分后的训练集大小: 5708, 验证集大小: 284\n", - "train_data最大日期: 2023-05-16, 训练天数20\n", - "test_data最大日期: 2023-05-18\n", - "划分后的训练集大小: 5716, 验证集大小: 293\n", - "train_data最大日期: 2023-05-17, 训练天数20\n", - "test_data最大日期: 2023-05-19\n", - "划分后的训练集大小: 5715, 验证集大小: 289\n", - "train_data最大日期: 2023-05-18, 训练天数20\n", - "test_data最大日期: 2023-05-22\n", - "划分后的训练集大小: 5714, 验证集大小: 286\n", - "train_data最大日期: 2023-05-19, 训练天数20\n", - "test_data最大日期: 2023-05-23\n", - "划分后的训练集大小: 5714, 验证集大小: 289\n", - "train_data最大日期: 2023-05-22, 训练天数20\n", - "test_data最大日期: 2023-05-24\n", - "划分后的训练集大小: 5710, 验证集大小: 285\n", - "train_data最大日期: 2023-05-23, 训练天数20\n", - "test_data最大日期: 2023-05-25\n", - "划分后的训练集大小: 5700, 验证集大小: 278\n", - "train_data最大日期: 2023-05-24, 训练天数20\n", - "test_data最大日期: 2023-05-26\n", - "划分后的训练集大小: 5695, 验证集大小: 283\n", - "train_data最大日期: 2023-05-25, 训练天数20\n", - "test_data最大日期: 2023-05-29\n", - "划分后的训练集大小: 5691, 验证集大小: 282\n", - "train_data最大日期: 2023-05-26, 训练天数20\n", - "test_data最大日期: 2023-05-30\n", - "划分后的训练集大小: 5688, 验证集大小: 286\n", - "train_data最大日期: 2023-05-29, 训练天数20\n", - "test_data最大日期: 2023-05-31\n", - "划分后的训练集大小: 5674, 验证集大小: 280\n", - "train_data最大日期: 2023-05-30, 训练天数20\n", - "test_data最大日期: 2023-06-01\n", - "划分后的训练集大小: 5679, 验证集大小: 283\n", - "train_data最大日期: 2023-05-31, 训练天数20\n", - "test_data最大日期: 2023-06-02\n", - "划分后的训练集大小: 5667, 验证集大小: 279\n", - "train_data最大日期: 2023-06-01, 训练天数20\n", - "test_data最大日期: 2023-06-05\n", - "划分后的训练集大小: 5668, 验证集大小: 289\n", - "train_data最大日期: 2023-06-02, 训练天数20\n", - "test_data最大日期: 2023-06-06\n", - "划分后的训练集大小: 5676, 验证集大小: 287\n", - "train_data最大日期: 2023-06-05, 训练天数20\n", - "test_data最大日期: 2023-06-07\n", - "划分后的训练集大小: 5674, 验证集大小: 283\n", - "train_data最大日期: 2023-06-06, 训练天数20\n", - "test_data最大日期: 2023-06-08\n", - "划分后的训练集大小: 5677, 验证集大小: 279\n", - "train_data最大日期: 2023-06-07, 训练天数20\n", - "test_data最大日期: 2023-06-09\n", - "划分后的训练集大小: 5675, 验证集大小: 281\n", - "train_data最大日期: 2023-06-08, 训练天数20\n", - "test_data最大日期: 2023-06-12\n", - "划分后的训练集大小: 5674, 验证集大小: 282\n", - "train_data最大日期: 2023-06-09, 训练天数20\n", - "test_data最大日期: 2023-06-13\n", - "划分后的训练集大小: 5690, 验证集大小: 292\n", - "train_data最大日期: 2023-06-12, 训练天数20\n", - "test_data最大日期: 2023-06-14\n", - "划分后的训练集大小: 5689, 验证集大小: 283\n", - "train_data最大日期: 2023-06-13, 训练天数20\n", - "test_data最大日期: 2023-06-15\n", - "划分后的训练集大小: 5674, 验证集大小: 278\n", - "train_data最大日期: 2023-06-14, 训练天数20\n", - "test_data最大日期: 2023-06-16\n", - "划分后的训练集大小: 5671, 验证集大小: 286\n", - "train_data最大日期: 2023-06-15, 训练天数20\n", - "test_data最大日期: 2023-06-19\n", - "划分后的训练集大小: 5673, 验证集大小: 288\n", - "train_data最大日期: 2023-06-16, 训练天数20\n", - "test_data最大日期: 2023-06-20\n", - "划分后的训练集大小: 5672, 验证集大小: 288\n", - "train_data最大日期: 2023-06-19, 训练天数20\n", - "test_data最大日期: 2023-06-21\n", - "划分后的训练集大小: 5671, 验证集大小: 284\n", - "train_data最大日期: 2023-06-20, 训练天数20\n", - "test_data最大日期: 2023-06-26\n", - "划分后的训练集大小: 5675, 验证集大小: 282\n", - "train_data最大日期: 2023-06-21, 训练天数20\n", - "test_data最大日期: 2023-06-27\n", - "划分后的训练集大小: 5684, 验证集大小: 292\n", - "train_data最大日期: 2023-06-26, 训练天数20\n", - "test_data最大日期: 2023-06-28\n", - "划分后的训练集大小: 5688, 验证集大小: 286\n", - "train_data最大日期: 2023-06-27, 训练天数20\n", - "test_data最大日期: 2023-06-29\n", - "划分后的训练集大小: 5687, 验证集大小: 285\n", - "train_data最大日期: 2023-06-28, 训练天数20\n", - "test_data最大日期: 2023-06-30\n", - "划分后的训练集大小: 5697, 验证集大小: 290\n", - "train_data最大日期: 2023-06-29, 训练天数20\n", - "test_data最大日期: 2023-07-03\n", - "划分后的训练集大小: 5699, 验证集大小: 285\n", - "train_data最大日期: 2023-06-30, 训练天数20\n", - "test_data最大日期: 2023-07-04\n", - "划分后的训练集大小: 5708, 验证集大小: 288\n", - "train_data最大日期: 2023-07-03, 训练天数20\n", - "test_data最大日期: 2023-07-05\n", - "划分后的训练集大小: 5708, 验证集大小: 289\n", - "train_data最大日期: 2023-07-04, 训练天数20\n", - "test_data最大日期: 2023-07-06\n", - "划分后的训练集大小: 5709, 验证集大小: 288\n", - "train_data最大日期: 2023-07-05, 训练天数20\n", - "test_data最大日期: 2023-07-07\n", - "划分后的训练集大小: 5710, 验证集大小: 284\n", - "train_data最大日期: 2023-07-06, 训练天数20\n", - "test_data最大日期: 2023-07-10\n", - "划分后的训练集大小: 5721, 验证集大小: 290\n", - "train_data最大日期: 2023-07-07, 训练天数20\n", - "test_data最大日期: 2023-07-11\n", - "划分后的训练集大小: 5732, 验证集大小: 292\n", - "train_data最大日期: 2023-07-10, 训练天数20\n", - "test_data最大日期: 2023-07-12\n", - "划分后的训练集大小: 5739, 验证集大小: 289\n", - "train_data最大日期: 2023-07-11, 训练天数20\n", - "test_data最大日期: 2023-07-13\n", - "划分后的训练集大小: 5733, 验证集大小: 286\n", - "train_data最大日期: 2023-07-12, 训练天数20\n", - "test_data最大日期: 2023-07-14\n", - "划分后的训练集大小: 5738, 验证集大小: 288\n", - "train_data最大日期: 2023-07-13, 训练天数20\n", - "test_data最大日期: 2023-07-17\n", - "划分后的训练集大小: 5752, 验证集大小: 292\n", - "train_data最大日期: 2023-07-14, 训练天数20\n", - "test_data最大日期: 2023-07-18\n", - "划分后的训练集大小: 5754, 验证集大小: 288\n", - "train_data最大日期: 2023-07-17, 训练天数20\n", - "test_data最大日期: 2023-07-19\n", - "划分后的训练集大小: 5754, 验证集大小: 288\n", - "train_data最大日期: 2023-07-18, 训练天数20\n", - "test_data最大日期: 2023-07-20\n", - "划分后的训练集大小: 5753, 验证集大小: 287\n", - "train_data最大日期: 2023-07-19, 训练天数20\n", - "test_data最大日期: 2023-07-21\n", - "划分后的训练集大小: 5756, 验证集大小: 287\n", - "train_data最大日期: 2023-07-20, 训练天数20\n", - "test_data最大日期: 2023-07-24\n", - "划分后的训练集大小: 5755, 验证集大小: 281\n", - "train_data最大日期: 2023-07-21, 训练天数20\n", - "test_data最大日期: 2023-07-25\n", - "划分后的训练集大小: 5740, 验证集大小: 277\n", - "train_data最大日期: 2023-07-24, 训练天数20\n", - "test_data最大日期: 2023-07-26\n", - "划分后的训练集大小: 5736, 验证集大小: 282\n", - "train_data最大日期: 2023-07-25, 训练天数20\n", - "test_data最大日期: 2023-07-27\n", - "划分后的训练集大小: 5735, 验证集大小: 284\n", - "train_data最大日期: 2023-07-26, 训练天数20\n", - "test_data最大日期: 2023-07-28\n", - "划分后的训练集大小: 5729, 验证集大小: 284\n", - "train_data最大日期: 2023-07-27, 训练天数20\n", - "test_data最大日期: 2023-07-31\n", - "划分后的训练集大小: 5721, 验证集大小: 277\n", - "train_data最大日期: 2023-07-28, 训练天数20\n", - "test_data最大日期: 2023-08-01\n", - "划分后的训练集大小: 5725, 验证集大小: 292\n", - "train_data最大日期: 2023-07-31, 训练天数20\n", - "test_data最大日期: 2023-08-02\n", - "划分后的训练集大小: 5722, 验证集大小: 286\n", - "train_data最大日期: 2023-08-01, 训练天数20\n", - "test_data最大日期: 2023-08-03\n", - "划分后的训练集大小: 5721, 验证集大小: 287\n", - "train_data最大日期: 2023-08-02, 训练天数20\n", - "test_data最大日期: 2023-08-04\n", - "划分后的训练集大小: 5730, 验证集大小: 293\n", - "train_data最大日期: 2023-08-03, 训练天数20\n", - "test_data最大日期: 2023-08-07\n", - "划分后的训练集大小: 5729, 验证集大小: 289\n", - "train_data最大日期: 2023-08-04, 训练天数20\n", - "test_data最大日期: 2023-08-08\n", - "划分后的训练集大小: 5731, 验证集大小: 294\n", - "train_data最大日期: 2023-08-07, 训练天数20\n", - "test_data最大日期: 2023-08-09\n", - "划分后的训练集大小: 5735, 验证集大小: 293\n", - "train_data最大日期: 2023-08-08, 训练天数20\n", - "test_data最大日期: 2023-08-10\n", - "划分后的训练集大小: 5740, 验证集大小: 291\n", - "train_data最大日期: 2023-08-09, 训练天数20\n", - "test_data最大日期: 2023-08-11\n", - "划分后的训练集大小: 5745, 验证集大小: 293\n", - "train_data最大日期: 2023-08-10, 训练天数20\n", - "test_data最大日期: 2023-08-14\n", - "划分后的训练集大小: 5736, 验证集大小: 283\n", - "train_data最大日期: 2023-08-11, 训练天数20\n", - "test_data最大日期: 2023-08-15\n", - "划分后的训练集大小: 5728, 验证集大小: 280\n", - "train_data最大日期: 2023-08-14, 训练天数20\n", - "test_data最大日期: 2023-08-16\n", - "划分后的训练集大小: 5723, 验证集大小: 283\n", - "train_data最大日期: 2023-08-15, 训练天数20\n", - "test_data最大日期: 2023-08-17\n", - "划分后的训练集大小: 5719, 验证集大小: 283\n", - "train_data最大日期: 2023-08-16, 训练天数20\n", - "test_data最大日期: 2023-08-18\n", - "划分后的训练集大小: 5716, 验证集大小: 284\n", - "train_data最大日期: 2023-08-17, 训练天数20\n", - "test_data最大日期: 2023-08-21\n", - "划分后的训练集大小: 5713, 验证集大小: 278\n", - "train_data最大日期: 2023-08-18, 训练天数20\n", - "test_data最大日期: 2023-08-22\n", - "划分后的训练集大小: 5718, 验证集大小: 282\n", - "train_data最大日期: 2023-08-21, 训练天数20\n", - "test_data最大日期: 2023-08-23\n", - "划分后的训练集大小: 5718, 验证集大小: 282\n", - "train_data最大日期: 2023-08-22, 训练天数20\n", - "test_data最大日期: 2023-08-24\n", - "划分后的训练集大小: 5719, 验证集大小: 285\n", - "train_data最大日期: 2023-08-23, 训练天数20\n", - "test_data最大日期: 2023-08-25\n", - "划分后的训练集大小: 5710, 验证集大小: 275\n", - "train_data最大日期: 2023-08-24, 训练天数20\n", - "test_data最大日期: 2023-08-28\n", - "划分后的训练集大小: 5717, 验证集大小: 284\n", - "train_data最大日期: 2023-08-25, 训练天数20\n", - "test_data最大日期: 2023-08-29\n", - "划分后的训练集大小: 5719, 验证集大小: 294\n", - "train_data最大日期: 2023-08-28, 训练天数20\n", - "test_data最大日期: 2023-08-30\n", - "划分后的训练集大小: 5726, 验证集大小: 293\n", - "train_data最大日期: 2023-08-29, 训练天数20\n", - "test_data最大日期: 2023-08-31\n", - "划分后的训练集大小: 5730, 验证集大小: 291\n", - "train_data最大日期: 2023-08-30, 训练天数20\n", - "test_data最大日期: 2023-09-01\n", - "划分后的训练集大小: 5718, 验证集大小: 281\n", - "train_data最大日期: 2023-08-31, 训练天数20\n", - "test_data最大日期: 2023-09-04\n", - "划分后的训练集大小: 5721, 验证集大小: 292\n", - "train_data最大日期: 2023-09-01, 训练天数20\n", - "test_data最大日期: 2023-09-05\n", - "划分后的训练集大小: 5717, 验证集大小: 290\n", - "train_data最大日期: 2023-09-04, 训练天数20\n", - "test_data最大日期: 2023-09-06\n", - "划分后的训练集大小: 5717, 验证集大小: 293\n", - "train_data最大日期: 2023-09-05, 训练天数20\n", - "test_data最大日期: 2023-09-07\n", - "划分后的训练集大小: 5717, 验证集大小: 291\n", - "train_data最大日期: 2023-09-06, 训练天数20\n", - "test_data最大日期: 2023-09-08\n", - "划分后的训练集大小: 5714, 验证集大小: 290\n", - "train_data最大日期: 2023-09-07, 训练天数20\n", - "test_data最大日期: 2023-09-11\n", - "划分后的训练集大小: 5723, 验证集大小: 292\n", - "train_data最大日期: 2023-09-08, 训练天数20\n", - "test_data最大日期: 2023-09-12\n", - "划分后的训练集大小: 5734, 验证集大小: 291\n", - "train_data最大日期: 2023-09-11, 训练天数20\n", - "test_data最大日期: 2023-09-13\n", - "划分后的训练集大小: 5741, 验证集大小: 290\n", - "train_data最大日期: 2023-09-12, 训练天数20\n", - "test_data最大日期: 2023-09-14\n", - "划分后的训练集大小: 5751, 验证集大小: 293\n", - "train_data最大日期: 2023-09-13, 训练天数20\n", - "test_data最大日期: 2023-09-15\n", - "划分后的训练集大小: 5759, 验证集大小: 292\n", - "train_data最大日期: 2023-09-14, 训练天数20\n", - "test_data最大日期: 2023-09-18\n", - "划分后的训练集大小: 5775, 验证集大小: 294\n", - "train_data最大日期: 2023-09-15, 训练天数20\n", - "test_data最大日期: 2023-09-19\n", - "划分后的训练集大小: 5780, 验证集大小: 287\n", - "train_data最大日期: 2023-09-18, 训练天数20\n", - "test_data最大日期: 2023-09-20\n", - "划分后的训练集大小: 5787, 验证集大小: 289\n", - "train_data最大日期: 2023-09-19, 训练天数20\n", - "test_data最大日期: 2023-09-21\n", - "划分后的训练集大小: 5790, 验证集大小: 288\n", - "train_data最大日期: 2023-09-20, 训练天数20\n", - "test_data最大日期: 2023-09-22\n", - "划分后的训练集大小: 5805, 验证集大小: 290\n", - "train_data最大日期: 2023-09-21, 训练天数20\n", - "test_data最大日期: 2023-09-25\n", - "划分后的训练集大小: 5802, 验证集大小: 281\n", - "train_data最大日期: 2023-09-22, 训练天数20\n", - "test_data最大日期: 2023-09-26\n", - "划分后的训练集大小: 5798, 验证集大小: 290\n", - "train_data最大日期: 2023-09-25, 训练天数20\n", - "test_data最大日期: 2023-09-27\n", - "划分后的训练集大小: 5790, 验证集大小: 285\n", - "train_data最大日期: 2023-09-26, 训练天数20\n", - "test_data最大日期: 2023-09-28\n", - "划分后的训练集大小: 5783, 验证集大小: 284\n", - "train_data最大日期: 2023-09-27, 训练天数20\n", - "test_data最大日期: 2023-10-09\n", - "划分后的训练集大小: 5791, 验证集大小: 289\n", - "train_data最大日期: 2023-09-28, 训练天数20\n", - "test_data最大日期: 2023-10-10\n", - "划分后的训练集大小: 5790, 验证集大小: 291\n", - "train_data最大日期: 2023-10-09, 训练天数20\n", - "test_data最大日期: 2023-10-11\n", - "划分后的训练集大小: 5791, 验证集大小: 291\n", - "train_data最大日期: 2023-10-10, 训练天数20\n", - "test_data最大日期: 2023-10-12\n", - "划分后的训练集大小: 5786, 验证集大小: 288\n", - "train_data最大日期: 2023-10-11, 训练天数20\n", - "test_data最大日期: 2023-10-13\n", - "划分后的训练集大小: 5781, 验证集大小: 286\n", - "train_data最大日期: 2023-10-12, 训练天数20\n", - "test_data最大日期: 2023-10-16\n", - "划分后的训练集大小: 5779, 验证集大小: 288\n", - "train_data最大日期: 2023-10-13, 训练天数20\n", - "test_data最大日期: 2023-10-17\n", - "划分后的训练集大小: 5777, 验证集大小: 290\n", - "train_data最大日期: 2023-10-16, 训练天数20\n", - "test_data最大日期: 2023-10-18\n", - "划分后的训练集大小: 5772, 验证集大小: 286\n", - "train_data最大日期: 2023-10-17, 训练天数20\n", - "test_data最大日期: 2023-10-19\n", - "划分后的训练集大小: 5764, 验证集大小: 282\n", - "train_data最大日期: 2023-10-18, 训练天数20\n", - "test_data最大日期: 2023-10-20\n", - "划分后的训练集大小: 5746, 验证集大小: 275\n", - "train_data最大日期: 2023-10-19, 训练天数20\n", - "test_data最大日期: 2023-10-23\n", - "划分后的训练集大小: 5740, 验证集大小: 286\n", - "train_data最大日期: 2023-10-20, 训练天数20\n", - "test_data最大日期: 2023-10-24\n", - "划分后的训练集大小: 5735, 验证集大小: 289\n", - "train_data最大日期: 2023-10-23, 训练天数20\n", - "test_data最大日期: 2023-10-25\n", - "划分后的训练集大小: 5736, 验证集大小: 288\n", - "train_data最大日期: 2023-10-24, 训练天数20\n", - "test_data最大日期: 2023-10-26\n", - "划分后的训练集大小: 5734, 验证集大小: 287\n", - "train_data最大日期: 2023-10-25, 训练天数20\n", - "test_data最大日期: 2023-10-27\n", - "划分后的训练集大小: 5732, 验证集大小: 286\n", - "train_data最大日期: 2023-10-26, 训练天数20\n", - "test_data最大日期: 2023-10-30\n", - "划分后的训练集大小: 5724, 验证集大小: 282\n", - "train_data最大日期: 2023-10-27, 训练天数20\n", - "test_data最大日期: 2023-10-31\n", - "划分后的训练集大小: 5731, 验证集大小: 288\n", - "train_data最大日期: 2023-10-30, 训练天数20\n", - "test_data最大日期: 2023-11-01\n", - "划分后的训练集大小: 5733, 验证集大小: 292\n", - "train_data最大日期: 2023-10-31, 训练天数20\n", - "test_data最大日期: 2023-11-02\n", - "划分后的训练集大小: 5736, 验证集大小: 288\n", - "train_data最大日期: 2023-11-01, 训练天数20\n", - "test_data最大日期: 2023-11-03\n", - "划分后的训练集大小: 5745, 验证集大小: 293\n", - "train_data最大日期: 2023-11-02, 训练天数20\n", - "test_data最大日期: 2023-11-06\n", - "划分后的训练集大小: 5748, 验证集大小: 292\n", - "train_data最大日期: 2023-11-03, 训练天数20\n", - "test_data最大日期: 2023-11-07\n", - "划分后的训练集大小: 5747, 验证集大小: 290\n", - "train_data最大日期: 2023-11-06, 训练天数20\n", - "test_data最大日期: 2023-11-08\n", - "划分后的训练集大小: 5749, 验证集大小: 293\n", - "train_data最大日期: 2023-11-07, 训练天数20\n", - "test_data最大日期: 2023-11-09\n", - "划分后的训练集大小: 5743, 验证集大小: 282\n", - "train_data最大日期: 2023-11-08, 训练天数20\n", - "test_data最大日期: 2023-11-10\n", - "划分后的训练集大小: 5739, 验证集大小: 282\n", - "train_data最大日期: 2023-11-09, 训练天数20\n", - "test_data最大日期: 2023-11-13\n", - "划分后的训练集大小: 5740, 验证集大小: 289\n", - "train_data最大日期: 2023-11-10, 训练天数20\n", - "test_data最大日期: 2023-11-14\n", - "划分后的训练集大小: 5741, 验证集大小: 291\n", - "train_data最大日期: 2023-11-13, 训练天数20\n", - "test_data最大日期: 2023-11-15\n", - "划分后的训练集大小: 5745, 验证集大小: 290\n", - "train_data最大日期: 2023-11-14, 训练天数20\n", - "test_data最大日期: 2023-11-16\n", - "划分后的训练集大小: 5753, 验证集大小: 290\n", - "train_data最大日期: 2023-11-15, 训练天数20\n", - "test_data最大日期: 2023-11-17\n", - "划分后的训练集大小: 5760, 验证集大小: 282\n", - "train_data最大日期: 2023-11-16, 训练天数20\n", - "test_data最大日期: 2023-11-20\n", - "划分后的训练集大小: 5754, 验证集大小: 280\n", - "train_data最大日期: 2023-11-17, 训练天数20\n", - "test_data最大日期: 2023-11-21\n", - "划分后的训练集大小: 5750, 验证集大小: 285\n", - "train_data最大日期: 2023-11-20, 训练天数20\n", - "test_data最大日期: 2023-11-22\n", - "划分后的训练集大小: 5752, 验证集大小: 290\n", - "train_data最大日期: 2023-11-21, 训练天数20\n", - "test_data最大日期: 2023-11-23\n", - "划分后的训练集大小: 5753, 验证集大小: 288\n", - "train_data最大日期: 2023-11-22, 训练天数20\n", - "test_data最大日期: 2023-11-24\n", - "划分后的训练集大小: 5745, 验证集大小: 278\n", - "train_data最大日期: 2023-11-23, 训练天数20\n", - "test_data最大日期: 2023-11-27\n", - "划分后的训练集大小: 5750, 验证集大小: 287\n", - "train_data最大日期: 2023-11-24, 训练天数20\n", - "test_data最大日期: 2023-11-28\n", - "划分后的训练集大小: 5752, 验证集大小: 290\n", - "train_data最大日期: 2023-11-27, 训练天数20\n", - "test_data最大日期: 2023-11-29\n", - "划分后的训练集大小: 5752, 验证集大小: 292\n", - "train_data最大日期: 2023-11-28, 训练天数20\n", - "test_data最大日期: 2023-11-30\n", - "划分后的训练集大小: 5757, 验证集大小: 293\n", - "train_data最大日期: 2023-11-29, 训练天数20\n", - "test_data最大日期: 2023-12-01\n", - "划分后的训练集大小: 5758, 验证集大小: 294\n", - "train_data最大日期: 2023-11-30, 训练天数20\n", - "test_data最大日期: 2023-12-04\n", - "划分后的训练集大小: 5759, 验证集大小: 293\n", - "train_data最大日期: 2023-12-01, 训练天数20\n", - "test_data最大日期: 2023-12-05\n", - "划分后的训练集大小: 5762, 验证集大小: 293\n", - "train_data最大日期: 2023-12-04, 训练天数20\n", - "test_data最大日期: 2023-12-06\n", - "划分后的训练集大小: 5757, 验证集大小: 288\n", - "train_data最大日期: 2023-12-05, 训练天数20\n", - "test_data最大日期: 2023-12-07\n", - "划分后的训练集大小: 5761, 验证集大小: 286\n", - "train_data最大日期: 2023-12-06, 训练天数20\n", - "test_data最大日期: 2023-12-08\n", - "划分后的训练集大小: 5757, 验证集大小: 278\n", - "train_data最大日期: 2023-12-07, 训练天数20\n", - "test_data最大日期: 2023-12-11\n", - "划分后的训练集大小: 5752, 验证集大小: 284\n", - "train_data最大日期: 2023-12-08, 训练天数20\n", - "test_data最大日期: 2023-12-12\n", - "划分后的训练集大小: 5752, 验证集大小: 291\n", - "train_data最大日期: 2023-12-11, 训练天数20\n", - "test_data最大日期: 2023-12-13\n", - "划分后的训练集大小: 5751, 验证集大小: 289\n", - "train_data最大日期: 2023-12-12, 训练天数20\n", - "test_data最大日期: 2023-12-14\n", - "划分后的训练集大小: 5749, 验证集大小: 288\n", - "train_data最大日期: 2023-12-13, 训练天数20\n", - "test_data最大日期: 2023-12-15\n", - "划分后的训练集大小: 5754, 验证集大小: 287\n", - "train_data最大日期: 2023-12-14, 训练天数20\n", - "test_data最大日期: 2023-12-18\n", - "划分后的训练集大小: 5759, 验证集大小: 285\n", - "train_data最大日期: 2023-12-15, 训练天数20\n", - "test_data最大日期: 2023-12-19\n", - "划分后的训练集大小: 5758, 验证集大小: 284\n", - "train_data最大日期: 2023-12-18, 训练天数20\n", - "test_data最大日期: 2023-12-20\n", - "划分后的训练集大小: 5755, 验证集大小: 287\n", - "train_data最大日期: 2023-12-19, 训练天数20\n", - "test_data最大日期: 2023-12-21\n", - "划分后的训练集大小: 5756, 验证集大小: 289\n", - "train_data最大日期: 2023-12-20, 训练天数20\n", - "test_data最大日期: 2023-12-22\n", - "划分后的训练集大小: 5762, 验证集大小: 284\n", - "train_data最大日期: 2023-12-21, 训练天数20\n", - "test_data最大日期: 2023-12-25\n", - "划分后的训练集大小: 5748, 验证集大小: 273\n", - "train_data最大日期: 2023-12-22, 训练天数20\n", - "test_data最大日期: 2023-12-26\n", - "划分后的训练集大小: 5739, 验证集大小: 281\n", - "train_data最大日期: 2023-12-25, 训练天数20\n", - "test_data最大日期: 2023-12-27\n", - "划分后的训练集大小: 5736, 验证集大小: 289\n", - "train_data最大日期: 2023-12-26, 训练天数20\n", - "test_data最大日期: 2023-12-28\n", - "划分后的训练集大小: 5729, 验证集大小: 286\n", - "train_data最大日期: 2023-12-27, 训练天数20\n", - "test_data最大日期: 2023-12-29\n", - "划分后的训练集大小: 5718, 验证集大小: 283\n", - "train_data最大日期: 2023-12-28, 训练天数20\n", - "test_data最大日期: 2024-01-02\n", - "划分后的训练集大小: 5711, 验证集大小: 286\n", - "train_data最大日期: 2023-12-29, 训练天数20\n", - "test_data最大日期: 2024-01-03\n", - "划分后的训练集大小: 5707, 验证集大小: 289\n", - "train_data最大日期: 2024-01-02, 训练天数20\n", - "test_data最大日期: 2024-01-04\n", - "划分后的训练集大小: 5702, 验证集大小: 283\n", - "train_data最大日期: 2024-01-03, 训练天数20\n", - "test_data最大日期: 2024-01-05\n", - "划分后的训练集大小: 5698, 验证集大小: 282\n", - "train_data最大日期: 2024-01-04, 训练天数20\n", - "test_data最大日期: 2024-01-08\n", - "划分后的训练集大小: 5703, 验证集大小: 283\n", - "train_data最大日期: 2024-01-05, 训练天数20\n", - "test_data最大日期: 2024-01-09\n", - "划分后的训练集大小: 5699, 验证集大小: 280\n", - "train_data最大日期: 2024-01-08, 训练天数20\n", - "test_data最大日期: 2024-01-10\n", - "划分后的训练集大小: 5689, 验证集大小: 281\n", - "train_data最大日期: 2024-01-09, 训练天数20\n", - "test_data最大日期: 2024-01-11\n", - "划分后的训练集大小: 5685, 验证集大小: 285\n", - "train_data最大日期: 2024-01-10, 训练天数20\n", - "test_data最大日期: 2024-01-12\n", - "划分后的训练集大小: 5677, 验证集大小: 280\n", - "train_data最大日期: 2024-01-11, 训练天数20\n", - "test_data最大日期: 2024-01-15\n", - "划分后的训练集大小: 5668, 验证集大小: 278\n", - "train_data最大日期: 2024-01-12, 训练天数20\n", - "test_data最大日期: 2024-01-16\n", - "划分后的训练集大小: 5667, 验证集大小: 284\n", - "train_data最大日期: 2024-01-15, 训练天数20\n", - "test_data最大日期: 2024-01-17\n", - "划分后的训练集大小: 5662, 验证集大小: 279\n", - "train_data最大日期: 2024-01-16, 训练天数20\n", - "test_data最大日期: 2024-01-18\n", - "划分后的训练集大小: 5659, 验证集大小: 284\n", - "train_data最大日期: 2024-01-17, 训练天数20\n", - "test_data最大日期: 2024-01-19\n", - "划分后的训练集大小: 5657, 验证集大小: 287\n", - "train_data最大日期: 2024-01-18, 训练天数20\n", - "test_data最大日期: 2024-01-22\n", - "划分后的训练集大小: 5650, 验证集大小: 277\n", - "train_data最大日期: 2024-01-19, 训练天数20\n", - "test_data最大日期: 2024-01-23\n", - "划分后的训练集大小: 5658, 验证集大小: 281\n", - "train_data最大日期: 2024-01-22, 训练天数20\n", - "test_data最大日期: 2024-01-24\n", - "划分后的训练集大小: 5660, 验证集大小: 283\n", - "train_data最大日期: 2024-01-23, 训练天数20\n", - "test_data最大日期: 2024-01-25\n", - "划分后的训练集大小: 5653, 验证集大小: 282\n", - "train_data最大日期: 2024-01-24, 训练天数20\n", - "test_data最大日期: 2024-01-26\n", - "划分后的训练集大小: 5652, 验证集大小: 285\n", - "train_data最大日期: 2024-01-25, 训练天数20\n", - "test_data最大日期: 2024-01-29\n", - "划分后的训练集大小: 5649, 验证集大小: 280\n", - "train_data最大日期: 2024-01-26, 训练天数20\n", - "test_data最大日期: 2024-01-30\n", - "划分后的训练集大小: 5627, 验证集大小: 264\n", - "train_data最大日期: 2024-01-29, 训练天数20\n", - "test_data最大日期: 2024-01-31\n", - "划分后的训练集大小: 5611, 验证集大小: 273\n", - "train_data最大日期: 2024-01-30, 训练天数20\n", - "test_data最大日期: 2024-02-01\n", - "划分后的训练集大小: 5614, 验证集大小: 286\n", - "train_data最大日期: 2024-01-31, 训练天数20\n", - "test_data最大日期: 2024-02-02\n", - "划分后的训练集大小: 5620, 验证集大小: 288\n", - "train_data最大日期: 2024-02-01, 训练天数20\n", - "test_data最大日期: 2024-02-05\n", - "划分后的训练集大小: 5618, 验证集大小: 281\n", - "train_data最大日期: 2024-02-02, 训练天数20\n", - "test_data最大日期: 2024-02-06\n", - "划分后的训练集大小: 5624, 验证集大小: 286\n", - "train_data最大日期: 2024-02-05, 训练天数20\n", - "test_data最大日期: 2024-02-07\n", - "划分后的训练集大小: 5632, 验证集大小: 289\n", - "train_data最大日期: 2024-02-06, 训练天数20\n", - "test_data最大日期: 2024-02-08\n", - "划分后的训练集大小: 5644, 验证集大小: 297\n", - "train_data最大日期: 2024-02-07, 训练天数20\n", - "test_data最大日期: 2024-02-19\n", - "划分后的训练集大小: 5658, 验证集大小: 294\n", - "train_data最大日期: 2024-02-08, 训练天数20\n", - "test_data最大日期: 2024-02-20\n", - "划分后的训练集大小: 5676, 验证集大小: 296\n", - "train_data最大日期: 2024-02-19, 训练天数20\n", - "test_data最大日期: 2024-02-21\n", - "划分后的训练集大小: 5681, 验证集大小: 289\n", - "train_data最大日期: 2024-02-20, 训练天数20\n", - "test_data最大日期: 2024-02-22\n", - "划分后的训练集大小: 5694, 验证集大小: 292\n", - "train_data最大日期: 2024-02-21, 训练天数20\n", - "test_data最大日期: 2024-02-23\n", - "划分后的训练集大小: 5686, 验证集大小: 276\n", - "train_data最大日期: 2024-02-22, 训练天数20\n", - "test_data最大日期: 2024-02-26\n", - "划分后的训练集大小: 5693, 验证集大小: 294\n", - "train_data最大日期: 2024-02-23, 训练天数20\n", - "test_data最大日期: 2024-02-27\n", - "划分后的训练集大小: 5687, 验证集大小: 271\n", - "train_data最大日期: 2024-02-26, 训练天数20\n", - "test_data最大日期: 2024-02-28\n", - "划分后的训练集大小: 5672, 验证集大小: 266\n", - "train_data最大日期: 2024-02-27, 训练天数20\n", - "test_data最大日期: 2024-02-29\n", - "划分后的训练集大小: 5661, 验证集大小: 272\n", - "train_data最大日期: 2024-02-28, 训练天数20\n", - "test_data最大日期: 2024-03-01\n", - "划分后的训练集大小: 5641, 验证集大小: 262\n", - "train_data最大日期: 2024-02-29, 训练天数20\n", - "test_data最大日期: 2024-03-04\n", - "划分后的训练集大小: 5635, 验证集大小: 279\n", - "train_data最大日期: 2024-03-01, 训练天数20\n", - "test_data最大日期: 2024-03-05\n", - "划分后的训练集大小: 5640, 验证集大小: 285\n", - "train_data最大日期: 2024-03-04, 训练天数20\n", - "test_data最大日期: 2024-03-06\n", - "划分后的训练集大小: 5661, 验证集大小: 285\n", - "train_data最大日期: 2024-03-05, 训练天数20\n", - "test_data最大日期: 2024-03-07\n", - "划分后的训练集大小: 5668, 验证集大小: 280\n", - "train_data最大日期: 2024-03-06, 训练天数20\n", - "test_data最大日期: 2024-03-08\n", - "划分后的训练集大小: 5669, 验证集大小: 287\n", - "train_data最大日期: 2024-03-07, 训练天数20\n", - "test_data最大日期: 2024-03-11\n", - "划分后的训练集大小: 5660, 验证集大小: 279\n", - "train_data最大日期: 2024-03-08, 训练天数20\n", - "test_data最大日期: 2024-03-12\n", - "划分后的训练集大小: 5669, 验证集大小: 290\n", - "train_data最大日期: 2024-03-11, 训练天数20\n", - "test_data最大日期: 2024-03-13\n", - "划分后的训练集大小: 5667, 验证集大小: 284\n", - "train_data最大日期: 2024-03-12, 训练天数20\n", - "test_data最大日期: 2024-03-14\n", - "划分后的训练集大小: 5662, 验证集大小: 284\n", - "train_data最大日期: 2024-03-13, 训练天数20\n", - "test_data最大日期: 2024-03-15\n", - "划分后的训练集大小: 5650, 验证集大小: 285\n", - "train_data最大日期: 2024-03-14, 训练天数20\n", - "test_data最大日期: 2024-03-18\n", - "划分后的训练集大小: 5649, 验证集大小: 293\n", - "train_data最大日期: 2024-03-15, 训练天数20\n", - "test_data最大日期: 2024-03-19\n", - "划分后的训练集大小: 5643, 验证集大小: 290\n", - "train_data最大日期: 2024-03-18, 训练天数20\n", - "test_data最大日期: 2024-03-20\n", - "划分后的训练集大小: 5640, 验证集大小: 286\n", - "train_data最大日期: 2024-03-19, 训练天数20\n", - "test_data最大日期: 2024-03-21\n", - "划分后的训练集大小: 5632, 验证集大小: 284\n", - "train_data最大日期: 2024-03-20, 训练天数20\n", - "test_data最大日期: 2024-03-22\n", - "划分后的训练集大小: 5635, 验证集大小: 279\n", - "train_data最大日期: 2024-03-21, 训练天数20\n", - "test_data最大日期: 2024-03-25\n", - "划分后的训练集大小: 5617, 验证集大小: 276\n", - "train_data最大日期: 2024-03-22, 训练天数20\n", - "test_data最大日期: 2024-03-26\n", - "划分后的训练集大小: 5620, 验证集大小: 274\n", - "train_data最大日期: 2024-03-25, 训练天数20\n", - "test_data最大日期: 2024-03-27\n", - "划分后的训练集大小: 5637, 验证集大小: 283\n", - "train_data最大日期: 2024-03-26, 训练天数20\n", - "test_data最大日期: 2024-03-28\n", - "划分后的训练集大小: 5653, 验证集大小: 288\n", - "train_data最大日期: 2024-03-27, 训练天数20\n", - "test_data最大日期: 2024-03-29\n", - "划分后的训练集大小: 5680, 验证集大小: 289\n", - "train_data最大日期: 2024-03-28, 训练天数20\n", - "test_data最大日期: 2024-04-01\n", - "划分后的训练集大小: 5691, 验证集大小: 290\n", - "train_data最大日期: 2024-03-29, 训练天数20\n", - "test_data最大日期: 2024-04-02\n", - "划分后的训练集大小: 5695, 验证集大小: 289\n", - "train_data最大日期: 2024-04-01, 训练天数20\n", - "test_data最大日期: 2024-04-03\n", - "划分后的训练集大小: 5688, 验证集大小: 278\n", - "train_data最大日期: 2024-04-02, 训练天数20\n", - "test_data最大日期: 2024-04-08\n", - "划分后的训练集大小: 5679, 验证集大小: 271\n", - "train_data最大日期: 2024-04-03, 训练天数20\n", - "test_data最大日期: 2024-04-09\n", - "划分后的训练集大小: 5678, 验证集大小: 286\n", - "train_data最大日期: 2024-04-08, 训练天数20\n", - "test_data最大日期: 2024-04-10\n", - "划分后的训练集大小: 5687, 验证集大小: 288\n", - "train_data最大日期: 2024-04-09, 训练天数20\n", - "test_data最大日期: 2024-04-11\n", - "划分后的训练集大小: 5688, 验证集大小: 291\n", - "train_data最大日期: 2024-04-10, 训练天数20\n", - "test_data最大日期: 2024-04-12\n", - "划分后的训练集大小: 5690, 验证集大小: 286\n", - "train_data最大日期: 2024-04-11, 训练天数20\n", - "test_data最大日期: 2024-04-15\n", - "划分后的训练集大小: 5690, 验证集大小: 284\n", - "train_data最大日期: 2024-04-12, 训练天数20\n", - "test_data最大日期: 2024-04-16\n", - "划分后的训练集大小: 5645, 验证集大小: 240\n", - "train_data最大日期: 2024-04-15, 训练天数20\n", - "test_data最大日期: 2024-04-17\n", - "划分后的训练集大小: 5640, 验证集大小: 288\n", - "train_data最大日期: 2024-04-16, 训练天数20\n", - "test_data最大日期: 2024-04-18\n", - "划分后的训练集大小: 5639, 验证集大小: 289\n", - "train_data最大日期: 2024-04-17, 训练天数20\n", - "test_data最大日期: 2024-04-19\n", - "划分后的训练集大小: 5641, 验证集大小: 288\n", - "train_data最大日期: 2024-04-18, 训练天数20\n", - "test_data最大日期: 2024-04-22\n", - "划分后的训练集大小: 5644, 验证集大小: 287\n", - "train_data最大日期: 2024-04-19, 训练天数20\n", - "test_data最大日期: 2024-04-23\n", - "划分后的训练集大小: 5656, 验证集大小: 291\n", - "train_data最大日期: 2024-04-22, 训练天数20\n", - "test_data最大日期: 2024-04-24\n", - "划分后的训练集大小: 5674, 验证集大小: 294\n", - "train_data最大日期: 2024-04-23, 训练天数20\n", - "test_data最大日期: 2024-04-25\n", - "划分后的训练集大小: 5686, 验证集大小: 286\n", - "train_data最大日期: 2024-04-24, 训练天数20\n", - "test_data最大日期: 2024-04-26\n", - "划分后的训练集大小: 5691, 验证集大小: 288\n", - "train_data最大日期: 2024-04-25, 训练天数20\n", - "test_data最大日期: 2024-04-29\n", - "划分后的训练集大小: 5692, 验证集大小: 289\n", - "train_data最大日期: 2024-04-26, 训练天数20\n", - "test_data最大日期: 2024-04-30\n", - "划分后的训练集大小: 5697, 验证集大小: 294\n", - "train_data最大日期: 2024-04-29, 训练天数20\n", - "test_data最大日期: 2024-05-06\n", - "划分后的训练集大小: 5697, 验证集大小: 290\n", - "train_data最大日期: 2024-04-30, 训练天数20\n", - "test_data最大日期: 2024-05-07\n", - "划分后的训练集大小: 5697, 验证集大小: 289\n", - "train_data最大日期: 2024-05-06, 训练天数20\n", - "test_data最大日期: 2024-05-08\n", - "划分后的训练集大小: 5698, 验证集大小: 279\n", - "train_data最大日期: 2024-05-07, 训练天数20\n", - "test_data最大日期: 2024-05-09\n", - "划分后的训练集大小: 5713, 验证集大小: 286\n", - "train_data最大日期: 2024-05-08, 训练天数20\n", - "test_data最大日期: 2024-05-10\n", - "划分后的训练集大小: 5713, 验证集大小: 286\n", - "train_data最大日期: 2024-05-09, 训练天数20\n", - "test_data最大日期: 2024-05-13\n", - "划分后的训练集大小: 5708, 验证集大小: 283\n", - "train_data最大日期: 2024-05-10, 训练天数20\n", - "test_data最大日期: 2024-05-14\n", - "划分后的训练集大小: 5707, 验证集大小: 290\n", - "train_data最大日期: 2024-05-13, 训练天数20\n", - "test_data最大日期: 2024-05-15\n", - "划分后的训练集大小: 5715, 验证集大小: 294\n", - "train_data最大日期: 2024-05-14, 训练天数20\n", - "test_data最大日期: 2024-05-16\n", - "划分后的训练集大小: 5724, 验证集大小: 293\n", - "train_data最大日期: 2024-05-15, 训练天数20\n", - "test_data最大日期: 2024-05-17\n", - "划分后的训练集大小: 5774, 验证集大小: 290\n", - "train_data最大日期: 2024-05-16, 训练天数20\n", - "test_data最大日期: 2024-05-20\n", - "划分后的训练集大小: 5778, 验证集大小: 292\n", - "train_data最大日期: 2024-05-17, 训练天数20\n", - "test_data最大日期: 2024-05-21\n", - "划分后的训练集大小: 5784, 验证集大小: 295\n", - "train_data最大日期: 2024-05-20, 训练天数20\n", - "test_data最大日期: 2024-05-22\n", - "划分后的训练集大小: 5781, 验证集大小: 285\n", - "train_data最大日期: 2024-05-21, 训练天数20\n", - "test_data最大日期: 2024-05-23\n", - "划分后的训练集大小: 5786, 验证集大小: 292\n", - "train_data最大日期: 2024-05-22, 训练天数20\n", - "test_data最大日期: 2024-05-24\n", - "划分后的训练集大小: 5785, 验证集大小: 290\n", - "train_data最大日期: 2024-05-23, 训练天数20\n", - "test_data最大日期: 2024-05-27\n", - "划分后的训练集大小: 5781, 验证集大小: 290\n", - "train_data最大日期: 2024-05-24, 训练天数20\n", - "test_data最大日期: 2024-05-28\n", - "划分后的训练集大小: 5790, 验证集大小: 295\n", - "train_data最大日期: 2024-05-27, 训练天数20\n", - "test_data最大日期: 2024-05-29\n", - "划分后的训练集大小: 5796, 验证集大小: 294\n", - "train_data最大日期: 2024-05-28, 训练天数20\n", - "test_data最大日期: 2024-05-30\n", - "划分后的训练集大小: 5797, 验证集大小: 290\n", - "train_data最大日期: 2024-05-29, 训练天数20\n", - "test_data最大日期: 2024-05-31\n", - "划分后的训练集大小: 5790, 验证集大小: 287\n", - "train_data最大日期: 2024-05-30, 训练天数20\n", - "test_data最大日期: 2024-06-03\n", - "划分后的训练集大小: 5791, 验证集大小: 291\n", - "train_data最大日期: 2024-05-31, 训练天数20\n", - "test_data最大日期: 2024-06-04\n", - "划分后的训练集大小: 5795, 验证集大小: 293\n", - "train_data最大日期: 2024-06-03, 训练天数20\n", - "test_data最大日期: 2024-06-05\n", - "划分后的训练集大小: 5810, 验证集大小: 294\n", - "train_data最大日期: 2024-06-04, 训练天数20\n", - "test_data最大日期: 2024-06-06\n", - "划分后的训练集大小: 5817, 验证集大小: 293\n", - "train_data最大日期: 2024-06-05, 训练天数20\n", - "test_data最大日期: 2024-06-07\n", - "划分后的训练集大小: 5825, 验证集大小: 294\n", - "train_data最大日期: 2024-06-06, 训练天数20\n", - "test_data最大日期: 2024-06-11\n", - "划分后的训练集大小: 5837, 验证集大小: 295\n", - "train_data最大日期: 2024-06-07, 训练天数20\n", - "test_data最大日期: 2024-06-12\n", - "划分后的训练集大小: 5840, 验证集大小: 293\n", - "train_data最大日期: 2024-06-11, 训练天数20\n", - "test_data最大日期: 2024-06-13\n", - "划分后的训练集大小: 5844, 验证集大小: 298\n", - "train_data最大日期: 2024-06-12, 训练天数20\n", - "test_data最大日期: 2024-06-14\n", - "划分后的训练集大小: 5840, 验证集大小: 289\n", - "train_data最大日期: 2024-06-13, 训练天数20\n", - "test_data最大日期: 2024-06-17\n", - "划分后的训练集大小: 5841, 验证集大小: 291\n", - "train_data最大日期: 2024-06-14, 训练天数20\n", - "test_data最大日期: 2024-06-18\n", - "划分后的训练集大小: 5844, 验证集大小: 295\n", - "train_data最大日期: 2024-06-17, 训练天数20\n", - "test_data最大日期: 2024-06-19\n", - "划分后的训练集大小: 5840, 验证集大小: 291\n", - "train_data最大日期: 2024-06-18, 训练天数20\n", - "test_data最大日期: 2024-06-20\n", - "划分后的训练集大小: 5849, 验证集大小: 294\n", - "train_data最大日期: 2024-06-19, 训练天数20\n", - "test_data最大日期: 2024-06-21\n", - "划分后的训练集大小: 5848, 验证集大小: 291\n", - "train_data最大日期: 2024-06-20, 训练天数20\n", - "test_data最大日期: 2024-06-24\n", - "划分后的训练集大小: 5848, 验证集大小: 290\n", - "train_data最大日期: 2024-06-21, 训练天数20\n", - "test_data最大日期: 2024-06-25\n", - "划分后的训练集大小: 5846, 验证集大小: 288\n", - "train_data最大日期: 2024-06-24, 训练天数20\n", - "test_data最大日期: 2024-06-26\n", - "划分后的训练集大小: 5846, 验证集大小: 295\n", - "train_data最大日期: 2024-06-25, 训练天数20\n", - "test_data最大日期: 2024-06-27\n", - "划分后的训练集大小: 5847, 验证集大小: 295\n", - "train_data最大日期: 2024-06-26, 训练天数20\n", - "test_data最大日期: 2024-06-28\n", - "划分后的训练集大小: 5845, 验证集大小: 288\n", - "train_data最大日期: 2024-06-27, 训练天数20\n", - "test_data最大日期: 2024-07-01\n", - "划分后的训练集大小: 5852, 验证集大小: 294\n", - "train_data最大日期: 2024-06-28, 训练天数20\n", - "test_data最大日期: 2024-07-02\n", - "划分后的训练集大小: 5853, 验证集大小: 292\n", - "train_data最大日期: 2024-07-01, 训练天数20\n", - "test_data最大日期: 2024-07-03\n", - "划分后的训练集大小: 5849, 验证集大小: 289\n", - "train_data最大日期: 2024-07-02, 训练天数20\n", - "test_data最大日期: 2024-07-04\n", - "划分后的训练集大小: 5842, 验证集大小: 287\n", - "train_data最大日期: 2024-07-03, 训练天数20\n", - "test_data最大日期: 2024-07-05\n", - "划分后的训练集大小: 5839, 验证集大小: 290\n", - "train_data最大日期: 2024-07-04, 训练天数20\n", - "test_data最大日期: 2024-07-08\n", - "划分后的训练集大小: 5837, 验证集大小: 292\n", - "train_data最大日期: 2024-07-05, 训练天数20\n", - "test_data最大日期: 2024-07-09\n", - "划分后的训练集大小: 5830, 验证集大小: 288\n", - "train_data最大日期: 2024-07-08, 训练天数20\n", - "test_data最大日期: 2024-07-10\n", - "划分后的训练集大小: 5827, 验证集大小: 290\n", - "train_data最大日期: 2024-07-09, 训练天数20\n", - "test_data最大日期: 2024-07-11\n", - "划分后的训练集大小: 5826, 验证集大小: 297\n", - "train_data最大日期: 2024-07-10, 训练天数20\n", - "test_data最大日期: 2024-07-12\n", - "划分后的训练集大小: 5835, 验证集大小: 298\n", - "train_data最大日期: 2024-07-11, 训练天数20\n", - "test_data最大日期: 2024-07-15\n", - "划分后的训练集大小: 5835, 验证集大小: 291\n", - "train_data最大日期: 2024-07-12, 训练天数20\n", - "test_data最大日期: 2024-07-16\n", - "划分后的训练集大小: 5830, 验证集大小: 290\n", - "train_data最大日期: 2024-07-15, 训练天数20\n", - "test_data最大日期: 2024-07-17\n", - "划分后的训练集大小: 5827, 验证集大小: 288\n", - "train_data最大日期: 2024-07-16, 训练天数20\n", - "test_data最大日期: 2024-07-18\n", - "划分后的训练集大小: 5826, 验证集大小: 293\n", - "train_data最大日期: 2024-07-17, 训练天数20\n", - "test_data最大日期: 2024-07-19\n", - "划分后的训练集大小: 5828, 验证集大小: 293\n", - "train_data最大日期: 2024-07-18, 训练天数20\n", - "test_data最大日期: 2024-07-22\n", - "划分后的训练集大小: 5831, 验证集大小: 293\n", - "train_data最大日期: 2024-07-19, 训练天数20\n", - "test_data最大日期: 2024-07-23\n", - "划分后的训练集大小: 5836, 验证集大小: 293\n", - "train_data最大日期: 2024-07-22, 训练天数20\n", - "test_data最大日期: 2024-07-24\n", - "划分后的训练集大小: 5832, 验证集大小: 291\n", - "train_data最大日期: 2024-07-23, 训练天数20\n", - "test_data最大日期: 2024-07-25\n", - "划分后的训练集大小: 5828, 验证集大小: 291\n", - "train_data最大日期: 2024-07-24, 训练天数20\n", - "test_data最大日期: 2024-07-26\n", - "划分后的训练集大小: 5834, 验证集大小: 294\n", - "train_data最大日期: 2024-07-25, 训练天数20\n", - "test_data最大日期: 2024-07-29\n", - "划分后的训练集大小: 5832, 验证集大小: 292\n", - "train_data最大日期: 2024-07-26, 训练天数20\n", - "test_data最大日期: 2024-07-30\n", - "划分后的训练集大小: 5834, 验证集大小: 294\n", - "train_data最大日期: 2024-07-29, 训练天数20\n", - "test_data最大日期: 2024-07-31\n", - "划分后的训练集大小: 5840, 验证集大小: 295\n", - "train_data最大日期: 2024-07-30, 训练天数20\n", - "test_data最大日期: 2024-08-01\n", - "划分后的训练集大小: 5835, 验证集大小: 282\n", - "train_data最大日期: 2024-07-31, 训练天数20\n", - "test_data最大日期: 2024-08-02\n", - "划分后的训练集大小: 5836, 验证集大小: 291\n", - "train_data最大日期: 2024-08-01, 训练天数20\n", - "test_data最大日期: 2024-08-05\n", - "划分后的训练集大小: 5828, 验证集大小: 284\n", - "train_data最大日期: 2024-08-02, 训练天数20\n", - "test_data最大日期: 2024-08-06\n", - "划分后的训练集大小: 5827, 验证集大小: 287\n", - "train_data最大日期: 2024-08-05, 训练天数20\n", - "test_data最大日期: 2024-08-07\n", - "划分后的训练集大小: 5826, 验证集大小: 289\n", - "train_data最大日期: 2024-08-06, 训练天数20\n", - "test_data最大日期: 2024-08-08\n", - "划分后的训练集大小: 5824, 验证集大小: 295\n", - "train_data最大日期: 2024-08-07, 训练天数20\n", - "test_data最大日期: 2024-08-09\n", - "划分后的训练集大小: 5817, 验证集大小: 291\n", - "train_data最大日期: 2024-08-08, 训练天数20\n", - "test_data最大日期: 2024-08-12\n", - "划分后的训练集大小: 5814, 验证集大小: 288\n", - "train_data最大日期: 2024-08-09, 训练天数20\n", - "test_data最大日期: 2024-08-13\n", - "划分后的训练集大小: 5817, 验证集大小: 293\n", - "train_data最大日期: 2024-08-12, 训练天数20\n", - "test_data最大日期: 2024-08-14\n", - "划分后的训练集大小: 5819, 验证集大小: 290\n", - "train_data最大日期: 2024-08-13, 训练天数20\n", - "test_data最大日期: 2024-08-15\n", - "划分后的训练集大小: 5817, 验证集大小: 291\n", - "train_data最大日期: 2024-08-14, 训练天数20\n", - "test_data最大日期: 2024-08-16\n", - "划分后的训练集大小: 5818, 验证集大小: 294\n", - "train_data最大日期: 2024-08-15, 训练天数20\n", - "test_data最大日期: 2024-08-19\n", - "划分后的训练集大小: 5818, 验证集大小: 293\n", - "train_data最大日期: 2024-08-16, 训练天数20\n", - "test_data最大日期: 2024-08-20\n", - "划分后的训练集大小: 5820, 验证集大小: 295\n", - "train_data最大日期: 2024-08-19, 训练天数20\n", - "test_data最大日期: 2024-08-21\n", - "划分后的训练集大小: 5823, 验证集大小: 294\n", - "train_data最大日期: 2024-08-20, 训练天数20\n", - "test_data最大日期: 2024-08-22\n", - "划分后的训练集大小: 5829, 验证集大小: 297\n", - "train_data最大日期: 2024-08-21, 训练天数20\n", - "test_data最大日期: 2024-08-23\n", - "划分后的训练集大小: 5827, 验证集大小: 292\n", - "train_data最大日期: 2024-08-22, 训练天数20\n", - "test_data最大日期: 2024-08-26\n", - "划分后的训练集大小: 5830, 验证集大小: 295\n", - "train_data最大日期: 2024-08-23, 训练天数20\n", - "test_data最大日期: 2024-08-27\n", - "划分后的训练集大小: 5830, 验证集大小: 294\n", - "train_data最大日期: 2024-08-26, 训练天数20\n", - "test_data最大日期: 2024-08-28\n", - "划分后的训练集大小: 5830, 验证集大小: 295\n", - "train_data最大日期: 2024-08-27, 训练天数20\n", - "test_data最大日期: 2024-08-29\n", - "划分后的训练集大小: 5839, 验证集大小: 291\n", - "train_data最大日期: 2024-08-28, 训练天数20\n", - "test_data最大日期: 2024-08-30\n", - "划分后的训练集大小: 5837, 验证集大小: 289\n", - "train_data最大日期: 2024-08-29, 训练天数20\n", - "test_data最大日期: 2024-09-02\n", - "划分后的训练集大小: 5846, 验证集大小: 293\n", - "train_data最大日期: 2024-08-30, 训练天数20\n", - "test_data最大日期: 2024-09-03\n", - "划分后的训练集大小: 5852, 验证集大小: 293\n", - "train_data最大日期: 2024-09-02, 训练天数20\n", - "test_data最大日期: 2024-09-04\n", - "划分后的训练集大小: 5855, 验证集大小: 292\n", - "train_data最大日期: 2024-09-03, 训练天数20\n", - "test_data最大日期: 2024-09-05\n", - "划分后的训练集大小: 5853, 验证集大小: 293\n", - "train_data最大日期: 2024-09-04, 训练天数20\n", - "test_data最大日期: 2024-09-06\n", - "划分后的训练集大小: 5855, 验证集大小: 293\n", - "train_data最大日期: 2024-09-05, 训练天数20\n", - "test_data最大日期: 2024-09-09\n", - "划分后的训练集大小: 5860, 验证集大小: 293\n", - "train_data最大日期: 2024-09-06, 训练天数20\n", - "test_data最大日期: 2024-09-10\n", - "划分后的训练集大小: 5855, 验证集大小: 288\n", - "train_data最大日期: 2024-09-09, 训练天数20\n", - "test_data最大日期: 2024-09-11\n", - "划分后的训练集大小: 5854, 验证集大小: 289\n", - "train_data最大日期: 2024-09-10, 训练天数20\n", - "test_data最大日期: 2024-09-12\n", - "划分后的训练集大小: 5855, 验证集大小: 292\n", - "train_data最大日期: 2024-09-11, 训练天数20\n", - "test_data最大日期: 2024-09-13\n", - "划分后的训练集大小: 5851, 验证集大小: 290\n", - "train_data最大日期: 2024-09-12, 训练天数20\n", - "test_data最大日期: 2024-09-18\n", - "划分后的训练集大小: 5846, 验证集大小: 288\n", - "train_data最大日期: 2024-09-13, 训练天数20\n", - "test_data最大日期: 2024-09-19\n", - "划分后的训练集大小: 5842, 验证集大小: 291\n", - "train_data最大日期: 2024-09-18, 训练天数20\n", - "test_data最大日期: 2024-09-20\n", - "划分后的训练集大小: 5836, 验证集大小: 288\n", - "train_data最大日期: 2024-09-19, 训练天数20\n", - "test_data最大日期: 2024-09-23\n", - "划分后的训练集大小: 5832, 验证集大小: 293\n", - "train_data最大日期: 2024-09-20, 训练天数20\n", - "test_data最大日期: 2024-09-24\n", - "划分后的训练集大小: 5828, 验证集大小: 288\n", - "train_data最大日期: 2024-09-23, 训练天数20\n", - "test_data最大日期: 2024-09-25\n", - "划分后的训练集大小: 5813, 验证集大小: 280\n", - "train_data最大日期: 2024-09-24, 训练天数20\n", - "test_data最大日期: 2024-09-26\n", - "划分后的训练集大小: 5809, 验证集大小: 290\n", - "train_data最大日期: 2024-09-25, 训练天数20\n", - "test_data最大日期: 2024-09-27\n", - "划分后的训练集大小: 5781, 验证集大小: 267\n", - "train_data最大日期: 2024-09-26, 训练天数20\n", - "test_data最大日期: 2024-09-30\n", - "划分后的训练集大小: 5761, 验证集大小: 271\n", - "train_data最大日期: 2024-09-27, 训练天数20\n", - "test_data最大日期: 2024-10-08\n", - "划分后的训练集大小: 5739, 验证集大小: 267\n", - "train_data最大日期: 2024-09-30, 训练天数20\n", - "test_data最大日期: 2024-10-09\n", - "划分后的训练集大小: 5617, 验证集大小: 171\n", - "train_data最大日期: 2024-10-08, 训练天数20\n", - "test_data最大日期: 2024-10-10\n", - "划分后的训练集大小: 5410, 验证集大小: 86\n", - "train_data最大日期: 2024-10-09, 训练天数20\n", - "test_data最大日期: 2024-10-11\n", - "划分后的训练集大小: 5371, 验证集大小: 253\n", - "train_data最大日期: 2024-10-10, 训练天数20\n", - "test_data最大日期: 2024-10-14\n", - "划分后的训练集大小: 5369, 验证集大小: 291\n", - "train_data最大日期: 2024-10-11, 训练天数20\n", - "test_data最大日期: 2024-10-15\n", - "划分后的训练集大小: 5365, 验证集大小: 289\n", - "train_data最大日期: 2024-10-14, 训练天数20\n", - "test_data最大日期: 2024-10-16\n", - "划分后的训练集大小: 5359, 验证集大小: 287\n", - "train_data最大日期: 2024-10-15, 训练天数20\n", - "test_data最大日期: 2024-10-17\n", - "划分后的训练集大小: 5362, 验证集大小: 291\n", - "train_data最大日期: 2024-10-16, 训练天数20\n", - "test_data最大日期: 2024-10-18\n", - "划分后的训练集大小: 5359, 验证集大小: 286\n", - "train_data最大日期: 2024-10-17, 训练天数20\n", - "test_data最大日期: 2024-10-21\n", - "划分后的训练集大小: 5347, 验证集大小: 280\n", - "train_data最大日期: 2024-10-18, 训练天数20\n", - "test_data最大日期: 2024-10-22\n", - "划分后的训练集大小: 5346, 验证集大小: 289\n", - "train_data最大日期: 2024-10-21, 训练天数20\n", - "test_data最大日期: 2024-10-23\n", - "划分后的训练集大小: 5336, 验证集大小: 278\n", - "train_data最大日期: 2024-10-22, 训练天数20\n", - "test_data最大日期: 2024-10-24\n", - "划分后的训练集大小: 5319, 验证集大小: 274\n", - "train_data最大日期: 2024-10-23, 训练天数20\n", - "test_data最大日期: 2024-10-25\n", - "划分后的训练集大小: 5300, 验证集大小: 269\n", - "train_data最大日期: 2024-10-24, 训练天数20\n", - "test_data最大日期: 2024-10-28\n", - "划分后的训练集大小: 5275, 验证集大小: 268\n", - "train_data最大日期: 2024-10-25, 训练天数20\n", - "test_data最大日期: 2024-10-29\n", - "划分后的训练集大小: 5268, 验证集大小: 281\n", - "train_data最大日期: 2024-10-28, 训练天数20\n", - "test_data最大日期: 2024-10-30\n", - "划分后的训练集大小: 5264, 验证集大小: 276\n", - "train_data最大日期: 2024-10-29, 训练天数20\n", - "test_data最大日期: 2024-10-31\n", - "划分后的训练集大小: 5199, 验证集大小: 225\n", - "train_data最大日期: 2024-10-30, 训练天数20\n", - "test_data最大日期: 2024-11-01\n", - "划分后的训练集大小: 5197, 验证集大小: 265\n", - "train_data最大日期: 2024-10-31, 训练天数20\n", - "test_data最大日期: 2024-11-04\n", - "划分后的训练集大小: 5166, 验证集大小: 240\n", - "train_data最大日期: 2024-11-01, 训练天数20\n", - "test_data最大日期: 2024-11-05\n", - "划分后的训练集大小: 5176, 验证集大小: 277\n", - "train_data最大日期: 2024-11-04, 训练天数20\n", - "test_data最大日期: 2024-11-06\n", - "划分后的训练集大小: 5284, 验证集大小: 279\n", - "train_data最大日期: 2024-11-05, 训练天数20\n", - "test_data最大日期: 2024-11-07\n", - "划分后的训练集大小: 5479, 验证集大小: 281\n", - "train_data最大日期: 2024-11-06, 训练天数20\n", - "test_data最大日期: 2024-11-08\n", - "划分后的训练集大小: 5492, 验证集大小: 266\n", - "train_data最大日期: 2024-11-07, 训练天数20\n", - "test_data最大日期: 2024-11-11\n", - "划分后的训练集大小: 5472, 验证集大小: 271\n", - "train_data最大日期: 2024-11-08, 训练天数20\n", - "test_data最大日期: 2024-11-12\n", - "划分后的训练集大小: 5461, 验证集大小: 278\n", - "train_data最大日期: 2024-11-11, 训练天数20\n", - "test_data最大日期: 2024-11-13\n", - "划分后的训练集大小: 5456, 验证集大小: 282\n", - "train_data最大日期: 2024-11-12, 训练天数20\n", - "test_data最大日期: 2024-11-14\n", - "划分后的训练集大小: 5438, 验证集大小: 273\n", - "train_data最大日期: 2024-11-13, 训练天数20\n", - "test_data最大日期: 2024-11-15\n", - "划分后的训练集大小: 5433, 验证集大小: 281\n", - "train_data最大日期: 2024-11-14, 训练天数20\n", - "test_data最大日期: 2024-11-18\n", - "划分后的训练集大小: 5427, 验证集大小: 274\n", - "train_data最大日期: 2024-11-15, 训练天数20\n", - "test_data最大日期: 2024-11-19\n", - "划分后的训练集大小: 5420, 验证集大小: 282\n", - "train_data最大日期: 2024-11-18, 训练天数20\n", - "test_data最大日期: 2024-11-20\n", - "划分后的训练集大小: 5426, 验证集大小: 284\n", - "train_data最大日期: 2024-11-19, 训练天数20\n", - "test_data最大日期: 2024-11-21\n", - "划分后的训练集大小: 5436, 验证集大小: 284\n", - "train_data最大日期: 2024-11-20, 训练天数20\n", - "test_data最大日期: 2024-11-22\n", - "划分后的训练集大小: 5452, 验证集大小: 285\n", - "train_data最大日期: 2024-11-21, 训练天数20\n", - "test_data最大日期: 2024-11-25\n", - "划分后的训练集大小: 5453, 验证集大小: 269\n", - "train_data最大日期: 2024-11-22, 训练天数20\n", - "test_data最大日期: 2024-11-26\n", - "划分后的训练集大小: 5442, 验证集大小: 270\n", - "train_data最大日期: 2024-11-25, 训练天数20\n", - "test_data最大日期: 2024-11-27\n", - "划分后的训练集大小: 5435, 验证集大小: 269\n", - "train_data最大日期: 2024-11-26, 训练天数20\n", - "test_data最大日期: 2024-11-28\n", - "划分后的训练集大小: 5485, 验证集大小: 275\n", - "train_data最大日期: 2024-11-27, 训练天数20\n", - "test_data最大日期: 2024-11-29\n", - "划分后的训练集大小: 5499, 验证集大小: 279\n", - "train_data最大日期: 2024-11-28, 训练天数20\n", - "test_data最大日期: 2024-12-02\n", - "划分后的训练集大小: 5542, 验证集大小: 283\n", - "train_data最大日期: 2024-11-29, 训练天数20\n", - "test_data最大日期: 2024-12-03\n", - "划分后的训练集大小: 5550, 验证集大小: 285\n", - "train_data最大日期: 2024-12-02, 训练天数20\n", - "test_data最大日期: 2024-12-04\n", - "划分后的训练集大小: 5550, 验证集大小: 279\n", - "train_data最大日期: 2024-12-03, 训练天数20\n", - "test_data最大日期: 2024-12-05\n", - "划分后的训练集大小: 5541, 验证集大小: 272\n", - "train_data最大日期: 2024-12-04, 训练天数20\n", - "test_data最大日期: 2024-12-06\n", - "划分后的训练集大小: 5542, 验证集大小: 267\n", - "train_data最大日期: 2024-12-05, 训练天数20\n", - "test_data最大日期: 2024-12-09\n", - "划分后的训练集大小: 5548, 验证集大小: 277\n", - "train_data最大日期: 2024-12-06, 训练天数20\n", - "test_data最大日期: 2024-12-10\n", - "划分后的训练集大小: 5550, 验证集大小: 280\n", - "train_data最大日期: 2024-12-09, 训练天数20\n", - "test_data最大日期: 2024-12-11\n", - "划分后的训练集大小: 5548, 验证集大小: 280\n", - "train_data最大日期: 2024-12-10, 训练天数20\n", - "test_data最大日期: 2024-12-12\n", - "划分后的训练集大小: 5550, 验证集大小: 275\n", - "train_data最大日期: 2024-12-11, 训练天数20\n", - "test_data最大日期: 2024-12-13\n", - "划分后的训练集大小: 5555, 验证集大小: 286\n", - "train_data最大日期: 2024-12-12, 训练天数20\n", - "test_data最大日期: 2024-12-16\n", - "划分后的训练集大小: 5553, 验证集大小: 272\n", - "train_data最大日期: 2024-12-13, 训练天数20\n", - "test_data最大日期: 2024-12-17\n", - "划分后的训练集大小: 5537, 验证集大小: 266\n", - "train_data最大日期: 2024-12-16, 训练天数20\n", - "test_data最大日期: 2024-12-18\n", - "划分后的训练集大小: 5507, 验证集大小: 254\n", - "train_data最大日期: 2024-12-17, 训练天数20\n", - "test_data最大日期: 2024-12-19\n", - "划分后的训练集大小: 5509, 验证集大小: 286\n", - "train_data最大日期: 2024-12-18, 训练天数20\n", - "test_data最大日期: 2024-12-20\n", - "划分后的训练集大小: 5506, 验证集大小: 282\n", - "train_data最大日期: 2024-12-19, 训练天数20\n", - "test_data最大日期: 2024-12-23\n", - "划分后的训练集大小: 5522, 验证集大小: 285\n", - "train_data最大日期: 2024-12-20, 训练天数20\n", - "test_data最大日期: 2024-12-24\n", - "划分后的训练集大小: 5543, 验证集大小: 291\n", - "train_data最大日期: 2024-12-23, 训练天数20\n", - "test_data最大日期: 2024-12-25\n", - "划分后的训练集大小: 5562, 验证集大小: 288\n", - "train_data最大日期: 2024-12-24, 训练天数20\n", - "test_data最大日期: 2024-12-26\n", - "划分后的训练集大小: 5568, 验证集大小: 281\n", - "train_data最大日期: 2024-12-25, 训练天数20\n", - "test_data最大日期: 2024-12-27\n", - "划分后的训练集大小: 5578, 验证集大小: 289\n", - "train_data最大日期: 2024-12-26, 训练天数20\n", - "test_data最大日期: 2024-12-30\n", - "划分后的训练集大小: 5583, 验证集大小: 288\n", - "train_data最大日期: 2024-12-27, 训练天数20\n", - "test_data最大日期: 2024-12-31\n", - "划分后的训练集大小: 5588, 验证集大小: 290\n", - "train_data最大日期: 2024-12-30, 训练天数20\n", - "test_data最大日期: 2025-01-02\n", - "划分后的训练集大小: 5592, 验证集大小: 283\n", - "train_data最大日期: 2024-12-31, 训练天数20\n", - "test_data最大日期: 2025-01-03\n", - "划分后的训练集大小: 5602, 验证集大小: 282\n", - "train_data最大日期: 2025-01-02, 训练天数20\n", - "test_data最大日期: 2025-01-06\n", - "划分后的训练集大小: 5583, 验证集大小: 248\n", - "train_data最大日期: 2025-01-03, 训练天数20\n", - "test_data最大日期: 2025-01-07\n", - "划分后的训练集大小: 5584, 验证集大小: 278\n", - "train_data最大日期: 2025-01-06, 训练天数20\n", - "test_data最大日期: 2025-01-08\n", - "划分后的训练集大小: 5595, 验证集大小: 291\n", - "train_data最大日期: 2025-01-07, 训练天数20\n", - "test_data最大日期: 2025-01-09\n", - "划分后的训练集大小: 5606, 验证集大小: 291\n", - "train_data最大日期: 2025-01-08, 训练天数20\n", - "test_data最大日期: 2025-01-10\n", - "划分后的训练集大小: 5620, 验证集大小: 289\n", - "train_data最大日期: 2025-01-09, 训练天数20\n", - "test_data最大日期: 2025-01-13\n", - "划分后的训练集大小: 5608, 验证集大小: 274\n", - "train_data最大日期: 2025-01-10, 训练天数20\n", - "test_data最大日期: 2025-01-14\n", - "划分后的训练集大小: 5629, 验证集大小: 293\n", - "train_data最大日期: 2025-01-13, 训练天数20\n", - "test_data最大日期: 2025-01-15\n", - "划分后的训练集大小: 5655, 验证集大小: 292\n", - "train_data最大日期: 2025-01-14, 训练天数20\n", - "test_data最大日期: 2025-01-16\n", - "划分后的训练集大小: 5690, 验证集大小: 289\n", - "train_data最大日期: 2025-01-15, 训练天数20\n", - "test_data最大日期: 2025-01-17\n", - "划分后的训练集大小: 5698, 验证集大小: 294\n", - "train_data最大日期: 2025-01-16, 训练天数20\n", - "test_data最大日期: 2025-01-20\n", - "划分后的训练集大小: 5694, 验证集大小: 278\n", - "train_data最大日期: 2025-01-17, 训练天数20\n", - "test_data最大日期: 2025-01-21\n", - "划分后的训练集大小: 5694, 验证集大小: 285\n", - "train_data最大日期: 2025-01-20, 训练天数20\n", - "test_data最大日期: 2025-01-22\n", - "划分后的训练集大小: 5690, 验证集大小: 287\n", - "train_data最大日期: 2025-01-21, 训练天数20\n", - "test_data最大日期: 2025-01-23\n", - "划分后的训练集大小: 5690, 验证集大小: 288\n", - "train_data最大日期: 2025-01-22, 训练天数20\n", - "test_data最大日期: 2025-01-24\n", - "划分后的训练集大小: 5695, 验证集大小: 286\n", - "train_data最大日期: 2025-01-23, 训练天数20\n", - "test_data最大日期: 2025-01-27\n", - "划分后的训练集大小: 5699, 验证集大小: 293\n", - "train_data最大日期: 2025-01-24, 训练天数20\n", - "test_data最大日期: 2025-02-05\n", - "划分后的训练集大小: 5706, 验证集大小: 295\n", - "train_data最大日期: 2025-01-27, 训练天数20\n", - "test_data最大日期: 2025-02-06\n", - "划分后的训练集大小: 5707, 验证集大小: 291\n", - "train_data最大日期: 2025-02-05, 训练天数20\n", - "test_data最大日期: 2025-02-07\n", - "划分后的训练集大小: 5709, 验证集大小: 285\n", - "train_data最大日期: 2025-02-06, 训练天数20\n", - "test_data最大日期: 2025-02-10\n", - "划分后的训练集大小: 5710, 验证集大小: 283\n", - "train_data最大日期: 2025-02-07, 训练天数20\n", - "test_data最大日期: 2025-02-11\n", - "划分后的训练集大小: 5748, 验证集大小: 286\n", - "train_data最大日期: 2025-02-10, 训练天数20\n", - "test_data最大日期: 2025-02-12\n", - "划分后的训练集大小: 5755, 验证集大小: 285\n", - "train_data最大日期: 2025-02-11, 训练天数20\n", - "test_data最大日期: 2025-02-13\n", - "划分后的训练集大小: 5746, 验证集大小: 282\n", - "train_data最大日期: 2025-02-12, 训练天数20\n", - "test_data最大日期: 2025-02-14\n", - "划分后的训练集大小: 5740, 验证集大小: 285\n", - "train_data最大日期: 2025-02-13, 训练天数20\n", - "test_data最大日期: 2025-02-17\n", - "划分后的训练集大小: 5738, 验证集大小: 287\n", - "train_data最大日期: 2025-02-14, 训练天数20\n", - "test_data最大日期: 2025-02-18\n", - "划分后的训练集大小: 5751, 验证集大小: 287\n", - "train_data最大日期: 2025-02-17, 训练天数20\n", - "test_data最大日期: 2025-02-19\n", - "划分后的训练集大小: 5750, 验证集大小: 292\n", - "train_data最大日期: 2025-02-18, 训练天数20\n", - "test_data最大日期: 2025-02-20\n", - "划分后的训练集大小: 5746, 验证集大小: 288\n", - "train_data最大日期: 2025-02-19, 训练天数20\n", - "test_data最大日期: 2025-02-21\n", - "划分后的训练集大小: 5742, 验证集大小: 285\n", - "train_data最大日期: 2025-02-20, 训练天数20\n", - "test_data最大日期: 2025-02-24\n", - "划分后的训练集大小: 5735, 验证集大小: 287\n", - "train_data最大日期: 2025-02-21, 训练天数20\n", - "test_data最大日期: 2025-02-25\n", - "划分后的训练集大小: 5747, 验证集大小: 290\n", - "train_data最大日期: 2025-02-24, 训练天数20\n", - "test_data最大日期: 2025-02-26\n", - "划分后的训练集大小: 5746, 验证集大小: 284\n", - "train_data最大日期: 2025-02-25, 训练天数20\n", - "test_data最大日期: 2025-02-27\n", - "划分后的训练集大小: 5752, 验证集大小: 293\n", - "train_data最大日期: 2025-02-26, 训练天数20\n", - "test_data最大日期: 2025-02-28\n", - "划分后的训练集大小: 5746, 验证集大小: 282\n", - "train_data最大日期: 2025-02-27, 训练天数20\n", - "test_data最大日期: 2025-03-03\n", - "划分后的训练集大小: 5731, 验证集大小: 271\n", - "train_data最大日期: 2025-02-28, 训练天数20\n", - "test_data最大日期: 2025-03-04\n", - "划分后的训练集大小: 5721, 验证集大小: 283\n", - "train_data最大日期: 2025-03-03, 训练天数20\n", - "test_data最大日期: 2025-03-05\n", - "划分后的训练集大小: 5712, 验证集大小: 286\n", - "train_data最大日期: 2025-03-04, 训练天数20\n", - "test_data最大日期: 2025-03-06\n", - "划分后的训练集大小: 5712, 验证集大小: 291\n", - "train_data最大日期: 2025-03-05, 训练天数20\n", - "test_data最大日期: 2025-03-07\n", - "划分后的训练集大小: 5713, 验证集大小: 286\n", - "train_data最大日期: 2025-03-06, 训练天数20\n", - "test_data最大日期: 2025-03-10\n", - "划分后的训练集大小: 5722, 验证集大小: 292\n", - "train_data最大日期: 2025-03-07, 训练天数20\n", - "test_data最大日期: 2025-03-11\n", - "划分后的训练集大小: 5729, 验证集大小: 293\n", - "train_data最大日期: 2025-03-10, 训练天数20\n", - "test_data最大日期: 2025-03-12\n", - "划分后的训练集大小: 5731, 验证集大小: 287\n", - "train_data最大日期: 2025-03-11, 训练天数20\n", - "test_data最大日期: 2025-03-13\n", - "划分后的训练集大小: 5742, 验证集大小: 293\n", - "train_data最大日期: 2025-03-12, 训练天数20\n", - "test_data最大日期: 2025-03-14\n", - "划分后的训练集大小: 5746, 验证集大小: 289\n", - "train_data最大日期: 2025-03-13, 训练天数20\n", - "test_data最大日期: 2025-03-17\n", - "划分后的训练集大小: 5754, 验证集大小: 295\n", - "train_data最大日期: 2025-03-14, 训练天数20\n", - "test_data最大日期: 2025-03-18\n", - "划分后的训练集大小: 5757, 验证集大小: 290\n", - "train_data最大日期: 2025-03-17, 训练天数20\n", - "test_data最大日期: 2025-03-19\n", - "划分后的训练集大小: 5749, 验证集大小: 284\n", - "train_data最大日期: 2025-03-18, 训练天数20\n", - "test_data最大日期: 2025-03-20\n", - "划分后的训练集大小: 5741, 验证集大小: 280\n", - "train_data最大日期: 2025-03-19, 训练天数20\n", - "test_data最大日期: 2025-03-21\n", - "划分后的训练集大小: 5736, 验证集大小: 280\n", - "train_data最大日期: 2025-03-20, 训练天数20\n", - "test_data最大日期: 2025-03-24\n", - "划分后的训练集大小: 5733, 验证集大小: 284\n", - "train_data最大日期: 2025-03-21, 训练天数20\n", - "test_data最大日期: 2025-03-25\n", - "划分后的训练集大小: 5714, 验证集大小: 271\n", - "train_data最大日期: 2025-03-24, 训练天数20\n", - "test_data最大日期: 2025-03-26\n", - "划分后的训练集大小: 5720, 验证集大小: 290\n", - "train_data最大日期: 2025-03-25, 训练天数20\n", - "test_data最大日期: 2025-03-27\n", - "划分后的训练集大小: 5720, 验证集大小: 293\n", - "train_data最大日期: 2025-03-26, 训练天数20\n", - "test_data最大日期: 2025-03-28\n", - "划分后的训练集大小: 5721, 验证集大小: 283\n", - "train_data最大日期: 2025-03-27, 训练天数20\n", - "test_data最大日期: 2025-03-31\n", - "划分后的训练集大小: 5734, 验证集大小: 284\n", - "train_data最大日期: 2025-03-28, 训练天数20\n", - "test_data最大日期: 2025-04-01\n", - "划分后的训练集大小: 5742, 验证集大小: 291\n", - "train_data最大日期: 2025-03-31, 训练天数20\n", - "test_data最大日期: 2025-04-02\n", - "划分后的训练集大小: 5743, 验证集大小: 287\n", - "train_data最大日期: 2025-04-01, 训练天数20\n", - "test_data最大日期: 2025-04-03\n", - "划分后的训练集大小: 5739, 验证集大小: 287\n", - "train_data最大日期: 2025-04-02, 训练天数20\n", - "test_data最大日期: 2025-04-07\n", - "划分后的训练集大小: 5738, 验证集大小: 285\n", - "train_data最大日期: 2025-04-03, 训练天数20\n", - "test_data最大日期: 2025-04-08\n", - "划分后的训练集大小: 5733, 验证集大小: 287\n", - "train_data最大日期: 2025-04-07, 训练天数20\n", - "test_data最大日期: 2025-04-09\n", - "划分后的训练集大小: 5725, 验证集大小: 285\n" - ] - } - ], - "execution_count": 149 - }, - { - "cell_type": "code", - "id": "e01fe33b-e30d-4bc6-bf40-de91e61862b4", - "metadata": { - "ExecuteTime": { - "end_time": "2025-04-10T14:59:10.659121Z", - "start_time": "2025-04-10T14:59:10.651976Z" - } - }, - "source": [ - "print(final_predictions[['ts_code', 'trade_date', 'score']].tail())" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " ts_code trade_date score\n", - "561 000953.SZ 2025-04-02 1.055776\n", - "562 002667.SZ 2025-04-03 0.869295\n", - "563 002005.SZ 2025-04-07 -0.000617\n", - "564 603609.SH 2025-04-08 0.882270\n", - "565 600258.SH 2025-04-09 0.900722\n" - ] - } - ], - "execution_count": 142 + ] }, { "cell_type": "code", + "execution_count": 144, "id": "0dc75517-c857-4f1d-8815-e807400a6d33", "metadata": { "ExecuteTime": { @@ -3289,6 +3406,7 @@ "start_time": "2025-04-10T14:59:10.687575Z" } }, + "outputs": [], "source": [ "# pdf1 = select_pre_zt_stocks_dynamic(df[(df['trade_date'] >= '2022-03-26') & (df['trade_date'] <= '2029-03-26')])\n", "# pdf1 = pdf1.merge(industry_df, on=['cat_l2_code', 'trade_date'], how='left')\n", @@ -3334,12 +3452,11 @@ "# # pdf2 = pdf2[pdf2['trade_date'] <= '2025-03-26']\n", "# pdf2 = pdf2.sort_values(by=['ts_code', 'trade_date'])\n", "# filter_index2 = pdf2['future_return'].between(pdf2['future_return'].quantile(0.01), pdf2['future_return'].quantile(0.99))\n" - ], - "outputs": [], - "execution_count": 143 + ] }, { "cell_type": "code", + "execution_count": 145, "id": "8299a6f461097f14", "metadata": { "ExecuteTime": { @@ -3347,6 +3464,7 @@ "start_time": "2025-04-10T14:59:10.824106Z" } }, + "outputs": [], "source": [ "# are_equal = pdf1[filter_index1].equals(pdf2[filter_index2])\n", "# print(are_equal) # 输出 True 或 False\n", @@ -3356,12 +3474,11 @@ "#\n", "# are_equal = filter_index1.equals(filter_index2)\n", "# print(are_equal) # 输出 True 或 False" - ], - "outputs": [], - "execution_count": 144 + ] }, { "cell_type": "code", + "execution_count": 146, "id": "3f5079aa2c937c22", "metadata": { "ExecuteTime": { @@ -3369,6 +3486,7 @@ "start_time": "2025-04-10T14:59:10.837769Z" } }, + "outputs": [], "source": [ "# final_predictions1 = rolling_train_predict(\n", "# pdf1[(pdf1['trade_date'] >= '2024-12-01')], 5, 1, feature_columns,\n", @@ -3379,12 +3497,11 @@ "# pdf2[(pdf2['trade_date'] >= '2024-12-01')], 5, 1, feature_columns,\n", "# days=days, validation_days=0, filter_index=filter_index2, params=light_params)\n", "# final_predictions2.to_csv('test2.tsv', index=False)" - ], - "outputs": [], - "execution_count": 145 + ] }, { "cell_type": "code", + "execution_count": 147, "id": "199b12e7e20e4e6a", "metadata": { "ExecuteTime": { @@ -3392,20 +3509,19 @@ "start_time": "2025-04-10T14:59:10.887182Z" } }, + "outputs": [], "source": [ "# print(final_predictions1['trade_date'].max())\n", "# print(final_predictions2['trade_date'].max())\n", "#\n", "# are_equal = final_predictions1[(final_predictions1['trade_date'] >= '2022-12-01') & (final_predictions1['trade_date'] <= '2025-03-26')].equals(final_predictions2[(final_predictions2['trade_date'] >= '2022-12-01') & (final_predictions2['trade_date'] <= '2025-03-26')])\n", "# print(are_equal) # 输出 True 或 False" - ], - "outputs": [], - "execution_count": 146 + ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "new_trader", "language": "python", "name": "python3" }, diff --git a/code/train/TRank.ipynb b/main/train/TRank.ipynb similarity index 99% rename from code/train/TRank.ipynb rename to main/train/TRank.ipynb index 2eae67f..85cbcdb 100644 --- a/code/train/TRank.ipynb +++ b/main/train/TRank.ipynb @@ -37,7 +37,7 @@ } }, "source": [ - "from utils.utils import read_and_merge_h5_data\n", + "from code.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", @@ -587,7 +587,7 @@ } }, "source": [ - "from utils.factor import get_act_factor\n", + "from code.utils.factor import get_act_factor\n", "\n", "\n", "def read_industry_data(h5_filename):\n", @@ -1205,10 +1205,6 @@ "cell_type": "code", "source": [ "import torch\n", - "import torch.nn as nn\n", - "import torch.optim as optim\n", - "from torch.utils.data import DataLoader, Dataset\n", - "from sklearn.metrics import accuracy_score\n", "\n", "num_heads = 4 # Transformer头数\n", "transformer_input_dim = 64\n", @@ -1243,8 +1239,6 @@ "cell_type": "code", "source": [ "from tqdm import tqdm\n", - "import torch\n", - "import torch.nn as nn\n", "import torch.optim as optim\n", "from sklearn.preprocessing import LabelEncoder, StandardScaler\n", "from sklearn.metrics import accuracy_score\n", diff --git a/code/train/Transformer.ipynb b/main/train/Transformer.ipynb similarity index 99% rename from code/train/Transformer.ipynb rename to main/train/Transformer.ipynb index 9118ecf..a4cf32a 100644 --- a/code/train/Transformer.ipynb +++ b/main/train/Transformer.ipynb @@ -44,7 +44,7 @@ }, "source": [ "\n", - "from utils.utils import read_and_merge_h5_data\n", + "from code.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", diff --git a/code/train/UpdateClassify.ipynb b/main/train/UpdateClassify.ipynb similarity index 99% rename from code/train/UpdateClassify.ipynb rename to main/train/UpdateClassify.ipynb index d422c96..42b1fe3 100644 --- a/code/train/UpdateClassify.ipynb +++ b/main/train/UpdateClassify.ipynb @@ -33,7 +33,7 @@ } }, "source": [ - "from utils.utils import read_and_merge_h5_data\n", + "from code.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", diff --git a/code/train/UpdateRank.ipynb b/main/train/UpdateRank.ipynb similarity index 99% rename from code/train/UpdateRank.ipynb rename to main/train/UpdateRank.ipynb index 9e634e9..803f61d 100644 --- a/code/train/UpdateRank.ipynb +++ b/main/train/UpdateRank.ipynb @@ -33,7 +33,7 @@ } }, "source": [ - "from utils.utils import read_and_merge_h5_data\n", + "from code.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", @@ -592,7 +592,7 @@ } }, "source": [ - "from utils.factor import get_act_factor\n", + "from code.utils.factor import get_act_factor\n", "\n", "\n", "def read_industry_data(h5_filename):\n", @@ -735,9 +735,6 @@ }, "source": [ "from scipy.stats import ks_2samp, wasserstein_distance\n", - "from sklearn.metrics import roc_auc_score\n", - "from sklearn.model_selection import train_test_split\n", - "from sklearn.preprocessing import StandardScaler\n", "\n", "\n", "def remove_shifted_features(train_data, test_data, feature_columns, ks_threshold=0.1, wasserstein_threshold=0.15,\n", @@ -902,7 +899,6 @@ "\n", "\n", "import pandas as pd\n", - "from sklearn.preprocessing import StandardScaler\n", "\n", "\n", "def cross_sectional_standardization(df, features):\n", diff --git a/code/train/UpdateRegression.ipynb b/main/train/UpdateRegression.ipynb similarity index 99% rename from code/train/UpdateRegression.ipynb rename to main/train/UpdateRegression.ipynb index 0956196..6cb6350 100644 --- a/code/train/UpdateRegression.ipynb +++ b/main/train/UpdateRegression.ipynb @@ -41,7 +41,7 @@ } }, "source": [ - "from utils.utils import read_and_merge_h5_data\n", + "from code.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", diff --git a/code/train/UpdateSGD.ipynb b/main/train/UpdateSGD.ipynb similarity index 99% rename from code/train/UpdateSGD.ipynb rename to main/train/UpdateSGD.ipynb index 23daaa2..d12e240 100644 --- a/code/train/UpdateSGD.ipynb +++ b/main/train/UpdateSGD.ipynb @@ -36,7 +36,7 @@ } }, "source": [ - "from utils.utils import read_and_merge_h5_data\n", + "from code.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", @@ -142,8 +142,7 @@ } }, "source": [ - "import pandas as pd\n", - "import numpy as np\n", + "\n", "\n", "\n", "def calculate_indicators(df):\n", @@ -791,15 +790,11 @@ } }, "source": [ - "from sklearn.linear_model import SGDRegressor\n", - "from sklearn.preprocessing import StandardScaler, OneHotEncoder\n", - "from sklearn.compose import ColumnTransformer\n", - "from sklearn.pipeline import Pipeline\n", - "import matplotlib.pyplot as plt\n", + "\n", "import numpy as np\n", "\n", "import pandas as pd\n", - "from sklearn.linear_model import SGDRegressor, LinearRegression\n", + "from sklearn.linear_model import LinearRegression\n", "from sklearn.preprocessing import StandardScaler, OneHotEncoder\n", "\n", "\n", @@ -1304,7 +1299,6 @@ }, "source": [ "import joblib\n", - "import lightgbm as lgb\n", "import pandas as pd\n", "\n", "\n", diff --git a/code/train/V1-copy.ipynb b/main/train/V1-copy.ipynb similarity index 99% rename from code/train/V1-copy.ipynb rename to main/train/V1-copy.ipynb index e0d0446..3acbfea 100644 --- a/code/train/V1-copy.ipynb +++ b/main/train/V1-copy.ipynb @@ -12,7 +12,7 @@ "%load_ext autoreload\n", "%autoreload 2\n", "\n", - "from utils.utils import read_and_merge_h5_data" + "from code.utils.utils import read_and_merge_h5_data" ], "id": "79a7758178bafdd3", "outputs": [], diff --git a/code/train/V1.1.ipynb b/main/train/V1.1.ipynb similarity index 100% rename from code/train/V1.1.ipynb rename to main/train/V1.1.ipynb diff --git a/code/train/V1.ipynb b/main/train/V1.ipynb similarity index 99% rename from code/train/V1.ipynb rename to main/train/V1.ipynb index 06b8da6..5e4608c 100644 --- a/code/train/V1.ipynb +++ b/main/train/V1.ipynb @@ -12,7 +12,7 @@ "%load_ext autoreload\n", "%autoreload 2\n", "\n", - "from utils.utils import read_and_merge_h5_data\n" + "from code.utils.utils import read_and_merge_h5_data\n" ], "id": "79a7758178bafdd3", "outputs": [ diff --git a/main/train/__init__.py b/main/train/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/code/train/best_model.pth b/main/train/best_model.pth similarity index 100% rename from code/train/best_model.pth rename to main/train/best_model.pth diff --git a/code/train/catboost_info/catboost_training.json b/main/train/catboost_info/catboost_training.json similarity index 100% rename from code/train/catboost_info/catboost_training.json rename to main/train/catboost_info/catboost_training.json diff --git a/code/train/catboost_info/learn/events.out.tfevents b/main/train/catboost_info/learn/events.out.tfevents similarity index 100% rename from code/train/catboost_info/learn/events.out.tfevents rename to main/train/catboost_info/learn/events.out.tfevents diff --git a/code/train/catboost_info/learn_error.tsv b/main/train/catboost_info/learn_error.tsv similarity index 100% rename from code/train/catboost_info/learn_error.tsv rename to main/train/catboost_info/learn_error.tsv diff --git a/code/train/catboost_info/test/events.out.tfevents b/main/train/catboost_info/test/events.out.tfevents similarity index 100% rename from code/train/catboost_info/test/events.out.tfevents rename to main/train/catboost_info/test/events.out.tfevents diff --git a/code/train/catboost_info/test1/events.out.tfevents b/main/train/catboost_info/test1/events.out.tfevents similarity index 100% rename from code/train/catboost_info/test1/events.out.tfevents rename to main/train/catboost_info/test1/events.out.tfevents diff --git a/code/train/catboost_info/test_error.tsv b/main/train/catboost_info/test_error.tsv similarity index 100% rename from code/train/catboost_info/test_error.tsv rename to main/train/catboost_info/test_error.tsv diff --git a/code/train/catboost_info/time_left.tsv b/main/train/catboost_info/time_left.tsv similarity index 100% rename from code/train/catboost_info/time_left.tsv rename to main/train/catboost_info/time_left.tsv diff --git a/code/train/catboost_info/tmp/cat_feature_index.7083db21-2a602535-fc74a793-f24f9ca1.tmp b/main/train/catboost_info/tmp/cat_feature_index.7083db21-2a602535-fc74a793-f24f9ca1.tmp similarity index 100% rename from code/train/catboost_info/tmp/cat_feature_index.7083db21-2a602535-fc74a793-f24f9ca1.tmp rename to main/train/catboost_info/tmp/cat_feature_index.7083db21-2a602535-fc74a793-f24f9ca1.tmp diff --git a/code/train/catboost_info/tmp/cat_feature_index.c8182d04-ba2f7d40-17be2993-7ebf9fac.tmp b/main/train/catboost_info/tmp/cat_feature_index.c8182d04-ba2f7d40-17be2993-7ebf9fac.tmp similarity index 100% rename from code/train/catboost_info/tmp/cat_feature_index.c8182d04-ba2f7d40-17be2993-7ebf9fac.tmp rename to main/train/catboost_info/tmp/cat_feature_index.c8182d04-ba2f7d40-17be2993-7ebf9fac.tmp diff --git a/code/train/catboost_info/tmp/cat_feature_index.d44701fd-1e864ae1-30a8fb3f-c689cc7f.tmp b/main/train/catboost_info/tmp/cat_feature_index.d44701fd-1e864ae1-30a8fb3f-c689cc7f.tmp similarity index 100% rename from code/train/catboost_info/tmp/cat_feature_index.d44701fd-1e864ae1-30a8fb3f-c689cc7f.tmp rename to main/train/catboost_info/tmp/cat_feature_index.d44701fd-1e864ae1-30a8fb3f-c689cc7f.tmp diff --git a/code/train/catboost_info/tmp/cat_feature_index.ea11afac-3656bfa6-7640c1ea-9df76981.tmp b/main/train/catboost_info/tmp/cat_feature_index.ea11afac-3656bfa6-7640c1ea-9df76981.tmp similarity index 100% rename from code/train/catboost_info/tmp/cat_feature_index.ea11afac-3656bfa6-7640c1ea-9df76981.tmp rename to main/train/catboost_info/tmp/cat_feature_index.ea11afac-3656bfa6-7640c1ea-9df76981.tmp diff --git a/code/train/code.ipynb b/main/train/code.ipynb similarity index 100% rename from code/train/code.ipynb rename to main/train/code.ipynb diff --git a/code/train/predictions.tsv b/main/train/predictions.tsv similarity index 100% rename from code/train/predictions.tsv rename to main/train/predictions.tsv diff --git a/main/train/predictions_test.tsv b/main/train/predictions_test.tsv new file mode 100644 index 0000000..1669567 --- /dev/null +++ b/main/train/predictions_test.tsv @@ -0,0 +1,565 @@ +trade_date,score,ts_code +2022-12-08,0.27431420966080605,600778.SH +2022-12-09,0.6150539465999814,002995.SZ +2022-12-12,0.32582588516973016,001219.SZ +2022-12-13,0.449772253615743,603183.SH +2022-12-14,0.6769511128551923,001219.SZ +2022-12-15,0.5930979713048357,001219.SZ +2022-12-16,0.43211109874606424,603183.SH +2022-12-19,0.5066203384263489,000892.SZ +2022-12-20,0.2882618462700443,000691.SZ +2022-12-21,0.40494380930765467,001219.SZ +2022-12-22,0.7379517535413331,002762.SZ +2022-12-23,0.5775898117404806,002566.SZ +2022-12-26,0.3292293609625978,002719.SZ +2022-12-27,0.580738686242899,000679.SZ +2022-12-28,0.5180122078878033,605289.SH +2022-12-29,0.643325626734685,002103.SZ +2022-12-30,0.5378362015974298,603209.SH +2023-01-03,0.36814451293952416,000985.SZ +2023-01-04,0.4506419163930136,605133.SH +2023-01-05,-0.08745711573292192,605167.SH +2023-01-06,0.3958417326952953,605289.SH +2023-01-09,0.16620697664167175,600778.SH +2023-01-10,0.25992110313636035,000985.SZ +2023-01-11,0.5095437644681087,002771.SZ +2023-01-12,0.4397750442288285,605258.SH +2023-01-13,0.6102622318789971,003043.SZ +2023-01-16,0.31204276505440004,002808.SZ +2023-01-17,0.4972787924897241,002975.SZ +2023-01-18,0.026553404105244968,002975.SZ +2023-01-19,0.29558268580158115,603860.SH +2023-01-20,0.2510349420297213,002849.SZ +2023-01-30,0.21942028551157527,003039.SZ +2023-01-31,0.3575069234093295,605081.SH +2023-02-01,0.4427957172082794,002893.SZ +2023-02-02,0.6212207641739337,600817.SH +2023-02-03,0.6202750689624308,002993.SZ +2023-02-06,0.20845430964837489,000010.SZ +2023-02-07,0.3667829939094325,600593.SH +2023-02-08,0.32215761217132205,000820.SZ +2023-02-09,0.1516026707537734,002021.SZ +2023-02-10,0.5453734923733047,003016.SZ +2023-02-13,0.7491169288183265,003037.SZ +2023-02-14,0.32476512974212635,002828.SZ +2023-02-15,0.6984519009806621,605128.SH +2023-02-16,0.2078494458450699,605378.SH +2023-02-17,0.20087261579967608,000668.SZ +2023-02-20,0.6724819126277912,002715.SZ +2023-02-21,0.7209181859866042,605028.SH +2023-02-22,0.42474501256326314,002900.SZ +2023-02-23,0.43124729325039124,001236.SZ +2023-02-24,0.6008854884810912,603102.SH +2023-02-27,0.5702542696831331,605259.SH +2023-02-28,0.24318268223778186,002857.SZ +2023-03-01,0.5388577927345274,603950.SH +2023-03-02,0.6815724852841429,001236.SZ +2023-03-03,0.6064483180272962,002098.SZ +2023-03-06,0.5180664638865109,605178.SH +2023-03-07,0.7291442722387731,001339.SZ +2023-03-08,0.3240206100047592,603268.SH +2023-03-09,0.5619204909224714,603030.SH +2023-03-10,0.6055962888677536,003027.SZ +2023-03-13,0.10471064296768949,605296.SH +2023-03-14,0.5148688231123284,603176.SH +2023-03-15,0.41425644779572274,605287.SH +2023-03-16,0.3858205191834723,605303.SH +2023-03-17,0.38210649704563177,002899.SZ +2023-03-20,0.20755090351337924,002778.SZ +2023-03-21,0.2184477420463366,603155.SH +2023-03-22,0.07842488490864312,002836.SZ +2023-03-23,0.26327386834675565,002899.SZ +2023-03-24,0.21281930224537013,605086.SH +2023-03-27,0.19455767073518335,603729.SH +2023-03-28,0.18440479662298903,603324.SH +2023-03-29,0.5577394899737692,002995.SZ +2023-03-30,0.28537485170922117,603679.SH +2023-03-31,0.30705863202777134,603615.SH +2023-04-03,0.43719928717137047,603321.SH +2023-04-04,0.7949399014212187,603139.SH +2023-04-06,0.5079656399994698,002715.SZ +2023-04-07,0.701235747536229,605299.SH +2023-04-10,0.5142089175897191,001316.SZ +2023-04-11,0.6097058153625001,002835.SZ +2023-04-12,0.42821688099056865,003043.SZ +2023-04-13,0.6086458195457266,605296.SH +2023-04-14,0.40520429106061684,001316.SZ +2023-04-17,0.7332476184295339,002862.SZ +2023-04-18,0.24978196798538302,600768.SH +2023-04-19,0.5235224445388739,603657.SH +2023-04-20,0.5073410973887871,000702.SZ +2023-04-21,0.25827344858110657,002848.SZ +2023-04-24,0.441433820804789,603685.SH +2023-04-25,0.45710917638850534,603230.SH +2023-04-26,0.28288056233393655,002725.SZ +2023-04-27,0.13616135413238703,002972.SZ +2023-04-28,0.26068199992734814,603178.SH +2023-05-04,0.5654404518697154,600107.SH +2023-05-05,0.26758125911217795,603021.SH +2023-05-08,0.23558429168600836,002778.SZ +2023-05-09,0.2707962779077066,603213.SH +2023-05-10,0.33701828135159717,600778.SH +2023-05-11,0.5467076847749692,603958.SH +2023-05-12,0.6956005090125644,603958.SH +2023-05-15,0.28587355864974423,000679.SZ +2023-05-16,0.6092507418432053,600796.SH +2023-05-17,0.4723632871528185,002633.SZ +2023-05-18,0.44171920333992315,605089.SH +2023-05-19,0.15743942037394715,001317.SZ +2023-05-22,0.47338926108587503,603151.SH +2023-05-23,0.7537765588258426,603721.SH +2023-05-24,0.35894033254239865,003007.SZ +2023-05-25,0.6230303733419829,003005.SZ +2023-05-26,0.5243725213664181,003005.SZ +2023-05-29,0.5460639613578377,001288.SZ +2023-05-30,-0.14324964018444036,605151.SH +2023-05-31,0.1321851497388741,003041.SZ +2023-06-01,0.488265280236323,603170.SH +2023-06-02,0.2725329302903607,002875.SZ +2023-06-05,0.4445215836414108,001316.SZ +2023-06-06,0.233866225393599,600753.SH +2023-06-07,0.1512953839015877,603097.SH +2023-06-08,0.5303933339784708,002780.SZ +2023-06-09,0.595474766855165,002893.SZ +2023-06-12,0.7044220035173576,002820.SZ +2023-06-13,0.46792362066084003,002702.SZ +2023-06-14,0.5917956764629129,000880.SZ +2023-06-15,0.3231002542961875,002981.SZ +2023-06-16,0.3426911954075076,600847.SH +2023-06-19,-0.02150391139369695,603132.SH +2023-06-20,0.6704208966606625,002949.SZ +2023-06-21,0.7415606269689047,002806.SZ +2023-06-26,0.2389199769543643,600847.SH +2023-06-27,0.2003853580878301,605169.SH +2023-06-28,0.46623595119888966,605218.SH +2023-06-29,0.5589108980336046,603958.SH +2023-06-30,0.6290675381060588,603286.SH +2023-07-03,0.30433310431106353,600778.SH +2023-07-04,0.41651276650561014,002513.SZ +2023-07-05,0.3473548650199746,603132.SH +2023-07-06,0.40969750497772167,002591.SZ +2023-07-07,0.2430362735691786,001231.SZ +2023-07-10,0.4983750803303532,001267.SZ +2023-07-11,0.733386176985722,002551.SZ +2023-07-12,0.7936049551065578,000004.SZ +2023-07-13,0.40916765144188155,000638.SZ +2023-07-14,0.21996055437116258,605580.SH +2023-07-17,0.22071234127281886,605369.SH +2023-07-18,0.32992331418284704,002802.SZ +2023-07-19,0.3337178034533016,001222.SZ +2023-07-20,0.44391528952121656,600234.SH +2023-07-21,0.5703993630872055,600448.SH +2023-07-24,0.13840705878806345,002753.SZ +2023-07-25,0.14047801960398054,002377.SZ +2023-07-26,0.44664932418756537,003032.SZ +2023-07-27,0.5452266508240136,603838.SH +2023-07-28,0.6501197606840003,002397.SZ +2023-07-31,0.8161881604231447,002397.SZ +2023-08-01,0.8054314713785248,002397.SZ +2023-08-02,0.5699471212343736,600119.SH +2023-08-03,0.3961899673469923,002787.SZ +2023-08-04,0.747143154431229,600082.SH +2023-08-07,0.5542807418220157,605162.SH +2023-08-08,0.4660499541690493,605369.SH +2023-08-09,0.1846960748819621,605060.SH +2023-08-10,0.6657079567366654,003020.SZ +2023-08-11,0.657079341742516,000953.SZ +2023-08-14,0.42201036027484534,002495.SZ +2023-08-15,0.4783974585467736,002495.SZ +2023-08-16,0.5756999736912221,003030.SZ +2023-08-17,0.7578964013923504,002052.SZ +2023-08-18,0.004897979672684783,603151.SH +2023-08-21,0.11103177103375994,605339.SH +2023-08-22,0.5482563310657345,603021.SH +2023-08-23,0.7223546665888397,000669.SZ +2023-08-24,0.750140979575826,600235.SH +2023-08-25,0.34893747282432125,001318.SZ +2023-08-28,0.17097259367409923,603329.SH +2023-08-29,0.290639411928478,001267.SZ +2023-08-30,0.07811174210597455,603021.SH +2023-08-31,0.2910924076064356,603838.SH +2023-09-01,0.6435370857973789,002696.SZ +2023-09-04,0.04554737468797225,605259.SH +2023-09-05,0.18999529865866976,001231.SZ +2023-09-06,0.47343827547785233,002982.SZ +2023-09-07,0.5685183560937441,001231.SZ +2023-09-08,0.4682919982486746,003025.SZ +2023-09-11,0.511414318533627,002535.SZ +2023-09-12,0.34217637355801866,003020.SZ +2023-09-13,0.4524964916922371,001269.SZ +2023-09-14,0.6440683894231696,002856.SZ +2023-09-15,0.6265975964127983,001269.SZ +2023-09-18,0.6451154901817582,002857.SZ +2023-09-19,0.41416994363886955,605151.SH +2023-09-20,0.4097659657161061,600615.SH +2023-09-21,0.3308468663518861,603616.SH +2023-09-22,0.7278118492027132,600608.SH +2023-09-25,0.3087058065638187,002963.SZ +2023-09-26,0.44957535540535354,000638.SZ +2023-09-27,0.6529354742977974,000609.SZ +2023-09-28,0.5608546287364546,605080.SH +2023-10-09,0.2684689324603092,000004.SZ +2023-10-10,0.7514903867910352,001337.SZ +2023-10-11,0.6833204831817536,000010.SZ +2023-10-12,0.6849345854259707,001288.SZ +2023-10-13,0.45213040270359944,001223.SZ +2023-10-16,0.44367713319364266,001311.SZ +2023-10-17,0.4358062265247695,001266.SZ +2023-10-18,0.7896227965981543,002535.SZ +2023-10-19,0.8091287635227896,000609.SZ +2023-10-20,0.7497841605463051,000705.SZ +2023-10-23,0.5162013866354915,600615.SH +2023-10-24,0.5346626404470584,000554.SZ +2023-10-25,0.31017050910898813,002836.SZ +2023-10-26,0.41928742617604475,002798.SZ +2023-10-27,0.6206681321070086,600791.SH +2023-10-30,0.2809249638133884,600697.SH +2023-10-31,0.41380204486883465,605299.SH +2023-11-01,0.3913649017002345,002952.SZ +2023-11-02,0.25190883932779223,603272.SH +2023-11-03,0.25483193696737405,600697.SH +2023-11-06,0.6843951349633363,603900.SH +2023-11-07,0.6794539224187386,002005.SZ +2023-11-08,0.2787637201989255,605337.SH +2023-11-09,0.43869442213023335,603307.SH +2023-11-10,0.2901012944614997,002615.SZ +2023-11-13,0.48928158926409887,003020.SZ +2023-11-14,0.46232236500040824,603268.SH +2023-11-15,0.6895995906987776,000010.SZ +2023-11-16,0.45286066066734804,001298.SZ +2023-11-17,0.7301876489705413,000010.SZ +2023-11-20,0.7343688038104235,000004.SZ +2023-11-21,0.23914531702237296,600361.SH +2023-11-22,0.6007850824537518,002735.SZ +2023-11-23,0.6504458118708949,603655.SH +2023-11-24,0.46094310596129545,002842.SZ +2023-11-27,0.6273014444813882,603729.SH +2023-11-28,0.44076850931480105,002188.SZ +2023-11-29,0.2215431212240851,605598.SH +2023-11-30,0.47752407474308556,002247.SZ +2023-12-01,0.5451043441108514,603045.SH +2023-12-04,0.37633081988016603,603183.SH +2023-12-05,0.7161351255511346,000929.SZ +2023-12-06,0.467262040140511,002848.SZ +2023-12-07,0.39095280707015256,600883.SH +2023-12-08,0.4253618928722024,001373.SZ +2023-12-11,0.2828511933586843,002753.SZ +2023-12-12,0.5178019880022604,600099.SH +2023-12-13,0.5539680447662736,000702.SZ +2023-12-14,0.7341038153763678,000609.SZ +2023-12-15,0.5186263801346903,002495.SZ +2023-12-18,0.45857609424669377,002835.SZ +2023-12-19,0.4688848440159626,000004.SZ +2023-12-20,0.528943977776945,002571.SZ +2023-12-21,0.22718655814787117,000020.SZ +2023-12-22,0.5928968748610841,002052.SZ +2023-12-25,0.3483050510250432,001201.SZ +2023-12-26,0.32710963694268524,600778.SH +2023-12-27,0.8023420246227895,603061.SH +2023-12-28,0.6117050022740004,001223.SZ +2023-12-29,0.30978967753335185,600791.SH +2024-01-02,0.7213136293380755,001339.SZ +2024-01-03,0.27966735262244463,603255.SH +2024-01-04,0.4113020595403535,002569.SZ +2024-01-05,0.5862299814842546,002397.SZ +2024-01-08,0.2751799131522872,002207.SZ +2024-01-09,0.3983758716199103,600462.SH +2024-01-10,0.27583418641572577,002381.SZ +2024-01-11,0.5163369671207251,001308.SZ +2024-01-12,0.1869457075250673,001333.SZ +2024-01-15,0.38890973684559677,600778.SH +2024-01-16,0.628350379976437,002973.SZ +2024-01-17,0.567643592779436,001259.SZ +2024-01-18,0.45358303557672786,605003.SH +2024-01-19,0.17143291074790643,605003.SH +2024-01-22,0.38033202178552034,600137.SH +2024-01-23,0.2682996358307573,603307.SH +2024-01-24,0.09762885957278981,002200.SZ +2024-01-25,0.2791248936457759,600322.SH +2024-01-26,0.6698373552722515,001300.SZ +2024-01-29,0.3453399213641141,001212.SZ +2024-01-30,0.2018197040316142,000609.SZ +2024-01-31,0.13839592742162668,002862.SZ +2024-02-01,0.304731410465905,002397.SZ +2024-02-02,0.4968354158495188,600791.SH +2024-02-05,0.6076128945914563,002883.SZ +2024-02-06,0.27529438199416406,001313.SZ +2024-02-07,0.7032777773473656,601279.SH +2024-02-08,0.5124649392628607,001339.SZ +2024-02-19,0.47259882754683735,600608.SH +2024-02-20,-0.14666017874686663,605289.SH +2024-02-21,0.658199830069353,001317.SZ +2024-02-22,-0.03512517816725689,603895.SH +2024-02-23,0.49070407040956543,603286.SH +2024-02-26,0.5874761268473985,603192.SH +2024-02-27,0.6988377838547319,002856.SZ +2024-02-28,0.2914956876374956,002513.SZ +2024-02-29,0.39780267057003393,002760.SZ +2024-03-01,0.270122240597905,000908.SZ +2024-03-04,0.6403053454891889,000622.SZ +2024-03-05,0.7898552633062592,002848.SZ +2024-03-06,0.3809277227308279,002888.SZ +2024-03-07,0.853455303753637,002199.SZ +2024-03-08,0.2596463171676263,603206.SH +2024-03-11,0.7555631824344116,002888.SZ +2024-03-12,0.22682284013007994,001298.SZ +2024-03-13,0.5422854277369126,001298.SZ +2024-03-14,0.45779191251005164,600444.SH +2024-03-15,0.6383376746111096,603023.SH +2024-03-18,0.3201696636960583,001217.SZ +2024-03-19,0.5996501433774017,001300.SZ +2024-03-20,0.679915283803281,603273.SH +2024-03-21,0.33698105904573383,000820.SZ +2024-03-22,0.5218294653991029,002272.SZ +2024-03-25,0.6420731490646061,002272.SZ +2024-03-26,0.2833072190123176,000985.SZ +2024-03-27,0.47781267803027716,002094.SZ +2024-03-28,0.3495920493283059,001368.SZ +2024-03-29,0.6814619283051855,605167.SH +2024-04-01,0.13340757834476227,002787.SZ +2024-04-02,0.6485933245807073,002629.SZ +2024-04-03,0.5005374249650347,600889.SH +2024-04-08,0.3458211793083884,002998.SZ +2024-04-09,0.4543106999676991,603268.SH +2024-04-10,0.3782795993313912,600289.SH +2024-04-11,0.5682306510607521,002999.SZ +2024-04-12,0.7563586796123376,001333.SZ +2024-04-15,0.6820371060072895,605259.SH +2024-04-16,0.20646370209051096,603177.SH +2024-04-17,0.397823887165538,001367.SZ +2024-04-18,0.5284023840181794,001288.SZ +2024-04-19,0.5531779115633008,603137.SH +2024-04-22,0.5285690780224108,603270.SH +2024-04-23,0.4948891344668796,605003.SH +2024-04-24,0.18745324505195465,002899.SZ +2024-04-25,0.5561527695473475,605287.SH +2024-04-26,0.3910800271321991,603813.SH +2024-04-29,0.22800522777162466,600083.SH +2024-04-30,0.47575239303431954,603991.SH +2024-05-06,0.24135147235517762,603991.SH +2024-05-07,0.6505337985284155,603955.SH +2024-05-08,-0.008011195615733824,000820.SZ +2024-05-09,0.3612607813981246,002848.SZ +2024-05-10,0.5729784871026853,002295.SZ +2024-05-13,0.42642459942636673,001228.SZ +2024-05-14,0.616654660412696,603150.SH +2024-05-15,0.14069160886717613,000995.SZ +2024-05-16,0.2893164763341796,002893.SZ +2024-05-17,0.26559138928183623,000593.SZ +2024-05-20,0.6202830453921107,605318.SH +2024-05-21,0.5962739970033031,002231.SZ +2024-05-22,0.24212148187955357,600408.SH +2024-05-23,0.5866973439001457,000056.SZ +2024-05-24,0.4950026505575876,002620.SZ +2024-05-27,0.2716568170957144,001367.SZ +2024-05-28,0.614584069552624,603062.SH +2024-05-29,0.5190445551469995,605567.SH +2024-05-30,0.5650446836882232,001299.SZ +2024-05-31,0.05127480727491687,002811.SZ +2024-06-03,0.34948424376750514,603276.SH +2024-06-04,0.2962107247005024,605365.SH +2024-06-05,0.4265710639424621,600793.SH +2024-06-06,0.2352508879607737,603193.SH +2024-06-07,0.48735816467729426,001306.SZ +2024-06-11,0.46798640763968125,002569.SZ +2024-06-12,0.47153387530090407,001215.SZ +2024-06-13,0.5520110912359264,603280.SH +2024-06-14,0.19454715507705864,605598.SH +2024-06-17,0.0963230849661045,002888.SZ +2024-06-18,-0.07804798333558113,002615.SZ +2024-06-19,0.610634439198648,002848.SZ +2024-06-20,0.528548137823535,002888.SZ +2024-06-21,0.5548914160403448,603097.SH +2024-06-24,0.39920928373259934,001336.SZ +2024-06-25,0.3187385965144802,603255.SH +2024-06-26,0.32449821843842575,002582.SZ +2024-06-27,0.5820092583390628,001387.SZ +2024-06-28,0.3390536217231177,600493.SH +2024-07-01,0.6778726275046811,002875.SZ +2024-07-02,0.20520989636689582,605255.SH +2024-07-03,0.5743008555845837,002495.SZ +2024-07-04,0.9067074210889693,002247.SZ +2024-07-05,0.7460402549612926,002094.SZ +2024-07-08,0.6484051297498691,002012.SZ +2024-07-09,0.4656464752321579,000056.SZ +2024-07-10,0.5651250099764935,600421.SH +2024-07-11,0.3919914680384934,000010.SZ +2024-07-12,0.6138163480575665,002861.SZ +2024-07-15,0.7779874584252403,605318.SH +2024-07-16,0.4480483662651257,001217.SZ +2024-07-17,0.4172203427639832,002094.SZ +2024-07-18,0.7704811874850669,600561.SH +2024-07-19,0.47075926007399854,002883.SZ +2024-07-22,0.7111332900169166,002094.SZ +2024-07-23,0.2511920883015322,002094.SZ +2024-07-24,0.4172115642303865,000017.SZ +2024-07-25,0.3128542596085397,002485.SZ +2024-07-26,0.7143056769547913,002800.SZ +2024-07-29,0.7348554188058531,001212.SZ +2024-07-30,0.6544735763131749,600678.SH +2024-07-31,0.33170353517328943,000619.SZ +2024-08-01,0.5622688198785722,603021.SH +2024-08-02,0.553901968970459,600355.SH +2024-08-05,0.7203888768112711,002652.SZ +2024-08-06,0.06845913509306308,002388.SZ +2024-08-07,0.32607442357451333,002647.SZ +2024-08-08,0.16487864247139689,600538.SH +2024-08-09,0.541184606366378,603657.SH +2024-08-12,0.26199426116748675,605180.SH +2024-08-13,0.3717346916082516,600281.SH +2024-08-14,0.6248284781260683,603151.SH +2024-08-15,0.4701587394310862,001288.SZ +2024-08-16,0.4980961856837102,600538.SH +2024-08-19,0.6280757532182177,002622.SZ +2024-08-20,0.4216952108346084,002094.SZ +2024-08-21,0.8872335373988718,001317.SZ +2024-08-22,0.5745604057145399,000056.SZ +2024-08-23,0.505744305740485,603122.SH +2024-08-26,0.8419619283484038,002072.SZ +2024-08-27,0.6810091806857562,002084.SZ +2024-08-28,0.6875657067302675,002072.SZ +2024-08-29,0.44229937751556,600692.SH +2024-08-30,0.5551556278148088,605100.SH +2024-09-02,0.6992787682915002,000004.SZ +2024-09-03,0.29976910325882256,001218.SZ +2024-09-04,0.6768429298090569,002717.SZ +2024-09-05,0.6206771676543172,002622.SZ +2024-09-06,0.7271087976454396,002861.SZ +2024-09-09,0.5051345343079485,002199.SZ +2024-09-10,0.606941618095118,600533.SH +2024-09-11,0.44563057902498,000632.SZ +2024-09-12,0.544127097473236,600889.SH +2024-09-13,0.8023872948409544,000702.SZ +2024-09-18,0.27102315041971126,002729.SZ +2024-09-19,0.8222201895966966,002629.SZ +2024-09-20,0.7270786760641249,001296.SZ +2024-09-23,0.6662260924500418,603778.SH +2024-09-24,0.45201298931080247,600322.SH +2024-09-25,0.7020112241768041,000573.SZ +2024-09-26,0.9042164267563502,600322.SH +2024-09-27,0.6660782992616279,600156.SH +2024-09-30,0.6863709879538685,600156.SH +2024-10-08,0.6127578713627717,600791.SH +2024-10-09,0.42399006878436024,002486.SZ +2024-10-10,0.19353810507130897,001379.SZ +2024-10-11,0.32072111647856577,001306.SZ +2024-10-14,0.3485052899457631,605169.SH +2024-10-15,0.23682051553226138,603130.SH +2024-10-16,0.06953864745342996,000695.SZ +2024-10-17,0.6514983088909282,600599.SH +2024-10-18,0.4389209760325074,002977.SZ +2024-10-21,0.7197768607464181,603261.SH +2024-10-22,0.6635882450692222,002806.SZ +2024-10-23,0.45400588695990063,603172.SH +2024-10-24,0.38830884380759845,600107.SH +2024-10-25,0.8934238346702872,600243.SH +2024-10-28,0.2797309154889543,600539.SH +2024-10-29,0.2969987104969114,600768.SH +2024-10-30,0.40287164661797453,002551.SZ +2024-10-31,0.3564256188428194,002086.SZ +2024-11-01,0.6321036634810505,002551.SZ +2024-11-04,0.5456367520507691,603682.SH +2024-11-05,0.4395577653608702,600802.SH +2024-11-06,0.6650198272206568,600243.SH +2024-11-07,0.43235770657243566,000430.SZ +2024-11-08,0.5643849119436214,002076.SZ +2024-11-11,0.7137448249635079,000007.SZ +2024-11-12,0.5201716819040841,000679.SZ +2024-11-13,0.6337096887315559,603214.SH +2024-11-14,0.4979383369165651,603201.SH +2024-11-15,0.45240021627553617,603201.SH +2024-11-18,0.22669552732089976,605177.SH +2024-11-19,0.38573714473203297,603331.SH +2024-11-20,0.2979426663257885,001378.SZ +2024-11-21,0.42041152689995204,002980.SZ +2024-11-22,0.4998877526755884,003003.SZ +2024-11-25,0.3166022033285757,002381.SZ +2024-11-26,0.5105217556899093,001300.SZ +2024-11-27,0.4581772650911419,603183.SH +2024-11-28,0.2582863137914294,600302.SH +2024-11-29,0.6516611156931627,600202.SH +2024-12-02,0.6264742314126309,603021.SH +2024-12-03,0.17876631396560913,605287.SH +2024-12-04,0.571815529773696,603637.SH +2024-12-05,0.5009836942607793,002615.SZ +2024-12-06,0.5935816089479097,002615.SZ +2024-12-09,0.2732740647491455,000880.SZ +2024-12-10,0.8403246240655503,002211.SZ +2024-12-11,0.6063814254598854,000952.SZ +2024-12-12,0.65530104057359,002213.SZ +2024-12-13,0.4326422618589393,002193.SZ +2024-12-16,0.6951945851895344,002582.SZ +2024-12-17,0.4367668554492269,002846.SZ +2024-12-18,0.6034186500026795,600844.SH +2024-12-19,0.6478095239545749,000695.SZ +2024-12-20,0.12678291780050743,002687.SZ +2024-12-23,0.0005977226174160126,603176.SH +2024-12-24,0.6931664421535906,000790.SZ +2024-12-25,0.9280754228431425,000004.SZ +2024-12-26,0.5025149234980124,603082.SH +2024-12-27,0.5619791111821815,603325.SH +2024-12-30,0.5970375646444621,603291.SH +2024-12-31,0.6210268787938896,603798.SH +2025-01-02,0.5150847228562943,603255.SH +2025-01-03,0.3936455223882481,001238.SZ +2025-01-06,0.42512989288467096,001256.SZ +2025-01-07,0.20046282642128765,002763.SZ +2025-01-08,0.3594789763099251,603137.SH +2025-01-09,0.17148562512671917,603150.SH +2025-01-10,0.8141461510449396,603909.SH +2025-01-13,0.7278259334554208,002365.SZ +2025-01-14,0.5332116728293693,000955.SZ +2025-01-15,0.26395540555061114,001223.SZ +2025-01-16,0.20321325349056088,603637.SH +2025-01-17,0.5155710238940482,000586.SZ +2025-01-20,0.7692783436325927,002072.SZ +2025-01-21,0.3743531875901297,601798.SH +2025-01-22,0.5609509357408301,603059.SH +2025-01-23,0.20207700522454125,001378.SZ +2025-01-24,0.3138610824997807,002760.SZ +2025-01-27,0.3887191549307029,603192.SH +2025-02-05,0.5131470103792286,600599.SH +2025-02-06,0.42133133196663924,603255.SH +2025-02-07,0.2981033776962701,605003.SH +2025-02-10,0.78495727017451,600225.SH +2025-02-11,0.9081192688195034,600225.SH +2025-02-12,0.8027995368952746,600225.SH +2025-02-13,0.6500142590724168,603789.SH +2025-02-14,0.7725392087365835,002058.SZ +2025-02-17,0.49255867173448825,600228.SH +2025-02-18,0.5576519868864848,600243.SH +2025-02-19,0.2592919075461544,002496.SZ +2025-02-20,0.421298468924212,002848.SZ +2025-02-21,0.29697775540100313,001316.SZ +2025-02-24,0.7638868267339545,603211.SH +2025-02-25,0.5526784340520452,003028.SZ +2025-02-26,0.5065861650146529,603716.SH +2025-02-27,0.7407038446632749,603211.SH +2025-02-28,0.4668521688585335,003043.SZ +2025-03-03,0.2680721667617631,600753.SH +2025-03-04,0.34955816615272756,002977.SZ +2025-03-05,0.7482244194415444,603057.SH +2025-03-06,0.6305254140888802,603280.SH +2025-03-07,0.19351037427994797,600241.SH +2025-03-10,0.38766031239447357,603325.SH +2025-03-11,0.4303913500153944,002872.SZ +2025-03-12,0.18459289202598228,002898.SZ +2025-03-13,0.8576596052682522,001319.SZ +2025-03-14,0.6230146680130096,000757.SZ +2025-03-17,0.7328875682123387,603843.SH +2025-03-18,0.47499472013228067,000669.SZ +2025-03-19,0.3268919044509167,002305.SZ +2025-03-20,0.4569272632462979,600356.SH +2025-03-21,0.07591851931376978,000586.SZ +2025-03-24,0.49072061900133407,603335.SH +2025-03-25,0.4306632631450777,603381.SH +2025-03-26,0.46006642069249487,001299.SZ +2025-03-27,0.41362062710862235,002394.SZ +2025-03-28,0.564157006795436,001332.SZ +2025-03-31,0.5981134959932276,001238.SZ +2025-04-01,0.6363729449100586,603102.SH +2025-04-02,0.2865246522723796,002872.SZ +2025-04-03,0.29802040504689753,000633.SZ +2025-04-07,0.554762051627518,002872.SZ +2025-04-08,0.34687738661031947,603682.SH +2025-04-09,0.13896921728258024,001331.SZ diff --git a/code/train/predictions_train.tsv b/main/train/predictions_train.tsv similarity index 100% rename from code/train/predictions_train.tsv rename to main/train/predictions_train.tsv diff --git a/main/train/test.py b/main/train/test.py new file mode 100644 index 0000000..509dae1 --- /dev/null +++ b/main/train/test.py @@ -0,0 +1,14 @@ +from operator import index + +import tushare as ts +import pandas as pd +import time + +ts.set_token('3a0741c702ee7e5e5f2bf1f0846bafaafe4e320833240b2a7e4a685f') +pro = ts.pro_api() + +df = pro.index_member_all(ts_code='603579.SH') +print(df) + +df = pro.sw_daily(trade_date='20250305', fields='ts_code,name,open,close,vol,pe,pb') +print(df[df['ts_code'] == '851171.SI']) \ No newline at end of file diff --git a/main/train/test1.tsv b/main/train/test1.tsv new file mode 100644 index 0000000..103561f --- /dev/null +++ b/main/train/test1.tsv @@ -0,0 +1,565 @@ +trade_date,score,ts_code +2022-12-08,1.2708337806641494,603816.SH +2022-12-09,1.4207120834806832,603567.SH +2022-12-12,1.0198883623815167,002305.SZ +2022-12-13,1.7022732146012465,002910.SZ +2022-12-14,0.4115956442621504,600493.SH +2022-12-15,1.2308250306434583,601858.SH +2022-12-16,0.5214964254452716,601677.SH +2022-12-19,1.5635207796349075,000721.SZ +2022-12-20,0.9950031675966513,002314.SZ +2022-12-21,1.867139344678808,603238.SH +2022-12-22,0.11397346668733664,002095.SZ +2022-12-23,0.7020503260530933,600706.SH +2022-12-26,1.064077090528082,002707.SZ +2022-12-27,0.5487905008977592,000978.SZ +2022-12-28,0.9795388321537417,600225.SH +2022-12-29,0.6402559056339422,600056.SH +2022-12-30,0.9466308655445547,002357.SZ +2023-01-03,0.6849950582517478,002031.SZ +2023-01-04,0.8958700703884613,003010.SZ +2023-01-05,0.9901544872773684,002357.SZ +2023-01-06,0.7029762528454185,000929.SZ +2023-01-09,1.2070723183050875,002279.SZ +2023-01-10,0.28632510343867906,002933.SZ +2023-01-11,0.7059503351778397,002576.SZ +2023-01-12,1.700028635026902,002576.SZ +2023-01-13,1.4228228373146723,002043.SZ +2023-01-16,0.24930703006686591,600958.SH +2023-01-17,1.0616927130654037,603882.SH +2023-01-18,0.6166412038694548,000739.SZ +2023-01-19,0.5967697229641841,603806.SH +2023-01-20,0.8290879039003781,600705.SH +2023-01-30,1.0826864888349266,000972.SZ +2023-01-31,1.7476350470413293,605133.SH +2023-02-01,1.0698795326344217,002297.SZ +2023-02-02,1.168956058233029,002762.SZ +2023-02-03,0.6068761459217956,002474.SZ +2023-02-06,1.3603267774479497,002855.SZ +2023-02-07,1.3722562072579707,002167.SZ +2023-02-08,1.444800461687164,002117.SZ +2023-02-09,0.6478721098934555,600501.SH +2023-02-10,1.7330712792214502,002122.SZ +2023-02-13,1.0751336841418047,603711.SH +2023-02-14,0.858121706097957,002354.SZ +2023-02-15,1.0628443879922715,600817.SH +2023-02-16,1.0941227999628862,002660.SZ +2023-02-17,0.5452970336991657,002792.SZ +2023-02-20,0.7452925786277558,600817.SH +2023-02-21,1.2263444506836183,601360.SH +2023-02-22,0.8498400500947443,002882.SZ +2023-02-23,1.3778772059701936,002942.SZ +2023-02-24,0.8116211264751758,002942.SZ +2023-02-27,1.369491951000112,600118.SH +2023-02-28,1.7437044662527195,600325.SH +2023-03-01,0.6172338223208104,002350.SZ +2023-03-02,0.9753294078191806,002261.SZ +2023-03-03,0.9460072368251595,605389.SH +2023-03-06,0.7661730237898733,000977.SZ +2023-03-07,1.5306012129925908,601728.SH +2023-03-08,1.7347243229852956,603042.SH +2023-03-09,1.7785688963407722,601698.SH +2023-03-10,1.794639030708944,002808.SZ +2023-03-13,2.2765957078169055,601728.SH +2023-03-14,1.5770232731123273,002236.SZ +2023-03-15,1.9886076279595977,601698.SH +2023-03-16,1.7538871949426555,601138.SH +2023-03-17,1.2850616649676168,000506.SZ +2023-03-20,0.6617355633181617,601117.SH +2023-03-21,1.2834165832572753,600633.SH +2023-03-22,1.286625601927238,002803.SZ +2023-03-23,1.2442366849499193,601138.SH +2023-03-24,1.7385288121049993,601138.SH +2023-03-27,0.5271836596864287,600633.SH +2023-03-28,0.9233261884964775,000890.SZ +2023-03-29,1.0388156797328032,600633.SH +2023-03-30,0.880222808466912,600975.SH +2023-03-31,1.7723670660012394,002153.SZ +2023-04-03,1.4447814388081068,600633.SH +2023-04-04,0.9805981968002965,000988.SZ +2023-04-06,1.2735568908129031,002558.SZ +2023-04-07,0.5977729773368881,002222.SZ +2023-04-10,0.36120306701232185,000032.SZ +2023-04-11,2.0134197062348904,603258.SH +2023-04-12,0.6807091195842823,603888.SH +2023-04-13,1.5510435282176684,600415.SH +2023-04-14,1.6158618609191548,603258.SH +2023-04-17,0.5935406330588169,603918.SH +2023-04-18,1.438798944751228,603258.SH +2023-04-19,0.4851330354034662,002975.SZ +2023-04-20,0.17004215747506052,600415.SH +2023-04-21,1.3733089702528274,601595.SH +2023-04-24,2.3249160418531685,603258.SH +2023-04-25,2.4887955829326054,601858.SH +2023-04-26,1.9420082198135482,601019.SH +2023-04-27,2.3040109178691113,601811.SH +2023-04-28,1.0754625899722956,601811.SH +2023-05-04,1.6688121146522907,601336.SH +2023-05-05,1.1037723664352612,601989.SH +2023-05-08,1.6994199603704685,601288.SH +2023-05-09,1.2636377329259567,002354.SZ +2023-05-10,1.2628967915122853,601949.SH +2023-05-11,0.8020741700988911,603083.SH +2023-05-12,0.22312816960298115,600629.SH +2023-05-15,0.7341052846591558,002229.SZ +2023-05-16,0.6350705971737554,603268.SH +2023-05-17,1.0396627856239795,603958.SH +2023-05-18,1.4091099521269763,601858.SH +2023-05-19,0.6341161328902458,600239.SH +2023-05-22,0.4664478043150085,603798.SH +2023-05-23,0.3950180406443093,002864.SZ +2023-05-24,0.9532057286987137,002366.SZ +2023-05-25,0.661525047825837,605011.SH +2023-05-26,0.873646794491419,600088.SH +2023-05-29,1.0161343809163572,600636.SH +2023-05-30,1.8522924730896868,603918.SH +2023-05-31,0.14065827549083917,002315.SZ +2023-06-01,1.0647192154325815,002229.SZ +2023-06-02,1.0897714474656055,605028.SH +2023-06-05,0.818149194152834,002995.SZ +2023-06-06,1.1559913886165554,002229.SZ +2023-06-07,0.9730919792856488,603933.SH +2023-06-08,1.1740853193005574,003010.SZ +2023-06-09,0.7055820145524615,002395.SZ +2023-06-12,0.8768369889703852,000977.SZ +2023-06-13,0.5333934871843615,600839.SH +2023-06-14,1.1828705214010444,002229.SZ +2023-06-15,1.9054644381740913,600602.SH +2023-06-16,1.6671793256997451,002920.SZ +2023-06-19,0.4424093682681172,002194.SZ +2023-06-20,0.7166566485622967,600100.SH +2023-06-21,1.185368125310508,600592.SH +2023-06-26,0.49477817284107434,605016.SH +2023-06-27,0.6467017315354233,002865.SZ +2023-06-28,1.4462997720570885,600310.SH +2023-06-29,0.9079748876905797,000809.SZ +2023-06-30,1.1417365323043627,002920.SZ +2023-07-03,1.0292231512798002,600105.SH +2023-07-04,0.9764499369108617,002355.SZ +2023-07-05,1.1950967963313073,603489.SH +2023-07-06,0.8067305519266362,603809.SH +2023-07-07,-0.11113958569144997,603786.SH +2023-07-10,1.4365223354022805,002835.SZ +2023-07-11,0.9055036034028278,603767.SH +2023-07-12,0.662603535490377,002265.SZ +2023-07-13,0.6580169744401991,605005.SH +2023-07-14,0.7806145283148259,002284.SZ +2023-07-17,0.8928179964563782,002616.SZ +2023-07-18,1.0102033286200784,603709.SH +2023-07-19,0.28926601683884473,603429.SH +2023-07-20,1.0778442223423874,603709.SH +2023-07-21,0.49418969039024113,000068.SZ +2023-07-24,1.152792861172028,002172.SZ +2023-07-25,-0.48175589465657037,000656.SZ +2023-07-26,0.9348834119551785,000608.SZ +2023-07-27,1.5360069738010982,600231.SH +2023-07-28,1.5514157372959714,600782.SH +2023-07-31,0.31996521869248884,000750.SZ +2023-08-01,0.5474246928163892,601860.SH +2023-08-02,1.277218052964389,600162.SH +2023-08-03,0.784739789704388,002400.SZ +2023-08-04,1.5311245596423297,002310.SZ +2023-08-07,1.3664999923727883,002377.SZ +2023-08-08,0.6775719496805408,000004.SZ +2023-08-09,1.0473202246778897,003005.SZ +2023-08-10,0.7095500932241833,600610.SH +2023-08-11,0.9061227536071346,000656.SZ +2023-08-14,1.712926297349448,600272.SH +2023-08-15,0.7173624078005385,000656.SZ +2023-08-16,1.0612842867590515,600155.SH +2023-08-17,-0.11242980180119416,002898.SZ +2023-08-18,0.7958378602153298,002221.SZ +2023-08-21,1.2818341414622505,000006.SZ +2023-08-22,1.4925485997390293,000006.SZ +2023-08-23,1.0205726179128791,002178.SZ +2023-08-24,0.5123818439671383,603000.SH +2023-08-25,1.1561261254770698,600684.SH +2023-08-28,1.0243035845865234,600602.SH +2023-08-29,0.48711445602297804,002279.SZ +2023-08-30,1.5892134655969756,002279.SZ +2023-08-31,0.8832038176770832,002235.SZ +2023-09-01,0.7778267621876187,002527.SZ +2023-09-04,0.9715794190221472,000766.SZ +2023-09-05,0.6363690681522609,000701.SZ +2023-09-06,1.1626954331771984,603078.SH +2023-09-07,0.8950438021027404,601001.SH +2023-09-08,0.4907685411177441,603722.SH +2023-09-11,1.4880263364860575,000851.SZ +2023-09-12,0.7192761393042792,603329.SH +2023-09-13,0.7091837012523252,000158.SZ +2023-09-14,0.7548676419597591,601699.SH +2023-09-15,0.9847401608369946,600257.SH +2023-09-18,1.7523740102808545,600814.SH +2023-09-19,0.9311114338734422,000536.SZ +2023-09-20,1.5094699709720083,002902.SZ +2023-09-21,1.10032325281936,603890.SH +2023-09-22,0.5455145772639567,001268.SZ +2023-09-25,1.5392432178391338,600895.SH +2023-09-26,1.139513204589203,002642.SZ +2023-09-27,1.0155696225867201,002827.SZ +2023-09-28,0.7631131516304462,001298.SZ +2023-10-09,1.3121583124085132,002513.SZ +2023-10-10,1.192726847273738,600895.SH +2023-10-11,1.378300878017007,600895.SH +2023-10-12,1.3374848728915072,000010.SZ +2023-10-13,1.247352917522287,000536.SZ +2023-10-16,1.0205919288199263,002456.SZ +2023-10-17,1.8124967402525871,601127.SH +2023-10-18,1.6545503123646252,002456.SZ +2023-10-19,1.403565591711023,603009.SH +2023-10-20,1.479674671497312,002176.SZ +2023-10-23,1.1364881895480998,002337.SZ +2023-10-24,1.603597993080332,002122.SZ +2023-10-25,1.2135837368610372,603123.SH +2023-10-26,1.0507834567170797,002671.SZ +2023-10-27,1.2313059730365872,603266.SH +2023-10-30,1.4721372697796935,603353.SH +2023-10-31,0.8329626816487755,600599.SH +2023-11-01,0.7167096735509632,603186.SH +2023-11-02,1.2802635893255563,601633.SH +2023-11-03,1.2590421473918265,002640.SZ +2023-11-06,0.7531355226612281,003040.SZ +2023-11-07,0.6577034595225779,603768.SH +2023-11-08,1.8511623446368441,002889.SZ +2023-11-09,1.4717991539794943,603266.SH +2023-11-10,0.46890167407485095,002584.SZ +2023-11-13,1.1394805956388967,603220.SH +2023-11-14,1.0217982309018654,603883.SH +2023-11-15,0.6661740992600602,600106.SH +2023-11-16,1.3719183134079516,002584.SZ +2023-11-17,1.5131093025579179,603266.SH +2023-11-20,1.6229596862404188,002103.SZ +2023-11-21,0.9595377845746105,002291.SZ +2023-11-22,1.1541727944363123,600506.SH +2023-11-23,0.6172428975445288,000029.SZ +2023-11-24,0.3581002329377547,000710.SZ +2023-11-27,1.0865084227106108,002691.SZ +2023-11-28,0.36551005528336356,603721.SH +2023-11-29,0.6325689777455243,600780.SH +2023-11-30,0.9673540111120547,600148.SH +2023-12-01,1.6031005266953247,002238.SZ +2023-12-04,0.3139675086881397,601156.SH +2023-12-05,0.4471787298728605,002238.SZ +2023-12-06,1.519722809770461,600128.SH +2023-12-07,0.9306959636346697,601599.SH +2023-12-08,1.11955592447384,600678.SH +2023-12-11,0.920549654717692,600981.SH +2023-12-12,1.1192972102468268,603999.SH +2023-12-13,1.059999047897406,601595.SH +2023-12-14,1.3135891954923238,605577.SH +2023-12-15,1.0121314183460337,603358.SH +2023-12-18,1.2923561000198138,601595.SH +2023-12-19,0.4727877781694898,002856.SZ +2023-12-20,-0.01336745195249621,002937.SZ +2023-12-21,0.9432845860894691,603825.SH +2023-12-22,1.5927408932377043,603167.SH +2023-12-25,1.1489781197830256,001314.SZ +2023-12-26,2.075405615830143,605117.SH +2023-12-27,1.1634401553385165,002952.SZ +2023-12-28,1.9276471606617283,600610.SH +2023-12-29,1.1664703290672742,600621.SH +2024-01-02,1.4036288516782467,603396.SH +2024-01-03,0.6064916104024441,603052.SH +2024-01-04,0.27194078032727403,000810.SZ +2024-01-05,0.7248236406349026,002884.SZ +2024-01-08,1.917000409535365,600983.SH +2024-01-09,1.3670026723187405,601225.SH +2024-01-10,0.6428111801429847,002419.SZ +2024-01-11,0.8603468507895841,603097.SH +2024-01-12,0.8616154280062541,001269.SZ +2024-01-15,1.6162131927011785,002140.SZ +2024-01-16,1.6278064283944136,605366.SH +2024-01-17,1.2234956634332685,603556.SH +2024-01-18,0.2930411130350266,601865.SH +2024-01-19,0.5463003069500746,002033.SZ +2024-01-22,1.4568410090998594,600438.SH +2024-01-23,0.4796543547584619,002056.SZ +2024-01-24,1.1933657317735877,000921.SZ +2024-01-25,1.1196073236619992,000070.SZ +2024-01-26,0.22116166302548493,601225.SH +2024-01-29,1.0247482544629951,000717.SZ +2024-01-30,1.0239528607343813,000651.SZ +2024-01-31,1.371059822546121,600188.SH +2024-02-01,0.9952706097104108,601288.SH +2024-02-02,1.1495734266360917,601658.SH +2024-02-05,0.29949274220153294,600018.SH +2024-02-06,1.299845239071017,600004.SH +2024-02-07,1.000836675958177,600350.SH +2024-02-08,0.6401652900537433,600131.SH +2024-02-19,0.08475967168317824,002749.SZ +2024-02-20,0.5804821425310229,002085.SZ +2024-02-21,0.5362126107341831,600053.SH +2024-02-22,1.206204153162725,600639.SH +2024-02-23,1.6848572441872742,603958.SH +2024-02-26,-0.22984388407558198,000017.SZ +2024-02-27,-0.02096017812243845,603011.SH +2024-02-28,1.1590591855669556,603933.SH +2024-02-29,0.1819266114797644,002085.SZ +2024-03-01,0.6727454630028324,600571.SH +2024-03-04,1.113038537485005,601058.SH +2024-03-05,0.8578265704409984,600160.SH +2024-03-06,1.0085170866918751,600331.SH +2024-03-07,1.0605223005794961,603220.SH +2024-03-08,0.9054891550652612,601001.SH +2024-03-11,1.3538575989659345,002085.SZ +2024-03-12,0.9889456011580702,603580.SH +2024-03-13,1.212835645663162,603499.SH +2024-03-14,0.648581745020845,603773.SH +2024-03-15,1.2772879612108654,002902.SZ +2024-03-18,0.8179927345023094,002591.SZ +2024-03-19,1.6214555790374034,000908.SZ +2024-03-20,2.2115174455952333,603580.SH +2024-03-21,1.545209963736103,603499.SH +2024-03-22,0.6838446792203144,605180.SH +2024-03-25,0.31578511420724414,603985.SH +2024-03-26,1.781904626165001,605198.SH +2024-03-27,-0.16938824239950148,603006.SH +2024-03-28,0.9649663433118687,002739.SZ +2024-03-29,0.3320943922901912,603499.SH +2024-04-01,0.9952388185722638,603878.SH +2024-04-02,0.9364155422135148,000603.SZ +2024-04-03,0.7982619216871609,603878.SH +2024-04-08,1.358471943036704,605198.SH +2024-04-09,0.18053926667744466,002574.SZ +2024-04-10,1.3153422390391303,000528.SZ +2024-04-11,1.3657881440950266,002716.SZ +2024-04-12,1.5676274828749224,000975.SZ +2024-04-15,-0.13567914644119722,000157.SZ +2024-04-16,0.93425932274213,600690.SH +2024-04-17,1.327024106545604,001696.SZ +2024-04-18,1.8296222086183656,600210.SH +2024-04-19,0.578844593874061,002716.SZ +2024-04-22,1.0676680912492613,603050.SH +2024-04-23,1.5669359888338785,002167.SZ +2024-04-24,0.7255550236691839,603017.SH +2024-04-25,1.997033122105935,000737.SZ +2024-04-26,1.1631716891035941,001696.SZ +2024-04-29,0.5416930775042976,002715.SZ +2024-04-30,0.4865144780512015,002167.SZ +2024-05-06,1.6985755115133785,002125.SZ +2024-05-07,1.5949401523876496,600789.SH +2024-05-08,0.978355142201031,605177.SH +2024-05-09,0.1605421067543249,603018.SH +2024-05-10,0.6676316772066117,002264.SZ +2024-05-13,0.733933392442504,603222.SH +2024-05-14,0.5547173325807716,603025.SH +2024-05-15,1.3155422143795996,000913.SZ +2024-05-16,1.4221204991742524,001696.SZ +2024-05-17,1.1562985723597323,600383.SH +2024-05-20,0.4695922603130449,002264.SZ +2024-05-21,1.8689264604736717,000002.SZ +2024-05-22,1.3121218875647056,600843.SH +2024-05-23,0.7664908704814034,002225.SZ +2024-05-24,0.7589930057344444,600530.SH +2024-05-27,1.19159766228358,601919.SH +2024-05-28,1.0019105122452157,600726.SH +2024-05-29,0.4881622770453212,601019.SH +2024-05-30,1.4287668616222775,002938.SZ +2024-05-31,1.0528834774374836,601019.SH +2024-06-03,0.7762962067944593,603530.SH +2024-06-04,1.7857240618535462,601117.SH +2024-06-05,1.3040177518621878,603530.SH +2024-06-06,1.0260169367235976,600900.SH +2024-06-07,0.8588448524419576,603172.SH +2024-06-11,1.1812779982242239,600584.SH +2024-06-12,1.3882505145638304,003026.SZ +2024-06-13,1.4302228321179788,001339.SZ +2024-06-14,0.9306175962728962,002889.SZ +2024-06-17,0.6869594774433183,600530.SH +2024-06-18,1.1904711061862112,001298.SZ +2024-06-19,1.697703035579816,605258.SH +2024-06-20,0.8952978126779231,600733.SH +2024-06-21,1.6755370442001838,001298.SZ +2024-06-24,0.7658870375300119,002252.SZ +2024-06-25,0.5860602429129975,002485.SZ +2024-06-26,0.3448818899550934,003031.SZ +2024-06-27,0.5265851943756428,000793.SZ +2024-06-28,1.9979062957915383,603838.SH +2024-07-01,1.0825164001234038,002485.SZ +2024-07-02,0.9075039211419761,601985.SH +2024-07-03,1.409183048681464,600025.SH +2024-07-04,0.904029442040085,600025.SH +2024-07-05,1.0426545217304006,001286.SZ +2024-07-08,1.698642218338944,600584.SH +2024-07-09,0.6470181687793882,002463.SZ +2024-07-10,2.06122700428312,002947.SZ +2024-07-11,0.7410903122804028,600601.SH +2024-07-12,1.340083218079972,600686.SH +2024-07-15,1.5618937384484992,600733.SH +2024-07-16,1.758580945058497,600733.SH +2024-07-17,1.5157121004090266,000625.SZ +2024-07-18,0.3083377315759717,603898.SH +2024-07-19,0.8944208661047769,000078.SZ +2024-07-22,1.0429143541686636,002829.SZ +2024-07-23,0.8234894660266799,002005.SZ +2024-07-24,1.0749929990189133,600626.SH +2024-07-25,1.880220214501819,600611.SH +2024-07-26,1.8509059338376548,600650.SH +2024-07-29,2.076506303850798,600817.SH +2024-07-30,2.1084511316806602,000712.SZ +2024-07-31,1.638115197432452,000421.SZ +2024-08-01,1.040980502901104,600811.SH +2024-08-02,1.7885572982299731,001696.SZ +2024-08-05,0.9537751859233105,001379.SZ +2024-08-06,0.3092017392193045,002461.SZ +2024-08-07,1.466643911628964,002488.SZ +2024-08-08,0.7882843739164674,603863.SH +2024-08-09,0.23494689741035102,603488.SH +2024-08-12,2.131668395298663,002488.SZ +2024-08-13,2.0937880721661606,603978.SH +2024-08-14,1.131943220068558,002232.SZ +2024-08-15,0.5873737710201326,002611.SZ +2024-08-16,1.199835048451392,000062.SZ +2024-08-19,1.1805751411608383,600811.SH +2024-08-20,1.5503271129180245,600661.SH +2024-08-21,0.578554281440386,000880.SZ +2024-08-22,1.2853820450612699,600105.SH +2024-08-23,0.6423692277445572,002403.SZ +2024-08-26,0.9239367963781929,002986.SZ +2024-08-27,0.46409556994532736,605183.SH +2024-08-28,1.0631407155165504,002647.SZ +2024-08-29,1.1245501698512903,603639.SH +2024-08-30,0.6361599235773499,002246.SZ +2024-09-02,1.2492364629991852,002072.SZ +2024-09-03,1.1926755374832714,002199.SZ +2024-09-04,1.6204729565651932,600599.SH +2024-09-05,1.0957778793973632,002595.SZ +2024-09-06,1.6168023970816094,002005.SZ +2024-09-09,0.8605482386029011,002456.SZ +2024-09-10,1.3546410789459808,002717.SZ +2024-09-11,0.5980254226205031,603196.SH +2024-09-12,0.9187303745519808,002640.SZ +2024-09-13,0.6801121207893971,600358.SH +2024-09-18,1.079264299860147,600550.SH +2024-09-19,1.9477947178464494,002786.SZ +2024-09-20,1.1194340099294373,002123.SZ +2024-09-23,1.6714675557693415,002453.SZ +2024-09-24,1.3675826070587052,002453.SZ +2024-09-25,0.7076301816428471,000627.SZ +2024-09-26,0.8882412710980511,603398.SH +2024-09-27,0.7521975260737696,000002.SZ +2024-09-30,0.7230331038260748,600570.SH +2024-10-08,1.0593517928482987,600550.SH +2024-10-09,1.0220239311477122,000560.SZ +2024-10-10,1.234368197028218,600606.SH +2024-10-11,0.8664109594444052,000750.SZ +2024-10-14,1.8450296800922745,603822.SH +2024-10-15,1.8353472785641625,002583.SZ +2024-10-16,0.9543901161454763,000536.SZ +2024-10-17,1.0015738096430133,600619.SH +2024-10-18,1.2591757462030437,600622.SH +2024-10-21,1.364327760137209,002583.SZ +2024-10-22,2.0756244365506276,002685.SZ +2024-10-23,0.38458758527962894,000004.SZ +2024-10-24,1.5124132416681377,002094.SZ +2024-10-25,1.4347033681199353,600839.SH +2024-10-28,1.5745059258009038,001696.SZ +2024-10-29,1.8529251428567266,002628.SZ +2024-10-30,1.7713369405635928,002717.SZ +2024-10-31,1.1151492405672683,001696.SZ +2024-11-01,0.7164870376829126,600246.SH +2024-11-04,1.769839917258226,002384.SZ +2024-11-05,1.092728971983151,600212.SH +2024-11-06,0.6671879019120053,603336.SH +2024-11-07,0.5602821558966659,002416.SZ +2024-11-08,1.1858491806130256,001696.SZ +2024-11-11,1.3270787455846025,002456.SZ +2024-11-12,1.4104327679900899,603398.SH +2024-11-13,1.2197272384042277,600839.SH +2024-11-14,0.6314176029145626,603697.SH +2024-11-15,-0.13737144527610326,603268.SH +2024-11-18,0.7120357386859574,000605.SZ +2024-11-19,0.42252100937945863,002469.SZ +2024-11-20,1.007664521961136,600619.SH +2024-11-21,0.9709988043471746,002694.SZ +2024-11-22,-0.01837433439991007,000415.SZ +2024-11-25,0.7354373323734968,000973.SZ +2024-11-26,1.4832316580516,002348.SZ +2024-11-27,1.0092630330488712,002899.SZ +2024-11-28,1.6528548440755675,600327.SH +2024-11-29,1.5310967942763765,003010.SZ +2024-12-02,0.9985829805322318,000981.SZ +2024-12-03,1.1009990341574898,600628.SH +2024-12-04,1.4406720428536548,000679.SZ +2024-12-05,1.5166810165916147,002640.SZ +2024-12-06,1.5480151614841313,003021.SZ +2024-12-09,0.7658546815142482,600593.SH +2024-12-10,1.6164052159572586,000981.SZ +2024-12-11,1.0871733525157767,000882.SZ +2024-12-12,1.3618111397645727,002265.SZ +2024-12-13,1.1378238320491278,605006.SH +2024-12-16,0.5942319336561258,603533.SH +2024-12-17,0.8922367248452927,600503.SH +2024-12-18,0.29961260322010785,600796.SH +2024-12-19,0.5126832965650956,002031.SZ +2024-12-20,0.4049032161823233,600172.SH +2024-12-23,0.378293448285725,600172.SH +2024-12-24,0.7270156370860105,600673.SH +2024-12-25,0.8938311286037234,603610.SH +2024-12-26,0.8824820051198142,603893.SH +2024-12-27,1.3362649834557183,002068.SZ +2024-12-30,0.35138947858631225,600183.SH +2024-12-31,1.1948708599448123,600506.SH +2025-01-02,1.4558889081434663,002730.SZ +2025-01-03,0.7128978848132725,600707.SH +2025-01-06,0.7650844601290192,600803.SH +2025-01-07,1.0635380792047187,600405.SH +2025-01-08,0.7951289392847068,002837.SZ +2025-01-09,1.0252099583039425,603306.SH +2025-01-10,0.30155175957668673,002916.SZ +2025-01-13,0.9708056750766493,603986.SH +2025-01-14,0.7870312186305877,000063.SZ +2025-01-15,1.6878038525240218,002917.SZ +2025-01-16,0.8478674953917144,002449.SZ +2025-01-17,1.0235316084839805,600693.SH +2025-01-20,0.8827244215589688,002730.SZ +2025-01-21,0.9142145000698546,002379.SZ +2025-01-22,1.632439883586815,603228.SH +2025-01-23,0.5950475737248152,603121.SH +2025-01-24,1.512800316493977,000880.SZ +2025-01-27,0.280247948525134,002532.SZ +2025-02-05,0.7525772298409897,600722.SH +2025-02-06,-0.059951823103750426,601869.SH +2025-02-07,0.45215019046862537,000977.SZ +2025-02-10,1.2658737579405763,003007.SZ +2025-02-11,1.0408324160127955,603106.SH +2025-02-12,1.6119030499830551,000856.SZ +2025-02-13,1.59310517514652,002929.SZ +2025-02-14,1.5077797651403821,002410.SZ +2025-02-17,1.3860825096123015,002044.SZ +2025-02-18,1.1270921559091267,002779.SZ +2025-02-19,1.187211682113541,603286.SH +2025-02-20,1.2669618354736996,605488.SH +2025-02-21,1.025703816755235,600588.SH +2025-02-24,1.5648046166656084,600602.SH +2025-02-25,0.8745657353291226,002896.SZ +2025-02-26,0.9230669528117501,000032.SZ +2025-02-27,0.9367812438709472,603200.SH +2025-02-28,1.0306691811926367,002276.SZ +2025-03-03,0.6479828536311146,002044.SZ +2025-03-04,0.9022328614595625,001306.SZ +2025-03-05,0.13297506000529996,002036.SZ +2025-03-06,1.0473094549911606,001309.SZ +2025-03-07,0.8908293616489041,002580.SZ +2025-03-10,0.9714032058498194,600126.SH +2025-03-11,1.5066583997940508,000678.SZ +2025-03-12,1.2822828774552384,603059.SH +2025-03-13,1.1088751386616387,003038.SZ +2025-03-14,1.2806594439606795,002713.SZ +2025-03-17,0.9239379376977839,001256.SZ +2025-03-18,1.155518645532713,600610.SH +2025-03-19,1.566452580640379,605008.SH +2025-03-20,1.6658038834776503,603949.SH +2025-03-21,0.3002046262826852,603112.SH +2025-03-24,0.969025415982965,001256.SZ +2025-03-25,1.0089862742297053,002300.SZ +2025-03-26,0.9563316876479583,600468.SH +2025-03-27,0.7596417124134163,000633.SZ +2025-03-28,0.8823950983342874,000006.SZ +2025-03-31,1.5478113985550597,002851.SZ +2025-04-01,0.5961759062409911,002847.SZ +2025-04-02,0.1297394192678443,002093.SZ +2025-04-03,1.6113567681708816,603353.SH +2025-04-07,1.6243924524047828,601008.SH +2025-04-08,1.5465757662622548,600598.SH +2025-04-09,1.2262057864670963,601952.SH diff --git a/main/train/test2.tsv b/main/train/test2.tsv new file mode 100644 index 0000000..f5b3c20 --- /dev/null +++ b/main/train/test2.tsv @@ -0,0 +1,72 @@ +trade_date,score,ts_code +2024-12-09,1.1968650846005326,600593.SH +2024-12-10,0.21490252296809745,002611.SZ +2024-12-11,0.5721914798956016,002321.SZ +2024-12-12,0.6509338263544048,600628.SH +2024-12-13,2.1288113028385376,600628.SH +2024-12-16,1.378346480524284,002086.SZ +2024-12-17,1.45986967550941,002741.SZ +2024-12-18,1.3436778254529067,600579.SH +2024-12-19,0.41218776805787716,600796.SH +2024-12-20,1.0840917563770454,603421.SH +2024-12-23,1.00141172278312,600889.SH +2024-12-24,1.0354156548919864,600725.SH +2024-12-25,0.9562524807100355,600066.SH +2024-12-26,1.1279048294352958,002916.SZ +2024-12-27,0.6532174116474766,002068.SZ +2024-12-30,-0.1308794790538431,002918.SZ +2024-12-31,0.7160474599127873,600857.SH +2025-01-02,1.5067649520721882,002449.SZ +2025-01-03,0.9282246137432282,603379.SH +2025-01-06,0.6797051204009213,603893.SH +2025-01-07,0.9376184079476354,603236.SH +2025-01-08,0.9064516934700023,603308.SH +2025-01-09,0.9314493554789942,000880.SZ +2025-01-10,0.5025761501709369,600584.SH +2025-01-13,0.7483210862212708,000063.SZ +2025-01-14,1.2632673941368837,000063.SZ +2025-01-15,1.8580661802761587,002917.SZ +2025-01-16,1.1918089652002073,600693.SH +2025-01-17,0.8288939941365315,600693.SH +2025-01-20,0.677726091977902,002577.SZ +2025-01-21,1.8336548268410158,603893.SH +2025-01-22,1.0395051538956546,000573.SZ +2025-01-23,0.4308220427423068,003021.SZ +2025-01-24,1.8057941775723685,002862.SZ +2025-01-27,1.216662909774701,002779.SZ +2025-02-05,0.8484867753831473,603990.SH +2025-02-06,0.5038824073142949,001380.SZ +2025-02-07,0.7672133571524726,002031.SZ +2025-02-10,0.5417223016033719,000681.SZ +2025-02-11,0.9399374716518157,000034.SZ +2025-02-12,1.8742056631297925,000856.SZ +2025-02-13,1.4837670146272484,600633.SH +2025-02-14,1.2043600916692372,605488.SH +2025-02-17,1.1259104542173328,603918.SH +2025-02-18,1.1806931791732853,600126.SH +2025-02-19,1.020437698817749,603956.SH +2025-02-20,0.5818349669113919,003021.SZ +2025-02-21,1.0941497070930342,603950.SH +2025-02-24,1.867258980329339,600602.SH +2025-02-25,0.8646726218943293,002691.SZ +2025-02-26,1.2878484406363957,002245.SZ +2025-02-27,1.3013902577988068,600173.SH +2025-02-28,0.7804376426721501,603040.SH +2025-03-03,0.45593268249434266,002345.SZ +2025-03-04,0.9265705061587579,600589.SH +2025-03-05,0.766962270753268,002575.SZ +2025-03-06,0.7030260458187082,601100.SH +2025-03-07,0.924011193171594,002896.SZ +2025-03-10,1.0811487252993004,600126.SH +2025-03-11,1.272392599656189,002896.SZ +2025-03-12,1.0905437448562905,002276.SZ +2025-03-13,1.0688995313878895,003038.SZ +2025-03-14,1.2418913857438587,001256.SZ +2025-03-17,1.004550155323,001256.SZ +2025-03-18,0.7517848278576412,600403.SH +2025-03-19,1.5106246878723002,605008.SH +2025-03-20,1.1509811695536982,600610.SH +2025-03-21,0.6033998331536018,603196.SH +2025-03-24,0.3456173948047773,002345.SZ +2025-03-25,1.470314131581159,600320.SH +2025-03-26,0.745243100558546,603325.SH diff --git a/main/utils/__init__.py b/main/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/main/utils/__pycache__/__init__.cpython-310.pyc b/main/utils/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..3c49b9a Binary files /dev/null and b/main/utils/__pycache__/__init__.cpython-310.pyc differ diff --git a/main/utils/__pycache__/__init__.cpython-311.pyc b/main/utils/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..045cc76 Binary files /dev/null and b/main/utils/__pycache__/__init__.cpython-311.pyc differ diff --git a/main/utils/__pycache__/factor.cpython-311.pyc b/main/utils/__pycache__/factor.cpython-311.pyc new file mode 100644 index 0000000..ff62e40 Binary files /dev/null and b/main/utils/__pycache__/factor.cpython-311.pyc differ diff --git a/main/utils/__pycache__/factor_processor.cpython-311.pyc b/main/utils/__pycache__/factor_processor.cpython-311.pyc new file mode 100644 index 0000000..b393583 Binary files /dev/null and b/main/utils/__pycache__/factor_processor.cpython-311.pyc differ diff --git a/main/utils/__pycache__/utils.cpython-310.pyc b/main/utils/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000..a3d878a Binary files /dev/null and b/main/utils/__pycache__/utils.cpython-310.pyc differ diff --git a/main/utils/__pycache__/utils.cpython-311.pyc b/main/utils/__pycache__/utils.cpython-311.pyc new file mode 100644 index 0000000..836bdb6 Binary files /dev/null and b/main/utils/__pycache__/utils.cpython-311.pyc differ diff --git a/code/utils/factor.py b/main/utils/factor.py similarity index 93% rename from code/utils/factor.py rename to main/utils/factor.py index 8c8e9fa..9fc8db0 100644 --- a/code/utils/factor.py +++ b/main/utils/factor.py @@ -2,6 +2,7 @@ import numpy as np import talib import pandas as pd + def get_technical_factor(df): # 按股票和日期排序 df = df.sort_values(by=['ts_code', 'trade_date']) @@ -735,4 +736,45 @@ def generate_index_indicators(h5_filename): df_final.columns = [f"{col[1]}_{col[0]}" for col in df_final.columns] df_final = df_final.reset_index() - return df_final \ No newline at end of file + return df_final + + +def read_industry_data(h5_filename): + # 读取 H5 文件中所有的行业数据 + industry_data = pd.read_hdf(h5_filename, key='sw_daily', columns=[ + 'ts_code', 'trade_date', 'open', 'close', 'high', 'low', 'pe', 'pb', 'vol' + ]) # 假设 H5 文件的键是 'industry_data' + industry_data = industry_data.sort_values(by=['ts_code', 'trade_date']) + industry_data = industry_data.reindex() + industry_data['trade_date'] = pd.to_datetime(industry_data['trade_date'], format='%Y%m%d') + + grouped = industry_data.groupby('ts_code', group_keys=False) + industry_data['obv'] = grouped.apply( + lambda x: pd.Series(talib.OBV(x['close'].values, x['vol'].values), index=x.index) + ) + industry_data['return_5'] = grouped['close'].apply(lambda x: x / x.shift(5) - 1) + industry_data['return_20'] = grouped['close'].apply(lambda x: x / x.shift(20) - 1) + + industry_data = get_act_factor(industry_data, cat=False) + industry_data = industry_data.sort_values(by=['trade_date', 'ts_code']) + + # # 计算每天每个 ts_code 的因子和当天所有 ts_code 的中位数的偏差 + # factor_columns = ['obv', 'return_5', 'return_20', 'act_factor1', 'act_factor2', 'act_factor3', 'act_factor4'] # 因子列 + # + # for factor in factor_columns: + # if factor in industry_data.columns: + # # 计算每天每个 ts_code 的因子值与当天所有 ts_code 的中位数的偏差 + # industry_data[f'{factor}_deviation'] = industry_data.groupby('trade_date')[factor].transform( + # lambda x: x - x.mean()) + + industry_data['return_5_percentile'] = industry_data.groupby('trade_date')['return_5'].transform( + lambda x: x.rank(pct=True)) + industry_data['return_20_percentile'] = industry_data.groupby('trade_date')['return_20'].transform( + lambda x: x.rank(pct=True)) + industry_data = industry_data.drop(columns=['open', 'close', 'high', 'low', 'pe', 'pb', 'vol']) + + industry_data = industry_data.rename( + columns={col: f'industry_{col}' for col in industry_data.columns if col not in ['ts_code', 'trade_date']}) + + industry_data = industry_data.rename(columns={'ts_code': 'cat_l2_code'}) + return industry_data diff --git a/main/utils/factor_processor.py b/main/utils/factor_processor.py new file mode 100644 index 0000000..1c8ea55 --- /dev/null +++ b/main/utils/factor_processor.py @@ -0,0 +1,233 @@ +import numpy as np +import pandas as pd + +from scipy.stats import ks_2samp +from sklearn.preprocessing import StandardScaler + + +def remove_shifted_features(train_data, feature_columns, ks_threshold=0.05, wasserstein_threshold=0.1, size=0.8, + log=True, val_data=None): + dropped_features = [] + + if val_data is None: + all_dates = sorted(train_data['trade_date'].unique().tolist()) # 获取所有唯一的 trade_date + split_date = all_dates[int(len(all_dates) * size)] # 划分点为倒数第 validation_days 天 + train_data_split = train_data[train_data['trade_date'] < split_date] # 训练集 + val_data_split = train_data[train_data['trade_date'] >= split_date] # 验证集 + else: + train_data_split = train_data + val_data_split = val_data + + # **统计数据漂移** + numeric_columns = train_data_split.select_dtypes(include=['float64', 'int64']).columns + numeric_columns = [col for col in numeric_columns if col in feature_columns] + for feature in numeric_columns: + ks_stat, p_value = ks_2samp(train_data_split[feature], val_data_split[feature]) + # wasserstein_dist = wasserstein_distance(train_data_split[feature], val_data_split[feature]) + + # if p_value < ks_threshold or wasserstein_dist > wasserstein_threshold: + if p_value < ks_threshold: + dropped_features.append(feature) + if log: + print(f"检测到 {len(dropped_features)} 个可能漂移的特征: {dropped_features}") + + # **应用阈值进行最终筛选** + filtered_features = [f for f in feature_columns if f not in dropped_features] + + return filtered_features, dropped_features + + +def remove_outliers_label_percentile(label: pd.Series, lower_percentile: float = 0.01, upper_percentile: float = 0.99, + log=True): + if not (0 <= lower_percentile < upper_percentile <= 1): + raise ValueError("Percentile values must satisfy 0 <= lower_percentile < upper_percentile <= 1.") + + # Calculate lower and upper bounds based on percentiles + lower_bound = label.quantile(lower_percentile) + upper_bound = label.quantile(upper_percentile) + + # Filter out values outside the bounds + filtered_label = label[(label >= lower_bound) & (label <= upper_bound)] + + # Print the number of removed outliers + if log: + print(f"Removed {len(label) - len(filtered_label)} outliers.") + return filtered_label + + +def calculate_risk_adjusted_target(df, days=5): + df = df.sort_values(by=['ts_code', 'trade_date']) + + df['future_close'] = df.groupby('ts_code')['close'].shift(-days) + df['future_open'] = df.groupby('ts_code')['open'].shift(-1) + df['future_return'] = (df['future_close'] - df['future_open']) / df['future_open'] + + df['future_volatility'] = df.groupby('ts_code')['future_return'].rolling(days, min_periods=1).std().reset_index( + level=0, drop=True) + sharpe_ratio = df['future_return'] * df['future_volatility'] + sharpe_ratio.replace([np.inf, -np.inf], np.nan, inplace=True) + + return sharpe_ratio + + +def calculate_score(df, days=5, lambda_param=1.0): + def calculate_max_drawdown(prices): + peak = prices.iloc[0] # 初始化峰值 + max_drawdown = 0 # 初始化最大回撤 + + for price in prices: + if price > peak: + peak = price # 更新峰值 + else: + drawdown = (peak - price) / peak # 计算当前回撤 + max_drawdown = max(max_drawdown, drawdown) # 更新最大回撤 + + return max_drawdown + + def compute_stock_score(stock_df): + stock_df = stock_df.sort_values(by=['trade_date']) + future_return = stock_df['future_return'] + # 使用已有的 pct_chg 字段计算波动率 + volatility = stock_df['pct_chg'].rolling(days).std().shift(-days) + max_drawdown = stock_df['close'].rolling(days).apply(calculate_max_drawdown, raw=False).shift(-days) + score = future_return - lambda_param * max_drawdown + return score + + # # 确保 DataFrame 按照股票代码和交易日期排序 + # df = df.sort_values(by=['ts_code', 'trade_date']) + + # 对每个股票分别计算 score + df['score'] = df.groupby('ts_code').apply(compute_stock_score).reset_index(level=0, drop=True) + + return df['score'] + + +def remove_highly_correlated_features(df, feature_columns, threshold=0.9): + numeric_features = df[feature_columns].select_dtypes(include=[np.number]).columns.tolist() + if not numeric_features: + raise ValueError("No numeric features found in the provided data.") + + corr_matrix = df[numeric_features].corr().abs() + upper = corr_matrix.where(np.triu(np.ones(corr_matrix.shape), k=1).astype(bool)) + to_drop = [column for column in upper.columns if any(upper[column] > threshold)] + remaining_features = [col for col in feature_columns if col not in to_drop + or 'act' in col or 'af' in col] + return remaining_features + + +def cross_sectional_standardization(df, features): + df_sorted = df.sort_values(by='trade_date') # 按时间排序 + df_standardized = df_sorted.copy() + + for date in df_sorted['trade_date'].unique(): + # 获取当前时间点的数据 + current_data = df_standardized[df_standardized['trade_date'] == date] + + # 只对指定特征进行标准化 + scaler = StandardScaler() + standardized_values = scaler.fit_transform(current_data[features]) + + # 将标准化结果重新赋值回去 + df_standardized.loc[df_standardized['trade_date'] == date, features] = standardized_values + + return df_standardized + + +def neutralize_manual(df, features, industry_col, mkt_cap_col): + """ 手动实现简单回归以提升速度 """ + + for col in features: + residuals = [] + for _, group in df.groupby(industry_col): + if len(group) > 1: + x = np.log(group[mkt_cap_col]) # 市值对数 + y = group[col] # 因子值 + beta = np.cov(y, x)[0, 1] / np.var(x) # 计算斜率 + alpha = np.mean(y) - beta * np.mean(x) # 计算截距 + resid = y - (alpha + beta * x) # 计算残差 + residuals.extend(resid) + else: + residuals.extend(group[col]) # 样本不足时保留原值 + + df[col] = residuals + + return df + + +def mad_filter(df, features, n=3): + for col in features: + median = df[col].median() + mad = np.median(np.abs(df[col] - median)) + upper = median + n * mad + lower = median - n * mad + df[col] = np.clip(df[col], lower, upper) # 截断极值 + return df + + +def percentile_filter(df, features, lower_percentile=0.01, upper_percentile=0.99): + for col in features: + # 按日期分组计算上下百分位数 + lower_bound = df.groupby('trade_date')[col].transform( + lambda x: x.quantile(lower_percentile) + ) + upper_bound = df.groupby('trade_date')[col].transform( + lambda x: x.quantile(upper_percentile) + ) + # 截断超出范围的值 + df[col] = np.clip(df[col], lower_bound, upper_bound) + return df + + +from scipy.stats import iqr + + +def iqr_filter(df, features): + for col in features: + df[col] = df.groupby('trade_date')[col].transform( + lambda x: (x - x.median()) / iqr(x) if iqr(x) != 0 else x + ) + return df + + +def quantile_filter(df, features, lower_quantile=0.01, upper_quantile=0.99, window=60): + df = df.copy() + for col in features: + # 计算 rolling 统计量,需要按日期进行 groupby + rolling_lower = df.groupby('trade_date')[col].transform( + lambda x: x.rolling(window=min(len(x), window)).quantile(lower_quantile)) + rolling_upper = df.groupby('trade_date')[col].transform( + lambda x: x.rolling(window=min(len(x), window)).quantile(upper_quantile)) + + # 对数据进行裁剪 + df[col] = np.clip(df[col], rolling_lower, rolling_upper) + + return df + + +def time_series_quantile_filter(df, features, lower_quantile=0.01, upper_quantile=0.99, window=60): + df = df.copy() + # 确保按股票和时间排序 + df = df.sort_values(['ts_code', 'trade_date']) + grouped = df.groupby('ts_code') + for col in features: + # 对每个股票的时间序列计算滚动分位数 + rolling_lower = grouped[col].rolling(window=window, min_periods=window // 2).quantile(lower_quantile) + rolling_upper = grouped[col].rolling(window=window, min_periods=window // 2).quantile(upper_quantile) + # rolling结果带有多重索引,需要对齐 + rolling_lower = rolling_lower.reset_index(level=0, drop=True) + rolling_upper = rolling_upper.reset_index(level=0, drop=True) + # 应用 clip + df[col] = np.clip(df[col], rolling_lower, rolling_upper) + return df + + +def cross_sectional_quantile_filter(df, features, lower_quantile=0.01, upper_quantile=0.99): + df = df.copy() + grouped = df.groupby('trade_date') + for col in features: + # 计算每日截面的分位数边界 + lower_bound = grouped[col].transform(lambda x: x.quantile(lower_quantile)) + upper_bound = grouped[col].transform(lambda x: x.quantile(upper_quantile)) + # 应用 clip + df[col] = np.clip(df[col], lower_bound, upper_bound) + return df \ No newline at end of file diff --git a/code/utils/utils.py b/main/utils/utils.py similarity index 79% rename from code/utils/utils.py rename to main/utils/utils.py index 87ccaa6..0e43dda 100644 --- a/code/utils/utils.py +++ b/main/utils/utils.py @@ -42,6 +42,43 @@ def read_and_merge_h5_data(h5_filename, key, columns, df=None, join='left', on=[ return merged_df +def merge_with_industry_data(df, industry_df): + # 确保日期字段是 datetime 类型 + df['trade_date'] = pd.to_datetime(df['trade_date']) + industry_df['in_date'] = pd.to_datetime(industry_df['in_date']) + + # 对 industry_df 按 ts_code 和 in_date 排序 + industry_df_sorted = industry_df.sort_values(['in_date', 'ts_code']) + + # 对原始 df 按 ts_code 和 trade_date 排序 + df_sorted = df.sort_values(['trade_date', 'ts_code']) + + # 使用 merge_asof 进行向后合并 + merged = pd.merge_asof( + df_sorted, + industry_df_sorted, + by='ts_code', # 按 ts_code 分组 + left_on='trade_date', + right_on='in_date', + direction='backward' + ) + + # 获取每个 ts_code 的最早 in_date 记录 + min_in_date_per_ts = (industry_df_sorted + .groupby('ts_code') + .first() + .reset_index()[['ts_code', 'l2_code']]) + + # 填充未匹配到的记录(trade_date 早于所有 in_date 的情况) + merged['l2_code'] = merged['l2_code'].fillna( + merged['ts_code'].map(min_in_date_per_ts.set_index('ts_code')['l2_code']) + ) + + # 保留需要的列并重置索引 + result = merged.reset_index(drop=True) + return result + + def calculate_risk_adjusted_return(df, days=1, method='ratio', lambda_=0.5, eps=1e-8): """ 计算单只股票的风险调整收益。 @@ -81,7 +118,6 @@ def calculate_risk_adjusted_return(df, days=1, method='ratio', lambda_=0.5, eps= return df - # import polars as pl # # def read_and_merge_h5_data_polars(h5_filename, key, columns, df=None, join='left', on=['ts_code', 'trade_date']): @@ -116,5 +152,3 @@ def calculate_risk_adjusted_return(df, days=1, method='ratio', lambda_=0.5, eps= # merged_df = data # # return merged_df - -